diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 89b7ffa10..885612f13 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,3 +1,4 @@ # These are supported funding model platforms +github: odin-lang patreon: gingerbill diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6742b56f3..e6341671a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,5 +1,5 @@ name: CI -on: [push, pull_request] +on: [push, pull_request, workflow_dispatch] jobs: build_linux: @@ -7,9 +7,9 @@ jobs: steps: - uses: actions/checkout@v1 - name: Download LLVM, botan - run: sudo apt-get install llvm-11 clang-11 llvm libbotan-2-dev botan + run: sudo apt-get install llvm-11 clang-11 libbotan-2-dev botan - name: build odin - run: make release + run: ./build_odin.sh release - name: Odin version run: ./odin version timeout-minutes: 1 @@ -17,13 +17,16 @@ jobs: run: ./odin report timeout-minutes: 1 - name: Odin check - run: ./odin check examples/demo/demo.odin -vet + run: ./odin check examples/demo -vet timeout-minutes: 10 - name: Odin run - run: ./odin run examples/demo/demo.odin + run: ./odin run examples/demo timeout-minutes: 10 - name: Odin run -debug - run: ./odin run examples/demo/demo.odin -debug + run: ./odin run examples/demo -debug + timeout-minutes: 10 + - name: Odin check examples/all + run: ./odin check examples/all -strict-style timeout-minutes: 10 - name: Core library tests run: | @@ -35,6 +38,20 @@ jobs: cd tests/vendor make timeout-minutes: 10 + - name: Odin issues tests + run: | + cd tests/issues + ./run.sh + timeout-minutes: 10 + - name: Odin check examples/all for Linux i386 + run: ./odin check examples/all -vet -strict-style -target:linux_i386 + timeout-minutes: 10 + - name: Odin check examples/all for FreeBSD amd64 + run: ./odin check examples/all -vet -strict-style -target:freebsd_amd64 + timeout-minutes: 10 + - name: Odin check examples/all for OpenBSD amd64 + run: ./odin check examples/all -vet -strict-style -target:openbsd_amd64 + timeout-minutes: 10 build_macOS: runs-on: macos-latest steps: @@ -46,7 +63,7 @@ jobs: TMP_PATH=$(xcrun --show-sdk-path)/user/include echo "CPATH=$TMP_PATH" >> $GITHUB_ENV - name: build odin - run: make release + run: ./build_odin.sh release - name: Odin version run: ./odin version timeout-minutes: 1 @@ -54,13 +71,16 @@ jobs: run: ./odin report timeout-minutes: 1 - name: Odin check - run: ./odin check examples/demo/demo.odin -vet + run: ./odin check examples/demo -vet timeout-minutes: 10 - name: Odin run - run: ./odin run examples/demo/demo.odin + run: ./odin run examples/demo timeout-minutes: 10 - name: Odin run -debug - run: ./odin run examples/demo/demo.odin -debug + run: ./odin run examples/demo -debug + timeout-minutes: 10 + - name: Odin check examples/all + run: ./odin check examples/all -strict-style timeout-minutes: 10 - name: Core library tests run: | @@ -72,8 +92,19 @@ jobs: cd tests/vendor make timeout-minutes: 10 + - name: Odin issues tests + run: | + cd tests/issues + ./run.sh + timeout-minutes: 10 + - name: Odin check examples/all for Darwin arm64 + run: ./odin check examples/all -vet -strict-style -target:darwin_arm64 + timeout-minutes: 10 + - name: Odin check examples/all for Linux arm64 + run: ./odin check examples/all -vet -strict-style -target:linux_arm64 + timeout-minutes: 10 build_windows: - runs-on: windows-latest + runs-on: windows-2019 steps: - uses: actions/checkout@v1 - name: build Odin @@ -91,19 +122,25 @@ jobs: shell: cmd run: | call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat - odin check examples/demo/demo.odin -vet + odin check examples/demo -vet timeout-minutes: 10 - name: Odin run shell: cmd run: | call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat - odin run examples/demo/demo.odin + odin run examples/demo timeout-minutes: 10 - name: Odin run -debug shell: cmd run: | call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat - odin run examples/demo/demo.odin -debug + odin run examples/demo -debug + timeout-minutes: 10 + - name: Odin check examples/all + shell: cmd + run: | + call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat + odin check examples/all -strict-style timeout-minutes: 10 - name: Core library tests shell: cmd @@ -126,3 +163,16 @@ jobs: cd tests\core\math\big call build.bat timeout-minutes: 10 + - name: Odin issues tests + shell: cmd + run: | + call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat + cd tests\issues + call run.bat + timeout-minutes: 10 + - name: Odin check examples/all for Windows 32bits + shell: cmd + run: | + call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat + odin check examples/all -strict-style -target:windows_i386 + timeout-minutes: 10 diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 2b33c45a8..3c4185830 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -7,7 +7,7 @@ on: jobs: build_windows: - runs-on: windows-latest + runs-on: windows-2019 steps: - uses: actions/checkout@v1 - name: build Odin @@ -19,7 +19,7 @@ jobs: shell: cmd run: | call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat - odin run examples/demo/demo.odin + odin run examples/demo - name: Copy artifacts run: | rm bin/llvm/windows/LLVM-C.lib @@ -41,11 +41,11 @@ jobs: steps: - uses: actions/checkout@v1 - name: (Linux) Download LLVM - run: sudo apt-get install llvm-11 clang-11 llvm + run: sudo apt-get install llvm-11 clang-11 - name: build odin run: make nightly - name: Odin run - run: ./odin run examples/demo/demo.odin + run: ./odin run examples/demo - name: Copy artifacts run: | mkdir dist @@ -72,7 +72,7 @@ jobs: - name: build odin run: make nightly - name: Odin run - run: ./odin run examples/demo/demo.odin + run: ./odin run examples/demo - name: Copy artifacts run: | mkdir dist @@ -129,7 +129,7 @@ jobs: run: | echo Authorizing B2 account b2 authorize-account "$APPID" "$APPKEY" - + echo Uploading artifcates to B2 chmod +x ./ci/upload_create_nightly.sh ./ci/upload_create_nightly.sh "$BUCKET" windows-amd64 windows_artifacts/ @@ -141,7 +141,7 @@ jobs: echo Creating nightly.json python3 ci/create_nightly_json.py "$BUCKET" > nightly.json - + echo Uploading nightly.json b2 upload-file "$BUCKET" nightly.json nightly.json diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 000000000..341a09409 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,43 @@ +name: "Close Stale Issues & PRs" +on: + workflow_dispatch: + schedule: + - cron: "0 21 * * *" + +permissions: + issues: write + pull-requests: write + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - name: Close Stale Issues + uses: actions/stale@v4.1.0 + with: +# stale-issue-message: | +# Hello! +# +# I am marking this issue as stale as it has not received any engagement from the community or maintainers 120 days. That does not imply that the issue has no merit! If you feel strongly about this issue +# - open a PR referencing and resolving the issue; +# - leave a comment on it and discuss ideas how you could contribute towards resolving it; +# - leave a comment and describe in detail why this issue is critical for your use case; +# - open a new issue with updated details and a plan on resolving the issue. +# +# The motivation for this automation is to help prioritize issues in the backlog and not ignore, reject, or belittle anyone.. +# +# stale-pr-message: | +# Hello! +# +# I am marking this PR as stale as it has not received any engagement from the community or maintainers 120 days. That does not imply that the issue has no merit! If you feel strongly about this issue +# - leave a comment on it and discuss ideas how you could contribute towards resolving it; +# - leave a comment and describe in detail why this issue is critical for your use case; +# +# The motivation for this automation is to help prioritize issues in the backlog and not ignore, reject, or belittle anyone.. + + days-before-stale: 120 + days-before-close: 30 + exempt-draft-pr: true + ascending: true + operations-per-run: 1000 + exempt-issue-labels: "ignore" diff --git a/.gitignore b/.gitignore index 0d606498e..4c69a3f43 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,9 @@ # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs +# For macOS +.DS_Store + # Build results [Dd]ebug/ [Dd]ebugPublic/ @@ -266,6 +269,8 @@ bin/ # - Linux/MacOS odin odin.dSYM +*.bin +demo.bin # shared collection shared/ @@ -276,3 +281,5 @@ shared/ *.ll *.sublime-workspace +examples/bug/ +build.sh diff --git a/LICENSE b/LICENSE index 9ca94bcdf..9a87ab8da 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2016-2021 Ginger Bill. All rights reserved. +Copyright (c) 2016-2022 Ginger Bill. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/Makefile b/Makefile index d3d3c6a2d..91010a620 100644 --- a/Makefile +++ b/Makefile @@ -1,80 +1,19 @@ -GIT_SHA=$(shell git rev-parse --short HEAD) -DISABLED_WARNINGS=-Wno-switch -Wno-macro-redefined -Wno-unused-value -LDFLAGS=-pthread -ldl -lm -lstdc++ -CFLAGS=-std=c++14 -DGIT_SHA=\"$(GIT_SHA)\" -CFLAGS:=$(CFLAGS) -DODIN_VERSION_RAW=\"dev-$(shell date +"%Y-%m")\" -CC=clang - -OS=$(shell uname) - -ifeq ($(OS), Darwin) - - ARCH=$(shell uname -m) - LLVM_CONFIG= - - # allow for arm only llvm's with version 13 - ifeq ($(ARCH), arm64) - LLVM_VERSIONS = "13.%.%" - else - # allow for x86 / amd64 all llvm versions begining from 11 - LLVM_VERSIONS = "13.%.%" "12.0.1" "11.1.0" - endif - - LLVM_VERSION_PATTERN_SEPERATOR = )|( - LLVM_VERSION_PATTERNS_ESCAPED_DOT = $(subst .,\.,$(LLVM_VERSIONS)) - LLVM_VERSION_PATTERNS_REPLACE_PERCENT = $(subst %,.*,$(LLVM_VERSION_PATTERNS_ESCAPED_DOT)) - LLVM_VERSION_PATTERN_REMOVE_ELEMENTS = $(subst " ",$(LLVM_VERSION_PATTERN_SEPERATOR),$(LLVM_VERSION_PATTERNS_REPLACE_PERCENT)) - LLMV_VERSION_PATTERN_REMOVE_SINGLE_STR = $(subst ",,$(LLVM_VERSION_PATTERN_REMOVE_ELEMENTS)) - LLVM_VERSION_PATTERN = "^(($(LLMV_VERSION_PATTERN_REMOVE_SINGLE_STR)))" - - ifneq ($(shell llvm-config --version | grep -E $(LLVM_VERSION_PATTERN)),) - LLVM_CONFIG=llvm-config - else - ifeq ($(ARCH), arm64) - $(error "Requirement: llvm-config must be base version 13 for arm64") - else - $(error "Requirement: llvm-config must be base version greater than 11 for amd64/x86") - endif - endif - - LDFLAGS:=$(LDFLAGS) -liconv - CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags) - LDFLAGS:=$(LDFLAGS) -lLLVM-C -endif -ifeq ($(OS), Linux) - LLVM_CONFIG=llvm-config-11 - ifneq ($(shell which llvm-config-11 2>/dev/null),) - LLVM_CONFIG=llvm-config-11 - else ifneq ($(shell which llvm-config-11-64 2>/dev/null),) - LLVM_CONFIG=llvm-config-11-64 - else - ifneq ($(shell llvm-config --version | grep '^11\.'),) - LLVM_CONFIG=llvm-config - else - $(error "Requirement: llvm-config must be version 11") - endif - endif - - CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags) - LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs) -endif - -all: debug demo +all: debug demo: - ./odin run examples/demo/demo.odin + ./odin run examples/demo/demo.odin -file report: ./odin report debug: - $(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -g $(LDFLAGS) -o odin + ./build_odin.sh debug release: - $(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -O3 $(LDFLAGS) -o odin + ./build_odin.sh release release_native: - $(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -O3 -march=native $(LDFLAGS) -o odin + ./build_odin.sh release-native nightly: - $(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -DNIGHTLY -O3 $(LDFLAGS) -o odin + ./build_odin.sh nightly diff --git a/README.md b/README.md index 5b8c25492..9e46f80d0 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@
- + @@ -58,6 +58,10 @@ main :: proc() { Instructions for downloading and installing the Odin compiler and libraries. +#### [Nightly Builds](https://odin-lang.org/docs/nightly/) + +Get the latest nightly builds of Odin. + ### Learning Odin #### [Overview of Odin](https://odin-lang.org/docs/overview) @@ -68,6 +72,10 @@ An overview of the Odin programming language. Answers to common questions about Odin. +#### [Packages](https://pkg.odin-lang.org/) + +Documentation for all the official packages part of the [core](https://pkg.odin-lang.org/core/) and [vendor](https://pkg.odin-lang.org/vendor/) library collections. + #### [The Odin Wiki](https://github.com/odin-lang/Odin/wiki) A wiki maintained by the Odin community. @@ -76,12 +84,6 @@ A wiki maintained by the Odin community. Get live support and talk with other odiners on the Odin Discord. -### References - -#### [Language Specification](https://odin-lang.org/docs/spec/) - -The official Odin Language specification. - ### Articles #### [The Odin Blog](https://odin-lang.org/news/) diff --git a/build.bat b/build.bat index f663c185c..06bc6c823 100644 --- a/build.bat +++ b/build.bat @@ -58,7 +58,7 @@ set libs= ^ set linker_flags= -incremental:no -opt:ref -subsystem:console if %release_mode% EQU 0 ( rem Debug - set linker_flags=%linker_flags% -debug + set linker_flags=%linker_flags% -debug /NATVIS:src\odin_compiler.natvis ) else ( rem Release set linker_flags=%linker_flags% -debug ) diff --git a/build_odin.sh b/build_odin.sh new file mode 100755 index 000000000..b00d33323 --- /dev/null +++ b/build_odin.sh @@ -0,0 +1,150 @@ +#!/usr/bin/env bash +set -eu + +GIT_SHA=$(git rev-parse --short HEAD) +DISABLED_WARNINGS="-Wno-switch -Wno-macro-redefined -Wno-unused-value" +LDFLAGS="-pthread -lm -lstdc++" +CFLAGS="-std=c++14 -DGIT_SHA=\"$GIT_SHA\"" +CFLAGS="$CFLAGS -DODIN_VERSION_RAW=\"dev-$(date +"%Y-%m")\"" +CC=clang +OS=$(uname) + +panic() { + printf "%s\n" "$1" + exit 1 +} + +version() { echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }'; } + +config_darwin() { + ARCH=$(uname -m) + LLVM_CONFIG=llvm-config + + # allow for arm only llvm's with version 13 + if [ ARCH == arm64 ]; then + MIN_LLVM_VERSION=("13.0.0") + else + # allow for x86 / amd64 all llvm versions begining from 11 + MIN_LLVM_VERSION=("11.1.0") + fi + + if [ $(version $($LLVM_CONFIG --version)) -lt $(version $MIN_LLVM_VERSION) ]; then + if [ ARCH == arm64 ]; then + panic "Requirement: llvm-config must be base version 13 for arm64" + else + panic "Requirement: llvm-config must be base version greater than 11 for amd64/x86" + fi + fi + + LDFLAGS="$LDFLAGS -liconv -ldl" + CFLAGS="$CFLAGS $($LLVM_CONFIG --cxxflags --ldflags)" + LDFLAGS="$LDFLAGS -lLLVM-C" +} + +config_freebsd() { + LLVM_CONFIG=/usr/local/bin/llvm-config11 + + CFLAGS="$CFLAGS $($LLVM_CONFIG --cxxflags --ldflags)" + LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)" +} + +config_openbsd() { + LLVM_CONFIG=/usr/local/bin/llvm-config + + LDFLAGS="$LDFLAGS -liconv" + CFLAGS="$CFLAGS $($LLVM_CONFIG --cxxflags --ldflags)" + LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)" +} + +config_linux() { + if which llvm-config > /dev/null 2>&1; then + LLVM_CONFIG=llvm-config + elif which llvm-config-11 > /dev/null 2>&1; then + LLVM_CONFIG=llvm-config-11 + elif which llvm-config-11-64 > /dev/null 2>&1; then + LLVM_CONFIG=llvm-config-11-64 + else + panic "Unable to find LLVM-config" + fi + + MIN_LLVM_VERSION=("11.0.0") + if [ $(version $($LLVM_CONFIG --version)) -lt $(version $MIN_LLVM_VERSION) ]; then + echo "Tried to use " $(which $LLVM_CONFIG) "version" $($LLVM_CONFIG --version) + panic "Requirement: llvm-config must be base version greater than 11" + fi + + LDFLAGS="$LDFLAGS -ldl" + CFLAGS="$CFLAGS $($LLVM_CONFIG --cxxflags --ldflags)" + LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)" +} + +build_odin() { + case $1 in + debug) + EXTRAFLAGS="-g" + ;; + release) + EXTRAFLAGS="-O3" + ;; + release-native) + EXTRAFLAGS="-O3 -march=native" + ;; + nightly) + EXTRAFLAGS="-DNIGHTLY -O3" + ;; + *) + panic "Build mode unsupported!" + esac + + set -x + $CC src/main.cpp src/libtommath.cpp $DISABLED_WARNINGS $CFLAGS $EXTRAFLAGS $LDFLAGS -o odin + set +x +} + +run_demo() { + ./odin run examples/demo/demo.odin -file +} + +case $OS in +Linux) + config_linux + ;; +Darwin) + config_darwin + ;; +OpenBSD) + config_openbsd + ;; +FreeBSD) + config_freebsd + ;; +*) + panic "Platform unsupported!" +esac + +if [[ $# -eq 0 ]]; then + build_odin debug + run_demo + exit 0 +fi + +if [[ $# -eq 1 ]]; then + case $1 in + report) + if [[ ! -f "./odin" ]]; then + build_odin debug + fi + + ./odin report + exit 0 + ;; + *) + build_odin $1 + ;; + esac + + run_demo + exit 0 +else + panic "Too many arguments!" +fi diff --git a/core/bufio/scanner.odin b/core/bufio/scanner.odin index cc3b4533a..86e6d8eb0 100644 --- a/core/bufio/scanner.odin +++ b/core/bufio/scanner.odin @@ -8,6 +8,7 @@ import "core:intrinsics" // Extra errors returns by scanning procedures Scanner_Extra_Error :: enum i32 { + None, Negative_Advance, Advanced_Too_Far, Bad_Read_Count, @@ -15,7 +16,7 @@ Scanner_Extra_Error :: enum i32 { Too_Short, } -Scanner_Error :: union { +Scanner_Error :: union #shared_nil { io.Error, Scanner_Extra_Error, } @@ -68,7 +69,7 @@ scanner_destroy :: proc(s: ^Scanner) { // Returns the first non-EOF error that was encounted by the scanner scanner_error :: proc(s: ^Scanner) -> Scanner_Error { switch s._err { - case .EOF, .None: + case .EOF, nil: return nil } return s._err @@ -93,10 +94,6 @@ scanner_text :: proc(s: ^Scanner) -> string { // scanner_scan advances the scanner scanner_scan :: proc(s: ^Scanner) -> bool { set_err :: proc(s: ^Scanner, err: Scanner_Error) { - err := err - if err == .None { - err = nil - } switch s._err { case nil, .EOF: s._err = err diff --git a/core/builtin/builtin.odin b/core/builtin/builtin.odin index 74283720f..d1cdfa6e7 100644 --- a/core/builtin/builtin.odin +++ b/core/builtin/builtin.odin @@ -1,90 +1,90 @@ // This is purely for documentation package builtin -nil :: nil; -false :: 0!==0; -true :: 0==0; +nil :: nil +false :: 0!=0 +true :: 0==0 -ODIN_OS :: ODIN_OS; -ODIN_ARCH :: ODIN_ARCH; -ODIN_ENDIAN :: ODIN_ENDIAN; -ODIN_VENDOR :: ODIN_VENDOR; -ODIN_VERSION :: ODIN_VERSION; -ODIN_ROOT :: ODIN_ROOT; -ODIN_DEBUG :: ODIN_DEBUG; +ODIN_OS :: ODIN_OS +ODIN_ARCH :: ODIN_ARCH +ODIN_ENDIAN :: ODIN_ENDIAN +ODIN_VENDOR :: ODIN_VENDOR +ODIN_VERSION :: ODIN_VERSION +ODIN_ROOT :: ODIN_ROOT +ODIN_DEBUG :: ODIN_DEBUG -byte :: u8; // alias +byte :: u8 // alias -bool :: bool; -b8 :: b8; -b16 :: b16; -b32 :: b32; -b64 :: b64; +bool :: bool +b8 :: b8 +b16 :: b16 +b32 :: b32 +b64 :: b64 -i8 :: i8; -u8 :: u8; -i16 :: i16; -u16 :: u16; -i32 :: i32; -u32 :: u32; -i64 :: i64; -u64 :: u64; +i8 :: i8 +u8 :: u8 +i16 :: i16 +u16 :: u16 +i32 :: i32 +u32 :: u32 +i64 :: i64 +u64 :: u64 -i128 :: i128; -u128 :: u128; +i128 :: i128 +u128 :: u128 -rune :: rune; +rune :: rune -f16 :: f16; -f32 :: f32; -f64 :: f64; +f16 :: f16 +f32 :: f32 +f64 :: f64 -complex32 :: complex32; -complex64 :: complex64; -complex128 :: complex128; +complex32 :: complex32 +complex64 :: complex64 +complex128 :: complex128 -quaternion64 :: quaternion64; -quaternion128 :: quaternion128; -quaternion256 :: quaternion256; +quaternion64 :: quaternion64 +quaternion128 :: quaternion128 +quaternion256 :: quaternion256 -int :: int; -uint :: uint; -uintptr :: uintptr; +int :: int +uint :: uint +uintptr :: uintptr -rawptr :: rawptr; -string :: string; -cstring :: cstring; -any :: any; +rawptr :: rawptr +string :: string +cstring :: cstring +any :: any -typeid :: typeid; +typeid :: typeid // Endian Specific Types -i16le :: i16le; -u16le :: u16le; -i32le :: i32le; -u32le :: u32le; -i64le :: i64le; -u64le :: u64le; -i128le :: i128le; -u128le :: u128le; +i16le :: i16le +u16le :: u16le +i32le :: i32le +u32le :: u32le +i64le :: i64le +u64le :: u64le +i128le :: i128le +u128le :: u128le -i16be :: i16be; -u16be :: u16be; -i32be :: i32be; -u32be :: u32be; -i64be :: i64be; -u64be :: u64be; -i128be :: i128be; -u128be :: u128be; +i16be :: i16be +u16be :: u16be +i32be :: i32be +u32be :: u32be +i64be :: i64be +u64be :: u64be +i128be :: i128be +u128be :: u128be -f16le :: f16le; -f32le :: f32le; -f64le :: f64le; +f16le :: f16le +f32le :: f32le +f64le :: f64le -f16be :: f16be; -f32be :: f32be; -f64be :: f64be; +f16be :: f16be +f32be :: f32be +f64be :: f64be diff --git a/core/bytes/buffer.odin b/core/bytes/buffer.odin index 7d8ae596d..96f057a9b 100644 --- a/core/bytes/buffer.odin +++ b/core/bytes/buffer.odin @@ -161,6 +161,10 @@ buffer_write :: proc(b: ^Buffer, p: []byte) -> (n: int, err: io.Error) { return copy(b.buf[m:], p), nil } +buffer_write_ptr :: proc(b: ^Buffer, ptr: rawptr, size: int) -> (n: int, err: io.Error) { + return buffer_write(b, ([^]byte)(ptr)[:size]) +} + buffer_write_string :: proc(b: ^Buffer, s: string) -> (n: int, err: io.Error) { b.last_read = .Invalid m, ok := _buffer_try_grow(b, len(s)) @@ -229,6 +233,10 @@ buffer_read :: proc(b: ^Buffer, p: []byte) -> (n: int, err: io.Error) { return } +buffer_read_ptr :: proc(b: ^Buffer, ptr: rawptr, size: int) -> (n: int, err: io.Error) { + return buffer_read(b, ([^]byte)(ptr)[:size]) +} + buffer_read_at :: proc(b: ^Buffer, p: []byte, offset: int) -> (n: int, err: io.Error) { b.last_read = .Invalid diff --git a/core/bytes/bytes.odin b/core/bytes/bytes.odin index 1bf11e0b0..f1737f3c5 100644 --- a/core/bytes/bytes.odin +++ b/core/bytes/bytes.odin @@ -10,7 +10,14 @@ clone :: proc(s: []byte, allocator := context.allocator, loc := #caller_location return c[:len(s)] } -ptr_from_slice :: proc(str: []byte) -> ^byte { +clone_safe :: proc(s: []byte, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: mem.Allocator_Error) { + c := make([]byte, len(s), allocator, loc) or_return + copy(c, s) + return c[:len(s)], nil +} + +ptr_from_slice :: ptr_from_bytes +ptr_from_bytes :: proc(str: []byte) -> ^byte { d := transmute(mem.Raw_String)str return d.data } @@ -134,6 +141,25 @@ join :: proc(a: [][]byte, sep: []byte, allocator := context.allocator) -> []byte return b } +join_safe :: proc(a: [][]byte, sep: []byte, allocator := context.allocator) -> (data: []byte, err: mem.Allocator_Error) { + if len(a) == 0 { + return nil, nil + } + + n := len(sep) * (len(a) - 1) + for s in a { + n += len(s) + } + + b := make([]byte, n, allocator) or_return + i := copy(b, a[0]) + for s in a[1:] { + i += copy(b[i:], sep) + i += copy(b[i:], s) + } + return b, nil +} + concatenate :: proc(a: [][]byte, allocator := context.allocator) -> []byte { if len(a) == 0 { return nil @@ -151,6 +177,24 @@ concatenate :: proc(a: [][]byte, allocator := context.allocator) -> []byte { return b } +concatenate_safe :: proc(a: [][]byte, allocator := context.allocator) -> (data: []byte, err: mem.Allocator_Error) { + if len(a) == 0 { + return nil, nil + } + + n := 0 + for s in a { + n += len(s) + } + b := make([]byte, n, allocator) or_return + i := 0 + for s in a { + i += copy(b[i:], s) + } + return b, nil +} + + @private _split :: proc(s, sep: []byte, sep_save, n: int, allocator := context.allocator) -> [][]byte { s, n := s, n @@ -218,61 +262,37 @@ split_after_n :: proc(s, sep: []byte, n: int, allocator := context.allocator) -> @private -_split_iterator :: proc(s: ^[]byte, sep: []byte, sep_save, n: int) -> (res: []byte, ok: bool) { - s, n := s, n - - if n == 0 { - return - } - - if sep == nil { +_split_iterator :: proc(s: ^[]byte, sep: []byte, sep_save: int) -> (res: []byte, ok: bool) { + if len(sep) == 0 { res = s[:] ok = true s^ = s[len(s):] return } - if n < 0 { - n = count(s^, sep) + 1 - } - - n -= 1 - - i := 0 - for ; i < n; i += 1 { - m := index(s^, sep) - if m < 0 { - break - } + m := index(s^, sep) + if m < 0 { + // not found + res = s[:] + ok = len(res) != 0 + s^ = s[len(s):] + } else { res = s[:m+sep_save] ok = true s^ = s[m+len(sep):] - return } - res = s[:] - ok = res != nil - s^ = s[len(s):] return } split_iterator :: proc(s: ^[]byte, sep: []byte) -> ([]byte, bool) { - return _split_iterator(s, sep, 0, -1) -} - -split_n_iterator :: proc(s: ^[]byte, sep: []byte, n: int) -> ([]byte, bool) { - return _split_iterator(s, sep, 0, n) + return _split_iterator(s, sep, 0) } split_after_iterator :: proc(s: ^[]byte, sep: []byte) -> ([]byte, bool) { - return _split_iterator(s, sep, len(sep), -1) + return _split_iterator(s, sep, len(sep)) } -split_after_n_iterator :: proc(s: ^[]byte, sep: []byte, n: int) -> ([]byte, bool) { - return _split_iterator(s, sep, len(sep), n) -} - - index_byte :: proc(s: []byte, c: byte) -> int { for i := 0; i < len(s); i += 1 { diff --git a/core/c/c.odin b/core/c/c.odin index d0b8e377f..05732476f 100644 --- a/core/c/c.odin +++ b/core/c/c.odin @@ -7,20 +7,20 @@ char :: builtin.u8 // assuming -funsigned-char schar :: builtin.i8 short :: builtin.i16 int :: builtin.i32 -long :: builtin.i32 when (ODIN_OS == "windows" || size_of(builtin.rawptr) == 4) else builtin.i64 +long :: builtin.i32 when (ODIN_OS == .Windows || size_of(builtin.rawptr) == 4) else builtin.i64 longlong :: builtin.i64 uchar :: builtin.u8 ushort :: builtin.u16 uint :: builtin.u32 -ulong :: builtin.u32 when (ODIN_OS == "windows" || size_of(builtin.rawptr) == 4) else builtin.u64 +ulong :: builtin.u32 when (ODIN_OS == .Windows || size_of(builtin.rawptr) == 4) else builtin.u64 ulonglong :: builtin.u64 bool :: builtin.bool size_t :: builtin.uint ssize_t :: builtin.int -wchar_t :: builtin.u16 when (ODIN_OS == "windows") else builtin.u32 +wchar_t :: builtin.u16 when (ODIN_OS == .Windows) else builtin.u32 float :: builtin.f32 double :: builtin.f64 @@ -48,7 +48,7 @@ int_least64_t :: builtin.i64 uint_least64_t :: builtin.u64 // Same on Windows, Linux, and FreeBSD -when ODIN_ARCH == "i386" || ODIN_ARCH == "amd64" { +when ODIN_ARCH == .i386 || ODIN_ARCH == .amd64 { int_fast8_t :: builtin.i8 uint_fast8_t :: builtin.u8 int_fast16_t :: builtin.i32 diff --git a/core/c/frontend/preprocessor/preprocess.odin b/core/c/frontend/preprocessor/preprocess.odin index 9651cc81c..4cfad2c1c 100644 --- a/core/c/frontend/preprocessor/preprocess.odin +++ b/core/c/frontend/preprocessor/preprocess.odin @@ -519,7 +519,7 @@ join_adjacent_string_literals :: proc(cpp: ^Preprocessor, initial_tok: ^Token) { quote_string :: proc(s: string) -> []byte { - b := strings.make_builder(0, len(s)+2) + b := strings.builder_make(0, len(s)+2) io.write_quoted_string(strings.to_writer(&b), s, '"') return b.buf[:] } @@ -1276,7 +1276,7 @@ preprocess_internal :: proc(cpp: ^Preprocessor, tok: ^Token) -> ^Token { if start.file != nil { dir = filepath.dir(start.file.name) } - path := filepath.join(dir, filename) + path := filepath.join({dir, filename}) if os.exists(path) { tok = include_file(cpp, tok, path, start.next.next) continue diff --git a/core/c/libc/complex.odin b/core/c/libc/complex.odin index 62b28f0cd..22c422cce 100644 --- a/core/c/libc/complex.odin +++ b/core/c/libc/complex.odin @@ -2,9 +2,9 @@ package libc // 7.3 Complex arithmetic -when ODIN_OS == "windows" { +when ODIN_OS == .Windows { foreign import libc "system:libucrt.lib" -} else when ODIN_OS == "darwin" { +} else when ODIN_OS == .Darwin { foreign import libc "system:System.framework" } else { foreign import libc "system:c" diff --git a/core/c/libc/ctype.odin b/core/c/libc/ctype.odin index 05d9dcd37..185385a5e 100644 --- a/core/c/libc/ctype.odin +++ b/core/c/libc/ctype.odin @@ -1,8 +1,8 @@ package libc -when ODIN_OS == "windows" { +when ODIN_OS == .Windows { foreign import libc "system:libucrt.lib" -} else when ODIN_OS == "darwin" { +} else when ODIN_OS == .Darwin { foreign import libc "system:System.framework" } else { foreign import libc "system:c" diff --git a/core/c/libc/errno.odin b/core/c/libc/errno.odin index 8ebe2f734..53437f42f 100644 --- a/core/c/libc/errno.odin +++ b/core/c/libc/errno.odin @@ -2,9 +2,9 @@ package libc // 7.5 Errors -when ODIN_OS == "windows" { +when ODIN_OS == .Windows { foreign import libc "system:libucrt.lib" -} else when ODIN_OS == "darwin" { +} else when ODIN_OS == .Darwin { foreign import libc "system:System.framework" } else { foreign import libc "system:c" @@ -14,7 +14,7 @@ when ODIN_OS == "windows" { // EDOM, // EILSEQ // ERANGE -when ODIN_OS == "linux" || ODIN_OS == "freebsd" { +when ODIN_OS == .Linux || ODIN_OS == .FreeBSD { @(private="file") @(default_calling_convention="c") foreign libc { @@ -27,7 +27,20 @@ when ODIN_OS == "linux" || ODIN_OS == "freebsd" { ERANGE :: 34 } -when ODIN_OS == "windows" { +when ODIN_OS == .OpenBSD { + @(private="file") + @(default_calling_convention="c") + foreign libc { + @(link_name="__errno") + _get_errno :: proc() -> ^int --- + } + + EDOM :: 33 + EILSEQ :: 84 + ERANGE :: 34 +} + +when ODIN_OS == .Windows { @(private="file") @(default_calling_convention="c") foreign libc { @@ -40,7 +53,7 @@ when ODIN_OS == "windows" { ERANGE :: 34 } -when ODIN_OS == "darwin" { +when ODIN_OS == .Darwin { @(private="file") @(default_calling_convention="c") foreign libc { diff --git a/core/c/libc/math.odin b/core/c/libc/math.odin index ee702b82e..6a7b81850 100644 --- a/core/c/libc/math.odin +++ b/core/c/libc/math.odin @@ -4,9 +4,9 @@ package libc import "core:intrinsics" -when ODIN_OS == "windows" { +when ODIN_OS == .Windows { foreign import libc "system:libucrt.lib" -} else when ODIN_OS == "darwin" { +} else when ODIN_OS == .Darwin { foreign import libc "system:System.framework" } else { foreign import libc "system:c" @@ -211,19 +211,19 @@ _signbitf :: #force_inline proc(x: float) -> int { return int(transmute(uint32_t)x >> 31) } -isfinite :: #force_inline proc(x: $T) where intrinsics.type_is_float(T) { +isfinite :: #force_inline proc(x: $T) -> bool where intrinsics.type_is_float(T) { return fpclassify(x) == FP_INFINITE } -isinf :: #force_inline proc(x: $T) where intrinsics.type_is_float(T) { +isinf :: #force_inline proc(x: $T) -> bool where intrinsics.type_is_float(T) { return fpclassify(x) > FP_INFINITE } -isnan :: #force_inline proc(x: $T) where intrinsics.type_is_float(T) { +isnan :: #force_inline proc(x: $T) -> bool where intrinsics.type_is_float(T) { return fpclassify(x) == FP_NAN } -isnormal :: #force_inline proc(x: $T) where intrinsics.type_is_float(T) { +isnormal :: #force_inline proc(x: $T) -> bool where intrinsics.type_is_float(T) { return fpclassify(x) == FP_NORMAL } @@ -231,27 +231,27 @@ isnormal :: #force_inline proc(x: $T) where intrinsics.type_is_float(T) { // implemented as the relational comparisons, as that would produce an invalid // "sticky" state that propagates and affects maths results. These need // to be implemented natively in Odin assuming isunordered to prevent that. -isgreater :: #force_inline proc(x, y: $T) where intrinsics.type_is_float(T) { +isgreater :: #force_inline proc(x, y: $T) -> bool where intrinsics.type_is_float(T) { return !isunordered(x, y) && x > y } -isgreaterequal :: #force_inline proc(x, y: $T) where intrinsics.type_is_float(T) { +isgreaterequal :: #force_inline proc(x, y: $T) -> bool where intrinsics.type_is_float(T) { return !isunordered(x, y) && x >= y } -isless :: #force_inline proc(x, y: $T) where intrinsics.type_is_float(T) { +isless :: #force_inline proc(x, y: $T) -> bool where intrinsics.type_is_float(T) { return !isunordered(x, y) && x < y } -islessequal :: #force_inline proc(x, y: $T) where intrinsics.type_is_float(T) { +islessequal :: #force_inline proc(x, y: $T) -> bool where intrinsics.type_is_float(T) { return !isunordered(x, y) && x <= y } -islessgreater :: #force_inline proc(x, y: $T) where intrinsics.type_is_float(T) { +islessgreater :: #force_inline proc(x, y: $T) -> bool where intrinsics.type_is_float(T) { return !isunordered(x, y) && x <= y } -isunordered :: #force_inline proc(x, y: $T) where intrinsics.type_is_float(T) { +isunordered :: #force_inline proc(x, y: $T) -> bool where intrinsics.type_is_float(T) { if isnan(x) { // Force evaluation of y to propagate exceptions for ordering semantics. // To ensure correct semantics of IEEE 754 this cannot be compiled away. diff --git a/core/c/libc/setjmp.odin b/core/c/libc/setjmp.odin index dcd4a9c64..c2cd1d047 100644 --- a/core/c/libc/setjmp.odin +++ b/core/c/libc/setjmp.odin @@ -2,14 +2,14 @@ package libc // 7.13 Nonlocal jumps -when ODIN_OS == "windows" { +when ODIN_OS == .Windows { foreign import libc "system:libucrt.lib" -} else when ODIN_OS == "darwin" { +} else when ODIN_OS == .Darwin { foreign import libc "system:System.framework" } else { foreign import libc "system:c" } -when ODIN_OS == "windows" { +when ODIN_OS == .Windows { @(default_calling_convention="c") foreign libc { // 7.13.1 Save calling environment diff --git a/core/c/libc/signal.odin b/core/c/libc/signal.odin index e1044dc02..186b74d8c 100644 --- a/core/c/libc/signal.odin +++ b/core/c/libc/signal.odin @@ -2,9 +2,9 @@ package libc // 7.14 Signal handling -when ODIN_OS == "windows" { +when ODIN_OS == .Windows { foreign import libc "system:libucrt.lib" -} else when ODIN_OS == "darwin" { +} else when ODIN_OS == .Darwin { foreign import libc "system:System.framework" } else { foreign import libc "system:c" @@ -21,7 +21,7 @@ foreign libc { raise :: proc(sig: int) -> int --- } -when ODIN_OS == "windows" { +when ODIN_OS == .Windows { SIG_ERR :: rawptr(~uintptr(0)) SIG_DFL :: rawptr(uintptr(0)) SIG_IGN :: rawptr(uintptr(1)) @@ -34,7 +34,7 @@ when ODIN_OS == "windows" { SIGTERM :: 15 } -when ODIN_OS == "linux" || ODIN_OS == "freebsd" { +when ODIN_OS == .Linux || ODIN_OS == .FreeBSD { SIG_ERR :: rawptr(~uintptr(0)) SIG_DFL :: rawptr(uintptr(0)) SIG_IGN :: rawptr(uintptr(1)) @@ -47,7 +47,7 @@ when ODIN_OS == "linux" || ODIN_OS == "freebsd" { SIGTERM :: 15 } -when ODIN_OS == "darwin" { +when ODIN_OS == .Darwin { SIG_ERR :: rawptr(~uintptr(0)) SIG_DFL :: rawptr(uintptr(0)) SIG_IGN :: rawptr(uintptr(1)) diff --git a/core/c/libc/stdatomic.odin b/core/c/libc/stdatomic.odin index fbe1ef7ea..6e1581c58 100644 --- a/core/c/libc/stdatomic.odin +++ b/core/c/libc/stdatomic.odin @@ -47,29 +47,30 @@ kill_dependency :: #force_inline proc(value: $T) -> T { // 7.17.4 Fences atomic_thread_fence :: #force_inline proc(order: memory_order) { - switch (order) { - case .relaxed: - return - case .consume: - intrinsics.atomic_fence_acq() - case .acquire: - intrinsics.atomic_fence_acq() - case .release: - intrinsics.atomic_fence_rel() - case .acq_rel: - intrinsics.atomic_fence_acqrel() - case .seq_cst: - intrinsics.atomic_fence_acqrel() + assert(order != .relaxed) + assert(order != .consume) + #partial switch order { + case .acquire: intrinsics.atomic_thread_fence(.Acquire) + case .release: intrinsics.atomic_thread_fence(.Release) + case .acq_rel: intrinsics.atomic_thread_fence(.Acq_Rel) + case .seq_cst: intrinsics.atomic_thread_fence(.Seq_Cst) } } atomic_signal_fence :: #force_inline proc(order: memory_order) { - atomic_thread_fence(order) + assert(order != .relaxed) + assert(order != .consume) + #partial switch order { + case .acquire: intrinsics.atomic_signal_fence(.Acquire) + case .release: intrinsics.atomic_signal_fence(.Release) + case .acq_rel: intrinsics.atomic_signal_fence(.Acq_Rel) + case .seq_cst: intrinsics.atomic_signal_fence(.Seq_Cst) + } } // 7.17.5 Lock-free property atomic_is_lock_free :: #force_inline proc(obj: ^$T) -> bool { - return size_of(T) <= 8 && (intrinsics.type_is_integer(T) || intrinsics.type_is_pointer(T)) + return intrinsics.atomic_type_is_lock_free(T) } // 7.17.6 Atomic integer types @@ -121,13 +122,10 @@ atomic_store_explicit :: #force_inline proc(object: ^$T, desired: T, order: memo assert(order != .acquire) assert(order != .acq_rel) - #partial switch (order) { - case .relaxed: - intrinsics.atomic_store_relaxed(object, desired) - case .release: - intrinsics.atomic_store_rel(object, desired) - case .seq_cst: - intrinsics.atomic_store(object, desired) + #partial switch order { + case .relaxed: intrinsics.atomic_store_explicit(object, desired, .Relaxed) + case .release: intrinsics.atomic_store_explicit(object, desired, .Release) + case .seq_cst: intrinsics.atomic_store_explicit(object, desired, .Seq_Cst) } } @@ -139,36 +137,26 @@ atomic_load_explicit :: #force_inline proc(object: ^$T, order: memory_order) { assert(order != .release) assert(order != .acq_rel) - #partial switch (order) { - case .relaxed: - return intrinsics.atomic_load_relaxed(object) - case .consume: - return intrinsics.atomic_load_acq(object) - case .acquire: - return intrinsics.atomic_load_acq(object) - case .seq_cst: - return intrinsics.atomic_load(object) + #partial switch order { + case .relaxed: return intrinsics.atomic_load_explicit(object, .Relaxed) + case .consume: return intrinsics.atomic_load_explicit(object, .Consume) + case .acquire: return intrinsics.atomic_load_explicit(object, .Acquire) + case .seq_cst: return intrinsics.atomic_load_explicit(object, .Seq_Cst) } } atomic_exchange :: #force_inline proc(object: ^$T, desired: T) -> T { - return intrinsics.atomic_xchg(object, desired) + return intrinsics.atomic_exchange(object, desired) } atomic_exchange_explicit :: #force_inline proc(object: ^$T, desired: T, order: memory_order) -> T { - switch (order) { - case .relaxed: - return intrinsics.atomic_xchg_relaxed(object, desired) - case .consume: - return intrinsics.atomic_xchg_acq(object, desired) - case .acquire: - return intrinsics.atomic_xchg_acq(object, desired) - case .release: - return intrinsics.atomic_xchg_rel(object, desired) - case .acq_rel: - return intrinsics.atomic_xchg_acqrel(object, desired) - case .seq_cst: - return intrinsics.atomic_xchg(object, desired) + switch order { + case .relaxed: return intrinsics.atomic_exchange_explicit(object, desired, .Relaxed) + case .consume: return intrinsics.atomic_exchange_explicit(object, desired, .Consume) + case .acquire: return intrinsics.atomic_exchange_explicit(object, desired, .Acquire) + case .release: return intrinsics.atomic_exchange_explicit(object, desired, .Release) + case .acq_rel: return intrinsics.atomic_exchange_explicit(object, desired, .Acq_Rel) + case .seq_cst: return intrinsics.atomic_exchange_explicit(object, desired, .Seq_Cst) } return false } @@ -189,102 +177,104 @@ atomic_exchange_explicit :: #force_inline proc(object: ^$T, desired: T, order: m // [success = seq_cst, failure = acquire] => failacq // [success = acquire, failure = relaxed] => acq_failrelaxed // [success = acq_rel, failure = relaxed] => acqrel_failrelaxed -atomic_compare_exchange_strong :: #force_inline proc(object, expected: ^$T, desired: T) { - value, ok := intrinsics.atomic_cxchg(object, expected^, desired) +atomic_compare_exchange_strong :: #force_inline proc(object, expected: ^$T, desired: T) -> bool { + value, ok := intrinsics.atomic_compare_exchange_strong(object, expected^, desired) if !ok { expected^ = value } return ok } -atomic_compare_exchange_strong_explicit :: #force_inline proc(object, expected: ^$T, desired: T, success, failure: memory_order) { +atomic_compare_exchange_strong_explicit :: #force_inline proc(object, expected: ^$T, desired: T, success, failure: memory_order) -> bool { assert(failure != .release) assert(failure != .acq_rel) value: T; ok: bool - #partial switch (failure) { + #partial switch failure { case .seq_cst: assert(success != .relaxed) - #partial switch (success) { + #partial switch success { case .seq_cst: - value, ok := intrinsics.atomic_cxchg(object, expected^, desired) + value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Seq_Cst, .Seq_Cst) case .acquire: - value, ok := intrinsics.atomic_cxchg_acq(object, expected^, desired) + value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Acquire, .Seq_Cst) case .consume: - value, ok := intrinsics.atomic_cxchg_acq(object, expected^, desired) + value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Consume, .Seq_Cst) case .release: - value, ok := intrinsics.atomic_cxchg_rel(object, expected^, desired) + value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Release, .Seq_Cst) case .acq_rel: - value, ok := intrinsics.atomic_cxchg_acqrel(object, expected^, desired) + value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Acq_Rel, .Seq_Cst) } case .relaxed: assert(success != .release) - #partial switch (success) { + #partial switch success { case .relaxed: - value, ok := intrinsics.atomic_cxchg_relaxed(object, expected^, desired) + value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Relaxed, .Relaxed) case .seq_cst: - value, ok := intrinsics.atomic_cxchg_failrelaxed(object, expected^, desired) + value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Seq_Cst, .Relaxed) case .acquire: - value, ok := intrinsics.atomic_cxchg_acq_failrelaxed(object, expected^, desired) + value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Acquire, .Relaxed) case .consume: - value, ok := intrinsics.atomic_cxchg_acq_failrelaxed(object, expected^, desired) + value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Consume, .Relaxed) case .acq_rel: - value, ok := intrinsics.atomic_cxchg_acqrel_failrelaxed(object, expected^, desired) + value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Acq_Rel, .Relaxed) } case .consume: - fallthrough + assert(success == .seq_cst) + value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Seq_Cst, .Consume) case .acquire: assert(success == .seq_cst) - value, ok := intrinsics.atomic_cxchg_failacq(object, expected^, desired) + value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Seq_Cst, .Acquire) } if !ok { expected^ = value } return ok } -atomic_compare_exchange_weak :: #force_inline proc(object, expected: ^$T, desired: T) { - value, ok := intrinsics.atomic_cxchgweak(object, expected^, desired) +atomic_compare_exchange_weak :: #force_inline proc(object, expected: ^$T, desired: T) -> bool { + value, ok := intrinsics.atomic_compare_exchange_weak(object, expected^, desired) if !ok { expected^ = value } return ok } -atomic_compare_exchange_weak_explicit :: #force_inline proc(object, expected: ^$T, desited: T, success, failure: memory_order) { +atomic_compare_exchange_weak_explicit :: #force_inline proc(object, expected: ^$T, desited: T, success, failure: memory_order) -> bool { assert(failure != .release) assert(failure != .acq_rel) value: T; ok: bool - #partial switch (failure) { + #partial switch failure { case .seq_cst: assert(success != .relaxed) - #partial switch (success) { + #partial switch success { case .seq_cst: - value, ok := intrinsics.atomic_cxchgweak(object, expected^, desired) + value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Seq_Cst, .Seq_Cst) case .acquire: - value, ok := intrinsics.atomic_cxchgweak_acq(object, expected^, desired) + value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Acquire, .Seq_Cst) case .consume: - value, ok := intrinsics.atomic_cxchgweak_acq(object, expected^, desired) + value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Consume, .Seq_Cst) case .release: - value, ok := intrinsics.atomic_cxchgweak_rel(object, expected^, desired) + value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Release, .Seq_Cst) case .acq_rel: - value, ok := intrinsics.atomic_cxchgweak_acqrel(object, expected^, desired) + value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Acq_Rel, .Seq_Cst) } case .relaxed: assert(success != .release) - #partial switch (success) { + #partial switch success { case .relaxed: - value, ok := intrinsics.atomic_cxchgweak_relaxed(object, expected^, desired) + value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Relaxed, .Relaxed) case .seq_cst: - value, ok := intrinsics.atomic_cxchgweak_failrelaxed(object, expected^, desired) + value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Seq_Cst, .Relaxed) case .acquire: - value, ok := intrinsics.atomic_cxchgweak_acq_failrelaxed(object, expected^, desired) + value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Acquire, .Relaxed) case .consume: - value, ok := intrinsics.atomic_cxchgweak_acq_failrelaxed(object, expected^, desired) + value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Consume, .Relaxed) case .acq_rel: - value, ok := intrinsics.atomic_cxchgweak_acqrel_failrelaxed(object, expected^, desired) + value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Acq_Rel, .Relaxed) } case .consume: - fallthrough + assert(success == .seq_cst) + value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Seq_Cst, .Consume) case .acquire: assert(success == .seq_cst) - value, ok := intrinsics.atomic_cxchgweak_failacq(object, expected^, desired) + value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Seq_Cst, .Acquire) } if !ok { expected^ = value } @@ -297,19 +287,14 @@ atomic_fetch_add :: #force_inline proc(object: ^$T, operand: T) -> T { } atomic_fetch_add_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T { - switch (order) { - case .relaxed: - return intrinsics.atomic_add_relaxed(object, operand) - case .consume: - return intrinsics.atomic_add_acq(object, operand) - case .acquire: - return intrinsics.atomic_add_acq(object, operand) - case .release: - return intrinsics.atomic_add_rel(object, operand) - case .acq_rel: - return intrinsics.atomic_add_acqrel(object, operand) - case .seq_cst: - return intrinsics.atomic_add(object, operand) + switch order { + case .relaxed: return intrinsics.atomic_add_explicit(object, operand, .Relaxed) + case .consume: return intrinsics.atomic_add_explicit(object, operand, .Consume) + case .acquire: return intrinsics.atomic_add_explicit(object, operand, .Acquire) + case .release: return intrinsics.atomic_add_explicit(object, operand, .Release) + case .acq_rel: return intrinsics.atomic_add_explicit(object, operand, .Acq_Rel) + case: fallthrough + case .seq_cst: return intrinsics.atomic_add_explicit(object, operand, .Seq_Cst) } } @@ -318,19 +303,14 @@ atomic_fetch_sub :: #force_inline proc(object: ^$T, operand: T) -> T { } atomic_fetch_sub_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T { - switch (order) { - case .relaxed: - return intrinsics.atomic_sub_relaxed(object, operand) - case .consume: - return intrinsics.atomic_sub_acq(object, operand) - case .acquire: - return intrinsics.atomic_sub_acq(object, operand) - case .release: - return intrinsics.atomic_sub_rel(object, operand) - case .acq_rel: - return intrinsics.atomic_sub_acqrel(object, operand) - case .seq_cst: - return intrinsics.atomic_sub(object, operand) + switch order { + case .relaxed: return intrinsics.atomic_sub_explicit(object, operand, .Relaxed) + case .consume: return intrinsics.atomic_sub_explicit(object, operand, .Consume) + case .acquire: return intrinsics.atomic_sub_explicit(object, operand, .Acquire) + case .release: return intrinsics.atomic_sub_explicit(object, operand, .Release) + case .acq_rel: return intrinsics.atomic_sub_explicit(object, operand, .Acq_Rel) + case: fallthrough + case .seq_cst: return intrinsics.atomic_sub_explicit(object, operand, .Seq_Cst) } } @@ -339,19 +319,14 @@ atomic_fetch_or :: #force_inline proc(object: ^$T, operand: T) -> T { } atomic_fetch_or_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T { - switch (order) { - case .relaxed: - return intrinsics.atomic_or_relaxed(object, operand) - case .consume: - return intrinsics.atomic_or_acq(object, operand) - case .acquire: - return intrinsics.atomic_or_acq(object, operand) - case .release: - return intrinsics.atomic_or_rel(object, operand) - case .acq_rel: - return intrinsics.atomic_or_acqrel(object, operand) - case .seq_cst: - return intrinsics.atomic_or(object, operand) + switch order { + case .relaxed: return intrinsics.atomic_or_explicit(object, operand, .Relaxed) + case .consume: return intrinsics.atomic_or_explicit(object, operand, .Consume) + case .acquire: return intrinsics.atomic_or_explicit(object, operand, .Acquire) + case .release: return intrinsics.atomic_or_explicit(object, operand, .Release) + case .acq_rel: return intrinsics.atomic_or_explicit(object, operand, .Acq_Rel) + case: fallthrough + case .seq_cst: return intrinsics.atomic_or_explicit(object, operand, .Seq_Cst) } } @@ -360,19 +335,14 @@ atomic_fetch_xor :: #force_inline proc(object: ^$T, operand: T) -> T { } atomic_fetch_xor_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T { - switch (order) { - case .relaxed: - return intrinsics.atomic_xor_relaxed(object, operand) - case .consume: - return intrinsics.atomic_xor_acq(object, operand) - case .acquire: - return intrinsics.atomic_xor_acq(object, operand) - case .release: - return intrinsics.atomic_xor_rel(object, operand) - case .acq_rel: - return intrinsics.atomic_xor_acqrel(object, operand) - case .seq_cst: - return intrinsics.atomic_xor(object, operand) + switch order { + case .relaxed: return intrinsics.atomic_xor_explicit(object, operand, .Relaxed) + case .consume: return intrinsics.atomic_xor_explicit(object, operand, .Consume) + case .acquire: return intrinsics.atomic_xor_explicit(object, operand, .Acquire) + case .release: return intrinsics.atomic_xor_explicit(object, operand, .Release) + case .acq_rel: return intrinsics.atomic_xor_explicit(object, operand, .Acq_Rel) + case: fallthrough + case .seq_cst: return intrinsics.atomic_xor_explicit(object, operand, .Seq_Cst) } } @@ -380,19 +350,14 @@ atomic_fetch_and :: #force_inline proc(object: ^$T, operand: T) -> T { return intrinsics.atomic_and(object, operand) } atomic_fetch_and_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T { - switch (order) { - case .relaxed: - return intrinsics.atomic_and_relaxed(object, operand) - case .consume: - return intrinsics.atomic_and_acq(object, operand) - case .acquire: - return intrinsics.atomic_and_acq(object, operand) - case .release: - return intrinsics.atomic_and_rel(object, operand) - case .acq_rel: - return intrinsics.atomic_and_acqrel(object, operand) - case .seq_cst: - return intrinsics.atomic_and(object, operand) + switch order { + case .relaxed: return intrinsics.atomic_and_explicit(object, operand, .Relaxed) + case .consume: return intrinsics.atomic_and_explicit(object, operand, .Consume) + case .acquire: return intrinsics.atomic_and_explicit(object, operand, .Acquire) + case .release: return intrinsics.atomic_and_explicit(object, operand, .Release) + case .acq_rel: return intrinsics.atomic_and_explicit(object, operand, .Acq_Rel) + case: fallthrough + case .seq_cst: return intrinsics.atomic_and_explicit(object, operand, .Seq_Cst) } } diff --git a/core/c/libc/stdio.odin b/core/c/libc/stdio.odin index c5c936d16..9b1089d85 100644 --- a/core/c/libc/stdio.odin +++ b/core/c/libc/stdio.odin @@ -1,8 +1,8 @@ package libc -when ODIN_OS == "windows" { +when ODIN_OS == .Windows { foreign import libc "system:libucrt.lib" -} else when ODIN_OS == "darwin" { +} else when ODIN_OS == .Darwin { foreign import libc "system:System.framework" } else { foreign import libc "system:c" @@ -13,7 +13,7 @@ when ODIN_OS == "windows" { FILE :: struct {} // MSVCRT compatible. -when ODIN_OS == "windows" { +when ODIN_OS == .Windows { _IOFBF :: 0x0000 _IONBF :: 0x0004 _IOLBF :: 0x0040 @@ -48,7 +48,7 @@ when ODIN_OS == "windows" { } // GLIBC and MUSL compatible. -when ODIN_OS == "linux" { +when ODIN_OS == .Linux { fpos_t :: struct #raw_union { _: [16]char, _: longlong, _: double, } _IOFBF :: 0 @@ -78,7 +78,57 @@ when ODIN_OS == "linux" { } } -when ODIN_OS == "darwin" { +when ODIN_OS == .OpenBSD { + fpos_t :: distinct i64 + + _IOFBF :: 0 + _IOLBF :: 1 + _IONBF :: 1 + + BUFSIZ :: 1024 + + EOF :: int(-1) + + FOPEN_MAX :: 20 + FILENAME_MAX :: 1024 + + SEEK_SET :: 0 + SEEK_CUR :: 1 + SEEK_END :: 2 + + foreign libc { + stderr: ^FILE + stdin: ^FILE + stdout: ^FILE + } +} + +when ODIN_OS == .FreeBSD { + fpos_t :: distinct i64 + + _IOFBF :: 0 + _IOLBF :: 1 + _IONBF :: 1 + + BUFSIZ :: 1024 + + EOF :: int(-1) + + FOPEN_MAX :: 20 + FILENAME_MAX :: 1024 + + SEEK_SET :: 0 + SEEK_CUR :: 1 + SEEK_END :: 2 + + foreign libc { + stderr: ^FILE + stdin: ^FILE + stdout: ^FILE + } +} + +when ODIN_OS == .Darwin { fpos_t :: distinct i64 _IOFBF :: 0 @@ -146,7 +196,7 @@ foreign libc { getc :: proc(stream: ^FILE) -> int --- getchar :: proc() -> int --- putc :: proc(c: int, stream: ^FILE) -> int --- - putchar :: proc() -> int --- + putchar :: proc(c: int) -> int --- puts :: proc(s: cstring) -> int --- ungetc :: proc(c: int, stream: ^FILE) -> int --- fread :: proc(ptr: rawptr, size: size_t, nmemb: size_t, stream: ^FILE) -> size_t --- diff --git a/core/c/libc/stdlib.odin b/core/c/libc/stdlib.odin index b368c0cee..a278db0ef 100644 --- a/core/c/libc/stdlib.odin +++ b/core/c/libc/stdlib.odin @@ -2,15 +2,15 @@ package libc // 7.22 General utilities -when ODIN_OS == "windows" { +when ODIN_OS == .Windows { foreign import libc "system:libucrt.lib" -} else when ODIN_OS == "darwin" { +} else when ODIN_OS == .Darwin { foreign import libc "system:System.framework" } else { foreign import libc "system:c" } -when ODIN_OS == "windows" { +when ODIN_OS == .Windows { RAND_MAX :: 0x7fff @(private="file") @@ -24,7 +24,7 @@ when ODIN_OS == "windows" { } } -when ODIN_OS == "linux" { +when ODIN_OS == .Linux { RAND_MAX :: 0x7fffffff // GLIBC and MUSL only @@ -40,7 +40,7 @@ when ODIN_OS == "linux" { } -when ODIN_OS == "darwin" { +when ODIN_OS == .Darwin { RAND_MAX :: 0x7fffffff // GLIBC and MUSL only diff --git a/core/c/libc/string.odin b/core/c/libc/string.odin index c91124dcb..8f83ee1b9 100644 --- a/core/c/libc/string.odin +++ b/core/c/libc/string.odin @@ -4,9 +4,9 @@ import "core:runtime" // 7.24 String handling -when ODIN_OS == "windows" { +when ODIN_OS == .Windows { foreign import libc "system:libucrt.lib" -} else when ODIN_OS == "darwin" { +} else when ODIN_OS == .Darwin { foreign import libc "system:System.framework" } else { foreign import libc "system:c" diff --git a/core/c/libc/threads.odin b/core/c/libc/threads.odin index ad305b517..f6aae2e98 100644 --- a/core/c/libc/threads.odin +++ b/core/c/libc/threads.odin @@ -5,10 +5,10 @@ package libc thrd_start_t :: proc "c" (rawptr) -> int tss_dtor_t :: proc "c" (rawptr) -when ODIN_OS == "windows" { +when ODIN_OS == .Windows { foreign import libc { "system:libucrt.lib", - "system:msvcprt.lib" + "system:msvcprt.lib", } thrd_success :: 0 // _Thrd_success @@ -74,10 +74,10 @@ when ODIN_OS == "windows" { } // GLIBC and MUSL compatible constants and types. -when ODIN_OS == "linux" { +when ODIN_OS == .Linux { foreign import libc { "system:c", - "system:pthread" + "system:pthread", } thrd_success :: 0 @@ -138,6 +138,6 @@ when ODIN_OS == "linux" { } -when ODIN_OS == "darwin" { +when ODIN_OS == .Darwin { // TODO: find out what this is meant to be! } diff --git a/core/c/libc/time.odin b/core/c/libc/time.odin index 96e80e216..b337e139a 100644 --- a/core/c/libc/time.odin +++ b/core/c/libc/time.odin @@ -2,9 +2,9 @@ package libc // 7.27 Date and time -when ODIN_OS == "windows" { +when ODIN_OS == .Windows { foreign import libc "system:libucrt.lib" -} else when ODIN_OS == "darwin" { +} else when ODIN_OS == .Darwin { foreign import libc "system:System.framework" } else { foreign import libc "system:c" @@ -12,7 +12,7 @@ when ODIN_OS == "windows" { // We enforce 64-bit time_t and timespec as there is no reason to use 32-bit as // we approach the 2038 problem. Windows has defaulted to this since VC8 (2005). -when ODIN_OS == "windows" { +when ODIN_OS == .Windows { foreign libc { // 7.27.2 Time manipulation functions clock :: proc() -> clock_t --- @@ -45,7 +45,7 @@ when ODIN_OS == "windows" { } } -when ODIN_OS == "linux" || ODIN_OS == "freebsd" || ODIN_OS == "darwin" { +when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin || ODIN_OS == .OpenBSD { @(default_calling_convention="c") foreign libc { // 7.27.2 Time manipulation functions @@ -63,7 +63,12 @@ when ODIN_OS == "linux" || ODIN_OS == "freebsd" || ODIN_OS == "darwin" { strftime :: proc(s: [^]char, maxsize: size_t, format: cstring, timeptr: ^tm) -> size_t --- } - CLOCKS_PER_SEC :: 1000000 + when ODIN_OS == .OpenBSD { + CLOCKS_PER_SEC :: 100 + } else { + CLOCKS_PER_SEC :: 1000000 + } + TIME_UTC :: 1 time_t :: distinct i64 diff --git a/core/c/libc/uchar.odin b/core/c/libc/uchar.odin index e49c29e51..a10969ceb 100644 --- a/core/c/libc/uchar.odin +++ b/core/c/libc/uchar.odin @@ -2,9 +2,9 @@ package libc // 7.28 Unicode utilities -when ODIN_OS == "windows" { +when ODIN_OS == .Windows { foreign import libc "system:libucrt.lib" -} else when ODIN_OS == "darwin" { +} else when ODIN_OS == .Darwin { foreign import libc "system:System.framework" } else { foreign import libc "system:c" diff --git a/core/c/libc/wchar.odin b/core/c/libc/wchar.odin index fc206e494..f2aa8410e 100644 --- a/core/c/libc/wchar.odin +++ b/core/c/libc/wchar.odin @@ -2,9 +2,9 @@ package libc // 7.29 Extended multibyte and wide character utilities -when ODIN_OS == "windows" { +when ODIN_OS == .Windows { foreign import libc "system:libucrt.lib" -} else when ODIN_OS == "darwin" { +} else when ODIN_OS == .Darwin { foreign import libc "system:System.framework" } else { foreign import libc "system:c" diff --git a/core/c/libc/wctype.odin b/core/c/libc/wctype.odin index 47f17e0b4..43aee9dc6 100644 --- a/core/c/libc/wctype.odin +++ b/core/c/libc/wctype.odin @@ -2,27 +2,34 @@ package libc // 7.30 Wide character classification and mapping utilities -when ODIN_OS == "windows" { +when ODIN_OS == .Windows { foreign import libc "system:libucrt.lib" -} else when ODIN_OS == "darwin" { +} else when ODIN_OS == .Darwin { foreign import libc "system:System.framework" } else { foreign import libc "system:c" } -when ODIN_OS == "windows" { +when ODIN_OS == .Windows { wctrans_t :: distinct wchar_t wctype_t :: distinct ushort -} -when ODIN_OS == "linux" { +} else when ODIN_OS == .Linux { wctrans_t :: distinct intptr_t wctype_t :: distinct ulong -} -when ODIN_OS == "darwin" { +} else when ODIN_OS == .Darwin { wctrans_t :: distinct int wctype_t :: distinct u32 + +} else when ODIN_OS == .OpenBSD { + wctrans_t :: distinct rawptr + wctype_t :: distinct rawptr + +} else when ODIN_OS == .FreeBSD { + wctrans_t :: distinct int + wctype_t :: distinct ulong + } @(default_calling_convention="c") diff --git a/core/compress/common.odin b/core/compress/common.odin index 5f5ef2413..f4e378269 100644 --- a/core/compress/common.odin +++ b/core/compress/common.odin @@ -47,7 +47,7 @@ when size_of(uintptr) == 8 { } -Error :: union { +Error :: union #shared_nil { General_Error, Deflate_Error, ZLIB_Error, @@ -58,6 +58,7 @@ Error :: union { } General_Error :: enum { + None = 0, File_Not_Found, Cannot_Open_File, File_Too_Short, @@ -76,6 +77,7 @@ General_Error :: enum { } GZIP_Error :: enum { + None = 0, Invalid_GZIP_Signature, Reserved_Flag_Set, Invalid_Extra_Data, @@ -100,6 +102,7 @@ GZIP_Error :: enum { } ZIP_Error :: enum { + None = 0, Invalid_ZIP_File_Signature, Unexpected_Signature, Insert_Next_Disk, @@ -107,6 +110,7 @@ ZIP_Error :: enum { } ZLIB_Error :: enum { + None = 0, Unsupported_Window_Size, FDICT_Unsupported, Unsupported_Compression_Level, @@ -114,6 +118,7 @@ ZLIB_Error :: enum { } Deflate_Error :: enum { + None = 0, Huffman_Bad_Sizes, Huffman_Bad_Code_Lengths, Inflate_Error, @@ -123,7 +128,6 @@ Deflate_Error :: enum { BType_3, } - // General I/O context for ZLIB, LZW, etc. Context_Memory_Input :: struct #packed { input_data: []u8, @@ -139,7 +143,12 @@ Context_Memory_Input :: struct #packed { size_packed: i64, size_unpacked: i64, } -#assert(size_of(Context_Memory_Input) == 64) +when size_of(rawptr) == 8 { + #assert(size_of(Context_Memory_Input) == 64) +} else { + // e.g. `-target:windows_i386` + #assert(size_of(Context_Memory_Input) == 52) +} Context_Stream_Input :: struct #packed { input_data: []u8, @@ -174,8 +183,6 @@ Context_Stream_Input :: struct #packed { This simplifies end-of-stream handling where bits may be left in the bit buffer. */ -// TODO: Make these return compress.Error errors. - input_size_from_memory :: proc(z: ^Context_Memory_Input) -> (res: i64, err: Error) { return i64(len(z.input_data)), nil } @@ -473,4 +480,4 @@ discard_to_next_byte_lsb_from_stream :: proc(z: ^Context_Stream_Input) { consume_bits_lsb(z, discard) } -discard_to_next_byte_lsb :: proc{discard_to_next_byte_lsb_from_memory, discard_to_next_byte_lsb_from_stream}; \ No newline at end of file +discard_to_next_byte_lsb :: proc{discard_to_next_byte_lsb_from_memory, discard_to_next_byte_lsb_from_stream} diff --git a/core/compress/gzip/example.odin b/core/compress/gzip/example.odin index 0e2c2b9f6..c010d5979 100644 --- a/core/compress/gzip/example.odin +++ b/core/compress/gzip/example.odin @@ -45,7 +45,7 @@ main :: proc() { if len(args) < 2 { stderr("No input file specified.\n") - err := load(slice=TEST, buf=&buf, known_gzip_size=len(TEST)) + err := load(data=TEST, buf=&buf, known_gzip_size=len(TEST)) if err == nil { stdout("Displaying test vector: ") stdout(bytes.buffer_to_string(&buf)) diff --git a/core/compress/gzip/gzip.odin b/core/compress/gzip/gzip.odin index 1a72500bf..4de4d1b63 100644 --- a/core/compress/gzip/gzip.odin +++ b/core/compress/gzip/gzip.odin @@ -66,7 +66,8 @@ OS :: enum u8 { _Unknown = 14, Unknown = 255, } -OS_Name :: #partial [OS]string{ +OS_Name :: #sparse[OS]string{ + ._Unknown = "", .FAT = "FAT", .Amiga = "Amiga", .VMS = "VMS/OpenVMS", @@ -99,9 +100,9 @@ E_GZIP :: compress.GZIP_Error E_ZLIB :: compress.ZLIB_Error E_Deflate :: compress.Deflate_Error -GZIP_MAX_PAYLOAD_SIZE :: int(max(u32le)) +GZIP_MAX_PAYLOAD_SIZE :: i64(max(u32le)) -load :: proc{load_from_slice, load_from_file, load_from_context} +load :: proc{load_from_bytes, load_from_file, load_from_context} load_from_file :: proc(filename: string, buf: ^bytes.Buffer, expected_output_size := -1, allocator := context.allocator) -> (err: Error) { context.allocator = allocator @@ -111,16 +112,16 @@ load_from_file :: proc(filename: string, buf: ^bytes.Buffer, expected_output_siz err = E_General.File_Not_Found if ok { - err = load_from_slice(data, buf, len(data), expected_output_size) + err = load_from_bytes(data, buf, len(data), expected_output_size) } return } -load_from_slice :: proc(slice: []u8, buf: ^bytes.Buffer, known_gzip_size := -1, expected_output_size := -1, allocator := context.allocator) -> (err: Error) { +load_from_bytes :: proc(data: []byte, buf: ^bytes.Buffer, known_gzip_size := -1, expected_output_size := -1, allocator := context.allocator) -> (err: Error) { buf := buf z := &compress.Context_Memory_Input{ - input_data = slice, + input_data = data, output = buf, } return load_from_context(z, buf, known_gzip_size, expected_output_size, allocator) @@ -135,7 +136,7 @@ load_from_context :: proc(z: ^$C, buf: ^bytes.Buffer, known_gzip_size := -1, exp z.output = buf - if expected_output_size > GZIP_MAX_PAYLOAD_SIZE { + if i64(expected_output_size) > i64(GZIP_MAX_PAYLOAD_SIZE) { return E_GZIP.Payload_Size_Exceeds_Max_Payload } diff --git a/core/compress/shoco/model.odin b/core/compress/shoco/model.odin new file mode 100644 index 000000000..bbc38903d --- /dev/null +++ b/core/compress/shoco/model.odin @@ -0,0 +1,148 @@ +/* + This file was generated, so don't edit this by hand. + Transliterated from https://github.com/Ed-von-Schleck/shoco/blob/master/shoco_model.h, + which is an English word model. +*/ + +// package shoco is an implementation of the shoco short string compressor +package shoco + +DEFAULT_MODEL :: Shoco_Model { + min_char = 39, + max_char = 122, + characters_by_id = { + 'e', 'a', 'i', 'o', 't', 'h', 'n', 'r', 's', 'l', 'u', 'c', 'w', 'm', 'd', 'b', 'p', 'f', 'g', 'v', 'y', 'k', '-', 'H', 'M', 'T', '\'', 'B', 'x', 'I', 'W', 'L', + }, + ids_by_character = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, -1, -1, -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 27, -1, -1, -1, -1, -1, 23, 29, -1, -1, 31, 24, -1, -1, -1, -1, -1, -1, 25, -1, -1, 30, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 15, 11, 14, 0, 17, 18, 5, 2, -1, 21, 9, 13, 6, 3, 16, -1, 7, 8, 4, 10, 19, 12, 28, 20, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + }, + successors_by_bigram = { + 7, 4, 12, -1, 6, -1, 1, 0, 3, 5, -1, 9, -1, 8, 2, -1, 15, 14, -1, 10, 11, -1, -1, -1, -1, -1, -1, -1, 13, -1, -1, -1, + 1, -1, 6, -1, 1, -1, 0, 3, 2, 4, 15, 11, -1, 9, 5, 10, 13, -1, 12, 8, 7, 14, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 9, 11, -1, 4, 2, -1, 0, 8, 1, 5, -1, 6, -1, 3, 7, 15, -1, 12, 10, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 14, 7, 5, -1, 1, 2, 8, 9, 0, 15, 6, 4, 11, -1, 12, 3, -1, 10, -1, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 2, 4, 3, 1, 5, 0, -1, 6, 10, 9, 7, 12, 11, -1, -1, -1, -1, 13, -1, -1, 8, -1, 15, -1, -1, -1, 14, -1, -1, -1, -1, -1, + 0, 1, 2, 3, 4, -1, -1, 5, 9, 10, 6, -1, -1, 8, 15, 11, -1, 14, -1, -1, 7, -1, 13, -1, -1, -1, 12, -1, -1, -1, -1, -1, + 2, 8, 7, 4, 3, -1, 9, -1, 6, 11, -1, 5, -1, -1, 0, -1, -1, 14, 1, 15, 10, 12, -1, -1, -1, -1, 13, -1, -1, -1, -1, -1, + 0, 3, 1, 2, 6, -1, 9, 8, 4, 12, 13, 10, -1, 11, 7, -1, -1, 15, 14, -1, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 6, 3, 4, 1, 2, -1, -1, 5, 10, 7, 9, 11, 12, -1, -1, 8, 14, -1, -1, 15, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 6, 2, 5, 9, -1, -1, -1, 10, 1, 8, -1, 12, 14, 4, -1, 15, 7, -1, 13, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 8, 10, 9, 15, 1, -1, 4, 0, 3, 2, -1, 6, -1, 12, 11, 13, 7, 14, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 1, 3, 6, 0, 4, 2, -1, 7, 13, 8, 9, 11, -1, -1, 15, -1, -1, -1, -1, -1, 10, 5, 14, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 3, 0, 1, 4, -1, 2, 5, 6, 7, 8, -1, 14, -1, -1, 9, 15, -1, 12, -1, -1, -1, 10, 11, -1, -1, -1, 13, -1, -1, -1, -1, -1, + 0, 1, 3, 2, 15, -1, 12, -1, 7, 14, 4, -1, -1, 9, -1, 8, 5, 10, -1, -1, 6, -1, 13, -1, -1, -1, 11, -1, -1, -1, -1, -1, + 0, 3, 1, 2, -1, -1, 12, 6, 4, 9, 7, -1, -1, 14, 8, -1, -1, 15, 11, 13, 5, -1, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 5, 7, 2, 10, 13, -1, 6, 8, 1, 3, -1, -1, 14, 15, 11, -1, -1, -1, 12, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 2, 6, 3, 7, 10, -1, 1, 9, 4, 8, -1, -1, 15, -1, 12, 5, -1, -1, -1, 11, -1, 13, -1, -1, -1, 14, -1, -1, -1, -1, -1, + 1, 3, 4, 0, 7, -1, 12, 2, 11, 8, 6, 13, -1, -1, -1, -1, -1, 5, -1, -1, 10, 15, 9, -1, -1, -1, 14, -1, -1, -1, -1, -1, + 1, 3, 5, 2, 13, 0, 9, 4, 7, 6, 8, -1, -1, 15, -1, 11, -1, -1, 10, -1, 14, -1, 12, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 2, 1, 3, -1, -1, -1, 6, -1, -1, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 1, 11, 4, 0, 3, -1, 13, 12, 2, 7, -1, -1, 15, 10, 5, 8, 14, -1, -1, -1, -1, -1, 9, -1, -1, -1, 6, -1, -1, -1, -1, -1, + 0, 9, 2, 14, 15, 4, 1, 13, 3, 5, -1, -1, 10, -1, -1, -1, -1, 6, 12, -1, 7, -1, 8, -1, -1, -1, 11, -1, -1, -1, -1, -1, + -1, 2, 14, -1, 1, 5, 8, 7, 4, 12, -1, 6, 9, 11, 13, 3, 10, 15, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 1, 3, 2, -1, -1, -1, -1, -1, -1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 4, 3, 1, 5, -1, -1, -1, 0, -1, -1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 2, 8, 4, 1, -1, 0, -1, 6, -1, -1, 5, -1, 7, -1, -1, -1, -1, -1, -1, -1, 10, -1, -1, 9, -1, -1, -1, -1, -1, -1, -1, -1, + 12, 5, -1, -1, 1, -1, -1, 7, 0, 3, -1, 2, -1, 4, 6, -1, -1, -1, -1, 8, -1, -1, 15, -1, 13, 9, -1, -1, -1, -1, -1, 11, + 1, 3, 2, 4, -1, -1, -1, 5, -1, 7, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, 6, -1, -1, -1, -1, -1, -1, -1, -1, 8, -1, -1, + 5, 3, 4, 12, 1, 6, -1, -1, -1, -1, 8, 2, -1, -1, -1, -1, 0, 9, -1, -1, 11, -1, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 0, -1, 1, 12, 3, -1, -1, -1, -1, 5, -1, -1, -1, 2, -1, -1, -1, -1, -1, -1, -1, -1, 4, -1, -1, 6, -1, 10, + 2, 3, 1, 4, -1, 0, -1, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 7, -1, -1, -1, -1, -1, -1, -1, -1, 6, -1, -1, + 5, 1, 3, 0, -1, -1, -1, -1, -1, -1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, -1, -1, -1, -1, -1, 9, -1, -1, 6, -1, 7, + }, + successors_reversed = { + 's', 't', 'c', 'l', 'm', 'a', 'd', 'r', 'v', 'T', 'A', 'L', 'e', 'M', 'Y', '-', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '-', 't', 'a', 'b', 's', 'h', 'c', 'r', 'n', 'w', 'p', 'm', 'l', 'd', 'i', 'f', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + 'u', 'e', 'i', 'a', 'o', 'r', 'y', 'l', 'I', 'E', 'R', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + 'e', 'a', 'o', 'i', 'u', 'A', 'y', 'E', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + 't', 'n', 'f', 's', '\'', 'm', 'I', 'N', 'A', 'E', 'L', 'Z', 'r', 'V', 'R', 'C', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + 'o', 'a', 'y', 'i', 'u', 'e', 'I', 'L', 'D', '\'', 'E', 'Y', '\x00', '\x00', '\x00', '\x00', + 'r', 'i', 'y', 'a', 'e', 'o', 'u', 'Y', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + 'h', 'o', 'e', 'E', 'i', 'u', 'r', 'w', 'a', 'H', 'y', 'R', 'Z', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + 'h', 'i', 'e', 'a', 'o', 'r', 'I', 'y', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + 'n', 't', 's', 'r', 'l', 'd', 'i', 'y', 'v', 'm', 'b', 'c', 'g', 'p', 'k', 'u', + 'e', 'l', 'o', 'u', 'y', 'a', 'r', 'i', 's', 'j', 't', 'b', 'v', 'h', 'm', 'd', + 'o', 'e', 'h', 'a', 't', 'k', 'i', 'r', 'l', 'u', 'y', 'c', 'q', 's', '-', 'd', + 'e', 'i', 'o', 'a', 's', 'y', 'r', 'u', 'd', 'l', '-', 'g', 'n', 'v', 'm', 'f', + 'r', 'n', 'd', 's', 'a', 'l', 't', 'e', 'm', 'c', 'v', 'y', 'i', 'x', 'f', 'p', + 'o', 'e', 'r', 'a', 'i', 'f', 'u', 't', 'l', '-', 'y', 's', 'n', 'c', '\'', 'k', + 'h', 'e', 'o', 'a', 'r', 'i', 'l', 's', 'u', 'n', 'g', 'b', '-', 't', 'y', 'm', + 'e', 'a', 'i', 'o', 't', 'r', 'u', 'y', 'm', 's', 'l', 'b', '\'', '-', 'f', 'd', + 'n', 's', 't', 'm', 'o', 'l', 'c', 'd', 'r', 'e', 'g', 'a', 'f', 'v', 'z', 'b', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + 'e', 'n', 'i', 's', 'h', 'l', 'f', 'y', '-', 'a', 'w', '\'', 'g', 'r', 'o', 't', + 'e', 'l', 'i', 'y', 'd', 'o', 'a', 'f', 'u', 't', 's', 'k', 'w', 'v', 'm', 'p', + 'e', 'a', 'o', 'i', 'u', 'p', 'y', 's', 'b', 'm', 'f', '\'', 'n', '-', 'l', 't', + 'd', 'g', 'e', 't', 'o', 'c', 's', 'i', 'a', 'n', 'y', 'l', 'k', '\'', 'f', 'v', + 'u', 'n', 'r', 'f', 'm', 't', 'w', 'o', 's', 'l', 'v', 'd', 'p', 'k', 'i', 'c', + 'e', 'r', 'a', 'o', 'l', 'p', 'i', 't', 'u', 's', 'h', 'y', 'b', '-', '\'', 'm', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + 'e', 'i', 'o', 'a', 's', 'y', 't', 'd', 'r', 'n', 'c', 'm', 'l', 'u', 'g', 'f', + 'e', 't', 'h', 'i', 'o', 's', 'a', 'u', 'p', 'c', 'l', 'w', 'm', 'k', 'f', 'y', + 'h', 'o', 'e', 'i', 'a', 't', 'r', 'u', 'y', 'l', 's', 'w', 'c', 'f', '\'', '-', + 'r', 't', 'l', 's', 'n', 'g', 'c', 'p', 'e', 'i', 'a', 'd', 'm', 'b', 'f', 'o', + 'e', 'i', 'a', 'o', 'y', 'u', 'r', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + 'a', 'i', 'h', 'e', 'o', 'n', 'r', 's', 'l', 'd', 'k', '-', 'f', '\'', 'c', 'b', + 'p', 't', 'c', 'a', 'i', 'e', 'h', 'q', 'u', 'f', '-', 'y', 'o', '\x00', '\x00', '\x00', + 'o', 'e', 's', 't', 'i', 'd', '\'', 'l', 'b', '-', 'm', 'a', 'r', 'n', 'p', 'w', + }, + + character_count = 32, + successor_count = 16, + + max_successor_n = 7, + packs = { + { 0x80000000, 1, 2, { 26, 24, 24, 24, 24, 24, 24, 24 }, { 15, 3, 0, 0, 0, 0, 0, 0 }, 0xc0, 0x80 }, + { 0xc0000000, 2, 4, { 25, 22, 19, 16, 16, 16, 16, 16 }, { 15, 7, 7, 7, 0, 0, 0, 0 }, 0xe0, 0xc0 }, + { 0xe0000000, 4, 8, { 23, 19, 15, 11, 8, 5, 2, 0 }, { 31, 15, 15, 15, 7, 7, 7, 3 }, 0xf0, 0xe0 }, + }, +} \ No newline at end of file diff --git a/core/compress/shoco/shoco.odin b/core/compress/shoco/shoco.odin new file mode 100644 index 000000000..f94ce70b7 --- /dev/null +++ b/core/compress/shoco/shoco.odin @@ -0,0 +1,318 @@ +/* + Copyright 2022 Jeroen van Rijn . + Made available under Odin's BSD-3 license. + + List of contributors: + Jeroen van Rijn: Initial implementation. + + An implementation of [shoco](https://github.com/Ed-von-Schleck/shoco) by Christian Schramm. +*/ + +// package shoco is an implementation of the shoco short string compressor +package shoco + +import "core:intrinsics" +import "core:compress" + +Shoco_Pack :: struct { + word: u32, + bytes_packed: i8, + bytes_unpacked: i8, + offsets: [8]u16, + masks: [8]i16, + header_mask: u8, + header: u8, +} + +Shoco_Model :: struct { + min_char: u8, + max_char: u8, + characters_by_id: []u8, + ids_by_character: [256]i16, + successors_by_bigram: []i8, + successors_reversed: []u8, + + character_count: u8, + successor_count: u8, + max_successor_n: i8, + packs: []Shoco_Pack, +} + +compress_bound :: proc(uncompressed_size: int) -> (worst_case_compressed_size: int) { + // Worst case compression happens when input is non-ASCII (128-255) + // Encoded as 0x00 + the byte in question. + return uncompressed_size * 2 +} + +decompress_bound :: proc(compressed_size: int, model := DEFAULT_MODEL) -> (maximum_decompressed_size: int) { + // Best case compression is 2:1 + most: f64 + for pack in model.packs { + val := f64(compressed_size) / f64(pack.bytes_packed) * f64(pack.bytes_unpacked) + most = max(most, val) + } + return int(most) +} + +find_best_encoding :: proc(indices: []i16, n_consecutive: i8, model := DEFAULT_MODEL) -> (res: int) { + for p := len(model.packs); p > 0; p -= 1 { + pack := model.packs[p - 1] + if n_consecutive >= pack.bytes_unpacked { + have_index := true + for i := 0; i < int(pack.bytes_unpacked); i += 1 { + if indices[i] > pack.masks[i] { + have_index = false + break + } + } + if have_index { + return p - 1 + } + } + } + return -1 +} + +validate_model :: proc(model: Shoco_Model) -> (int, compress.Error) { + if len(model.characters_by_id) != int(model.character_count) { + return 0, .Unknown_Compression_Method + } + + if len(model.successors_by_bigram) != int(model.character_count) * int(model.character_count) { + return 0, .Unknown_Compression_Method + } + + if len(model.successors_reversed) != int(model.successor_count) * int(model.max_char - model.min_char) { + return 0, .Unknown_Compression_Method + } + + // Model seems legit. + return 0, nil +} + +// Decompresses into provided buffer. +decompress_slice_to_output_buffer :: proc(input: []u8, output: []u8, model := DEFAULT_MODEL) -> (size: int, err: compress.Error) { + inp, inp_end := 0, len(input) + out, out_end := 0, len(output) + + validate_model(model) or_return + + for inp < inp_end { + val := transmute(i8)input[inp] + mark := int(-1) + + for val < 0 { + val <<= 1 + mark += 1 + } + + if mark > len(model.packs) { + return out, .Unknown_Compression_Method + } + + if mark < 0 { + if out >= out_end { + return out, .Output_Too_Short + } + + // Ignore the sentinel value for non-ASCII chars + if input[inp] == 0x00 { + inp += 1 + if inp >= inp_end { + return out, .Stream_Too_Short + } + } + output[out] = input[inp] + inp, out = inp + 1, out + 1 + + } else { + pack := model.packs[mark] + + if out + int(pack.bytes_unpacked) > out_end { + return out, .Output_Too_Short + } else if inp + int(pack.bytes_packed) > inp_end { + return out, .Stream_Too_Short + } + + code := intrinsics.unaligned_load((^u32)(&input[inp])) + when ODIN_ENDIAN == .Little { + code = intrinsics.byte_swap(code) + } + + // Unpack the leading char + offset := pack.offsets[0] + mask := pack.masks[0] + + last_chr := model.characters_by_id[(code >> offset) & u32(mask)] + output[out] = last_chr + + // Unpack the successor chars + for i := 1; i < int(pack.bytes_unpacked); i += 1 { + offset = pack.offsets[i] + mask = pack.masks[i] + + index_major := u32(last_chr - model.min_char) * u32(model.successor_count) + index_minor := (code >> offset) & u32(mask) + + last_chr = model.successors_reversed[index_major + index_minor] + + output[out + i] = last_chr + } + + out += int(pack.bytes_unpacked) + inp += int(pack.bytes_packed) + } + } + + return out, nil +} + +decompress_slice_to_string :: proc(input: []u8, model := DEFAULT_MODEL, allocator := context.allocator) -> (res: string, err: compress.Error) { + context.allocator = allocator + + if len(input) == 0 { + return "", .Stream_Too_Short + } + + max_output_size := decompress_bound(len(input), model) + + buf: [dynamic]u8 + if !resize(&buf, max_output_size) { + return "", .Out_Of_Memory + } + + length, result := decompress_slice_to_output_buffer(input, buf[:]) + resize(&buf, length) + return string(buf[:]), result +} +decompress :: proc{decompress_slice_to_output_buffer, decompress_slice_to_string} + +compress_string_to_buffer :: proc(input: string, output: []u8, model := DEFAULT_MODEL, allocator := context.allocator) -> (size: int, err: compress.Error) { + inp, inp_end := 0, len(input) + out, out_end := 0, len(output) + output := output + + validate_model(model) or_return + + indices := make([]i16, model.max_successor_n + 1) + defer delete(indices) + + last_resort := false + + encode: for inp < inp_end { + if last_resort { + last_resort = false + + if input[inp] & 0x80 == 0x80 { + // Non-ASCII case + if out + 2 > out_end { + return out, .Output_Too_Short + } + + // Put in a sentinel byte + output[out] = 0x00 + out += 1 + } else { + // An ASCII byte + if out + 1 > out_end { + return out, .Output_Too_Short + } + } + output[out] = input[inp] + out, inp = out + 1, inp + 1 + } else { + // Find the longest string of known successors + indices[0] = model.ids_by_character[input[inp]] + last_chr_index := indices[0] + + if last_chr_index < 0 { + last_resort = true + continue encode + } + + rest := inp_end - inp + n_consecutive: i8 = 1 + for ; n_consecutive <= model.max_successor_n; n_consecutive += 1 { + if inp_end > 0 && int(n_consecutive) == rest { + break + } + + current_index := model.ids_by_character[input[inp + int(n_consecutive)]] + if current_index < 0 { // '\0' is always -1 + break + } + + successor_index := model.successors_by_bigram[last_chr_index * i16(model.character_count) + current_index] + if successor_index < 0 { + break + } + + indices[n_consecutive] = i16(successor_index) + last_chr_index = current_index + } + + if n_consecutive < 2 { + last_resort = true + continue encode + } + + pack_n := find_best_encoding(indices, n_consecutive) + if pack_n >= 0 { + if out + int(model.packs[pack_n].bytes_packed) > out_end { + return out, .Output_Too_Short + } + + pack := model.packs[pack_n] + code := pack.word + + for i := 0; i < int(pack.bytes_unpacked); i += 1 { + code |= u32(indices[i]) << pack.offsets[i] + } + + // In the little-endian world, we need to swap what's in the register to match the memory representation. + when ODIN_ENDIAN == .Little { + code = intrinsics.byte_swap(code) + } + out_ptr := raw_data(output[out:]) + + switch pack.bytes_packed { + case 4: + intrinsics.unaligned_store(transmute(^u32)out_ptr, code) + case 2: + intrinsics.unaligned_store(transmute(^u16)out_ptr, u16(code)) + case 1: + intrinsics.unaligned_store(transmute(^u8)out_ptr, u8(code)) + case: + return out, .Unknown_Compression_Method + } + + out += int(pack.bytes_packed) + inp += int(pack.bytes_unpacked) + } else { + last_resort = true + continue encode + } + } + } + return out, nil +} + +compress_string :: proc(input: string, model := DEFAULT_MODEL, allocator := context.allocator) -> (output: []u8, err: compress.Error) { + context.allocator = allocator + + if len(input) == 0 { + return {}, .Stream_Too_Short + } + + max_output_size := compress_bound(len(input)) + + buf: [dynamic]u8 + if !resize(&buf, max_output_size) { + return {}, .Out_Of_Memory + } + + length, result := compress_string_to_buffer(input, buf[:]) + resize(&buf, length) + return buf[:length], result +} +compress :: proc{compress_string_to_buffer, compress_string} \ No newline at end of file diff --git a/core/compress/zlib/zlib.odin b/core/compress/zlib/zlib.odin index 9ae980042..855eef7a8 100644 --- a/core/compress/zlib/zlib.odin +++ b/core/compress/zlib/zlib.odin @@ -47,10 +47,10 @@ Options :: struct { level: u8, } -Error :: compress.Error -E_General :: compress.General_Error -E_ZLIB :: compress.ZLIB_Error -E_Deflate :: compress.Deflate_Error +Error :: compress.Error +General_Error :: compress.General_Error +ZLIB_Error :: compress.ZLIB_Error +Deflate_Error :: compress.Deflate_Error DEFLATE_MAX_CHUNK_SIZE :: 65535 DEFLATE_MAX_LITERAL_SIZE :: 65535 @@ -111,9 +111,9 @@ ZFAST_MASK :: ((1 << ZFAST_BITS) - 1) */ Huffman_Table :: struct { fast: [1 << ZFAST_BITS]u16, - firstcode: [16]u16, + firstcode: [17]u16, maxcode: [17]int, - firstsymbol: [16]u16, + firstsymbol: [17]u16, size: [288]u8, value: [288]u16, } @@ -244,7 +244,7 @@ allocate_huffman_table :: proc(allocator := context.allocator) -> (z: ^Huffman_T @(optimization_mode="speed") build_huffman :: proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) { sizes: [HUFFMAN_MAX_BITS+1]int - next_code: [HUFFMAN_MAX_BITS]int + next_code: [HUFFMAN_MAX_BITS+1]int k := int(0) @@ -256,21 +256,21 @@ build_huffman :: proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) { } sizes[0] = 0 - for i in 1..<(HUFFMAN_MAX_BITS+1) { + for i in 1 ..< HUFFMAN_MAX_BITS { if sizes[i] > (1 << uint(i)) { - return E_Deflate.Huffman_Bad_Sizes + return .Huffman_Bad_Sizes } } code := int(0) - for i in 1..= (1 << u16(i)) { - return E_Deflate.Huffman_Bad_Code_Lengths + return .Huffman_Bad_Code_Lengths } } z.maxcode[i] = code << (HUFFMAN_MAX_BITS - uint(i)) @@ -314,15 +314,15 @@ decode_huffman_slowpath :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Erro s += 1 } if s >= 16 { - return 0, E_Deflate.Bad_Huffman_Code + return 0, .Bad_Huffman_Code } // code size is s, so: b := (k >> (16-s)) - int(t.firstcode[s]) + int(t.firstsymbol[s]) if b >= size_of(t.size) { - return 0, E_Deflate.Bad_Huffman_Code + return 0, .Bad_Huffman_Code } if t.size[b] != s { - return 0, E_Deflate.Bad_Huffman_Code + return 0, .Bad_Huffman_Code } compress.consume_bits_lsb(z, s) @@ -335,11 +335,11 @@ decode_huffman_slowpath :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Erro decode_huffman :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check { if z.num_bits < 16 { if z.num_bits > 63 { - return 0, E_ZLIB.Code_Buffer_Malformed + return 0, .Code_Buffer_Malformed } compress.refill_lsb(z) if z.num_bits > 63 { - return 0, E_General.Stream_Too_Short + return 0, .Stream_Too_Short } } #no_bounds_check b := t.fast[z.code_buffer & ZFAST_MASK] @@ -361,7 +361,7 @@ parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err: if value < 256 { e := write_byte(z, u8(value)) if e != .None { - return E_General.Output_Too_Short + return .Output_Too_Short } } else { if value == 256 { @@ -377,7 +377,7 @@ parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err: value, e = decode_huffman(z, z_offset) if e != nil { - return E_Deflate.Bad_Huffman_Code + return .Bad_Huffman_Code } distance := Z_DIST_BASE[value] @@ -387,7 +387,7 @@ parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err: if z.bytes_written < i64(distance) { // Distance is longer than we've decoded so far. - return E_Deflate.Bad_Distance + return .Bad_Distance } /* @@ -405,14 +405,14 @@ parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err: c := z.output.buf[z.bytes_written - i64(distance)] e := repl_byte(z, length, c) if e != .None { - return E_General.Output_Too_Short + return .Output_Too_Short } } } else { if length > 0 { e := repl_bytes(z, length, distance) if e != .None { - return E_General.Output_Too_Short + return .Output_Too_Short } } } @@ -432,25 +432,25 @@ inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := f if !raw { size, size_err := compress.input_size(ctx) if size < 6 || size_err != nil { - return E_General.Stream_Too_Short + return .Stream_Too_Short } cmf, _ := compress.read_u8(ctx) method := Compression_Method(cmf & 0xf) if method != .DEFLATE { - return E_General.Unknown_Compression_Method + return .Unknown_Compression_Method } if cinfo := (cmf >> 4) & 0xf; cinfo > 7 { - return E_ZLIB.Unsupported_Window_Size + return .Unsupported_Window_Size } flg, _ := compress.read_u8(ctx) fcheck := flg & 0x1f fcheck_computed := (cmf << 8 | flg) & 0x1f if fcheck != fcheck_computed { - return E_General.Checksum_Failed + return .Checksum_Failed } /* @@ -458,7 +458,7 @@ inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := f They're application specific and PNG doesn't use them. */ if fdict := (flg >> 5) & 1; fdict != 0 { - return E_ZLIB.FDICT_Unsupported + return .FDICT_Unsupported } // flevel := Compression_Level((flg >> 6) & 3); @@ -485,7 +485,7 @@ inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := f output_hash := hash.adler32(ctx.output.buf[:]) if output_hash != u32(adler) { - return E_General.Checksum_Failed + return .Checksum_Failed } } return nil @@ -538,23 +538,24 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all final = compress.read_bits_lsb(z, 1) type = compress.read_bits_lsb(z, 2) - // fmt.printf("Final: %v | Type: %v\n", final, type); + // fmt.printf("Final: %v | Type: %v\n", final, type) switch type { case 0: + // fmt.printf("Method 0: STORED\n") // Uncompressed block // Discard bits until next byte boundary compress.discard_to_next_byte_lsb(z) - uncompressed_len := i16(compress.read_bits_lsb(z, 16)) - length_check := i16(compress.read_bits_lsb(z, 16)) + uncompressed_len := u16(compress.read_bits_lsb(z, 16)) + length_check := u16(compress.read_bits_lsb(z, 16)) - // fmt.printf("LEN: %v, ~LEN: %v, NLEN: %v, ~NLEN: %v\n", uncompressed_len, ~uncompressed_len, length_check, ~length_check); + // fmt.printf("LEN: %v, ~LEN: %v, NLEN: %v, ~NLEN: %v\n", uncompressed_len, ~uncompressed_len, length_check, ~length_check) if ~uncompressed_len != length_check { - return E_Deflate.Len_Nlen_Mismatch + return .Len_Nlen_Mismatch } /* @@ -567,10 +568,12 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all write_byte(z, u8(lit)) uncompressed_len -= 1 } + assert(uncompressed_len == 0) + case 3: - return E_Deflate.BType_3 + return .BType_3 case: - // log.debugf("Err: %v | Final: %v | Type: %v\n", err, final, type); + // fmt.printf("Err: %v | Final: %v | Type: %v\n", err, final, type) if type == 1 { // Use fixed code lengths. build_huffman(z_repeat, Z_FIXED_LENGTH[:]) or_return @@ -601,7 +604,7 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all c = decode_huffman(z, codelength_ht) or_return if c < 0 || c >= 19 { - return E_Deflate.Huffman_Bad_Code_Lengths + return .Huffman_Bad_Code_Lengths } if c < 16 { lencodes[n] = u8(c) @@ -613,7 +616,7 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all case 16: c = u16(compress.read_bits_no_refill_lsb(z, 2) + 3) if n == 0 { - return E_Deflate.Huffman_Bad_Code_Lengths + return .Huffman_Bad_Code_Lengths } fill = lencodes[n - 1] case 17: @@ -621,11 +624,11 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all case 18: c = u16(compress.read_bits_no_refill_lsb(z, 7) + 11) case: - return E_Deflate.Huffman_Bad_Code_Lengths + return .Huffman_Bad_Code_Lengths } if ntot - n < u32(c) { - return E_Deflate.Huffman_Bad_Code_Lengths + return .Huffman_Bad_Code_Lengths } nc := n + u32(c) @@ -636,7 +639,7 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all } if n != ntot { - return E_Deflate.Huffman_Bad_Code_Lengths + return .Huffman_Bad_Code_Lengths } build_huffman(z_repeat, lencodes[:hlit]) or_return @@ -674,4 +677,4 @@ inflate_from_byte_array_raw :: proc(input: []u8, buf: ^bytes.Buffer, raw := fals return inflate_raw(z=&ctx, expected_output_size=expected_output_size) } -inflate :: proc{inflate_from_context, inflate_from_byte_array}; \ No newline at end of file +inflate :: proc{inflate_from_context, inflate_from_byte_array} diff --git a/core/container/bit_array/bit_array.odin b/core/container/bit_array/bit_array.odin index 61f6f86e8..763a19f8b 100644 --- a/core/container/bit_array/bit_array.odin +++ b/core/container/bit_array/bit_array.odin @@ -1,6 +1,7 @@ package dynamic_bit_array import "core:intrinsics" +import "core:mem" /* Note that these constants are dependent on the backing being a u64. @@ -11,11 +12,120 @@ INDEX_SHIFT :: 6 @(private="file") INDEX_MASK :: 63 +@(private="file") +NUM_BITS :: 64 + Bit_Array :: struct { - bits: [dynamic]u64, - bias: int, + bits: [dynamic]u64, + bias: int, + max_index: int, + free_pointer: bool, } +Bit_Array_Iterator :: struct { + array: ^Bit_Array, + word_idx: int, + bit_idx: uint, +} + +/* + In: + - ba: ^Bit_Array - the array to iterate over + + Out: + - it: ^Bit_Array_Iterator - the iterator that holds iteration state +*/ +make_iterator :: proc (ba: ^Bit_Array) -> (it: Bit_Array_Iterator) { + return Bit_Array_Iterator { array = ba } +} + +/* + In: + - it: ^Bit_Array_Iterator - the iterator struct that holds the state. + + Out: + - set: bool - the state of the bit at `index` + - index: int - the next bit of the Bit_Array referenced by `it`. + - ok: bool - `true` if the iterator returned a valid index, + `false` if there were no more bits +*/ +iterate_by_all :: proc (it: ^Bit_Array_Iterator) -> (set: bool, index: int, ok: bool) { + index = it.word_idx * NUM_BITS + int(it.bit_idx) + it.array.bias + if index > it.array.max_index { return false, 0, false } + + word := it.array.bits[it.word_idx] if len(it.array.bits) > it.word_idx else 0 + set = (word >> it.bit_idx & 1) == 1 + + it.bit_idx += 1 + if it.bit_idx >= NUM_BITS { + it.bit_idx = 0 + it.word_idx += 1 + } + + return set, index, true +} + +/* + In: + - it: ^Bit_Array_Iterator - the iterator struct that holds the state. + + Out: + - index: int - the next set bit of the Bit_Array referenced by `it`. + - ok: bool - `true` if the iterator returned a valid index, + `false` if there were no more bits set +*/ +iterate_by_set :: proc (it: ^Bit_Array_Iterator) -> (index: int, ok: bool) { + return iterate_internal_(it, true) +} + +/* + In: + - it: ^Bit_Array_Iterator - the iterator struct that holds the state. + + Out: + - index: int - the next unset bit of the Bit_Array referenced by `it`. + - ok: bool - `true` if the iterator returned a valid index, + `false` if there were no more unset bits +*/ +iterate_by_unset:: proc (it: ^Bit_Array_Iterator) -> (index: int, ok: bool) { + return iterate_internal_(it, false) +} + +@(private="file") +iterate_internal_ :: proc (it: ^Bit_Array_Iterator, $ITERATE_SET_BITS: bool) -> (index: int, ok: bool) { + word := it.array.bits[it.word_idx] if len(it.array.bits) > it.word_idx else 0 + when ! ITERATE_SET_BITS { word = ~word } + + // if the word is empty or we have already gone over all the bits in it, + // b.bit_idx is greater than the index of any set bit in the word, + // meaning that word >> b.bit_idx == 0. + for it.word_idx < len(it.array.bits) && word >> it.bit_idx == 0 { + it.word_idx += 1 + it.bit_idx = 0 + word = it.array.bits[it.word_idx] if len(it.array.bits) > it.word_idx else 0 + when ! ITERATE_SET_BITS { word = ~word } + } + + // if we are iterating the set bits, reaching the end of the array means we have no more bits to check + when ITERATE_SET_BITS { + if it.word_idx >= len(it.array.bits) { + return 0, false + } + } + + // reaching here means that the word has some set bits + it.bit_idx += uint(intrinsics.count_trailing_zeros(word >> it.bit_idx)) + index = it.word_idx * NUM_BITS + int(it.bit_idx) + it.array.bias + + it.bit_idx += 1 + if it.bit_idx >= NUM_BITS { + it.bit_idx = 0 + it.word_idx += 1 + } + return index, index <= it.array.max_index +} + + /* In: - ba: ^Bit_Array - a pointer to the Bit Array @@ -70,14 +180,42 @@ set :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator resize_if_needed(ba, leg_index) or_return + ba.max_index = max(idx, ba.max_index) ba.bits[leg_index] |= 1 << uint(bit_index) return true } +/* + In: + - ba: ^Bit_Array - a pointer to the Bit Array + - index: The bit index. Can be an enum member. + + Out: + - ok: Whether or not we managed to unset requested bit. + + `unset` automatically resizes the Bit Array to accommodate the requested index if needed. +*/ +unset :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator) -> (ok: bool) { + + idx := int(index) - ba.bias + + if ba == nil || int(index) < ba.bias { return false } + context.allocator = allocator + + leg_index := idx >> INDEX_SHIFT + bit_index := idx & INDEX_MASK + + resize_if_needed(ba, leg_index) or_return + + ba.max_index = max(idx, ba.max_index) + ba.bits[leg_index] &= ~(1 << uint(bit_index)) + return true +} + /* A helper function to create a Bit Array with optional bias, in case your smallest index is non-zero (including negative). */ -create :: proc(max_index: int, min_index := 0, allocator := context.allocator) -> (res: Bit_Array, ok: bool) #optional_ok { +create :: proc(max_index: int, min_index := 0, allocator := context.allocator) -> (res: ^Bit_Array, ok: bool) #optional_ok { context.allocator = allocator size_in_bits := max_index - min_index @@ -85,10 +223,11 @@ create :: proc(max_index: int, min_index := 0, allocator := context.allocator) - legs := size_in_bits >> INDEX_SHIFT - res = Bit_Array{ - bias = min_index, - } - return res, resize_if_needed(&res, size_in_bits) + res = new(Bit_Array) + res.bias = min_index + res.max_index = max_index + res.free_pointer = true + return res, resize_if_needed(res, legs) } /* @@ -96,7 +235,7 @@ create :: proc(max_index: int, min_index := 0, allocator := context.allocator) - */ clear :: proc(ba: ^Bit_Array) { if ba == nil { return } - ba.bits = {} + mem.zero_slice(ba.bits[:]) } /* @@ -105,6 +244,9 @@ clear :: proc(ba: ^Bit_Array) { destroy :: proc(ba: ^Bit_Array) { if ba == nil { return } delete(ba.bits) + if ba.free_pointer { // Only free if this Bit_Array was created using `create`, not when on the stack. + free(ba) + } } /* @@ -121,4 +263,4 @@ resize_if_needed :: proc(ba: ^Bit_Array, legs: int, allocator := context.allocat resize(&ba.bits, legs + 1) } return len(ba.bits) > legs -} \ No newline at end of file +} diff --git a/core/container/bit_array/doc.odin b/core/container/bit_array/doc.odin index 91e1362dd..52e252d8a 100644 --- a/core/container/bit_array/doc.odin +++ b/core/container/bit_array/doc.odin @@ -21,6 +21,7 @@ package dynamic_bit_array // returns `false`, `false`, because this Bit Array wasn't created to allow negative indices. was_set, was_retrieved := get(&bits, -1) fmt.println(was_set, was_retrieved) + destroy(&bits) } -- A Bit Array can optionally allow for negative indices, if the mininum value was given during creation: @@ -40,13 +41,13 @@ package dynamic_bit_array using bit_array bits := create(int(max(Foo)), int(min(Foo))) - defer destroy(&bits) + defer destroy(bits) - fmt.printf("Set(Bar): %v\n", set(&bits, Foo.Bar)) - fmt.printf("Get(Bar): %v, %v\n", get(&bits, Foo.Bar)) - fmt.printf("Set(Negative_Test): %v\n", set(&bits, Foo.Negative_Test)) - fmt.printf("Get(Leaves): %v, %v\n", get(&bits, Foo.Leaves)) - fmt.printf("Get(Negative_Test): %v, %v\n", get(&bits, Foo.Negative_Test)) + fmt.printf("Set(Bar): %v\n", set(bits, Foo.Bar)) + fmt.printf("Get(Bar): %v, %v\n", get(bits, Foo.Bar)) + fmt.printf("Set(Negative_Test): %v\n", set(bits, Foo.Negative_Test)) + fmt.printf("Get(Leaves): %v, %v\n", get(bits, Foo.Leaves)) + fmt.printf("Get(Negative_Test): %v, %v\n", get(bits, Foo.Negative_Test)) fmt.printf("Freed.\n") } */ \ No newline at end of file diff --git a/core/container/intrusive/list/intrusive_list.odin b/core/container/intrusive/list/intrusive_list.odin new file mode 100644 index 000000000..88e21edc5 --- /dev/null +++ b/core/container/intrusive/list/intrusive_list.odin @@ -0,0 +1,173 @@ +package container_intrusive_list + +import "core:intrinsics" + +// An intrusive doubly-linked list +// +// As this is an intrusive container, a `Node` must be embedded in your own +// structure which is conventionally called a "link". The use of `push_front` +// and `push_back` take the address of this node. Retrieving the data +// associated with the node requires finding the relative offset of the node +// of the parent structure. The parent type and field name are given to +// `iterator_*` procedures, or to the built-in `container_of` procedure. +// +// This data structure is two-pointers in size: +// 8 bytes on 32-bit platforms and 16 bytes on 64-bit platforms +List :: struct { + head: ^Node, + tail: ^Node, +} + + +Node :: struct { + next, prev: ^Node, +} + +push_front :: proc(list: ^List, node: ^Node) { + if list.head != nil { + list.head.prev = node + node.prev, node.next = nil, list.head + list.head = node + } else { + list.head, list.tail = node, node + node.prev, node.next = nil, nil + } +} + +push_back :: proc(list: ^List, node: ^Node) { + if list.tail != nil { + list.tail.next = node + node.prev, node.next = list.tail, nil + list.tail = node + } else { + list.head, list.tail = node, node + node.prev, node.next = nil, nil + } +} + +remove :: proc(list: ^List, node: ^Node) { + if node != nil { + if node.next != nil { + node.next.prev = node.prev + } + if node.prev != nil { + node.prev.next = node.next + } + if list.head == node { + list.head = node.next + } + if list.tail == node { + list.tail = node.prev + } + } +} + +remove_by_proc :: proc(list: ^List, to_erase: proc(^Node) -> bool) { + for node := list.head; node != nil; { + next := node.next + if to_erase(node) { + if node.next != nil { + node.next.prev = node.prev + } + if node.prev != nil { + node.prev.next = node.next + } + if list.head == node { + list.head = node.next + } + if list.tail == node { + list.tail = node.prev + } + } + node = next + } +} + + +is_empty :: proc(list: ^List) -> bool { + return list.head == nil +} + +pop_front :: proc(list: ^List) -> ^Node { + link := list.head + if link == nil { + return nil + } + if link.next != nil { + link.next.prev = link.prev + } + if link.prev != nil { + link.prev.next = link.next + } + if link == list.head { + list.head = link.next + } + if link == list.tail { + list.tail = link.prev + } + return link + +} +pop_back :: proc(list: ^List) -> ^Node { + link := list.tail + if link == nil { + return nil + } + if link.next != nil { + link.next.prev = link.prev + } + if link.prev != nil { + link.prev.next = link.next + } + if link == list.head { + list.head = link.next + } + if link == list.tail { + list.tail = link.prev + } + return link +} + + +Iterator :: struct($T: typeid) { + curr: ^Node, + offset: uintptr, +} + +iterator_head :: proc(list: List, $T: typeid, $field_name: string) -> Iterator(T) + where intrinsics.type_has_field(T, field_name), + intrinsics.type_field_type(T, field_name) == Node { + return {list.head, offset_of_by_string(T, field_name)} +} + +iterator_tail :: proc(list: List, $T: typeid, $field_name: string) -> Iterator(T) + where intrinsics.type_has_field(T, field_name), + intrinsics.type_field_type(T, field_name) == Node { + return {list.tail, offset_of_by_string(T, field_name)} +} + +iterator_from_node :: proc(node: ^Node, $T: typeid, $field_name: string) -> Iterator(T) + where intrinsics.type_has_field(T, field_name), + intrinsics.type_field_type(T, field_name) == Node { + return {node, offset_of_by_string(T, field_name)} +} + +iterate_next :: proc(it: ^Iterator($T)) -> (ptr: ^T, ok: bool) { + node := it.curr + if node == nil { + return nil, false + } + it.curr = node.next + + return (^T)(uintptr(node) - it.offset), true +} + +iterate_prev :: proc(it: ^Iterator($T)) -> (ptr: ^T, ok: bool) { + node := it.curr + if node == nil { + return nil, false + } + it.curr = node.prev + + return (^T)(uintptr(node) - it.offset), true +} \ No newline at end of file diff --git a/core/container/lru/lru_cache.odin b/core/container/lru/lru_cache.odin new file mode 100644 index 000000000..b59f29f0c --- /dev/null +++ b/core/container/lru/lru_cache.odin @@ -0,0 +1,201 @@ +package container_lru + +import "core:runtime" +import "core:intrinsics" +_ :: runtime +_ :: intrinsics + +Node :: struct($Key, $Value: typeid) where intrinsics.type_is_valid_map_key(Key) { + prev, next: ^Node(Key, Value), + key: Key, + value: Value, +} + +// Cache is an LRU cache. It automatically removes entries as new entries are +// added if the capacity is reached. Entries are removed based on how recently +// they were used where the oldest entries are removed first. +Cache :: struct($Key, $Value: typeid) where intrinsics.type_is_valid_map_key(Key) { + head: ^Node(Key, Value), + tail: ^Node(Key, Value), + + entries: map[Key]^Node(Key, Value), + + count: int, + capacity: int, + + node_allocator: runtime.Allocator, + + on_remove: proc(key: Key, value: Value, user_data: rawptr), + on_remove_user_data: rawptr, +} + +// init initializes a Cache +init :: proc(c: ^$C/Cache($Key, $Value), capacity: int, entries_allocator := context.allocator, node_allocator := context.allocator) { + c.entries.allocator = entries_allocator + c.node_allocator = node_allocator + c.capacity = capacity +} + +// destroy deinitializes a Cachem +destroy :: proc(c: ^$C/Cache($Key, $Value), call_on_remove: bool) { + clear(c, call_on_remove) + delete(c.entries) +} + +// clear the contents of a Cache +clear :: proc(c: ^$C/Cache($Key, $Value), call_on_remove: bool) { + for _, node in c.entries { + if call_on_remove { + _call_on_remove(c, node) + } + free(node, c.node_allocator) + } + runtime.clear(&c.entries) + c.head = nil + c.tail = nil + c.count = 0 +} + +// set the given key value pair. This operation updates the recent usage of the item. +set :: proc(c: ^$C/Cache($Key, $Value), key: Key, value: Value) -> runtime.Allocator_Error { + if e, ok := c.entries[key]; ok { + e.value = value + _pop_node(c, e) + _push_front_node(c, e) + return nil + } + + e : ^Node(Key, Value) = nil + assert(c.count <= c.capacity) + if c.count == c.capacity { + e = c.tail + _remove_node(c, e) + } + else { + c.count += 1 + e = new(Node(Key, Value), c.node_allocator) or_return + } + + e.key = key + e.value = value + _push_front_node(c, e) + c.entries[key] = e + + return nil +} + +// get a value from the cache from a given key. This operation updates the usage of the item. +get :: proc(c: ^$C/Cache($Key, $Value), key: Key) -> (value: Value, ok: bool) #optional_ok { + e: ^Node(Key, Value) + e, ok = c.entries[key] + if !ok { + return + } + _pop_node(c, e) + _push_front_node(c, e) + return e.value, true +} + +// get_ptr gets the pointer to a value the cache from a given key. This operation updates the usage of the item. +get_ptr :: proc(c: ^$C/Cache($Key, $Value), key: Key) -> (value: ^Value, ok: bool) #optional_ok { + e: ^Node(Key, Value) + e, ok = c.entries[key] + if !ok { + return + } + _pop_node(c, e) + _push_front_node(c, e) + return &e.value, true +} + +// peek gets the value from the cache from a given key without updating the recent usage. +peek :: proc(c: ^$C/Cache($Key, $Value), key: Key) -> (value: Value, ok: bool) #optional_ok { + e: ^Node(Key, Value) + e, ok = c.entries[key] + if !ok { + return + } + return e.value, true +} + +// exists checks for the existence of a value from a given key without updating the recent usage. +exists :: proc(c: ^$C/Cache($Key, $Value), key: Key) -> bool { + return key in c.entries +} + +// remove removes an item from the cache. +remove :: proc(c: ^$C/Cache($Key, $Value), key: Key) -> bool { + e, ok := c.entries[key] + if !ok { + return false + } + _remove_node(c, e) + free(node, c.node_allocator) + c.count -= 1 + return true +} + + +@(private) +_remove_node :: proc(c: ^$C/Cache($Key, $Value), node: ^Node(Key, Value)) { + if c.head == node { + c.head = node.next + } + if c.tail == node { + c.tail = node.prev + } + if node.prev != nil { + node.prev.next = node.next + } + if node.next != nil { + node.next.prev = node.prev + } + node.prev = nil + node.next = nil + + delete_key(&c.entries, node.key) + + _call_on_remove(c, node) +} + +@(private) +_call_on_remove :: proc(c: ^$C/Cache($Key, $Value), node: ^Node(Key, Value)) { + if c.on_remove != nil { + c.on_remove(node.key, node.value, c.on_remove_user_data) + } +} + +@(private) +_push_front_node :: proc(c: ^$C/Cache($Key, $Value), e: ^Node(Key, Value)) { + if c.head != nil { + e.next = c.head + e.next.prev = e + } + c.head = e + if c.tail == nil { + c.tail = e + } + e.prev = nil +} + +@(private) +_pop_node :: proc(c: ^$C/Cache($Key, $Value), e: ^Node(Key, Value)) { + if e == nil { + return + } + if c.head == e { + c.head = e.next + } + if c.tail == e { + c.tail = e.prev + } + if e.prev != nil { + e.prev.next = e.next + } + + if e.next != nil { + e.next.prev = e.prev + } + e.prev = nil + e.next = nil +} \ No newline at end of file diff --git a/core/container/queue/queue.odin b/core/container/queue/queue.odin index feca6934c..ae1ca9f62 100644 --- a/core/container/queue/queue.odin +++ b/core/container/queue/queue.odin @@ -2,6 +2,7 @@ package container_queue import "core:builtin" import "core:runtime" +_ :: runtime // Dynamically resizable double-ended queue/ring-buffer Queue :: struct($T: typeid) { @@ -68,6 +69,16 @@ get :: proc(q: ^$Q/Queue($T), #any_int i: int, loc := #caller_location) -> T { idx := (uint(i)+q.offset)%builtin.len(q.data) return q.data[idx] } + +front :: proc(q: ^$Q/Queue($T)) -> T { + return q.data[q.offset] +} + +back :: proc(q: ^$Q/Queue($T)) -> T { + idx := (q.offset+uint(q.len))%builtin.len(q.data) + return q.data[idx] +} + set :: proc(q: ^$Q/Queue($T), #any_int i: int, val: T, loc := #caller_location) { runtime.bounds_check_error_loc(loc, i, builtin.len(q.data)) diff --git a/core/container/small_array/small_array.odin b/core/container/small_array/small_array.odin index d09e0c81c..4dd16f30c 100644 --- a/core/container/small_array/small_array.odin +++ b/core/container/small_array/small_array.odin @@ -25,14 +25,14 @@ slice :: proc(a: ^$A/Small_Array($N, $T)) -> []T { } -get :: proc(a: $A/Small_Array($N, $T), index: int, loc := #caller_location) -> T { +get :: proc(a: $A/Small_Array($N, $T), index: int) -> T { return a.data[index] } -get_ptr :: proc(a: $A/Small_Array($N, $T), index: int, loc := #caller_location) -> ^T { +get_ptr :: proc(a: ^$A/Small_Array($N, $T), index: int) -> ^T { return &a.data[index] } -set :: proc(a: ^$A/Small_Array($N, $T), index: int, item: T, loc := #caller_location) { +set :: proc(a: ^$A/Small_Array($N, $T), index: int, item: T) { a.data[index] = item } @@ -86,7 +86,7 @@ pop_back_safe :: proc(a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) { return } -pop_front_safe :: proc(a: ^$A/Small_Array($N, $T)) -> (T, bool) { +pop_front_safe :: proc(a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) { if N > 0 && a.len > 0 { item = a.data[0] s := slice(a) @@ -114,4 +114,4 @@ push_back_elems :: proc(a: ^$A/Small_Array($N, $T), items: ..T) { append_elem :: push_back append_elems :: push_back_elems push :: proc{push_back, push_back_elems} -append :: proc{push_back, push_back_elems} \ No newline at end of file +append :: proc{push_back, push_back_elems} diff --git a/core/container/topological_sort/topological_sort.odin b/core/container/topological_sort/topological_sort.odin new file mode 100644 index 000000000..4b69930d5 --- /dev/null +++ b/core/container/topological_sort/topological_sort.odin @@ -0,0 +1,98 @@ +// The following is a generic O(V+E) topological sorter implementation. +// This is the fastest known method for topological sorting and Odin's +// map type is being used to accelerate lookups. +package container_topological_sort + +import "core:intrinsics" +import "core:runtime" +_ :: intrinsics +_ :: runtime + + +Relations :: struct($K: typeid) where intrinsics.type_is_valid_map_key(K) { + dependents: map[K]bool, + dependencies: int, +} + +Sorter :: struct(K: typeid) where intrinsics.type_is_valid_map_key(K) { + relations: map[K]Relations(K), + dependents_allocator: runtime.Allocator, +} + +@(private="file") +make_relations :: proc(sorter: ^$S/Sorter($K)) -> (r: Relations(K)) { + r.dependents.allocator = sorter.dependents_allocator + return +} + + +init :: proc(sorter: ^$S/Sorter($K)) { + sorter.relations = make(map[K]Relations(K)) + sorter.dependents_allocator = context.allocator +} + +destroy :: proc(sorter: ^$S/Sorter($K)) { + for _, v in &sorter.relations { + delete(v.dependents) + } + delete(sorter.relations) +} + +add_key :: proc(sorter: ^$S/Sorter($K), key: K) -> bool { + if key in sorter.relations { + return false + } + sorter.relations[key] = make_relations(sorter) + return true +} + +add_dependency :: proc(sorter: ^$S/Sorter($K), key, dependency: K) -> bool { + if key == dependency { + return false + } + + find := &sorter.relations[dependency] + if find == nil { + find = map_insert(&sorter.relations, dependency, make_relations(sorter)) + } + + if find.dependents[key] { + return true + } + find.dependents[key] = true + + find = &sorter.relations[key] + if find == nil { + find = map_insert(&sorter.relations, key, make_relations(sorter)) + } + + find.dependencies += 1 + + return true +} + +sort :: proc(sorter: ^$S/Sorter($K)) -> (sorted, cycled: [dynamic]K) { + relations := &sorter.relations + + for k, v in relations { + if v.dependencies == 0 { + append(&sorted, k) + } + } + + for root in &sorted do for k, _ in relations[root].dependents { + relation := &relations[k] + relation.dependencies -= 1 + if relation.dependencies == 0 { + append(&sorted, k) + } + } + + for k, v in relations { + if v.dependencies != 0 { + append(&cycled, k) + } + } + + return +} \ No newline at end of file diff --git a/core/crypto/_fiat/field_poly1305/field.odin b/core/crypto/_fiat/field_poly1305/field.odin index 4ed8acbff..ca458e079 100644 --- a/core/crypto/_fiat/field_poly1305/field.odin +++ b/core/crypto/_fiat/field_poly1305/field.odin @@ -22,7 +22,7 @@ fe_from_bytes :: #force_inline proc (out1: ^Tight_Field_Element, arg1: []byte, a assert(len(arg1) == 16) - when ODIN_ARCH == "i386" || ODIN_ARCH == "amd64" { + when ODIN_ARCH == .i386 || ODIN_ARCH == .amd64 { // While it may be unwise to do deserialization here on our // own when fiat-crypto provides equivalent functionality, // doing it this way provides a little under 3x performance diff --git a/core/crypto/blake/blake.odin b/core/crypto/blake/blake.odin index 81924ab1e..5fc0a02b9 100644 --- a/core/crypto/blake/blake.odin +++ b/core/crypto/blake/blake.odin @@ -44,7 +44,7 @@ hash_bytes_224 :: proc "contextless" (data: []byte) -> [DIGEST_SIZE_224]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_224 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_224(transmute([]byte)(data), hash); + hash_bytes_to_buffer_224(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_224 will hash the given input and write the @@ -123,7 +123,7 @@ hash_bytes_256 :: proc "contextless" (data: []byte) -> [DIGEST_SIZE_256]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_256 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_256(transmute([]byte)(data), hash); + hash_bytes_to_buffer_256(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_256 will hash the given input and write the @@ -202,7 +202,7 @@ hash_bytes_384 :: proc "contextless" (data: []byte) -> [DIGEST_SIZE_384]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_384 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_384(transmute([]byte)(data), hash); + hash_bytes_to_buffer_384(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_384 will hash the given input and write the @@ -281,7 +281,7 @@ hash_bytes_512 :: proc "contextless" (data: []byte) -> [DIGEST_SIZE_512]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_512 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_512(transmute([]byte)(data), hash); + hash_bytes_to_buffer_512(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_512 will hash the given input and write the diff --git a/core/crypto/blake2b/blake2b.odin b/core/crypto/blake2b/blake2b.odin index 6d4689b88..e75d74197 100644 --- a/core/crypto/blake2b/blake2b.odin +++ b/core/crypto/blake2b/blake2b.odin @@ -46,7 +46,7 @@ hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer(transmute([]byte)(data), hash); + hash_bytes_to_buffer(transmute([]byte)(data), hash) } // hash_bytes_to_buffer will hash the given input and write the diff --git a/core/crypto/blake2s/blake2s.odin b/core/crypto/blake2s/blake2s.odin index ad2e800fd..831335081 100644 --- a/core/crypto/blake2s/blake2s.odin +++ b/core/crypto/blake2s/blake2s.odin @@ -47,7 +47,7 @@ hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer(transmute([]byte)(data), hash); + hash_bytes_to_buffer(transmute([]byte)(data), hash) } // hash_bytes_to_buffer will hash the given input and write the diff --git a/core/crypto/chacha20/chacha20.odin b/core/crypto/chacha20/chacha20.odin index e32dacb2c..229949c22 100644 --- a/core/crypto/chacha20/chacha20.odin +++ b/core/crypto/chacha20/chacha20.odin @@ -346,7 +346,7 @@ _do_blocks :: proc (ctx: ^Context, dst, src: []byte, nr_blocks: int) { // Until dedicated assembly can be written leverage the fact that // the callers of this routine ensure that src/dst are valid. - when ODIN_ARCH == "i386" || ODIN_ARCH == "amd64" { + when ODIN_ARCH == .i386 || ODIN_ARCH == .amd64 { // util.PUT_U32_LE/util.U32_LE are not required on little-endian // systems that also happen to not be strict about aligned // memory access. diff --git a/core/crypto/gost/gost.odin b/core/crypto/gost/gost.odin index eed684f72..1d0274fae 100644 --- a/core/crypto/gost/gost.odin +++ b/core/crypto/gost/gost.odin @@ -41,7 +41,7 @@ hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer(transmute([]byte)(data), hash); + hash_bytes_to_buffer(transmute([]byte)(data), hash) } // hash_bytes_to_buffer will hash the given input and write the diff --git a/core/crypto/groestl/groestl.odin b/core/crypto/groestl/groestl.odin index 5434e31e0..8e5a2440d 100644 --- a/core/crypto/groestl/groestl.odin +++ b/core/crypto/groestl/groestl.odin @@ -44,7 +44,7 @@ hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_224 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_224(transmute([]byte)(data), hash); + hash_bytes_to_buffer_224(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_224 will hash the given input and write the @@ -123,7 +123,7 @@ hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_256 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_256(transmute([]byte)(data), hash); + hash_bytes_to_buffer_256(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_256 will hash the given input and write the @@ -202,7 +202,7 @@ hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_384 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_384(transmute([]byte)(data), hash); + hash_bytes_to_buffer_384(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_384 will hash the given input and write the @@ -281,7 +281,7 @@ hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_512 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_512(transmute([]byte)(data), hash); + hash_bytes_to_buffer_512(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_512 will hash the given input and write the diff --git a/core/crypto/haval/haval.odin b/core/crypto/haval/haval.odin index 442a348e9..811ecf95d 100644 --- a/core/crypto/haval/haval.odin +++ b/core/crypto/haval/haval.odin @@ -50,7 +50,7 @@ hash_bytes_128_3 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_128_3 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_128_3(transmute([]byte)(data), hash); + hash_bytes_to_buffer_128_3(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_128_3 will hash the given input and write the @@ -135,7 +135,7 @@ hash_bytes_128_4 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_128_4 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_128_4(transmute([]byte)(data), hash); + hash_bytes_to_buffer_128_4(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_128_4 will hash the given input and write the @@ -220,7 +220,7 @@ hash_bytes_128_5 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_128_5 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_128_5(transmute([]byte)(data), hash); + hash_bytes_to_buffer_128_5(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_128_5 will hash the given input and write the @@ -305,7 +305,7 @@ hash_bytes_160_3 :: proc(data: []byte) -> [DIGEST_SIZE_160]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_160_3 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_160_3(transmute([]byte)(data), hash); + hash_bytes_to_buffer_160_3(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_160_3 will hash the given input and write the @@ -390,7 +390,7 @@ hash_bytes_160_4 :: proc(data: []byte) -> [DIGEST_SIZE_160]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_160_4 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_160_4(transmute([]byte)(data), hash); + hash_bytes_to_buffer_160_4(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_160_4 will hash the given input and write the @@ -475,7 +475,7 @@ hash_bytes_160_5 :: proc(data: []byte) -> [DIGEST_SIZE_160]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_160_5 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_160_5(transmute([]byte)(data), hash); + hash_bytes_to_buffer_160_5(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_160_5 will hash the given input and write the @@ -560,7 +560,7 @@ hash_bytes_192_3 :: proc(data: []byte) -> [DIGEST_SIZE_192]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_192_3 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_192_3(transmute([]byte)(data), hash); + hash_bytes_to_buffer_192_3(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_192_3 will hash the given input and write the @@ -645,7 +645,7 @@ hash_bytes_192_4 :: proc(data: []byte) -> [DIGEST_SIZE_192]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_192_4 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_192_4(transmute([]byte)(data), hash); + hash_bytes_to_buffer_192_4(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_192_4 will hash the given input and write the @@ -730,7 +730,7 @@ hash_bytes_192_5 :: proc(data: []byte) -> [DIGEST_SIZE_192]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_192_5 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_192_5(transmute([]byte)(data), hash); + hash_bytes_to_buffer_192_5(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_192_5 will hash the given input and write the @@ -815,7 +815,7 @@ hash_bytes_224_3 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_224_3 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_224_3(transmute([]byte)(data), hash); + hash_bytes_to_buffer_224_3(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_224_3 will hash the given input and write the @@ -900,7 +900,7 @@ hash_bytes_224_4 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_224_4 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_224_4(transmute([]byte)(data), hash); + hash_bytes_to_buffer_224_4(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_224_4 will hash the given input and write the @@ -985,7 +985,7 @@ hash_bytes_224_5 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_224_5 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_224_5(transmute([]byte)(data), hash); + hash_bytes_to_buffer_224_5(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_224_5 will hash the given input and write the @@ -1070,7 +1070,7 @@ hash_bytes_256_3 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_256_3 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_256_3(transmute([]byte)(data), hash); + hash_bytes_to_buffer_256_3(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_256_3 will hash the given input and write the @@ -1155,7 +1155,7 @@ hash_bytes_256_4 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_256_4 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_256_4(transmute([]byte)(data), hash); + hash_bytes_to_buffer_256_4(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_256_4 will hash the given input and write the @@ -1240,7 +1240,7 @@ hash_bytes_256_5 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_256_5 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_256_5(transmute([]byte)(data), hash); + hash_bytes_to_buffer_256_5(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_256_5 will hash the given input and write the diff --git a/core/crypto/jh/jh.odin b/core/crypto/jh/jh.odin index 4ebc0e5cb..42c2d1d34 100644 --- a/core/crypto/jh/jh.odin +++ b/core/crypto/jh/jh.odin @@ -44,7 +44,7 @@ hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_224 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_224(transmute([]byte)(data), hash); + hash_bytes_to_buffer_224(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_224 will hash the given input and write the @@ -123,7 +123,7 @@ hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_256 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_256(transmute([]byte)(data), hash); + hash_bytes_to_buffer_256(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_256 will hash the given input and write the @@ -202,7 +202,7 @@ hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_384 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_384(transmute([]byte)(data), hash); + hash_bytes_to_buffer_384(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_384 will hash the given input and write the @@ -281,7 +281,7 @@ hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_512 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_512(transmute([]byte)(data), hash); + hash_bytes_to_buffer_512(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_512 will hash the given input and write the diff --git a/core/crypto/keccak/keccak.odin b/core/crypto/keccak/keccak.odin index f5d4826b1..aeb5aac52 100644 --- a/core/crypto/keccak/keccak.odin +++ b/core/crypto/keccak/keccak.odin @@ -49,7 +49,7 @@ hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_224 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_224(transmute([]byte)(data), hash); + hash_bytes_to_buffer_224(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_224 will hash the given input and write the @@ -131,7 +131,7 @@ hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_256 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_256(transmute([]byte)(data), hash); + hash_bytes_to_buffer_256(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_256 will hash the given input and write the @@ -213,7 +213,7 @@ hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_384 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_384(transmute([]byte)(data), hash); + hash_bytes_to_buffer_384(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_384 will hash the given input and write the @@ -295,7 +295,7 @@ hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_512 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_512(transmute([]byte)(data), hash); + hash_bytes_to_buffer_512(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_512 will hash the given input and write the diff --git a/core/crypto/md2/md2.odin b/core/crypto/md2/md2.odin index 102c1b8b4..711e6e9f6 100644 --- a/core/crypto/md2/md2.odin +++ b/core/crypto/md2/md2.odin @@ -40,7 +40,7 @@ hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer(transmute([]byte)(data), hash); + hash_bytes_to_buffer(transmute([]byte)(data), hash) } // hash_bytes_to_buffer will hash the given input and write the diff --git a/core/crypto/md4/md4.odin b/core/crypto/md4/md4.odin index d944daa1d..b2651225b 100644 --- a/core/crypto/md4/md4.odin +++ b/core/crypto/md4/md4.odin @@ -44,7 +44,7 @@ hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer(transmute([]byte)(data), hash); + hash_bytes_to_buffer(transmute([]byte)(data), hash) } // hash_bytes_to_buffer will hash the given input and write the diff --git a/core/crypto/md5/md5.odin b/core/crypto/md5/md5.odin index 9129e6384..30a556102 100644 --- a/core/crypto/md5/md5.odin +++ b/core/crypto/md5/md5.odin @@ -43,7 +43,7 @@ hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer(transmute([]byte)(data), hash); + hash_bytes_to_buffer(transmute([]byte)(data), hash) } // hash_bytes_to_buffer will hash the given input and write the diff --git a/core/crypto/rand_generic.odin b/core/crypto/rand_generic.odin index 98890b5b1..52abfe4d7 100644 --- a/core/crypto/rand_generic.odin +++ b/core/crypto/rand_generic.odin @@ -1,6 +1,6 @@ package crypto -when ODIN_OS != "linux" { +when ODIN_OS != .Linux && ODIN_OS != .OpenBSD && ODIN_OS != .Windows { _rand_bytes :: proc (dst: []byte) { unimplemented("crypto: rand_bytes not supported on this OS") } diff --git a/core/crypto/rand_openbsd.odin b/core/crypto/rand_openbsd.odin new file mode 100644 index 000000000..bae97e8f0 --- /dev/null +++ b/core/crypto/rand_openbsd.odin @@ -0,0 +1,12 @@ +package crypto + +import "core:c" + +foreign import libc "system:c" +foreign libc { + arc4random_buf :: proc "c" (buf: rawptr, nbytes: c.size_t) --- +} + +_rand_bytes :: proc (dst: []byte) { + arc4random_buf(raw_data(dst), len(dst)) +} diff --git a/core/crypto/rand_windows.odin b/core/crypto/rand_windows.odin new file mode 100644 index 000000000..53b58c776 --- /dev/null +++ b/core/crypto/rand_windows.odin @@ -0,0 +1,23 @@ +package crypto + +import win32 "core:sys/windows" +import "core:os" +import "core:fmt" + +_rand_bytes :: proc(dst: []byte) { + ret := (os.Errno)(win32.BCryptGenRandom(nil, raw_data(dst), u32(len(dst)), win32.BCRYPT_USE_SYSTEM_PREFERRED_RNG)) + if ret != os.ERROR_NONE { + switch ret { + case os.ERROR_INVALID_HANDLE: + // The handle to the first parameter is invalid. + // This should not happen here, since we explicitly pass nil to it + panic("crypto: BCryptGenRandom Invalid handle for hAlgorithm") + case os.ERROR_INVALID_PARAMETER: + // One of the parameters was invalid + panic("crypto: BCryptGenRandom Invalid parameter") + case: + // Unknown error + panic(fmt.tprintf("crypto: BCryptGenRandom failed: %d\n", ret)) + } + } +} diff --git a/core/crypto/ripemd/ripemd.odin b/core/crypto/ripemd/ripemd.odin index c475c4803..702d29037 100644 --- a/core/crypto/ripemd/ripemd.odin +++ b/core/crypto/ripemd/ripemd.odin @@ -45,7 +45,7 @@ hash_bytes_128 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_128 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_128(transmute([]byte)(data), hash); + hash_bytes_to_buffer_128(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_128 will hash the given input and write the @@ -121,7 +121,7 @@ hash_bytes_160 :: proc(data: []byte) -> [DIGEST_SIZE_160]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_160 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_160(transmute([]byte)(data), hash); + hash_bytes_to_buffer_160(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_160 will hash the given input and write the @@ -197,7 +197,7 @@ hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_256 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_256(transmute([]byte)(data), hash); + hash_bytes_to_buffer_256(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_256 will hash the given input and write the @@ -273,7 +273,7 @@ hash_bytes_320 :: proc(data: []byte) -> [DIGEST_SIZE_320]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_320 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_320(transmute([]byte)(data), hash); + hash_bytes_to_buffer_320(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_320 will hash the given input and write the diff --git a/core/crypto/sha1/sha1.odin b/core/crypto/sha1/sha1.odin index e8df3c7f6..b0dbd7dc8 100644 --- a/core/crypto/sha1/sha1.odin +++ b/core/crypto/sha1/sha1.odin @@ -43,7 +43,7 @@ hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer(transmute([]byte)(data), hash); + hash_bytes_to_buffer(transmute([]byte)(data), hash) } // hash_bytes_to_buffer will hash the given input and write the diff --git a/core/crypto/sha2/sha2.odin b/core/crypto/sha2/sha2.odin index 2178b70b5..9792a4cb8 100644 --- a/core/crypto/sha2/sha2.odin +++ b/core/crypto/sha2/sha2.odin @@ -48,7 +48,7 @@ hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_224 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_224(transmute([]byte)(data), hash); + hash_bytes_to_buffer_224(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_224 will hash the given input and write the @@ -127,7 +127,7 @@ hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_256 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_256(transmute([]byte)(data), hash); + hash_bytes_to_buffer_256(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_256 will hash the given input and write the @@ -206,7 +206,7 @@ hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_384 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_384(transmute([]byte)(data), hash); + hash_bytes_to_buffer_384(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_384 will hash the given input and write the @@ -285,7 +285,7 @@ hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_512 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_512(transmute([]byte)(data), hash); + hash_bytes_to_buffer_512(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_512 will hash the given input and write the @@ -419,8 +419,10 @@ update :: proc(ctx: ^$T, data: []byte) { sha2_transf(ctx, shifted_message, block_nb) rem_len = new_len % CURR_BLOCK_SIZE - when T == Sha256_Context {copy(ctx.block[:], shifted_message[block_nb << 6:rem_len])} - else when T == Sha512_Context {copy(ctx.block[:], shifted_message[block_nb << 7:rem_len])} + if rem_len > 0 { + when T == Sha256_Context {copy(ctx.block[:], shifted_message[block_nb << 6:rem_len])} + else when T == Sha512_Context {copy(ctx.block[:], shifted_message[block_nb << 7:rem_len])} + } ctx.length = rem_len when T == Sha256_Context {ctx.tot_len += (block_nb + 1) << 6} diff --git a/core/crypto/sha3/sha3.odin b/core/crypto/sha3/sha3.odin index 2eceeaff6..1202f8b23 100644 --- a/core/crypto/sha3/sha3.odin +++ b/core/crypto/sha3/sha3.odin @@ -47,7 +47,7 @@ hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_224 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_224(transmute([]byte)(data), hash); + hash_bytes_to_buffer_224(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_224 will hash the given input and write the @@ -126,7 +126,7 @@ hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_256 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_256(transmute([]byte)(data), hash); + hash_bytes_to_buffer_256(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_256 will hash the given input and write the @@ -205,7 +205,7 @@ hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_384 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_384(transmute([]byte)(data), hash); + hash_bytes_to_buffer_384(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_384 will hash the given input and write the @@ -284,7 +284,7 @@ hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_512 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_512(transmute([]byte)(data), hash); + hash_bytes_to_buffer_512(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_512 will hash the given input and write the diff --git a/core/crypto/shake/shake.odin b/core/crypto/shake/shake.odin index 9fdc3ebf1..525dcfbd3 100644 --- a/core/crypto/shake/shake.odin +++ b/core/crypto/shake/shake.odin @@ -46,7 +46,7 @@ hash_bytes_128 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_128 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_128(transmute([]byte)(data), hash); + hash_bytes_to_buffer_128(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_128 will hash the given input and write the @@ -128,7 +128,7 @@ hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_256 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_256(transmute([]byte)(data), hash); + hash_bytes_to_buffer_256(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_256 will hash the given input and write the diff --git a/core/crypto/siphash/siphash.odin b/core/crypto/siphash/siphash.odin new file mode 100644 index 000000000..a6c75f315 --- /dev/null +++ b/core/crypto/siphash/siphash.odin @@ -0,0 +1,335 @@ +package siphash + +/* + Copyright 2022 zhibog + Made available under the BSD-3 license. + + List of contributors: + zhibog: Initial implementation. + + Implementation of the SipHash hashing algorithm, as defined at and + + Use the specific procedures for a certain setup. The generic procdedures will default to Siphash 2-4 +*/ + +import "core:crypto" +import "core:crypto/util" + +/* + High level API +*/ + +KEY_SIZE :: 16 +DIGEST_SIZE :: 8 + +// sum_string_1_3 will hash the given message with the key and return +// the computed hash as a u64 +sum_string_1_3 :: proc(msg, key: string) -> u64 { + return sum_bytes_1_3(transmute([]byte)(msg), transmute([]byte)(key)) +} + +// sum_bytes_1_3 will hash the given message with the key and return +// the computed hash as a u64 +sum_bytes_1_3 :: proc (msg, key: []byte) -> u64 { + ctx: Context + hash: u64 + init(&ctx, key, 1, 3) + update(&ctx, msg) + final(&ctx, &hash) + return hash +} + +// sum_string_to_buffer_1_3 will hash the given message with the key and write +// the computed hash into the provided destination buffer +sum_string_to_buffer_1_3 :: proc(msg, key: string, dst: []byte) { + sum_bytes_to_buffer_1_3(transmute([]byte)(msg), transmute([]byte)(key), dst) +} + +// sum_bytes_to_buffer_1_3 will hash the given message with the key and write +// the computed hash into the provided destination buffer +sum_bytes_to_buffer_1_3 :: proc(msg, key, dst: []byte) { + assert(len(dst) >= DIGEST_SIZE, "crypto/siphash: Destination buffer needs to be at least of size 8") + hash := sum_bytes_1_3(msg, key) + _collect_output(dst[:], hash) +} + +sum_1_3 :: proc { + sum_string_1_3, + sum_bytes_1_3, + sum_string_to_buffer_1_3, + sum_bytes_to_buffer_1_3, +} + +// verify_u64_1_3 will check if the supplied tag matches with the output you +// will get from the provided message and key +verify_u64_1_3 :: proc (tag: u64 msg, key: []byte) -> bool { + return sum_bytes_1_3(msg, key) == tag +} + +// verify_bytes will check if the supplied tag matches with the output you +// will get from the provided message and key +verify_bytes_1_3 :: proc (tag, msg, key: []byte) -> bool { + derived_tag: [8]byte + sum_bytes_to_buffer_1_3(msg, key, derived_tag[:]) + return crypto.compare_constant_time(derived_tag[:], tag) == 1 +} + +verify_1_3 :: proc { + verify_bytes_1_3, + verify_u64_1_3, +} + +// sum_string_2_4 will hash the given message with the key and return +// the computed hash as a u64 +sum_string_2_4 :: proc(msg, key: string) -> u64 { + return sum_bytes_2_4(transmute([]byte)(msg), transmute([]byte)(key)) +} + +// sum_bytes_2_4 will hash the given message with the key and return +// the computed hash as a u64 +sum_bytes_2_4 :: proc (msg, key: []byte) -> u64 { + ctx: Context + hash: u64 + init(&ctx, key, 2, 4) + update(&ctx, msg) + final(&ctx, &hash) + return hash +} + +// sum_string_to_buffer_2_4 will hash the given message with the key and write +// the computed hash into the provided destination buffer +sum_string_to_buffer_2_4 :: proc(msg, key: string, dst: []byte) { + sum_bytes_to_buffer_2_4(transmute([]byte)(msg), transmute([]byte)(key), dst) +} + +// sum_bytes_to_buffer_2_4 will hash the given message with the key and write +// the computed hash into the provided destination buffer +sum_bytes_to_buffer_2_4 :: proc(msg, key, dst: []byte) { + assert(len(dst) >= DIGEST_SIZE, "crypto/siphash: Destination buffer needs to be at least of size 8") + hash := sum_bytes_2_4(msg, key) + _collect_output(dst[:], hash) +} + +sum_2_4 :: proc { + sum_string_2_4, + sum_bytes_2_4, + sum_string_to_buffer_2_4, + sum_bytes_to_buffer_2_4, +} + +sum_string :: sum_string_2_4 +sum_bytes :: sum_bytes_2_4 +sum_string_to_buffer :: sum_string_to_buffer_2_4 +sum_bytes_to_buffer :: sum_bytes_to_buffer_2_4 +sum :: proc { + sum_string, + sum_bytes, + sum_string_to_buffer, + sum_bytes_to_buffer, +} + +// verify_u64_2_4 will check if the supplied tag matches with the output you +// will get from the provided message and key +verify_u64_2_4 :: proc (tag: u64 msg, key: []byte) -> bool { + return sum_bytes_2_4(msg, key) == tag +} + +// verify_bytes will check if the supplied tag matches with the output you +// will get from the provided message and key +verify_bytes_2_4 :: proc (tag, msg, key: []byte) -> bool { + derived_tag: [8]byte + sum_bytes_to_buffer_2_4(msg, key, derived_tag[:]) + return crypto.compare_constant_time(derived_tag[:], tag) == 1 +} + +verify_2_4 :: proc { + verify_bytes_2_4, + verify_u64_2_4, +} + +verify_bytes :: verify_bytes_2_4 +verify_u64 :: verify_u64_2_4 +verify :: proc { + verify_bytes, + verify_u64, +} + +// sum_string_4_8 will hash the given message with the key and return +// the computed hash as a u64 +sum_string_4_8 :: proc(msg, key: string) -> u64 { + return sum_bytes_4_8(transmute([]byte)(msg), transmute([]byte)(key)) +} + +// sum_bytes_4_8 will hash the given message with the key and return +// the computed hash as a u64 +sum_bytes_4_8 :: proc (msg, key: []byte) -> u64 { + ctx: Context + hash: u64 + init(&ctx, key, 4, 8) + update(&ctx, msg) + final(&ctx, &hash) + return hash +} + +// sum_string_to_buffer_4_8 will hash the given message with the key and write +// the computed hash into the provided destination buffer +sum_string_to_buffer_4_8 :: proc(msg, key: string, dst: []byte) { + sum_bytes_to_buffer_4_8(transmute([]byte)(msg), transmute([]byte)(key), dst) +} + +// sum_bytes_to_buffer_4_8 will hash the given message with the key and write +// the computed hash into the provided destination buffer +sum_bytes_to_buffer_4_8 :: proc(msg, key, dst: []byte) { + assert(len(dst) >= DIGEST_SIZE, "crypto/siphash: Destination buffer needs to be at least of size 8") + hash := sum_bytes_4_8(msg, key) + _collect_output(dst[:], hash) +} + +sum_4_8 :: proc { + sum_string_4_8, + sum_bytes_4_8, + sum_string_to_buffer_4_8, + sum_bytes_to_buffer_4_8, +} + +// verify_u64_4_8 will check if the supplied tag matches with the output you +// will get from the provided message and key +verify_u64_4_8 :: proc (tag: u64 msg, key: []byte) -> bool { + return sum_bytes_4_8(msg, key) == tag +} + +// verify_bytes will check if the supplied tag matches with the output you +// will get from the provided message and key +verify_bytes_4_8 :: proc (tag, msg, key: []byte) -> bool { + derived_tag: [8]byte + sum_bytes_to_buffer_4_8(msg, key, derived_tag[:]) + return crypto.compare_constant_time(derived_tag[:], tag) == 1 +} + +verify_4_8 :: proc { + verify_bytes_4_8, + verify_u64_4_8, +} + +/* + Low level API +*/ + +init :: proc(ctx: ^Context, key: []byte, c_rounds, d_rounds: int) { + assert(len(key) == KEY_SIZE, "crypto/siphash: Invalid key size, want 16") + ctx.c_rounds = c_rounds + ctx.d_rounds = d_rounds + is_valid_setting := (ctx.c_rounds == 1 && ctx.d_rounds == 3) || + (ctx.c_rounds == 2 && ctx.d_rounds == 4) || + (ctx.c_rounds == 4 && ctx.d_rounds == 8) + assert(is_valid_setting, "crypto/siphash: Incorrect rounds set up. Valid pairs are (1,3), (2,4) and (4,8)") + ctx.k0 = util.U64_LE(key[:8]) + ctx.k1 = util.U64_LE(key[8:]) + ctx.v0 = 0x736f6d6570736575 ~ ctx.k0 + ctx.v1 = 0x646f72616e646f6d ~ ctx.k1 + ctx.v2 = 0x6c7967656e657261 ~ ctx.k0 + ctx.v3 = 0x7465646279746573 ~ ctx.k1 + ctx.is_initialized = true +} + +update :: proc(ctx: ^Context, data: []byte) { + assert(ctx.is_initialized, "crypto/siphash: Context is not initalized") + ctx.last_block = len(data) / 8 * 8 + ctx.buf = data + i := 0 + m: u64 + for i < ctx.last_block { + m = u64(ctx.buf[i] & 0xff) + i += 1 + + for r in u64(1)..<8 { + m |= u64(ctx.buf[i] & 0xff) << (r * 8) + i += 1 + } + + ctx.v3 ~= m + for _ in 0..= ctx.last_block; i -= 1 { + m <<= 8 + m |= u64(ctx.buf[i] & 0xff) + } + m |= u64(len(ctx.buf) << 56) + + ctx.v3 ~= m + + for _ in 0.. byte { + return byte(into >> (((~byte_num) & (size_of(u64) - 1)) << 3)) +} + +_collect_output :: #force_inline proc "contextless" (dst: []byte, hash: u64) { + dst[0] = _get_byte(7, hash) + dst[1] = _get_byte(6, hash) + dst[2] = _get_byte(5, hash) + dst[3] = _get_byte(4, hash) + dst[4] = _get_byte(3, hash) + dst[5] = _get_byte(2, hash) + dst[6] = _get_byte(1, hash) + dst[7] = _get_byte(0, hash) +} + +_compress :: #force_inline proc "contextless" (ctx: ^Context) { + ctx.v0 += ctx.v1 + ctx.v1 = util.ROTL64(ctx.v1, 13) + ctx.v1 ~= ctx.v0 + ctx.v0 = util.ROTL64(ctx.v0, 32) + ctx.v2 += ctx.v3 + ctx.v3 = util.ROTL64(ctx.v3, 16) + ctx.v3 ~= ctx.v2 + ctx.v0 += ctx.v3 + ctx.v3 = util.ROTL64(ctx.v3, 21) + ctx.v3 ~= ctx.v0 + ctx.v2 += ctx.v1 + ctx.v1 = util.ROTL64(ctx.v1, 17) + ctx.v1 ~= ctx.v2 + ctx.v2 = util.ROTL64(ctx.v2, 32) +} diff --git a/core/crypto/sm3/sm3.odin b/core/crypto/sm3/sm3.odin index e72973e33..74c9f22e2 100644 --- a/core/crypto/sm3/sm3.odin +++ b/core/crypto/sm3/sm3.odin @@ -42,7 +42,7 @@ hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer(transmute([]byte)(data), hash); + hash_bytes_to_buffer(transmute([]byte)(data), hash) } // hash_bytes_to_buffer will hash the given input and write the diff --git a/core/crypto/streebog/streebog.odin b/core/crypto/streebog/streebog.odin index deb71120d..f85977cba 100644 --- a/core/crypto/streebog/streebog.odin +++ b/core/crypto/streebog/streebog.odin @@ -44,7 +44,7 @@ hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_256 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_256(transmute([]byte)(data), hash); + hash_bytes_to_buffer_256(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_256 will hash the given input and write the @@ -122,7 +122,7 @@ hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_512 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_512(transmute([]byte)(data), hash); + hash_bytes_to_buffer_512(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_512 will hash the given input and write the diff --git a/core/crypto/tiger/tiger.odin b/core/crypto/tiger/tiger.odin index 4ea80c66c..cf6159fad 100644 --- a/core/crypto/tiger/tiger.odin +++ b/core/crypto/tiger/tiger.odin @@ -45,7 +45,7 @@ hash_bytes_128 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_128 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_128(transmute([]byte)(data), hash); + hash_bytes_to_buffer_128(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_128 will hash the given input and write the @@ -124,7 +124,7 @@ hash_bytes_160 :: proc(data: []byte) -> [DIGEST_SIZE_160]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_160 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_160(transmute([]byte)(data), hash); + hash_bytes_to_buffer_160(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_160 will hash the given input and write the @@ -203,7 +203,7 @@ hash_bytes_192 :: proc(data: []byte) -> [DIGEST_SIZE_192]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_192 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_192(transmute([]byte)(data), hash); + hash_bytes_to_buffer_192(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_192 will hash the given input and write the diff --git a/core/crypto/tiger2/tiger2.odin b/core/crypto/tiger2/tiger2.odin index 84333f344..e8f2c4edb 100644 --- a/core/crypto/tiger2/tiger2.odin +++ b/core/crypto/tiger2/tiger2.odin @@ -45,7 +45,7 @@ hash_bytes_128 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_128 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_128(transmute([]byte)(data), hash); + hash_bytes_to_buffer_128(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_128 will hash the given input and write the @@ -124,7 +124,7 @@ hash_bytes_160 :: proc(data: []byte) -> [DIGEST_SIZE_160]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_160 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_160(transmute([]byte)(data), hash); + hash_bytes_to_buffer_160(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_160 will hash the given input and write the @@ -203,7 +203,7 @@ hash_bytes_192 :: proc(data: []byte) -> [DIGEST_SIZE_192]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_192 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_192(transmute([]byte)(data), hash); + hash_bytes_to_buffer_192(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_192 will hash the given input and write the diff --git a/core/crypto/whirlpool/whirlpool.odin b/core/crypto/whirlpool/whirlpool.odin index 255f57bc2..0cfef7c6b 100644 --- a/core/crypto/whirlpool/whirlpool.odin +++ b/core/crypto/whirlpool/whirlpool.odin @@ -42,7 +42,7 @@ hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer(transmute([]byte)(data), hash); + hash_bytes_to_buffer(transmute([]byte)(data), hash) } // hash_bytes_to_buffer will hash the given input and write the diff --git a/core/dynlib/lib.odin b/core/dynlib/lib.odin index 00655d650..a6c857ee4 100644 --- a/core/dynlib/lib.odin +++ b/core/dynlib/lib.odin @@ -1,3 +1,15 @@ package dynlib Library :: distinct rawptr + +load_library :: proc(path: string, global_symbols := false) -> (Library, bool) { + return _load_library(path, global_symbols) +} + +unload_library :: proc(library: Library) -> bool { + return _unload_library(library) +} + +symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) #optional_ok { + return _symbol_address(library, symbol) +} diff --git a/core/dynlib/lib_unix.odin b/core/dynlib/lib_unix.odin index bb8affb79..b0cc37e99 100644 --- a/core/dynlib/lib_unix.odin +++ b/core/dynlib/lib_unix.odin @@ -1,23 +1,24 @@ -// +build linux, darwin, freebsd +//+build linux, darwin, freebsd, openbsd +//+private package dynlib import "core:os" -load_library :: proc(path: string, global_symbols := false) -> (Library, bool) { - flags := os.RTLD_NOW - if global_symbols { - flags |= os.RTLD_GLOBAL - } - lib := os.dlopen(path, flags) - return Library(lib), lib != nil +_load_library :: proc(path: string, global_symbols := false) -> (Library, bool) { + flags := os.RTLD_NOW + if global_symbols { + flags |= os.RTLD_GLOBAL + } + lib := os.dlopen(path, flags) + return Library(lib), lib != nil } -unload_library :: proc(library: Library) { - os.dlclose(rawptr(library)) +_unload_library :: proc(library: Library) -> bool { + return os.dlclose(rawptr(library)) } -symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) { - ptr = os.dlsym(rawptr(library), symbol) - found = ptr != nil - return +_symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) { + ptr = os.dlsym(rawptr(library), symbol) + found = ptr != nil + return } diff --git a/core/dynlib/lib_windows.odin b/core/dynlib/lib_windows.odin index b9aae9cf1..d48e43ca2 100644 --- a/core/dynlib/lib_windows.odin +++ b/core/dynlib/lib_windows.odin @@ -1,10 +1,11 @@ -// +build windows +//+build windows +//+private package dynlib import win32 "core:sys/windows" import "core:strings" -load_library :: proc(path: string, global_symbols := false) -> (Library, bool) { +_load_library :: proc(path: string, global_symbols := false) -> (Library, bool) { // NOTE(bill): 'global_symbols' is here only for consistency with POSIX which has RTLD_GLOBAL wide_path := win32.utf8_to_wstring(path, context.temp_allocator) @@ -12,12 +13,12 @@ load_library :: proc(path: string, global_symbols := false) -> (Library, bool) { return handle, handle != nil } -unload_library :: proc(library: Library) -> bool { +_unload_library :: proc(library: Library) -> bool { ok := win32.FreeLibrary(cast(win32.HMODULE)library) return bool(ok) } -symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) { +_symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) { c_str := strings.clone_to_cstring(symbol, context.temp_allocator) ptr = win32.GetProcAddress(cast(win32.HMODULE)library, c_str) found = ptr != nil diff --git a/core/encoding/csv/reader.odin b/core/encoding/csv/reader.odin index aecb73d7b..f8f1d4051 100644 --- a/core/encoding/csv/reader.odin +++ b/core/encoding/csv/reader.odin @@ -34,6 +34,10 @@ Reader :: struct { // If lazy_quotes is true, a quote may appear in an unquoted field and a non-doubled quote may appear in a quoted field lazy_quotes: bool, + // multiline_fields, when set to true, will treat a field starting with a " as a multiline string + // therefore, instead of reading until the next \n, it'll read until the next " + multiline_fields: bool, + // reuse_record controls whether calls to 'read' may return a slice using the backing buffer // for performance // By default, each call to 'read' returns a newly allocated slice @@ -194,32 +198,72 @@ is_valid_delim :: proc(r: rune) -> bool { @private _read_record :: proc(r: ^Reader, dst: ^[dynamic]string, allocator := context.allocator) -> ([]string, Error) { read_line :: proc(r: ^Reader) -> ([]byte, io.Error) { - line, err := bufio.reader_read_slice(&r.r, '\n') - if err == .Buffer_Full { - clear(&r.raw_buffer) - append(&r.raw_buffer, ..line) - for err == .Buffer_Full { - line, err = bufio.reader_read_slice(&r.r, '\n') + if !r.multiline_fields { + line, err := bufio.reader_read_slice(&r.r, '\n') + if err == .Buffer_Full { + clear(&r.raw_buffer) append(&r.raw_buffer, ..line) + for err == .Buffer_Full { + line, err = bufio.reader_read_slice(&r.r, '\n') + append(&r.raw_buffer, ..line) + } + line = r.raw_buffer[:] } - line = r.raw_buffer[:] - } - if len(line) > 0 && err == .EOF { - err = nil - if line[len(line)-1] == '\r' { - line = line[:len(line)-1] + if len(line) > 0 && err == .EOF { + err = nil + if line[len(line)-1] == '\r' { + line = line[:len(line)-1] + } } - } - r.line_count += 1 + r.line_count += 1 - // normalize \r\n to \n - n := len(line) - for n >= 2 && string(line[n-2:]) == "\r\n" { - line[n-2] = '\n' - line = line[:n-1] - } + // normalize \r\n to \n + n := len(line) + for n >= 2 && string(line[n-2:]) == "\r\n" { + line[n-2] = '\n' + line = line[:n-1] + } + return line, err - return line, err + } else { + // Reading a "line" that can possibly contain multiline fields. + // Unfortunately, this means we need to read a character at a time. + + err: io.Error + cur: rune + is_quoted: bool + + field_length := 0 + + clear(&r.raw_buffer) + + read_loop: for err == .None { + cur, _, err = bufio.reader_read_rune(&r.r) + + if err != .None { break read_loop } + + switch cur { + case '"': + is_quoted = field_length == 0 + field_length += 1 + + case '\n', '\r': + if !is_quoted { break read_loop } + + case r.comma: + field_length = 0 + + case: + field_length += 1 + } + + rune_buf, rune_len := utf8.encode_rune(cur) + append(&r.raw_buffer, ..rune_buf[:rune_len]) + } + + return r.raw_buffer[:], err + } + unreachable() } length_newline :: proc(b: []byte) -> int { diff --git a/core/encoding/endian/doc.odin b/core/encoding/endian/doc.odin new file mode 100644 index 000000000..754ffa583 --- /dev/null +++ b/core/encoding/endian/doc.odin @@ -0,0 +1,23 @@ +/* + Package endian implements sa simple translation between bytes and numbers with + specific endian encodings. + + buf: [100]u8 + put_u16(buf[:], .Little, 16) or_return + + You may ask yourself, why isn't `byte_order` platform Endianness by default, so we can write: + put_u16(buf[:], 16) or_return + + The answer is that very few file formats are written in native/platform endianness. Most of them specify the endianness of + each of their fields, or use a header field which specifies it for the entire file. + + e.g. a file which specifies it at the top for all fields could do this: + file_order := .Little if buf[0] == 0 else .Big + field := get_u16(buf[1:], file_order) or_return + + If on the other hand a field is *always* Big-Endian, you're wise to explicitly state it for the benefit of the reader, + be that your future self or someone else. + + field := get_u16(buf[:], .Big) or_return +*/ +package encoding_endian diff --git a/core/encoding/endian/endian.odin b/core/encoding/endian/endian.odin new file mode 100644 index 000000000..08bde3139 --- /dev/null +++ b/core/encoding/endian/endian.odin @@ -0,0 +1,153 @@ +package encoding_endian + +Byte_Order :: enum u8 { + Little, + Big, +} + +PLATFORM_BYTE_ORDER :: Byte_Order.Little when ODIN_ENDIAN == .Little else Byte_Order.Big + +get_u16 :: proc(b: []byte, order: Byte_Order) -> (v: u16, ok: bool) { + if len(b) < 2 { + return 0, false + } + #no_bounds_check if order == .Little { + v = u16(b[0]) | u16(b[1])<<8 + } else { + v = u16(b[1]) | u16(b[0])<<8 + } + return v, true +} +get_u32 :: proc(b: []byte, order: Byte_Order) -> (v: u32, ok: bool) { + if len(b) < 4 { + return 0, false + } + #no_bounds_check if order == .Little { + v = u32(b[0]) | u32(b[1])<<8 | u32(b[2])<<16 | u32(b[3])<<24 + } else { + v = u32(b[3]) | u32(b[2])<<8 | u32(b[1])<<16 | u32(b[0])<<24 + } + return v, true +} + +get_u64 :: proc(b: []byte, order: Byte_Order) -> (v: u64, ok: bool) { + if len(b) < 8 { + return 0, false + } + #no_bounds_check if order == .Little { + v = u64(b[0]) | u64(b[1])<<8 | u64(b[2])<<16 | u64(b[3])<<24 | + u64(b[4])<<32 | u64(b[5])<<40 | u64(b[6])<<48 | u64(b[7])<<56 + } else { + v = u64(b[7]) | u64(b[6])<<8 | u64(b[5])<<16 | u64(b[4])<<24 | + u64(b[3])<<32 | u64(b[2])<<40 | u64(b[1])<<48 | u64(b[0])<<56 + } + return v, true +} + +get_i16 :: proc(b: []byte, order: Byte_Order) -> (i16, bool) { + v, ok := get_u16(b, order) + return i16(v), ok +} +get_i32 :: proc(b: []byte, order: Byte_Order) -> (i32, bool) { + v, ok := get_u32(b, order) + return i32(v), ok +} +get_i64 :: proc(b: []byte, order: Byte_Order) -> (i64, bool) { + v, ok := get_u64(b, order) + return i64(v), ok +} + +get_f16 :: proc(b: []byte, order: Byte_Order) -> (f16, bool) { + v, ok := get_u16(b, order) + return transmute(f16)v, ok +} +get_f32 :: proc(b: []byte, order: Byte_Order) -> (f32, bool) { + v, ok := get_u32(b, order) + return transmute(f32)v, ok +} +get_f64 :: proc(b: []byte, order: Byte_Order) -> (f64, bool) { + v, ok := get_u64(b, order) + return transmute(f64)v, ok +} + + +put_u16 :: proc(b: []byte, order: Byte_Order, v: u16) -> bool { + if len(b) < 2 { + return false + } + #no_bounds_check if order == .Little { + b[0] = byte(v) + b[1] = byte(v >> 8) + } else { + b[0] = byte(v >> 8) + b[1] = byte(v) + } + return true +} +put_u32 :: proc(b: []byte, order: Byte_Order, v: u32) -> bool { + if len(b) < 4 { + return false + } + #no_bounds_check if order == .Little { + b[0] = byte(v) + b[1] = byte(v >> 8) + b[2] = byte(v >> 16) + b[3] = byte(v >> 24) + } else { + b[0] = byte(v >> 24) + b[1] = byte(v >> 16) + b[2] = byte(v >> 8) + b[3] = byte(v) + } + return true +} +put_u64 :: proc(b: []byte, order: Byte_Order, v: u64) -> bool { + if len(b) < 8 { + return false + } + #no_bounds_check if order == .Little { + b[0] = byte(v >> 0) + b[1] = byte(v >> 8) + b[2] = byte(v >> 16) + b[3] = byte(v >> 24) + b[4] = byte(v >> 32) + b[5] = byte(v >> 40) + b[6] = byte(v >> 48) + b[7] = byte(v >> 56) + } else { + b[0] = byte(v >> 56) + b[1] = byte(v >> 48) + b[2] = byte(v >> 40) + b[3] = byte(v >> 32) + b[4] = byte(v >> 24) + b[5] = byte(v >> 16) + b[6] = byte(v >> 8) + b[7] = byte(v) + } + return true +} + +put_i16 :: proc(b: []byte, order: Byte_Order, v: i16) -> bool { + return put_u16(b, order, u16(v)) +} + +put_i32 :: proc(b: []byte, order: Byte_Order, v: i32) -> bool { + return put_u32(b, order, u32(v)) +} + +put_i64 :: proc(b: []byte, order: Byte_Order, v: i64) -> bool { + return put_u64(b, order, u64(v)) +} + + +put_f16 :: proc(b: []byte, order: Byte_Order, v: f16) -> bool { + return put_u16(b, order, transmute(u16)v) +} + +put_f32 :: proc(b: []byte, order: Byte_Order, v: f32) -> bool { + return put_u32(b, order, transmute(u32)v) +} + +put_f64 :: proc(b: []byte, order: Byte_Order, v: f64) -> bool { + return put_u64(b, order, transmute(u64)v) +} diff --git a/core/encoding/entity/LICENSE_table.md b/core/encoding/entity/LICENSE_table.md new file mode 100644 index 000000000..51e3f34b5 --- /dev/null +++ b/core/encoding/entity/LICENSE_table.md @@ -0,0 +1,21 @@ +# License + +By obtaining, using and/or copying this work, you (the licensee) agree that you have read, understood, and will comply with the following terms and conditions. + +Permission to copy, modify, and distribute this software and its documentation, with or without modification, for any purpose and without fee or royalty is hereby granted, provided that you include the following on ALL copies of the software and documentation or portions thereof, including modifications: + +The full text of this NOTICE in a location viewable to users of the redistributed or derivative work. +Any pre-existing intellectual property disclaimers, notices, or terms and conditions. If none exist, the W3C Software Short Notice should be included (hypertext is preferred, text is permitted) within the body of any redistributed or derivative code. + +Notice of any changes or modifications to the files, including the date changes were made. (We recommend you provide URIs to the location from which the code is derived.) + +# Disclaimers + +THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. + +COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENTATION. + +The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining to the software without specific, written prior permission. Title to copyright in this software and any associated documentation will at all times remain with copyright holders. + +# Notes +This version: http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231 \ No newline at end of file diff --git a/core/encoding/entity/entity.odin b/core/encoding/entity/entity.odin new file mode 100644 index 000000000..694fcdffc --- /dev/null +++ b/core/encoding/entity/entity.odin @@ -0,0 +1,374 @@ +package unicode_entity +/* + A unicode entity encoder/decoder + + Copyright 2021 Jeroen van Rijn . + Made available under Odin's BSD-3 license. + + This code has several procedures to map unicode runes to/from different textual encodings. + - SGML/XML/HTML entity + -- &#; + -- &#x; + -- &; (If the lookup tables are compiled in). + Reference: https://www.w3.org/2003/entities/2007xml/unicode.xml + + - URL encode / decode %hex entity + Reference: https://datatracker.ietf.org/doc/html/rfc3986/#section-2.1 + + List of contributors: + Jeroen van Rijn: Initial implementation. +*/ + +import "core:unicode/utf8" +import "core:unicode" +import "core:strings" + +MAX_RUNE_CODEPOINT :: int(unicode.MAX_RUNE) + +write_rune :: strings.write_rune +write_string :: strings.write_string + +Error :: enum u8 { + None = 0, + Tokenizer_Is_Nil, + + Illegal_NUL_Character, + Illegal_UTF_Encoding, + Illegal_BOM, + + CDATA_Not_Terminated, + Comment_Not_Terminated, + Invalid_Entity_Encoding, +} + +Tokenizer :: struct { + r: rune, + w: int, + + src: string, + offset: int, + read_offset: int, +} + +CDATA_START :: "" + +COMMENT_START :: "" + +/* + Default: CDATA and comments are passed through unchanged. +*/ +XML_Decode_Option :: enum u8 { + /* + Do not decode & entities. It decodes by default. + If given, overrides `Decode_CDATA`. + */ + No_Entity_Decode, + + /* + CDATA is unboxed. + */ + Unbox_CDATA, + + /* + Unboxed CDATA is decoded as well. + Ignored if `.Unbox_CDATA` is not given. + */ + Decode_CDATA, + + /* + Comments are stripped. + */ + Comment_Strip, +} +XML_Decode_Options :: bit_set[XML_Decode_Option; u8] + +/* + Decode a string that may include SGML/XML/HTML entities. + The caller has to free the result. +*/ +decode_xml :: proc(input: string, options := XML_Decode_Options{}, allocator := context.allocator) -> (decoded: string, err: Error) { + context.allocator = allocator + + l := len(input) + if l == 0 { return "", .None } + + builder := strings.builder_make() + defer strings.builder_destroy(&builder) + + t := Tokenizer{src=input} + in_data := false + + loop: for { + advance(&t) or_return + if t.r < 0 { break loop } + + /* + Below here we're never inside a CDATA tag. + At most we'll see the start of one, but that doesn't affect the logic. + */ + switch t.r { + case '<': + /* + Might be the start of a CDATA tag or comment. + + We don't need to check if we need to write a `<`, because if it isn't CDATA or a comment, + it couldn't have been part of an XML tag body to be decoded here. + + Keep in mind that we could already *be* inside a CDATA tag. + If so, write `>` as a literal and continue. + */ + if in_data { + write_rune(&builder, '<') + continue + } + in_data = _handle_xml_special(&t, &builder, options) or_return + + case ']': + /* + If we're unboxing _and_ decoding CDATA, we'll have to check for the end tag. + */ + if in_data { + if t.read_offset + len(CDATA_END) < len(t.src) { + if string(t.src[t.offset:][:len(CDATA_END)]) == CDATA_END { + in_data = false + t.read_offset += len(CDATA_END) - 1 + } + } + continue + } else { + write_rune(&builder, ']') + } + + case: + if in_data && .Decode_CDATA not_in options { + /* + Unboxed, but undecoded. + */ + write_rune(&builder, t.r) + continue + } + + if t.r == '&' { + if entity, entity_err := _extract_xml_entity(&t); entity_err != .None { + /* + We read to the end of the string without closing the entity. + Pass through as-is. + */ + write_string(&builder, entity) + } else { + + if .No_Entity_Decode not_in options { + if decoded, ok := xml_decode_entity(entity); ok { + write_rune(&builder, decoded) + continue + } + } + + /* + Literal passthrough because the decode failed or we want entities not decoded. + */ + write_string(&builder, "&") + write_string(&builder, entity) + write_string(&builder, ";") + } + } else { + write_rune(&builder, t.r) + } + } + } + + return strings.clone(strings.to_string(builder), allocator), err +} + +advance :: proc(t: ^Tokenizer) -> (err: Error) { + if t == nil { return .Tokenizer_Is_Nil } + using t + + #no_bounds_check { + if read_offset < len(src) { + offset = read_offset + r, w = rune(src[read_offset]), 1 + switch { + case r == 0: + return .Illegal_NUL_Character + case r >= utf8.RUNE_SELF: + r, w = utf8.decode_rune_in_string(src[read_offset:]) + if r == utf8.RUNE_ERROR && w == 1 { + return .Illegal_UTF_Encoding + } else if r == utf8.RUNE_BOM && offset > 0 { + return .Illegal_BOM + } + } + read_offset += w + return .None + } else { + offset = len(src) + r = -1 + return + } + } +} + +xml_decode_entity :: proc(entity: string) -> (decoded: rune, ok: bool) { + entity := entity + if len(entity) == 0 { return -1, false } + + switch entity[0] { + case '#': + base := 10 + val := 0 + entity = entity[1:] + + if len(entity) == 0 { return -1, false } + + if entity[0] == 'x' || entity[0] == 'X' { + base = 16 + entity = entity[1:] + } + + for len(entity) > 0 { + r := entity[0] + switch r { + case '0'..='9': + val *= base + val += int(r - '0') + + case 'a'..='f': + if base == 10 { return -1, false } + val *= base + val += int(r - 'a' + 10) + + case 'A'..='F': + if base == 10 { return -1, false } + val *= base + val += int(r - 'A' + 10) + + case: + return -1, false + } + + if val > MAX_RUNE_CODEPOINT { return -1, false } + entity = entity[1:] + } + return rune(val), true + + case: + /* + Named entity. + */ + return named_xml_entity_to_rune(entity) + } +} + +/* + Private XML helper to extract `&;` entity. +*/ +@(private="file") +_extract_xml_entity :: proc(t: ^Tokenizer) -> (entity: string, err: Error) { + assert(t != nil && t.r == '&') + + /* + All of these would be in the ASCII range. + Even if one is not, it doesn't matter. All characters we need to compare to extract are. + */ + using t + + length := len(t.src) + found := false + + #no_bounds_check { + for read_offset < length { + if src[read_offset] == ';' { + found = true + read_offset += 1 + break + } + read_offset += 1 + } + } + + if found { + return string(src[offset + 1 : read_offset - 1]), .None + } + return string(src[offset : read_offset]), .Invalid_Entity_Encoding +} + +/* + Private XML helper for CDATA and comments. +*/ +@(private="file") +_handle_xml_special :: proc(t: ^Tokenizer, builder: ^strings.Builder, options: XML_Decode_Options) -> (in_data: bool, err: Error) { + assert(t != nil && t.r == '<') + if t.read_offset + len(CDATA_START) >= len(t.src) { return false, .None } + + if string(t.src[t.offset:][:len(CDATA_START)]) == CDATA_START { + t.read_offset += len(CDATA_START) - 1 + + if .Unbox_CDATA in options && .Decode_CDATA in options { + /* + We're unboxing _and_ decoding CDATA + */ + return true, .None + } + + /* + CDATA is passed through. + */ + offset := t.offset + + /* + Scan until end of CDATA. + */ + for { + advance(t) or_return + if t.r < 0 { return true, .CDATA_Not_Terminated } + + if t.read_offset + len(CDATA_END) < len(t.src) { + if string(t.src[t.offset:][:len(CDATA_END)]) == CDATA_END { + t.read_offset += len(CDATA_END) - 1 + + cdata := string(t.src[offset : t.read_offset]) + + if .Unbox_CDATA in options { + cdata = cdata[len(CDATA_START):] + cdata = cdata[:len(cdata) - len(CDATA_END)] + } + + write_string(builder, cdata) + return false, .None + } + } + } + + } else if string(t.src[t.offset:][:len(COMMENT_START)]) == COMMENT_START { + t.read_offset += len(COMMENT_START) + /* + Comment is passed through by default. + */ + offset := t.offset + + /* + Scan until end of Comment. + */ + for { + advance(t) or_return + if t.r < 0 { return true, .Comment_Not_Terminated } + + if t.read_offset + len(COMMENT_END) < len(t.src) { + if string(t.src[t.offset:][:len(COMMENT_END)]) == COMMENT_END { + t.read_offset += len(COMMENT_END) - 1 + + if .Comment_Strip not_in options { + comment := string(t.src[offset : t.read_offset]) + write_string(builder, comment) + } + return false, .None + } + } + } + + } + return false, .None +} \ No newline at end of file diff --git a/core/encoding/entity/example/entity_example.odin b/core/encoding/entity/example/entity_example.odin new file mode 100644 index 000000000..6301eb263 --- /dev/null +++ b/core/encoding/entity/example/entity_example.odin @@ -0,0 +1,76 @@ +package unicode_entity_example + +import "core:encoding/xml" +import "core:strings" +import "core:mem" +import "core:fmt" +import "core:time" + +doc_print :: proc(doc: ^xml.Document) { + buf: strings.Builder + defer strings.builder_destroy(&buf) + w := strings.to_writer(&buf) + + xml.print(w, doc) + fmt.println(strings.to_string(buf)) +} + +_entities :: proc() { + doc: ^xml.Document + err: xml.Error + + DOC :: #load("../../../../tests/core/assets/XML/unicode.xml") + + OPTIONS :: xml.Options{ + flags = { + .Ignore_Unsupported, .Intern_Comments, + }, + expected_doctype = "", + } + + parse_duration: time.Duration + + { + time.SCOPED_TICK_DURATION(&parse_duration) + doc, err = xml.parse(DOC, OPTIONS) + } + defer xml.destroy(doc) + + doc_print(doc) + + ms := time.duration_milliseconds(parse_duration) + + speed := (f64(1000.0) / ms) * f64(len(DOC)) / 1_024.0 / 1_024.0 + + fmt.printf("Parse time: %.2f ms (%.2f MiB/s).\n", ms, speed) + fmt.printf("Error: %v\n", err) +} + +_main :: proc() { + using fmt + + options := xml.Options{ flags = { .Ignore_Unsupported, .Intern_Comments, .Unbox_CDATA, .Decode_SGML_Entities }} + + doc, _ := xml.parse(#load("test.html"), options) + + defer xml.destroy(doc) + doc_print(doc) +} + +main :: proc() { + using fmt + + track: mem.Tracking_Allocator + mem.tracking_allocator_init(&track, context.allocator) + context.allocator = mem.tracking_allocator(&track) + + // _main() + _entities() + + if len(track.allocation_map) > 0 { + println() + for _, v in track.allocation_map { + printf("%v Leaked %v bytes.\n", v.location, v.size) + } + } +} \ No newline at end of file diff --git a/core/encoding/entity/example/test.html b/core/encoding/entity/example/test.html new file mode 100644 index 000000000..ebbc6470c --- /dev/null +++ b/core/encoding/entity/example/test.html @@ -0,0 +1,28 @@ + + + Entity Reference Test + + + +

Entity Reference Test

+
+ Foozle]! © 42&;1234& +
+ +
+ Foozle]! © 42&;1234& +
+ +
+ | | | fj ` \ ® ϱ ∳ ⁏ +
+ + \ No newline at end of file diff --git a/core/encoding/entity/generated.odin b/core/encoding/entity/generated.odin new file mode 100644 index 000000000..9afdcae6d --- /dev/null +++ b/core/encoding/entity/generated.odin @@ -0,0 +1,7493 @@ +package unicode_entity + +/* + ------ GENERATED ------ DO NOT EDIT ------ GENERATED ------ DO NOT EDIT ------ GENERATED ------ +*/ + +/* + This file is generated from "https://www.w3.org/2003/entities/2007xml/unicode.xml". + + UPDATE: + - Ensure the XML file was downloaded using "tests\core\download_assets.py". + - Run "core/unicode/tools/generate_entity_table.odin" + + Odin unicode generated tables: https://github.com/odin-lang/Odin/tree/master/core/encoding/entity + + Copyright © 2021 World Wide Web Consortium, (Massachusetts Institute of Technology, + European Research Consortium for Informatics and Mathematics, Keio University, Beihang). + + All Rights Reserved. + + This work is distributed under the W3C® Software License [1] in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + [1] http://www.w3.org/Consortium/Legal/copyright-software + + See also: LICENSE_table.md +*/ + +// `<` +XML_NAME_TO_RUNE_MIN_LENGTH :: 2 +// `∳` +XML_NAME_TO_RUNE_MAX_LENGTH :: 31 + + +/* + Input: + entity_name - a string, like "copy" that describes a user-encoded Unicode entity as used in XML. + + Output: + "decoded" - The decoded rune if found by name, or -1 otherwise. + "ok" - true if found, false if not. + + IMPORTANT: XML processors (including browsers) treat these names as case-sensitive. So do we. +*/ +named_xml_entity_to_rune :: proc(name: string) -> (decoded: rune, ok: bool) { + /* + Early out if the name is too short or too long. + min as a precaution in case the generated table has a bogus value. + */ + if len(name) < min(1, XML_NAME_TO_RUNE_MIN_LENGTH) || len(name) > XML_NAME_TO_RUNE_MAX_LENGTH { + return -1, false + } + + switch rune(name[0]) { + + case 'A': + switch name { + case "AElig": + // LATIN CAPITAL LETTER AE + return rune(0xc6), true + case "AMP": + // AMPERSAND + return rune(0x26), true + case "Aacgr": + // GREEK CAPITAL LETTER ALPHA WITH TONOS + return rune(0x0386), true + case "Aacute": + // LATIN CAPITAL LETTER A WITH ACUTE + return rune(0xc1), true + case "Abreve": + // LATIN CAPITAL LETTER A WITH BREVE + return rune(0x0102), true + case "Acirc": + // LATIN CAPITAL LETTER A WITH CIRCUMFLEX + return rune(0xc2), true + case "Acy": + // CYRILLIC CAPITAL LETTER A + return rune(0x0410), true + case "Afr": + // MATHEMATICAL FRAKTUR CAPITAL A + return rune(0x01d504), true + case "Agrave": + // LATIN CAPITAL LETTER A WITH GRAVE + return rune(0xc0), true + case "Agr": + // GREEK CAPITAL LETTER ALPHA + return rune(0x0391), true + case "Alpha": + // GREEK CAPITAL LETTER ALPHA + return rune(0x0391), true + case "Amacr": + // LATIN CAPITAL LETTER A WITH MACRON + return rune(0x0100), true + case "And": + // DOUBLE LOGICAL AND + return rune(0x2a53), true + case "Aogon": + // LATIN CAPITAL LETTER A WITH OGONEK + return rune(0x0104), true + case "Aopf": + // MATHEMATICAL DOUBLE-STRUCK CAPITAL A + return rune(0x01d538), true + case "ApplyFunction": + // FUNCTION APPLICATION + return rune(0x2061), true + case "Aring": + // LATIN CAPITAL LETTER A WITH RING ABOVE + return rune(0xc5), true + case "Ascr": + // MATHEMATICAL SCRIPT CAPITAL A + return rune(0x01d49c), true + case "Assign": + // COLON EQUALS + return rune(0x2254), true + case "Ast": + // TWO ASTERISKS ALIGNED VERTICALLY + return rune(0x2051), true + case "Atilde": + // LATIN CAPITAL LETTER A WITH TILDE + return rune(0xc3), true + case "Auml": + // LATIN CAPITAL LETTER A WITH DIAERESIS + return rune(0xc4), true + } + + case 'B': + switch name { + case "Backslash": + // SET MINUS + return rune(0x2216), true + case "Barint": + // INTEGRAL WITH DOUBLE STROKE + return rune(0x2a0e), true + case "Barv": + // SHORT DOWN TACK WITH OVERBAR + return rune(0x2ae7), true + case "Barwedl": + // LOGICAL AND WITH DOUBLE OVERBAR + return rune(0x2a5e), true + case "Barwed": + // PERSPECTIVE + return rune(0x2306), true + case "Bcy": + // CYRILLIC CAPITAL LETTER BE + return rune(0x0411), true + case "Because": + // BECAUSE + return rune(0x2235), true + case "Bernoullis": + // SCRIPT CAPITAL B + return rune(0x212c), true + case "Beta": + // GREEK CAPITAL LETTER BETA + return rune(0x0392), true + case "Bfr": + // MATHEMATICAL FRAKTUR CAPITAL B + return rune(0x01d505), true + case "Bgr": + // GREEK CAPITAL LETTER BETA + return rune(0x0392), true + case "Bopf": + // MATHEMATICAL DOUBLE-STRUCK CAPITAL B + return rune(0x01d539), true + case "Breve": + // BREVE + return rune(0x02d8), true + case "Bscr": + // SCRIPT CAPITAL B + return rune(0x212c), true + case "Bumpeq": + // GEOMETRICALLY EQUIVALENT TO + return rune(0x224e), true + case "Bvert": + // BOX DRAWINGS LIGHT TRIPLE DASH VERTICAL + return rune(0x2506), true + } + + case 'C': + switch name { + case "CHcy": + // CYRILLIC CAPITAL LETTER CHE + return rune(0x0427), true + case "COPY": + // COPYRIGHT SIGN + return rune(0xa9), true + case "Cacute": + // LATIN CAPITAL LETTER C WITH ACUTE + return rune(0x0106), true + case "CapitalDifferentialD": + // DOUBLE-STRUCK ITALIC CAPITAL D + return rune(0x2145), true + case "Cap": + // DOUBLE INTERSECTION + return rune(0x22d2), true + case "Cayleys": + // BLACK-LETTER CAPITAL C + return rune(0x212d), true + case "Ccaron": + // LATIN CAPITAL LETTER C WITH CARON + return rune(0x010c), true + case "Ccedil": + // LATIN CAPITAL LETTER C WITH CEDILLA + return rune(0xc7), true + case "Ccirc": + // LATIN CAPITAL LETTER C WITH CIRCUMFLEX + return rune(0x0108), true + case "Cconint": + // VOLUME INTEGRAL + return rune(0x2230), true + case "Cdot": + // LATIN CAPITAL LETTER C WITH DOT ABOVE + return rune(0x010a), true + case "Cedilla": + // CEDILLA + return rune(0xb8), true + case "CenterDot": + // MIDDLE DOT + return rune(0xb7), true + case "Cfr": + // BLACK-LETTER CAPITAL C + return rune(0x212d), true + case "Chi": + // GREEK CAPITAL LETTER CHI + return rune(0x03a7), true + case "CircleDot": + // CIRCLED DOT OPERATOR + return rune(0x2299), true + case "CircleMinus": + // CIRCLED MINUS + return rune(0x2296), true + case "CirclePlus": + // CIRCLED PLUS + return rune(0x2295), true + case "CircleTimes": + // CIRCLED TIMES + return rune(0x2297), true + case "ClockwiseContourIntegral": + // CLOCKWISE CONTOUR INTEGRAL + return rune(0x2232), true + case "CloseCurlyDoubleQuote": + // RIGHT DOUBLE QUOTATION MARK + return rune(0x201d), true + case "CloseCurlyQuote": + // RIGHT SINGLE QUOTATION MARK + return rune(0x2019), true + case "Colon": + // PROPORTION + return rune(0x2237), true + case "Colone": + // DOUBLE COLON EQUAL + return rune(0x2a74), true + case "Congruent": + // IDENTICAL TO + return rune(0x2261), true + case "Conint": + // SURFACE INTEGRAL + return rune(0x222f), true + case "ContourIntegral": + // CONTOUR INTEGRAL + return rune(0x222e), true + case "Copf": + // DOUBLE-STRUCK CAPITAL C + return rune(0x2102), true + case "Coproduct": + // N-ARY COPRODUCT + return rune(0x2210), true + case "CounterClockwiseContourIntegral": + // ANTICLOCKWISE CONTOUR INTEGRAL + return rune(0x2233), true + case "Cross": + // VECTOR OR CROSS PRODUCT + return rune(0x2a2f), true + case "Cscr": + // MATHEMATICAL SCRIPT CAPITAL C + return rune(0x01d49e), true + case "CupCap": + // EQUIVALENT TO + return rune(0x224d), true + case "Cup": + // DOUBLE UNION + return rune(0x22d3), true + } + + case 'D': + switch name { + case "DD": + // DOUBLE-STRUCK ITALIC CAPITAL D + return rune(0x2145), true + case "DDotrahd": + // RIGHTWARDS ARROW WITH DOTTED STEM + return rune(0x2911), true + case "DJcy": + // CYRILLIC CAPITAL LETTER DJE + return rune(0x0402), true + case "DScy": + // CYRILLIC CAPITAL LETTER DZE + return rune(0x0405), true + case "DZcy": + // CYRILLIC CAPITAL LETTER DZHE + return rune(0x040f), true + case "Dagger": + // DOUBLE DAGGER + return rune(0x2021), true + case "Darr": + // DOWNWARDS TWO HEADED ARROW + return rune(0x21a1), true + case "Dashv": + // VERTICAL BAR DOUBLE LEFT TURNSTILE + return rune(0x2ae4), true + case "Dcaron": + // LATIN CAPITAL LETTER D WITH CARON + return rune(0x010e), true + case "Dcy": + // CYRILLIC CAPITAL LETTER DE + return rune(0x0414), true + case "Del": + // NABLA + return rune(0x2207), true + case "Delta": + // GREEK CAPITAL LETTER DELTA + return rune(0x0394), true + case "Dfr": + // MATHEMATICAL FRAKTUR CAPITAL D + return rune(0x01d507), true + case "Dgr": + // GREEK CAPITAL LETTER DELTA + return rune(0x0394), true + case "DiacriticalAcute": + // ACUTE ACCENT + return rune(0xb4), true + case "DiacriticalDot": + // DOT ABOVE + return rune(0x02d9), true + case "DiacriticalDoubleAcute": + // DOUBLE ACUTE ACCENT + return rune(0x02dd), true + case "DiacriticalGrave": + // GRAVE ACCENT + return rune(0x60), true + case "DiacriticalTilde": + // SMALL TILDE + return rune(0x02dc), true + case "Diamond": + // DIAMOND OPERATOR + return rune(0x22c4), true + case "DifferentialD": + // DOUBLE-STRUCK ITALIC SMALL D + return rune(0x2146), true + case "Dopf": + // MATHEMATICAL DOUBLE-STRUCK CAPITAL D + return rune(0x01d53b), true + case "Dot": + // DIAERESIS + return rune(0xa8), true + case "DotDot": + // COMBINING FOUR DOTS ABOVE + return rune(0x20dc), true + case "DotEqual": + // APPROACHES THE LIMIT + return rune(0x2250), true + case "DoubleContourIntegral": + // SURFACE INTEGRAL + return rune(0x222f), true + case "DoubleDot": + // DIAERESIS + return rune(0xa8), true + case "DoubleDownArrow": + // DOWNWARDS DOUBLE ARROW + return rune(0x21d3), true + case "DoubleLeftArrow": + // LEFTWARDS DOUBLE ARROW + return rune(0x21d0), true + case "DoubleLeftRightArrow": + // LEFT RIGHT DOUBLE ARROW + return rune(0x21d4), true + case "DoubleLeftTee": + // VERTICAL BAR DOUBLE LEFT TURNSTILE + return rune(0x2ae4), true + case "DoubleLongLeftArrow": + // LONG LEFTWARDS DOUBLE ARROW + return rune(0x27f8), true + case "DoubleLongLeftRightArrow": + // LONG LEFT RIGHT DOUBLE ARROW + return rune(0x27fa), true + case "DoubleLongRightArrow": + // LONG RIGHTWARDS DOUBLE ARROW + return rune(0x27f9), true + case "DoubleRightArrow": + // RIGHTWARDS DOUBLE ARROW + return rune(0x21d2), true + case "DoubleRightTee": + // TRUE + return rune(0x22a8), true + case "DoubleUpArrow": + // UPWARDS DOUBLE ARROW + return rune(0x21d1), true + case "DoubleUpDownArrow": + // UP DOWN DOUBLE ARROW + return rune(0x21d5), true + case "DoubleVerticalBar": + // PARALLEL TO + return rune(0x2225), true + case "DownArrowUpArrow": + // DOWNWARDS ARROW LEFTWARDS OF UPWARDS ARROW + return rune(0x21f5), true + case "DownArrow": + // DOWNWARDS ARROW + return rune(0x2193), true + case "DownArrowBar": + // DOWNWARDS ARROW TO BAR + return rune(0x2913), true + case "DownBreve": + // COMBINING INVERTED BREVE + return rune(0x0311), true + case "DownLeftRightVector": + // LEFT BARB DOWN RIGHT BARB DOWN HARPOON + return rune(0x2950), true + case "DownLeftTeeVector": + // LEFTWARDS HARPOON WITH BARB DOWN FROM BAR + return rune(0x295e), true + case "DownLeftVector": + // LEFTWARDS HARPOON WITH BARB DOWNWARDS + return rune(0x21bd), true + case "DownLeftVectorBar": + // LEFTWARDS HARPOON WITH BARB DOWN TO BAR + return rune(0x2956), true + case "DownRightTeeVector": + // RIGHTWARDS HARPOON WITH BARB DOWN FROM BAR + return rune(0x295f), true + case "DownRightVector": + // RIGHTWARDS HARPOON WITH BARB DOWNWARDS + return rune(0x21c1), true + case "DownRightVectorBar": + // RIGHTWARDS HARPOON WITH BARB DOWN TO BAR + return rune(0x2957), true + case "DownTeeArrow": + // DOWNWARDS ARROW FROM BAR + return rune(0x21a7), true + case "DownTee": + // DOWN TACK + return rune(0x22a4), true + case "Downarrow": + // DOWNWARDS DOUBLE ARROW + return rune(0x21d3), true + case "Dscr": + // MATHEMATICAL SCRIPT CAPITAL D + return rune(0x01d49f), true + case "Dstrok": + // LATIN CAPITAL LETTER D WITH STROKE + return rune(0x0110), true + } + + case 'E': + switch name { + case "EEacgr": + // GREEK CAPITAL LETTER ETA WITH TONOS + return rune(0x0389), true + case "EEgr": + // GREEK CAPITAL LETTER ETA + return rune(0x0397), true + case "ENG": + // LATIN CAPITAL LETTER ENG + return rune(0x014a), true + case "ETH": + // LATIN CAPITAL LETTER ETH + return rune(0xd0), true + case "Eacgr": + // GREEK CAPITAL LETTER EPSILON WITH TONOS + return rune(0x0388), true + case "Eacute": + // LATIN CAPITAL LETTER E WITH ACUTE + return rune(0xc9), true + case "Ecaron": + // LATIN CAPITAL LETTER E WITH CARON + return rune(0x011a), true + case "Ecirc": + // LATIN CAPITAL LETTER E WITH CIRCUMFLEX + return rune(0xca), true + case "Ecy": + // CYRILLIC CAPITAL LETTER E + return rune(0x042d), true + case "Edot": + // LATIN CAPITAL LETTER E WITH DOT ABOVE + return rune(0x0116), true + case "Efr": + // MATHEMATICAL FRAKTUR CAPITAL E + return rune(0x01d508), true + case "Egrave": + // LATIN CAPITAL LETTER E WITH GRAVE + return rune(0xc8), true + case "Egr": + // GREEK CAPITAL LETTER EPSILON + return rune(0x0395), true + case "Element": + // ELEMENT OF + return rune(0x2208), true + case "Emacr": + // LATIN CAPITAL LETTER E WITH MACRON + return rune(0x0112), true + case "EmptySmallSquare": + // WHITE MEDIUM SQUARE + return rune(0x25fb), true + case "EmptyVerySmallSquare": + // WHITE SMALL SQUARE + return rune(0x25ab), true + case "Eogon": + // LATIN CAPITAL LETTER E WITH OGONEK + return rune(0x0118), true + case "Eopf": + // MATHEMATICAL DOUBLE-STRUCK CAPITAL E + return rune(0x01d53c), true + case "Epsilon": + // GREEK CAPITAL LETTER EPSILON + return rune(0x0395), true + case "EqualTilde": + // MINUS TILDE + return rune(0x2242), true + case "Equal": + // TWO CONSECUTIVE EQUALS SIGNS + return rune(0x2a75), true + case "Equilibrium": + // RIGHTWARDS HARPOON OVER LEFTWARDS HARPOON + return rune(0x21cc), true + case "Escr": + // SCRIPT CAPITAL E + return rune(0x2130), true + case "Esim": + // EQUALS SIGN ABOVE TILDE OPERATOR + return rune(0x2a73), true + case "Eta": + // GREEK CAPITAL LETTER ETA + return rune(0x0397), true + case "Euml": + // LATIN CAPITAL LETTER E WITH DIAERESIS + return rune(0xcb), true + case "Exists": + // THERE EXISTS + return rune(0x2203), true + case "ExponentialE": + // DOUBLE-STRUCK ITALIC SMALL E + return rune(0x2147), true + } + + case 'F': + switch name { + case "Fcy": + // CYRILLIC CAPITAL LETTER EF + return rune(0x0424), true + case "Ffr": + // MATHEMATICAL FRAKTUR CAPITAL F + return rune(0x01d509), true + case "FilledSmallSquare": + // BLACK MEDIUM SQUARE + return rune(0x25fc), true + case "FilledVerySmallSquare": + // BLACK SMALL SQUARE + return rune(0x25aa), true + case "Fopf": + // MATHEMATICAL DOUBLE-STRUCK CAPITAL F + return rune(0x01d53d), true + case "ForAll": + // FOR ALL + return rune(0x2200), true + case "Fouriertrf": + // SCRIPT CAPITAL F + return rune(0x2131), true + case "Fscr": + // SCRIPT CAPITAL F + return rune(0x2131), true + } + + case 'G': + switch name { + case "GJcy": + // CYRILLIC CAPITAL LETTER GJE + return rune(0x0403), true + case "GT": + // GREATER-THAN SIGN + return rune(0x3e), true + case "Game": + // TURNED SANS-SERIF CAPITAL G + return rune(0x2141), true + case "Gamma": + // GREEK CAPITAL LETTER GAMMA + return rune(0x0393), true + case "Gammad": + // GREEK LETTER DIGAMMA + return rune(0x03dc), true + case "Gbreve": + // LATIN CAPITAL LETTER G WITH BREVE + return rune(0x011e), true + case "Gcedil": + // LATIN CAPITAL LETTER G WITH CEDILLA + return rune(0x0122), true + case "Gcirc": + // LATIN CAPITAL LETTER G WITH CIRCUMFLEX + return rune(0x011c), true + case "Gcy": + // CYRILLIC CAPITAL LETTER GHE + return rune(0x0413), true + case "Gdot": + // LATIN CAPITAL LETTER G WITH DOT ABOVE + return rune(0x0120), true + case "Gfr": + // MATHEMATICAL FRAKTUR CAPITAL G + return rune(0x01d50a), true + case "Ggr": + // GREEK CAPITAL LETTER GAMMA + return rune(0x0393), true + case "Gg": + // VERY MUCH GREATER-THAN + return rune(0x22d9), true + case "Gopf": + // MATHEMATICAL DOUBLE-STRUCK CAPITAL G + return rune(0x01d53e), true + case "GreaterEqual": + // GREATER-THAN OR EQUAL TO + return rune(0x2265), true + case "GreaterEqualLess": + // GREATER-THAN EQUAL TO OR LESS-THAN + return rune(0x22db), true + case "GreaterFullEqual": + // GREATER-THAN OVER EQUAL TO + return rune(0x2267), true + case "GreaterGreater": + // DOUBLE NESTED GREATER-THAN + return rune(0x2aa2), true + case "GreaterLess": + // GREATER-THAN OR LESS-THAN + return rune(0x2277), true + case "GreaterSlantEqual": + // GREATER-THAN OR SLANTED EQUAL TO + return rune(0x2a7e), true + case "GreaterTilde": + // GREATER-THAN OR EQUIVALENT TO + return rune(0x2273), true + case "Gscr": + // MATHEMATICAL SCRIPT CAPITAL G + return rune(0x01d4a2), true + case "Gt": + // MUCH GREATER-THAN + return rune(0x226b), true + } + + case 'H': + switch name { + case "HARDcy": + // CYRILLIC CAPITAL LETTER HARD SIGN + return rune(0x042a), true + case "Hacek": + // CARON + return rune(0x02c7), true + case "Hat": + // CIRCUMFLEX ACCENT + return rune(0x5e), true + case "Hcirc": + // LATIN CAPITAL LETTER H WITH CIRCUMFLEX + return rune(0x0124), true + case "Hfr": + // BLACK-LETTER CAPITAL H + return rune(0x210c), true + case "HilbertSpace": + // SCRIPT CAPITAL H + return rune(0x210b), true + case "Hopf": + // DOUBLE-STRUCK CAPITAL H + return rune(0x210d), true + case "HorizontalLine": + // BOX DRAWINGS LIGHT HORIZONTAL + return rune(0x2500), true + case "Hscr": + // SCRIPT CAPITAL H + return rune(0x210b), true + case "Hstrok": + // LATIN CAPITAL LETTER H WITH STROKE + return rune(0x0126), true + case "HumpDownHump": + // GEOMETRICALLY EQUIVALENT TO + return rune(0x224e), true + case "HumpEqual": + // DIFFERENCE BETWEEN + return rune(0x224f), true + } + + case 'I': + switch name { + case "IEcy": + // CYRILLIC CAPITAL LETTER IE + return rune(0x0415), true + case "IJlig": + // LATIN CAPITAL LIGATURE IJ + return rune(0x0132), true + case "IOcy": + // CYRILLIC CAPITAL LETTER IO + return rune(0x0401), true + case "Iacgr": + // GREEK CAPITAL LETTER IOTA WITH TONOS + return rune(0x038a), true + case "Iacute": + // LATIN CAPITAL LETTER I WITH ACUTE + return rune(0xcd), true + case "Icirc": + // LATIN CAPITAL LETTER I WITH CIRCUMFLEX + return rune(0xce), true + case "Icy": + // CYRILLIC CAPITAL LETTER I + return rune(0x0418), true + case "Idigr": + // GREEK CAPITAL LETTER IOTA WITH DIALYTIKA + return rune(0x03aa), true + case "Idot": + // LATIN CAPITAL LETTER I WITH DOT ABOVE + return rune(0x0130), true + case "Ifr": + // BLACK-LETTER CAPITAL I + return rune(0x2111), true + case "Igrave": + // LATIN CAPITAL LETTER I WITH GRAVE + return rune(0xcc), true + case "Igr": + // GREEK CAPITAL LETTER IOTA + return rune(0x0399), true + case "Imacr": + // LATIN CAPITAL LETTER I WITH MACRON + return rune(0x012a), true + case "ImaginaryI": + // DOUBLE-STRUCK ITALIC SMALL I + return rune(0x2148), true + case "Implies": + // RIGHTWARDS DOUBLE ARROW + return rune(0x21d2), true + case "Im": + // BLACK-LETTER CAPITAL I + return rune(0x2111), true + case "Integral": + // INTEGRAL + return rune(0x222b), true + case "Int": + // DOUBLE INTEGRAL + return rune(0x222c), true + case "Intersection": + // N-ARY INTERSECTION + return rune(0x22c2), true + case "InvisibleComma": + // INVISIBLE SEPARATOR + return rune(0x2063), true + case "InvisibleTimes": + // INVISIBLE TIMES + return rune(0x2062), true + case "Iogon": + // LATIN CAPITAL LETTER I WITH OGONEK + return rune(0x012e), true + case "Iopf": + // MATHEMATICAL DOUBLE-STRUCK CAPITAL I + return rune(0x01d540), true + case "Iota": + // GREEK CAPITAL LETTER IOTA + return rune(0x0399), true + case "Iscr": + // SCRIPT CAPITAL I + return rune(0x2110), true + case "Itilde": + // LATIN CAPITAL LETTER I WITH TILDE + return rune(0x0128), true + case "Iukcy": + // CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I + return rune(0x0406), true + case "Iuml": + // LATIN CAPITAL LETTER I WITH DIAERESIS + return rune(0xcf), true + } + + case 'J': + switch name { + case "Jcirc": + // LATIN CAPITAL LETTER J WITH CIRCUMFLEX + return rune(0x0134), true + case "Jcy": + // CYRILLIC CAPITAL LETTER SHORT I + return rune(0x0419), true + case "Jfr": + // MATHEMATICAL FRAKTUR CAPITAL J + return rune(0x01d50d), true + case "Jopf": + // MATHEMATICAL DOUBLE-STRUCK CAPITAL J + return rune(0x01d541), true + case "Jscr": + // MATHEMATICAL SCRIPT CAPITAL J + return rune(0x01d4a5), true + case "Jsercy": + // CYRILLIC CAPITAL LETTER JE + return rune(0x0408), true + case "Jukcy": + // CYRILLIC CAPITAL LETTER UKRAINIAN IE + return rune(0x0404), true + } + + case 'K': + switch name { + case "KHcy": + // CYRILLIC CAPITAL LETTER HA + return rune(0x0425), true + case "KHgr": + // GREEK CAPITAL LETTER CHI + return rune(0x03a7), true + case "KJcy": + // CYRILLIC CAPITAL LETTER KJE + return rune(0x040c), true + case "Kappa": + // GREEK CAPITAL LETTER KAPPA + return rune(0x039a), true + case "Kcedil": + // LATIN CAPITAL LETTER K WITH CEDILLA + return rune(0x0136), true + case "Kcy": + // CYRILLIC CAPITAL LETTER KA + return rune(0x041a), true + case "Kfr": + // MATHEMATICAL FRAKTUR CAPITAL K + return rune(0x01d50e), true + case "Kgr": + // GREEK CAPITAL LETTER KAPPA + return rune(0x039a), true + case "Kopf": + // MATHEMATICAL DOUBLE-STRUCK CAPITAL K + return rune(0x01d542), true + case "Kscr": + // MATHEMATICAL SCRIPT CAPITAL K + return rune(0x01d4a6), true + } + + case 'L': + switch name { + case "LJcy": + // CYRILLIC CAPITAL LETTER LJE + return rune(0x0409), true + case "LT": + // LESS-THAN SIGN + return rune(0x3c), true + case "Lacute": + // LATIN CAPITAL LETTER L WITH ACUTE + return rune(0x0139), true + case "Lambda": + // GREEK CAPITAL LETTER LAMDA + return rune(0x039b), true + case "Lang": + // MATHEMATICAL LEFT DOUBLE ANGLE BRACKET + return rune(0x27ea), true + case "Laplacetrf": + // SCRIPT CAPITAL L + return rune(0x2112), true + case "Larr": + // LEFTWARDS TWO HEADED ARROW + return rune(0x219e), true + case "Lcaron": + // LATIN CAPITAL LETTER L WITH CARON + return rune(0x013d), true + case "Lcedil": + // LATIN CAPITAL LETTER L WITH CEDILLA + return rune(0x013b), true + case "Lcy": + // CYRILLIC CAPITAL LETTER EL + return rune(0x041b), true + case "LeftAngleBracket": + // MATHEMATICAL LEFT ANGLE BRACKET + return rune(0x27e8), true + case "LeftArrowBar": + // LEFTWARDS ARROW TO BAR + return rune(0x21e4), true + case "LeftArrowRightArrow": + // LEFTWARDS ARROW OVER RIGHTWARDS ARROW + return rune(0x21c6), true + case "LeftArrow": + // LEFTWARDS ARROW + return rune(0x2190), true + case "LeftCeiling": + // LEFT CEILING + return rune(0x2308), true + case "LeftDoubleBracket": + // MATHEMATICAL LEFT WHITE SQUARE BRACKET + return rune(0x27e6), true + case "LeftDownTeeVector": + // DOWNWARDS HARPOON WITH BARB LEFT FROM BAR + return rune(0x2961), true + case "LeftDownVector": + // DOWNWARDS HARPOON WITH BARB LEFTWARDS + return rune(0x21c3), true + case "LeftDownVectorBar": + // DOWNWARDS HARPOON WITH BARB LEFT TO BAR + return rune(0x2959), true + case "LeftFloor": + // LEFT FLOOR + return rune(0x230a), true + case "LeftRightArrow": + // LEFT RIGHT ARROW + return rune(0x2194), true + case "LeftRightVector": + // LEFT BARB UP RIGHT BARB UP HARPOON + return rune(0x294e), true + case "LeftTeeArrow": + // LEFTWARDS ARROW FROM BAR + return rune(0x21a4), true + case "LeftTeeVector": + // LEFTWARDS HARPOON WITH BARB UP FROM BAR + return rune(0x295a), true + case "LeftTee": + // LEFT TACK + return rune(0x22a3), true + case "LeftTriangleBar": + // LEFT TRIANGLE BESIDE VERTICAL BAR + return rune(0x29cf), true + case "LeftTriangle": + // NORMAL SUBGROUP OF + return rune(0x22b2), true + case "LeftTriangleEqual": + // NORMAL SUBGROUP OF OR EQUAL TO + return rune(0x22b4), true + case "LeftUpDownVector": + // UP BARB LEFT DOWN BARB LEFT HARPOON + return rune(0x2951), true + case "LeftUpTeeVector": + // UPWARDS HARPOON WITH BARB LEFT FROM BAR + return rune(0x2960), true + case "LeftUpVector": + // UPWARDS HARPOON WITH BARB LEFTWARDS + return rune(0x21bf), true + case "LeftUpVectorBar": + // UPWARDS HARPOON WITH BARB LEFT TO BAR + return rune(0x2958), true + case "LeftVector": + // LEFTWARDS HARPOON WITH BARB UPWARDS + return rune(0x21bc), true + case "LeftVectorBar": + // LEFTWARDS HARPOON WITH BARB UP TO BAR + return rune(0x2952), true + case "Leftarrow": + // LEFTWARDS DOUBLE ARROW + return rune(0x21d0), true + case "Leftrightarrow": + // LEFT RIGHT DOUBLE ARROW + return rune(0x21d4), true + case "LessEqualGreater": + // LESS-THAN EQUAL TO OR GREATER-THAN + return rune(0x22da), true + case "LessFullEqual": + // LESS-THAN OVER EQUAL TO + return rune(0x2266), true + case "LessGreater": + // LESS-THAN OR GREATER-THAN + return rune(0x2276), true + case "LessLess": + // DOUBLE NESTED LESS-THAN + return rune(0x2aa1), true + case "LessSlantEqual": + // LESS-THAN OR SLANTED EQUAL TO + return rune(0x2a7d), true + case "LessTilde": + // LESS-THAN OR EQUIVALENT TO + return rune(0x2272), true + case "Lfr": + // MATHEMATICAL FRAKTUR CAPITAL L + return rune(0x01d50f), true + case "Lgr": + // GREEK CAPITAL LETTER LAMDA + return rune(0x039b), true + case "Lleftarrow": + // LEFTWARDS TRIPLE ARROW + return rune(0x21da), true + case "Ll": + // VERY MUCH LESS-THAN + return rune(0x22d8), true + case "Lmidot": + // LATIN CAPITAL LETTER L WITH MIDDLE DOT + return rune(0x013f), true + case "LongLeftArrow": + // LONG LEFTWARDS ARROW + return rune(0x27f5), true + case "LongLeftRightArrow": + // LONG LEFT RIGHT ARROW + return rune(0x27f7), true + case "LongRightArrow": + // LONG RIGHTWARDS ARROW + return rune(0x27f6), true + case "Longleftarrow": + // LONG LEFTWARDS DOUBLE ARROW + return rune(0x27f8), true + case "Longleftrightarrow": + // LONG LEFT RIGHT DOUBLE ARROW + return rune(0x27fa), true + case "Longrightarrow": + // LONG RIGHTWARDS DOUBLE ARROW + return rune(0x27f9), true + case "Lopf": + // MATHEMATICAL DOUBLE-STRUCK CAPITAL L + return rune(0x01d543), true + case "LowerLeftArrow": + // SOUTH WEST ARROW + return rune(0x2199), true + case "LowerRightArrow": + // SOUTH EAST ARROW + return rune(0x2198), true + case "Lscr": + // SCRIPT CAPITAL L + return rune(0x2112), true + case "Lsh": + // UPWARDS ARROW WITH TIP LEFTWARDS + return rune(0x21b0), true + case "Lstrok": + // LATIN CAPITAL LETTER L WITH STROKE + return rune(0x0141), true + case "Ltbar": + // DOUBLE NESTED LESS-THAN WITH UNDERBAR + return rune(0x2aa3), true + case "Lt": + // MUCH LESS-THAN + return rune(0x226a), true + } + + case 'M': + switch name { + case "Mapfrom": + // LEFTWARDS DOUBLE ARROW FROM BAR + return rune(0x2906), true + case "Mapto": + // RIGHTWARDS DOUBLE ARROW FROM BAR + return rune(0x2907), true + case "Map": + // RIGHTWARDS TWO-HEADED ARROW FROM BAR + return rune(0x2905), true + case "Mcy": + // CYRILLIC CAPITAL LETTER EM + return rune(0x041c), true + case "MediumSpace": + // MEDIUM MATHEMATICAL SPACE + return rune(0x205f), true + case "Mellintrf": + // SCRIPT CAPITAL M + return rune(0x2133), true + case "Mfr": + // MATHEMATICAL FRAKTUR CAPITAL M + return rune(0x01d510), true + case "Mgr": + // GREEK CAPITAL LETTER MU + return rune(0x039c), true + case "MinusPlus": + // MINUS-OR-PLUS SIGN + return rune(0x2213), true + case "Mopf": + // MATHEMATICAL DOUBLE-STRUCK CAPITAL M + return rune(0x01d544), true + case "Mscr": + // SCRIPT CAPITAL M + return rune(0x2133), true + case "Mu": + // GREEK CAPITAL LETTER MU + return rune(0x039c), true + } + + case 'N': + switch name { + case "NJcy": + // CYRILLIC CAPITAL LETTER NJE + return rune(0x040a), true + case "Nacute": + // LATIN CAPITAL LETTER N WITH ACUTE + return rune(0x0143), true + case "Ncaron": + // LATIN CAPITAL LETTER N WITH CARON + return rune(0x0147), true + case "Ncedil": + // LATIN CAPITAL LETTER N WITH CEDILLA + return rune(0x0145), true + case "Ncy": + // CYRILLIC CAPITAL LETTER EN + return rune(0x041d), true + case "NegativeMediumSpace": + // ZERO WIDTH SPACE + return rune(0x200b), true + case "NegativeThickSpace": + // ZERO WIDTH SPACE + return rune(0x200b), true + case "NegativeThinSpace": + // ZERO WIDTH SPACE + return rune(0x200b), true + case "NegativeVeryThinSpace": + // ZERO WIDTH SPACE + return rune(0x200b), true + case "NestedGreaterGreater": + // MUCH GREATER-THAN + return rune(0x226b), true + case "NestedLessLess": + // MUCH LESS-THAN + return rune(0x226a), true + case "NewLine": + // LINE FEED (LF) + return rune(0x0a), true + case "Nfr": + // MATHEMATICAL FRAKTUR CAPITAL N + return rune(0x01d511), true + case "Ngr": + // GREEK CAPITAL LETTER NU + return rune(0x039d), true + case "NoBreak": + // WORD JOINER + return rune(0x2060), true + case "NonBreakingSpace": + // NO-BREAK SPACE + return rune(0xa0), true + case "Nopf": + // DOUBLE-STRUCK CAPITAL N + return rune(0x2115), true + case "NotDoubleVerticalBar": + // NOT PARALLEL TO + return rune(0x2226), true + case "NotElement": + // NOT AN ELEMENT OF + return rune(0x2209), true + case "NotEqualTilde": + // MINUS TILDE with slash + return rune(0x2242), true + case "NotEqual": + // NOT EQUAL TO + return rune(0x2260), true + case "NotExists": + // THERE DOES NOT EXIST + return rune(0x2204), true + case "NotHumpDownHump": + // GEOMETRICALLY EQUIVALENT TO with slash + return rune(0x224e), true + case "NotHumpEqual": + // DIFFERENCE BETWEEN with slash + return rune(0x224f), true + case "NotLessGreater": + // NEITHER LESS-THAN NOR GREATER-THAN + return rune(0x2278), true + case "NotReverseElement": + // DOES NOT CONTAIN AS MEMBER + return rune(0x220c), true + case "NotTilde": + // NOT TILDE + return rune(0x2241), true + case "NotTildeEqual": + // NOT ASYMPTOTICALLY EQUAL TO + return rune(0x2244), true + case "NotTildeFullEqual": + // NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO + return rune(0x2247), true + case "NotTildeTilde": + // NOT ALMOST EQUAL TO + return rune(0x2249), true + case "NotVerticalBar": + // DOES NOT DIVIDE + return rune(0x2224), true + case "Not": + // DOUBLE STROKE NOT SIGN + return rune(0x2aec), true + case "NotCongruent": + // NOT IDENTICAL TO + return rune(0x2262), true + case "NotCupCap": + // NOT EQUIVALENT TO + return rune(0x226d), true + case "NotGreaterFullEqual": + // GREATER-THAN OVER EQUAL TO with slash + return rune(0x2267), true + case "NotGreaterGreater": + // MUCH GREATER THAN with slash + return rune(0x226b), true + case "NotGreaterSlantEqual": + // GREATER-THAN OR SLANTED EQUAL TO with slash + return rune(0x2a7e), true + case "NotGreater": + // NOT GREATER-THAN + return rune(0x226f), true + case "NotGreaterEqual": + // NEITHER GREATER-THAN NOR EQUAL TO + return rune(0x2271), true + case "NotGreaterLess": + // NEITHER GREATER-THAN NOR LESS-THAN + return rune(0x2279), true + case "NotGreaterTilde": + // NEITHER GREATER-THAN NOR EQUIVALENT TO + return rune(0x2275), true + case "NotLeftTriangleBar": + // LEFT TRIANGLE BESIDE VERTICAL BAR with slash + return rune(0x29cf), true + case "NotLeftTriangle": + // NOT NORMAL SUBGROUP OF + return rune(0x22ea), true + case "NotLeftTriangleEqual": + // NOT NORMAL SUBGROUP OF OR EQUAL TO + return rune(0x22ec), true + case "NotLessLess": + // MUCH LESS THAN with slash + return rune(0x226a), true + case "NotLessSlantEqual": + // LESS-THAN OR SLANTED EQUAL TO with slash + return rune(0x2a7d), true + case "NotLess": + // NOT LESS-THAN + return rune(0x226e), true + case "NotLessEqual": + // NEITHER LESS-THAN NOR EQUAL TO + return rune(0x2270), true + case "NotLessTilde": + // NEITHER LESS-THAN NOR EQUIVALENT TO + return rune(0x2274), true + case "NotNestedGreaterGreater": + // DOUBLE NESTED GREATER-THAN with slash + return rune(0x2aa2), true + case "NotNestedLessLess": + // DOUBLE NESTED LESS-THAN with slash + return rune(0x2aa1), true + case "NotPrecedesEqual": + // PRECEDES ABOVE SINGLE-LINE EQUALS SIGN with slash + return rune(0x2aaf), true + case "NotPrecedes": + // DOES NOT PRECEDE + return rune(0x2280), true + case "NotPrecedesSlantEqual": + // DOES NOT PRECEDE OR EQUAL + return rune(0x22e0), true + case "NotRightTriangleBar": + // VERTICAL BAR BESIDE RIGHT TRIANGLE with slash + return rune(0x29d0), true + case "NotRightTriangle": + // DOES NOT CONTAIN AS NORMAL SUBGROUP + return rune(0x22eb), true + case "NotRightTriangleEqual": + // DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL + return rune(0x22ed), true + case "NotSquareSubset": + // SQUARE IMAGE OF with slash + return rune(0x228f), true + case "NotSquareSubsetEqual": + // NOT SQUARE IMAGE OF OR EQUAL TO + return rune(0x22e2), true + case "NotSquareSuperset": + // SQUARE ORIGINAL OF with slash + return rune(0x2290), true + case "NotSquareSupersetEqual": + // NOT SQUARE ORIGINAL OF OR EQUAL TO + return rune(0x22e3), true + case "NotSubset": + // SUBSET OF with vertical line + return rune(0x2282), true + case "NotSubsetEqual": + // NEITHER A SUBSET OF NOR EQUAL TO + return rune(0x2288), true + case "NotSucceedsEqual": + // SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN with slash + return rune(0x2ab0), true + case "NotSucceedsTilde": + // SUCCEEDS OR EQUIVALENT TO with slash + return rune(0x227f), true + case "NotSucceeds": + // DOES NOT SUCCEED + return rune(0x2281), true + case "NotSucceedsSlantEqual": + // DOES NOT SUCCEED OR EQUAL + return rune(0x22e1), true + case "NotSuperset": + // SUPERSET OF with vertical line + return rune(0x2283), true + case "NotSupersetEqual": + // NEITHER A SUPERSET OF NOR EQUAL TO + return rune(0x2289), true + case "Nscr": + // MATHEMATICAL SCRIPT CAPITAL N + return rune(0x01d4a9), true + case "Ntilde": + // LATIN CAPITAL LETTER N WITH TILDE + return rune(0xd1), true + case "Nu": + // GREEK CAPITAL LETTER NU + return rune(0x039d), true + } + + case 'O': + switch name { + case "OElig": + // LATIN CAPITAL LIGATURE OE + return rune(0x0152), true + case "OHacgr": + // GREEK CAPITAL LETTER OMEGA WITH TONOS + return rune(0x038f), true + case "OHgr": + // GREEK CAPITAL LETTER OMEGA + return rune(0x03a9), true + case "Oacgr": + // GREEK CAPITAL LETTER OMICRON WITH TONOS + return rune(0x038c), true + case "Oacute": + // LATIN CAPITAL LETTER O WITH ACUTE + return rune(0xd3), true + case "Ocirc": + // LATIN CAPITAL LETTER O WITH CIRCUMFLEX + return rune(0xd4), true + case "Ocy": + // CYRILLIC CAPITAL LETTER O + return rune(0x041e), true + case "Odblac": + // LATIN CAPITAL LETTER O WITH DOUBLE ACUTE + return rune(0x0150), true + case "Ofr": + // MATHEMATICAL FRAKTUR CAPITAL O + return rune(0x01d512), true + case "Ograve": + // LATIN CAPITAL LETTER O WITH GRAVE + return rune(0xd2), true + case "Ogr": + // GREEK CAPITAL LETTER OMICRON + return rune(0x039f), true + case "Omacr": + // LATIN CAPITAL LETTER O WITH MACRON + return rune(0x014c), true + case "Omega": + // GREEK CAPITAL LETTER OMEGA + return rune(0x03a9), true + case "Omicron": + // GREEK CAPITAL LETTER OMICRON + return rune(0x039f), true + case "Oopf": + // MATHEMATICAL DOUBLE-STRUCK CAPITAL O + return rune(0x01d546), true + case "OpenCurlyDoubleQuote": + // LEFT DOUBLE QUOTATION MARK + return rune(0x201c), true + case "OpenCurlyQuote": + // LEFT SINGLE QUOTATION MARK + return rune(0x2018), true + case "Or": + // DOUBLE LOGICAL OR + return rune(0x2a54), true + case "Oscr": + // MATHEMATICAL SCRIPT CAPITAL O + return rune(0x01d4aa), true + case "Oslash": + // LATIN CAPITAL LETTER O WITH STROKE + return rune(0xd8), true + case "Otilde": + // LATIN CAPITAL LETTER O WITH TILDE + return rune(0xd5), true + case "Otimes": + // MULTIPLICATION SIGN IN DOUBLE CIRCLE + return rune(0x2a37), true + case "Ouml": + // LATIN CAPITAL LETTER O WITH DIAERESIS + return rune(0xd6), true + case "OverBar": + // OVERLINE + return rune(0x203e), true + case "OverBrace": + // TOP CURLY BRACKET + return rune(0x23de), true + case "OverBracket": + // TOP SQUARE BRACKET + return rune(0x23b4), true + case "OverParenthesis": + // TOP PARENTHESIS + return rune(0x23dc), true + } + + case 'P': + switch name { + case "PHgr": + // GREEK CAPITAL LETTER PHI + return rune(0x03a6), true + case "PSgr": + // GREEK CAPITAL LETTER PSI + return rune(0x03a8), true + case "PartialD": + // PARTIAL DIFFERENTIAL + return rune(0x2202), true + case "Pcy": + // CYRILLIC CAPITAL LETTER PE + return rune(0x041f), true + case "Pfr": + // MATHEMATICAL FRAKTUR CAPITAL P + return rune(0x01d513), true + case "Pgr": + // GREEK CAPITAL LETTER PI + return rune(0x03a0), true + case "Phi": + // GREEK CAPITAL LETTER PHI + return rune(0x03a6), true + case "Pi": + // GREEK CAPITAL LETTER PI + return rune(0x03a0), true + case "PlusMinus": + // PLUS-MINUS SIGN + return rune(0xb1), true + case "Poincareplane": + // BLACK-LETTER CAPITAL H + return rune(0x210c), true + case "Popf": + // DOUBLE-STRUCK CAPITAL P + return rune(0x2119), true + case "Product": + // N-ARY PRODUCT + return rune(0x220f), true + case "Proportional": + // PROPORTIONAL TO + return rune(0x221d), true + case "Proportion": + // PROPORTION + return rune(0x2237), true + case "Pr": + // DOUBLE PRECEDES + return rune(0x2abb), true + case "PrecedesEqual": + // PRECEDES ABOVE SINGLE-LINE EQUALS SIGN + return rune(0x2aaf), true + case "Precedes": + // PRECEDES + return rune(0x227a), true + case "PrecedesSlantEqual": + // PRECEDES OR EQUAL TO + return rune(0x227c), true + case "PrecedesTilde": + // PRECEDES OR EQUIVALENT TO + return rune(0x227e), true + case "Prime": + // DOUBLE PRIME + return rune(0x2033), true + case "Pscr": + // MATHEMATICAL SCRIPT CAPITAL P + return rune(0x01d4ab), true + case "Psi": + // GREEK CAPITAL LETTER PSI + return rune(0x03a8), true + } + + case 'Q': + switch name { + case "QUOT": + // QUOTATION MARK + return rune(0x22), true + case "Qfr": + // MATHEMATICAL FRAKTUR CAPITAL Q + return rune(0x01d514), true + case "Qopf": + // DOUBLE-STRUCK CAPITAL Q + return rune(0x211a), true + case "Qscr": + // MATHEMATICAL SCRIPT CAPITAL Q + return rune(0x01d4ac), true + } + + case 'R': + switch name { + case "RBarr": + // RIGHTWARDS TWO-HEADED TRIPLE DASH ARROW + return rune(0x2910), true + case "REG": + // REGISTERED SIGN + return rune(0xae), true + case "Racute": + // LATIN CAPITAL LETTER R WITH ACUTE + return rune(0x0154), true + case "Rang": + // MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET + return rune(0x27eb), true + case "Rarr": + // RIGHTWARDS TWO HEADED ARROW + return rune(0x21a0), true + case "Rarrtl": + // RIGHTWARDS TWO-HEADED ARROW WITH TAIL + return rune(0x2916), true + case "Rcaron": + // LATIN CAPITAL LETTER R WITH CARON + return rune(0x0158), true + case "Rcedil": + // LATIN CAPITAL LETTER R WITH CEDILLA + return rune(0x0156), true + case "Rcy": + // CYRILLIC CAPITAL LETTER ER + return rune(0x0420), true + case "ReverseElement": + // CONTAINS AS MEMBER + return rune(0x220b), true + case "ReverseEquilibrium": + // LEFTWARDS HARPOON OVER RIGHTWARDS HARPOON + return rune(0x21cb), true + case "Re": + // BLACK-LETTER CAPITAL R + return rune(0x211c), true + case "ReverseUpEquilibrium": + // DOWNWARDS HARPOON WITH BARB LEFT BESIDE UPWARDS HARPOON WITH BARB RIGHT + return rune(0x296f), true + case "Rfr": + // BLACK-LETTER CAPITAL R + return rune(0x211c), true + case "Rgr": + // GREEK CAPITAL LETTER RHO + return rune(0x03a1), true + case "Rho": + // GREEK CAPITAL LETTER RHO + return rune(0x03a1), true + case "RightAngleBracket": + // MATHEMATICAL RIGHT ANGLE BRACKET + return rune(0x27e9), true + case "RightArrowBar": + // RIGHTWARDS ARROW TO BAR + return rune(0x21e5), true + case "RightArrow": + // RIGHTWARDS ARROW + return rune(0x2192), true + case "RightArrowLeftArrow": + // RIGHTWARDS ARROW OVER LEFTWARDS ARROW + return rune(0x21c4), true + case "RightCeiling": + // RIGHT CEILING + return rune(0x2309), true + case "RightDoubleBracket": + // MATHEMATICAL RIGHT WHITE SQUARE BRACKET + return rune(0x27e7), true + case "RightDownTeeVector": + // DOWNWARDS HARPOON WITH BARB RIGHT FROM BAR + return rune(0x295d), true + case "RightDownVector": + // DOWNWARDS HARPOON WITH BARB RIGHTWARDS + return rune(0x21c2), true + case "RightDownVectorBar": + // DOWNWARDS HARPOON WITH BARB RIGHT TO BAR + return rune(0x2955), true + case "RightFloor": + // RIGHT FLOOR + return rune(0x230b), true + case "RightTeeArrow": + // RIGHTWARDS ARROW FROM BAR + return rune(0x21a6), true + case "RightTeeVector": + // RIGHTWARDS HARPOON WITH BARB UP FROM BAR + return rune(0x295b), true + case "RightTee": + // RIGHT TACK + return rune(0x22a2), true + case "RightTriangleBar": + // VERTICAL BAR BESIDE RIGHT TRIANGLE + return rune(0x29d0), true + case "RightTriangle": + // CONTAINS AS NORMAL SUBGROUP + return rune(0x22b3), true + case "RightTriangleEqual": + // CONTAINS AS NORMAL SUBGROUP OR EQUAL TO + return rune(0x22b5), true + case "RightUpDownVector": + // UP BARB RIGHT DOWN BARB RIGHT HARPOON + return rune(0x294f), true + case "RightUpTeeVector": + // UPWARDS HARPOON WITH BARB RIGHT FROM BAR + return rune(0x295c), true + case "RightUpVector": + // UPWARDS HARPOON WITH BARB RIGHTWARDS + return rune(0x21be), true + case "RightUpVectorBar": + // UPWARDS HARPOON WITH BARB RIGHT TO BAR + return rune(0x2954), true + case "RightVector": + // RIGHTWARDS HARPOON WITH BARB UPWARDS + return rune(0x21c0), true + case "RightVectorBar": + // RIGHTWARDS HARPOON WITH BARB UP TO BAR + return rune(0x2953), true + case "Rightarrow": + // RIGHTWARDS DOUBLE ARROW + return rune(0x21d2), true + case "Ropf": + // DOUBLE-STRUCK CAPITAL R + return rune(0x211d), true + case "RoundImplies": + // RIGHT DOUBLE ARROW WITH ROUNDED HEAD + return rune(0x2970), true + case "Rrightarrow": + // RIGHTWARDS TRIPLE ARROW + return rune(0x21db), true + case "Rscr": + // SCRIPT CAPITAL R + return rune(0x211b), true + case "Rsh": + // UPWARDS ARROW WITH TIP RIGHTWARDS + return rune(0x21b1), true + case "RuleDelayed": + // RULE-DELAYED + return rune(0x29f4), true + } + + case 'S': + switch name { + case "SHCHcy": + // CYRILLIC CAPITAL LETTER SHCHA + return rune(0x0429), true + case "SHcy": + // CYRILLIC CAPITAL LETTER SHA + return rune(0x0428), true + case "SOFTcy": + // CYRILLIC CAPITAL LETTER SOFT SIGN + return rune(0x042c), true + case "Sacute": + // LATIN CAPITAL LETTER S WITH ACUTE + return rune(0x015a), true + case "Sc": + // DOUBLE SUCCEEDS + return rune(0x2abc), true + case "Scaron": + // LATIN CAPITAL LETTER S WITH CARON + return rune(0x0160), true + case "Scedil": + // LATIN CAPITAL LETTER S WITH CEDILLA + return rune(0x015e), true + case "Scirc": + // LATIN CAPITAL LETTER S WITH CIRCUMFLEX + return rune(0x015c), true + case "Scy": + // CYRILLIC CAPITAL LETTER ES + return rune(0x0421), true + case "Sfr": + // MATHEMATICAL FRAKTUR CAPITAL S + return rune(0x01d516), true + case "Sgr": + // GREEK CAPITAL LETTER SIGMA + return rune(0x03a3), true + case "ShortDownArrow": + // DOWNWARDS ARROW + return rune(0x2193), true + case "ShortLeftArrow": + // LEFTWARDS ARROW + return rune(0x2190), true + case "ShortRightArrow": + // RIGHTWARDS ARROW + return rune(0x2192), true + case "ShortUpArrow": + // UPWARDS ARROW + return rune(0x2191), true + case "Sigma": + // GREEK CAPITAL LETTER SIGMA + return rune(0x03a3), true + case "SmallCircle": + // RING OPERATOR + return rune(0x2218), true + case "Sopf": + // MATHEMATICAL DOUBLE-STRUCK CAPITAL S + return rune(0x01d54a), true + case "Sqrt": + // SQUARE ROOT + return rune(0x221a), true + case "SquareIntersection": + // SQUARE CAP + return rune(0x2293), true + case "SquareSubset": + // SQUARE IMAGE OF + return rune(0x228f), true + case "SquareSubsetEqual": + // SQUARE IMAGE OF OR EQUAL TO + return rune(0x2291), true + case "Square": + // WHITE SQUARE + return rune(0x25a1), true + case "SquareSuperset": + // SQUARE ORIGINAL OF + return rune(0x2290), true + case "SquareSupersetEqual": + // SQUARE ORIGINAL OF OR EQUAL TO + return rune(0x2292), true + case "SquareUnion": + // SQUARE CUP + return rune(0x2294), true + case "Sscr": + // MATHEMATICAL SCRIPT CAPITAL S + return rune(0x01d4ae), true + case "Star": + // STAR OPERATOR + return rune(0x22c6), true + case "Sub": + // DOUBLE SUBSET + return rune(0x22d0), true + case "Subset": + // DOUBLE SUBSET + return rune(0x22d0), true + case "SubsetEqual": + // SUBSET OF OR EQUAL TO + return rune(0x2286), true + case "Succeeds": + // SUCCEEDS + return rune(0x227b), true + case "SucceedsEqual": + // SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN + return rune(0x2ab0), true + case "SucceedsSlantEqual": + // SUCCEEDS OR EQUAL TO + return rune(0x227d), true + case "SucceedsTilde": + // SUCCEEDS OR EQUIVALENT TO + return rune(0x227f), true + case "SuchThat": + // CONTAINS AS MEMBER + return rune(0x220b), true + case "Sum": + // N-ARY SUMMATION + return rune(0x2211), true + case "SupersetEqual": + // SUPERSET OF OR EQUAL TO + return rune(0x2287), true + case "Sup": + // DOUBLE SUPERSET + return rune(0x22d1), true + case "Superset": + // SUPERSET OF + return rune(0x2283), true + case "Supset": + // DOUBLE SUPERSET + return rune(0x22d1), true + } + + case 'T': + switch name { + case "THORN": + // LATIN CAPITAL LETTER THORN + return rune(0xde), true + case "THgr": + // GREEK CAPITAL LETTER THETA + return rune(0x0398), true + case "TRADE": + // TRADE MARK SIGN + return rune(0x2122), true + case "TSHcy": + // CYRILLIC CAPITAL LETTER TSHE + return rune(0x040b), true + case "TScy": + // CYRILLIC CAPITAL LETTER TSE + return rune(0x0426), true + case "Tab": + // CHARACTER TABULATION + return rune(0x09), true + case "Tau": + // GREEK CAPITAL LETTER TAU + return rune(0x03a4), true + case "Tcaron": + // LATIN CAPITAL LETTER T WITH CARON + return rune(0x0164), true + case "Tcedil": + // LATIN CAPITAL LETTER T WITH CEDILLA + return rune(0x0162), true + case "Tcy": + // CYRILLIC CAPITAL LETTER TE + return rune(0x0422), true + case "Tfr": + // MATHEMATICAL FRAKTUR CAPITAL T + return rune(0x01d517), true + case "Tgr": + // GREEK CAPITAL LETTER TAU + return rune(0x03a4), true + case "Therefore": + // THEREFORE + return rune(0x2234), true + case "Theta": + // GREEK CAPITAL LETTER THETA + return rune(0x0398), true + case "Thetav": + // GREEK CAPITAL THETA SYMBOL + return rune(0x03f4), true + case "ThickSpace": + // space of width 5/18 em + return rune(0x205f), true + case "ThinSpace": + // THIN SPACE + return rune(0x2009), true + case "Tilde": + // TILDE OPERATOR + return rune(0x223c), true + case "TildeEqual": + // ASYMPTOTICALLY EQUAL TO + return rune(0x2243), true + case "TildeFullEqual": + // APPROXIMATELY EQUAL TO + return rune(0x2245), true + case "TildeTilde": + // ALMOST EQUAL TO + return rune(0x2248), true + case "Topf": + // MATHEMATICAL DOUBLE-STRUCK CAPITAL T + return rune(0x01d54b), true + case "TripleDot": + // COMBINING THREE DOTS ABOVE + return rune(0x20db), true + case "Tscr": + // MATHEMATICAL SCRIPT CAPITAL T + return rune(0x01d4af), true + case "Tstrok": + // LATIN CAPITAL LETTER T WITH STROKE + return rune(0x0166), true + } + + case 'U': + switch name { + case "Uacgr": + // GREEK CAPITAL LETTER UPSILON WITH TONOS + return rune(0x038e), true + case "Uacute": + // LATIN CAPITAL LETTER U WITH ACUTE + return rune(0xda), true + case "Uarrocir": + // UPWARDS TWO-HEADED ARROW FROM SMALL CIRCLE + return rune(0x2949), true + case "Uarr": + // UPWARDS TWO HEADED ARROW + return rune(0x219f), true + case "Ubrcy": + // CYRILLIC CAPITAL LETTER SHORT U + return rune(0x040e), true + case "Ubreve": + // LATIN CAPITAL LETTER U WITH BREVE + return rune(0x016c), true + case "Ucirc": + // LATIN CAPITAL LETTER U WITH CIRCUMFLEX + return rune(0xdb), true + case "Ucy": + // CYRILLIC CAPITAL LETTER U + return rune(0x0423), true + case "Udblac": + // LATIN CAPITAL LETTER U WITH DOUBLE ACUTE + return rune(0x0170), true + case "Udigr": + // GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA + return rune(0x03ab), true + case "Ufr": + // MATHEMATICAL FRAKTUR CAPITAL U + return rune(0x01d518), true + case "Ugrave": + // LATIN CAPITAL LETTER U WITH GRAVE + return rune(0xd9), true + case "Ugr": + // GREEK CAPITAL LETTER UPSILON + return rune(0x03a5), true + case "Umacr": + // LATIN CAPITAL LETTER U WITH MACRON + return rune(0x016a), true + case "UnderBar": + // LOW LINE + return rune(0x5f), true + case "UnderBrace": + // BOTTOM CURLY BRACKET + return rune(0x23df), true + case "UnderBracket": + // BOTTOM SQUARE BRACKET + return rune(0x23b5), true + case "UnderParenthesis": + // BOTTOM PARENTHESIS + return rune(0x23dd), true + case "Union": + // N-ARY UNION + return rune(0x22c3), true + case "UnionPlus": + // MULTISET UNION + return rune(0x228e), true + case "Uogon": + // LATIN CAPITAL LETTER U WITH OGONEK + return rune(0x0172), true + case "Uopf": + // MATHEMATICAL DOUBLE-STRUCK CAPITAL U + return rune(0x01d54c), true + case "UpArrow": + // UPWARDS ARROW + return rune(0x2191), true + case "UpArrowBar": + // UPWARDS ARROW TO BAR + return rune(0x2912), true + case "UpArrowDownArrow": + // UPWARDS ARROW LEFTWARDS OF DOWNWARDS ARROW + return rune(0x21c5), true + case "UpDownArrow": + // UP DOWN ARROW + return rune(0x2195), true + case "UpEquilibrium": + // UPWARDS HARPOON WITH BARB LEFT BESIDE DOWNWARDS HARPOON WITH BARB RIGHT + return rune(0x296e), true + case "UpTee": + // UP TACK + return rune(0x22a5), true + case "UpTeeArrow": + // UPWARDS ARROW FROM BAR + return rune(0x21a5), true + case "Uparrow": + // UPWARDS DOUBLE ARROW + return rune(0x21d1), true + case "Updownarrow": + // UP DOWN DOUBLE ARROW + return rune(0x21d5), true + case "UpperLeftArrow": + // NORTH WEST ARROW + return rune(0x2196), true + case "UpperRightArrow": + // NORTH EAST ARROW + return rune(0x2197), true + case "Upsilon": + // GREEK CAPITAL LETTER UPSILON + return rune(0x03a5), true + case "Upsi": + // GREEK UPSILON WITH HOOK SYMBOL + return rune(0x03d2), true + case "Uring": + // LATIN CAPITAL LETTER U WITH RING ABOVE + return rune(0x016e), true + case "Uscr": + // MATHEMATICAL SCRIPT CAPITAL U + return rune(0x01d4b0), true + case "Utilde": + // LATIN CAPITAL LETTER U WITH TILDE + return rune(0x0168), true + case "Uuml": + // LATIN CAPITAL LETTER U WITH DIAERESIS + return rune(0xdc), true + } + + case 'V': + switch name { + case "VDash": + // DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE + return rune(0x22ab), true + case "Vbar": + // DOUBLE UP TACK + return rune(0x2aeb), true + case "Vcy": + // CYRILLIC CAPITAL LETTER VE + return rune(0x0412), true + case "Vdashl": + // LONG DASH FROM LEFT MEMBER OF DOUBLE VERTICAL + return rune(0x2ae6), true + case "Vdash": + // FORCES + return rune(0x22a9), true + case "Vee": + // N-ARY LOGICAL OR + return rune(0x22c1), true + case "Verbar": + // DOUBLE VERTICAL LINE + return rune(0x2016), true + case "Vert": + // DOUBLE VERTICAL LINE + return rune(0x2016), true + case "VerticalBar": + // DIVIDES + return rune(0x2223), true + case "VerticalLine": + // VERTICAL LINE + return rune(0x7c), true + case "VerticalSeparator": + // LIGHT VERTICAL BAR + return rune(0x2758), true + case "VerticalTilde": + // WREATH PRODUCT + return rune(0x2240), true + case "VeryThinSpace": + // HAIR SPACE + return rune(0x200a), true + case "Vfr": + // MATHEMATICAL FRAKTUR CAPITAL V + return rune(0x01d519), true + case "Vopf": + // MATHEMATICAL DOUBLE-STRUCK CAPITAL V + return rune(0x01d54d), true + case "Vscr": + // MATHEMATICAL SCRIPT CAPITAL V + return rune(0x01d4b1), true + case "Vvdash": + // TRIPLE VERTICAL BAR RIGHT TURNSTILE + return rune(0x22aa), true + } + + case 'W': + switch name { + case "Wcirc": + // LATIN CAPITAL LETTER W WITH CIRCUMFLEX + return rune(0x0174), true + case "Wedge": + // N-ARY LOGICAL AND + return rune(0x22c0), true + case "Wfr": + // MATHEMATICAL FRAKTUR CAPITAL W + return rune(0x01d51a), true + case "Wopf": + // MATHEMATICAL DOUBLE-STRUCK CAPITAL W + return rune(0x01d54e), true + case "Wscr": + // MATHEMATICAL SCRIPT CAPITAL W + return rune(0x01d4b2), true + } + + case 'X': + switch name { + case "Xfr": + // MATHEMATICAL FRAKTUR CAPITAL X + return rune(0x01d51b), true + case "Xgr": + // GREEK CAPITAL LETTER XI + return rune(0x039e), true + case "Xi": + // GREEK CAPITAL LETTER XI + return rune(0x039e), true + case "Xopf": + // MATHEMATICAL DOUBLE-STRUCK CAPITAL X + return rune(0x01d54f), true + case "Xscr": + // MATHEMATICAL SCRIPT CAPITAL X + return rune(0x01d4b3), true + } + + case 'Y': + switch name { + case "YAcy": + // CYRILLIC CAPITAL LETTER YA + return rune(0x042f), true + case "YIcy": + // CYRILLIC CAPITAL LETTER YI + return rune(0x0407), true + case "YUcy": + // CYRILLIC CAPITAL LETTER YU + return rune(0x042e), true + case "Yacute": + // LATIN CAPITAL LETTER Y WITH ACUTE + return rune(0xdd), true + case "Ycirc": + // LATIN CAPITAL LETTER Y WITH CIRCUMFLEX + return rune(0x0176), true + case "Ycy": + // CYRILLIC CAPITAL LETTER YERU + return rune(0x042b), true + case "Yfr": + // MATHEMATICAL FRAKTUR CAPITAL Y + return rune(0x01d51c), true + case "Yopf": + // MATHEMATICAL DOUBLE-STRUCK CAPITAL Y + return rune(0x01d550), true + case "Yscr": + // MATHEMATICAL SCRIPT CAPITAL Y + return rune(0x01d4b4), true + case "Yuml": + // LATIN CAPITAL LETTER Y WITH DIAERESIS + return rune(0x0178), true + } + + case 'Z': + switch name { + case "ZHcy": + // CYRILLIC CAPITAL LETTER ZHE + return rune(0x0416), true + case "Zacute": + // LATIN CAPITAL LETTER Z WITH ACUTE + return rune(0x0179), true + case "Zcaron": + // LATIN CAPITAL LETTER Z WITH CARON + return rune(0x017d), true + case "Zcy": + // CYRILLIC CAPITAL LETTER ZE + return rune(0x0417), true + case "Zdot": + // LATIN CAPITAL LETTER Z WITH DOT ABOVE + return rune(0x017b), true + case "ZeroWidthSpace": + // ZERO WIDTH SPACE + return rune(0x200b), true + case "Zeta": + // GREEK CAPITAL LETTER ZETA + return rune(0x0396), true + case "Zfr": + // BLACK-LETTER CAPITAL Z + return rune(0x2128), true + case "Zgr": + // GREEK CAPITAL LETTER ZETA + return rune(0x0396), true + case "Zopf": + // DOUBLE-STRUCK CAPITAL Z + return rune(0x2124), true + case "Zscr": + // MATHEMATICAL SCRIPT CAPITAL Z + return rune(0x01d4b5), true + } + + case 'a': + switch name { + case "aacgr": + // GREEK SMALL LETTER ALPHA WITH TONOS + return rune(0x03ac), true + case "aacute": + // LATIN SMALL LETTER A WITH ACUTE + return rune(0xe1), true + case "abreve": + // LATIN SMALL LETTER A WITH BREVE + return rune(0x0103), true + case "acE": + // INVERTED LAZY S with double underline + return rune(0x223e), true + case "acd": + // SINE WAVE + return rune(0x223f), true + case "acute": + // ACUTE ACCENT + return rune(0xb4), true + case "ac": + // INVERTED LAZY S + return rune(0x223e), true + case "acirc": + // LATIN SMALL LETTER A WITH CIRCUMFLEX + return rune(0xe2), true + case "actuary": + // COMBINING ANNUITY SYMBOL + return rune(0x20e7), true + case "acy": + // CYRILLIC SMALL LETTER A + return rune(0x0430), true + case "aelig": + // LATIN SMALL LETTER AE + return rune(0xe6), true + case "af": + // FUNCTION APPLICATION + return rune(0x2061), true + case "afr": + // MATHEMATICAL FRAKTUR SMALL A + return rune(0x01d51e), true + case "agr": + // GREEK SMALL LETTER ALPHA + return rune(0x03b1), true + case "agrave": + // LATIN SMALL LETTER A WITH GRAVE + return rune(0xe0), true + case "alefsym": + // ALEF SYMBOL + return rune(0x2135), true + case "aleph": + // ALEF SYMBOL + return rune(0x2135), true + case "alpha": + // GREEK SMALL LETTER ALPHA + return rune(0x03b1), true + case "amacr": + // LATIN SMALL LETTER A WITH MACRON + return rune(0x0101), true + case "amalg": + // AMALGAMATION OR COPRODUCT + return rune(0x2a3f), true + case "amp": + // AMPERSAND + return rune(0x26), true + case "andand": + // TWO INTERSECTING LOGICAL AND + return rune(0x2a55), true + case "andd": + // LOGICAL AND WITH HORIZONTAL DASH + return rune(0x2a5c), true + case "andslope": + // SLOPING LARGE AND + return rune(0x2a58), true + case "andv": + // LOGICAL AND WITH MIDDLE STEM + return rune(0x2a5a), true + case "and": + // LOGICAL AND + return rune(0x2227), true + case "angdnl": + // TURNED ANGLE + return rune(0x29a2), true + case "angdnr": + // ACUTE ANGLE + return rune(0x299f), true + case "ange": + // ANGLE WITH UNDERBAR + return rune(0x29a4), true + case "angles": + // ANGLE WITH S INSIDE + return rune(0x299e), true + case "angle": + // ANGLE + return rune(0x2220), true + case "angmsdaa": + // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING UP AND RIGHT + return rune(0x29a8), true + case "angmsdab": + // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING UP AND LEFT + return rune(0x29a9), true + case "angmsdac": + // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING DOWN AND RIGHT + return rune(0x29aa), true + case "angmsdad": + // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING DOWN AND LEFT + return rune(0x29ab), true + case "angmsdae": + // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING RIGHT AND UP + return rune(0x29ac), true + case "angmsdaf": + // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING LEFT AND UP + return rune(0x29ad), true + case "angmsdag": + // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING RIGHT AND DOWN + return rune(0x29ae), true + case "angmsdah": + // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING LEFT AND DOWN + return rune(0x29af), true + case "angmsd": + // MEASURED ANGLE + return rune(0x2221), true + case "angrtvbd": + // MEASURED RIGHT ANGLE WITH DOT + return rune(0x299d), true + case "angrtvb": + // RIGHT ANGLE WITH ARC + return rune(0x22be), true + case "angsph": + // SPHERICAL ANGLE + return rune(0x2222), true + case "angst": + // LATIN CAPITAL LETTER A WITH RING ABOVE + return rune(0xc5), true + case "angupl": + // REVERSED ANGLE + return rune(0x29a3), true + case "angzarr": + // RIGHT ANGLE WITH DOWNWARDS ZIGZAG ARROW + return rune(0x237c), true + case "ang": + // ANGLE + return rune(0x2220), true + case "ang90": + // RIGHT ANGLE + return rune(0x221f), true + case "angrt": + // RIGHT ANGLE + return rune(0x221f), true + case "aogon": + // LATIN SMALL LETTER A WITH OGONEK + return rune(0x0105), true + case "aopf": + // MATHEMATICAL DOUBLE-STRUCK SMALL A + return rune(0x01d552), true + case "apE": + // APPROXIMATELY EQUAL OR EQUAL TO + return rune(0x2a70), true + case "apacir": + // ALMOST EQUAL TO WITH CIRCUMFLEX ACCENT + return rune(0x2a6f), true + case "ape": + // ALMOST EQUAL OR EQUAL TO + return rune(0x224a), true + case "apid": + // TRIPLE TILDE + return rune(0x224b), true + case "approxeq": + // ALMOST EQUAL OR EQUAL TO + return rune(0x224a), true + case "approx": + // ALMOST EQUAL TO + return rune(0x2248), true + case "ap": + // ALMOST EQUAL TO + return rune(0x2248), true + case "apos": + // APOSTROPHE + return rune(0x27), true + case "aring": + // LATIN SMALL LETTER A WITH RING ABOVE + return rune(0xe5), true + case "arrllsr": + // LEFTWARDS ARROW ABOVE SHORT RIGHTWARDS ARROW + return rune(0x2943), true + case "arrlrsl": + // RIGHTWARDS ARROW ABOVE SHORT LEFTWARDS ARROW + return rune(0x2942), true + case "arrsrll": + // SHORT RIGHTWARDS ARROW ABOVE LEFTWARDS ARROW + return rune(0x2944), true + case "ascr": + // MATHEMATICAL SCRIPT SMALL A + return rune(0x01d4b6), true + case "astb": + // SQUARED ASTERISK + return rune(0x29c6), true + case "ast": + // ASTERISK + return rune(0x2a), true + case "asympeq": + // EQUIVALENT TO + return rune(0x224d), true + case "asymp": + // ALMOST EQUAL TO + return rune(0x2248), true + case "atilde": + // LATIN SMALL LETTER A WITH TILDE + return rune(0xe3), true + case "auml": + // LATIN SMALL LETTER A WITH DIAERESIS + return rune(0xe4), true + case "awconint": + // ANTICLOCKWISE CONTOUR INTEGRAL + return rune(0x2233), true + case "awint": + // ANTICLOCKWISE INTEGRATION + return rune(0x2a11), true + } + + case 'b': + switch name { + case "b.Delta": + // MATHEMATICAL BOLD CAPITAL DELTA + return rune(0x01d6ab), true + case "b.Gamma": + // MATHEMATICAL BOLD CAPITAL GAMMA + return rune(0x01d6aa), true + case "b.Gammad": + // MATHEMATICAL BOLD CAPITAL DIGAMMA + return rune(0x01d7ca), true + case "b.Lambda": + // MATHEMATICAL BOLD CAPITAL LAMDA + return rune(0x01d6b2), true + case "b.Omega": + // MATHEMATICAL BOLD CAPITAL OMEGA + return rune(0x01d6c0), true + case "b.Phi": + // MATHEMATICAL BOLD CAPITAL PHI + return rune(0x01d6bd), true + case "b.Pi": + // MATHEMATICAL BOLD CAPITAL PI + return rune(0x01d6b7), true + case "b.Psi": + // MATHEMATICAL BOLD CAPITAL PSI + return rune(0x01d6bf), true + case "b.Sigma": + // MATHEMATICAL BOLD CAPITAL SIGMA + return rune(0x01d6ba), true + case "b.Theta": + // MATHEMATICAL BOLD CAPITAL THETA + return rune(0x01d6af), true + case "b.Upsi": + // MATHEMATICAL BOLD CAPITAL UPSILON + return rune(0x01d6bc), true + case "b.Xi": + // MATHEMATICAL BOLD CAPITAL XI + return rune(0x01d6b5), true + case "b.alpha": + // MATHEMATICAL BOLD SMALL ALPHA + return rune(0x01d6c2), true + case "b.beta": + // MATHEMATICAL BOLD SMALL BETA + return rune(0x01d6c3), true + case "b.chi": + // MATHEMATICAL BOLD SMALL CHI + return rune(0x01d6d8), true + case "b.delta": + // MATHEMATICAL BOLD SMALL DELTA + return rune(0x01d6c5), true + case "b.epsi": + // MATHEMATICAL BOLD SMALL EPSILON + return rune(0x01d6c6), true + case "b.epsiv": + // MATHEMATICAL BOLD EPSILON SYMBOL + return rune(0x01d6dc), true + case "b.eta": + // MATHEMATICAL BOLD SMALL ETA + return rune(0x01d6c8), true + case "b.gammad": + // MATHEMATICAL BOLD SMALL DIGAMMA + return rune(0x01d7cb), true + case "b.gamma": + // MATHEMATICAL BOLD SMALL GAMMA + return rune(0x01d6c4), true + case "b.iota": + // MATHEMATICAL BOLD SMALL IOTA + return rune(0x01d6ca), true + case "b.kappa": + // MATHEMATICAL BOLD SMALL KAPPA + return rune(0x01d6cb), true + case "b.kappav": + // MATHEMATICAL BOLD KAPPA SYMBOL + return rune(0x01d6de), true + case "b.lambda": + // MATHEMATICAL BOLD SMALL LAMDA + return rune(0x01d6cc), true + case "b.mu": + // MATHEMATICAL BOLD SMALL MU + return rune(0x01d6cd), true + case "b.nu": + // MATHEMATICAL BOLD SMALL NU + return rune(0x01d6ce), true + case "b.omega": + // MATHEMATICAL BOLD SMALL OMEGA + return rune(0x01d6da), true + case "b.phi": + // MATHEMATICAL BOLD SMALL PHI + return rune(0x01d6d7), true + case "b.phiv": + // MATHEMATICAL BOLD PHI SYMBOL + return rune(0x01d6df), true + case "b.pi": + // MATHEMATICAL BOLD SMALL PI + return rune(0x01d6d1), true + case "b.piv": + // MATHEMATICAL BOLD PI SYMBOL + return rune(0x01d6e1), true + case "b.psi": + // MATHEMATICAL BOLD SMALL PSI + return rune(0x01d6d9), true + case "b.rho": + // MATHEMATICAL BOLD SMALL RHO + return rune(0x01d6d2), true + case "b.rhov": + // MATHEMATICAL BOLD RHO SYMBOL + return rune(0x01d6e0), true + case "b.sigmav": + // MATHEMATICAL BOLD SMALL FINAL SIGMA + return rune(0x01d6d3), true + case "b.sigma": + // MATHEMATICAL BOLD SMALL SIGMA + return rune(0x01d6d4), true + case "b.tau": + // MATHEMATICAL BOLD SMALL TAU + return rune(0x01d6d5), true + case "b.thetas": + // MATHEMATICAL BOLD SMALL THETA + return rune(0x01d6c9), true + case "b.thetav": + // MATHEMATICAL BOLD THETA SYMBOL + return rune(0x01d6dd), true + case "b.upsi": + // MATHEMATICAL BOLD SMALL UPSILON + return rune(0x01d6d6), true + case "b.xi": + // MATHEMATICAL BOLD SMALL XI + return rune(0x01d6cf), true + case "b.zeta": + // MATHEMATICAL BOLD SMALL ZETA + return rune(0x01d6c7), true + case "bNot": + // REVERSED DOUBLE STROKE NOT SIGN + return rune(0x2aed), true + case "backcong": + // ALL EQUAL TO + return rune(0x224c), true + case "backepsilon": + // GREEK REVERSED LUNATE EPSILON SYMBOL + return rune(0x03f6), true + case "backprime": + // REVERSED PRIME + return rune(0x2035), true + case "backsimeq": + // REVERSED TILDE EQUALS + return rune(0x22cd), true + case "backsim": + // REVERSED TILDE + return rune(0x223d), true + case "barV": + // DOUBLE DOWN TACK + return rune(0x2aea), true + case "barvee": + // NOR + return rune(0x22bd), true + case "barwed": + // PROJECTIVE + return rune(0x2305), true + case "barwedge": + // PROJECTIVE + return rune(0x2305), true + case "bbrk": + // BOTTOM SQUARE BRACKET + return rune(0x23b5), true + case "bbrktbrk": + // BOTTOM SQUARE BRACKET OVER TOP SQUARE BRACKET + return rune(0x23b6), true + case "bcong": + // ALL EQUAL TO + return rune(0x224c), true + case "bcy": + // CYRILLIC SMALL LETTER BE + return rune(0x0431), true + case "bdlhar": + // DOWNWARDS HARPOON WITH BARB LEFT FROM BAR + return rune(0x2961), true + case "bdquo": + // DOUBLE LOW-9 QUOTATION MARK + return rune(0x201e), true + case "bdrhar": + // DOWNWARDS HARPOON WITH BARB RIGHT FROM BAR + return rune(0x295d), true + case "because": + // BECAUSE + return rune(0x2235), true + case "becaus": + // BECAUSE + return rune(0x2235), true + case "bemptyv": + // REVERSED EMPTY SET + return rune(0x29b0), true + case "bepsi": + // GREEK REVERSED LUNATE EPSILON SYMBOL + return rune(0x03f6), true + case "bernou": + // SCRIPT CAPITAL B + return rune(0x212c), true + case "beta": + // GREEK SMALL LETTER BETA + return rune(0x03b2), true + case "beth": + // BET SYMBOL + return rune(0x2136), true + case "between": + // BETWEEN + return rune(0x226c), true + case "bfr": + // MATHEMATICAL FRAKTUR SMALL B + return rune(0x01d51f), true + case "bgr": + // GREEK SMALL LETTER BETA + return rune(0x03b2), true + case "bigcap": + // N-ARY INTERSECTION + return rune(0x22c2), true + case "bigcirc": + // LARGE CIRCLE + return rune(0x25ef), true + case "bigcup": + // N-ARY UNION + return rune(0x22c3), true + case "bigodot": + // N-ARY CIRCLED DOT OPERATOR + return rune(0x2a00), true + case "bigoplus": + // N-ARY CIRCLED PLUS OPERATOR + return rune(0x2a01), true + case "bigotimes": + // N-ARY CIRCLED TIMES OPERATOR + return rune(0x2a02), true + case "bigsqcup": + // N-ARY SQUARE UNION OPERATOR + return rune(0x2a06), true + case "bigstar": + // BLACK STAR + return rune(0x2605), true + case "bigtriangledown": + // WHITE DOWN-POINTING TRIANGLE + return rune(0x25bd), true + case "bigtriangleup": + // WHITE UP-POINTING TRIANGLE + return rune(0x25b3), true + case "biguplus": + // N-ARY UNION OPERATOR WITH PLUS + return rune(0x2a04), true + case "bigvee": + // N-ARY LOGICAL OR + return rune(0x22c1), true + case "bigwedge": + // N-ARY LOGICAL AND + return rune(0x22c0), true + case "bkarow": + // RIGHTWARDS DOUBLE DASH ARROW + return rune(0x290d), true + case "blacklozenge": + // BLACK LOZENGE + return rune(0x29eb), true + case "blacksquare": + // BLACK SMALL SQUARE + return rune(0x25aa), true + case "blacktriangledown": + // BLACK DOWN-POINTING SMALL TRIANGLE + return rune(0x25be), true + case "blacktriangleleft": + // BLACK LEFT-POINTING SMALL TRIANGLE + return rune(0x25c2), true + case "blacktriangleright": + // BLACK RIGHT-POINTING SMALL TRIANGLE + return rune(0x25b8), true + case "blacktriangle": + // BLACK UP-POINTING SMALL TRIANGLE + return rune(0x25b4), true + case "blank": + // BLANK SYMBOL + return rune(0x2422), true + case "bldhar": + // LEFTWARDS HARPOON WITH BARB DOWN FROM BAR + return rune(0x295e), true + case "blk12": + // MEDIUM SHADE + return rune(0x2592), true + case "blk14": + // LIGHT SHADE + return rune(0x2591), true + case "blk34": + // DARK SHADE + return rune(0x2593), true + case "block": + // FULL BLOCK + return rune(0x2588), true + case "bluhar": + // LEFTWARDS HARPOON WITH BARB UP FROM BAR + return rune(0x295a), true + case "bnequiv": + // IDENTICAL TO with reverse slash + return rune(0x2261), true + case "bne": + // EQUALS SIGN with reverse slash + return rune(0x3d), true + case "bnot": + // REVERSED NOT SIGN + return rune(0x2310), true + case "bopf": + // MATHEMATICAL DOUBLE-STRUCK SMALL B + return rune(0x01d553), true + case "bot": + // UP TACK + return rune(0x22a5), true + case "bottom": + // UP TACK + return rune(0x22a5), true + case "bowtie": + // BOWTIE + return rune(0x22c8), true + case "boxDL": + // BOX DRAWINGS DOUBLE DOWN AND LEFT + return rune(0x2557), true + case "boxDR": + // BOX DRAWINGS DOUBLE DOWN AND RIGHT + return rune(0x2554), true + case "boxDl": + // BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE + return rune(0x2556), true + case "boxDr": + // BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE + return rune(0x2553), true + case "boxHD": + // BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL + return rune(0x2566), true + case "boxHU": + // BOX DRAWINGS DOUBLE UP AND HORIZONTAL + return rune(0x2569), true + case "boxHd": + // BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE + return rune(0x2564), true + case "boxHu": + // BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE + return rune(0x2567), true + case "boxH": + // BOX DRAWINGS DOUBLE HORIZONTAL + return rune(0x2550), true + case "boxUL": + // BOX DRAWINGS DOUBLE UP AND LEFT + return rune(0x255d), true + case "boxUR": + // BOX DRAWINGS DOUBLE UP AND RIGHT + return rune(0x255a), true + case "boxUl": + // BOX DRAWINGS UP DOUBLE AND LEFT SINGLE + return rune(0x255c), true + case "boxUr": + // BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE + return rune(0x2559), true + case "boxVH": + // BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL + return rune(0x256c), true + case "boxVL": + // BOX DRAWINGS DOUBLE VERTICAL AND LEFT + return rune(0x2563), true + case "boxVR": + // BOX DRAWINGS DOUBLE VERTICAL AND RIGHT + return rune(0x2560), true + case "boxVh": + // BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE + return rune(0x256b), true + case "boxVl": + // BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE + return rune(0x2562), true + case "boxVr": + // BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE + return rune(0x255f), true + case "boxV": + // BOX DRAWINGS DOUBLE VERTICAL + return rune(0x2551), true + case "boxbox": + // TWO JOINED SQUARES + return rune(0x29c9), true + case "boxdL": + // BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE + return rune(0x2555), true + case "boxdR": + // BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE + return rune(0x2552), true + case "boxdl": + // BOX DRAWINGS LIGHT DOWN AND LEFT + return rune(0x2510), true + case "boxdr": + // BOX DRAWINGS LIGHT DOWN AND RIGHT + return rune(0x250c), true + case "boxhU": + // BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE + return rune(0x2568), true + case "boxh": + // BOX DRAWINGS LIGHT HORIZONTAL + return rune(0x2500), true + case "boxhD": + // BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE + return rune(0x2565), true + case "boxhd": + // BOX DRAWINGS LIGHT DOWN AND HORIZONTAL + return rune(0x252c), true + case "boxhu": + // BOX DRAWINGS LIGHT UP AND HORIZONTAL + return rune(0x2534), true + case "boxminus": + // SQUARED MINUS + return rune(0x229f), true + case "boxplus": + // SQUARED PLUS + return rune(0x229e), true + case "boxtimes": + // SQUARED TIMES + return rune(0x22a0), true + case "boxuL": + // BOX DRAWINGS UP SINGLE AND LEFT DOUBLE + return rune(0x255b), true + case "boxuR": + // BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE + return rune(0x2558), true + case "boxul": + // BOX DRAWINGS LIGHT UP AND LEFT + return rune(0x2518), true + case "boxur": + // BOX DRAWINGS LIGHT UP AND RIGHT + return rune(0x2514), true + case "boxvL": + // BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE + return rune(0x2561), true + case "boxvR": + // BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE + return rune(0x255e), true + case "boxvl": + // BOX DRAWINGS LIGHT VERTICAL AND LEFT + return rune(0x2524), true + case "boxvr": + // BOX DRAWINGS LIGHT VERTICAL AND RIGHT + return rune(0x251c), true + case "boxv": + // BOX DRAWINGS LIGHT VERTICAL + return rune(0x2502), true + case "boxvH": + // BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE + return rune(0x256a), true + case "boxvh": + // BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL + return rune(0x253c), true + case "bprime": + // REVERSED PRIME + return rune(0x2035), true + case "brdhar": + // RIGHTWARDS HARPOON WITH BARB DOWN FROM BAR + return rune(0x295f), true + case "breve": + // BREVE + return rune(0x02d8), true + case "bruhar": + // RIGHTWARDS HARPOON WITH BARB UP FROM BAR + return rune(0x295b), true + case "brvbar": + // BROKEN BAR + return rune(0xa6), true + case "bscr": + // MATHEMATICAL SCRIPT SMALL B + return rune(0x01d4b7), true + case "bsemi": + // REVERSED SEMICOLON + return rune(0x204f), true + case "bsim": + // REVERSED TILDE + return rune(0x223d), true + case "bsime": + // REVERSED TILDE EQUALS + return rune(0x22cd), true + case "bsolb": + // SQUARED FALLING DIAGONAL SLASH + return rune(0x29c5), true + case "bsolhsub": + // REVERSE SOLIDUS PRECEDING SUBSET + return rune(0x27c8), true + case "bsol": + // REVERSE SOLIDUS + return rune(0x5c), true + case "btimes": + // SEMIDIRECT PRODUCT WITH BOTTOM CLOSED + return rune(0x2a32), true + case "bulhar": + // UPWARDS HARPOON WITH BARB LEFT FROM BAR + return rune(0x2960), true + case "bullet": + // BULLET + return rune(0x2022), true + case "bull": + // BULLET + return rune(0x2022), true + case "bump": + // GEOMETRICALLY EQUIVALENT TO + return rune(0x224e), true + case "bumpE": + // EQUALS SIGN WITH BUMPY ABOVE + return rune(0x2aae), true + case "bumpe": + // DIFFERENCE BETWEEN + return rune(0x224f), true + case "bumpeq": + // DIFFERENCE BETWEEN + return rune(0x224f), true + case "burhar": + // UPWARDS HARPOON WITH BARB RIGHT FROM BAR + return rune(0x295c), true + } + + case 'c': + switch name { + case "cacute": + // LATIN SMALL LETTER C WITH ACUTE + return rune(0x0107), true + case "cap": + // INTERSECTION + return rune(0x2229), true + case "capand": + // INTERSECTION WITH LOGICAL AND + return rune(0x2a44), true + case "capbrcup": + // INTERSECTION ABOVE BAR ABOVE UNION + return rune(0x2a49), true + case "capcap": + // INTERSECTION BESIDE AND JOINED WITH INTERSECTION + return rune(0x2a4b), true + case "capcup": + // INTERSECTION ABOVE UNION + return rune(0x2a47), true + case "capdot": + // INTERSECTION WITH DOT + return rune(0x2a40), true + case "capint": + // INTEGRAL WITH INTERSECTION + return rune(0x2a19), true + case "caps": + // INTERSECTION with serifs + return rune(0x2229), true + case "caret": + // CARET INSERTION POINT + return rune(0x2041), true + case "caron": + // CARON + return rune(0x02c7), true + case "ccaps": + // CLOSED INTERSECTION WITH SERIFS + return rune(0x2a4d), true + case "ccaron": + // LATIN SMALL LETTER C WITH CARON + return rune(0x010d), true + case "ccedil": + // LATIN SMALL LETTER C WITH CEDILLA + return rune(0xe7), true + case "ccirc": + // LATIN SMALL LETTER C WITH CIRCUMFLEX + return rune(0x0109), true + case "ccups": + // CLOSED UNION WITH SERIFS + return rune(0x2a4c), true + case "ccupssm": + // CLOSED UNION WITH SERIFS AND SMASH PRODUCT + return rune(0x2a50), true + case "cdot": + // LATIN SMALL LETTER C WITH DOT ABOVE + return rune(0x010b), true + case "cedil": + // CEDILLA + return rune(0xb8), true + case "cemptyv": + // EMPTY SET WITH SMALL CIRCLE ABOVE + return rune(0x29b2), true + case "centerdot": + // MIDDLE DOT + return rune(0xb7), true + case "cent": + // CENT SIGN + return rune(0xa2), true + case "cfr": + // MATHEMATICAL FRAKTUR SMALL C + return rune(0x01d520), true + case "chcy": + // CYRILLIC SMALL LETTER CHE + return rune(0x0447), true + case "check": + // CHECK MARK + return rune(0x2713), true + case "checkmark": + // CHECK MARK + return rune(0x2713), true + case "chi": + // GREEK SMALL LETTER CHI + return rune(0x03c7), true + case "circeq": + // RING EQUAL TO + return rune(0x2257), true + case "circlearrowleft": + // ANTICLOCKWISE OPEN CIRCLE ARROW + return rune(0x21ba), true + case "circlearrowright": + // CLOCKWISE OPEN CIRCLE ARROW + return rune(0x21bb), true + case "circledS": + // CIRCLED LATIN CAPITAL LETTER S + return rune(0x24c8), true + case "circledast": + // CIRCLED ASTERISK OPERATOR + return rune(0x229b), true + case "circledcirc": + // CIRCLED RING OPERATOR + return rune(0x229a), true + case "circleddash": + // CIRCLED DASH + return rune(0x229d), true + case "cire": + // RING EQUAL TO + return rune(0x2257), true + case "cir": + // WHITE CIRCLE + return rune(0x25cb), true + case "cirE": + // CIRCLE WITH TWO HORIZONTAL STROKES TO THE RIGHT + return rune(0x29c3), true + case "cirb": + // SQUARED SMALL CIRCLE + return rune(0x29c7), true + case "circ": + // MODIFIER LETTER CIRCUMFLEX ACCENT + return rune(0x02c6), true + case "circledR": + // REGISTERED SIGN + return rune(0xae), true + case "cirdarr": + // WHITE CIRCLE WITH DOWN ARROW + return rune(0x29ec), true + case "cirerr": + // ERROR-BARRED WHITE CIRCLE + return rune(0x29f2), true + case "cirfdarr": + // BLACK CIRCLE WITH DOWN ARROW + return rune(0x29ed), true + case "cirferr": + // ERROR-BARRED BLACK CIRCLE + return rune(0x29f3), true + case "cirfnint": + // CIRCULATION FUNCTION + return rune(0x2a10), true + case "cirmid": + // VERTICAL LINE WITH CIRCLE ABOVE + return rune(0x2aef), true + case "cirscir": + // CIRCLE WITH SMALL CIRCLE TO THE RIGHT + return rune(0x29c2), true + case "closur": + // CLOSE UP + return rune(0x2050), true + case "clubs": + // BLACK CLUB SUIT + return rune(0x2663), true + case "clubsuit": + // BLACK CLUB SUIT + return rune(0x2663), true + case "colone": + // COLON EQUALS + return rune(0x2254), true + case "coloneq": + // COLON EQUALS + return rune(0x2254), true + case "colon": + // COLON + return rune(0x3a), true + case "commat": + // COMMERCIAL AT + return rune(0x40), true + case "comma": + // COMMA + return rune(0x2c), true + case "comp": + // COMPLEMENT + return rune(0x2201), true + case "compfn": + // RING OPERATOR + return rune(0x2218), true + case "complement": + // COMPLEMENT + return rune(0x2201), true + case "complexes": + // DOUBLE-STRUCK CAPITAL C + return rune(0x2102), true + case "cong": + // APPROXIMATELY EQUAL TO + return rune(0x2245), true + case "congdot": + // CONGRUENT WITH DOT ABOVE + return rune(0x2a6d), true + case "conint": + // CONTOUR INTEGRAL + return rune(0x222e), true + case "copf": + // MATHEMATICAL DOUBLE-STRUCK SMALL C + return rune(0x01d554), true + case "coprod": + // N-ARY COPRODUCT + return rune(0x2210), true + case "copysr": + // SOUND RECORDING COPYRIGHT + return rune(0x2117), true + case "copy": + // COPYRIGHT SIGN + return rune(0xa9), true + case "crarr": + // DOWNWARDS ARROW WITH CORNER LEFTWARDS + return rune(0x21b5), true + case "cross": + // BALLOT X + return rune(0x2717), true + case "cscr": + // MATHEMATICAL SCRIPT SMALL C + return rune(0x01d4b8), true + case "csub": + // CLOSED SUBSET + return rune(0x2acf), true + case "csube": + // CLOSED SUBSET OR EQUAL TO + return rune(0x2ad1), true + case "csup": + // CLOSED SUPERSET + return rune(0x2ad0), true + case "csupe": + // CLOSED SUPERSET OR EQUAL TO + return rune(0x2ad2), true + case "ctdot": + // MIDLINE HORIZONTAL ELLIPSIS + return rune(0x22ef), true + case "cudarrl": + // RIGHT-SIDE ARC CLOCKWISE ARROW + return rune(0x2938), true + case "cudarrr": + // ARROW POINTING RIGHTWARDS THEN CURVING DOWNWARDS + return rune(0x2935), true + case "cuepr": + // EQUAL TO OR PRECEDES + return rune(0x22de), true + case "cuesc": + // EQUAL TO OR SUCCEEDS + return rune(0x22df), true + case "cularr": + // ANTICLOCKWISE TOP SEMICIRCLE ARROW + return rune(0x21b6), true + case "cularrp": + // TOP ARC ANTICLOCKWISE ARROW WITH PLUS + return rune(0x293d), true + case "cup": + // UNION + return rune(0x222a), true + case "cupbrcap": + // UNION ABOVE BAR ABOVE INTERSECTION + return rune(0x2a48), true + case "cupcap": + // UNION ABOVE INTERSECTION + return rune(0x2a46), true + case "cupcup": + // UNION BESIDE AND JOINED WITH UNION + return rune(0x2a4a), true + case "cupdot": + // MULTISET MULTIPLICATION + return rune(0x228d), true + case "cupint": + // INTEGRAL WITH UNION + return rune(0x2a1a), true + case "cupor": + // UNION WITH LOGICAL OR + return rune(0x2a45), true + case "cupre": + // PRECEDES OR EQUAL TO + return rune(0x227c), true + case "cups": + // UNION with serifs + return rune(0x222a), true + case "curarr": + // CLOCKWISE TOP SEMICIRCLE ARROW + return rune(0x21b7), true + case "curarrm": + // TOP ARC CLOCKWISE ARROW WITH MINUS + return rune(0x293c), true + case "curlyeqprec": + // EQUAL TO OR PRECEDES + return rune(0x22de), true + case "curlyeqsucc": + // EQUAL TO OR SUCCEEDS + return rune(0x22df), true + case "curlyvee": + // CURLY LOGICAL OR + return rune(0x22ce), true + case "curlywedge": + // CURLY LOGICAL AND + return rune(0x22cf), true + case "curren": + // CURRENCY SIGN + return rune(0xa4), true + case "curvearrowleft": + // ANTICLOCKWISE TOP SEMICIRCLE ARROW + return rune(0x21b6), true + case "curvearrowright": + // CLOCKWISE TOP SEMICIRCLE ARROW + return rune(0x21b7), true + case "cuvee": + // CURLY LOGICAL OR + return rune(0x22ce), true + case "cuwed": + // CURLY LOGICAL AND + return rune(0x22cf), true + case "cwconint": + // CLOCKWISE CONTOUR INTEGRAL + return rune(0x2232), true + case "cwint": + // CLOCKWISE INTEGRAL + return rune(0x2231), true + case "cylcty": + // CYLINDRICITY + return rune(0x232d), true + } + + case 'd': + switch name { + case "dAarr": + // DOWNWARDS TRIPLE ARROW + return rune(0x290b), true + case "dArr": + // DOWNWARDS DOUBLE ARROW + return rune(0x21d3), true + case "dHar": + // DOWNWARDS HARPOON WITH BARB LEFT BESIDE DOWNWARDS HARPOON WITH BARB RIGHT + return rune(0x2965), true + case "dagger": + // DAGGER + return rune(0x2020), true + case "dalembrt": + // SQUARE WITH CONTOURED OUTLINE + return rune(0x29e0), true + case "daleth": + // DALET SYMBOL + return rune(0x2138), true + case "darr2": + // DOWNWARDS PAIRED ARROWS + return rune(0x21ca), true + case "darr": + // DOWNWARDS ARROW + return rune(0x2193), true + case "darrb": + // DOWNWARDS ARROW TO BAR + return rune(0x2913), true + case "darrln": + // DOWNWARDS ARROW WITH HORIZONTAL STROKE + return rune(0x2908), true + case "dashv": + // LEFT TACK + return rune(0x22a3), true + case "dash": + // HYPHEN + return rune(0x2010), true + case "dashV": + // DOUBLE VERTICAL BAR LEFT TURNSTILE + return rune(0x2ae3), true + case "dbkarow": + // RIGHTWARDS TRIPLE DASH ARROW + return rune(0x290f), true + case "dblac": + // DOUBLE ACUTE ACCENT + return rune(0x02dd), true + case "dcaron": + // LATIN SMALL LETTER D WITH CARON + return rune(0x010f), true + case "dcy": + // CYRILLIC SMALL LETTER DE + return rune(0x0434), true + case "ddarr": + // DOWNWARDS PAIRED ARROWS + return rune(0x21ca), true + case "dd": + // DOUBLE-STRUCK ITALIC SMALL D + return rune(0x2146), true + case "ddagger": + // DOUBLE DAGGER + return rune(0x2021), true + case "ddotseq": + // EQUALS SIGN WITH TWO DOTS ABOVE AND TWO DOTS BELOW + return rune(0x2a77), true + case "deg": + // DEGREE SIGN + return rune(0xb0), true + case "delta": + // GREEK SMALL LETTER DELTA + return rune(0x03b4), true + case "demptyv": + // EMPTY SET WITH OVERBAR + return rune(0x29b1), true + case "dfisht": + // DOWN FISH TAIL + return rune(0x297f), true + case "dfr": + // MATHEMATICAL FRAKTUR SMALL D + return rune(0x01d521), true + case "dgr": + // GREEK SMALL LETTER DELTA + return rune(0x03b4), true + case "dharl": + // DOWNWARDS HARPOON WITH BARB LEFTWARDS + return rune(0x21c3), true + case "dharr": + // DOWNWARDS HARPOON WITH BARB RIGHTWARDS + return rune(0x21c2), true + case "diam": + // DIAMOND OPERATOR + return rune(0x22c4), true + case "diamdarr": + // BLACK DIAMOND WITH DOWN ARROW + return rune(0x29ea), true + case "diamerr": + // ERROR-BARRED WHITE DIAMOND + return rune(0x29f0), true + case "diamerrf": + // ERROR-BARRED BLACK DIAMOND + return rune(0x29f1), true + case "diamond": + // DIAMOND OPERATOR + return rune(0x22c4), true + case "diamondsuit": + // BLACK DIAMOND SUIT + return rune(0x2666), true + case "diams": + // BLACK DIAMOND SUIT + return rune(0x2666), true + case "die": + // DIAERESIS + return rune(0xa8), true + case "digamma": + // GREEK SMALL LETTER DIGAMMA + return rune(0x03dd), true + case "disin": + // ELEMENT OF WITH LONG HORIZONTAL STROKE + return rune(0x22f2), true + case "divideontimes": + // DIVISION TIMES + return rune(0x22c7), true + case "divonx": + // DIVISION TIMES + return rune(0x22c7), true + case "div": + // DIVISION SIGN + return rune(0xf7), true + case "divide": + // DIVISION SIGN + return rune(0xf7), true + case "djcy": + // CYRILLIC SMALL LETTER DJE + return rune(0x0452), true + case "dlarr": + // SOUTH WEST ARROW + return rune(0x2199), true + case "dlcorn": + // BOTTOM LEFT CORNER + return rune(0x231e), true + case "dlcrop": + // BOTTOM LEFT CROP + return rune(0x230d), true + case "dlharb": + // DOWNWARDS HARPOON WITH BARB LEFT TO BAR + return rune(0x2959), true + case "dollar": + // DOLLAR SIGN + return rune(0x24), true + case "dopf": + // MATHEMATICAL DOUBLE-STRUCK SMALL D + return rune(0x01d555), true + case "doteq": + // APPROACHES THE LIMIT + return rune(0x2250), true + case "doteqdot": + // GEOMETRICALLY EQUAL TO + return rune(0x2251), true + case "dotminus": + // DOT MINUS + return rune(0x2238), true + case "dotplus": + // DOT PLUS + return rune(0x2214), true + case "dotsquare": + // SQUARED DOT OPERATOR + return rune(0x22a1), true + case "dot": + // DOT ABOVE + return rune(0x02d9), true + case "doublebarwedge": + // PERSPECTIVE + return rune(0x2306), true + case "downarrow": + // DOWNWARDS ARROW + return rune(0x2193), true + case "downdownarrows": + // DOWNWARDS PAIRED ARROWS + return rune(0x21ca), true + case "downharpoonleft": + // DOWNWARDS HARPOON WITH BARB LEFTWARDS + return rune(0x21c3), true + case "downharpoonright": + // DOWNWARDS HARPOON WITH BARB RIGHTWARDS + return rune(0x21c2), true + case "drarr": + // SOUTH EAST ARROW + return rune(0x2198), true + case "drbkarow": + // RIGHTWARDS TWO-HEADED TRIPLE DASH ARROW + return rune(0x2910), true + case "drcorn": + // BOTTOM RIGHT CORNER + return rune(0x231f), true + case "drcrop": + // BOTTOM RIGHT CROP + return rune(0x230c), true + case "drharb": + // DOWNWARDS HARPOON WITH BARB RIGHT TO BAR + return rune(0x2955), true + case "dscr": + // MATHEMATICAL SCRIPT SMALL D + return rune(0x01d4b9), true + case "dscy": + // CYRILLIC SMALL LETTER DZE + return rune(0x0455), true + case "dsol": + // SOLIDUS WITH OVERBAR + return rune(0x29f6), true + case "dstrok": + // LATIN SMALL LETTER D WITH STROKE + return rune(0x0111), true + case "dtdot": + // DOWN RIGHT DIAGONAL ELLIPSIS + return rune(0x22f1), true + case "dtrif": + // BLACK DOWN-POINTING SMALL TRIANGLE + return rune(0x25be), true + case "dtri": + // WHITE DOWN-POINTING SMALL TRIANGLE + return rune(0x25bf), true + case "dtrilf": + // DOWN-POINTING TRIANGLE WITH LEFT HALF BLACK + return rune(0x29e8), true + case "dtrirf": + // DOWN-POINTING TRIANGLE WITH RIGHT HALF BLACK + return rune(0x29e9), true + case "duarr": + // DOWNWARDS ARROW LEFTWARDS OF UPWARDS ARROW + return rune(0x21f5), true + case "duhar": + // DOWNWARDS HARPOON WITH BARB LEFT BESIDE UPWARDS HARPOON WITH BARB RIGHT + return rune(0x296f), true + case "dumap": + // DOUBLE-ENDED MULTIMAP + return rune(0x29df), true + case "dwangle": + // OBLIQUE ANGLE OPENING UP + return rune(0x29a6), true + case "dzcy": + // CYRILLIC SMALL LETTER DZHE + return rune(0x045f), true + case "dzigrarr": + // LONG RIGHTWARDS SQUIGGLE ARROW + return rune(0x27ff), true + } + + case 'e': + switch name { + case "eDDot": + // EQUALS SIGN WITH TWO DOTS ABOVE AND TWO DOTS BELOW + return rune(0x2a77), true + case "eDot": + // GEOMETRICALLY EQUAL TO + return rune(0x2251), true + case "eacgr": + // GREEK SMALL LETTER EPSILON WITH TONOS + return rune(0x03ad), true + case "eacute": + // LATIN SMALL LETTER E WITH ACUTE + return rune(0xe9), true + case "easter": + // EQUALS WITH ASTERISK + return rune(0x2a6e), true + case "ecaron": + // LATIN SMALL LETTER E WITH CARON + return rune(0x011b), true + case "ecir": + // RING IN EQUAL TO + return rune(0x2256), true + case "ecirc": + // LATIN SMALL LETTER E WITH CIRCUMFLEX + return rune(0xea), true + case "ecolon": + // EQUALS COLON + return rune(0x2255), true + case "ecy": + // CYRILLIC SMALL LETTER E + return rune(0x044d), true + case "edot": + // LATIN SMALL LETTER E WITH DOT ABOVE + return rune(0x0117), true + case "ee": + // DOUBLE-STRUCK ITALIC SMALL E + return rune(0x2147), true + case "eeacgr": + // GREEK SMALL LETTER ETA WITH TONOS + return rune(0x03ae), true + case "eegr": + // GREEK SMALL LETTER ETA + return rune(0x03b7), true + case "efDot": + // APPROXIMATELY EQUAL TO OR THE IMAGE OF + return rune(0x2252), true + case "efr": + // MATHEMATICAL FRAKTUR SMALL E + return rune(0x01d522), true + case "egr": + // GREEK SMALL LETTER EPSILON + return rune(0x03b5), true + case "egs": + // SLANTED EQUAL TO OR GREATER-THAN + return rune(0x2a96), true + case "egsdot": + // SLANTED EQUAL TO OR GREATER-THAN WITH DOT INSIDE + return rune(0x2a98), true + case "eg": + // DOUBLE-LINE EQUAL TO OR GREATER-THAN + return rune(0x2a9a), true + case "egrave": + // LATIN SMALL LETTER E WITH GRAVE + return rune(0xe8), true + case "elinters": + // ELECTRICAL INTERSECTION + return rune(0x23e7), true + case "ell": + // SCRIPT SMALL L + return rune(0x2113), true + case "els": + // SLANTED EQUAL TO OR LESS-THAN + return rune(0x2a95), true + case "elsdot": + // SLANTED EQUAL TO OR LESS-THAN WITH DOT INSIDE + return rune(0x2a97), true + case "el": + // DOUBLE-LINE EQUAL TO OR LESS-THAN + return rune(0x2a99), true + case "emacr": + // LATIN SMALL LETTER E WITH MACRON + return rune(0x0113), true + case "empty": + // EMPTY SET + return rune(0x2205), true + case "emptyset": + // EMPTY SET + return rune(0x2205), true + case "emptyv": + // EMPTY SET + return rune(0x2205), true + case "emsp13": + // THREE-PER-EM SPACE + return rune(0x2004), true + case "emsp14": + // FOUR-PER-EM SPACE + return rune(0x2005), true + case "emsp": + // EM SPACE + return rune(0x2003), true + case "eng": + // LATIN SMALL LETTER ENG + return rune(0x014b), true + case "ensp": + // EN SPACE + return rune(0x2002), true + case "eogon": + // LATIN SMALL LETTER E WITH OGONEK + return rune(0x0119), true + case "eopf": + // MATHEMATICAL DOUBLE-STRUCK SMALL E + return rune(0x01d556), true + case "epar": + // EQUAL AND PARALLEL TO + return rune(0x22d5), true + case "eparsl": + // EQUALS SIGN AND SLANTED PARALLEL + return rune(0x29e3), true + case "eplus": + // EQUALS SIGN ABOVE PLUS SIGN + return rune(0x2a71), true + case "epsilon": + // GREEK SMALL LETTER EPSILON + return rune(0x03b5), true + case "epsis": + // GREEK LUNATE EPSILON SYMBOL + return rune(0x03f5), true + case "epsiv": + // GREEK LUNATE EPSILON SYMBOL + return rune(0x03f5), true + case "epsi": + // GREEK SMALL LETTER EPSILON + return rune(0x03b5), true + case "eqcirc": + // RING IN EQUAL TO + return rune(0x2256), true + case "eqcolon": + // EQUALS COLON + return rune(0x2255), true + case "eqeq": + // TWO CONSECUTIVE EQUALS SIGNS + return rune(0x2a75), true + case "eqsim": + // MINUS TILDE + return rune(0x2242), true + case "eqslantgtr": + // SLANTED EQUAL TO OR GREATER-THAN + return rune(0x2a96), true + case "eqslantless": + // SLANTED EQUAL TO OR LESS-THAN + return rune(0x2a95), true + case "equals": + // EQUALS SIGN + return rune(0x3d), true + case "equest": + // QUESTIONED EQUAL TO + return rune(0x225f), true + case "equiv": + // IDENTICAL TO + return rune(0x2261), true + case "equivDD": + // EQUIVALENT WITH FOUR DOTS ABOVE + return rune(0x2a78), true + case "eqvparsl": + // IDENTICAL TO AND SLANTED PARALLEL + return rune(0x29e5), true + case "erDot": + // IMAGE OF OR APPROXIMATELY EQUAL TO + return rune(0x2253), true + case "erarr": + // EQUALS SIGN ABOVE RIGHTWARDS ARROW + return rune(0x2971), true + case "escr": + // SCRIPT SMALL E + return rune(0x212f), true + case "esdot": + // APPROACHES THE LIMIT + return rune(0x2250), true + case "esim": + // MINUS TILDE + return rune(0x2242), true + case "eta": + // GREEK SMALL LETTER ETA + return rune(0x03b7), true + case "eth": + // LATIN SMALL LETTER ETH + return rune(0xf0), true + case "euml": + // LATIN SMALL LETTER E WITH DIAERESIS + return rune(0xeb), true + case "euro": + // EURO SIGN + return rune(0x20ac), true + case "excl": + // EXCLAMATION MARK + return rune(0x21), true + case "exist": + // THERE EXISTS + return rune(0x2203), true + case "expectation": + // SCRIPT CAPITAL E + return rune(0x2130), true + case "exponentiale": + // DOUBLE-STRUCK ITALIC SMALL E + return rune(0x2147), true + } + + case 'f': + switch name { + case "fallingdotseq": + // APPROXIMATELY EQUAL TO OR THE IMAGE OF + return rune(0x2252), true + case "fbowtie": + // BLACK BOWTIE + return rune(0x29d3), true + case "fcy": + // CYRILLIC SMALL LETTER EF + return rune(0x0444), true + case "fdiag": + // BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT + return rune(0x2572), true + case "fdiordi": + // FALLING DIAGONAL CROSSING RISING DIAGONAL + return rune(0x292c), true + case "fdonearr": + // FALLING DIAGONAL CROSSING NORTH EAST ARROW + return rune(0x292f), true + case "female": + // FEMALE SIGN + return rune(0x2640), true + case "ffilig": + // LATIN SMALL LIGATURE FFI + return rune(0xfb03), true + case "fflig": + // LATIN SMALL LIGATURE FF + return rune(0xfb00), true + case "ffllig": + // LATIN SMALL LIGATURE FFL + return rune(0xfb04), true + case "ffr": + // MATHEMATICAL FRAKTUR SMALL F + return rune(0x01d523), true + case "fhrglass": + // BLACK HOURGLASS + return rune(0x29d7), true + case "filig": + // LATIN SMALL LIGATURE FI + return rune(0xfb01), true + case "fjlig": + // fj ligature + return rune(0x66), true + case "flat": + // MUSIC FLAT SIGN + return rune(0x266d), true + case "fllig": + // LATIN SMALL LIGATURE FL + return rune(0xfb02), true + case "fltns": + // WHITE PARALLELOGRAM + return rune(0x25b1), true + case "fnof": + // LATIN SMALL LETTER F WITH HOOK + return rune(0x0192), true + case "fopf": + // MATHEMATICAL DOUBLE-STRUCK SMALL F + return rune(0x01d557), true + case "forall": + // FOR ALL + return rune(0x2200), true + case "fork": + // PITCHFORK + return rune(0x22d4), true + case "forkv": + // ELEMENT OF OPENING DOWNWARDS + return rune(0x2ad9), true + case "fpartint": + // FINITE PART INTEGRAL + return rune(0x2a0d), true + case "frac12": + // VULGAR FRACTION ONE HALF + return rune(0xbd), true + case "frac13": + // VULGAR FRACTION ONE THIRD + return rune(0x2153), true + case "frac14": + // VULGAR FRACTION ONE QUARTER + return rune(0xbc), true + case "frac15": + // VULGAR FRACTION ONE FIFTH + return rune(0x2155), true + case "frac16": + // VULGAR FRACTION ONE SIXTH + return rune(0x2159), true + case "frac18": + // VULGAR FRACTION ONE EIGHTH + return rune(0x215b), true + case "frac23": + // VULGAR FRACTION TWO THIRDS + return rune(0x2154), true + case "frac25": + // VULGAR FRACTION TWO FIFTHS + return rune(0x2156), true + case "frac34": + // VULGAR FRACTION THREE QUARTERS + return rune(0xbe), true + case "frac35": + // VULGAR FRACTION THREE FIFTHS + return rune(0x2157), true + case "frac38": + // VULGAR FRACTION THREE EIGHTHS + return rune(0x215c), true + case "frac45": + // VULGAR FRACTION FOUR FIFTHS + return rune(0x2158), true + case "frac56": + // VULGAR FRACTION FIVE SIXTHS + return rune(0x215a), true + case "frac58": + // VULGAR FRACTION FIVE EIGHTHS + return rune(0x215d), true + case "frac78": + // VULGAR FRACTION SEVEN EIGHTHS + return rune(0x215e), true + case "frasl": + // FRACTION SLASH + return rune(0x2044), true + case "frown": + // FROWN + return rune(0x2322), true + case "fscr": + // MATHEMATICAL SCRIPT SMALL F + return rune(0x01d4bb), true + } + + case 'g': + switch name { + case "gE": + // GREATER-THAN OVER EQUAL TO + return rune(0x2267), true + case "gEl": + // GREATER-THAN ABOVE DOUBLE-LINE EQUAL ABOVE LESS-THAN + return rune(0x2a8c), true + case "gacute": + // LATIN SMALL LETTER G WITH ACUTE + return rune(0x01f5), true + case "gammad": + // GREEK SMALL LETTER DIGAMMA + return rune(0x03dd), true + case "gamma": + // GREEK SMALL LETTER GAMMA + return rune(0x03b3), true + case "gap": + // GREATER-THAN OR APPROXIMATE + return rune(0x2a86), true + case "gbreve": + // LATIN SMALL LETTER G WITH BREVE + return rune(0x011f), true + case "gcedil": + // LATIN SMALL LETTER G WITH CEDILLA + return rune(0x0123), true + case "gcirc": + // LATIN SMALL LETTER G WITH CIRCUMFLEX + return rune(0x011d), true + case "gcy": + // CYRILLIC SMALL LETTER GHE + return rune(0x0433), true + case "gdot": + // LATIN SMALL LETTER G WITH DOT ABOVE + return rune(0x0121), true + case "ge": + // GREATER-THAN OR EQUAL TO + return rune(0x2265), true + case "gel": + // GREATER-THAN EQUAL TO OR LESS-THAN + return rune(0x22db), true + case "geq": + // GREATER-THAN OR EQUAL TO + return rune(0x2265), true + case "geqq": + // GREATER-THAN OVER EQUAL TO + return rune(0x2267), true + case "geqslant": + // GREATER-THAN OR SLANTED EQUAL TO + return rune(0x2a7e), true + case "gesl": + // GREATER-THAN slanted EQUAL TO OR LESS-THAN + return rune(0x22db), true + case "ges": + // GREATER-THAN OR SLANTED EQUAL TO + return rune(0x2a7e), true + case "gescc": + // GREATER-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL + return rune(0x2aa9), true + case "gesdot": + // GREATER-THAN OR SLANTED EQUAL TO WITH DOT INSIDE + return rune(0x2a80), true + case "gesdoto": + // GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE + return rune(0x2a82), true + case "gesdotol": + // GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE LEFT + return rune(0x2a84), true + case "gesles": + // GREATER-THAN ABOVE SLANTED EQUAL ABOVE LESS-THAN ABOVE SLANTED EQUAL + return rune(0x2a94), true + case "gfr": + // MATHEMATICAL FRAKTUR SMALL G + return rune(0x01d524), true + case "gg": + // MUCH GREATER-THAN + return rune(0x226b), true + case "ggg": + // VERY MUCH GREATER-THAN + return rune(0x22d9), true + case "ggr": + // GREEK SMALL LETTER GAMMA + return rune(0x03b3), true + case "gimel": + // GIMEL SYMBOL + return rune(0x2137), true + case "gjcy": + // CYRILLIC SMALL LETTER GJE + return rune(0x0453), true + case "gl": + // GREATER-THAN OR LESS-THAN + return rune(0x2277), true + case "glE": + // GREATER-THAN ABOVE LESS-THAN ABOVE DOUBLE-LINE EQUAL + return rune(0x2a92), true + case "gla": + // GREATER-THAN BESIDE LESS-THAN + return rune(0x2aa5), true + case "glj": + // GREATER-THAN OVERLAPPING LESS-THAN + return rune(0x2aa4), true + case "gnE": + // GREATER-THAN BUT NOT EQUAL TO + return rune(0x2269), true + case "gnap": + // GREATER-THAN AND NOT APPROXIMATE + return rune(0x2a8a), true + case "gnapprox": + // GREATER-THAN AND NOT APPROXIMATE + return rune(0x2a8a), true + case "gneqq": + // GREATER-THAN BUT NOT EQUAL TO + return rune(0x2269), true + case "gne": + // GREATER-THAN AND SINGLE-LINE NOT EQUAL TO + return rune(0x2a88), true + case "gneq": + // GREATER-THAN AND SINGLE-LINE NOT EQUAL TO + return rune(0x2a88), true + case "gnsim": + // GREATER-THAN BUT NOT EQUIVALENT TO + return rune(0x22e7), true + case "gopf": + // MATHEMATICAL DOUBLE-STRUCK SMALL G + return rune(0x01d558), true + case "grave": + // GRAVE ACCENT + return rune(0x60), true + case "gscr": + // SCRIPT SMALL G + return rune(0x210a), true + case "gsdot": + // GREATER-THAN WITH DOT + return rune(0x22d7), true + case "gsim": + // GREATER-THAN OR EQUIVALENT TO + return rune(0x2273), true + case "gsime": + // GREATER-THAN ABOVE SIMILAR OR EQUAL + return rune(0x2a8e), true + case "gsiml": + // GREATER-THAN ABOVE SIMILAR ABOVE LESS-THAN + return rune(0x2a90), true + case "gtcc": + // GREATER-THAN CLOSED BY CURVE + return rune(0x2aa7), true + case "gtcir": + // GREATER-THAN WITH CIRCLE INSIDE + return rune(0x2a7a), true + case "gtdot": + // GREATER-THAN WITH DOT + return rune(0x22d7), true + case "gtlPar": + // DOUBLE LEFT ARC GREATER-THAN BRACKET + return rune(0x2995), true + case "gtquest": + // GREATER-THAN WITH QUESTION MARK ABOVE + return rune(0x2a7c), true + case "gtrapprox": + // GREATER-THAN OR APPROXIMATE + return rune(0x2a86), true + case "gtrarr": + // GREATER-THAN ABOVE RIGHTWARDS ARROW + return rune(0x2978), true + case "gtrdot": + // GREATER-THAN WITH DOT + return rune(0x22d7), true + case "gtreqless": + // GREATER-THAN EQUAL TO OR LESS-THAN + return rune(0x22db), true + case "gtreqqless": + // GREATER-THAN ABOVE DOUBLE-LINE EQUAL ABOVE LESS-THAN + return rune(0x2a8c), true + case "gtrless": + // GREATER-THAN OR LESS-THAN + return rune(0x2277), true + case "gtrpar": + // SPHERICAL ANGLE OPENING LEFT + return rune(0x29a0), true + case "gtrsim": + // GREATER-THAN OR EQUIVALENT TO + return rune(0x2273), true + case "gt": + // GREATER-THAN SIGN + return rune(0x3e), true + case "gvertneqq": + // GREATER-THAN BUT NOT EQUAL TO - with vertical stroke + return rune(0x2269), true + case "gvnE": + // GREATER-THAN BUT NOT EQUAL TO - with vertical stroke + return rune(0x2269), true + } + + case 'h': + switch name { + case "hArr": + // LEFT RIGHT DOUBLE ARROW + return rune(0x21d4), true + case "hairsp": + // HAIR SPACE + return rune(0x200a), true + case "half": + // VULGAR FRACTION ONE HALF + return rune(0xbd), true + case "hamilt": + // SCRIPT CAPITAL H + return rune(0x210b), true + case "hardcy": + // CYRILLIC SMALL LETTER HARD SIGN + return rune(0x044a), true + case "harrw": + // LEFT RIGHT WAVE ARROW + return rune(0x21ad), true + case "harr": + // LEFT RIGHT ARROW + return rune(0x2194), true + case "harrcir": + // LEFT RIGHT ARROW THROUGH SMALL CIRCLE + return rune(0x2948), true + case "hbar": + // PLANCK CONSTANT OVER TWO PI + return rune(0x210f), true + case "hcirc": + // LATIN SMALL LETTER H WITH CIRCUMFLEX + return rune(0x0125), true + case "hearts": + // BLACK HEART SUIT + return rune(0x2665), true + case "heartsuit": + // BLACK HEART SUIT + return rune(0x2665), true + case "hellip": + // HORIZONTAL ELLIPSIS + return rune(0x2026), true + case "hercon": + // HERMITIAN CONJUGATE MATRIX + return rune(0x22b9), true + case "hfr": + // MATHEMATICAL FRAKTUR SMALL H + return rune(0x01d525), true + case "hksearow": + // SOUTH EAST ARROW WITH HOOK + return rune(0x2925), true + case "hkswarow": + // SOUTH WEST ARROW WITH HOOK + return rune(0x2926), true + case "hoarr": + // LEFT RIGHT OPEN-HEADED ARROW + return rune(0x21ff), true + case "homtht": + // HOMOTHETIC + return rune(0x223b), true + case "hookleftarrow": + // LEFTWARDS ARROW WITH HOOK + return rune(0x21a9), true + case "hookrightarrow": + // RIGHTWARDS ARROW WITH HOOK + return rune(0x21aa), true + case "hopf": + // MATHEMATICAL DOUBLE-STRUCK SMALL H + return rune(0x01d559), true + case "horbar": + // HORIZONTAL BAR + return rune(0x2015), true + case "hrglass": + // WHITE HOURGLASS + return rune(0x29d6), true + case "hscr": + // MATHEMATICAL SCRIPT SMALL H + return rune(0x01d4bd), true + case "hslash": + // PLANCK CONSTANT OVER TWO PI + return rune(0x210f), true + case "hstrok": + // LATIN SMALL LETTER H WITH STROKE + return rune(0x0127), true + case "htimes": + // VECTOR OR CROSS PRODUCT + return rune(0x2a2f), true + case "hybull": + // HYPHEN BULLET + return rune(0x2043), true + case "hyphen": + // HYPHEN + return rune(0x2010), true + } + + case 'i': + switch name { + case "iacgr": + // GREEK SMALL LETTER IOTA WITH TONOS + return rune(0x03af), true + case "iacute": + // LATIN SMALL LETTER I WITH ACUTE + return rune(0xed), true + case "ic": + // INVISIBLE SEPARATOR + return rune(0x2063), true + case "icirc": + // LATIN SMALL LETTER I WITH CIRCUMFLEX + return rune(0xee), true + case "icy": + // CYRILLIC SMALL LETTER I + return rune(0x0438), true + case "idiagr": + // GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS + return rune(0x0390), true + case "idigr": + // GREEK SMALL LETTER IOTA WITH DIALYTIKA + return rune(0x03ca), true + case "iecy": + // CYRILLIC SMALL LETTER IE + return rune(0x0435), true + case "iexcl": + // INVERTED EXCLAMATION MARK + return rune(0xa1), true + case "iff": + // LEFT RIGHT DOUBLE ARROW + return rune(0x21d4), true + case "ifr": + // MATHEMATICAL FRAKTUR SMALL I + return rune(0x01d526), true + case "igr": + // GREEK SMALL LETTER IOTA + return rune(0x03b9), true + case "igrave": + // LATIN SMALL LETTER I WITH GRAVE + return rune(0xec), true + case "iiint": + // TRIPLE INTEGRAL + return rune(0x222d), true + case "ii": + // DOUBLE-STRUCK ITALIC SMALL I + return rune(0x2148), true + case "iiiint": + // QUADRUPLE INTEGRAL OPERATOR + return rune(0x2a0c), true + case "iinfin": + // INCOMPLETE INFINITY + return rune(0x29dc), true + case "iiota": + // TURNED GREEK SMALL LETTER IOTA + return rune(0x2129), true + case "ijlig": + // LATIN SMALL LIGATURE IJ + return rune(0x0133), true + case "imacr": + // LATIN SMALL LETTER I WITH MACRON + return rune(0x012b), true + case "image": + // BLACK-LETTER CAPITAL I + return rune(0x2111), true + case "imagline": + // SCRIPT CAPITAL I + return rune(0x2110), true + case "imagpart": + // BLACK-LETTER CAPITAL I + return rune(0x2111), true + case "imath": + // LATIN SMALL LETTER DOTLESS I + return rune(0x0131), true + case "imof": + // IMAGE OF + return rune(0x22b7), true + case "imped": + // LATIN CAPITAL LETTER Z WITH STROKE + return rune(0x01b5), true + case "in": + // ELEMENT OF + return rune(0x2208), true + case "incare": + // CARE OF + return rune(0x2105), true + case "infin": + // INFINITY + return rune(0x221e), true + case "infintie": + // TIE OVER INFINITY + return rune(0x29dd), true + case "inodot": + // LATIN SMALL LETTER DOTLESS I + return rune(0x0131), true + case "int": + // INTEGRAL + return rune(0x222b), true + case "intcal": + // INTERCALATE + return rune(0x22ba), true + case "integers": + // DOUBLE-STRUCK CAPITAL Z + return rune(0x2124), true + case "intercal": + // INTERCALATE + return rune(0x22ba), true + case "intlarhk": + // INTEGRAL WITH LEFTWARDS ARROW WITH HOOK + return rune(0x2a17), true + case "intprod": + // INTERIOR PRODUCT + return rune(0x2a3c), true + case "iocy": + // CYRILLIC SMALL LETTER IO + return rune(0x0451), true + case "iogon": + // LATIN SMALL LETTER I WITH OGONEK + return rune(0x012f), true + case "iopf": + // MATHEMATICAL DOUBLE-STRUCK SMALL I + return rune(0x01d55a), true + case "iota": + // GREEK SMALL LETTER IOTA + return rune(0x03b9), true + case "iprod": + // INTERIOR PRODUCT + return rune(0x2a3c), true + case "iprodr": + // RIGHTHAND INTERIOR PRODUCT + return rune(0x2a3d), true + case "iquest": + // INVERTED QUESTION MARK + return rune(0xbf), true + case "iscr": + // MATHEMATICAL SCRIPT SMALL I + return rune(0x01d4be), true + case "isin": + // ELEMENT OF + return rune(0x2208), true + case "isinE": + // ELEMENT OF WITH TWO HORIZONTAL STROKES + return rune(0x22f9), true + case "isindot": + // ELEMENT OF WITH DOT ABOVE + return rune(0x22f5), true + case "isinsv": + // ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE + return rune(0x22f3), true + case "isins": + // SMALL ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE + return rune(0x22f4), true + case "isinv": + // ELEMENT OF + return rune(0x2208), true + case "isinvb": + // ELEMENT OF WITH UNDERBAR + return rune(0x22f8), true + case "it": + // INVISIBLE TIMES + return rune(0x2062), true + case "itilde": + // LATIN SMALL LETTER I WITH TILDE + return rune(0x0129), true + case "iukcy": + // CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I + return rune(0x0456), true + case "iuml": + // LATIN SMALL LETTER I WITH DIAERESIS + return rune(0xef), true + } + + case 'j': + switch name { + case "jcirc": + // LATIN SMALL LETTER J WITH CIRCUMFLEX + return rune(0x0135), true + case "jcy": + // CYRILLIC SMALL LETTER SHORT I + return rune(0x0439), true + case "jfr": + // MATHEMATICAL FRAKTUR SMALL J + return rune(0x01d527), true + case "jmath": + // LATIN SMALL LETTER DOTLESS J + return rune(0x0237), true + case "jnodot": + // LATIN SMALL LETTER DOTLESS J + return rune(0x0237), true + case "jopf": + // MATHEMATICAL DOUBLE-STRUCK SMALL J + return rune(0x01d55b), true + case "jscr": + // MATHEMATICAL SCRIPT SMALL J + return rune(0x01d4bf), true + case "jsercy": + // CYRILLIC SMALL LETTER JE + return rune(0x0458), true + case "jukcy": + // CYRILLIC SMALL LETTER UKRAINIAN IE + return rune(0x0454), true + } + + case 'k': + switch name { + case "kappav": + // GREEK KAPPA SYMBOL + return rune(0x03f0), true + case "kappa": + // GREEK SMALL LETTER KAPPA + return rune(0x03ba), true + case "kcedil": + // LATIN SMALL LETTER K WITH CEDILLA + return rune(0x0137), true + case "kcy": + // CYRILLIC SMALL LETTER KA + return rune(0x043a), true + case "kfr": + // MATHEMATICAL FRAKTUR SMALL K + return rune(0x01d528), true + case "kgr": + // GREEK SMALL LETTER KAPPA + return rune(0x03ba), true + case "kgreen": + // LATIN SMALL LETTER KRA + return rune(0x0138), true + case "khcy": + // CYRILLIC SMALL LETTER HA + return rune(0x0445), true + case "khgr": + // GREEK SMALL LETTER CHI + return rune(0x03c7), true + case "kjcy": + // CYRILLIC SMALL LETTER KJE + return rune(0x045c), true + case "kopf": + // MATHEMATICAL DOUBLE-STRUCK SMALL K + return rune(0x01d55c), true + case "koppa": + // GREEK LETTER KOPPA + return rune(0x03de), true + case "kscr": + // MATHEMATICAL SCRIPT SMALL K + return rune(0x01d4c0), true + } + + case 'l': + switch name { + case "lAarr": + // LEFTWARDS TRIPLE ARROW + return rune(0x21da), true + case "lArr": + // LEFTWARDS DOUBLE ARROW + return rune(0x21d0), true + case "lAtail": + // LEFTWARDS DOUBLE ARROW-TAIL + return rune(0x291b), true + case "lBarr": + // LEFTWARDS TRIPLE DASH ARROW + return rune(0x290e), true + case "lE": + // LESS-THAN OVER EQUAL TO + return rune(0x2266), true + case "lEg": + // LESS-THAN ABOVE DOUBLE-LINE EQUAL ABOVE GREATER-THAN + return rune(0x2a8b), true + case "lHar": + // LEFTWARDS HARPOON WITH BARB UP ABOVE LEFTWARDS HARPOON WITH BARB DOWN + return rune(0x2962), true + case "lacute": + // LATIN SMALL LETTER L WITH ACUTE + return rune(0x013a), true + case "laemptyv": + // EMPTY SET WITH LEFT ARROW ABOVE + return rune(0x29b4), true + case "lagran": + // SCRIPT CAPITAL L + return rune(0x2112), true + case "lambda": + // GREEK SMALL LETTER LAMDA + return rune(0x03bb), true + case "lang": + // MATHEMATICAL LEFT ANGLE BRACKET + return rune(0x27e8), true + case "langd": + // LEFT ANGLE BRACKET WITH DOT + return rune(0x2991), true + case "langle": + // MATHEMATICAL LEFT ANGLE BRACKET + return rune(0x27e8), true + case "lap": + // LESS-THAN OR APPROXIMATE + return rune(0x2a85), true + case "laquo": + // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK + return rune(0xab), true + case "larr2": + // LEFTWARDS PAIRED ARROWS + return rune(0x21c7), true + case "larrb": + // LEFTWARDS ARROW TO BAR + return rune(0x21e4), true + case "larrhk": + // LEFTWARDS ARROW WITH HOOK + return rune(0x21a9), true + case "larrlp": + // LEFTWARDS ARROW WITH LOOP + return rune(0x21ab), true + case "larrtl": + // LEFTWARDS ARROW WITH TAIL + return rune(0x21a2), true + case "larr": + // LEFTWARDS ARROW + return rune(0x2190), true + case "larrbfs": + // LEFTWARDS ARROW FROM BAR TO BLACK DIAMOND + return rune(0x291f), true + case "larrfs": + // LEFTWARDS ARROW TO BLACK DIAMOND + return rune(0x291d), true + case "larrpl": + // LEFT-SIDE ARC ANTICLOCKWISE ARROW + return rune(0x2939), true + case "larrsim": + // LEFTWARDS ARROW ABOVE TILDE OPERATOR + return rune(0x2973), true + case "latail": + // LEFTWARDS ARROW-TAIL + return rune(0x2919), true + case "lat": + // LARGER THAN + return rune(0x2aab), true + case "late": + // LARGER THAN OR EQUAL TO + return rune(0x2aad), true + case "lates": + // LARGER THAN OR slanted EQUAL + return rune(0x2aad), true + case "lbarr": + // LEFTWARDS DOUBLE DASH ARROW + return rune(0x290c), true + case "lbbrk": + // LIGHT LEFT TORTOISE SHELL BRACKET ORNAMENT + return rune(0x2772), true + case "lbrace": + // LEFT CURLY BRACKET + return rune(0x7b), true + case "lbrack": + // LEFT SQUARE BRACKET + return rune(0x5b), true + case "lbrke": + // LEFT SQUARE BRACKET WITH UNDERBAR + return rune(0x298b), true + case "lbrksld": + // LEFT SQUARE BRACKET WITH TICK IN BOTTOM CORNER + return rune(0x298f), true + case "lbrkslu": + // LEFT SQUARE BRACKET WITH TICK IN TOP CORNER + return rune(0x298d), true + case "lcaron": + // LATIN SMALL LETTER L WITH CARON + return rune(0x013e), true + case "lcedil": + // LATIN SMALL LETTER L WITH CEDILLA + return rune(0x013c), true + case "lceil": + // LEFT CEILING + return rune(0x2308), true + case "lcub": + // LEFT CURLY BRACKET + return rune(0x7b), true + case "lcy": + // CYRILLIC SMALL LETTER EL + return rune(0x043b), true + case "ldca": + // ARROW POINTING DOWNWARDS THEN CURVING LEFTWARDS + return rune(0x2936), true + case "ldharb": + // LEFTWARDS HARPOON WITH BARB DOWN TO BAR + return rune(0x2956), true + case "ldot": + // LESS-THAN WITH DOT + return rune(0x22d6), true + case "ldquor": + // DOUBLE LOW-9 QUOTATION MARK + return rune(0x201e), true + case "ldquo": + // LEFT DOUBLE QUOTATION MARK + return rune(0x201c), true + case "ldrdhar": + // LEFTWARDS HARPOON WITH BARB DOWN ABOVE RIGHTWARDS HARPOON WITH BARB DOWN + return rune(0x2967), true + case "ldrdshar": + // LEFT BARB DOWN RIGHT BARB DOWN HARPOON + return rune(0x2950), true + case "ldrushar": + // LEFT BARB DOWN RIGHT BARB UP HARPOON + return rune(0x294b), true + case "ldsh": + // DOWNWARDS ARROW WITH TIP LEFTWARDS + return rune(0x21b2), true + case "leftarrowtail": + // LEFTWARDS ARROW WITH TAIL + return rune(0x21a2), true + case "leftarrow": + // LEFTWARDS ARROW + return rune(0x2190), true + case "leftharpoondown": + // LEFTWARDS HARPOON WITH BARB DOWNWARDS + return rune(0x21bd), true + case "leftharpoonup": + // LEFTWARDS HARPOON WITH BARB UPWARDS + return rune(0x21bc), true + case "leftleftarrows": + // LEFTWARDS PAIRED ARROWS + return rune(0x21c7), true + case "leftrightarrows": + // LEFTWARDS ARROW OVER RIGHTWARDS ARROW + return rune(0x21c6), true + case "leftrightarrow": + // LEFT RIGHT ARROW + return rune(0x2194), true + case "leftrightharpoons": + // LEFTWARDS HARPOON OVER RIGHTWARDS HARPOON + return rune(0x21cb), true + case "leftrightsquigarrow": + // LEFT RIGHT WAVE ARROW + return rune(0x21ad), true + case "le": + // LESS-THAN OR EQUAL TO + return rune(0x2264), true + case "leftthreetimes": + // LEFT SEMIDIRECT PRODUCT + return rune(0x22cb), true + case "leg": + // LESS-THAN EQUAL TO OR GREATER-THAN + return rune(0x22da), true + case "leq": + // LESS-THAN OR EQUAL TO + return rune(0x2264), true + case "leqq": + // LESS-THAN OVER EQUAL TO + return rune(0x2266), true + case "leqslant": + // LESS-THAN OR SLANTED EQUAL TO + return rune(0x2a7d), true + case "lesg": + // LESS-THAN slanted EQUAL TO OR GREATER-THAN + return rune(0x22da), true + case "lessdot": + // LESS-THAN WITH DOT + return rune(0x22d6), true + case "lesseqgtr": + // LESS-THAN EQUAL TO OR GREATER-THAN + return rune(0x22da), true + case "lessgtr": + // LESS-THAN OR GREATER-THAN + return rune(0x2276), true + case "lesssim": + // LESS-THAN OR EQUIVALENT TO + return rune(0x2272), true + case "les": + // LESS-THAN OR SLANTED EQUAL TO + return rune(0x2a7d), true + case "lescc": + // LESS-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL + return rune(0x2aa8), true + case "lesdot": + // LESS-THAN OR SLANTED EQUAL TO WITH DOT INSIDE + return rune(0x2a7f), true + case "lesdoto": + // LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE + return rune(0x2a81), true + case "lesdotor": + // LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE RIGHT + return rune(0x2a83), true + case "lesges": + // LESS-THAN ABOVE SLANTED EQUAL ABOVE GREATER-THAN ABOVE SLANTED EQUAL + return rune(0x2a93), true + case "lessapprox": + // LESS-THAN OR APPROXIMATE + return rune(0x2a85), true + case "lesseqqgtr": + // LESS-THAN ABOVE DOUBLE-LINE EQUAL ABOVE GREATER-THAN + return rune(0x2a8b), true + case "lfbowtie": + // BOWTIE WITH LEFT HALF BLACK + return rune(0x29d1), true + case "lfisht": + // LEFT FISH TAIL + return rune(0x297c), true + case "lfloor": + // LEFT FLOOR + return rune(0x230a), true + case "lfr": + // MATHEMATICAL FRAKTUR SMALL L + return rune(0x01d529), true + case "lftimes": + // TIMES WITH LEFT HALF BLACK + return rune(0x29d4), true + case "lg": + // LESS-THAN OR GREATER-THAN + return rune(0x2276), true + case "lgE": + // LESS-THAN ABOVE GREATER-THAN ABOVE DOUBLE-LINE EQUAL + return rune(0x2a91), true + case "lgr": + // GREEK SMALL LETTER LAMDA + return rune(0x03bb), true + case "lhard": + // LEFTWARDS HARPOON WITH BARB DOWNWARDS + return rune(0x21bd), true + case "lharu": + // LEFTWARDS HARPOON WITH BARB UPWARDS + return rune(0x21bc), true + case "lharul": + // LEFTWARDS HARPOON WITH BARB UP ABOVE LONG DASH + return rune(0x296a), true + case "lhblk": + // LOWER HALF BLOCK + return rune(0x2584), true + case "ljcy": + // CYRILLIC SMALL LETTER LJE + return rune(0x0459), true + case "llarr": + // LEFTWARDS PAIRED ARROWS + return rune(0x21c7), true + case "ll": + // MUCH LESS-THAN + return rune(0x226a), true + case "llcorner": + // BOTTOM LEFT CORNER + return rune(0x231e), true + case "llhard": + // LEFTWARDS HARPOON WITH BARB DOWN BELOW LONG DASH + return rune(0x296b), true + case "lltrif": + // BLACK LOWER LEFT TRIANGLE + return rune(0x25e3), true + case "lltri": + // LOWER LEFT TRIANGLE + return rune(0x25fa), true + case "lmidot": + // LATIN SMALL LETTER L WITH MIDDLE DOT + return rune(0x0140), true + case "lmoust": + // UPPER LEFT OR LOWER RIGHT CURLY BRACKET SECTION + return rune(0x23b0), true + case "lmoustache": + // UPPER LEFT OR LOWER RIGHT CURLY BRACKET SECTION + return rune(0x23b0), true + case "lnE": + // LESS-THAN BUT NOT EQUAL TO + return rune(0x2268), true + case "lnap": + // LESS-THAN AND NOT APPROXIMATE + return rune(0x2a89), true + case "lnapprox": + // LESS-THAN AND NOT APPROXIMATE + return rune(0x2a89), true + case "lneqq": + // LESS-THAN BUT NOT EQUAL TO + return rune(0x2268), true + case "lne": + // LESS-THAN AND SINGLE-LINE NOT EQUAL TO + return rune(0x2a87), true + case "lneq": + // LESS-THAN AND SINGLE-LINE NOT EQUAL TO + return rune(0x2a87), true + case "lnsim": + // LESS-THAN BUT NOT EQUIVALENT TO + return rune(0x22e6), true + case "loang": + // MATHEMATICAL LEFT WHITE TORTOISE SHELL BRACKET + return rune(0x27ec), true + case "loarr": + // LEFTWARDS OPEN-HEADED ARROW + return rune(0x21fd), true + case "lobrk": + // MATHEMATICAL LEFT WHITE SQUARE BRACKET + return rune(0x27e6), true + case "locub": + // LEFT WHITE CURLY BRACKET + return rune(0x2983), true + case "longleftarrow": + // LONG LEFTWARDS ARROW + return rune(0x27f5), true + case "longleftrightarrow": + // LONG LEFT RIGHT ARROW + return rune(0x27f7), true + case "longmapsto": + // LONG RIGHTWARDS ARROW FROM BAR + return rune(0x27fc), true + case "longrightarrow": + // LONG RIGHTWARDS ARROW + return rune(0x27f6), true + case "looparrowleft": + // LEFTWARDS ARROW WITH LOOP + return rune(0x21ab), true + case "looparrowright": + // RIGHTWARDS ARROW WITH LOOP + return rune(0x21ac), true + case "lopar": + // LEFT WHITE PARENTHESIS + return rune(0x2985), true + case "lopf": + // MATHEMATICAL DOUBLE-STRUCK SMALL L + return rune(0x01d55d), true + case "loplus": + // PLUS SIGN IN LEFT HALF CIRCLE + return rune(0x2a2d), true + case "lotimes": + // MULTIPLICATION SIGN IN LEFT HALF CIRCLE + return rune(0x2a34), true + case "lowast": + // LOW ASTERISK + return rune(0x204e), true + case "lowbar": + // LOW LINE + return rune(0x5f), true + case "lowint": + // INTEGRAL WITH UNDERBAR + return rune(0x2a1c), true + case "loz": + // LOZENGE + return rune(0x25ca), true + case "lozenge": + // LOZENGE + return rune(0x25ca), true + case "lozf": + // BLACK LOZENGE + return rune(0x29eb), true + case "lpargt": + // SPHERICAL ANGLE OPENING LEFT + return rune(0x29a0), true + case "lparlt": + // LEFT ARC LESS-THAN BRACKET + return rune(0x2993), true + case "lpar": + // LEFT PARENTHESIS + return rune(0x28), true + case "lrarr2": + // LEFTWARDS ARROW OVER RIGHTWARDS ARROW + return rune(0x21c6), true + case "lrarr": + // LEFTWARDS ARROW OVER RIGHTWARDS ARROW + return rune(0x21c6), true + case "lrcorner": + // BOTTOM RIGHT CORNER + return rune(0x231f), true + case "lrhar": + // LEFTWARDS HARPOON OVER RIGHTWARDS HARPOON + return rune(0x21cb), true + case "lrhar2": + // LEFTWARDS HARPOON OVER RIGHTWARDS HARPOON + return rune(0x21cb), true + case "lrhard": + // RIGHTWARDS HARPOON WITH BARB DOWN BELOW LONG DASH + return rune(0x296d), true + case "lrm": + // LEFT-TO-RIGHT MARK + return rune(0x200e), true + case "lrtri": + // RIGHT TRIANGLE + return rune(0x22bf), true + case "lsaquo": + // SINGLE LEFT-POINTING ANGLE QUOTATION MARK + return rune(0x2039), true + case "lscr": + // MATHEMATICAL SCRIPT SMALL L + return rune(0x01d4c1), true + case "lsh": + // UPWARDS ARROW WITH TIP LEFTWARDS + return rune(0x21b0), true + case "lsim": + // LESS-THAN OR EQUIVALENT TO + return rune(0x2272), true + case "lsime": + // LESS-THAN ABOVE SIMILAR OR EQUAL + return rune(0x2a8d), true + case "lsimg": + // LESS-THAN ABOVE SIMILAR ABOVE GREATER-THAN + return rune(0x2a8f), true + case "lsqb": + // LEFT SQUARE BRACKET + return rune(0x5b), true + case "lsquor": + // SINGLE LOW-9 QUOTATION MARK + return rune(0x201a), true + case "lsquo": + // LEFT SINGLE QUOTATION MARK + return rune(0x2018), true + case "lstrok": + // LATIN SMALL LETTER L WITH STROKE + return rune(0x0142), true + case "ltcc": + // LESS-THAN CLOSED BY CURVE + return rune(0x2aa6), true + case "ltcir": + // LESS-THAN WITH CIRCLE INSIDE + return rune(0x2a79), true + case "ltdot": + // LESS-THAN WITH DOT + return rune(0x22d6), true + case "lthree": + // LEFT SEMIDIRECT PRODUCT + return rune(0x22cb), true + case "ltimes": + // LEFT NORMAL FACTOR SEMIDIRECT PRODUCT + return rune(0x22c9), true + case "ltlarr": + // LESS-THAN ABOVE LEFTWARDS ARROW + return rune(0x2976), true + case "ltquest": + // LESS-THAN WITH QUESTION MARK ABOVE + return rune(0x2a7b), true + case "ltrPar": + // DOUBLE RIGHT ARC LESS-THAN BRACKET + return rune(0x2996), true + case "ltrie": + // NORMAL SUBGROUP OF OR EQUAL TO + return rune(0x22b4), true + case "ltrif": + // BLACK LEFT-POINTING SMALL TRIANGLE + return rune(0x25c2), true + case "ltri": + // WHITE LEFT-POINTING SMALL TRIANGLE + return rune(0x25c3), true + case "ltrivb": + // LEFT TRIANGLE BESIDE VERTICAL BAR + return rune(0x29cf), true + case "lt": + // LESS-THAN SIGN + return rune(0x3c), true + case "luharb": + // LEFTWARDS HARPOON WITH BARB UP TO BAR + return rune(0x2952), true + case "lurdshar": + // LEFT BARB UP RIGHT BARB DOWN HARPOON + return rune(0x294a), true + case "luruhar": + // LEFTWARDS HARPOON WITH BARB UP ABOVE RIGHTWARDS HARPOON WITH BARB UP + return rune(0x2966), true + case "lurushar": + // LEFT BARB UP RIGHT BARB UP HARPOON + return rune(0x294e), true + case "lvertneqq": + // LESS-THAN BUT NOT EQUAL TO - with vertical stroke + return rune(0x2268), true + case "lvnE": + // LESS-THAN BUT NOT EQUAL TO - with vertical stroke + return rune(0x2268), true + } + + case 'm': + switch name { + case "mDDot": + // GEOMETRIC PROPORTION + return rune(0x223a), true + case "macr": + // MACRON + return rune(0xaf), true + case "male": + // MALE SIGN + return rune(0x2642), true + case "malt": + // MALTESE CROSS + return rune(0x2720), true + case "maltese": + // MALTESE CROSS + return rune(0x2720), true + case "mapstodown": + // DOWNWARDS ARROW FROM BAR + return rune(0x21a7), true + case "mapsto": + // RIGHTWARDS ARROW FROM BAR + return rune(0x21a6), true + case "map": + // RIGHTWARDS ARROW FROM BAR + return rune(0x21a6), true + case "mapstoleft": + // LEFTWARDS ARROW FROM BAR + return rune(0x21a4), true + case "mapstoup": + // UPWARDS ARROW FROM BAR + return rune(0x21a5), true + case "marker": + // BLACK VERTICAL RECTANGLE + return rune(0x25ae), true + case "mcomma": + // MINUS SIGN WITH COMMA ABOVE + return rune(0x2a29), true + case "mcy": + // CYRILLIC SMALL LETTER EM + return rune(0x043c), true + case "mdash": + // EM DASH + return rune(0x2014), true + case "measuredangle": + // MEASURED ANGLE + return rune(0x2221), true + case "mfr": + // MATHEMATICAL FRAKTUR SMALL M + return rune(0x01d52a), true + case "mgr": + // GREEK SMALL LETTER MU + return rune(0x03bc), true + case "mho": + // INVERTED OHM SIGN + return rune(0x2127), true + case "micro": + // MICRO SIGN + return rune(0xb5), true + case "mid": + // DIVIDES + return rune(0x2223), true + case "midast": + // ASTERISK + return rune(0x2a), true + case "midcir": + // VERTICAL LINE WITH CIRCLE BELOW + return rune(0x2af0), true + case "middot": + // MIDDLE DOT + return rune(0xb7), true + case "minus": + // MINUS SIGN + return rune(0x2212), true + case "minusb": + // SQUARED MINUS + return rune(0x229f), true + case "minusd": + // DOT MINUS + return rune(0x2238), true + case "minusdu": + // MINUS SIGN WITH DOT BELOW + return rune(0x2a2a), true + case "mlcp": + // TRANSVERSAL INTERSECTION + return rune(0x2adb), true + case "mldr": + // HORIZONTAL ELLIPSIS + return rune(0x2026), true + case "mnplus": + // MINUS-OR-PLUS SIGN + return rune(0x2213), true + case "models": + // MODELS + return rune(0x22a7), true + case "mopf": + // MATHEMATICAL DOUBLE-STRUCK SMALL M + return rune(0x01d55e), true + case "mp": + // MINUS-OR-PLUS SIGN + return rune(0x2213), true + case "mscr": + // MATHEMATICAL SCRIPT SMALL M + return rune(0x01d4c2), true + case "mstpos": + // INVERTED LAZY S + return rune(0x223e), true + case "multimap": + // MULTIMAP + return rune(0x22b8), true + case "mumap": + // MULTIMAP + return rune(0x22b8), true + case "mu": + // GREEK SMALL LETTER MU + return rune(0x03bc), true + } + + case 'n': + switch name { + case "nGg": + // VERY MUCH GREATER-THAN with slash + return rune(0x22d9), true + case "nGtv": + // MUCH GREATER THAN with slash + return rune(0x226b), true + case "nGt": + // MUCH GREATER THAN with vertical line + return rune(0x226b), true + case "nLeftarrow": + // LEFTWARDS DOUBLE ARROW WITH STROKE + return rune(0x21cd), true + case "nLeftrightarrow": + // LEFT RIGHT DOUBLE ARROW WITH STROKE + return rune(0x21ce), true + case "nLl": + // VERY MUCH LESS-THAN with slash + return rune(0x22d8), true + case "nLtv": + // MUCH LESS THAN with slash + return rune(0x226a), true + case "nLt": + // MUCH LESS THAN with vertical line + return rune(0x226a), true + case "nRightarrow": + // RIGHTWARDS DOUBLE ARROW WITH STROKE + return rune(0x21cf), true + case "nVDash": + // NEGATED DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE + return rune(0x22af), true + case "nVdash": + // DOES NOT FORCE + return rune(0x22ae), true + case "nabla": + // NABLA + return rune(0x2207), true + case "nacute": + // LATIN SMALL LETTER N WITH ACUTE + return rune(0x0144), true + case "nang": + // ANGLE with vertical line + return rune(0x2220), true + case "nap": + // NOT ALMOST EQUAL TO + return rune(0x2249), true + case "napE": + // APPROXIMATELY EQUAL OR EQUAL TO with slash + return rune(0x2a70), true + case "napid": + // TRIPLE TILDE with slash + return rune(0x224b), true + case "napos": + // LATIN SMALL LETTER N PRECEDED BY APOSTROPHE + return rune(0x0149), true + case "napprox": + // NOT ALMOST EQUAL TO + return rune(0x2249), true + case "naturals": + // DOUBLE-STRUCK CAPITAL N + return rune(0x2115), true + case "natur": + // MUSIC NATURAL SIGN + return rune(0x266e), true + case "natural": + // MUSIC NATURAL SIGN + return rune(0x266e), true + case "nbsp": + // NO-BREAK SPACE + return rune(0xa0), true + case "nbump": + // GEOMETRICALLY EQUIVALENT TO with slash + return rune(0x224e), true + case "nbumpe": + // DIFFERENCE BETWEEN with slash + return rune(0x224f), true + case "ncap": + // INTERSECTION WITH OVERBAR + return rune(0x2a43), true + case "ncaron": + // LATIN SMALL LETTER N WITH CARON + return rune(0x0148), true + case "ncedil": + // LATIN SMALL LETTER N WITH CEDILLA + return rune(0x0146), true + case "ncong": + // NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO + return rune(0x2247), true + case "ncongdot": + // CONGRUENT WITH DOT ABOVE with slash + return rune(0x2a6d), true + case "ncup": + // UNION WITH OVERBAR + return rune(0x2a42), true + case "ncy": + // CYRILLIC SMALL LETTER EN + return rune(0x043d), true + case "ndash": + // EN DASH + return rune(0x2013), true + case "neArr": + // NORTH EAST DOUBLE ARROW + return rune(0x21d7), true + case "nearrow": + // NORTH EAST ARROW + return rune(0x2197), true + case "nearr": + // NORTH EAST ARROW + return rune(0x2197), true + case "nedot": + // APPROACHES THE LIMIT with slash + return rune(0x2250), true + case "nesim": + // MINUS TILDE with slash + return rune(0x2242), true + case "nexist": + // THERE DOES NOT EXIST + return rune(0x2204), true + case "nexists": + // THERE DOES NOT EXIST + return rune(0x2204), true + case "ne": + // NOT EQUAL TO + return rune(0x2260), true + case "nearhk": + // NORTH EAST ARROW WITH HOOK + return rune(0x2924), true + case "neonwarr": + // NORTH EAST ARROW CROSSING NORTH WEST ARROW + return rune(0x2931), true + case "neosearr": + // NORTH EAST ARROW CROSSING SOUTH EAST ARROW + return rune(0x292e), true + case "nequiv": + // NOT IDENTICAL TO + return rune(0x2262), true + case "nesear": + // NORTH EAST ARROW AND SOUTH EAST ARROW + return rune(0x2928), true + case "neswsarr": + // NORTH EAST AND SOUTH WEST ARROW + return rune(0x2922), true + case "nfr": + // MATHEMATICAL FRAKTUR SMALL N + return rune(0x01d52b), true + case "ngE": + // GREATER-THAN OVER EQUAL TO with slash + return rune(0x2267), true + case "ngeqq": + // GREATER-THAN OVER EQUAL TO with slash + return rune(0x2267), true + case "nge": + // NEITHER GREATER-THAN NOR EQUAL TO + return rune(0x2271), true + case "ngeq": + // NEITHER GREATER-THAN NOR EQUAL TO + return rune(0x2271), true + case "ngeqslant": + // GREATER-THAN OR SLANTED EQUAL TO with slash + return rune(0x2a7e), true + case "nges": + // GREATER-THAN OR SLANTED EQUAL TO with slash + return rune(0x2a7e), true + case "ngr": + // GREEK SMALL LETTER NU + return rune(0x03bd), true + case "ngsim": + // NEITHER GREATER-THAN NOR EQUIVALENT TO + return rune(0x2275), true + case "ngt": + // NOT GREATER-THAN + return rune(0x226f), true + case "ngtr": + // NOT GREATER-THAN + return rune(0x226f), true + case "nhArr": + // LEFT RIGHT DOUBLE ARROW WITH STROKE + return rune(0x21ce), true + case "nharr": + // LEFT RIGHT ARROW WITH STROKE + return rune(0x21ae), true + case "nhpar": + // PARALLEL WITH HORIZONTAL STROKE + return rune(0x2af2), true + case "niv": + // CONTAINS AS MEMBER + return rune(0x220b), true + case "ni": + // CONTAINS AS MEMBER + return rune(0x220b), true + case "nisd": + // CONTAINS WITH LONG HORIZONTAL STROKE + return rune(0x22fa), true + case "nis": + // SMALL CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE + return rune(0x22fc), true + case "njcy": + // CYRILLIC SMALL LETTER NJE + return rune(0x045a), true + case "nlArr": + // LEFTWARDS DOUBLE ARROW WITH STROKE + return rune(0x21cd), true + case "nlE": + // LESS-THAN OVER EQUAL TO with slash + return rune(0x2266), true + case "nlarr": + // LEFTWARDS ARROW WITH STROKE + return rune(0x219a), true + case "nldr": + // TWO DOT LEADER + return rune(0x2025), true + case "nleftarrow": + // LEFTWARDS ARROW WITH STROKE + return rune(0x219a), true + case "nleftrightarrow": + // LEFT RIGHT ARROW WITH STROKE + return rune(0x21ae), true + case "nleqq": + // LESS-THAN OVER EQUAL TO with slash + return rune(0x2266), true + case "nless": + // NOT LESS-THAN + return rune(0x226e), true + case "nle": + // NEITHER LESS-THAN NOR EQUAL TO + return rune(0x2270), true + case "nleq": + // NEITHER LESS-THAN NOR EQUAL TO + return rune(0x2270), true + case "nleqslant": + // LESS-THAN OR SLANTED EQUAL TO with slash + return rune(0x2a7d), true + case "nles": + // LESS-THAN OR SLANTED EQUAL TO with slash + return rune(0x2a7d), true + case "nlsim": + // NEITHER LESS-THAN NOR EQUIVALENT TO + return rune(0x2274), true + case "nlt": + // NOT LESS-THAN + return rune(0x226e), true + case "nltri": + // NOT NORMAL SUBGROUP OF + return rune(0x22ea), true + case "nltrie": + // NOT NORMAL SUBGROUP OF OR EQUAL TO + return rune(0x22ec), true + case "nltrivb": + // LEFT TRIANGLE BESIDE VERTICAL BAR with slash + return rune(0x29cf), true + case "nmid": + // DOES NOT DIVIDE + return rune(0x2224), true + case "nopf": + // MATHEMATICAL DOUBLE-STRUCK SMALL N + return rune(0x01d55f), true + case "notin": + // NOT AN ELEMENT OF + return rune(0x2209), true + case "notinE": + // ELEMENT OF WITH TWO HORIZONTAL STROKES with slash + return rune(0x22f9), true + case "notindot": + // ELEMENT OF WITH DOT ABOVE with slash + return rune(0x22f5), true + case "notinva": + // NOT AN ELEMENT OF + return rune(0x2209), true + case "notinvb": + // SMALL ELEMENT OF WITH OVERBAR + return rune(0x22f7), true + case "notinvc": + // ELEMENT OF WITH OVERBAR + return rune(0x22f6), true + case "notni": + // DOES NOT CONTAIN AS MEMBER + return rune(0x220c), true + case "notniva": + // DOES NOT CONTAIN AS MEMBER + return rune(0x220c), true + case "notnivb": + // SMALL CONTAINS WITH OVERBAR + return rune(0x22fe), true + case "notnivc": + // CONTAINS WITH OVERBAR + return rune(0x22fd), true + case "not": + // NOT SIGN + return rune(0xac), true + case "npart": + // PARTIAL DIFFERENTIAL with slash + return rune(0x2202), true + case "npar": + // NOT PARALLEL TO + return rune(0x2226), true + case "nparallel": + // NOT PARALLEL TO + return rune(0x2226), true + case "nparsl": + // DOUBLE SOLIDUS OPERATOR with reverse slash + return rune(0x2afd), true + case "npolint": + // LINE INTEGRATION NOT INCLUDING THE POLE + return rune(0x2a14), true + case "nprsim": + // PRECEDES OR EQUIVALENT TO with slash + return rune(0x227e), true + case "npr": + // DOES NOT PRECEDE + return rune(0x2280), true + case "nprcue": + // DOES NOT PRECEDE OR EQUAL + return rune(0x22e0), true + case "nprec": + // DOES NOT PRECEDE + return rune(0x2280), true + case "npre": + // PRECEDES ABOVE SINGLE-LINE EQUALS SIGN with slash + return rune(0x2aaf), true + case "npreceq": + // PRECEDES ABOVE SINGLE-LINE EQUALS SIGN with slash + return rune(0x2aaf), true + case "nrArr": + // RIGHTWARDS DOUBLE ARROW WITH STROKE + return rune(0x21cf), true + case "nrarrw": + // RIGHTWARDS WAVE ARROW with slash + return rune(0x219d), true + case "nrarr": + // RIGHTWARDS ARROW WITH STROKE + return rune(0x219b), true + case "nrarrc": + // WAVE ARROW POINTING DIRECTLY RIGHT with slash + return rune(0x2933), true + case "nrightarrow": + // RIGHTWARDS ARROW WITH STROKE + return rune(0x219b), true + case "nrtri": + // DOES NOT CONTAIN AS NORMAL SUBGROUP + return rune(0x22eb), true + case "nrtrie": + // DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL + return rune(0x22ed), true + case "nsGt": + // DOUBLE NESTED GREATER-THAN with slash + return rune(0x2aa2), true + case "nsLt": + // DOUBLE NESTED LESS-THAN with slash + return rune(0x2aa1), true + case "nscsim": + // SUCCEEDS OR EQUIVALENT TO with slash + return rune(0x227f), true + case "nsc": + // DOES NOT SUCCEED + return rune(0x2281), true + case "nsccue": + // DOES NOT SUCCEED OR EQUAL + return rune(0x22e1), true + case "nsce": + // SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN with slash + return rune(0x2ab0), true + case "nscr": + // MATHEMATICAL SCRIPT SMALL N + return rune(0x01d4c3), true + case "nshortmid": + // DOES NOT DIVIDE + return rune(0x2224), true + case "nshortparallel": + // NOT PARALLEL TO + return rune(0x2226), true + case "nsim": + // NOT TILDE + return rune(0x2241), true + case "nsime": + // NOT ASYMPTOTICALLY EQUAL TO + return rune(0x2244), true + case "nsimeq": + // NOT ASYMPTOTICALLY EQUAL TO + return rune(0x2244), true + case "nsmid": + // DOES NOT DIVIDE + return rune(0x2224), true + case "nspar": + // NOT PARALLEL TO + return rune(0x2226), true + case "nsqsub": + // SQUARE IMAGE OF with slash + return rune(0x228f), true + case "nsqsube": + // NOT SQUARE IMAGE OF OR EQUAL TO + return rune(0x22e2), true + case "nsqsup": + // SQUARE ORIGINAL OF with slash + return rune(0x2290), true + case "nsqsupe": + // NOT SQUARE ORIGINAL OF OR EQUAL TO + return rune(0x22e3), true + case "nsubset": + // SUBSET OF with vertical line + return rune(0x2282), true + case "nsub": + // NOT A SUBSET OF + return rune(0x2284), true + case "nsubE": + // SUBSET OF ABOVE EQUALS SIGN with slash + return rune(0x2ac5), true + case "nsube": + // NEITHER A SUBSET OF NOR EQUAL TO + return rune(0x2288), true + case "nsubseteq": + // NEITHER A SUBSET OF NOR EQUAL TO + return rune(0x2288), true + case "nsubseteqq": + // SUBSET OF ABOVE EQUALS SIGN with slash + return rune(0x2ac5), true + case "nsucc": + // DOES NOT SUCCEED + return rune(0x2281), true + case "nsucceq": + // SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN with slash + return rune(0x2ab0), true + case "nsupset": + // SUPERSET OF with vertical line + return rune(0x2283), true + case "nsup": + // NOT A SUPERSET OF + return rune(0x2285), true + case "nsupE": + // SUPERSET OF ABOVE EQUALS SIGN with slash + return rune(0x2ac6), true + case "nsupe": + // NEITHER A SUPERSET OF NOR EQUAL TO + return rune(0x2289), true + case "nsupseteq": + // NEITHER A SUPERSET OF NOR EQUAL TO + return rune(0x2289), true + case "nsupseteqq": + // SUPERSET OF ABOVE EQUALS SIGN with slash + return rune(0x2ac6), true + case "ntgl": + // NEITHER GREATER-THAN NOR LESS-THAN + return rune(0x2279), true + case "ntilde": + // LATIN SMALL LETTER N WITH TILDE + return rune(0xf1), true + case "ntlg": + // NEITHER LESS-THAN NOR GREATER-THAN + return rune(0x2278), true + case "ntriangleleft": + // NOT NORMAL SUBGROUP OF + return rune(0x22ea), true + case "ntrianglelefteq": + // NOT NORMAL SUBGROUP OF OR EQUAL TO + return rune(0x22ec), true + case "ntriangleright": + // DOES NOT CONTAIN AS NORMAL SUBGROUP + return rune(0x22eb), true + case "ntrianglerighteq": + // DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL + return rune(0x22ed), true + case "numero": + // NUMERO SIGN + return rune(0x2116), true + case "numsp": + // FIGURE SPACE + return rune(0x2007), true + case "nu": + // GREEK SMALL LETTER NU + return rune(0x03bd), true + case "num": + // NUMBER SIGN + return rune(0x23), true + case "nvDash": + // NOT TRUE + return rune(0x22ad), true + case "nvHarr": + // LEFT RIGHT DOUBLE ARROW WITH VERTICAL STROKE + return rune(0x2904), true + case "nvap": + // EQUIVALENT TO with vertical line + return rune(0x224d), true + case "nvbrtri": + // VERTICAL BAR BESIDE RIGHT TRIANGLE with slash + return rune(0x29d0), true + case "nvdash": + // DOES NOT PROVE + return rune(0x22ac), true + case "nvge": + // GREATER-THAN OR EQUAL TO with vertical line + return rune(0x2265), true + case "nvgt": + // GREATER-THAN SIGN with vertical line + return rune(0x3e), true + case "nvinfin": + // INFINITY NEGATED WITH VERTICAL BAR + return rune(0x29de), true + case "nvlArr": + // LEFTWARDS DOUBLE ARROW WITH VERTICAL STROKE + return rune(0x2902), true + case "nvle": + // LESS-THAN OR EQUAL TO with vertical line + return rune(0x2264), true + case "nvltrie": + // NORMAL SUBGROUP OF OR EQUAL TO with vertical line + return rune(0x22b4), true + case "nvlt": + // LESS-THAN SIGN with vertical line + return rune(0x3c), true + case "nvrArr": + // RIGHTWARDS DOUBLE ARROW WITH VERTICAL STROKE + return rune(0x2903), true + case "nvrtrie": + // CONTAINS AS NORMAL SUBGROUP OR EQUAL TO with vertical line + return rune(0x22b5), true + case "nvsim": + // TILDE OPERATOR with vertical line + return rune(0x223c), true + case "nwArr": + // NORTH WEST DOUBLE ARROW + return rune(0x21d6), true + case "nwarhk": + // NORTH WEST ARROW WITH HOOK + return rune(0x2923), true + case "nwarrow": + // NORTH WEST ARROW + return rune(0x2196), true + case "nwarr": + // NORTH WEST ARROW + return rune(0x2196), true + case "nwnear": + // NORTH WEST ARROW AND NORTH EAST ARROW + return rune(0x2927), true + case "nwonearr": + // NORTH WEST ARROW CROSSING NORTH EAST ARROW + return rune(0x2932), true + case "nwsesarr": + // NORTH WEST AND SOUTH EAST ARROW + return rune(0x2921), true + } + + case 'o': + switch name { + case "oS": + // CIRCLED LATIN CAPITAL LETTER S + return rune(0x24c8), true + case "oacgr": + // GREEK SMALL LETTER OMICRON WITH TONOS + return rune(0x03cc), true + case "oacute": + // LATIN SMALL LETTER O WITH ACUTE + return rune(0xf3), true + case "oast": + // CIRCLED ASTERISK OPERATOR + return rune(0x229b), true + case "obsol": + // CIRCLED REVERSE SOLIDUS + return rune(0x29b8), true + case "ocir": + // CIRCLED RING OPERATOR + return rune(0x229a), true + case "ocirc": + // LATIN SMALL LETTER O WITH CIRCUMFLEX + return rune(0xf4), true + case "ocy": + // CYRILLIC SMALL LETTER O + return rune(0x043e), true + case "odash": + // CIRCLED DASH + return rune(0x229d), true + case "odblac": + // LATIN SMALL LETTER O WITH DOUBLE ACUTE + return rune(0x0151), true + case "odiv": + // CIRCLED DIVISION SIGN + return rune(0x2a38), true + case "odot": + // CIRCLED DOT OPERATOR + return rune(0x2299), true + case "odsold": + // CIRCLED ANTICLOCKWISE-ROTATED DIVISION SIGN + return rune(0x29bc), true + case "oelig": + // LATIN SMALL LIGATURE OE + return rune(0x0153), true + case "ofcir": + // CIRCLED BULLET + return rune(0x29bf), true + case "ofr": + // MATHEMATICAL FRAKTUR SMALL O + return rune(0x01d52c), true + case "ogon": + // OGONEK + return rune(0x02db), true + case "ogr": + // GREEK SMALL LETTER OMICRON + return rune(0x03bf), true + case "ograve": + // LATIN SMALL LETTER O WITH GRAVE + return rune(0xf2), true + case "ogt": + // CIRCLED GREATER-THAN + return rune(0x29c1), true + case "ohacgr": + // GREEK SMALL LETTER OMEGA WITH TONOS + return rune(0x03ce), true + case "ohbar": + // CIRCLE WITH HORIZONTAL BAR + return rune(0x29b5), true + case "ohgr": + // GREEK SMALL LETTER OMEGA + return rune(0x03c9), true + case "ohm": + // GREEK CAPITAL LETTER OMEGA + return rune(0x03a9), true + case "oint": + // CONTOUR INTEGRAL + return rune(0x222e), true + case "olarr": + // ANTICLOCKWISE OPEN CIRCLE ARROW + return rune(0x21ba), true + case "olcir": + // CIRCLED WHITE BULLET + return rune(0x29be), true + case "olcross": + // CIRCLE WITH SUPERIMPOSED X + return rune(0x29bb), true + case "oline": + // OVERLINE + return rune(0x203e), true + case "olt": + // CIRCLED LESS-THAN + return rune(0x29c0), true + case "omacr": + // LATIN SMALL LETTER O WITH MACRON + return rune(0x014d), true + case "omega": + // GREEK SMALL LETTER OMEGA + return rune(0x03c9), true + case "omicron": + // GREEK SMALL LETTER OMICRON + return rune(0x03bf), true + case "omid": + // CIRCLED VERTICAL BAR + return rune(0x29b6), true + case "ominus": + // CIRCLED MINUS + return rune(0x2296), true + case "oopf": + // MATHEMATICAL DOUBLE-STRUCK SMALL O + return rune(0x01d560), true + case "opar": + // CIRCLED PARALLEL + return rune(0x29b7), true + case "operp": + // CIRCLED PERPENDICULAR + return rune(0x29b9), true + case "opfgamma": + // DOUBLE-STRUCK SMALL GAMMA + return rune(0x213d), true + case "opfpi": + // DOUBLE-STRUCK CAPITAL PI + return rune(0x213f), true + case "opfsum": + // DOUBLE-STRUCK N-ARY SUMMATION + return rune(0x2140), true + case "oplus": + // CIRCLED PLUS + return rune(0x2295), true + case "orarr": + // CLOCKWISE OPEN CIRCLE ARROW + return rune(0x21bb), true + case "or": + // LOGICAL OR + return rune(0x2228), true + case "orderof": + // SCRIPT SMALL O + return rune(0x2134), true + case "order": + // SCRIPT SMALL O + return rune(0x2134), true + case "ord": + // LOGICAL OR WITH HORIZONTAL DASH + return rune(0x2a5d), true + case "ordf": + // FEMININE ORDINAL INDICATOR + return rune(0xaa), true + case "ordm": + // MASCULINE ORDINAL INDICATOR + return rune(0xba), true + case "origof": + // ORIGINAL OF + return rune(0x22b6), true + case "oror": + // TWO INTERSECTING LOGICAL OR + return rune(0x2a56), true + case "orslope": + // SLOPING LARGE OR + return rune(0x2a57), true + case "orv": + // LOGICAL OR WITH MIDDLE STEM + return rune(0x2a5b), true + case "oscr": + // SCRIPT SMALL O + return rune(0x2134), true + case "oslash": + // LATIN SMALL LETTER O WITH STROKE + return rune(0xf8), true + case "osol": + // CIRCLED DIVISION SLASH + return rune(0x2298), true + case "otilde": + // LATIN SMALL LETTER O WITH TILDE + return rune(0xf5), true + case "otimes": + // CIRCLED TIMES + return rune(0x2297), true + case "otimesas": + // CIRCLED MULTIPLICATION SIGN WITH CIRCUMFLEX ACCENT + return rune(0x2a36), true + case "ouml": + // LATIN SMALL LETTER O WITH DIAERESIS + return rune(0xf6), true + case "ovbar": + // APL FUNCTIONAL SYMBOL CIRCLE STILE + return rune(0x233d), true + case "ovrbrk": + // TOP SQUARE BRACKET + return rune(0x23b4), true + case "ovrcub": + // TOP CURLY BRACKET + return rune(0x23de), true + case "ovrpar": + // TOP PARENTHESIS + return rune(0x23dc), true + case "oxuarr": + // UP ARROW THROUGH CIRCLE + return rune(0x29bd), true + } + + case 'p': + switch name { + case "part": + // PARTIAL DIFFERENTIAL + return rune(0x2202), true + case "par": + // PARALLEL TO + return rune(0x2225), true + case "parallel": + // PARALLEL TO + return rune(0x2225), true + case "para": + // PILCROW SIGN + return rune(0xb6), true + case "parsim": + // PARALLEL WITH TILDE OPERATOR + return rune(0x2af3), true + case "parsl": + // DOUBLE SOLIDUS OPERATOR + return rune(0x2afd), true + case "pcy": + // CYRILLIC SMALL LETTER PE + return rune(0x043f), true + case "percnt": + // PERCENT SIGN + return rune(0x25), true + case "period": + // FULL STOP + return rune(0x2e), true + case "permil": + // PER MILLE SIGN + return rune(0x2030), true + case "perp": + // UP TACK + return rune(0x22a5), true + case "pertenk": + // PER TEN THOUSAND SIGN + return rune(0x2031), true + case "pfr": + // MATHEMATICAL FRAKTUR SMALL P + return rune(0x01d52d), true + case "pgr": + // GREEK SMALL LETTER PI + return rune(0x03c0), true + case "phgr": + // GREEK SMALL LETTER PHI + return rune(0x03c6), true + case "phis": + // GREEK PHI SYMBOL + return rune(0x03d5), true + case "phiv": + // GREEK PHI SYMBOL + return rune(0x03d5), true + case "phi": + // GREEK SMALL LETTER PHI + return rune(0x03c6), true + case "phmmat": + // SCRIPT CAPITAL M + return rune(0x2133), true + case "phone": + // BLACK TELEPHONE + return rune(0x260e), true + case "pitchfork": + // PITCHFORK + return rune(0x22d4), true + case "piv": + // GREEK PI SYMBOL + return rune(0x03d6), true + case "pi": + // GREEK SMALL LETTER PI + return rune(0x03c0), true + case "planck": + // PLANCK CONSTANT OVER TWO PI + return rune(0x210f), true + case "planckh": + // PLANCK CONSTANT + return rune(0x210e), true + case "plankv": + // PLANCK CONSTANT OVER TWO PI + return rune(0x210f), true + case "plusacir": + // PLUS SIGN WITH CIRCUMFLEX ACCENT ABOVE + return rune(0x2a23), true + case "plusb": + // SQUARED PLUS + return rune(0x229e), true + case "pluscir": + // PLUS SIGN WITH SMALL CIRCLE ABOVE + return rune(0x2a22), true + case "plusdo": + // DOT PLUS + return rune(0x2214), true + case "plusdu": + // PLUS SIGN WITH DOT BELOW + return rune(0x2a25), true + case "pluse": + // PLUS SIGN ABOVE EQUALS SIGN + return rune(0x2a72), true + case "plusmn": + // PLUS-MINUS SIGN + return rune(0xb1), true + case "plussim": + // PLUS SIGN WITH TILDE BELOW + return rune(0x2a26), true + case "plustrif": + // PLUS SIGN WITH BLACK TRIANGLE + return rune(0x2a28), true + case "plustwo": + // PLUS SIGN WITH SUBSCRIPT TWO + return rune(0x2a27), true + case "plus": + // PLUS SIGN + return rune(0x2b), true + case "pm": + // PLUS-MINUS SIGN + return rune(0xb1), true + case "pointint": + // INTEGRAL AROUND A POINT OPERATOR + return rune(0x2a15), true + case "popf": + // MATHEMATICAL DOUBLE-STRUCK SMALL P + return rune(0x01d561), true + case "pound": + // POUND SIGN + return rune(0xa3), true + case "prod": + // N-ARY PRODUCT + return rune(0x220f), true + case "prop": + // PROPORTIONAL TO + return rune(0x221d), true + case "propto": + // PROPORTIONAL TO + return rune(0x221d), true + case "pr": + // PRECEDES + return rune(0x227a), true + case "prE": + // PRECEDES ABOVE EQUALS SIGN + return rune(0x2ab3), true + case "prap": + // PRECEDES ABOVE ALMOST EQUAL TO + return rune(0x2ab7), true + case "prcue": + // PRECEDES OR EQUAL TO + return rune(0x227c), true + case "prec": + // PRECEDES + return rune(0x227a), true + case "preccurlyeq": + // PRECEDES OR EQUAL TO + return rune(0x227c), true + case "precnsim": + // PRECEDES BUT NOT EQUIVALENT TO + return rune(0x22e8), true + case "precsim": + // PRECEDES OR EQUIVALENT TO + return rune(0x227e), true + case "pre": + // PRECEDES ABOVE SINGLE-LINE EQUALS SIGN + return rune(0x2aaf), true + case "precapprox": + // PRECEDES ABOVE ALMOST EQUAL TO + return rune(0x2ab7), true + case "preceq": + // PRECEDES ABOVE SINGLE-LINE EQUALS SIGN + return rune(0x2aaf), true + case "precnapprox": + // PRECEDES ABOVE NOT ALMOST EQUAL TO + return rune(0x2ab9), true + case "precneqq": + // PRECEDES ABOVE NOT EQUAL TO + return rune(0x2ab5), true + case "primes": + // DOUBLE-STRUCK CAPITAL P + return rune(0x2119), true + case "prime": + // PRIME + return rune(0x2032), true + case "prnE": + // PRECEDES ABOVE NOT EQUAL TO + return rune(0x2ab5), true + case "prnap": + // PRECEDES ABOVE NOT ALMOST EQUAL TO + return rune(0x2ab9), true + case "prnsim": + // PRECEDES BUT NOT EQUIVALENT TO + return rune(0x22e8), true + case "profalar": + // ALL AROUND-PROFILE + return rune(0x232e), true + case "profline": + // ARC + return rune(0x2312), true + case "profsurf": + // SEGMENT + return rune(0x2313), true + case "prsim": + // PRECEDES OR EQUIVALENT TO + return rune(0x227e), true + case "prurel": + // PRECEDES UNDER RELATION + return rune(0x22b0), true + case "pscr": + // MATHEMATICAL SCRIPT SMALL P + return rune(0x01d4c5), true + case "psgr": + // GREEK SMALL LETTER PSI + return rune(0x03c8), true + case "psi": + // GREEK SMALL LETTER PSI + return rune(0x03c8), true + case "puncsp": + // PUNCTUATION SPACE + return rune(0x2008), true + } + + case 'q': + switch name { + case "qfr": + // MATHEMATICAL FRAKTUR SMALL Q + return rune(0x01d52e), true + case "qint": + // QUADRUPLE INTEGRAL OPERATOR + return rune(0x2a0c), true + case "qopf": + // MATHEMATICAL DOUBLE-STRUCK SMALL Q + return rune(0x01d562), true + case "qprime": + // QUADRUPLE PRIME + return rune(0x2057), true + case "qscr": + // MATHEMATICAL SCRIPT SMALL Q + return rune(0x01d4c6), true + case "quaternions": + // DOUBLE-STRUCK CAPITAL H + return rune(0x210d), true + case "quatint": + // QUATERNION INTEGRAL OPERATOR + return rune(0x2a16), true + case "questeq": + // QUESTIONED EQUAL TO + return rune(0x225f), true + case "quest": + // QUESTION MARK + return rune(0x3f), true + case "quot": + // QUOTATION MARK + return rune(0x22), true + } + + case 'r': + switch name { + case "rAarr": + // RIGHTWARDS TRIPLE ARROW + return rune(0x21db), true + case "rArr": + // RIGHTWARDS DOUBLE ARROW + return rune(0x21d2), true + case "rAtail": + // RIGHTWARDS DOUBLE ARROW-TAIL + return rune(0x291c), true + case "rBarr": + // RIGHTWARDS TRIPLE DASH ARROW + return rune(0x290f), true + case "rHar": + // RIGHTWARDS HARPOON WITH BARB UP ABOVE RIGHTWARDS HARPOON WITH BARB DOWN + return rune(0x2964), true + case "race": + // REVERSED TILDE with underline + return rune(0x223d), true + case "racute": + // LATIN SMALL LETTER R WITH ACUTE + return rune(0x0155), true + case "radic": + // SQUARE ROOT + return rune(0x221a), true + case "raemptyv": + // EMPTY SET WITH RIGHT ARROW ABOVE + return rune(0x29b3), true + case "rang": + // MATHEMATICAL RIGHT ANGLE BRACKET + return rune(0x27e9), true + case "rangd": + // RIGHT ANGLE BRACKET WITH DOT + return rune(0x2992), true + case "range": + // REVERSED ANGLE WITH UNDERBAR + return rune(0x29a5), true + case "rangle": + // MATHEMATICAL RIGHT ANGLE BRACKET + return rune(0x27e9), true + case "raquo": + // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK + return rune(0xbb), true + case "rarr2": + // RIGHTWARDS PAIRED ARROWS + return rune(0x21c9), true + case "rarr3": + // THREE RIGHTWARDS ARROWS + return rune(0x21f6), true + case "rarrb": + // RIGHTWARDS ARROW TO BAR + return rune(0x21e5), true + case "rarrhk": + // RIGHTWARDS ARROW WITH HOOK + return rune(0x21aa), true + case "rarrlp": + // RIGHTWARDS ARROW WITH LOOP + return rune(0x21ac), true + case "rarrtl": + // RIGHTWARDS ARROW WITH TAIL + return rune(0x21a3), true + case "rarrw": + // RIGHTWARDS WAVE ARROW + return rune(0x219d), true + case "rarr": + // RIGHTWARDS ARROW + return rune(0x2192), true + case "rarrap": + // RIGHTWARDS ARROW ABOVE ALMOST EQUAL TO + return rune(0x2975), true + case "rarrbfs": + // RIGHTWARDS ARROW FROM BAR TO BLACK DIAMOND + return rune(0x2920), true + case "rarrc": + // WAVE ARROW POINTING DIRECTLY RIGHT + return rune(0x2933), true + case "rarrfs": + // RIGHTWARDS ARROW TO BLACK DIAMOND + return rune(0x291e), true + case "rarrpl": + // RIGHTWARDS ARROW WITH PLUS BELOW + return rune(0x2945), true + case "rarrsim": + // RIGHTWARDS ARROW ABOVE TILDE OPERATOR + return rune(0x2974), true + case "rarrx": + // RIGHTWARDS ARROW THROUGH X + return rune(0x2947), true + case "ratail": + // RIGHTWARDS ARROW-TAIL + return rune(0x291a), true + case "ratio": + // RATIO + return rune(0x2236), true + case "rationals": + // DOUBLE-STRUCK CAPITAL Q + return rune(0x211a), true + case "rbarr": + // RIGHTWARDS DOUBLE DASH ARROW + return rune(0x290d), true + case "rbbrk": + // LIGHT RIGHT TORTOISE SHELL BRACKET ORNAMENT + return rune(0x2773), true + case "rbrace": + // RIGHT CURLY BRACKET + return rune(0x7d), true + case "rbrack": + // RIGHT SQUARE BRACKET + return rune(0x5d), true + case "rbrke": + // RIGHT SQUARE BRACKET WITH UNDERBAR + return rune(0x298c), true + case "rbrksld": + // RIGHT SQUARE BRACKET WITH TICK IN BOTTOM CORNER + return rune(0x298e), true + case "rbrkslu": + // RIGHT SQUARE BRACKET WITH TICK IN TOP CORNER + return rune(0x2990), true + case "rcaron": + // LATIN SMALL LETTER R WITH CARON + return rune(0x0159), true + case "rcedil": + // LATIN SMALL LETTER R WITH CEDILLA + return rune(0x0157), true + case "rceil": + // RIGHT CEILING + return rune(0x2309), true + case "rcub": + // RIGHT CURLY BRACKET + return rune(0x7d), true + case "rcy": + // CYRILLIC SMALL LETTER ER + return rune(0x0440), true + case "rdca": + // ARROW POINTING DOWNWARDS THEN CURVING RIGHTWARDS + return rune(0x2937), true + case "rdharb": + // RIGHTWARDS HARPOON WITH BARB DOWN TO BAR + return rune(0x2957), true + case "rdiag": + // BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT + return rune(0x2571), true + case "rdiofdi": + // RISING DIAGONAL CROSSING FALLING DIAGONAL + return rune(0x292b), true + case "rdldhar": + // RIGHTWARDS HARPOON WITH BARB DOWN ABOVE LEFTWARDS HARPOON WITH BARB DOWN + return rune(0x2969), true + case "rdosearr": + // RISING DIAGONAL CROSSING SOUTH EAST ARROW + return rune(0x2930), true + case "rdquor": + // RIGHT DOUBLE QUOTATION MARK + return rune(0x201d), true + case "rdquo": + // RIGHT DOUBLE QUOTATION MARK + return rune(0x201d), true + case "rdsh": + // DOWNWARDS ARROW WITH TIP RIGHTWARDS + return rune(0x21b3), true + case "realpart": + // BLACK-LETTER CAPITAL R + return rune(0x211c), true + case "reals": + // DOUBLE-STRUCK CAPITAL R + return rune(0x211d), true + case "real": + // BLACK-LETTER CAPITAL R + return rune(0x211c), true + case "realine": + // SCRIPT CAPITAL R + return rune(0x211b), true + case "rect": + // WHITE RECTANGLE + return rune(0x25ad), true + case "reg": + // REGISTERED SIGN + return rune(0xae), true + case "rfbowtie": + // BOWTIE WITH RIGHT HALF BLACK + return rune(0x29d2), true + case "rfisht": + // RIGHT FISH TAIL + return rune(0x297d), true + case "rfloor": + // RIGHT FLOOR + return rune(0x230b), true + case "rfr": + // MATHEMATICAL FRAKTUR SMALL R + return rune(0x01d52f), true + case "rftimes": + // TIMES WITH RIGHT HALF BLACK + return rune(0x29d5), true + case "rgr": + // GREEK SMALL LETTER RHO + return rune(0x03c1), true + case "rhard": + // RIGHTWARDS HARPOON WITH BARB DOWNWARDS + return rune(0x21c1), true + case "rharu": + // RIGHTWARDS HARPOON WITH BARB UPWARDS + return rune(0x21c0), true + case "rharul": + // RIGHTWARDS HARPOON WITH BARB UP ABOVE LONG DASH + return rune(0x296c), true + case "rhov": + // GREEK RHO SYMBOL + return rune(0x03f1), true + case "rho": + // GREEK SMALL LETTER RHO + return rune(0x03c1), true + case "rightarrowtail": + // RIGHTWARDS ARROW WITH TAIL + return rune(0x21a3), true + case "rightarrow": + // RIGHTWARDS ARROW + return rune(0x2192), true + case "rightharpoondown": + // RIGHTWARDS HARPOON WITH BARB DOWNWARDS + return rune(0x21c1), true + case "rightharpoonup": + // RIGHTWARDS HARPOON WITH BARB UPWARDS + return rune(0x21c0), true + case "rightleftarrows": + // RIGHTWARDS ARROW OVER LEFTWARDS ARROW + return rune(0x21c4), true + case "rightleftharpoons": + // RIGHTWARDS HARPOON OVER LEFTWARDS HARPOON + return rune(0x21cc), true + case "rightrightarrows": + // RIGHTWARDS PAIRED ARROWS + return rune(0x21c9), true + case "rightsquigarrow": + // RIGHTWARDS WAVE ARROW + return rune(0x219d), true + case "rightthreetimes": + // RIGHT SEMIDIRECT PRODUCT + return rune(0x22cc), true + case "rimply": + // RIGHT DOUBLE ARROW WITH ROUNDED HEAD + return rune(0x2970), true + case "ring": + // RING ABOVE + return rune(0x02da), true + case "risingdotseq": + // IMAGE OF OR APPROXIMATELY EQUAL TO + return rune(0x2253), true + case "rlarr2": + // RIGHTWARDS ARROW OVER LEFTWARDS ARROW + return rune(0x21c4), true + case "rlarr": + // RIGHTWARDS ARROW OVER LEFTWARDS ARROW + return rune(0x21c4), true + case "rlhar": + // RIGHTWARDS HARPOON OVER LEFTWARDS HARPOON + return rune(0x21cc), true + case "rlhar2": + // RIGHTWARDS HARPOON OVER LEFTWARDS HARPOON + return rune(0x21cc), true + case "rlm": + // RIGHT-TO-LEFT MARK + return rune(0x200f), true + case "rmoust": + // UPPER RIGHT OR LOWER LEFT CURLY BRACKET SECTION + return rune(0x23b1), true + case "rmoustache": + // UPPER RIGHT OR LOWER LEFT CURLY BRACKET SECTION + return rune(0x23b1), true + case "rnmid": + // DOES NOT DIVIDE WITH REVERSED NEGATION SLASH + return rune(0x2aee), true + case "roang": + // MATHEMATICAL RIGHT WHITE TORTOISE SHELL BRACKET + return rune(0x27ed), true + case "roarr": + // RIGHTWARDS OPEN-HEADED ARROW + return rune(0x21fe), true + case "robrk": + // MATHEMATICAL RIGHT WHITE SQUARE BRACKET + return rune(0x27e7), true + case "rocub": + // RIGHT WHITE CURLY BRACKET + return rune(0x2984), true + case "ropar": + // RIGHT WHITE PARENTHESIS + return rune(0x2986), true + case "ropf": + // MATHEMATICAL DOUBLE-STRUCK SMALL R + return rune(0x01d563), true + case "roplus": + // PLUS SIGN IN RIGHT HALF CIRCLE + return rune(0x2a2e), true + case "rotimes": + // MULTIPLICATION SIGN IN RIGHT HALF CIRCLE + return rune(0x2a35), true + case "rpargt": + // RIGHT ARC GREATER-THAN BRACKET + return rune(0x2994), true + case "rpar": + // RIGHT PARENTHESIS + return rune(0x29), true + case "rppolint": + // LINE INTEGRATION WITH RECTANGULAR PATH AROUND POLE + return rune(0x2a12), true + case "rrarr": + // RIGHTWARDS PAIRED ARROWS + return rune(0x21c9), true + case "rsaquo": + // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK + return rune(0x203a), true + case "rscr": + // MATHEMATICAL SCRIPT SMALL R + return rune(0x01d4c7), true + case "rsh": + // UPWARDS ARROW WITH TIP RIGHTWARDS + return rune(0x21b1), true + case "rsolbar": + // REVERSE SOLIDUS WITH HORIZONTAL STROKE + return rune(0x29f7), true + case "rsqb": + // RIGHT SQUARE BRACKET + return rune(0x5d), true + case "rsquor": + // RIGHT SINGLE QUOTATION MARK + return rune(0x2019), true + case "rsquo": + // RIGHT SINGLE QUOTATION MARK + return rune(0x2019), true + case "rthree": + // RIGHT SEMIDIRECT PRODUCT + return rune(0x22cc), true + case "rtimes": + // RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT + return rune(0x22ca), true + case "rtrie": + // CONTAINS AS NORMAL SUBGROUP OR EQUAL TO + return rune(0x22b5), true + case "rtrif": + // BLACK RIGHT-POINTING SMALL TRIANGLE + return rune(0x25b8), true + case "rtri": + // WHITE RIGHT-POINTING SMALL TRIANGLE + return rune(0x25b9), true + case "rtriltri": + // RIGHT TRIANGLE ABOVE LEFT TRIANGLE + return rune(0x29ce), true + case "ruharb": + // RIGHTWARDS HARPOON WITH BARB UP TO BAR + return rune(0x2953), true + case "ruluhar": + // RIGHTWARDS HARPOON WITH BARB UP ABOVE LEFTWARDS HARPOON WITH BARB UP + return rune(0x2968), true + case "rx": + // PRESCRIPTION TAKE + return rune(0x211e), true + } + + case 's': + switch name { + case "sacute": + // LATIN SMALL LETTER S WITH ACUTE + return rune(0x015b), true + case "samalg": + // N-ARY COPRODUCT + return rune(0x2210), true + case "sampi": + // GREEK LETTER SAMPI + return rune(0x03e0), true + case "sbquo": + // SINGLE LOW-9 QUOTATION MARK + return rune(0x201a), true + case "sbsol": + // SMALL REVERSE SOLIDUS + return rune(0xfe68), true + case "sc": + // SUCCEEDS + return rune(0x227b), true + case "scE": + // SUCCEEDS ABOVE EQUALS SIGN + return rune(0x2ab4), true + case "scap": + // SUCCEEDS ABOVE ALMOST EQUAL TO + return rune(0x2ab8), true + case "scaron": + // LATIN SMALL LETTER S WITH CARON + return rune(0x0161), true + case "sccue": + // SUCCEEDS OR EQUAL TO + return rune(0x227d), true + case "scedil": + // LATIN SMALL LETTER S WITH CEDILLA + return rune(0x015f), true + case "sce": + // SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN + return rune(0x2ab0), true + case "scirc": + // LATIN SMALL LETTER S WITH CIRCUMFLEX + return rune(0x015d), true + case "scnE": + // SUCCEEDS ABOVE NOT EQUAL TO + return rune(0x2ab6), true + case "scnap": + // SUCCEEDS ABOVE NOT ALMOST EQUAL TO + return rune(0x2aba), true + case "scnsim": + // SUCCEEDS BUT NOT EQUIVALENT TO + return rune(0x22e9), true + case "scpolint": + // LINE INTEGRATION WITH SEMICIRCULAR PATH AROUND POLE + return rune(0x2a13), true + case "scsim": + // SUCCEEDS OR EQUIVALENT TO + return rune(0x227f), true + case "scy": + // CYRILLIC SMALL LETTER ES + return rune(0x0441), true + case "sdotb": + // SQUARED DOT OPERATOR + return rune(0x22a1), true + case "sdot": + // DOT OPERATOR + return rune(0x22c5), true + case "sdote": + // EQUALS SIGN WITH DOT BELOW + return rune(0x2a66), true + case "seArr": + // SOUTH EAST DOUBLE ARROW + return rune(0x21d8), true + case "searhk": + // SOUTH EAST ARROW WITH HOOK + return rune(0x2925), true + case "searrow": + // SOUTH EAST ARROW + return rune(0x2198), true + case "searr": + // SOUTH EAST ARROW + return rune(0x2198), true + case "sect": + // SECTION SIGN + return rune(0xa7), true + case "semi": + // SEMICOLON + return rune(0x3b), true + case "seonearr": + // SOUTH EAST ARROW CROSSING NORTH EAST ARROW + return rune(0x292d), true + case "seswar": + // SOUTH EAST ARROW AND SOUTH WEST ARROW + return rune(0x2929), true + case "setminus": + // SET MINUS + return rune(0x2216), true + case "setmn": + // SET MINUS + return rune(0x2216), true + case "sext": + // SIX POINTED BLACK STAR + return rune(0x2736), true + case "sfgr": + // GREEK SMALL LETTER FINAL SIGMA + return rune(0x03c2), true + case "sfrown": + // FROWN + return rune(0x2322), true + case "sfr": + // MATHEMATICAL FRAKTUR SMALL S + return rune(0x01d530), true + case "sgr": + // GREEK SMALL LETTER SIGMA + return rune(0x03c3), true + case "sharp": + // MUSIC SHARP SIGN + return rune(0x266f), true + case "shchcy": + // CYRILLIC SMALL LETTER SHCHA + return rune(0x0449), true + case "shcy": + // CYRILLIC SMALL LETTER SHA + return rune(0x0448), true + case "shortmid": + // DIVIDES + return rune(0x2223), true + case "shortparallel": + // PARALLEL TO + return rune(0x2225), true + case "shuffle": + // SHUFFLE PRODUCT + return rune(0x29e2), true + case "shy": + // SOFT HYPHEN + return rune(0xad), true + case "sigma": + // GREEK SMALL LETTER SIGMA + return rune(0x03c3), true + case "sigmaf": + // GREEK SMALL LETTER FINAL SIGMA + return rune(0x03c2), true + case "sigmav": + // GREEK SMALL LETTER FINAL SIGMA + return rune(0x03c2), true + case "sim": + // TILDE OPERATOR + return rune(0x223c), true + case "simdot": + // TILDE OPERATOR WITH DOT ABOVE + return rune(0x2a6a), true + case "sime": + // ASYMPTOTICALLY EQUAL TO + return rune(0x2243), true + case "simeq": + // ASYMPTOTICALLY EQUAL TO + return rune(0x2243), true + case "simg": + // SIMILAR OR GREATER-THAN + return rune(0x2a9e), true + case "simgE": + // SIMILAR ABOVE GREATER-THAN ABOVE EQUALS SIGN + return rune(0x2aa0), true + case "siml": + // SIMILAR OR LESS-THAN + return rune(0x2a9d), true + case "simlE": + // SIMILAR ABOVE LESS-THAN ABOVE EQUALS SIGN + return rune(0x2a9f), true + case "simne": + // APPROXIMATELY BUT NOT ACTUALLY EQUAL TO + return rune(0x2246), true + case "simplus": + // PLUS SIGN WITH TILDE ABOVE + return rune(0x2a24), true + case "simrarr": + // TILDE OPERATOR ABOVE RIGHTWARDS ARROW + return rune(0x2972), true + case "slarr": + // LEFTWARDS ARROW + return rune(0x2190), true + case "slint": + // INTEGRAL AVERAGE WITH SLASH + return rune(0x2a0f), true + case "smallsetminus": + // SET MINUS + return rune(0x2216), true + case "smashp": + // SMASH PRODUCT + return rune(0x2a33), true + case "smeparsl": + // EQUALS SIGN AND SLANTED PARALLEL WITH TILDE ABOVE + return rune(0x29e4), true + case "smid": + // DIVIDES + return rune(0x2223), true + case "smile": + // SMILE + return rune(0x2323), true + case "smt": + // SMALLER THAN + return rune(0x2aaa), true + case "smte": + // SMALLER THAN OR EQUAL TO + return rune(0x2aac), true + case "smtes": + // SMALLER THAN OR slanted EQUAL + return rune(0x2aac), true + case "softcy": + // CYRILLIC SMALL LETTER SOFT SIGN + return rune(0x044c), true + case "solbar": + // APL FUNCTIONAL SYMBOL SLASH BAR + return rune(0x233f), true + case "solb": + // SQUARED RISING DIAGONAL SLASH + return rune(0x29c4), true + case "sol": + // SOLIDUS + return rune(0x2f), true + case "sopf": + // MATHEMATICAL DOUBLE-STRUCK SMALL S + return rune(0x01d564), true + case "spades": + // BLACK SPADE SUIT + return rune(0x2660), true + case "spadesuit": + // BLACK SPADE SUIT + return rune(0x2660), true + case "spar": + // PARALLEL TO + return rune(0x2225), true + case "sqcap": + // SQUARE CAP + return rune(0x2293), true + case "sqcaps": + // SQUARE CAP with serifs + return rune(0x2293), true + case "sqcup": + // SQUARE CUP + return rune(0x2294), true + case "sqcups": + // SQUARE CUP with serifs + return rune(0x2294), true + case "sqsub": + // SQUARE IMAGE OF + return rune(0x228f), true + case "sqsube": + // SQUARE IMAGE OF OR EQUAL TO + return rune(0x2291), true + case "sqsubset": + // SQUARE IMAGE OF + return rune(0x228f), true + case "sqsubseteq": + // SQUARE IMAGE OF OR EQUAL TO + return rune(0x2291), true + case "sqsup": + // SQUARE ORIGINAL OF + return rune(0x2290), true + case "sqsupe": + // SQUARE ORIGINAL OF OR EQUAL TO + return rune(0x2292), true + case "sqsupset": + // SQUARE ORIGINAL OF + return rune(0x2290), true + case "sqsupseteq": + // SQUARE ORIGINAL OF OR EQUAL TO + return rune(0x2292), true + case "squ": + // WHITE SQUARE + return rune(0x25a1), true + case "square": + // WHITE SQUARE + return rune(0x25a1), true + case "squarf": + // BLACK SMALL SQUARE + return rune(0x25aa), true + case "squb": + // SQUARED SQUARE + return rune(0x29c8), true + case "squerr": + // ERROR-BARRED WHITE SQUARE + return rune(0x29ee), true + case "squf": + // BLACK SMALL SQUARE + return rune(0x25aa), true + case "squferr": + // ERROR-BARRED BLACK SQUARE + return rune(0x29ef), true + case "srarr": + // RIGHTWARDS ARROW + return rune(0x2192), true + case "sscr": + // MATHEMATICAL SCRIPT SMALL S + return rune(0x01d4c8), true + case "ssetmn": + // SET MINUS + return rune(0x2216), true + case "ssmile": + // SMILE + return rune(0x2323), true + case "sstarf": + // STAR OPERATOR + return rune(0x22c6), true + case "starf": + // BLACK STAR + return rune(0x2605), true + case "star": + // WHITE STAR + return rune(0x2606), true + case "stigma": + // GREEK LETTER STIGMA + return rune(0x03da), true + case "straightepsilon": + // GREEK LUNATE EPSILON SYMBOL + return rune(0x03f5), true + case "straightphi": + // GREEK PHI SYMBOL + return rune(0x03d5), true + case "strns": + // MACRON + return rune(0xaf), true + case "sub": + // SUBSET OF + return rune(0x2282), true + case "subE": + // SUBSET OF ABOVE EQUALS SIGN + return rune(0x2ac5), true + case "subdot": + // SUBSET WITH DOT + return rune(0x2abd), true + case "sube": + // SUBSET OF OR EQUAL TO + return rune(0x2286), true + case "subedot": + // SUBSET OF OR EQUAL TO WITH DOT ABOVE + return rune(0x2ac3), true + case "submult": + // SUBSET WITH MULTIPLICATION SIGN BELOW + return rune(0x2ac1), true + case "subnE": + // SUBSET OF ABOVE NOT EQUAL TO + return rune(0x2acb), true + case "subne": + // SUBSET OF WITH NOT EQUAL TO + return rune(0x228a), true + case "subplus": + // SUBSET WITH PLUS SIGN BELOW + return rune(0x2abf), true + case "subrarr": + // SUBSET ABOVE RIGHTWARDS ARROW + return rune(0x2979), true + case "subset": + // SUBSET OF + return rune(0x2282), true + case "subseteq": + // SUBSET OF OR EQUAL TO + return rune(0x2286), true + case "subseteqq": + // SUBSET OF ABOVE EQUALS SIGN + return rune(0x2ac5), true + case "subsetneq": + // SUBSET OF WITH NOT EQUAL TO + return rune(0x228a), true + case "subsetneqq": + // SUBSET OF ABOVE NOT EQUAL TO + return rune(0x2acb), true + case "subsim": + // SUBSET OF ABOVE TILDE OPERATOR + return rune(0x2ac7), true + case "subsub": + // SUBSET ABOVE SUBSET + return rune(0x2ad5), true + case "subsup": + // SUBSET ABOVE SUPERSET + return rune(0x2ad3), true + case "succ": + // SUCCEEDS + return rune(0x227b), true + case "succapprox": + // SUCCEEDS ABOVE ALMOST EQUAL TO + return rune(0x2ab8), true + case "succcurlyeq": + // SUCCEEDS OR EQUAL TO + return rune(0x227d), true + case "succeq": + // SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN + return rune(0x2ab0), true + case "succnapprox": + // SUCCEEDS ABOVE NOT ALMOST EQUAL TO + return rune(0x2aba), true + case "succneqq": + // SUCCEEDS ABOVE NOT EQUAL TO + return rune(0x2ab6), true + case "succnsim": + // SUCCEEDS BUT NOT EQUIVALENT TO + return rune(0x22e9), true + case "succsim": + // SUCCEEDS OR EQUIVALENT TO + return rune(0x227f), true + case "sum": + // N-ARY SUMMATION + return rune(0x2211), true + case "sumint": + // SUMMATION WITH INTEGRAL + return rune(0x2a0b), true + case "sung": + // EIGHTH NOTE + return rune(0x266a), true + case "sup": + // SUPERSET OF + return rune(0x2283), true + case "sup1": + // SUPERSCRIPT ONE + return rune(0xb9), true + case "sup2": + // SUPERSCRIPT TWO + return rune(0xb2), true + case "sup3": + // SUPERSCRIPT THREE + return rune(0xb3), true + case "supE": + // SUPERSET OF ABOVE EQUALS SIGN + return rune(0x2ac6), true + case "supdot": + // SUPERSET WITH DOT + return rune(0x2abe), true + case "supdsub": + // SUPERSET BESIDE AND JOINED BY DASH WITH SUBSET + return rune(0x2ad8), true + case "supe": + // SUPERSET OF OR EQUAL TO + return rune(0x2287), true + case "supedot": + // SUPERSET OF OR EQUAL TO WITH DOT ABOVE + return rune(0x2ac4), true + case "suphsol": + // SUPERSET PRECEDING SOLIDUS + return rune(0x27c9), true + case "suphsub": + // SUPERSET BESIDE SUBSET + return rune(0x2ad7), true + case "suplarr": + // SUPERSET ABOVE LEFTWARDS ARROW + return rune(0x297b), true + case "supmult": + // SUPERSET WITH MULTIPLICATION SIGN BELOW + return rune(0x2ac2), true + case "supnE": + // SUPERSET OF ABOVE NOT EQUAL TO + return rune(0x2acc), true + case "supne": + // SUPERSET OF WITH NOT EQUAL TO + return rune(0x228b), true + case "supplus": + // SUPERSET WITH PLUS SIGN BELOW + return rune(0x2ac0), true + case "supset": + // SUPERSET OF + return rune(0x2283), true + case "supseteq": + // SUPERSET OF OR EQUAL TO + return rune(0x2287), true + case "supseteqq": + // SUPERSET OF ABOVE EQUALS SIGN + return rune(0x2ac6), true + case "supsetneq": + // SUPERSET OF WITH NOT EQUAL TO + return rune(0x228b), true + case "supsetneqq": + // SUPERSET OF ABOVE NOT EQUAL TO + return rune(0x2acc), true + case "supsim": + // SUPERSET OF ABOVE TILDE OPERATOR + return rune(0x2ac8), true + case "supsub": + // SUPERSET ABOVE SUBSET + return rune(0x2ad4), true + case "supsup": + // SUPERSET ABOVE SUPERSET + return rune(0x2ad6), true + case "swArr": + // SOUTH WEST DOUBLE ARROW + return rune(0x21d9), true + case "swarhk": + // SOUTH WEST ARROW WITH HOOK + return rune(0x2926), true + case "swarrow": + // SOUTH WEST ARROW + return rune(0x2199), true + case "swarr": + // SOUTH WEST ARROW + return rune(0x2199), true + case "swnwar": + // SOUTH WEST ARROW AND NORTH WEST ARROW + return rune(0x292a), true + case "szlig": + // LATIN SMALL LETTER SHARP S + return rune(0xdf), true + } + + case 't': + switch name { + case "target": + // POSITION INDICATOR + return rune(0x2316), true + case "tau": + // GREEK SMALL LETTER TAU + return rune(0x03c4), true + case "tbrk": + // TOP SQUARE BRACKET + return rune(0x23b4), true + case "tcaron": + // LATIN SMALL LETTER T WITH CARON + return rune(0x0165), true + case "tcedil": + // LATIN SMALL LETTER T WITH CEDILLA + return rune(0x0163), true + case "tcy": + // CYRILLIC SMALL LETTER TE + return rune(0x0442), true + case "tdot": + // COMBINING THREE DOTS ABOVE + return rune(0x20db), true + case "telrec": + // TELEPHONE RECORDER + return rune(0x2315), true + case "tfr": + // MATHEMATICAL FRAKTUR SMALL T + return rune(0x01d531), true + case "tgr": + // GREEK SMALL LETTER TAU + return rune(0x03c4), true + case "there4": + // THEREFORE + return rune(0x2234), true + case "therefore": + // THEREFORE + return rune(0x2234), true + case "thermod": + // THERMODYNAMIC + return rune(0x29e7), true + case "thetasym": + // GREEK THETA SYMBOL + return rune(0x03d1), true + case "thetas": + // GREEK SMALL LETTER THETA + return rune(0x03b8), true + case "thetav": + // GREEK THETA SYMBOL + return rune(0x03d1), true + case "theta": + // GREEK SMALL LETTER THETA + return rune(0x03b8), true + case "thgr": + // GREEK SMALL LETTER THETA + return rune(0x03b8), true + case "thickapprox": + // ALMOST EQUAL TO + return rune(0x2248), true + case "thicksim": + // TILDE OPERATOR + return rune(0x223c), true + case "thinsp": + // THIN SPACE + return rune(0x2009), true + case "thkap": + // ALMOST EQUAL TO + return rune(0x2248), true + case "thksim": + // TILDE OPERATOR + return rune(0x223c), true + case "thorn": + // LATIN SMALL LETTER THORN + return rune(0xfe), true + case "tilde": + // SMALL TILDE + return rune(0x02dc), true + case "timeint": + // INTEGRAL WITH TIMES SIGN + return rune(0x2a18), true + case "timesb": + // SQUARED TIMES + return rune(0x22a0), true + case "timesbar": + // MULTIPLICATION SIGN WITH UNDERBAR + return rune(0x2a31), true + case "timesd": + // MULTIPLICATION SIGN WITH DOT ABOVE + return rune(0x2a30), true + case "times": + // MULTIPLICATION SIGN + return rune(0xd7), true + case "tint": + // TRIPLE INTEGRAL + return rune(0x222d), true + case "toea": + // NORTH EAST ARROW AND SOUTH EAST ARROW + return rune(0x2928), true + case "top": + // DOWN TACK + return rune(0x22a4), true + case "topbot": + // APL FUNCTIONAL SYMBOL I-BEAM + return rune(0x2336), true + case "topcir": + // DOWN TACK WITH CIRCLE BELOW + return rune(0x2af1), true + case "topfork": + // PITCHFORK WITH TEE TOP + return rune(0x2ada), true + case "topf": + // MATHEMATICAL DOUBLE-STRUCK SMALL T + return rune(0x01d565), true + case "tosa": + // SOUTH EAST ARROW AND SOUTH WEST ARROW + return rune(0x2929), true + case "tprime": + // TRIPLE PRIME + return rune(0x2034), true + case "trade": + // TRADE MARK SIGN + return rune(0x2122), true + case "triS": + // S IN TRIANGLE + return rune(0x29cc), true + case "trianglelefteq": + // NORMAL SUBGROUP OF OR EQUAL TO + return rune(0x22b4), true + case "triangleq": + // DELTA EQUAL TO + return rune(0x225c), true + case "trianglerighteq": + // CONTAINS AS NORMAL SUBGROUP OR EQUAL TO + return rune(0x22b5), true + case "triangle": + // WHITE UP-POINTING SMALL TRIANGLE + return rune(0x25b5), true + case "triangledown": + // WHITE DOWN-POINTING SMALL TRIANGLE + return rune(0x25bf), true + case "triangleleft": + // WHITE LEFT-POINTING SMALL TRIANGLE + return rune(0x25c3), true + case "triangleright": + // WHITE RIGHT-POINTING SMALL TRIANGLE + return rune(0x25b9), true + case "tribar": + // TRIANGLE WITH UNDERBAR + return rune(0x29cb), true + case "tridot": + // WHITE UP-POINTING TRIANGLE WITH DOT + return rune(0x25ec), true + case "tridoto": + // TRIANGLE WITH DOT ABOVE + return rune(0x29ca), true + case "trie": + // DELTA EQUAL TO + return rune(0x225c), true + case "triminus": + // MINUS SIGN IN TRIANGLE + return rune(0x2a3a), true + case "triplus": + // PLUS SIGN IN TRIANGLE + return rune(0x2a39), true + case "trisb": + // TRIANGLE WITH SERIFS AT BOTTOM + return rune(0x29cd), true + case "tritime": + // MULTIPLICATION SIGN IN TRIANGLE + return rune(0x2a3b), true + case "trpezium": + // WHITE TRAPEZIUM + return rune(0x23e2), true + case "tscr": + // MATHEMATICAL SCRIPT SMALL T + return rune(0x01d4c9), true + case "tscy": + // CYRILLIC SMALL LETTER TSE + return rune(0x0446), true + case "tshcy": + // CYRILLIC SMALL LETTER TSHE + return rune(0x045b), true + case "tstrok": + // LATIN SMALL LETTER T WITH STROKE + return rune(0x0167), true + case "tverbar": + // TRIPLE VERTICAL BAR DELIMITER + return rune(0x2980), true + case "twixt": + // BETWEEN + return rune(0x226c), true + case "twoheadleftarrow": + // LEFTWARDS TWO HEADED ARROW + return rune(0x219e), true + case "twoheadrightarrow": + // RIGHTWARDS TWO HEADED ARROW + return rune(0x21a0), true + } + + case 'u': + switch name { + case "uAarr": + // UPWARDS TRIPLE ARROW + return rune(0x290a), true + case "uArr": + // UPWARDS DOUBLE ARROW + return rune(0x21d1), true + case "uHar": + // UPWARDS HARPOON WITH BARB LEFT BESIDE UPWARDS HARPOON WITH BARB RIGHT + return rune(0x2963), true + case "uacgr": + // GREEK SMALL LETTER UPSILON WITH TONOS + return rune(0x03cd), true + case "uacute": + // LATIN SMALL LETTER U WITH ACUTE + return rune(0xfa), true + case "uarr2": + // UPWARDS PAIRED ARROWS + return rune(0x21c8), true + case "uarr": + // UPWARDS ARROW + return rune(0x2191), true + case "uarrb": + // UPWARDS ARROW TO BAR + return rune(0x2912), true + case "uarrln": + // UPWARDS ARROW WITH HORIZONTAL STROKE + return rune(0x2909), true + case "ubrcy": + // CYRILLIC SMALL LETTER SHORT U + return rune(0x045e), true + case "ubreve": + // LATIN SMALL LETTER U WITH BREVE + return rune(0x016d), true + case "ucirc": + // LATIN SMALL LETTER U WITH CIRCUMFLEX + return rune(0xfb), true + case "ucy": + // CYRILLIC SMALL LETTER U + return rune(0x0443), true + case "udarr": + // UPWARDS ARROW LEFTWARDS OF DOWNWARDS ARROW + return rune(0x21c5), true + case "udblac": + // LATIN SMALL LETTER U WITH DOUBLE ACUTE + return rune(0x0171), true + case "udhar": + // UPWARDS HARPOON WITH BARB LEFT BESIDE DOWNWARDS HARPOON WITH BARB RIGHT + return rune(0x296e), true + case "udiagr": + // GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS + return rune(0x03b0), true + case "udigr": + // GREEK SMALL LETTER UPSILON WITH DIALYTIKA + return rune(0x03cb), true + case "udrbrk": + // BOTTOM SQUARE BRACKET + return rune(0x23b5), true + case "udrcub": + // BOTTOM CURLY BRACKET + return rune(0x23df), true + case "udrpar": + // BOTTOM PARENTHESIS + return rune(0x23dd), true + case "ufisht": + // UP FISH TAIL + return rune(0x297e), true + case "ufr": + // MATHEMATICAL FRAKTUR SMALL U + return rune(0x01d532), true + case "ugr": + // GREEK SMALL LETTER UPSILON + return rune(0x03c5), true + case "ugrave": + // LATIN SMALL LETTER U WITH GRAVE + return rune(0xf9), true + case "uharl": + // UPWARDS HARPOON WITH BARB LEFTWARDS + return rune(0x21bf), true + case "uharr": + // UPWARDS HARPOON WITH BARB RIGHTWARDS + return rune(0x21be), true + case "uhblk": + // UPPER HALF BLOCK + return rune(0x2580), true + case "ulcorn": + // TOP LEFT CORNER + return rune(0x231c), true + case "ulcorner": + // TOP LEFT CORNER + return rune(0x231c), true + case "ulcrop": + // TOP LEFT CROP + return rune(0x230f), true + case "uldlshar": + // UP BARB LEFT DOWN BARB LEFT HARPOON + return rune(0x2951), true + case "ulharb": + // UPWARDS HARPOON WITH BARB LEFT TO BAR + return rune(0x2958), true + case "ultri": + // UPPER LEFT TRIANGLE + return rune(0x25f8), true + case "umacr": + // LATIN SMALL LETTER U WITH MACRON + return rune(0x016b), true + case "uml": + // DIAERESIS + return rune(0xa8), true + case "uogon": + // LATIN SMALL LETTER U WITH OGONEK + return rune(0x0173), true + case "uopf": + // MATHEMATICAL DOUBLE-STRUCK SMALL U + return rune(0x01d566), true + case "uparrow": + // UPWARDS ARROW + return rune(0x2191), true + case "updownarrow": + // UP DOWN ARROW + return rune(0x2195), true + case "upharpoonleft": + // UPWARDS HARPOON WITH BARB LEFTWARDS + return rune(0x21bf), true + case "upharpoonright": + // UPWARDS HARPOON WITH BARB RIGHTWARDS + return rune(0x21be), true + case "upint": + // INTEGRAL WITH OVERBAR + return rune(0x2a1b), true + case "uplus": + // MULTISET UNION + return rune(0x228e), true + case "upsih": + // GREEK UPSILON WITH HOOK SYMBOL + return rune(0x03d2), true + case "upsilon": + // GREEK SMALL LETTER UPSILON + return rune(0x03c5), true + case "upsi": + // GREEK SMALL LETTER UPSILON + return rune(0x03c5), true + case "upuparrows": + // UPWARDS PAIRED ARROWS + return rune(0x21c8), true + case "urcorn": + // TOP RIGHT CORNER + return rune(0x231d), true + case "urcorner": + // TOP RIGHT CORNER + return rune(0x231d), true + case "urcrop": + // TOP RIGHT CROP + return rune(0x230e), true + case "urdrshar": + // UP BARB RIGHT DOWN BARB RIGHT HARPOON + return rune(0x294f), true + case "urharb": + // UPWARDS HARPOON WITH BARB RIGHT TO BAR + return rune(0x2954), true + case "uring": + // LATIN SMALL LETTER U WITH RING ABOVE + return rune(0x016f), true + case "urtrif": + // BLACK UPPER RIGHT TRIANGLE + return rune(0x25e5), true + case "urtri": + // UPPER RIGHT TRIANGLE + return rune(0x25f9), true + case "uscr": + // MATHEMATICAL SCRIPT SMALL U + return rune(0x01d4ca), true + case "utdot": + // UP RIGHT DIAGONAL ELLIPSIS + return rune(0x22f0), true + case "utilde": + // LATIN SMALL LETTER U WITH TILDE + return rune(0x0169), true + case "utrif": + // BLACK UP-POINTING SMALL TRIANGLE + return rune(0x25b4), true + case "utri": + // WHITE UP-POINTING SMALL TRIANGLE + return rune(0x25b5), true + case "uuarr": + // UPWARDS PAIRED ARROWS + return rune(0x21c8), true + case "uuml": + // LATIN SMALL LETTER U WITH DIAERESIS + return rune(0xfc), true + case "uwangle": + // OBLIQUE ANGLE OPENING DOWN + return rune(0x29a7), true + } + + case 'v': + switch name { + case "vArr": + // UP DOWN DOUBLE ARROW + return rune(0x21d5), true + case "vBar": + // SHORT UP TACK WITH UNDERBAR + return rune(0x2ae8), true + case "vBarv": + // SHORT UP TACK ABOVE SHORT DOWN TACK + return rune(0x2ae9), true + case "vDash": + // TRUE + return rune(0x22a8), true + case "vDdash": + // VERTICAL BAR TRIPLE RIGHT TURNSTILE + return rune(0x2ae2), true + case "vangrt": + // RIGHT ANGLE VARIANT WITH SQUARE + return rune(0x299c), true + case "varepsilon": + // GREEK LUNATE EPSILON SYMBOL + return rune(0x03f5), true + case "varkappa": + // GREEK KAPPA SYMBOL + return rune(0x03f0), true + case "varnothing": + // EMPTY SET + return rune(0x2205), true + case "varphi": + // GREEK PHI SYMBOL + return rune(0x03d5), true + case "varpi": + // GREEK PI SYMBOL + return rune(0x03d6), true + case "varpropto": + // PROPORTIONAL TO + return rune(0x221d), true + case "varr": + // UP DOWN ARROW + return rune(0x2195), true + case "varrho": + // GREEK RHO SYMBOL + return rune(0x03f1), true + case "varsigma": + // GREEK SMALL LETTER FINAL SIGMA + return rune(0x03c2), true + case "varsubsetneq": + // SUBSET OF WITH NOT EQUAL TO - variant with stroke through bottom members + return rune(0x228a), true + case "varsubsetneqq": + // SUBSET OF ABOVE NOT EQUAL TO - variant with stroke through bottom members + return rune(0x2acb), true + case "varsupsetneq": + // SUPERSET OF WITH NOT EQUAL TO - variant with stroke through bottom members + return rune(0x228b), true + case "varsupsetneqq": + // SUPERSET OF ABOVE NOT EQUAL TO - variant with stroke through bottom members + return rune(0x2acc), true + case "vartheta": + // GREEK THETA SYMBOL + return rune(0x03d1), true + case "vartriangleleft": + // NORMAL SUBGROUP OF + return rune(0x22b2), true + case "vartriangleright": + // CONTAINS AS NORMAL SUBGROUP + return rune(0x22b3), true + case "vbrtri": + // VERTICAL BAR BESIDE RIGHT TRIANGLE + return rune(0x29d0), true + case "vcy": + // CYRILLIC SMALL LETTER VE + return rune(0x0432), true + case "vdash": + // RIGHT TACK + return rune(0x22a2), true + case "vee": + // LOGICAL OR + return rune(0x2228), true + case "veeBar": + // LOGICAL OR WITH DOUBLE UNDERBAR + return rune(0x2a63), true + case "veebar": + // XOR + return rune(0x22bb), true + case "veeeq": + // EQUIANGULAR TO + return rune(0x225a), true + case "vellip": + // VERTICAL ELLIPSIS + return rune(0x22ee), true + case "vellip4": + // DOTTED FENCE + return rune(0x2999), true + case "vellipv": + // TRIPLE COLON OPERATOR + return rune(0x2af6), true + case "verbar": + // VERTICAL LINE + return rune(0x7c), true + case "vert3": + // TRIPLE VERTICAL BAR BINARY RELATION + return rune(0x2af4), true + case "vert": + // VERTICAL LINE + return rune(0x7c), true + case "vfr": + // MATHEMATICAL FRAKTUR SMALL V + return rune(0x01d533), true + case "vldash": + // LEFT SQUARE BRACKET LOWER CORNER + return rune(0x23a3), true + case "vltri": + // NORMAL SUBGROUP OF + return rune(0x22b2), true + case "vnsub": + // SUBSET OF with vertical line + return rune(0x2282), true + case "vnsup": + // SUPERSET OF with vertical line + return rune(0x2283), true + case "vopf": + // MATHEMATICAL DOUBLE-STRUCK SMALL V + return rune(0x01d567), true + case "vprime": + // PRIME + return rune(0x2032), true + case "vprop": + // PROPORTIONAL TO + return rune(0x221d), true + case "vrtri": + // CONTAINS AS NORMAL SUBGROUP + return rune(0x22b3), true + case "vscr": + // MATHEMATICAL SCRIPT SMALL V + return rune(0x01d4cb), true + case "vsubnE": + // SUBSET OF ABOVE NOT EQUAL TO - variant with stroke through bottom members + return rune(0x2acb), true + case "vsubne": + // SUBSET OF WITH NOT EQUAL TO - variant with stroke through bottom members + return rune(0x228a), true + case "vsupnE": + // SUPERSET OF ABOVE NOT EQUAL TO - variant with stroke through bottom members + return rune(0x2acc), true + case "vsupne": + // SUPERSET OF WITH NOT EQUAL TO - variant with stroke through bottom members + return rune(0x228b), true + case "vzigzag": + // VERTICAL ZIGZAG LINE + return rune(0x299a), true + } + + case 'w': + switch name { + case "wcirc": + // LATIN SMALL LETTER W WITH CIRCUMFLEX + return rune(0x0175), true + case "wedbar": + // LOGICAL AND WITH UNDERBAR + return rune(0x2a5f), true + case "wedge": + // LOGICAL AND + return rune(0x2227), true + case "wedgeq": + // ESTIMATES + return rune(0x2259), true + case "weierp": + // SCRIPT CAPITAL P + return rune(0x2118), true + case "wfr": + // MATHEMATICAL FRAKTUR SMALL W + return rune(0x01d534), true + case "wopf": + // MATHEMATICAL DOUBLE-STRUCK SMALL W + return rune(0x01d568), true + case "wp": + // SCRIPT CAPITAL P + return rune(0x2118), true + case "wreath": + // WREATH PRODUCT + return rune(0x2240), true + case "wr": + // WREATH PRODUCT + return rune(0x2240), true + case "wscr": + // MATHEMATICAL SCRIPT SMALL W + return rune(0x01d4cc), true + } + + case 'x': + switch name { + case "xandand": + // TWO LOGICAL AND OPERATOR + return rune(0x2a07), true + case "xbsol": + // BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT + return rune(0x2571), true + case "xcap": + // N-ARY INTERSECTION + return rune(0x22c2), true + case "xcirc": + // LARGE CIRCLE + return rune(0x25ef), true + case "xcup": + // N-ARY UNION + return rune(0x22c3), true + case "xcupdot": + // N-ARY UNION OPERATOR WITH DOT + return rune(0x2a03), true + case "xdtri": + // WHITE DOWN-POINTING TRIANGLE + return rune(0x25bd), true + case "xfr": + // MATHEMATICAL FRAKTUR SMALL X + return rune(0x01d535), true + case "xgr": + // GREEK SMALL LETTER XI + return rune(0x03be), true + case "xhArr": + // LONG LEFT RIGHT DOUBLE ARROW + return rune(0x27fa), true + case "xharr": + // LONG LEFT RIGHT ARROW + return rune(0x27f7), true + case "xi": + // GREEK SMALL LETTER XI + return rune(0x03be), true + case "xlArr": + // LONG LEFTWARDS DOUBLE ARROW + return rune(0x27f8), true + case "xlarr": + // LONG LEFTWARDS ARROW + return rune(0x27f5), true + case "xmap": + // LONG RIGHTWARDS ARROW FROM BAR + return rune(0x27fc), true + case "xnis": + // CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE + return rune(0x22fb), true + case "xodot": + // N-ARY CIRCLED DOT OPERATOR + return rune(0x2a00), true + case "xopf": + // MATHEMATICAL DOUBLE-STRUCK SMALL X + return rune(0x01d569), true + case "xoplus": + // N-ARY CIRCLED PLUS OPERATOR + return rune(0x2a01), true + case "xoror": + // TWO LOGICAL OR OPERATOR + return rune(0x2a08), true + case "xotime": + // N-ARY CIRCLED TIMES OPERATOR + return rune(0x2a02), true + case "xrArr": + // LONG RIGHTWARDS DOUBLE ARROW + return rune(0x27f9), true + case "xrarr": + // LONG RIGHTWARDS ARROW + return rune(0x27f6), true + case "xscr": + // MATHEMATICAL SCRIPT SMALL X + return rune(0x01d4cd), true + case "xsol": + // BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT + return rune(0x2572), true + case "xsqcap": + // N-ARY SQUARE INTERSECTION OPERATOR + return rune(0x2a05), true + case "xsqcup": + // N-ARY SQUARE UNION OPERATOR + return rune(0x2a06), true + case "xsqu": + // WHITE MEDIUM SQUARE + return rune(0x25fb), true + case "xsquf": + // BLACK MEDIUM SQUARE + return rune(0x25fc), true + case "xtimes": + // N-ARY TIMES OPERATOR + return rune(0x2a09), true + case "xuplus": + // N-ARY UNION OPERATOR WITH PLUS + return rune(0x2a04), true + case "xutri": + // WHITE UP-POINTING TRIANGLE + return rune(0x25b3), true + case "xvee": + // N-ARY LOGICAL OR + return rune(0x22c1), true + case "xwedge": + // N-ARY LOGICAL AND + return rune(0x22c0), true + } + + case 'y': + switch name { + case "yacute": + // LATIN SMALL LETTER Y WITH ACUTE + return rune(0xfd), true + case "yacy": + // CYRILLIC SMALL LETTER YA + return rune(0x044f), true + case "ycirc": + // LATIN SMALL LETTER Y WITH CIRCUMFLEX + return rune(0x0177), true + case "ycy": + // CYRILLIC SMALL LETTER YERU + return rune(0x044b), true + case "yen": + // YEN SIGN + return rune(0xa5), true + case "yfr": + // MATHEMATICAL FRAKTUR SMALL Y + return rune(0x01d536), true + case "yicy": + // CYRILLIC SMALL LETTER YI + return rune(0x0457), true + case "yopf": + // MATHEMATICAL DOUBLE-STRUCK SMALL Y + return rune(0x01d56a), true + case "yscr": + // MATHEMATICAL SCRIPT SMALL Y + return rune(0x01d4ce), true + case "yucy": + // CYRILLIC SMALL LETTER YU + return rune(0x044e), true + case "yuml": + // LATIN SMALL LETTER Y WITH DIAERESIS + return rune(0xff), true + } + + case 'z': + switch name { + case "zacute": + // LATIN SMALL LETTER Z WITH ACUTE + return rune(0x017a), true + case "zcaron": + // LATIN SMALL LETTER Z WITH CARON + return rune(0x017e), true + case "zcy": + // CYRILLIC SMALL LETTER ZE + return rune(0x0437), true + case "zdot": + // LATIN SMALL LETTER Z WITH DOT ABOVE + return rune(0x017c), true + case "zeetrf": + // BLACK-LETTER CAPITAL Z + return rune(0x2128), true + case "zeta": + // GREEK SMALL LETTER ZETA + return rune(0x03b6), true + case "zfr": + // MATHEMATICAL FRAKTUR SMALL Z + return rune(0x01d537), true + case "zgr": + // GREEK SMALL LETTER ZETA + return rune(0x03b6), true + case "zhcy": + // CYRILLIC SMALL LETTER ZHE + return rune(0x0436), true + case "zigrarr": + // RIGHTWARDS SQUIGGLE ARROW + return rune(0x21dd), true + case "zopf": + // MATHEMATICAL DOUBLE-STRUCK SMALL Z + return rune(0x01d56b), true + case "zscr": + // MATHEMATICAL SCRIPT SMALL Z + return rune(0x01d4cf), true + case "zwj": + // ZERO WIDTH JOINER + return rune(0x200d), true + case "zwnj": + // ZERO WIDTH NON-JOINER + return rune(0x200c), true + } + } + return -1, false +} + +/* + ------ GENERATED ------ DO NOT EDIT ------ GENERATED ------ DO NOT EDIT ------ GENERATED ------ +*/ diff --git a/core/encoding/hxa/read.odin b/core/encoding/hxa/read.odin index ef7edc8b7..abe295530 100644 --- a/core/encoding/hxa/read.odin +++ b/core/encoding/hxa/read.odin @@ -39,6 +39,9 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato read_value :: proc(r: ^Reader, $T: typeid) -> (value: T, err: Read_Error) { remaining := len(r.data) - r.offset if remaining < size_of(T) { + if r.print_error { + fmt.eprintf("file '%s' failed to read value at offset %v\n", r.filename, r.offset) + } err = .Short_Read return } @@ -51,6 +54,10 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato read_array :: proc(r: ^Reader, $T: typeid, count: int) -> (value: []T, err: Read_Error) { remaining := len(r.data) - r.offset if remaining < size_of(T)*count { + if r.print_error { + fmt.eprintf("file '%s' failed to read array of %d elements at offset %v\n", + r.filename, count, r.offset) + } err = .Short_Read return } @@ -82,7 +89,8 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato type := read_value(r, Meta_Value_Type) or_return if type > max(Meta_Value_Type) { if r.print_error { - fmt.eprintf("HxA Error: file '%s' has meta value type %d. Maximum value is ", r.filename, u8(type), u8(max(Meta_Value_Type))) + fmt.eprintf("HxA Error: file '%s' has meta value type %d. Maximum value is %d\n", + r.filename, u8(type), u8(max(Meta_Value_Type))) } err = .Invalid_Data return @@ -114,7 +122,8 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato type := read_value(r, Layer_Data_Type) or_return if type > max(type) { if r.print_error { - fmt.eprintf("HxA Error: file '%s' has layer data type %d. Maximum value is ", r.filename, u8(type), u8(max(Layer_Data_Type))) + fmt.eprintf("HxA Error: file '%s' has layer data type %d. Maximum value is %d\n", + r.filename, u8(type), u8(max(Layer_Data_Type))) } err = .Invalid_Data return @@ -134,13 +143,23 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato } if len(data) < size_of(Header) { + if print_error { + fmt.eprintf("HxA Error: file '%s' has no header\n", filename) + } + err = .Short_Read return } context.allocator = allocator header := cast(^Header)raw_data(data) - assert(header.magic_number == MAGIC_NUMBER) + if (header.magic_number != MAGIC_NUMBER) { + if print_error { + fmt.eprintf("HxA Error: file '%s' has invalid magic number 0x%x\n", filename, header.magic_number) + } + err = .Invalid_Data + return + } r := &Reader{ filename = filename, @@ -150,6 +169,7 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato } node_count := 0 + file.header = header^ file.nodes = make([]Node, header.internal_node_count) defer if err != nil { nodes_destroy(file.nodes) @@ -162,7 +182,8 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato type := read_value(r, Node_Type) or_return if type > max(Node_Type) { if r.print_error { - fmt.eprintf("HxA Error: file '%s' has node type %d. Maximum value is ", r.filename, u8(type), u8(max(Node_Type))) + fmt.eprintf("HxA Error: file '%s' has node type %d. Maximum value is %d\n", + r.filename, u8(type), u8(max(Node_Type))) } err = .Invalid_Data return diff --git a/core/encoding/hxa/write.odin b/core/encoding/hxa/write.odin index e774018b2..5bb950e81 100644 --- a/core/encoding/hxa/write.odin +++ b/core/encoding/hxa/write.odin @@ -84,7 +84,7 @@ write_internal :: proc(w: ^Writer, file: File) { write_metadata :: proc(w: ^Writer, meta_data: []Meta) { for m in meta_data { - name_len := max(len(m.name), 255) + name_len := min(len(m.name), 255) write_value(w, u8(name_len)) write_string(w, m.name[:name_len]) @@ -127,7 +127,7 @@ write_internal :: proc(w: ^Writer, file: File) { write_layer_stack :: proc(w: ^Writer, layers: Layer_Stack) { write_value(w, u32(len(layers))) for layer in layers { - name_len := max(len(layer.name), 255) + name_len := min(len(layer.name), 255) write_value(w, u8(name_len)) write_string(w, layer .name[:name_len]) @@ -152,7 +152,7 @@ write_internal :: proc(w: ^Writer, file: File) { return } - write_value(w, &Header{ + write_value(w, Header{ magic_number = MAGIC_NUMBER, version = LATEST_VERSION, internal_node_count = u32le(len(file.nodes)), diff --git a/core/encoding/json/marshal.odin b/core/encoding/json/marshal.odin index aa1c1559c..54fab44c6 100644 --- a/core/encoding/json/marshal.odin +++ b/core/encoding/json/marshal.odin @@ -8,18 +8,19 @@ import "core:strings" import "core:io" Marshal_Data_Error :: enum { + None, Unsupported_Type, } -Marshal_Error :: union { +Marshal_Error :: union #shared_nil { Marshal_Data_Error, io.Error, } marshal :: proc(v: any, allocator := context.allocator) -> (data: []byte, err: Marshal_Error) { - b := strings.make_builder(allocator) - defer if err != .None { - strings.destroy_builder(&b) + b := strings.builder_make(allocator) + defer if err != nil { + strings.builder_destroy(&b) } marshal_to_builder(&b, v) or_return @@ -27,7 +28,7 @@ marshal :: proc(v: any, allocator := context.allocator) -> (data: []byte, err: M if len(b.buf) != 0 { data = b.buf[:] } - return data, .None + return data, nil } marshal_to_builder :: proc(b: ^strings.Builder, v: any) -> Marshal_Error { @@ -48,7 +49,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) { unreachable() case runtime.Type_Info_Integer: - buf: [21]byte + buf: [40]byte u: u128 switch i in a { case i8: u = u128(i) diff --git a/core/encoding/json/parser.odin b/core/encoding/json/parser.odin index c682ec9bd..ed36ae33b 100644 --- a/core/encoding/json/parser.odin +++ b/core/encoding/json/parser.odin @@ -40,7 +40,7 @@ parse_string :: proc(data: string, spec := DEFAULT_SPECIFICATION, parse_integers return parse_object(&p) case .JSON5: return parse_value(&p) - case .MJSON: + case .SJSON: #partial switch p.curr_token.kind { case .Ident, .String: return parse_object_body(&p, .EOF) @@ -354,6 +354,12 @@ unquote_string :: proc(token: Token, spec: Specification, allocator := context.a b := bytes_make(len(s) + 2*utf8.UTF_MAX, 1, allocator) or_return w := copy(b, s[0:i]) + + if len(b) == 0 && allocator.data == nil { + // `unmarshal_count_array` calls us with a nil allocator + return string(b[:w]), nil + } + loop: for i < len(s) { c := s[i] switch { diff --git a/core/encoding/json/types.odin b/core/encoding/json/types.odin index 534d20311..468774aa9 100644 --- a/core/encoding/json/types.odin +++ b/core/encoding/json/types.odin @@ -33,8 +33,9 @@ package json Specification :: enum { JSON, JSON5, // https://json5.org/ - MJSON, // https://bitsquid.blogspot.com/2009/10/simplified-json-notation.html - Bitsquid = MJSON, + SJSON, // https://bitsquid.blogspot.com/2009/10/simplified-json-notation.html + Bitsquid = SJSON, + MJSON = SJSON, } diff --git a/core/encoding/json/unmarshal.odin b/core/encoding/json/unmarshal.odin index bd48011f1..97d2421d4 100644 --- a/core/encoding/json/unmarshal.odin +++ b/core/encoding/json/unmarshal.odin @@ -209,7 +209,7 @@ unmarshal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) { variant := u.variants[0] v.id = variant.id ti = reflect.type_info_base(variant) - if !(u.maybe && reflect.is_pointer(variant)) { + if !reflect.is_pointer_internally(variant) { tag := any{rawptr(uintptr(v.data) + u.tag_offset), u.tag_type.id} assign_int(tag, 1) } @@ -325,7 +325,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm UNSUPPORTED_TYPE := Unsupported_Type_Error{v.id, p.curr_token} if end_token == .Close_Brace { - assert(expect_token(p, .Open_Brace) == nil) + unmarshal_expect_token(p, .Open_Brace) } v := v @@ -473,7 +473,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm } if end_token == .Close_Brace { - assert(expect_token(p, .Close_Brace) == nil) + unmarshal_expect_token(p, .Close_Brace) } return } diff --git a/core/encoding/varint/doc.odin b/core/encoding/varint/doc.odin new file mode 100644 index 000000000..5e4708a59 --- /dev/null +++ b/core/encoding/varint/doc.odin @@ -0,0 +1,28 @@ +/* + Implementation of the LEB128 variable integer encoding as used by DWARF encoding and DEX files, among others. + + Author of this Odin package: Jeroen van Rijn + + Example: + ```odin + import "core:encoding/varint" + import "core:fmt" + + main :: proc() { + buf: [varint.LEB128_MAX_BYTES]u8 + + value := u128(42) + + encode_size, encode_err := varint.encode_uleb128(buf[:], value) + assert(encode_size == 1 && encode_err == .None) + + fmt.printf("Encoded as %v\n", buf[:encode_size]) + decoded_val, decode_size, decode_err := varint.decode_uleb128(buf[:]) + + assert(decoded_val == value && decode_size == encode_size && decode_err == .None) + fmt.printf("Decoded as %v, using %v byte%v\n", decoded_val, decode_size, "" if decode_size == 1 else "s") + } + ``` + +*/ +package varint \ No newline at end of file diff --git a/core/encoding/varint/leb128.odin b/core/encoding/varint/leb128.odin new file mode 100644 index 000000000..1cdbb81b0 --- /dev/null +++ b/core/encoding/varint/leb128.odin @@ -0,0 +1,163 @@ +/* + Copyright 2022 Jeroen van Rijn . + Made available under Odin's BSD-3 license. + + List of contributors: + Jeroen van Rijn: Initial implementation. +*/ + +// package varint implements variable length integer encoding and decoding using +// the LEB128 format as used by DWARF debug info, Android .dex and other file formats. +package varint + +// In theory we should use the bigint package. In practice, varints bigger than this indicate a corrupted file. +// Instead we'll set limits on the values we'll encode/decode +// 18 * 7 bits = 126, which means that a possible 19th byte may at most be `0b0000_0011`. +LEB128_MAX_BYTES :: 19 + +Error :: enum { + None = 0, + Buffer_Too_Small = 1, + Value_Too_Large = 2, +} + +// Decode a slice of bytes encoding an unsigned LEB128 integer into value and number of bytes used. +// Returns `size` == 0 for an invalid value, empty slice, or a varint > 18 bytes. +decode_uleb128_buffer :: proc(buf: []u8) -> (val: u128, size: int, err: Error) { + if len(buf) == 0 { + return 0, 0, .Buffer_Too_Small + } + + for v in buf { + val, size, err = decode_uleb128_byte(v, size, val) + if err != .Buffer_Too_Small { + return + } + } + + if err == .Buffer_Too_Small { + val, size = 0, 0 + } + return +} + +// Decodes an unsigned LEB128 integer into value a byte at a time. +// Returns `.None` when decoded properly, `.Value_Too_Large` when they value +// exceeds the limits of a u128, and `.Buffer_Too_Small` when it's not yet fully decoded. +decode_uleb128_byte :: proc(input: u8, offset: int, accumulator: u128) -> (val: u128, size: int, err: Error) { + size = offset + 1 + + // 18 * 7 bits = 126, which means that a possible 19th byte may at most be 0b0000_0011. + if size > LEB128_MAX_BYTES || size == LEB128_MAX_BYTES && input > 0b0000_0011 { + return 0, 0, .Value_Too_Large + } + + val = accumulator | u128(input & 0x7f) << uint(offset * 7) + + if input < 128 { + // We're done + return + } + + // If the buffer runs out before the number ends, return an error. + return val, size, .Buffer_Too_Small +} +decode_uleb128 :: proc {decode_uleb128_buffer, decode_uleb128_byte} + +// Decode a slice of bytes encoding a signed LEB128 integer into value and number of bytes used. +// Returns `size` == 0 for an invalid value, empty slice, or a varint > 18 bytes. +decode_ileb128_buffer :: proc(buf: []u8) -> (val: i128, size: int, err: Error) { + if len(buf) == 0 { + return 0, 0, .Buffer_Too_Small + } + + for v in buf { + val, size, err = decode_ileb128_byte(v, size, val) + if err != .Buffer_Too_Small { + return + } + } + + if err == .Buffer_Too_Small { + val, size = 0, 0 + } + return +} + +// Decode a a signed LEB128 integer into value and number of bytes used, one byte at a time. +// Returns `size` == 0 for an invalid value, empty slice, or a varint > 18 bytes. +decode_ileb128_byte :: proc(input: u8, offset: int, accumulator: i128) -> (val: i128, size: int, err: Error) { + size = offset + 1 + shift := uint(offset * 7) + + // 18 * 7 bits = 126, which including sign means we can have a 19th byte. + if size > LEB128_MAX_BYTES || size == LEB128_MAX_BYTES && input > 0x7f { + return 0, 0, .Value_Too_Large + } + + val = accumulator | i128(input & 0x7f) << shift + + if input < 128 { + if input & 0x40 == 0x40 { + val |= max(i128) << (shift + 7) + } + return val, size, .None + } + return val, size, .Buffer_Too_Small +} +decode_ileb128 :: proc{decode_ileb128_buffer, decode_ileb128_byte} + +// Encode `val` into `buf` as an unsigned LEB128 encoded series of bytes. +// `buf` must be appropriately sized. +encode_uleb128 :: proc(buf: []u8, val: u128) -> (size: int, err: Error) { + val := val + + for { + size += 1 + + if size > len(buf) { + return 0, .Buffer_Too_Small + } + + low := val & 0x7f + val >>= 7 + + if val > 0 { + low |= 0x80 // more bytes to follow + } + buf[size - 1] = u8(low) + + if val == 0 { break } + } + return +} + +// Encode `val` into `buf` as a signed LEB128 encoded series of bytes. +// `buf` must be appropriately sized. +encode_ileb128 :: proc(buf: []u8, val: i128) -> (size: int, err: Error) { + SIGN_MASK :: i128(1) << 121 // sign extend mask + + val, more := val, true + + for more { + size += 1 + + if size > len(buf) { + return 0, .Buffer_Too_Small + } + + low := val & 0x7f + val >>= 7 + + low = (low ~ SIGN_MASK) - SIGN_MASK + + if (val == 0 && low & 0x40 != 0x40) || (val == -1 && low & 0x40 == 0x40) { + more = false + } else { + low |= 0x80 + } + + buf[size - 1] = u8(low) + } + return +} \ No newline at end of file diff --git a/core/encoding/xml/debug_print.odin b/core/encoding/xml/debug_print.odin new file mode 100644 index 000000000..e9a1cb160 --- /dev/null +++ b/core/encoding/xml/debug_print.odin @@ -0,0 +1,86 @@ +/* + An XML 1.0 / 1.1 parser + + Copyright 2021-2022 Jeroen van Rijn . + Made available under Odin's BSD-3 license. + + A from-scratch XML implementation, loosely modeled on the [spec](https://www.w3.org/TR/2006/REC-xml11-20060816). + + List of contributors: + Jeroen van Rijn: Initial implementation. +*/ +package xml + +import "core:io" +import "core:fmt" + +/* + Just for debug purposes. +*/ +print :: proc(writer: io.Writer, doc: ^Document) -> (written: int, err: io.Error) { + if doc == nil { return } + using fmt + + written += wprintf(writer, "[XML Prolog]\n") + + for attr in doc.prologue { + written += wprintf(writer, "\t%v: %v\n", attr.key, attr.val) + } + + written += wprintf(writer, "[Encoding] %v\n", doc.encoding) + + if len(doc.doctype.ident) > 0 { + written += wprintf(writer, "[DOCTYPE] %v\n", doc.doctype.ident) + + if len(doc.doctype.rest) > 0 { + wprintf(writer, "\t%v\n", doc.doctype.rest) + } + } + + for comment in doc.comments { + written += wprintf(writer, "[Pre-root comment] %v\n", comment) + } + + if len(doc.elements) > 0 { + wprintln(writer, " --- ") + print_element(writer, doc, 0) + wprintln(writer, " --- ") + } + + return written, .None +} + +print_element :: proc(writer: io.Writer, doc: ^Document, element_id: Element_ID, indent := 0) -> (written: int, err: io.Error) { + using fmt + + tab :: proc(writer: io.Writer, indent: int) { + for _ in 0..=indent { + wprintf(writer, "\t") + } + } + + tab(writer, indent) + + element := doc.elements[element_id] + + if element.kind == .Element { + wprintf(writer, "<%v>\n", element.ident) + if len(element.value) > 0 { + tab(writer, indent + 1) + wprintf(writer, "[Value] %v\n", element.value) + } + + for attr in element.attribs { + tab(writer, indent + 1) + wprintf(writer, "[Attr] %v: %v\n", attr.key, attr.val) + } + + for child in element.children { + print_element(writer, doc, child, indent + 1) + } + } else if element.kind == .Comment { + wprintf(writer, "[COMMENT] %v\n", element.value) + } + + return written, .None +} \ No newline at end of file diff --git a/core/encoding/xml/example/xml_example.odin b/core/encoding/xml/example/xml_example.odin new file mode 100644 index 000000000..887b40764 --- /dev/null +++ b/core/encoding/xml/example/xml_example.odin @@ -0,0 +1,112 @@ +package xml_example + +import "core:encoding/xml" +import "core:mem" +import "core:fmt" +import "core:time" +import "core:strings" +import "core:hash" + +N :: 1 + +example :: proc() { + using fmt + + docs: [N]^xml.Document + errs: [N]xml.Error + times: [N]time.Duration + + defer for round in 0..` tag.") + return + } + + printf("Found `` with %v children, %v elements total\n", len(docs[0].elements[charlist].children), docs[0].element_count) + + crc32 := doc_hash(docs[0]) + printf("[%v] CRC32: 0x%08x\n", "🎉" if crc32 == 0xcaa042b9 else "🤬", crc32) + + for round in 0.. (crc32: u32) { + buf: strings.Builder + defer strings.builder_destroy(&buf) + w := strings.to_writer(&buf) + + xml.print(w, doc) + tree := strings.to_string(buf) + if print { fmt.println(tree) } + return hash.crc32(transmute([]u8)tree) +} + +main :: proc() { + using fmt + + track: mem.Tracking_Allocator + mem.tracking_allocator_init(&track, context.allocator) + context.allocator = mem.tracking_allocator(&track) + + example() + + if len(track.allocation_map) > 0 { + println() + for _, v in track.allocation_map { + printf("%v Leaked %v bytes.\n", v.location, v.size) + } + } + println("Done and cleaned up!") +} \ No newline at end of file diff --git a/core/encoding/xml/helpers.odin b/core/encoding/xml/helpers.odin new file mode 100644 index 000000000..48f058334 --- /dev/null +++ b/core/encoding/xml/helpers.odin @@ -0,0 +1,45 @@ +/* + An XML 1.0 / 1.1 parser + + Copyright 2021-2022 Jeroen van Rijn . + Made available under Odin's BSD-3 license. + + This file contains helper functions. +*/ +package xml + +// Find parent's nth child with a given ident. +find_child_by_ident :: proc(doc: ^Document, parent_id: Element_ID, ident: string, nth := 0) -> (res: Element_ID, found: bool) { + tag := doc.elements[parent_id] + + count := 0 + for child_id in tag.children { + child := doc.elements[child_id] + /* + Skip commments. They have no name. + */ + if child.kind != .Element { continue } + + /* + If the ident matches and it's the nth such child, return it. + */ + if child.ident == ident { + if count == nth { return child_id, true } + count += 1 + } + } + return 0, false +} + +// Find an attribute by key. +find_attribute_val_by_key :: proc(doc: ^Document, parent_id: Element_ID, key: string) -> (val: string, found: bool) { + tag := doc.elements[parent_id] + + for attr in tag.attribs { + /* + If the ident matches, we're done. There can only ever be one attribute with the same name. + */ + if attr.key == key { return attr.val, true } + } + return "", false +} \ No newline at end of file diff --git a/core/encoding/xml/tokenizer.odin b/core/encoding/xml/tokenizer.odin new file mode 100644 index 000000000..d225c5d90 --- /dev/null +++ b/core/encoding/xml/tokenizer.odin @@ -0,0 +1,436 @@ +/* + An XML 1.0 / 1.1 parser + + Copyright 2021-2022 Jeroen van Rijn . + Made available under Odin's BSD-3 license. + + A from-scratch XML implementation, loosely modeled on the [spec](https://www.w3.org/TR/2006/REC-xml11-20060816). + + List of contributors: + Jeroen van Rijn: Initial implementation. +*/ +package xml + +import "core:fmt" +import "core:unicode" +import "core:unicode/utf8" + +Error_Handler :: #type proc(pos: Pos, fmt: string, args: ..any) + +Token :: struct { + kind: Token_Kind, + text: string, + pos: Pos, +} + +Pos :: struct { + file: string, + offset: int, // starting at 0 + line: int, // starting at 1 + column: int, // starting at 1 +} + +Token_Kind :: enum { + Invalid, + + Ident, + Literal, + Rune, + String, + + Double_Quote, // " + Single_Quote, // ' + Colon, // : + + Eq, // = + Lt, // < + Gt, // > + Exclaim, // ! + Question, // ? + Hash, // # + Slash, // / + Dash, // - + + Open_Bracket, // [ + Close_Bracket, // ] + + EOF, +} + +CDATA_START :: "" + +COMMENT_START :: "" + +Tokenizer :: struct { + // Immutable data + path: string, + src: string, + err: Error_Handler, + + // Tokenizing state + ch: rune, + offset: int, + read_offset: int, + line_offset: int, + line_count: int, + + // Mutable data + error_count: int, +} + +init :: proc(t: ^Tokenizer, src: string, path: string, err: Error_Handler = default_error_handler) { + t.src = src + t.err = err + t.ch = ' ' + t.offset = 0 + t.read_offset = 0 + t.line_offset = 0 + t.line_count = len(src) > 0 ? 1 : 0 + t.error_count = 0 + t.path = path + + advance_rune(t) + if t.ch == utf8.RUNE_BOM { + advance_rune(t) + } +} + +@(private) +offset_to_pos :: proc(t: ^Tokenizer, offset: int) -> Pos { + line := t.line_count + column := offset - t.line_offset + 1 + + return Pos { + file = t.path, + offset = offset, + line = line, + column = column, + } +} + +default_error_handler :: proc(pos: Pos, msg: string, args: ..any) { + fmt.eprintf("%s(%d:%d) ", pos.file, pos.line, pos.column) + fmt.eprintf(msg, ..args) + fmt.eprintf("\n") +} + +error :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) { + pos := offset_to_pos(t, offset) + if t.err != nil { + t.err(pos, msg, ..args) + } + t.error_count += 1 +} + +@(optimization_mode="speed") +advance_rune :: proc(using t: ^Tokenizer) { + #no_bounds_check { + /* + Already bounds-checked here. + */ + if read_offset < len(src) { + offset = read_offset + if ch == '\n' { + line_offset = offset + line_count += 1 + } + r, w := rune(src[read_offset]), 1 + switch { + case r == 0: + error(t, t.offset, "illegal character NUL") + case r >= utf8.RUNE_SELF: + r, w = #force_inline utf8.decode_rune_in_string(src[read_offset:]) + if r == utf8.RUNE_ERROR && w == 1 { + error(t, t.offset, "illegal UTF-8 encoding") + } else if r == utf8.RUNE_BOM && offset > 0 { + error(t, t.offset, "illegal byte order mark") + } + } + read_offset += w + ch = r + } else { + offset = len(src) + if ch == '\n' { + line_offset = offset + line_count += 1 + } + ch = -1 + } + } +} + +peek_byte :: proc(t: ^Tokenizer, offset := 0) -> byte { + if t.read_offset+offset < len(t.src) { + #no_bounds_check return t.src[t.read_offset+offset] + } + return 0 +} + +@(optimization_mode="speed") +skip_whitespace :: proc(t: ^Tokenizer) { + for { + switch t.ch { + case ' ', '\t', '\r', '\n': + advance_rune(t) + case: + return + } + } +} + +@(optimization_mode="speed") +is_letter :: proc(r: rune) -> bool { + if r < utf8.RUNE_SELF { + switch r { + case '_': + return true + case 'A'..='Z', 'a'..='z': + return true + } + } + return unicode.is_letter(r) +} + +is_valid_identifier_rune :: proc(r: rune) -> bool { + if r < utf8.RUNE_SELF { + switch r { + case '_', '-', ':': return true + case 'A'..='Z', 'a'..='z': return true + case '0'..='9': return true + case -1: return false + } + } + + if unicode.is_letter(r) || unicode.is_digit(r) { + return true + } + return false +} + +scan_identifier :: proc(t: ^Tokenizer) -> string { + offset := t.offset + namespaced := false + + for is_valid_identifier_rune(t.ch) { + advance_rune(t) + if t.ch == ':' { + /* + A namespaced attr can have at most two parts, `namespace:ident`. + */ + if namespaced { + break + } + namespaced = true + } + } + return string(t.src[offset : t.offset]) +} + +/* + A comment ends when we see -->, preceded by a character that's not a dash. + "For compatibility, the string "--" (double-hyphen) must not occur within comments." + + See: https://www.w3.org/TR/2006/REC-xml11-20060816/#dt-comment + + Thanks to the length (4) of the comment start, we also have enough lookback, + and the peek at the next byte asserts that there's at least one more character + that's a `>`. +*/ +scan_comment :: proc(t: ^Tokenizer) -> (comment: string, err: Error) { + offset := t.offset + + for { + advance_rune(t) + ch := t.ch + + if ch < 0 { + error(t, offset, "[parse] Comment was not terminated\n") + return "", .Unclosed_Comment + } + + if string(t.src[t.offset - 1:][:2]) == "--" { + if peek_byte(t) == '>' { + break + } else { + error(t, t.offset - 1, "Invalid -- sequence in comment.\n") + return "", .Invalid_Sequence_In_Comment + } + } + } + + expect(t, .Dash) + expect(t, .Gt) + + return string(t.src[offset : t.offset - 1]), .None +} + +/* + Skip CDATA +*/ +skip_cdata :: proc(t: ^Tokenizer) -> (err: Error) { + if t.read_offset + len(CDATA_START) >= len(t.src) { + /* + Can't be the start of a CDATA tag. + */ + return .None + } + + if string(t.src[t.offset:][:len(CDATA_START)]) == CDATA_START { + t.read_offset += len(CDATA_START) + offset := t.offset + + cdata_scan: for { + advance_rune(t) + if t.ch < 0 { + error(t, offset, "[scan_string] CDATA was not terminated\n") + return .Premature_EOF + } + + /* + Scan until the end of a CDATA tag. + */ + if t.read_offset + len(CDATA_END) < len(t.src) { + if string(t.src[t.offset:][:len(CDATA_END)]) == CDATA_END { + t.read_offset += len(CDATA_END) + break cdata_scan + } + } + } + } + return +} + +@(optimization_mode="speed") +scan_string :: proc(t: ^Tokenizer, offset: int, close: rune = '<', consume_close := false, multiline := true) -> (value: string, err: Error) { + err = .None + + loop: for { + ch := t.ch + + switch ch { + case -1: + error(t, t.offset, "[scan_string] Premature end of file.\n") + return "", .Premature_EOF + + case '<': + if peek_byte(t) == '!' { + if peek_byte(t, 1) == '[' { + /* + Might be the start of a CDATA tag. + */ + skip_cdata(t) or_return + } else if peek_byte(t, 1) == '-' && peek_byte(t, 2) == '-' { + /* + Comment start. Eat comment. + */ + t.read_offset += 3 + _ = scan_comment(t) or_return + } + } + + case '\n': + if !multiline { + error(t, offset, string(t.src[offset : t.offset])) + error(t, offset, "[scan_string] Not terminated\n") + err = .Invalid_Tag_Value + break loop + } + } + + if t.ch == close { + /* + If it's not a CDATA or comment, it's the end of this body. + */ + break loop + } + advance_rune(t) + } + + /* + Strip trailing whitespace. + */ + lit := string(t.src[offset : t.offset]) + + end := len(lit) + eat: for ; end > 0; end -= 1 { + ch := lit[end - 1] + switch ch { + case ' ', '\t', '\r', '\n': + case: + break eat + } + } + lit = lit[:end] + + if consume_close { + advance_rune(t) + } + + /* + TODO: Handle decoding escape characters and unboxing CDATA. + */ + + return lit, err +} + +peek :: proc(t: ^Tokenizer) -> (token: Token) { + old := t^ + token = scan(t) + t^ = old + return token +} + +scan :: proc(t: ^Tokenizer) -> Token { + skip_whitespace(t) + + offset := t.offset + + kind: Token_Kind + err: Error + lit: string + pos := offset_to_pos(t, offset) + + switch ch := t.ch; true { + case is_letter(ch): + lit = scan_identifier(t) + kind = .Ident + + case: + advance_rune(t) + switch ch { + case -1: + kind = .EOF + + case '<': kind = .Lt + case '>': kind = .Gt + case '!': kind = .Exclaim + case '?': kind = .Question + case '=': kind = .Eq + case '#': kind = .Hash + case '/': kind = .Slash + case '-': kind = .Dash + case ':': kind = .Colon + + case '"', '\'': + kind = .Invalid + + lit, err = scan_string(t, t.offset, ch, true, false) + if err == .None { + kind = .String + } + + case '\n': + lit = "\n" + + case: + kind = .Invalid + } + } + + if kind != .String && lit == "" { + lit = string(t.src[offset : t.offset]) + } + return Token{kind, lit, pos} +} \ No newline at end of file diff --git a/core/encoding/xml/xml_reader.odin b/core/encoding/xml/xml_reader.odin new file mode 100644 index 000000000..b77ae97b3 --- /dev/null +++ b/core/encoding/xml/xml_reader.odin @@ -0,0 +1,713 @@ +/* + An XML 1.0 / 1.1 parser + + Copyright 2021-2022 Jeroen van Rijn . + Made available under Odin's BSD-3 license. + + A from-scratch XML implementation, loosely modelled on the [spec](https://www.w3.org/TR/2006/REC-xml11-20060816). + + Features: + - Supports enough of the XML 1.0/1.1 spec to handle the 99.9% of XML documents in common current usage. + - Simple to understand and use. Small. + + Caveats: + - We do NOT support HTML in this package, as that may or may not be valid XML. + If it works, great. If it doesn't, that's not considered a bug. + + - We do NOT support UTF-16. If you have a UTF-16 XML file, please convert it to UTF-8 first. Also, our condolences. + - <[!ELEMENT and <[!ATTLIST are not supported, and will be either ignored or return an error depending on the parser options. + + MAYBE: + - XML writer? + - Serialize/deserialize Odin types? + + List of contributors: + Jeroen van Rijn: Initial implementation. +*/ +package xml +// An XML 1.0 / 1.1 parser + +import "core:bytes" +import "core:encoding/entity" +import "core:intrinsics" +import "core:mem" +import "core:os" +import "core:strings" + +likely :: intrinsics.expect + +DEFAULT_OPTIONS :: Options{ + flags = {.Ignore_Unsupported}, + expected_doctype = "", +} + +Option_Flag :: enum { + /* + If the caller says that input may be modified, we can perform in-situ parsing. + If this flag isn't provided, the XML parser first duplicates the input so that it can. + */ + Input_May_Be_Modified, + + /* + Document MUST start with ` (doc: ^Document, err: Error) { + data := data + context.allocator = allocator + + opts := validate_options(options) or_return + + /* + If `.Input_May_Be_Modified` is not specified, we duplicate the input so that we can modify it in-place. + */ + if .Input_May_Be_Modified not_in opts.flags { + data = bytes.clone(data) + } + + t := &Tokenizer{} + init(t, string(data), path, error_handler) + + doc = new(Document) + doc.allocator = allocator + doc.tokenizer = t + doc.input = data + + doc.elements = make([dynamic]Element, 1024, 1024, allocator) + + // strings.intern_init(&doc.intern, allocator, allocator) + + err = .Unexpected_Token + element, parent: Element_ID + + tag_is_open := false + first_element := true + open: Token + + /* + If a DOCTYPE is present, the root tag has to match. + If an expected DOCTYPE is given in options (i.e. it's non-empty), the DOCTYPE (if present) and root tag have to match. + */ + expected_doctype := options.expected_doctype + + loop: for { + skip_whitespace(t) + // NOTE(Jeroen): This is faster as a switch. + switch t.ch { + case '<': + /* + Consume peeked `<` + */ + advance_rune(t) + + open = scan(t) + // NOTE(Jeroen): We're not using a switch because this if-else chain ordered by likelihood is 2.5% faster at -o:size and -o:speed. + if likely(open.kind, Token_Kind.Ident) == .Ident { + /* + e.g. 0 && expected_doctype != open.text { + error(t, t.offset, "Root Tag doesn't match DOCTYPE. Expected: %v, got: %v\n", expected_doctype, open.text) + return doc, .Invalid_DocType + } + } + + /* + One of these should follow: + - `>`, which means we've just opened this tag and expect a later element to close it. + - `/>`, which means this is an 'empty' or self-closing tag. + */ + end_token := scan(t) + #partial switch end_token.kind { + case .Gt: + /* + We're now the new parent. + */ + parent = element + + case .Slash: + /* + Empty tag. Close it. + */ + expect(t, .Gt) or_return + parent = doc.elements[element].parent + element = parent + tag_is_open = false + + case: + error(t, t.offset, "Expected close tag, got: %#v\n", end_token) + return + } + + } else if open.kind == .Slash { + /* + Close tag. + */ + ident := expect(t, .Ident) or_return + _ = expect(t, .Gt) or_return + + if doc.elements[element].ident != ident.text { + error(t, t.offset, "Mismatched Closing Tag. Expected %v, got %v\n", doc.elements[element].ident, ident.text) + return doc, .Mismatched_Closing_Tag + } + parent = doc.elements[element].parent + element = parent + tag_is_open = false + + } else if open.kind == .Exclaim { + /* + 0 { + return doc, .Too_Many_DocTypes + } + if doc.element_count > 0 { + return doc, .DocType_Must_Preceed_Elements + } + parse_doctype(doc) or_return + + if len(expected_doctype) > 0 && expected_doctype != doc.doctype.ident { + error(t, t.offset, "Invalid DOCTYPE. Expected: %v, got: %v\n", expected_doctype, doc.doctype.ident) + return doc, .Invalid_DocType + } + expected_doctype = doc.doctype.ident + + case: + if .Error_on_Unsupported in opts.flags { + error(t, t.offset, "Unhandled: . + The grammar does not allow a comment to end in ---> + */ + expect(t, .Dash) + comment := scan_comment(t) or_return + + if .Intern_Comments in opts.flags { + if len(doc.elements) == 0 { + append(&doc.comments, comment) + } else { + el := new_element(doc) + doc.elements[el].parent = element + doc.elements[el].kind = .Comment + doc.elements[el].value = comment + append(&doc.elements[element].children, el) + } + } + + case: + error(t, t.offset, "Invalid Token after 0 { + /* + We've already seen a prologue. + */ + return doc, .Too_Many_Prologs + } else { + /* + Could be ` (doc: ^Document, err: Error) { + _data := transmute([]u8)data + + return parse_bytes(_data, options, path, error_handler, allocator) +} + +parse :: proc { parse_string, parse_bytes } + +// Load an XML file +load_from_file :: proc(filename: string, options := DEFAULT_OPTIONS, error_handler := default_error_handler, allocator := context.allocator) -> (doc: ^Document, err: Error) { + context.allocator = allocator + options := options + + data, data_ok := os.read_entire_file(filename) + if !data_ok { return {}, .File_Error } + + options.flags += { .Input_May_Be_Modified } + + return parse_bytes(data, options, filename, error_handler, allocator) +} + +destroy :: proc(doc: ^Document) { + if doc == nil { return } + + for el in doc.elements { + delete(el.attribs) + delete(el.children) + } + delete(doc.elements) + + delete(doc.prologue) + delete(doc.comments) + delete(doc.input) + + for s in doc.strings_to_free { + delete(s) + } + delete(doc.strings_to_free) + + free(doc) +} + +/* + Helpers. +*/ + +validate_options :: proc(options: Options) -> (validated: Options, err: Error) { + validated = options + + if .Error_on_Unsupported in validated.flags && .Ignore_Unsupported in validated.flags { + return options, .Conflicting_Options + } + return validated, .None +} + +expect :: proc(t: ^Tokenizer, kind: Token_Kind) -> (tok: Token, err: Error) { + tok = scan(t) + if tok.kind == kind { return tok, .None } + + error(t, t.offset, "Expected \"%v\", got \"%v\".", kind, tok.kind) + return tok, .Unexpected_Token +} + +parse_attribute :: proc(doc: ^Document) -> (attr: Attribute, offset: int, err: Error) { + assert(doc != nil) + context.allocator = doc.allocator + t := doc.tokenizer + + key := expect(t, .Ident) or_return + offset = t.offset - len(key.text) + + _ = expect(t, .Eq) or_return + value := expect(t, .String) or_return + + attr.key = key.text + attr.val = value.text + + err = .None + return +} + +check_duplicate_attributes :: proc(t: ^Tokenizer, attribs: Attributes, attr: Attribute, offset: int) -> (err: Error) { + for a in attribs { + if attr.key == a.key { + error(t, offset, "Duplicate attribute: %v\n", attr.key) + return .Duplicate_Attribute + } + } + return .None +} + +parse_attributes :: proc(doc: ^Document, attribs: ^Attributes) -> (err: Error) { + assert(doc != nil) + context.allocator = doc.allocator + t := doc.tokenizer + + for peek(t).kind == .Ident { + attr, offset := parse_attribute(doc) or_return + check_duplicate_attributes(t, attribs^, attr, offset) or_return + append(attribs, attr) + } + skip_whitespace(t) + return .None +} + +parse_prologue :: proc(doc: ^Document) -> (err: Error) { + assert(doc != nil) + context.allocator = doc.allocator + t := doc.tokenizer + + offset := t.offset + parse_attributes(doc, &doc.prologue) or_return + + for attr in doc.prologue { + switch attr.key { + case "version": + switch attr.val { + case "1.0", "1.1": + case: + error(t, offset, "[parse_prologue] Warning: Unhandled XML version: %v\n", attr.val) + } + + case "encoding": + switch strings.to_lower(attr.val, context.temp_allocator) { + case "utf-8", "utf8": + doc.encoding = .UTF_8 + + case "latin-1", "latin1", "iso-8859-1": + doc.encoding = .LATIN_1 + + case: + /* + Unrecognized encoding, assume UTF-8. + */ + error(t, offset, "[parse_prologue] Warning: Unrecognized encoding: %v\n", attr.val) + } + + case: + // Ignored. + } + } + + _ = expect(t, .Question) or_return + _ = expect(t, .Gt) or_return + + return .None +} + +skip_element :: proc(t: ^Tokenizer) -> (err: Error) { + close := 1 + + loop: for { + tok := scan(t) + #partial switch tok.kind { + case .EOF: + error(t, t.offset, "[skip_element] Premature EOF\n") + return .Premature_EOF + + case .Lt: + close += 1 + + case .Gt: + close -= 1 + if close == 0 { + break loop + } + + case: + + } + } + return .None +} + +parse_doctype :: proc(doc: ^Document) -> (err: Error) { + /* + + + + ]> + */ + assert(doc != nil) + context.allocator = doc.allocator + t := doc.tokenizer + + tok := expect(t, .Ident) or_return + doc.doctype.ident = tok.text + + skip_whitespace(t) + offset := t.offset + skip_element(t) or_return + + /* + -1 because the current offset is that of the closing tag, so the rest of the DOCTYPE tag ends just before it. + */ + doc.doctype.rest = string(t.src[offset : t.offset - 1]) + return .None +} + +Element_ID :: u32 + +new_element :: proc(doc: ^Document) -> (id: Element_ID) { + element_space := len(doc.elements) + + // Need to resize + if int(doc.element_count) + 1 > element_space { + if element_space < 65536 { + element_space *= 2 + } else { + element_space += 65536 + } + resize(&doc.elements, element_space) + } + + cur := doc.element_count + doc.element_count += 1 + + return cur +} \ No newline at end of file diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 2cc192c12..4db140afa 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -11,6 +11,7 @@ import "core:time" import "core:unicode/utf8" import "core:intrinsics" +// Internal data structure that stores the required information for formatted printing Info :: struct { minus: bool, plus: bool, @@ -27,10 +28,17 @@ Info :: struct { reordered: bool, good_arg_index: bool, ignore_user_formatters: bool, + in_bad: bool, writer: io.Writer, arg: any, // Temporary + indirection_level: int, record_level: int, + + optional_len: Maybe(int), + use_nul_termination: bool, + + n: int, // bytes written } // Custom formatter signature. It returns true if the formatting was successful and false when it could not be done @@ -46,9 +54,13 @@ Register_User_Formatter_Error :: enum { // it is prefixed with `_` rather than marked with a private attribute so that users can access it if necessary _user_formatters: ^map[typeid]User_Formatter +// set_user_formatters assigns m to a global value allowing the user have custom print formatting for specific +// types set_user_formatters :: proc(m: ^map[typeid]User_Formatter) { _user_formatters = m } +// register_user_formatter assigns a formatter to a specific typeid. set_user_formatters must be called +// before any use of this procedure. register_user_formatter :: proc(id: typeid, formatter: User_Formatter) -> Register_User_Formatter_Error { if _user_formatters == nil { return .No_User_Formatter @@ -61,64 +73,73 @@ register_user_formatter :: proc(id: typeid, formatter: User_Formatter) -> Regist } -// aprint* procedures return a string that was allocated with the current context +// aprint procedure return a string that was allocated with the current context // They must be freed accordingly aprint :: proc(args: ..any, sep := " ") -> string { str: strings.Builder - strings.init_builder(&str) + strings.builder_init(&str) sbprint(buf=&str, args=args, sep=sep) return strings.to_string(str) } +// aprintln procedure return a string that was allocated with the current context +// They must be freed accordingly aprintln :: proc(args: ..any, sep := " ") -> string { str: strings.Builder - strings.init_builder(&str) + strings.builder_init(&str) sbprintln(buf=&str, args=args, sep=sep) return strings.to_string(str) } +// aprintf procedure return a string that was allocated with the current context +// They must be freed accordingly aprintf :: proc(fmt: string, args: ..any) -> string { str: strings.Builder - strings.init_builder(&str) + strings.builder_init(&str) sbprintf(&str, fmt, ..args) return strings.to_string(str) } -// tprint* procedures return a string that was allocated with the current context's temporary allocator +// tprint procedure return a string that was allocated with the current context's temporary allocator tprint :: proc(args: ..any, sep := " ") -> string { str: strings.Builder - strings.init_builder(&str, context.temp_allocator) + strings.builder_init(&str, context.temp_allocator) sbprint(buf=&str, args=args, sep=sep) return strings.to_string(str) } +// tprintln procedure return a string that was allocated with the current context's temporary allocator tprintln :: proc(args: ..any, sep := " ") -> string { str: strings.Builder - strings.init_builder(&str, context.temp_allocator) + strings.builder_init(&str, context.temp_allocator) sbprintln(buf=&str, args=args, sep=sep) return strings.to_string(str) } +// tprintf procedure return a string that was allocated with the current context's temporary allocator tprintf :: proc(fmt: string, args: ..any) -> string { str: strings.Builder - strings.init_builder(&str, context.temp_allocator) + strings.builder_init(&str, context.temp_allocator) sbprintf(&str, fmt, ..args) return strings.to_string(str) } -// bprint* procedures return a string using a buffer from an array +// bprint procedures return a string using a buffer from an array bprint :: proc(buf: []byte, args: ..any, sep := " ") -> string { - sb := strings.builder_from_slice(buf[0:len(buf)]) + sb := strings.builder_from_bytes(buf[0:len(buf)]) return sbprint(buf=&sb, args=args, sep=sep) } +// bprintln procedures return a string using a buffer from an array bprintln :: proc(buf: []byte, args: ..any, sep := " ") -> string { - sb := strings.builder_from_slice(buf[0:len(buf)]) + sb := strings.builder_from_bytes(buf[0:len(buf)]) return sbprintln(buf=&sb, args=args, sep=sep) } +// bprintf procedures return a string using a buffer from an array bprintf :: proc(buf: []byte, fmt: string, args: ..any) -> string { - sb := strings.builder_from_slice(buf[0:len(buf)]) + sb := strings.builder_from_bytes(buf[0:len(buf)]) return sbprintf(&sb, fmt, ..args) } +// formatted assert assertf :: proc(condition: bool, fmt: string, args: ..any, loc := #caller_location) -> bool { if !condition { p := context.assertion_failure_proc @@ -131,6 +152,7 @@ assertf :: proc(condition: bool, fmt: string, args: ..any, loc := #caller_locati return condition } +// formatted panic panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! { p := context.assertion_failure_proc if p == nil { @@ -142,24 +164,26 @@ panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! { - - +// sbprint formats using the default print settings and writes to buf sbprint :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string { wprint(w=strings.to_writer(buf), args=args, sep=sep) return strings.to_string(buf^) } +// sbprintln formats using the default print settings and writes to buf sbprintln :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string { wprintln(w=strings.to_writer(buf), args=args, sep=sep) return strings.to_string(buf^) } +// sbprintf formats according to the specififed format string and writes to buf sbprintf :: proc(buf: ^strings.Builder, fmt: string, args: ..any) -> string { wprintf(w=strings.to_writer(buf), fmt=fmt, args=args) return strings.to_string(buf^) } +// wprint formats using the default print settings and writes to w wprint :: proc(w: io.Writer, args: ..any, sep := " ") -> int { fi: Info fi.writer = w @@ -179,49 +203,42 @@ wprint :: proc(w: io.Writer, args: ..any, sep := " ") -> int { // so I am going to keep the same behaviour as `*println` for `*print` - size0 := io.size(auto_cast w) - for _, i in args { if i > 0 { - io.write_string(fi.writer, sep) + io.write_string(fi.writer, sep, &fi.n) } fmt_value(&fi, args[i], 'v') } io.flush(auto_cast w) - size1 := io.size(auto_cast w) - return int(size1 - size0) + return fi.n } +// wprintln formats using the default print settings and writes to w wprintln :: proc(w: io.Writer, args: ..any, sep := " ") -> int { fi: Info fi.writer = w - size0 := io.size(auto_cast w) - for _, i in args { if i > 0 { - io.write_string(fi.writer, sep) + io.write_string(fi.writer, sep, &fi.n) } fmt_value(&fi, args[i], 'v') } - io.write_byte(fi.writer, '\n') + io.write_byte(fi.writer, '\n', &fi.n) io.flush(auto_cast w) - - size1 := io.size(auto_cast w) - return int(size1 - size0) + return fi.n } +// wprintf formats according to the specififed format string and writes to w wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { fi: Info arg_index: int = 0 end := len(fmt) was_prev_index := false - size0 := io.size(auto_cast w) - loop: for i := 0; i < end; /**/ { fi = Info{writer = w, good_arg_index = true, reordered = fi.reordered} @@ -230,7 +247,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { i += 1 } if i > prev_i { - io.write_string(fi.writer, fmt[prev_i:i]) + io.write_string(fi.writer, fmt[prev_i:i], &fi.n) } if i >= end { break loop @@ -245,13 +262,13 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { // Skip extra one i += 1 } - io.write_byte(fi.writer, char) + io.write_byte(fi.writer, char, &fi.n) continue loop } else if char == '{' { if i < end && fmt[i] == char { // Skip extra one i += 1 - io.write_byte(fi.writer, char) + io.write_byte(fi.writer, char, &fi.n) continue loop } } @@ -282,7 +299,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { i += 1 fi.width, arg_index, fi.width_set = int_from_arg(args, arg_index) if !fi.width_set { - io.write_string(w, "%!(BAD WIDTH)") + io.write_string(w, "%!(BAD WIDTH)", &fi.n) } if fi.width < 0 { @@ -313,7 +330,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { fi.prec_set = false } if !fi.prec_set { - io.write_string(fi.writer, "%!(BAD PRECISION)") + io.write_string(fi.writer, "%!(BAD PRECISION)", &fi.n) } was_prev_index = false } else { @@ -326,7 +343,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { } if i >= end { - io.write_string(fi.writer, "%!(NO VERB)") + io.write_string(fi.writer, "%!(NO VERB)", &fi.n) break loop } @@ -335,11 +352,11 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { switch { case verb == '%': - io.write_byte(fi.writer, '%') + io.write_byte(fi.writer, '%', &fi.n) case !fi.good_arg_index: - io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER)") + io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER)", &fi.n) case arg_index >= len(args): - io.write_string(fi.writer, "%!(MISSING ARGUMENT)") + io.write_string(fi.writer, "%!(MISSING ARGUMENT)", &fi.n) case: fmt_arg(&fi, args[arg_index], verb) arg_index += 1 @@ -355,14 +372,14 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { arg_index = new_arg_index i = new_i } else { - io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER ") + io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER ", &fi.n) // Skip over the bad argument start_index := i for i < end && fmt[i] != '}' && fmt[i] != ':' { i += 1 } fmt_arg(&fi, fmt[start_index:i], 'v') - io.write_string(fi.writer, ")") + io.write_string(fi.writer, ")", &fi.n) } } @@ -395,7 +412,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { i += 1 fi.width, arg_index, fi.width_set = int_from_arg(args, arg_index) if !fi.width_set { - io.write_string(fi.writer, "%!(BAD WIDTH)") + io.write_string(fi.writer, "%!(BAD WIDTH)", &fi.n) } if fi.width < 0 { @@ -426,7 +443,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { fi.prec_set = false } if !fi.prec_set { - io.write_string(fi.writer, "%!(BAD PRECISION)") + io.write_string(fi.writer, "%!(BAD PRECISION)", &fi.n) } was_prev_index = false } else { @@ -440,7 +457,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { if i >= end { - io.write_string(fi.writer, "%!(NO VERB)") + io.write_string(fi.writer, "%!(NO VERB)", &fi.n) break loop } @@ -450,7 +467,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { } if i >= end { - io.write_string(fi.writer, "%!(MISSING CLOSE BRACE)") + io.write_string(fi.writer, "%!(MISSING CLOSE BRACE)", &fi.n) break loop } @@ -459,11 +476,11 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { switch { case brace != '}': - io.write_string(fi.writer, "%!(MISSING CLOSE BRACE)") + io.write_string(fi.writer, "%!(MISSING CLOSE BRACE)", &fi.n) case !fi.good_arg_index: - io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER)") + io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER)", &fi.n) case arg_index >= len(args): - io.write_string(fi.writer, "%!(MISSING ARGUMENT)") + io.write_string(fi.writer, "%!(MISSING ARGUMENT)", &fi.n) case: fmt_arg(&fi, args[arg_index], verb) arg_index += 1 @@ -472,32 +489,33 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { } if !fi.reordered && arg_index < len(args) { - io.write_string(fi.writer, "%!(EXTRA ") + io.write_string(fi.writer, "%!(EXTRA ", &fi.n) for arg, index in args[arg_index:] { if index > 0 { - io.write_string(fi.writer, ", ") + io.write_string(fi.writer, ", ", &fi.n) } if arg == nil { - io.write_string(fi.writer, "") + io.write_string(fi.writer, "", &fi.n) } else { fmt_arg(&fi, args[index], 'v') } } - io.write_string(fi.writer, ")") + io.write_string(fi.writer, ")", &fi.n) } io.flush(auto_cast w) - size1 := io.size(auto_cast w) - return int(size1 - size0) + return fi.n } +// wprint_type is a utility procedure to write a ^runtime.Type_Info value to w wprint_type :: proc(w: io.Writer, info: ^runtime.Type_Info) -> (int, io.Error) { n, err := reflect.write_type(w, info) io.flush(auto_cast w) return n, err } +// wprint_typeid is a utility procedure to write a typeid value to w wprint_typeid :: proc(w: io.Writer, id: typeid) -> (int, io.Error) { n, err := reflect.write_type(w, type_info_of(id)) io.flush(auto_cast w) @@ -576,23 +594,27 @@ int_from_arg :: proc(args: []any, arg_index: int) -> (int, int, bool) { fmt_bad_verb :: proc(using fi: ^Info, verb: rune) { - io.write_string(writer, "%!") - io.write_rune(writer, verb) - io.write_byte(writer, '(') + prev_in_bad := fi.in_bad + defer fi.in_bad = prev_in_bad + fi.in_bad = true + + io.write_string(writer, "%!", &fi.n) + io.write_rune(writer, verb, &fi.n) + io.write_byte(writer, '(', &fi.n) if arg.id != nil { - reflect.write_typeid(writer, arg.id) - io.write_byte(writer, '=') + reflect.write_typeid(writer, arg.id, &fi.n) + io.write_byte(writer, '=', &fi.n) fmt_value(fi, arg, 'v') } else { - io.write_string(writer, "") + io.write_string(writer, "", &fi.n) } - io.write_byte(writer, ')') + io.write_byte(writer, ')', &fi.n) } fmt_bool :: proc(using fi: ^Info, b: bool, verb: rune) { switch verb { case 't', 'v': - io.write_string(writer, b ? "true" : "false") + fmt_string(fi, b ? "true" : "false", 's') case: fmt_bad_verb(fi, verb) } @@ -610,7 +632,7 @@ fmt_write_padding :: proc(fi: ^Info, width: int) { } for i := 0; i < width; i += 1 { - io.write_byte(fi.writer, pad_byte) + io.write_byte(fi.writer, pad_byte, &fi.n) } } @@ -669,8 +691,8 @@ _fmt_int :: proc(fi: ^Info, u: u64, base: int, is_signed: bool, bit_size: int, d case 16: c = 'x' } if c != 0 { - io.write_byte(fi.writer, '0') - io.write_byte(fi.writer, c) + io.write_byte(fi.writer, '0', &fi.n) + io.write_byte(fi.writer, c, &fi.n) } } @@ -735,8 +757,8 @@ _fmt_int_128 :: proc(fi: ^Info, u: u128, base: int, is_signed: bool, bit_size: i case 16: c = 'x' } if c != 0 { - io.write_byte(fi.writer, '0') - io.write_byte(fi.writer, c) + io.write_byte(fi.writer, '0', &fi.n) + io.write_byte(fi.writer, c, &fi.n) } } @@ -752,9 +774,9 @@ __DIGITS_UPPER := "0123456789ABCDEFX" fmt_rune :: proc(fi: ^Info, r: rune, verb: rune) { switch verb { case 'c', 'r', 'v': - io.write_rune(fi.writer, r) + io.write_rune(fi.writer, r, &fi.n) case 'q': - strings.write_quoted_rune(fi.writer, r) + fi.n += io.write_quoted_rune(fi.writer, r) case: fmt_int(fi, u64(r), false, 32, verb) } @@ -776,7 +798,7 @@ fmt_int :: proc(fi: ^Info, u: u64, is_signed: bool, bit_size: int, verb: rune) { if r < 0 || r > utf8.MAX_RUNE { fmt_bad_verb(fi, verb) } else { - io.write_string(fi.writer, "U+") + io.write_string(fi.writer, "U+", &fi.n) _fmt_int(fi, u, 16, false, bit_size, __DIGITS_UPPER) } @@ -801,7 +823,7 @@ fmt_int_128 :: proc(fi: ^Info, u: u128, is_signed: bool, bit_size: int, verb: ru if r < 0 || r > utf8.MAX_RUNE { fmt_bad_verb(fi, verb) } else { - io.write_string(fi.writer, "U+") + io.write_string(fi.writer, "U+", &fi.n) _fmt_int_128(fi, u, 16, false, bit_size, __DIGITS_UPPER) } @@ -812,24 +834,24 @@ fmt_int_128 :: proc(fi: ^Info, u: u128, is_signed: bool, bit_size: int, verb: ru _pad :: proc(fi: ^Info, s: string) { if !fi.width_set { - io.write_string(fi.writer, s) + io.write_string(fi.writer, s, &fi.n) return } width := fi.width - utf8.rune_count_in_string(s) if fi.minus { // right pad - io.write_string(fi.writer, s) + io.write_string(fi.writer, s, &fi.n) fmt_write_padding(fi, width) } else { // left pad fmt_write_padding(fi, width) - io.write_string(fi.writer, s) + io.write_string(fi.writer, s, &fi.n) } } fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) { switch verb { - case 'f', 'F', 'v': + case 'f', 'F', 'g', 'G', 'v': prec: int = 3 if fi.prec_set { prec = fi.prec @@ -849,15 +871,15 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) { } if len(b) > 1 && (b[1] == 'N' || b[1] == 'I') { - io.write_string(fi.writer, string(b)) + io.write_string(fi.writer, string(b), &fi.n) return } if fi.plus || b[0] != '+' { if fi.zero && fi.width_set && fi.width > len(b) { - io.write_byte(fi.writer, b[0]) + io.write_byte(fi.writer, b[0], &fi.n) fmt_write_padding(fi, fi.width - len(b)) - io.write_string(fi.writer, string(b[1:])) + io.write_string(fi.writer, string(b[1:]), &fi.n) } else { _pad(fi, string(b)) } @@ -885,15 +907,15 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) { } if len(b) > 1 && (b[1] == 'N' || b[1] == 'I') { - io.write_string(fi.writer, string(b)) + io.write_string(fi.writer, string(b), &fi.n) return } if fi.plus || str[0] != '+' { if fi.zero && fi.width_set && fi.width > len(b) { - io.write_byte(fi.writer, b[0]) + io.write_byte(fi.writer, b[0], &fi.n) fmt_write_padding(fi, fi.width - len(b)) - io.write_string(fi.writer, string(b[1:])) + io.write_string(fi.writer, string(b[1:]), &fi.n) } else { _pad(fi, string(b)) } @@ -917,7 +939,7 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) { case: panic("Unhandled float size") } - io.write_string(fi.writer, "0h") + io.write_string(fi.writer, "0h", &fi.n) _fmt_int(fi, u, 16, false, bit_size, __DIGITS_LOWER if verb == 'h' else __DIGITS_UPPER) @@ -928,17 +950,41 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) { fmt_string :: proc(fi: ^Info, s: string, verb: rune) { + s, verb := s, verb + if ol, ok := fi.optional_len.?; ok { + s = s[:clamp(ol, 0, len(s))] + } + if !fi.in_bad && fi.record_level > 0 && verb == 'v' { + verb = 'q' + } + switch verb { case 's', 'v': - io.write_string(fi.writer, s) - if fi.width_set && len(s) < fi.width { - for _ in 0.. len(s) { + if fi.minus { + io.write_string(fi.writer, s, &fi.n) + } + + for _ in 0.. 0 && space { - io.write_byte(fi.writer, ' ') + io.write_byte(fi.writer, ' ', &fi.n) } char_set := __DIGITS_UPPER if verb == 'x' { @@ -968,8 +1014,8 @@ fmt_pointer :: proc(fi: ^Info, p: rawptr, verb: rune) { u := u64(uintptr(p)) switch verb { case 'p', 'v': - if !fi.hash || verb == 'v' { - io.write_string(fi.writer, "0x") + if !fi.hash && verb == 'v' { + io.write_string(fi.writer, "0x", &fi.n) } _fmt_int(fi, u, 16, false, 8*size_of(rawptr), __DIGITS_UPPER) @@ -1031,7 +1077,7 @@ string_to_enum_value :: proc($T: typeid, s: string) -> (T, bool) { fmt_enum :: proc(fi: ^Info, v: any, verb: rune) { if v.id == nil || v.data == nil { - io.write_string(fi.writer, "") + io.write_string(fi.writer, "", &fi.n) return } @@ -1043,12 +1089,14 @@ fmt_enum :: proc(fi: ^Info, v: any, verb: rune) { case: fmt_bad_verb(fi, verb) case 'i', 'd', 'f': fmt_arg(fi, any{v.data, runtime.type_info_base(e.base).id}, verb) - case 's', 'v': - str, ok := enum_value_to_string(v) - if !ok { - str = "%!(BAD ENUM VALUE)" + case 's', 'v', 'q': + if str, ok := enum_value_to_string(v); ok { + fmt_string(fi, str, verb) + } else { + io.write_string(fi.writer, "%!(BAD ENUM VALUE=", &fi.n) + fmt_arg(fi, any{v.data, runtime.type_info_base(e.base).id}, 'i') + io.write_string(fi.writer, ")", &fi.n) } - io.write_string(fi.writer, str) } } } @@ -1141,12 +1189,12 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") { et := runtime.type_info_base(info.elem) if name != "" { - io.write_string(fi.writer, name) + io.write_string(fi.writer, name, &fi.n) } else { - reflect.write_type(fi.writer, type_info) + reflect.write_type(fi.writer, type_info, &fi.n) } - io.write_byte(fi.writer, '{') - defer io.write_byte(fi.writer, '}') + io.write_byte(fi.writer, '{', &fi.n) + defer io.write_byte(fi.writer, '}', &fi.n) e, is_enum := et.variant.(runtime.Type_Info_Enum) commas := 0 @@ -1156,21 +1204,21 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") { } if commas > 0 { - io.write_string(fi.writer, ", ") + io.write_string(fi.writer, ", ", &fi.n) } if is_enum { for ev, evi in e.values { v := u64(ev) if v == u64(i) { - io.write_string(fi.writer, e.names[evi]) + io.write_string(fi.writer, e.names[evi], &fi.n) commas += 1 continue loop } } } v := i64(i) + info.lower - io.write_i64(fi.writer, v, 10) + io.write_i64(fi.writer, v, 10, &fi.n) commas += 1 } } @@ -1178,20 +1226,22 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") { fmt_write_indent :: proc(fi: ^Info) { for in 0.. 0 { io.write_string(fi.writer, ", ") } + if i > 0 { io.write_string(fi.writer, ", ", &fi.n) } data := uintptr(array_data) + uintptr(i*elem_size) fmt_arg(fi, any{rawptr(data), elem_id}, verb) @@ -1216,21 +1266,529 @@ fmt_write_array :: proc(fi: ^Info, array_data: rawptr, count: int, elem_size: in } } -fmt_value :: proc(fi: ^Info, v: any, verb: rune) { + +@(private) +handle_tag :: proc(data: rawptr, info: reflect.Type_Info_Struct, idx: int, verb: ^rune, optional_len: ^int, use_nul_termination: ^bool) -> (do_continue: bool) { + handle_optional_len :: proc(data: rawptr, info: reflect.Type_Info_Struct, field_name: string, optional_len: ^int) { + if optional_len == nil { + return + } + for f, i in info.names { + if f != field_name { + continue + } + ptr := rawptr(uintptr(data) + info.offsets[i]) + field := any{ptr, info.types[i].id} + if new_len, iok := reflect.as_int(field); iok { + optional_len^ = max(new_len, 0) + } + break + } + } + tag := info.tags[idx] + if vt, ok := reflect.struct_tag_lookup(reflect.Struct_Tag(tag), "fmt"); ok { + value := strings.trim_space(string(vt)) + switch value { + case "": return false + case "-": return true + } + r, w := utf8.decode_rune_in_string(value) + value = value[w:] + if value == "" || value[0] == ',' { + verb^ = r + if len(value) > 0 && value[0] == ',' { + field_name := value[1:] + if field_name == "0" { + if use_nul_termination != nil { + use_nul_termination^ = true + } + } else { + switch r { + case 's', 'q': + handle_optional_len(data, info, field_name, optional_len) + case 'v': + #partial switch reflect.type_kind(info.types[idx].id) { + case .String, .Multi_Pointer, .Array, .Slice, .Dynamic_Array: + handle_optional_len(data, info, field_name, optional_len) + } + } + } + } + } + } + return false +} + +fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_Struct, type_name: string) { + if the_verb != 'v' { + fmt_bad_verb(fi, the_verb) + return + } + if info.is_raw_union { + if type_name == "" { + io.write_string(fi.writer, "(raw union)", &fi.n) + } else { + io.write_string(fi.writer, type_name, &fi.n) + io.write_string(fi.writer, "{}", &fi.n) + } + return + } + + is_soa := info.soa_kind != .None + + io.write_string(fi.writer, type_name, &fi.n) + io.write_byte(fi.writer, '[' if is_soa else '{', &fi.n) + fi.record_level += 1 + defer fi.record_level -= 1 + + hash := fi.hash; defer fi.hash = hash + indent := fi.indent; defer fi.indent -= 1 + do_trailing_comma := hash + + // fi.hash = false; + fi.indent += 1 + + if hash { + io.write_byte(fi.writer, '\n', &fi.n) + } + defer { + if hash { + for in 0.. 0 { io.write_string(fi.writer, ", ", &fi.n) } + + field_count := -1 + + if !hash && field_count > 0 { io.write_string(fi.writer, ", ", &fi.n) } + + io.write_string(fi.writer, base_type_name, &fi.n) + io.write_byte(fi.writer, '{', &fi.n) + defer io.write_byte(fi.writer, '}', &fi.n) + fi.record_level += 1 + defer fi.record_level -= 1 + + for i in 0.. 0 { io.write_string(fi.writer, ", ", &fi.n) } + if hash { + fmt_write_indent(fi) + } + + io.write_string(fi.writer, name, &fi.n) + io.write_string(fi.writer, " = ", &fi.n) + + if info.soa_kind == .Fixed { + t := info.types[i].variant.(runtime.Type_Info_Array).elem + t_size := uintptr(t.size) + if reflect.is_any(t) { + io.write_string(fi.writer, "any{}", &fi.n) + } else { + data := rawptr(uintptr(v.data) + info.offsets[i] + index*t_size) + fmt_arg(fi, any{data, t.id}, verb) + } + } else { + t := info.types[i].variant.(runtime.Type_Info_Pointer).elem + t_size := uintptr(t.size) + if reflect.is_any(t) { + io.write_string(fi.writer, "any{}", &fi.n) + } else { + field_ptr := (^^byte)(uintptr(v.data) + info.offsets[i])^ + data := rawptr(uintptr(field_ptr) + index*t_size) + fmt_arg(fi, any{data, t.id}, verb) + } + } + + if hash { io.write_string(fi.writer, ",\n", &fi.n) } + } + } + } else { + field_count := -1 + for name, i in info.names { + optional_len: int = -1 + use_nul_termination: bool = false + verb := 'v' + if handle_tag(v.data, info, i, &verb, &optional_len, &use_nul_termination) { + continue + } + field_count += 1 + + if optional_len >= 0 { + fi.optional_len = optional_len + } + defer if optional_len >= 0 { + fi.optional_len = nil + } + fi.use_nul_termination = use_nul_termination + defer fi.use_nul_termination = false + + if !do_trailing_comma && field_count > 0 { io.write_string(fi.writer, ", ") } + if hash { + fmt_write_indent(fi) + } + + io.write_string(fi.writer, name, &fi.n) + io.write_string(fi.writer, " = ", &fi.n) + + if t := info.types[i]; reflect.is_any(t) { + io.write_string(fi.writer, "any{}", &fi.n) + } else { + data := rawptr(uintptr(v.data) + info.offsets[i]) + fmt_arg(fi, any{data, t.id}, verb) + } + + if do_trailing_comma { io.write_string(fi.writer, ",\n", &fi.n) } + } + } +} + +@(private) +search_nul_termination :: proc(ptr: rawptr, elem_size: int, max_n: int) -> (n: int) { + for p := uintptr(ptr); max_n < 0 || n < max_n; p += uintptr(elem_size) { + if mem.check_zero_ptr(rawptr(p), elem_size) { + break + } + n += 1 + } + return n +} + +fmt_array_nul_terminated :: proc(fi: ^Info, data: rawptr, max_n: int, elem_size: int, elem: ^reflect.Type_Info, verb: rune) { + if data == nil { + io.write_string(fi.writer, "", &fi.n) + return + } + n := search_nul_termination(data, elem_size, max_n) + fmt_array(fi, data, n, elem_size, elem, verb) +} + +fmt_array :: proc(fi: ^Info, data: rawptr, n: int, elem_size: int, elem: ^reflect.Type_Info, verb: rune) { + if data == nil && n > 0 { + io.write_string(fi.writer, "nil") + return + } + if verb == 's' || verb == 'q' { + print_utf16 :: proc(fi: ^Info, s: []$T) where size_of(T) == 2, intrinsics.type_is_integer(T) { + REPLACEMENT_CHAR :: '\ufffd' + _surr1 :: 0xd800 + _surr2 :: 0xdc00 + _surr3 :: 0xe000 + _surr_self :: 0x10000 + + for i := 0; i < len(s); i += 1 { + r := rune(REPLACEMENT_CHAR) + + switch c := s[i]; { + case c < _surr1, _surr3 <= c: + r = rune(c) + case _surr1 <= c && c < _surr2 && i+1 < len(s) && + _surr2 <= s[i+1] && s[i+1] < _surr3: + r1, r2 := rune(c), rune(s[i+1]) + if _surr1 <= r1 && r1 < _surr2 && _surr2 <= r2 && r2 < _surr3 { + r = (r1-_surr1)<<10 | (r2 - _surr2) + _surr_self + } + i += 1 + } + io.write_rune(fi.writer, r, &fi.n) + } + } + + print_utf32 :: proc(fi: ^Info, s: []$T) where size_of(T) == 4 { + for r in s { + io.write_rune(fi.writer, rune(r), &fi.n) + } + } + + switch reflect.type_info_base(elem).id { + case byte: fmt_string(fi, string(([^]byte)(data)[:n]), verb); return + case u16: print_utf16(fi, ([^]u16)(data)[:n]); return + case u16le: print_utf16(fi, ([^]u16le)(data)[:n]); return + case u16be: print_utf16(fi, ([^]u16be)(data)[:n]); return + case u32: print_utf32(fi, ([^]u32)(data)[:n]); return + case u32le: print_utf32(fi, ([^]u32le)(data)[:n]); return + case u32be: print_utf32(fi, ([^]u32be)(data)[:n]); return + case rune: print_utf32(fi, ([^]rune)(data)[:n]); return + } + } + if verb == 'p' { + fmt_pointer(fi, data, 'p') + } else { + fmt_write_array(fi, data, n, elem_size, elem.id, verb) + } +} + +fmt_named :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Named) { write_padded_number :: proc(fi: ^Info, i: i64, width: int) { n := width-1 for x := i; x >= 10; x /= 10 { n -= 1 } for in 0.. (nw: int, nv: u64) { + v := v + w := len(buf) + print := false + for in 0.. int { + v := v + w := len(buf) + if v == 0 { + w -= 1 + buf[w] = '0' + } else { + for v > 0 { + w -= 1 + buf[w] = byte(v%10) + '0' + v /= 10 + } + } + return w + } + + buf: [32]byte + w := len(buf) + u := u64(a) + neg := a < 0 + if neg { + u = -u + } + + if u < u64(time.Second) { + prec: int + w -= 1 + buf[w] = 's' + w -= 1 + switch { + case u == 0: + io.write_string(fi.writer, "0s", &fi.n) + return + case u < u64(time.Microsecond): + prec = 0 + buf[w] = 'n' + case u < u64(time.Millisecond): + prec = 3 + // U+00B5 'µ' micro sign == 0xC2 0xB5 + w -= 1 // Need room for two bytes + copy(buf[w:], "µ") + case: + prec = 6 + buf[w] = 'm' + } + w, u = ffrac(buf[:w], u, prec) + w = fint(buf[:w], u) + } else { + w -= 1 + buf[w] = 's' + w, u = ffrac(buf[:w], u, 9) + w = fint(buf[:w], u%60) + u /= 60 + if u > 0 { + w -= 1 + buf[w] = 'm' + w = fint(buf[:w], u%60) + u /= 60 + if u > 0 { + w -= 1 + buf[w] = 'h' + w = fint(buf[:w], u) + } + } + } + + if neg { + w -= 1 + buf[w] = '-' + } + io.write_string(fi.writer, string(buf[w:]), &fi.n) + return + + case time.Time: + t := a + y, mon, d := time.date(t) + h, min, s := time.clock(t) + ns := (t._nsec - (t._nsec/1e9 + time.UNIX_TO_ABSOLUTE)*1e9) % 1e9 + write_padded_number(fi, i64(y), 4) + io.write_byte(fi.writer, '-', &fi.n) + write_padded_number(fi, i64(mon), 2) + io.write_byte(fi.writer, '-', &fi.n) + write_padded_number(fi, i64(d), 2) + io.write_byte(fi.writer, ' ', &fi.n) + + write_padded_number(fi, i64(h), 2) + io.write_byte(fi.writer, ':', &fi.n) + write_padded_number(fi, i64(min), 2) + io.write_byte(fi.writer, ':', &fi.n) + write_padded_number(fi, i64(s), 2) + io.write_byte(fi.writer, '.', &fi.n) + write_padded_number(fi, (ns), 9) + io.write_string(fi.writer, " +0000 UTC", &fi.n) + return + } + + #partial switch b in info.base.variant { + case runtime.Type_Info_Struct: + fmt_struct(fi, v, verb, b, info.name) + case runtime.Type_Info_Bit_Set: + fmt_bit_set(fi, v) + case: + fmt_value(fi, any{v.data, info.base.id}, verb) + } +} + +fmt_union :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Union, type_size: int) { + if type_size == 0 { + io.write_string(fi.writer, "nil", &fi.n) + return + } + + if reflect.type_info_union_is_pure_maybe(info) { + if v.data == nil { + io.write_string(fi.writer, "nil", &fi.n) + } else { + id := info.variants[0].id + fmt_arg(fi, any{v.data, id}, verb) + } + return + } + + tag: i64 = -1 + tag_ptr := uintptr(v.data) + info.tag_offset + tag_any := any{rawptr(tag_ptr), info.tag_type.id} + + switch i in tag_any { + case u8: tag = i64(i) + case i8: tag = i64(i) + case u16: tag = i64(i) + case i16: tag = i64(i) + case u32: tag = i64(i) + case i32: tag = i64(i) + case u64: tag = i64(i) + case i64: tag = i + case: panic("Invalid union tag type") + } + assert(tag >= 0) + + if v.data == nil { + io.write_string(fi.writer, "nil", &fi.n) + } else if info.no_nil { + id := info.variants[tag].id + fmt_arg(fi, any{v.data, id}, verb) + } else if tag == 0 { + io.write_string(fi.writer, "nil", &fi.n) + } else { + id := info.variants[tag-1].id + fmt_arg(fi, any{v.data, id}, verb) + } +} + +fmt_matrix :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Matrix) { + io.write_string(fi.writer, "matrix[", &fi.n) + defer io.write_byte(fi.writer, ']', &fi.n) + + fi.indent += 1 + + if fi.hash { + // Printed as it is written + io.write_byte(fi.writer, '\n', &fi.n) + for row in 0.. 0 { io.write_string(fi.writer, ", ", &fi.n) } + + offset := (row + col*info.elem_stride)*info.elem_size + + data := uintptr(v.data) + uintptr(offset) + fmt_arg(fi, any{rawptr(data), info.elem.id}, verb) + } + io.write_string(fi.writer, ",\n", &fi.n) + } + } else { + // Printed in Row-Major layout to match text layout + for row in 0.. 0 { io.write_string(fi.writer, "; ", &fi.n) } + for col in 0.. 0 { io.write_string(fi.writer, ", ", &fi.n) } + + offset := (row + col*info.elem_stride)*info.elem_size + + data := uintptr(v.data) + uintptr(offset) + fmt_arg(fi, any{rawptr(data), info.elem.id}, verb) + } + } + } + + fi.indent -= 1 + + if fi.hash { + fmt_write_indent(fi) + } +} + +fmt_value :: proc(fi: ^Info, v: any, verb: rune) { if v.data == nil || v.id == nil { - io.write_string(fi.writer, "") + io.write_string(fi.writer, "", &fi.n) return } @@ -1253,238 +1811,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { case runtime.Type_Info_Tuple: // Ignore case runtime.Type_Info_Named: - // Built-in Custom Formatters for core library types - switch a in v { - case runtime.Source_Code_Location: - io.write_string(fi.writer, a.file_path) - io.write_byte(fi.writer, '(') - io.write_int(fi.writer, int(a.line)) - io.write_byte(fi.writer, ':') - io.write_int(fi.writer, int(a.column)) - io.write_byte(fi.writer, ')') - return - - case time.Duration: - ffrac :: proc(buf: []byte, v: u64, prec: int) -> (nw: int, nv: u64) { - v := v - w := len(buf) - print := false - for in 0.. int { - v := v - w := len(buf) - if v == 0 { - w -= 1 - buf[w] = '0' - } else { - for v > 0 { - w -= 1 - buf[w] = byte(v%10) + '0' - v /= 10 - } - } - return w - } - - buf: [32]byte - w := len(buf) - u := u64(a) - neg := a < 0 - if neg { - u = -u - } - - if u < u64(time.Second) { - prec: int - w -= 1 - buf[w] = 's' - w -= 1 - switch { - case u == 0: - io.write_string(fi.writer, "0s") - return - case u < u64(time.Microsecond): - prec = 0 - buf[w] = 'n' - case u < u64(time.Millisecond): - prec = 3 - // U+00B5 'µ' micro sign == 0xC2 0xB5 - w -= 1 // Need room for two bytes - copy(buf[w:], "µ") - case: - prec = 6 - buf[w] = 'm' - } - w, u = ffrac(buf[:w], u, prec) - w = fint(buf[:w], u) - } else { - w -= 1 - buf[w] = 's' - w, u = ffrac(buf[:w], u, 9) - w = fint(buf[:w], u%60) - u /= 60 - if u > 0 { - w -= 1 - buf[w] = 'm' - w = fint(buf[:w], u%60) - u /= 60 - if u > 0 { - w -= 1 - buf[w] = 'h' - w = fint(buf[:w], u) - } - } - } - - if neg { - w -= 1 - buf[w] = '-' - } - io.write_string(fi.writer, string(buf[w:])) - return - - case time.Time: - t := a - y, mon, d := time.date(t) - h, min, s := time.clock(t) - ns := (t._nsec - (t._nsec/1e9 + time.UNIX_TO_ABSOLUTE)*1e9) % 1e9 - write_padded_number(fi, i64(y), 4) - io.write_byte(fi.writer, '-') - write_padded_number(fi, i64(mon), 2) - io.write_byte(fi.writer, '-') - write_padded_number(fi, i64(d), 2) - io.write_byte(fi.writer, ' ') - - write_padded_number(fi, i64(h), 2) - io.write_byte(fi.writer, ':') - write_padded_number(fi, i64(min), 2) - io.write_byte(fi.writer, ':') - write_padded_number(fi, i64(s), 2) - io.write_byte(fi.writer, '.') - write_padded_number(fi, (ns), 9) - io.write_string(fi.writer, " +0000 UTC") - return - } - - #partial switch b in info.base.variant { - case runtime.Type_Info_Struct: - if verb != 'v' { - fmt_bad_verb(fi, verb) - return - } - if b.is_raw_union { - io.write_string(fi.writer, info.name) - io.write_string(fi.writer, "{}") - return - } - - is_soa := b.soa_kind != .None - - io.write_string(fi.writer, info.name) - io.write_byte(fi.writer, '[' if is_soa else '{') - - hash := fi.hash; defer fi.hash = hash - indent := fi.indent; defer fi.indent -= 1 - - // fi.hash = false; - fi.indent += 1 - - if hash { - io.write_byte(fi.writer, '\n') - } - defer { - if hash { - for in 0.. 0 { io.write_string(fi.writer, ", ") } - - field_count := -1 - - if !hash && field_count > 0 { io.write_string(fi.writer, ", ") } - - io.write_string(fi.writer, base_type_name) - io.write_byte(fi.writer, '{') - defer io.write_byte(fi.writer, '}') - - for name, i in b.names { - field_count += 1 - - if !hash && field_count > 0 { io.write_string(fi.writer, ", ") } - if hash { - fmt_write_indent(fi) - } - - io.write_string(fi.writer, name) - io.write_string(fi.writer, " = ") - - t := b.types[i].variant.(runtime.Type_Info_Array).elem - t_size := uintptr(t.size) - if reflect.is_any(t) { - io.write_string(fi.writer, "any{}") - } else { - data := rawptr(uintptr(v.data) + b.offsets[i] + index*t_size) - fmt_arg(fi, any{data, t.id}, 'v') - } - - if hash { io.write_string(fi.writer, ",\n") } - } - } - } else { - field_count := -1 - for name, i in b.names { - field_count += 1 - - if !hash && field_count > 0 { io.write_string(fi.writer, ", ") } - if hash { - fmt_write_indent(fi) - } - - io.write_string(fi.writer, name) - io.write_string(fi.writer, " = ") - - if t := b.types[i]; reflect.is_any(t) { - io.write_string(fi.writer, "any{}") - } else { - data := rawptr(uintptr(v.data) + b.offsets[i]) - fmt_arg(fi, any{data, t.id}, 'v') - } - - if hash { io.write_string(fi.writer, ",\n") } - } - } - - case runtime.Type_Info_Bit_Set: - fmt_bit_set(fi, v) - case: - fmt_value(fi, any{v.data, info.base.id}, verb) - } + fmt_named(fi, v, verb, info) case runtime.Type_Info_Boolean: fmt_arg(fi, v, verb) case runtime.Type_Info_Integer: fmt_arg(fi, v, verb) @@ -1496,7 +1823,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { case runtime.Type_Info_Pointer: if v.id == typeid_of(^runtime.Type_Info) { - reflect.write_type(fi.writer, (^^runtime.Type_Info)(v.data)^) + reflect.write_type(fi.writer, (^^runtime.Type_Info)(v.data)^, &fi.n) } else { ptr := (^rawptr)(v.data)^ if verb != 'p' && info.elem != nil { @@ -1510,12 +1837,12 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { runtime.Type_Info_Dynamic_Array, runtime.Type_Info_Map: if ptr == nil { - io.write_string(fi.writer, "") + io.write_string(fi.writer, "", &fi.n) return } - if fi.record_level < 1 { - fi.record_level += 1 - defer fi.record_level -= 1 + if fi.indirection_level < 1 { + fi.indirection_level += 1 + defer fi.indirection_level -= 1 io.write_byte(fi.writer, '&') fmt_value(fi, a, verb) return @@ -1524,13 +1851,13 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { case runtime.Type_Info_Struct, runtime.Type_Info_Union: if ptr == nil { - io.write_string(fi.writer, "") + io.write_string(fi.writer, "", &fi.n) return } - if fi.record_level < 1 { - fi.record_level += 1 - defer fi.record_level -= 1 - io.write_byte(fi.writer, '&') + if fi.indirection_level < 1 { + fi.indirection_level += 1 + defer fi.indirection_level -= 1 + io.write_byte(fi.writer, '&', &fi.n) fmt_value(fi, a, verb) return } @@ -1542,38 +1869,56 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { case runtime.Type_Info_Multi_Pointer: ptr := (^rawptr)(v.data)^ + if ptr == nil { + io.write_string(fi.writer, "", &fi.n) + return + } if verb != 'p' && info.elem != nil { a := any{ptr, info.elem.id} elem := runtime.type_info_base(info.elem) if elem != nil { + if n, ok := fi.optional_len.?; ok { + fmt_array(fi, ptr, n, elem.size, elem, verb) + return + } else if fi.use_nul_termination { + fmt_array_nul_terminated(fi, ptr, -1, elem.size, elem, verb) + return + } + #partial switch e in elem.variant { + case runtime.Type_Info_Integer: + switch verb { + case 's', 'q': + switch elem.id { + case u8: + fmt_cstring(fi, cstring(ptr), verb) + return + case u16, u32, rune: + n := search_nul_termination(ptr, elem.size, -1) + fmt_array(fi, ptr, n, elem.size, elem, verb) + return + } + } + case runtime.Type_Info_Array, runtime.Type_Info_Slice, runtime.Type_Info_Dynamic_Array, runtime.Type_Info_Map: - if ptr == nil { - io.write_string(fi.writer, "") - return - } - if fi.record_level < 1 { - fi.record_level += 1 - defer fi.record_level -= 1 - io.write_byte(fi.writer, '&') + if fi.indirection_level < 1 { + fi.indirection_level += 1 + defer fi.indirection_level -= 1 + io.write_byte(fi.writer, '&', &fi.n) fmt_value(fi, a, verb) return } case runtime.Type_Info_Struct, runtime.Type_Info_Union: - if ptr == nil { - io.write_string(fi.writer, "") - return - } - if fi.record_level < 1 { - fi.record_level += 1 - defer fi.record_level -= 1 - io.write_byte(fi.writer, '&') + if fi.indirection_level < 1 { + fi.indirection_level += 1 + defer fi.indirection_level -= 1 + io.write_byte(fi.writer, '&', &fi.n) fmt_value(fi, a, verb) return } @@ -1582,21 +1927,16 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { } fmt_pointer(fi, ptr, verb) - case runtime.Type_Info_Array: - if (verb == 's' || verb == 'q') && reflect.is_byte(info.elem) { - s := strings.string_from_ptr((^byte)(v.data), info.count) - fmt_string(fi, s, verb) - } else { - fmt_write_array(fi, v.data, info.count, info.elem_size, info.elem.id, verb) - } - case runtime.Type_Info_Enumerated_Array: + fi.record_level += 1 + defer fi.record_level -= 1 + if fi.hash { - io.write_string(fi.writer, "[\n") + io.write_string(fi.writer, "[\n", &fi.n) defer { - io.write_byte(fi.writer, '\n') + io.write_byte(fi.writer, '\n', &fi.n) fmt_write_indent(fi) - io.write_byte(fi.writer, ']') + io.write_byte(fi.writer, ']', &fi.n) } indent := fi.indent fi.indent += 1 @@ -1607,78 +1947,94 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { idx, ok := stored_enum_value_to_string(info.index, info.min_value, i) if ok { - io.write_byte(fi.writer, '.') - io.write_string(fi.writer, idx) + io.write_byte(fi.writer, '.', &fi.n) + io.write_string(fi.writer, idx, &fi.n) } else { - io.write_i64(fi.writer, i64(info.min_value)+i64(i)) + io.write_i64(fi.writer, i64(info.min_value)+i64(i), 10, &fi.n) } - io.write_string(fi.writer, " = ") + io.write_string(fi.writer, " = ", &fi.n) data := uintptr(v.data) + uintptr(i*info.elem_size) fmt_arg(fi, any{rawptr(data), info.elem.id}, verb) - io.write_string(fi.writer, ",\n") + io.write_string(fi.writer, ",\n", &fi.n) } } else { - io.write_byte(fi.writer, '[') - defer io.write_byte(fi.writer, ']') + io.write_byte(fi.writer, '[', &fi.n) + defer io.write_byte(fi.writer, ']', &fi.n) for i in 0.. 0 { io.write_string(fi.writer, ", ") } + if i > 0 { io.write_string(fi.writer, ", ", &fi.n) } idx, ok := stored_enum_value_to_string(info.index, info.min_value, i) if ok { - io.write_byte(fi.writer, '.') - io.write_string(fi.writer, idx) + io.write_byte(fi.writer, '.', &fi.n) + io.write_string(fi.writer, idx, &fi.n) } else { - io.write_i64(fi.writer, i64(info.min_value)+i64(i)) + io.write_i64(fi.writer, i64(info.min_value)+i64(i), 10, &fi.n) } - io.write_string(fi.writer, " = ") + io.write_string(fi.writer, " = ", &fi.n) data := uintptr(v.data) + uintptr(i*info.elem_size) fmt_arg(fi, any{rawptr(data), info.elem.id}, verb) } } + case runtime.Type_Info_Array: + n := info.count + ptr := v.data + if ol, ok := fi.optional_len.?; ok { + n = min(n, ol) + } else if fi.use_nul_termination { + fmt_array_nul_terminated(fi, ptr, n, info.elem_size, info.elem, verb) + return + } + fmt_array(fi, ptr, n, info.elem_size, info.elem, verb) + + case runtime.Type_Info_Slice: + slice := cast(^mem.Raw_Slice)v.data + n := slice.len + ptr := slice.data + if ol, ok := fi.optional_len.?; ok { + n = min(n, ol) + } else if fi.use_nul_termination { + fmt_array_nul_terminated(fi, ptr, n, info.elem_size, info.elem, verb) + return + } + fmt_array(fi, ptr, n, info.elem_size, info.elem, verb) + case runtime.Type_Info_Dynamic_Array: array := cast(^mem.Raw_Dynamic_Array)v.data - if (verb == 's' || verb == 'q') && reflect.is_byte(info.elem) { - s := strings.string_from_ptr((^byte)(array.data), array.len) - fmt_string(fi, s, verb) - } else if verb == 'p' { - fmt_pointer(fi, array.data, 'p') - } else { - fmt_write_array(fi, array.data, array.len, info.elem_size, info.elem.id, verb) + n := array.len + ptr := array.data + if ol, ok := fi.optional_len.?; ok { + n = min(n, ol) + } else if fi.use_nul_termination { + fmt_array_nul_terminated(fi, ptr, n, info.elem_size, info.elem, verb) + return } + fmt_array(fi, ptr, n, info.elem_size, info.elem, verb) case runtime.Type_Info_Simd_Vector: - io.write_byte(fi.writer, '<') - defer io.write_byte(fi.writer, '>') + io.write_byte(fi.writer, '<', &fi.n) + defer io.write_byte(fi.writer, '>', &fi.n) for i in 0.. 0 { io.write_string(fi.writer, ", ") } + if i > 0 { io.write_string(fi.writer, ", ", &fi.n) } data := uintptr(v.data) + uintptr(i*info.elem_size) fmt_arg(fi, any{rawptr(data), info.elem.id}, verb) } - case runtime.Type_Info_Slice: - slice := cast(^mem.Raw_Slice)v.data - if (verb == 's' || verb == 'q') && reflect.is_byte(info.elem) { - s := strings.string_from_ptr((^byte)(slice.data), slice.len) - fmt_string(fi, s, verb) - } else if verb == 'p' { - fmt_pointer(fi, slice.data, 'p') - } else { - fmt_write_array(fi, slice.data, slice.len, info.elem_size, info.elem.id, verb) - } case runtime.Type_Info_Map: if verb != 'v' { fmt_bad_verb(fi, verb) return } - io.write_string(fi.writer, "map[") - defer io.write_byte(fi.writer, ']') + io.write_string(fi.writer, "map[", &fi.n) + defer io.write_byte(fi.writer, ']', &fi.n) + fi.record_level += 1 + defer fi.record_level -= 1 m := (^mem.Raw_Map)(v.data) if m != nil { @@ -1692,14 +2048,14 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { entry_size := ed.elem_size for i in 0.. 0 { io.write_string(fi.writer, ", ") } + if i > 0 { io.write_string(fi.writer, ", ", &fi.n) } data := uintptr(entries.data) + uintptr(i*entry_size) key := data + entry_type.offsets[2] fmt_arg(&Info{writer = fi.writer}, any{rawptr(key), info.key.id}, 'v') - io.write_string(fi.writer, "=") + io.write_string(fi.writer, "=", &fi.n) value := data + entry_type.offsets[3] fmt_arg(fi, any{rawptr(value), info.value.id}, 'v') @@ -1707,168 +2063,10 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { } case runtime.Type_Info_Struct: - if info.is_raw_union { - io.write_string(fi.writer, "(raw_union)") - return - } - - is_soa := info.soa_kind != .None - - io.write_byte(fi.writer, '[' if is_soa else '{') - defer io.write_byte(fi.writer, ']' if is_soa else '}') - - fi.indent += 1; defer fi.indent -= 1 - hash := fi.hash; defer fi.hash = hash - // fi.hash = false; - - - if hash { io.write_byte(fi.writer, '\n') } - - if is_soa { - fi.indent += 1 - defer fi.indent -= 1 - - base_type_name: string - if v, ok := info.soa_base_type.variant.(runtime.Type_Info_Named); ok { - base_type_name = v.name - } - - actual_field_count := len(info.names) - - n := uintptr(info.soa_len) - - if info.soa_kind == .Slice { - actual_field_count = len(info.names)-1 // len - - n = uintptr((^int)(uintptr(v.data) + info.offsets[actual_field_count])^) - - } else if info.soa_kind == .Dynamic { - actual_field_count = len(info.names)-3 // len, cap, allocator - - n = uintptr((^int)(uintptr(v.data) + info.offsets[actual_field_count])^) - } - - - - for index in 0.. 0 { io.write_string(fi.writer, ", ") } - - field_count := -1 - - if !hash && field_count > 0 { io.write_string(fi.writer, ", ") } - - io.write_string(fi.writer, base_type_name) - io.write_byte(fi.writer, '{') - defer io.write_byte(fi.writer, '}') - - for i in 0.. 0 { io.write_string(fi.writer, ", ") } - if hash { - fmt_write_indent(fi) - } - - io.write_string(fi.writer, name) - io.write_string(fi.writer, " = ") - - if info.soa_kind == .Fixed { - t := info.types[i].variant.(runtime.Type_Info_Array).elem - t_size := uintptr(t.size) - if reflect.is_any(t) { - io.write_string(fi.writer, "any{}") - } else { - data := rawptr(uintptr(v.data) + info.offsets[i] + index*t_size) - fmt_arg(fi, any{data, t.id}, 'v') - } - } else { - t := info.types[i].variant.(runtime.Type_Info_Pointer).elem - t_size := uintptr(t.size) - if reflect.is_any(t) { - io.write_string(fi.writer, "any{}") - } else { - field_ptr := (^^byte)(uintptr(v.data) + info.offsets[i])^ - data := rawptr(uintptr(field_ptr) + index*t_size) - fmt_arg(fi, any{data, t.id}, 'v') - } - } - - if hash { io.write_string(fi.writer, ",\n") } - } - } - } else { - field_count := -1 - for name, i in info.names { - field_count += 1 - - if !hash && field_count > 0 { io.write_string(fi.writer, ", ") } - if hash { - fmt_write_indent(fi) - } - - io.write_string(fi.writer, name) - io.write_string(fi.writer, " = ") - - if t := info.types[i]; reflect.is_any(t) { - io.write_string(fi.writer, "any{}") - } else { - data := rawptr(uintptr(v.data) + info.offsets[i]) - fmt_arg(fi, any{data, t.id}, 'v') - } - - if hash { - io.write_string(fi.writer, ",\n") - } - } - } - + fmt_struct(fi, v, verb, info, "") case runtime.Type_Info_Union: - if type_info.size == 0 { - io.write_string(fi.writer, "nil") - return - } - - - if reflect.type_info_union_is_pure_maybe(info) { - if v.data == nil { - io.write_string(fi.writer, "nil") - } else { - id := info.variants[0].id - fmt_arg(fi, any{v.data, id}, verb) - } - return - } - - tag: i64 = -1 - tag_ptr := uintptr(v.data) + info.tag_offset - tag_any := any{rawptr(tag_ptr), info.tag_type.id} - - switch i in tag_any { - case u8: tag = i64(i) - case i8: tag = i64(i) - case u16: tag = i64(i) - case i16: tag = i64(i) - case u32: tag = i64(i) - case i32: tag = i64(i) - case u64: tag = i64(i) - case i64: tag = i - case: panic("Invalid union tag type") - } - assert(tag >= 0) - - if v.data == nil { - io.write_string(fi.writer, "nil") - } else if info.no_nil { - id := info.variants[tag].id - fmt_arg(fi, any{v.data, id}, verb) - } else if tag == 0 { - io.write_string(fi.writer, "nil") - } else { - id := info.variants[tag-1].id - fmt_arg(fi, any{v.data, id}, verb) - } + fmt_union(fi, v, verb, info, type_info.size) case runtime.Type_Info_Enum: fmt_enum(fi, v, verb) @@ -1876,16 +2074,16 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { case runtime.Type_Info_Procedure: ptr := (^rawptr)(v.data)^ if ptr == nil { - io.write_string(fi.writer, "nil") + io.write_string(fi.writer, "nil", &fi.n) } else { - reflect.write_typeid(fi.writer, v.id) - io.write_string(fi.writer, " @ ") + reflect.write_typeid(fi.writer, v.id, &fi.n) + io.write_string(fi.writer, " @ ", &fi.n) fmt_pointer(fi, ptr, 'p') } case runtime.Type_Info_Type_Id: id := (^typeid)(v.data)^ - reflect.write_typeid(fi.writer, id) + reflect.write_typeid(fi.writer, id, &fi.n) case runtime.Type_Info_Bit_Set: fmt_bit_set(fi, v) @@ -1902,18 +2100,21 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { if verb == 'p' { fmt_pointer(fi, ptr, 'p') } else if ptr == nil { - io.write_string(fi.writer, "[]") + io.write_string(fi.writer, "[]", &fi.n) } else { len_ptr := uintptr(v.data) + uintptr(info.base_integer.size) len_any := any{rawptr(len_ptr), info.base_integer.id} len, _ := reflect.as_int(len_any) slice_type := reflect.type_info_base(info.slice).variant.(runtime.Type_Info_Slice) - io.write_byte(fi.writer, '[') - defer io.write_byte(fi.writer, ']') + fi.record_level += 1 + defer fi.record_level -= 1 + + io.write_byte(fi.writer, '[', &fi.n) + defer io.write_byte(fi.writer, ']', &fi.n) for i in 0.. 0 { io.write_string(fi.writer, ", ") } + if i > 0 { io.write_string(fi.writer, ", ", &fi.n) } data := uintptr(ptr) + uintptr(i*slice_type.elem_size) fmt_arg(fi, any{rawptr(data), slice_type.elem.id}, verb) @@ -1921,46 +2122,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { } case runtime.Type_Info_Matrix: - io.write_string(fi.writer, "matrix[") - defer io.write_byte(fi.writer, ']') - - fi.indent += 1 - - if fi.hash { - // Printed as it is written - io.write_byte(fi.writer, '\n') - for row in 0.. 0 { io.write_string(fi.writer, ", ") } - - offset := (row + col*info.elem_stride)*info.elem_size - - data := uintptr(v.data) + uintptr(offset) - fmt_arg(fi, any{rawptr(data), info.elem.id}, verb) - } - io.write_string(fi.writer, ",\n") - } - } else { - // Printed in Row-Major layout to match text layout - for row in 0.. 0 { io.write_string(fi.writer, "; ") } - for col in 0.. 0 { io.write_string(fi.writer, ", ") } - - offset := (row + col*info.elem_stride)*info.elem_size - - data := uintptr(v.data) + uintptr(offset) - fmt_arg(fi, any{rawptr(data), info.elem.id}, verb) - } - } - } - - fi.indent -= 1 - - if fi.hash { - fmt_write_indent(fi) - } + fmt_matrix(fi, v, verb, info) } } @@ -1970,10 +2132,10 @@ fmt_complex :: proc(fi: ^Info, c: complex128, bits: int, verb: rune) { r, i := real(c), imag(c) fmt_float(fi, r, bits/2, verb) if !fi.plus && i >= 0 { - io.write_rune(fi.writer, '+') + io.write_rune(fi.writer, '+', &fi.n) } fmt_float(fi, i, bits/2, verb) - io.write_rune(fi.writer, 'i') + io.write_rune(fi.writer, 'i', &fi.n) case: fmt_bad_verb(fi, verb) @@ -1989,22 +2151,22 @@ fmt_quaternion :: proc(fi: ^Info, q: quaternion256, bits: int, verb: rune) { fmt_float(fi, r, bits/4, verb) if !fi.plus && i >= 0 { - io.write_rune(fi.writer, '+') + io.write_rune(fi.writer, '+', &fi.n) } fmt_float(fi, i, bits/4, verb) - io.write_rune(fi.writer, 'i') + io.write_rune(fi.writer, 'i', &fi.n) if !fi.plus && j >= 0 { - io.write_rune(fi.writer, '+') + io.write_rune(fi.writer, '+', &fi.n) } fmt_float(fi, j, bits/4, verb) - io.write_rune(fi.writer, 'j') + io.write_rune(fi.writer, 'j', &fi.n) if !fi.plus && k >= 0 { - io.write_rune(fi.writer, '+') + io.write_rune(fi.writer, '+', &fi.n) } fmt_float(fi, k, bits/4, verb) - io.write_rune(fi.writer, 'k') + io.write_rune(fi.writer, 'k', &fi.n) case: fmt_bad_verb(fi, verb) @@ -2024,7 +2186,7 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) { switch a in arg { case ^runtime.Type_Info: ti = a } - reflect.write_type(fi.writer, ti) + reflect.write_type(fi.writer, ti, &fi.n) return } @@ -2042,12 +2204,12 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) { custom_types: switch a in arg { case runtime.Source_Code_Location: if fi.hash && verb == 'v' { - io.write_string(fi.writer, a.file_path) - io.write_byte(fi.writer, '(') - io.write_i64(fi.writer, i64(a.line), 10) - io.write_byte(fi.writer, ':') - io.write_i64(fi.writer, i64(a.column), 10) - io.write_byte(fi.writer, ')') + io.write_string(fi.writer, a.file_path, &fi.n) + io.write_byte(fi.writer, '(', &fi.n) + io.write_i64(fi.writer, i64(a.line), 10, &fi.n) + io.write_byte(fi.writer, ':', &fi.n) + io.write_i64(fi.writer, i64(a.column), 10, &fi.n) + io.write_byte(fi.writer, ')', &fi.n) return } } @@ -2099,7 +2261,7 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) { case string: fmt_string(fi, a, verb) case cstring: fmt_cstring(fi, a, verb) - case typeid: reflect.write_typeid(fi.writer, a) + case typeid: reflect.write_typeid(fi.writer, a, &fi.n) case i16le: fmt_int(fi, u64(a), true, 16, verb) case u16le: fmt_int(fi, u64(a), false, 16, verb) diff --git a/core/fmt/fmt_js.odin b/core/fmt/fmt_js.odin index bcd9688a1..7a9876127 100644 --- a/core/fmt/fmt_js.odin +++ b/core/fmt/fmt_js.odin @@ -34,11 +34,16 @@ stderr := io.Writer{ }, } -// print* procedures return the number of bytes written +// print formats using the default print settings and writes to stdout print :: proc(args: ..any, sep := " ") -> int { return wprint(w=stdout, args=args, sep=sep) } +// println formats using the default print settings and writes to stdout println :: proc(args: ..any, sep := " ") -> int { return wprintln(w=stdout, args=args, sep=sep) } +// printf formats according to the specififed format string and writes to stdout printf :: proc(fmt: string, args: ..any) -> int { return wprintf(stdout, fmt, ..args) } +// eprint formats using the default print settings and writes to stderr eprint :: proc(args: ..any, sep := " ") -> int { return wprint(w=stderr, args=args, sep=sep) } +// eprintln formats using the default print settings and writes to stderr eprintln :: proc(args: ..any, sep := " ") -> int { return wprintln(w=stderr, args=args, sep=sep) } +// eprintf formats according to the specififed format string and writes to stderr eprintf :: proc(fmt: string, args: ..any) -> int { return wprintf(stderr, fmt, ..args) } diff --git a/core/fmt/fmt_os.odin b/core/fmt/fmt_os.odin index 7434d939d..f5c8d75bd 100644 --- a/core/fmt/fmt_os.odin +++ b/core/fmt/fmt_os.odin @@ -5,15 +5,18 @@ import "core:runtime" import "core:os" import "core:io" +// fprint formats using the default print settings and writes to fd fprint :: proc(fd: os.Handle, args: ..any, sep := " ") -> int { w := io.to_writer(os.stream_from_handle(fd)) return wprint(w=w, args=args, sep=sep) } +// fprintln formats using the default print settings and writes to fd fprintln :: proc(fd: os.Handle, args: ..any, sep := " ") -> int { w := io.to_writer(os.stream_from_handle(fd)) return wprintln(w=w, args=args, sep=sep) } +// fprintf formats according to the specififed format string and writes to fd fprintf :: proc(fd: os.Handle, fmt: string, args: ..any) -> int { w := io.to_writer(os.stream_from_handle(fd)) return wprintf(w, fmt, ..args) @@ -27,11 +30,16 @@ fprint_typeid :: proc(fd: os.Handle, id: typeid) -> (n: int, err: io.Error) { return wprint_typeid(w, id) } -// print* procedures return the number of bytes written +// print formats using the default print settings and writes to os.stdout print :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stdout, args=args, sep=sep) } +// println formats using the default print settings and writes to os.stdout println :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stdout, args=args, sep=sep) } +// printf formats according to the specififed format string and writes to os.stdout printf :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stdout, fmt, ..args) } +// eprint formats using the default print settings and writes to os.stderr eprint :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stderr, args=args, sep=sep) } +// eprintln formats using the default print settings and writes to os.stderr eprintln :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stderr, args=args, sep=sep) } +// eprintf formats according to the specififed format string and writes to os.stderr eprintf :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stderr, fmt, ..args) } diff --git a/core/hash/crc.odin b/core/hash/crc.odin index b504e92fb..9c0048a0f 100644 --- a/core/hash/crc.odin +++ b/core/hash/crc.odin @@ -1,8 +1,8 @@ package hash @(optimization_mode="speed") -crc64_ecma_182 :: proc(data: []byte, seed := u32(0)) -> u64 #no_bounds_check { - result := u64(seed) +crc64_ecma_182 :: proc(data: []byte, seed := u64(0)) -> (result: u64) #no_bounds_check { + result = seed #no_bounds_check for b in data { result = result<<8 ~ _crc64_table_ecma_182[((result>>56) ~ u64(b)) & 0xff] } diff --git a/core/hash/crc32.odin b/core/hash/crc32.odin index ccb304472..fead4d74f 100644 --- a/core/hash/crc32.odin +++ b/core/hash/crc32.odin @@ -9,8 +9,8 @@ crc32 :: proc(data: []byte, seed := u32(0)) -> u32 #no_bounds_check { length := len(data) for length != 0 && uintptr(buffer) & 7 != 0 { - crc = crc32_table[0][byte(crc) ~ buffer^] ~ (crc >> 8) - buffer = intrinsics.ptr_offset(buffer, 1) + crc = crc32_table[0][byte(crc) ~ buffer[0]] ~ (crc >> 8) + buffer = buffer[1:] length -= 1 } @@ -28,14 +28,14 @@ crc32 :: proc(data: []byte, seed := u32(0)) -> u32 #no_bounds_check { crc32_table[1][buf[6]] ~ crc32_table[0][buf[7]] - buffer = intrinsics.ptr_offset(buffer, 8) + buffer = buffer[8:] length -= 8 } for length != 0 { - crc = crc32_table[0][byte(crc) ~ buffer^] ~ (crc >> 8) - buffer = intrinsics.ptr_offset(buffer, 1) + crc = crc32_table[0][byte(crc) ~ buffer[0]] ~ (crc >> 8) + buffer = buffer[1:] length -= 1 } diff --git a/core/hash/hash.odin b/core/hash/hash.odin index 5044d567a..870d6a638 100644 --- a/core/hash/hash.odin +++ b/core/hash/hash.odin @@ -15,7 +15,7 @@ adler32 :: proc(data: []byte, seed := u32(1)) -> u32 #no_bounds_check { for len(buf) != 0 && uintptr(buffer) & 7 != 0 { a = (a + u64(buf[0])) b = (b + a) - buffer = intrinsics.ptr_offset(buffer, 1) + buffer = buffer[1:] buf = buf[1:] } @@ -130,9 +130,9 @@ murmur32 :: proc(data: []byte, seed := u32(0)) -> u32 { h1: u32 = seed nblocks := len(data)/4 p := raw_data(data) - p1 := mem.ptr_offset(p, 4*nblocks) + p1 := p[4*nblocks:] - for ; p < p1; p = mem.ptr_offset(p, 4) { + for ; p < p1; p = p[4:] { k1 := (cast(^u32)p)^ k1 *= c1_32 @@ -151,7 +151,7 @@ murmur32 :: proc(data: []byte, seed := u32(0)) -> u32 { k1 ~= u32(tail[2]) << 16 fallthrough case 2: - k1 ~= u32(tail[2]) << 8 + k1 ~= u32(tail[1]) << 8 fallthrough case 1: k1 ~= u32(tail[0]) @@ -172,108 +172,114 @@ murmur32 :: proc(data: []byte, seed := u32(0)) -> u32 { return h1 } +// See https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp#L96 @(optimization_mode="speed") -murmur64 :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 { - when size_of(int) == 8 { - m :: 0xc6a4a7935bd1e995 - r :: 47 +murmur64a :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 { + m :: 0xc6a4a7935bd1e995 + r :: 47 - h: u64 = seed ~ (u64(len(data)) * m) - data64 := mem.slice_ptr(cast(^u64)raw_data(data), len(data)/size_of(u64)) + h: u64 = seed ~ (u64(len(data)) * m) + data64 := mem.slice_data_cast([]u64, data) - for _, i in data64 { - k := data64[i] + for _, i in data64 { + k := data64[i] - k *= m - k ~= k>>r - k *= m + k *= m + k ~= k>>r + k *= m - h ~= k - h *= m - } - - switch len(data)&7 { - case 7: h ~= u64(data[6]) << 48; fallthrough - case 6: h ~= u64(data[5]) << 40; fallthrough - case 5: h ~= u64(data[4]) << 32; fallthrough - case 4: h ~= u64(data[3]) << 24; fallthrough - case 3: h ~= u64(data[2]) << 16; fallthrough - case 2: h ~= u64(data[1]) << 8; fallthrough - case 1: - h ~= u64(data[0]) - h *= m - } - - h ~= h>>r + h ~= k h *= m - h ~= h>>r - - return h - } else { - m :: 0x5bd1e995 - r :: 24 - - h1 := u32(seed) ~ u32(len(data)) - h2 := u32(seed) >> 32 - data32 := mem.slice_ptr(cast(^u32)raw_data(data), len(data)/size_of(u32)) - len := len(data) - i := 0 - - for len >= 8 { - k1, k2: u32 - k1 = data32[i]; i += 1 - k1 *= m - k1 ~= k1>>r - k1 *= m - h1 *= m - h1 ~= k1 - len -= 4 - - k2 = data32[i]; i += 1 - k2 *= m - k2 ~= k2>>r - k2 *= m - h2 *= m - h2 ~= k2 - len -= 4 - } - - if len >= 4 { - k1: u32 - k1 = data32[i]; i += 1 - k1 *= m - k1 ~= k1>>r - k1 *= m - h1 *= m - h1 ~= k1 - len -= 4 - } - - // TODO(bill): Fix this - #no_bounds_check data8 := mem.slice_to_bytes(data32[i:])[:3] - switch len { - case 3: - h2 ~= u32(data8[2]) << 16 - fallthrough - case 2: - h2 ~= u32(data8[1]) << 8 - fallthrough - case 1: - h2 ~= u32(data8[0]) - h2 *= m - } - - h1 ~= h2>>18 - h1 *= m - h2 ~= h1>>22 - h2 *= m - h1 ~= h2>>17 - h1 *= m - h2 ~= h1>>19 - h2 *= m - - return u64(h1)<<32 | u64(h2) } + + offset := len(data64) * size_of(u64) + + switch len(data)&7 { + case 7: h ~= u64(data[offset + 6]) << 48; fallthrough + case 6: h ~= u64(data[offset + 5]) << 40; fallthrough + case 5: h ~= u64(data[offset + 4]) << 32; fallthrough + case 4: h ~= u64(data[offset + 3]) << 24; fallthrough + case 3: h ~= u64(data[offset + 2]) << 16; fallthrough + case 2: h ~= u64(data[offset + 1]) << 8; fallthrough + case 1: + h ~= u64(data[offset + 0]) + h *= m + } + + h ~= h>>r + h *= m + h ~= h>>r + + return h +} + +// See https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp#L140 +@(optimization_mode="speed") +murmur64b :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 { + m :: 0x5bd1e995 + r :: 24 + + h1 := u32(seed) ~ u32(len(data)) + h2 := u32(seed) >> 32 + + data32 := mem.slice_ptr(cast(^u32)raw_data(data), len(data)/size_of(u32)) + len := len(data) + i := 0 + + for len >= 8 { + k1, k2: u32 + k1 = data32[i]; i += 1 + k1 *= m + k1 ~= k1>>r + k1 *= m + h1 *= m + h1 ~= k1 + len -= 4 + + k2 = data32[i]; i += 1 + k2 *= m + k2 ~= k2>>r + k2 *= m + h2 *= m + h2 ~= k2 + len -= 4 + } + + if len >= 4 { + k1: u32 + k1 = data32[i]; i += 1 + k1 *= m + k1 ~= k1>>r + k1 *= m + h1 *= m + h1 ~= k1 + len -= 4 + } + + // TODO(bill): Fix this + #no_bounds_check data8 := mem.slice_to_bytes(data32[i:])[:3] + switch len { + case 3: + h2 ~= u32(data8[2]) << 16 + fallthrough + case 2: + h2 ~= u32(data8[1]) << 8 + fallthrough + case 1: + h2 ~= u32(data8[0]) + h2 *= m + } + + h1 ~= h2>>18 + h1 *= m + h2 ~= h1>>22 + h2 *= m + h1 ~= h2>>17 + h1 *= m + h2 ~= h1>>19 + h2 *= m + + return u64(h1)<<32 | u64(h2) } @(optimization_mode="speed") diff --git a/core/hash/xxhash/streaming.odin b/core/hash/xxhash/streaming.odin index d6df1089f..6f630b042 100644 --- a/core/hash/xxhash/streaming.odin +++ b/core/hash/xxhash/streaming.odin @@ -52,9 +52,6 @@ XXH3_128_reset_with_seed :: proc(state: ^XXH3_state, seed: XXH64_hash) -> (err: XXH3_64_reset_with_seed :: XXH3_128_reset_with_seed XXH3_128_update :: proc(state: ^XXH3_state, input: []u8) -> (err: Error) { - if len(input) < XXH3_MIDSIZE_MAX { - return .Error - } return XXH3_update(state, input, XXH3_accumulate_512, XXH3_scramble_accumulator) } XXH3_64_update :: XXH3_128_update @@ -127,6 +124,7 @@ XXH3_create_state :: proc(allocator := context.allocator) -> (res: ^XXH3_state, err = nil if mem_error == nil else .Error XXH3_init_state(state) + XXH3_128_reset(state) return state, nil } @@ -213,7 +211,9 @@ XXH3_update :: #force_inline proc( length := len(input) secret := state.custom_secret[:] if len(state.external_secret) == 0 else state.external_secret[:] - assert(len(input) > 0) + if len(input) == 0 { + return + } state.total_length += u64(length) assert(state.buffered_size <= XXH3_INTERNAL_BUFFER_SIZE) @@ -234,7 +234,9 @@ XXH3_update :: #force_inline proc( */ if state.buffered_size > 0 { load_size := int(XXH3_INTERNAL_BUFFER_SIZE - state.buffered_size) - mem_copy(&state.buffer[state.buffered_size], &input[0], load_size) + + state_ptr := rawptr(uintptr(raw_data(state.buffer[:])) + uintptr(state.buffered_size)) + mem_copy(state_ptr, raw_data(input), load_size) input = input[load_size:] XXH3_consume_stripes( diff --git a/core/hash/xxhash/xxhash_32.odin b/core/hash/xxhash/xxhash_32.odin index e63d998dd..5bc87c2c0 100644 --- a/core/hash/xxhash/xxhash_32.odin +++ b/core/hash/xxhash/xxhash_32.odin @@ -197,6 +197,7 @@ XXH32 :: proc(input: []u8, seed := XXH32_DEFAULT_SEED) -> (digest: XXH32_hash) { */ XXH32_create_state :: proc(allocator := context.allocator) -> (res: ^XXH32_state, err: Error) { state := new(XXH32_state, allocator) + XXH32_reset_state(state) return state, .None if state != nil else .Error } @@ -258,7 +259,7 @@ XXH32_update :: proc(state: ^XXH32_state, input: []u8) -> (err: Error) { v3 := state.v3 v4 := state.v4 - for len(buf) >= 15 { + for len(buf) >= 16 { #no_bounds_check v1 = XXH32_round(v1, XXH32_read32(buf, .Unaligned)); buf = buf[4:] #no_bounds_check v2 = XXH32_round(v2, XXH32_read32(buf, .Unaligned)); buf = buf[4:] #no_bounds_check v3 = XXH32_round(v3, XXH32_read32(buf, .Unaligned)); buf = buf[4:] diff --git a/core/hash/xxhash/xxhash_64.odin b/core/hash/xxhash/xxhash_64.odin index e95842168..9280e9c59 100644 --- a/core/hash/xxhash/xxhash_64.odin +++ b/core/hash/xxhash/xxhash_64.odin @@ -163,6 +163,7 @@ XXH64 :: proc(input: []u8, seed := XXH64_DEFAULT_SEED) -> (digest: XXH64_hash) { */ XXH64_create_state :: proc(allocator := context.allocator) -> (res: ^XXH64_state, err: Error) { state := new(XXH64_state, allocator) + XXH64_reset_state(state) return state, .None if state != nil else .Error } diff --git a/core/image/common.odin b/core/image/common.odin index d72b770d5..baacd64d9 100644 --- a/core/image/common.odin +++ b/core/image/common.odin @@ -15,26 +15,56 @@ import "core:mem" import "core:compress" import "core:runtime" +/* + 67_108_864 pixels max by default. + + For QOI, the Worst case scenario means all pixels will be encoded as RGBA literals, costing 5 bytes each. + This caps memory usage at 320 MiB. + + The tunable is limited to 4_294_836_225 pixels maximum, or 4 GiB per 8-bit channel. + It is not advised to tune it this large. + + The 64 Megapixel default is considered to be a decent upper bound you won't run into in practice, + except in very specific circumstances. + +*/ +MAX_DIMENSIONS :: min(#config(MAX_DIMENSIONS, 8192 * 8192), 65535 * 65535) + +// Color +RGB_Pixel :: [3]u8 +RGBA_Pixel :: [4]u8 +RGB_Pixel_16 :: [3]u16 +RGBA_Pixel_16 :: [4]u16 +// Grayscale +G_Pixel :: [1]u8 +GA_Pixel :: [2]u8 +G_Pixel_16 :: [1]u16 +GA_Pixel_16 :: [2]u16 + Image :: struct { width: int, height: int, channels: int, - depth: int, + depth: int, // Channel depth in bits, typically 8 or 16 pixels: bytes.Buffer, /* Some image loaders/writers can return/take an optional background color. For convenience, we return them as u16 so we don't need to switch on the type in our viewer, and can just test against nil. */ - background: Maybe([3]u16), - + background: Maybe(RGB_Pixel_16), metadata: Image_Metadata, + which: Which_File_Type, } -Image_Metadata :: union { +Image_Metadata :: union #shared_nil { + ^Netpbm_Info, ^PNG_Info, + ^QOI_Info, } + + /* IMPORTANT: `.do_not_expand_*` options currently skip handling of the `alpha_*` options, therefore Gray+Alpha will be returned as such even if you add `.alpha_drop_if_present`, @@ -46,13 +76,13 @@ Image_Metadata :: union { /* Image_Option: `.info` - This option behaves as `.return_ihdr` and `.do_not_decompress_image` and can be used + This option behaves as `.return_metadata` and `.do_not_decompress_image` and can be used to gather an image's dimensions and color information. `.return_header` - Fill out img.sidecar.header with the image's format-specific header struct. + Fill out img.metadata.header with the image's format-specific header struct. If we only care about the image specs, we can set `.return_header` + - `.do_not_decompress_image`, or `.info`, which works as if both of these were set. + `.do_not_decompress_image`, or `.info`. `.return_metadata` Returns all chunks not needed to decode the data. @@ -88,7 +118,7 @@ Image_Option: `.alpha_premultiply` If the image has an alpha channel, returns image data as follows: - RGB *= A, Gray = Gray *= A + RGB *= A, Gray = Gray *= A `.blend_background` If a bKGD chunk is present in a PNG, we normally just set `img.background` @@ -103,24 +133,31 @@ Image_Option: */ Option :: enum { + // LOAD OPTIONS info = 0, do_not_decompress_image, return_header, return_metadata, - alpha_add_if_missing, - alpha_drop_if_present, - alpha_premultiply, - blend_background, + alpha_add_if_missing, // Ignored for QOI. Always returns RGBA8. + alpha_drop_if_present, // Unimplemented for QOI. Returns error. + alpha_premultiply, // Unimplemented for QOI. Returns error. + blend_background, // Ignored for non-PNG formats + // Unimplemented do_not_expand_grayscale, do_not_expand_indexed, do_not_expand_channels, + + // SAVE OPTIONS + qoi_all_channels_linear, // QOI, informative only. If not set, defaults to sRGB with linear alpha. } Options :: distinct bit_set[Option] -Error :: union { +Error :: union #shared_nil { General_Image_Error, + Netpbm_Error, PNG_Error, + QOI_Error, compress.Error, compress.General_Error, @@ -131,13 +168,76 @@ Error :: union { General_Image_Error :: enum { None = 0, - Invalid_Image_Dimensions, + // File I/O + Unable_To_Read_File, + Unable_To_Write_File, + + // Invalid + Unsupported_Format, + Invalid_Signature, + Invalid_Input_Image, Image_Dimensions_Too_Large, + Invalid_Image_Dimensions, + Invalid_Number_Of_Channels, Image_Does_Not_Adhere_to_Spec, + Invalid_Image_Depth, + Invalid_Bit_Depth, + Invalid_Color_Space, + + // More data than pixels to decode into, for example. + Corrupt, + + // Output buffer is the wrong size + Invalid_Output, + + // Allocation + Unable_To_Allocate_Or_Resize, } +/* + Netpbm-specific definitions +*/ +Netpbm_Format :: enum { + P1, P2, P3, P4, P5, P6, P7, Pf, PF, +} + +Netpbm_Header :: struct { + format: Netpbm_Format, + width: int, + height: int, + channels: int, + depth: int, + maxval: int, + tupltype: string, + scale: f32, + little_endian: bool, +} + +Netpbm_Info :: struct { + header: Netpbm_Header, +} + +Netpbm_Error :: enum { + None = 0, + + // reading + Invalid_Header_Token_Character, + Incomplete_Header, + Invalid_Header_Value, + Duplicate_Header_Field, + Buffer_Too_Small, + Invalid_Buffer_ASCII_Token, + Invalid_Buffer_Value, + + // writing + Invalid_Format, +} + +/* + PNG-specific definitions +*/ PNG_Error :: enum { - Invalid_PNG_Signature, + None = 0, IHDR_Not_First_Chunk, IHDR_Corrupt, IDAT_Missing, @@ -146,7 +246,9 @@ PNG_Error :: enum { IDAT_Size_Too_Large, PLTE_Encountered_Unexpectedly, PLTE_Invalid_Length, + PLTE_Missing, TRNS_Encountered_Unexpectedly, + TNRS_Invalid_Length, BKGD_Invalid_Length, Unknown_Color_Type, Invalid_Color_Bit_Depth_Combo, @@ -157,9 +259,6 @@ PNG_Error :: enum { Invalid_Chunk_Length, } -/* - PNG-specific structs -*/ PNG_Info :: struct { header: PNG_IHDR, chunks: [dynamic]PNG_Chunk, @@ -222,7 +321,7 @@ PNG_Chunk_Type :: enum u32be { */ iDOT = 'i' << 24 | 'D' << 16 | 'O' << 8 | 'T', - CbGI = 'C' << 24 | 'b' << 16 | 'H' << 8 | 'I', + CgBI = 'C' << 24 | 'g' << 16 | 'B' << 8 | 'I', } PNG_IHDR :: struct #packed { @@ -250,16 +349,53 @@ PNG_Interlace_Method :: enum u8 { } /* - Functions to help with image buffer calculations + QOI-specific definitions */ +QOI_Error :: enum { + None = 0, + Missing_Or_Corrupt_Trailer, // Image seemed to have decoded okay, but trailer is missing or corrupt. +} + +QOI_Magic :: u32be(0x716f6966) // "qoif" + +QOI_Color_Space :: enum u8 { + sRGB = 0, + Linear = 1, +} + +QOI_Header :: struct #packed { + magic: u32be, + width: u32be, + height: u32be, + channels: u8, + color_space: QOI_Color_Space, +} +#assert(size_of(QOI_Header) == 14) + +QOI_Info :: struct { + header: QOI_Header, +} + +TGA_Header :: struct #packed { + id_length: u8, + color_map_type: u8, + data_type_code: u8, + color_map_origin: u16le, + color_map_length: u16le, + color_map_depth: u8, + origin: [2]u16le, + dimensions: [2]u16le, + bits_per_pixel: u8, + image_descriptor: u8, +} +#assert(size_of(TGA_Header) == 18) + +// Function to help with image buffer calculations compute_buffer_size :: proc(width, height, channels, depth: int, extra_row_bytes := int(0)) -> (size: int) { size = ((((channels * width * depth) + 7) >> 3) + extra_row_bytes) * height return } -/* - For when you have an RGB(A) image, but want a particular channel. -*/ Channel :: enum u8 { R = 1, G = 2, @@ -267,7 +403,13 @@ Channel :: enum u8 { A = 4, } +// When you have an RGB(A) image, but want a particular channel. return_single_channel :: proc(img: ^Image, channel: Channel) -> (res: ^Image, ok: bool) { + // Were we actually given a valid image? + if img == nil { + return nil, false + } + ok = false t: bytes.Buffer @@ -297,7 +439,7 @@ return_single_channel :: proc(img: ^Image, channel: Channel) -> (res: ^Image, ok o = o[1:] } case 16: - buffer_size := compute_buffer_size(img.width, img.height, 2, 8) + buffer_size := compute_buffer_size(img.width, img.height, 1, 16) t = bytes.Buffer{} resize(&t.buf, buffer_size) @@ -325,3 +467,724 @@ return_single_channel :: proc(img: ^Image, channel: Channel) -> (res: ^Image, ok return res, true } + +// Does the image have 1 or 2 channels, a valid bit depth (8 or 16), +// Is the pointer valid, are the dimenions valid? +is_valid_grayscale_image :: proc(img: ^Image) -> (ok: bool) { + // Were we actually given a valid image? + if img == nil { + return false + } + + // Are we a Gray or Gray + Alpha image? + if img.channels != 1 && img.channels != 2 { + return false + } + + // Do we have an acceptable bit depth? + if img.depth != 8 && img.depth != 16 { + return false + } + + // This returns 0 if any of the inputs is zero. + bytes_expected := compute_buffer_size(img.width, img.height, img.channels, img.depth) + + // If the dimenions are invalid or the buffer size doesn't match the image characteristics, bail. + if bytes_expected == 0 || bytes_expected != len(img.pixels.buf) || img.width * img.height > MAX_DIMENSIONS { + return false + } + + return true +} + +// Does the image have 3 or 4 channels, a valid bit depth (8 or 16), +// Is the pointer valid, are the dimenions valid? +is_valid_color_image :: proc(img: ^Image) -> (ok: bool) { + // Were we actually given a valid image? + if img == nil { + return false + } + + // Are we an RGB or RGBA image? + if img.channels != 3 && img.channels != 4 { + return false + } + + // Do we have an acceptable bit depth? + if img.depth != 8 && img.depth != 16 { + return false + } + + // This returns 0 if any of the inputs is zero. + bytes_expected := compute_buffer_size(img.width, img.height, img.channels, img.depth) + + // If the dimenions are invalid or the buffer size doesn't match the image characteristics, bail. + if bytes_expected == 0 || bytes_expected != len(img.pixels.buf) || img.width * img.height > MAX_DIMENSIONS { + return false + } + + return true +} + +// Does the image have 1..4 channels, a valid bit depth (8 or 16), +// Is the pointer valid, are the dimenions valid? +is_valid_image :: proc(img: ^Image) -> (ok: bool) { + // Were we actually given a valid image? + if img == nil { + return false + } + + return is_valid_color_image(img) || is_valid_grayscale_image(img) +} + +Alpha_Key :: union { + GA_Pixel, + RGBA_Pixel, + GA_Pixel_16, + RGBA_Pixel_16, +} + +/* + Add alpha channel if missing, in-place. + + Expects 1..4 channels (Gray, Gray + Alpha, RGB, RGBA). + Any other number of channels will be considered an error, returning `false` without modifying the image. + If the input image already has an alpha channel, it'll return `true` early (without considering optional keyed alpha). + + If an image doesn't already have an alpha channel: + If the optional `alpha_key` is provided, it will be resolved as follows: + - For RGB, if pix = key.rgb -> pix = {0, 0, 0, key.a} + - For Gray, if pix = key.r -> pix = {0, key.g} + Otherwise, an opaque alpha channel will be added. +*/ +alpha_add_if_missing :: proc(img: ^Image, alpha_key := Alpha_Key{}, allocator := context.allocator) -> (ok: bool) { + context.allocator = allocator + + if !is_valid_image(img) { + return false + } + + // We should now have a valid Image with 1..4 channels. Do we already have alpha? + if img.channels == 2 || img.channels == 4 { + // We're done. + return true + } + + channels := img.channels + 1 + bytes_wanted := compute_buffer_size(img.width, img.height, channels, img.depth) + + buf := bytes.Buffer{} + + // Can we allocate the return buffer? + if !resize(&buf.buf, bytes_wanted) { + delete(buf.buf) + return false + } + + switch img.depth { + case 8: + switch channels { + case 2: + // Turn Gray into Gray + Alpha + inp := mem.slice_data_cast([]G_Pixel, img.pixels.buf[:]) + out := mem.slice_data_cast([]GA_Pixel, buf.buf[:]) + + if key, key_ok := alpha_key.(GA_Pixel); key_ok { + // We have keyed alpha. + o: GA_Pixel + for p in inp { + if p == key.r { + o = GA_Pixel{0, key.g} + } else { + o = GA_Pixel{p.r, 255} + } + out[0] = o + out = out[1:] + } + } else { + // No keyed alpha, just make all pixels opaque. + o := GA_Pixel{0, 255} + for p in inp { + o.r = p.r + out[0] = o + out = out[1:] + } + } + + case 4: + // Turn RGB into RGBA + inp := mem.slice_data_cast([]RGB_Pixel, img.pixels.buf[:]) + out := mem.slice_data_cast([]RGBA_Pixel, buf.buf[:]) + + if key, key_ok := alpha_key.(RGBA_Pixel); key_ok { + // We have keyed alpha. + o: RGBA_Pixel + for p in inp { + if p == key.rgb { + o = RGBA_Pixel{0, 0, 0, key.a} + } else { + o = RGBA_Pixel{p.r, p.g, p.b, 255} + } + out[0] = o + out = out[1:] + } + } else { + // No keyed alpha, just make all pixels opaque. + o := RGBA_Pixel{0, 0, 0, 255} + for p in inp { + o.rgb = p + out[0] = o + out = out[1:] + } + } + case: + // We shouldn't get here. + unreachable() + } + case 16: + switch channels { + case 2: + // Turn Gray into Gray + Alpha + inp := mem.slice_data_cast([]G_Pixel_16, img.pixels.buf[:]) + out := mem.slice_data_cast([]GA_Pixel_16, buf.buf[:]) + + if key, key_ok := alpha_key.(GA_Pixel_16); key_ok { + // We have keyed alpha. + o: GA_Pixel_16 + for p in inp { + if p == key.r { + o = GA_Pixel_16{0, key.g} + } else { + o = GA_Pixel_16{p.r, 65535} + } + out[0] = o + out = out[1:] + } + } else { + // No keyed alpha, just make all pixels opaque. + o := GA_Pixel_16{0, 65535} + for p in inp { + o.r = p.r + out[0] = o + out = out[1:] + } + } + + case 4: + // Turn RGB into RGBA + inp := mem.slice_data_cast([]RGB_Pixel_16, img.pixels.buf[:]) + out := mem.slice_data_cast([]RGBA_Pixel_16, buf.buf[:]) + + if key, key_ok := alpha_key.(RGBA_Pixel_16); key_ok { + // We have keyed alpha. + o: RGBA_Pixel_16 + for p in inp { + if p == key.rgb { + o = RGBA_Pixel_16{0, 0, 0, key.a} + } else { + o = RGBA_Pixel_16{p.r, p.g, p.b, 65535} + } + out[0] = o + out = out[1:] + } + } else { + // No keyed alpha, just make all pixels opaque. + o := RGBA_Pixel_16{0, 0, 0, 65535} + for p in inp { + o.rgb = p + out[0] = o + out = out[1:] + } + } + case: + // We shouldn't get here. + unreachable() + } + } + + // If we got here, that means we've now got a buffer with the alpha channel added. + // Destroy the old pixel buffer and replace it with the new one, and update the channel count. + bytes.buffer_destroy(&img.pixels) + img.pixels = buf + img.channels = channels + return true +} +alpha_apply_keyed_alpha :: alpha_add_if_missing + +/* + Drop alpha channel if present, in-place. + + Expects 1..4 channels (Gray, Gray + Alpha, RGB, RGBA). + Any other number of channels will be considered an error, returning `false` without modifying the image. + + Of the `options`, the following are considered: + `.alpha_premultiply` + If the image has an alpha channel, returns image data as follows: + RGB *= A, Gray = Gray *= A + + `.blend_background` + If `img.background` is set, it'll be blended in like this: + RGB = (1 - A) * Background + A * RGB + + If an image has 1 (Gray) or 3 (RGB) channels, it'll return early without modifying the image, + with one exception: `alpha_key` and `img.background` are present, and `.blend_background` is set. + + In this case a keyed alpha pixel will be replaced with the background color. +*/ +alpha_drop_if_present :: proc(img: ^Image, options := Options{}, alpha_key := Alpha_Key{}, allocator := context.allocator) -> (ok: bool) { + context.allocator = allocator + + if !is_valid_image(img) { + return false + } + + // Do we have a background to blend? + will_it_blend := false + switch v in img.background { + case RGB_Pixel_16: will_it_blend = true if .blend_background in options else false + } + + // Do we have keyed alpha? + keyed := false + switch v in alpha_key { + case GA_Pixel: keyed = true if img.channels == 1 && img.depth == 8 else false + case RGBA_Pixel: keyed = true if img.channels == 3 && img.depth == 8 else false + case GA_Pixel_16: keyed = true if img.channels == 1 && img.depth == 16 else false + case RGBA_Pixel_16: keyed = true if img.channels == 3 && img.depth == 16 else false + } + + // We should now have a valid Image with 1..4 channels. Do we have alpha? + if img.channels == 1 || img.channels == 3 { + if !(will_it_blend && keyed) { + // We're done + return true + } + } + + // # of destination channels + channels := 1 if img.channels < 3 else 3 + + bytes_wanted := compute_buffer_size(img.width, img.height, channels, img.depth) + buf := bytes.Buffer{} + + // Can we allocate the return buffer? + if !resize(&buf.buf, bytes_wanted) { + delete(buf.buf) + return false + } + + switch img.depth { + case 8: + switch img.channels { + case 1: // Gray to Gray, but we should have keyed alpha + background. + inp := mem.slice_data_cast([]G_Pixel, img.pixels.buf[:]) + out := mem.slice_data_cast([]G_Pixel, buf.buf[:]) + + key := alpha_key.(GA_Pixel).r + bg := G_Pixel{} + if temp_bg, temp_bg_ok := img.background.(RGB_Pixel_16); temp_bg_ok { + // Background is RGB 16-bit, take just the red channel's topmost byte. + bg = u8(temp_bg.r >> 8) + } + + for p in inp { + out[0] = bg if p == key else p + out = out[1:] + } + + case 2: // Gray + Alpha to Gray, no keyed alpha but we can have a background. + inp := mem.slice_data_cast([]GA_Pixel, img.pixels.buf[:]) + out := mem.slice_data_cast([]G_Pixel, buf.buf[:]) + + if will_it_blend { + // Blend with background "color", then drop alpha. + bg := f32(0.0) + if temp_bg, temp_bg_ok := img.background.(RGB_Pixel_16); temp_bg_ok { + // Background is RGB 16-bit, take just the red channel's topmost byte. + bg = f32(temp_bg.r >> 8) + } + + for p in inp { + a := f32(p.g) / 255.0 + c := ((1.0 - a) * bg + a * f32(p.r)) + out[0] = u8(c) + out = out[1:] + } + + } else if .alpha_premultiply in options { + // Premultiply component with alpha, then drop alpha. + for p in inp { + a := f32(p.g) / 255.0 + c := f32(p.r) * a + out[0] = u8(c) + out = out[1:] + } + } else { + // Just drop alpha on the floor. + for p in inp { + out[0] = p.r + out = out[1:] + } + } + + case 3: // RGB to RGB, but we should have keyed alpha + background. + inp := mem.slice_data_cast([]RGB_Pixel, img.pixels.buf[:]) + out := mem.slice_data_cast([]RGB_Pixel, buf.buf[:]) + + key := alpha_key.(RGBA_Pixel) + bg := RGB_Pixel{} + if temp_bg, temp_bg_ok := img.background.(RGB_Pixel_16); temp_bg_ok { + // Background is RGB 16-bit, squash down to 8 bits. + bg = {u8(temp_bg.r >> 8), u8(temp_bg.g >> 8), u8(temp_bg.b >> 8)} + } + + for p in inp { + out[0] = bg if p == key.rgb else p + out = out[1:] + } + + case 4: // RGBA to RGB, no keyed alpha but we can have a background or need to premultiply. + inp := mem.slice_data_cast([]RGBA_Pixel, img.pixels.buf[:]) + out := mem.slice_data_cast([]RGB_Pixel, buf.buf[:]) + + if will_it_blend { + // Blend with background "color", then drop alpha. + bg := [3]f32{} + if temp_bg, temp_bg_ok := img.background.(RGB_Pixel_16); temp_bg_ok { + // Background is RGB 16-bit, take just the red channel's topmost byte. + bg = {f32(temp_bg.r >> 8), f32(temp_bg.g >> 8), f32(temp_bg.b >> 8)} + } + + for p in inp { + a := f32(p.a) / 255.0 + rgb := [3]f32{f32(p.r), f32(p.g), f32(p.b)} + c := ((1.0 - a) * bg + a * rgb) + + out[0] = {u8(c.r), u8(c.g), u8(c.b)} + out = out[1:] + } + + } else if .alpha_premultiply in options { + // Premultiply component with alpha, then drop alpha. + for p in inp { + a := f32(p.a) / 255.0 + rgb := [3]f32{f32(p.r), f32(p.g), f32(p.b)} + c := rgb * a + + out[0] = {u8(c.r), u8(c.g), u8(c.b)} + out = out[1:] + } + } else { + // Just drop alpha on the floor. + for p in inp { + out[0] = p.rgb + out = out[1:] + } + } + } + + case 16: + switch img.channels { + case 1: // Gray to Gray, but we should have keyed alpha + background. + inp := mem.slice_data_cast([]G_Pixel_16, img.pixels.buf[:]) + out := mem.slice_data_cast([]G_Pixel_16, buf.buf[:]) + + key := alpha_key.(GA_Pixel_16).r + bg := G_Pixel_16{} + if temp_bg, temp_bg_ok := img.background.(RGB_Pixel_16); temp_bg_ok { + // Background is RGB 16-bit, take just the red channel. + bg = temp_bg.r + } + + for p in inp { + out[0] = bg if p == key else p + out = out[1:] + } + + case 2: // Gray + Alpha to Gray, no keyed alpha but we can have a background. + inp := mem.slice_data_cast([]GA_Pixel_16, img.pixels.buf[:]) + out := mem.slice_data_cast([]G_Pixel_16, buf.buf[:]) + + if will_it_blend { + // Blend with background "color", then drop alpha. + bg := f32(0.0) + if temp_bg, temp_bg_ok := img.background.(RGB_Pixel_16); temp_bg_ok { + // Background is RGB 16-bit, take just the red channel. + bg = f32(temp_bg.r) + } + + for p in inp { + a := f32(p.g) / 65535.0 + c := ((1.0 - a) * bg + a * f32(p.r)) + out[0] = u16(c) + out = out[1:] + } + + } else if .alpha_premultiply in options { + // Premultiply component with alpha, then drop alpha. + for p in inp { + a := f32(p.g) / 65535.0 + c := f32(p.r) * a + out[0] = u16(c) + out = out[1:] + } + } else { + // Just drop alpha on the floor. + for p in inp { + out[0] = p.r + out = out[1:] + } + } + + case 3: // RGB to RGB, but we should have keyed alpha + background. + inp := mem.slice_data_cast([]RGB_Pixel_16, img.pixels.buf[:]) + out := mem.slice_data_cast([]RGB_Pixel_16, buf.buf[:]) + + key := alpha_key.(RGBA_Pixel_16) + bg := img.background.(RGB_Pixel_16) + + for p in inp { + out[0] = bg if p == key.rgb else p + out = out[1:] + } + + case 4: // RGBA to RGB, no keyed alpha but we can have a background or need to premultiply. + inp := mem.slice_data_cast([]RGBA_Pixel_16, img.pixels.buf[:]) + out := mem.slice_data_cast([]RGB_Pixel_16, buf.buf[:]) + + if will_it_blend { + // Blend with background "color", then drop alpha. + bg := [3]f32{} + if temp_bg, temp_bg_ok := img.background.(RGB_Pixel_16); temp_bg_ok { + // Background is RGB 16-bit, convert to [3]f32 to blend. + bg = {f32(temp_bg.r), f32(temp_bg.g), f32(temp_bg.b)} + } + + for p in inp { + a := f32(p.a) / 65535.0 + rgb := [3]f32{f32(p.r), f32(p.g), f32(p.b)} + c := ((1.0 - a) * bg + a * rgb) + + out[0] = {u16(c.r), u16(c.g), u16(c.b)} + out = out[1:] + } + + } else if .alpha_premultiply in options { + // Premultiply component with alpha, then drop alpha. + for p in inp { + a := f32(p.a) / 65535.0 + rgb := [3]f32{f32(p.r), f32(p.g), f32(p.b)} + c := rgb * a + + out[0] = {u16(c.r), u16(c.g), u16(c.b)} + out = out[1:] + } + } else { + // Just drop alpha on the floor. + for p in inp { + out[0] = p.rgb + out = out[1:] + } + } + } + + case: + unreachable() + } + + // If we got here, that means we've now got a buffer with the alpha channel dropped. + // Destroy the old pixel buffer and replace it with the new one, and update the channel count. + bytes.buffer_destroy(&img.pixels) + img.pixels = buf + img.channels = channels + return true +} + +// Apply palette to 8-bit single-channel image and return an 8-bit RGB image, in-place. +// If the image given is not a valid 8-bit single channel image, the procedure will return `false` early. +apply_palette_rgb :: proc(img: ^Image, palette: [256]RGB_Pixel, allocator := context.allocator) -> (ok: bool) { + context.allocator = allocator + + if img == nil || img.channels != 1 || img.depth != 8 { + return false + } + + bytes_expected := compute_buffer_size(img.width, img.height, 1, 8) + if bytes_expected == 0 || bytes_expected != len(img.pixels.buf) || img.width * img.height > MAX_DIMENSIONS { + return false + } + + // Can we allocate the return buffer? + buf := bytes.Buffer{} + bytes_wanted := compute_buffer_size(img.width, img.height, 3, 8) + if !resize(&buf.buf, bytes_wanted) { + delete(buf.buf) + return false + } + + out := mem.slice_data_cast([]RGB_Pixel, buf.buf[:]) + + // Apply the palette + for p, i in img.pixels.buf { + out[i] = palette[p] + } + + // If we got here, that means we've now got a buffer with the alpha channel dropped. + // Destroy the old pixel buffer and replace it with the new one, and update the channel count. + bytes.buffer_destroy(&img.pixels) + img.pixels = buf + img.channels = 3 + return true +} + +// Apply palette to 8-bit single-channel image and return an 8-bit RGBA image, in-place. +// If the image given is not a valid 8-bit single channel image, the procedure will return `false` early. +apply_palette_rgba :: proc(img: ^Image, palette: [256]RGBA_Pixel, allocator := context.allocator) -> (ok: bool) { + context.allocator = allocator + + if img == nil || img.channels != 1 || img.depth != 8 { + return false + } + + bytes_expected := compute_buffer_size(img.width, img.height, 1, 8) + if bytes_expected == 0 || bytes_expected != len(img.pixels.buf) || img.width * img.height > MAX_DIMENSIONS { + return false + } + + // Can we allocate the return buffer? + buf := bytes.Buffer{} + bytes_wanted := compute_buffer_size(img.width, img.height, 4, 8) + if !resize(&buf.buf, bytes_wanted) { + delete(buf.buf) + return false + } + + out := mem.slice_data_cast([]RGBA_Pixel, buf.buf[:]) + + // Apply the palette + for p, i in img.pixels.buf { + out[i] = palette[p] + } + + // If we got here, that means we've now got a buffer with the alpha channel dropped. + // Destroy the old pixel buffer and replace it with the new one, and update the channel count. + bytes.buffer_destroy(&img.pixels) + img.pixels = buf + img.channels = 4 + return true +} +apply_palette :: proc{apply_palette_rgb, apply_palette_rgba} + + +// Replicates grayscale values into RGB(A) 8- or 16-bit images as appropriate. +// Returns early with `false` if already an RGB(A) image. +expand_grayscale :: proc(img: ^Image, allocator := context.allocator) -> (ok: bool) { + context.allocator = allocator + + if !is_valid_grayscale_image(img) { + return false + } + + // We should have 1 or 2 channels of 8- or 16 bits now. We need to turn that into 3 or 4. + // Can we allocate the return buffer? + buf := bytes.Buffer{} + bytes_wanted := compute_buffer_size(img.width, img.height, img.channels + 2, img.depth) + if !resize(&buf.buf, bytes_wanted) { + delete(buf.buf) + return false + } + + switch img.depth { + case 8: + switch img.channels { + case 1: // Turn Gray into RGB + out := mem.slice_data_cast([]RGB_Pixel, buf.buf[:]) + + for p in img.pixels.buf { + out[0] = p // Broadcast gray value into RGB components. + out = out[1:] + } + + case 2: // Turn Gray + Alpha into RGBA + inp := mem.slice_data_cast([]GA_Pixel, img.pixels.buf[:]) + out := mem.slice_data_cast([]RGBA_Pixel, buf.buf[:]) + + for p in inp { + out[0].rgb = p.r // Gray component. + out[0].a = p.g // Alpha component. + } + + case: + unreachable() + } + + case 16: + switch img.channels { + case 1: // Turn Gray into RGB + inp := mem.slice_data_cast([]u16, img.pixels.buf[:]) + out := mem.slice_data_cast([]RGB_Pixel_16, buf.buf[:]) + + for p in inp { + out[0] = p // Broadcast gray value into RGB components. + out = out[1:] + } + + case 2: // Turn Gray + Alpha into RGBA + inp := mem.slice_data_cast([]GA_Pixel_16, img.pixels.buf[:]) + out := mem.slice_data_cast([]RGBA_Pixel_16, buf.buf[:]) + + for p in inp { + out[0].rgb = p.r // Gray component. + out[0].a = p.g // Alpha component. + } + + case: + unreachable() + } + + case: + unreachable() + } + + + // If we got here, that means we've now got a buffer with the extra alpha channel. + // Destroy the old pixel buffer and replace it with the new one, and update the channel count. + bytes.buffer_destroy(&img.pixels) + img.pixels = buf + img.channels += 2 + return true +} + +/* + Helper functions to read and write data from/to a Context, etc. +*/ +@(optimization_mode="speed") +read_data :: proc(z: $C, $T: typeid) -> (res: T, err: compress.General_Error) { + if r, e := compress.read_data(z, T); e != .None { + return {}, .Stream_Too_Short + } else { + return r, nil + } +} + +@(optimization_mode="speed") +read_u8 :: proc(z: $C) -> (res: u8, err: compress.General_Error) { + if r, e := compress.read_u8(z); e != .None { + return {}, .Stream_Too_Short + } else { + return r, nil + } +} + +write_bytes :: proc(buf: ^bytes.Buffer, data: []u8) -> (err: compress.General_Error) { + if len(data) == 0 { + return nil + } else if len(data) == 1 { + if bytes.buffer_write_byte(buf, data[0]) != nil { + return .Resize_Failed + } + } else if n, _ := bytes.buffer_write(buf, data); n != len(data) { + return .Resize_Failed + } + return nil +} diff --git a/core/image/general_loader.odin b/core/image/general_loader.odin new file mode 100644 index 000000000..36629c39e --- /dev/null +++ b/core/image/general_loader.odin @@ -0,0 +1,61 @@ +package image + +import "core:mem" +import "core:os" +import "core:bytes" + +Loader_Proc :: #type proc(data: []byte, options: Options, allocator: mem.Allocator) -> (img: ^Image, err: Error) +Destroy_Proc :: #type proc(img: ^Image) + +@(private) +_internal_loaders: [Which_File_Type]Loader_Proc +_internal_destroyers: [Which_File_Type]Destroy_Proc + +register :: proc(kind: Which_File_Type, loader: Loader_Proc, destroyer: Destroy_Proc) { + assert(loader != nil) + assert(destroyer != nil) + assert(_internal_loaders[kind] == nil) + _internal_loaders[kind] = loader + + assert(_internal_destroyers[kind] == nil) + _internal_destroyers[kind] = destroyer +} + +load :: proc{ + load_from_bytes, + load_from_file, +} + +load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) { + loader := _internal_loaders[which(data)] + if loader == nil { + return nil, .Unsupported_Format + } + return loader(data, options, allocator) +} + + +load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) { + data, ok := os.read_entire_file(filename, allocator) + defer delete(data, allocator) + if ok { + return load_from_bytes(data, options, allocator) + } else { + return nil, .Unable_To_Read_File + } +} + +destroy :: proc(img: ^Image, allocator := context.allocator) { + if img == nil { + return + } + context.allocator = allocator + destroyer := _internal_destroyers[img.which] + if destroyer != nil { + destroyer(img) + } else { + assert(img.metadata == nil) + bytes.buffer_destroy(&img.pixels) + free(img) + } +} \ No newline at end of file diff --git a/core/image/netpbm/doc.odin b/core/image/netpbm/doc.odin new file mode 100644 index 000000000..1b5b46856 --- /dev/null +++ b/core/image/netpbm/doc.odin @@ -0,0 +1,33 @@ +/* +Formats: + PBM (P1, P4): Portable Bit Map, stores black and white images (1 channel) + PGM (P2, P5): Portable Gray Map, stores greyscale images (1 channel, 1 or 2 bytes per value) + PPM (P3, P6): Portable Pixel Map, stores colour images (3 channel, 1 or 2 bytes per value) + PAM (P7 ): Portable Arbitrary Map, stores arbitrary channel images (1 or 2 bytes per value) + PFM (Pf, PF): Portable Float Map, stores floating-point images (Pf: 1 channel, PF: 3 channel) + +Reading: + All formats fill out header fields `format`, `width`, `height`, `channels`, `depth` + Specific formats use more fields + PGM, PPM, and PAM set `maxval` (maximum of 65535) + PAM sets `tupltype` if there is one, and can set `channels` to any value (not just 1 or 3) + PFM sets `scale` (float equivalent of `maxval`) and `little_endian` (endianness of stored floats) + Currently doesn't support reading multiple images from one binary-format file + +Writing: + You can use your own `Netpbm_Info` struct to control how images are written + All formats require the header field `format` to be specified + Additional header fields are required for specific formats + PGM, PPM, and PAM require `maxval` (maximum of 65535) + PAM also uses `tupltype`, though it may be left as default (empty or nil string) + PFM requires `scale`, and optionally `little_endian` + +Some syntax differences from the specifications: + `channels` stores the number of values per pixel, what the PAM specification calls `depth` + `depth` instead is the number of bits for a single value (32 for PFM, 16 or 8 otherwise) + `scale` and `little_endian` are separated, so the `header` will always store a positive `scale` + `little_endian` will only be true for a negative `scale` PFM, every other format will be false + `little_endian` only describes the netpbm data being read/written, the image buffer will be native +*/ + +package netpbm diff --git a/core/image/netpbm/helpers.odin b/core/image/netpbm/helpers.odin new file mode 100644 index 000000000..016f9453e --- /dev/null +++ b/core/image/netpbm/helpers.odin @@ -0,0 +1,27 @@ +package netpbm + +import "core:bytes" +import "core:image" + +destroy :: proc(img: ^image.Image) -> bool { + if img == nil do return false + + defer free(img) + bytes.buffer_destroy(&img.pixels) + + info, ok := img.metadata.(^image.Netpbm_Info) + if !ok do return false + + header_destroy(&info.header) + free(info) + img.metadata = nil + + return true +} + +header_destroy :: proc(using header: ^Header) { + if format == .P7 && tupltype != "" { + delete(tupltype) + tupltype = "" + } +} diff --git a/core/image/netpbm/netpbm.odin b/core/image/netpbm/netpbm.odin new file mode 100644 index 000000000..18545092d --- /dev/null +++ b/core/image/netpbm/netpbm.odin @@ -0,0 +1,763 @@ +package netpbm + +import "core:bytes" +import "core:fmt" +import "core:image" +import "core:mem" +import "core:os" +import "core:strconv" +import "core:strings" +import "core:unicode" + +Image :: image.Image +Format :: image.Netpbm_Format +Header :: image.Netpbm_Header +Info :: image.Netpbm_Info +Error :: image.Error +Format_Error :: image.Netpbm_Error + +Formats :: bit_set[Format] +PBM :: Formats{.P1, .P4} +PGM :: Formats{.P2, .P5} +PPM :: Formats{.P3, .P6} +PNM :: PBM + PGM + PPM +PAM :: Formats{.P7} +PFM :: Formats{.Pf, .PF} +ASCII :: Formats{.P1, .P2, .P3} +BINARY :: Formats{.P4, .P5, .P6} + PAM + PFM + +load :: proc { + load_from_file, + load_from_bytes, +} + +load_from_file :: proc(filename: string, allocator := context.allocator) -> (img: ^Image, err: Error) { + context.allocator = allocator + + data, ok := os.read_entire_file(filename); defer delete(data) + if !ok { + err = .Unable_To_Read_File + return + } + + return load_from_bytes(data) +} + +load_from_bytes :: proc(data: []byte, allocator := context.allocator) -> (img: ^Image, err: Error) { + context.allocator = allocator + + img = new(Image) + img.which = .NetPBM + + header: Header; defer header_destroy(&header) + header_size: int + header, header_size = parse_header(data) or_return + + img_data := data[header_size:] + decode_image(img, header, img_data) or_return + + info := new(Info) + info.header = header + if header.format == .P7 && header.tupltype != "" { + info.header.tupltype = strings.clone(header.tupltype) + } + img.metadata = info + + return img, nil +} + +save :: proc { + save_to_file, + save_to_buffer, +} + +save_to_file :: proc(filename: string, img: ^Image, custom_info: Info = {}, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator + + data: []byte; defer delete(data) + data = save_to_buffer(img, custom_info) or_return + + if ok := os.write_entire_file(filename, data); !ok { + return .Unable_To_Write_File + } + + return Format_Error.None +} + +save_to_buffer :: proc(img: ^Image, custom_info: Info = {}, allocator := context.allocator) -> (buffer: []byte, err: Error) { + context.allocator = allocator + + info: Info = {} + if custom_info.header.width > 0 { + // Custom info has been set, use it. + info = custom_info + } else { + img_info, ok := img.metadata.(^image.Netpbm_Info) + if !ok { + // image doesn't have .Netpbm info, guess it + auto_info, auto_info_found := autoselect_pbm_format_from_image(img) + if auto_info_found { + info = auto_info + } else { + return {}, .Invalid_Input_Image + } + } else { + // use info as stored on image + info = img_info^ + } + } + + // using info so we can just talk about the header + using info + + // validation + if header.format in (PBM + PGM + Formats{.Pf}) && img.channels != 1 \ + || header.format in (PPM + Formats{.PF}) && img.channels != 3 { + err = .Invalid_Number_Of_Channels + return + } + + if header.format in (PNM + PAM) { + if header.maxval <= int(max(u8)) && img.depth != 8 \ + || header.maxval > int(max(u8)) && header.maxval <= int(max(u16)) && img.depth != 16 { + err = .Invalid_Image_Depth + return + } + } else if header.format in PFM && img.depth != 32 { + err = .Invalid_Image_Depth + return + } + + // we will write to a string builder + data: strings.Builder + strings.builder_init(&data) + + // all PNM headers start with the format + fmt.sbprintf(&data, "%s\n", header.format) + if header.format in PNM { + fmt.sbprintf(&data, "%i %i\n", img.width, img.height) + if header.format not_in PBM { + fmt.sbprintf(&data, "%i\n", header.maxval) + } + } else if header.format in PAM { + if len(header.tupltype) > 0 { + fmt.sbprintf(&data, "WIDTH %i\nHEIGHT %i\nMAXVAL %i\nDEPTH %i\nTUPLTYPE %s\nENDHDR\n", + img.width, img.height, header.maxval, img.channels, header.tupltype) + } else { + fmt.sbprintf(&data, "WIDTH %i\nHEIGHT %i\nMAXVAL %i\nDEPTH %i\nENDHDR\n", + img.width, img.height, header.maxval, img.channels) + } + + } else if header.format in PFM { + scale := -header.scale if header.little_endian else header.scale + fmt.sbprintf(&data, "%i %i\n%f\n", img.width, img.height, scale) + } + + switch header.format { + // Compressed binary + case .P4: + header_buf := data.buf[:] + pixels := img.pixels.buf[:] + + p4_buffer_size := (img.width / 8 + 1) * img.height + reserve(&data.buf, len(header_buf) + p4_buffer_size) + + // we build up a byte value until it is completely filled + // or we reach the end the row + for y in 0 ..< img.height { + b: byte + + for x in 0 ..< img.width { + i := y * img.width + x + bit := byte(7 - (x % 8)) + v : byte = 0 if pixels[i] == 0 else 1 + b |= (v << bit) + + if bit == 0 { + append(&data.buf, b) + b = 0 + } + } + + if b != 0 { + append(&data.buf, b) + b = 0 + } + } + + // Simple binary + case .P5, .P6, .P7, .Pf, .PF: + header_buf := data.buf[:] + pixels := img.pixels.buf[:] + + resize(&data.buf, len(header_buf) + len(pixels)) + mem.copy(raw_data(data.buf[len(header_buf):]), raw_data(pixels), len(pixels)) + + // convert from native endianness + if img.depth == 16 { + pixels := mem.slice_data_cast([]u16be, data.buf[len(header_buf):]) + for p in &pixels { + p = u16be(transmute(u16) p) + } + } else if header.format in PFM { + if header.little_endian { + pixels := mem.slice_data_cast([]f32le, data.buf[len(header_buf):]) + for p in &pixels { + p = f32le(transmute(f32) p) + } + } else { + pixels := mem.slice_data_cast([]f32be, data.buf[len(header_buf):]) + for p in &pixels { + p = f32be(transmute(f32) p) + } + } + } + + // If-it-looks-like-a-bitmap ASCII + case .P1: + pixels := img.pixels.buf[:] + for y in 0 ..< img.height { + for x in 0 ..< img.width { + i := y * img.width + x + append(&data.buf, '0' if pixels[i] == 0 else '1') + } + append(&data.buf, '\n') + } + + // Token ASCII + case .P2, .P3: + switch img.depth { + case 8: + pixels := img.pixels.buf[:] + for y in 0 ..< img.height { + for x in 0 ..< img.width { + i := y * img.width + x + for c in 0 ..< img.channels { + i := i * img.channels + c + fmt.sbprintf(&data, "%i ", pixels[i]) + } + fmt.sbprint(&data, "\n") + } + fmt.sbprint(&data, "\n") + } + + case 16: + pixels := mem.slice_data_cast([]u16, img.pixels.buf[:]) + for y in 0 ..< img.height { + for x in 0 ..< img.width { + i := y * img.width + x + for c in 0 ..< img.channels { + i := i * img.channels + c + fmt.sbprintf(&data, "%i ", pixels[i]) + } + fmt.sbprint(&data, "\n") + } + fmt.sbprint(&data, "\n") + } + + case: + return data.buf[:], .Invalid_Image_Depth + } + + case: + return data.buf[:], .Invalid_Format + } + + return data.buf[:], Format_Error.None +} + +parse_header :: proc(data: []byte, allocator := context.allocator) -> (header: Header, length: int, err: Error) { + context.allocator = allocator + + // we need the signature and a space + if len(data) < 3 { + err = Format_Error.Incomplete_Header + return + } + + if data[0] == 'P' { + switch data[1] { + case '1' ..= '6': + return _parse_header_pnm(data) + case '7': + return _parse_header_pam(data, allocator) + case 'F', 'f': + return _parse_header_pfm(data) + } + } + + err = .Invalid_Signature + return +} + +@(private) +_parse_header_pnm :: proc(data: []byte) -> (header: Header, length: int, err: Error) { + SIG_LENGTH :: 2 + + { + header_formats := []Format{.P1, .P2, .P3, .P4, .P5, .P6} + header.format = header_formats[data[1] - '0' - 1] + } + + // have a list of fielda for easy iteration + header_fields: []^int + if header.format in PBM { + header_fields = {&header.width, &header.height} + header.maxval = 1 // we know maxval for a bitmap + } else { + header_fields = {&header.width, &header.height, &header.maxval} + } + + // we're keeping track of the header byte length + length = SIG_LENGTH + + // loop state + in_comment := false + already_in_space := true + current_field := 0 + current_value := header_fields[0] + + parse_loop: for d, i in data[SIG_LENGTH:] { + length += 1 + + // handle comments + if in_comment { + switch d { + // comments only go up to next carriage return or line feed + case '\r', '\n': + in_comment = false + } + continue + } else if d == '#' { + in_comment = true + continue + } + + // handle whitespace + in_space := unicode.is_white_space(rune(d)) + if in_space { + if already_in_space { + continue + } + already_in_space = true + + // switch to next value + current_field += 1 + if current_field == len(header_fields) { + // header byte length is 1-index so we'll increment again + length += 1 + break parse_loop + } + current_value = header_fields[current_field] + } else { + already_in_space = false + + if !unicode.is_digit(rune(d)) { + err = Format_Error.Invalid_Header_Token_Character + return + } + + val := int(d - '0') + current_value^ = current_value^ * 10 + val + } + } + + // set extra info + header.channels = 3 if header.format in PPM else 1 + header.depth = 16 if header.maxval > int(max(u8)) else 8 + + // limit checking + if current_field < len(header_fields) { + err = Format_Error.Incomplete_Header + return + } + + if header.width < 1 \ + || header.height < 1 \ + || header.maxval < 1 || header.maxval > int(max(u16)) { + fmt.printf("[pnm] Header: {{width = %v, height = %v, maxval: %v}}\n", header.width, header.height, header.maxval) + err = .Invalid_Header_Value + return + } + + length -= 1 + err = Format_Error.None + return +} + +@(private) +_parse_header_pam :: proc(data: []byte, allocator := context.allocator) -> (header: Header, length: int, err: Error) { + context.allocator = allocator + + // the spec needs the newline apparently + if string(data[0:3]) != "P7\n" { + err = .Invalid_Signature + return + } + header.format = .P7 + + SIGNATURE_LENGTH :: 3 + HEADER_END :: "ENDHDR\n" + + // we can already work out the size of the header + header_end_index := strings.index(string(data), HEADER_END) + if header_end_index == -1 { + err = Format_Error.Incomplete_Header + return + } + length = header_end_index + len(HEADER_END) + + // string buffer for the tupltype + tupltype: strings.Builder + strings.builder_init(&tupltype, context.temp_allocator); defer strings.builder_destroy(&tupltype) + fmt.sbprint(&tupltype, "") + + // PAM uses actual lines, so we can iterate easily + line_iterator := string(data[SIGNATURE_LENGTH : header_end_index]) + parse_loop: for line in strings.split_lines_iterator(&line_iterator) { + line := line + + if len(line) == 0 || line[0] == '#' { + continue + } + + field, ok := strings.fields_iterator(&line) + value := strings.trim_space(line) + + // the field will change, but the logic stays the same + current_field: ^int + + switch field { + case "WIDTH": current_field = &header.width + case "HEIGHT": current_field = &header.height + case "DEPTH": current_field = &header.channels + case "MAXVAL": current_field = &header.maxval + + case "TUPLTYPE": + if len(value) == 0 { + err = .Invalid_Header_Value + return + } + + if len(tupltype.buf) == 0 { + fmt.sbprint(&tupltype, value) + } else { + fmt.sbprint(&tupltype, "", value) + } + + continue + + case: + continue + } + + if current_field^ != 0 { + err = Format_Error.Duplicate_Header_Field + return + } + current_field^, ok = strconv.parse_int(value) + if !ok { + err = Format_Error.Invalid_Header_Value + return + } + } + + // extra info + header.depth = 16 if header.maxval > int(max(u8)) else 8 + + // limit checking + if header.width < 1 \ + || header.height < 1 \ + || header.maxval < 1 \ + || header.maxval > int(max(u16)) { + fmt.printf("[pam] Header: {{width = %v, height = %v, maxval: %v}}\n", header.width, header.height, header.maxval) + err = Format_Error.Invalid_Header_Value + return + } + + header.tupltype = strings.clone(strings.to_string(tupltype)) + err = Format_Error.None + return +} + +@(private) +_parse_header_pfm :: proc(data: []byte) -> (header: Header, length: int, err: Error) { + // we can just cycle through tokens for PFM + field_iterator := string(data) + field, ok := strings.fields_iterator(&field_iterator) + + switch field { + case "Pf": + header.format = .Pf + header.channels = 1 + case "PF": + header.format = .PF + header.channels = 3 + case: + err = .Invalid_Signature + return + } + + // floating point + header.depth = 32 + + // width + field, ok = strings.fields_iterator(&field_iterator) + if !ok { + err = Format_Error.Incomplete_Header + return + } + header.width, ok = strconv.parse_int(field) + if !ok { + err = Format_Error.Invalid_Header_Value + return + } + + // height + field, ok = strings.fields_iterator(&field_iterator) + if !ok { + err = Format_Error.Incomplete_Header + return + } + header.height, ok = strconv.parse_int(field) + if !ok { + err = Format_Error.Invalid_Header_Value + return + } + + // scale (sign is endianness) + field, ok = strings.fields_iterator(&field_iterator) + if !ok { + err = Format_Error.Incomplete_Header + return + } + header.scale, ok = strconv.parse_f32(field) + if !ok { + err = Format_Error.Invalid_Header_Value + return + } + + if header.scale < 0.0 { + header.little_endian = true + header.scale = -header.scale + } + + // pointer math to get header size + length = int((uintptr(raw_data(field_iterator)) + 1) - uintptr(raw_data(data))) + + // limit checking + if header.width < 1 \ + || header.height < 1 \ + || header.scale == 0.0 { + fmt.printf("[pfm] Header: {{width = %v, height = %v, scale: %v}}\n", header.width, header.height, header.scale) + err = .Invalid_Header_Value + return + } + + err = Format_Error.None + return +} + +decode_image :: proc(img: ^Image, header: Header, data: []byte, allocator := context.allocator) -> (err: Error) { + assert(img != nil) + context.allocator = allocator + + img.width = header.width + img.height = header.height + img.channels = header.channels + img.depth = header.depth + + buffer_size := image.compute_buffer_size(img.width, img.height, img.channels, img.depth) + + // we can check data size for binary formats + if header.format in BINARY { + if len(data) < buffer_size { + fmt.printf("len(data): %v, buffer size: %v\n", len(data), buffer_size) + return .Buffer_Too_Small + } + } + + // for ASCII and P4, we use length for the termination condition, so start at 0 + // BINARY will be a simple memcopy so the buffer length should also be initialised + if header.format in ASCII || header.format == .P4 { + bytes.buffer_init_allocator(&img.pixels, 0, buffer_size) + } else { + bytes.buffer_init_allocator(&img.pixels, buffer_size, buffer_size) + } + + switch header.format { + // Compressed binary + case .P4: + for d in data { + for b in 1 ..= 8 { + bit := byte(8 - b) + pix := (d >> bit) & 1 + bytes.buffer_write_byte(&img.pixels, pix) + if len(img.pixels.buf) % img.width == 0 { + break + } + } + + if len(img.pixels.buf) == cap(img.pixels.buf) { + break + } + } + + // Simple binary + case .P5, .P6, .P7, .Pf, .PF: + copy(img.pixels.buf[:], data[:]) + + // convert to native endianness + if header.format in PFM { + pixels := mem.slice_data_cast([]f32, img.pixels.buf[:]) + if header.little_endian { + for p in &pixels { + p = f32(transmute(f32le) p) + } + } else { + for p in &pixels { + p = f32(transmute(f32be) p) + } + } + } else { + if img.depth == 16 { + pixels := mem.slice_data_cast([]u16, img.pixels.buf[:]) + for p in &pixels { + p = u16(transmute(u16be) p) + } + } + } + + // If-it-looks-like-a-bitmap ASCII + case .P1: + for c in data { + switch c { + case '0', '1': + bytes.buffer_write_byte(&img.pixels, c - '0') + } + + if len(img.pixels.buf) == cap(img.pixels.buf) { + break + } + } + + if len(img.pixels.buf) < cap(img.pixels.buf) { + err = Format_Error.Buffer_Too_Small + return + } + + // Token ASCII + case .P2, .P3: + field_iterator := string(data) + for field in strings.fields_iterator(&field_iterator) { + value, ok := strconv.parse_int(field) + if !ok { + err = Format_Error.Invalid_Buffer_ASCII_Token + return + } + + //? do we want to enforce the maxval, the limit, or neither + if value > int(max(u16)) /*header.maxval*/ { + err = Format_Error.Invalid_Buffer_Value + return + } + + switch img.depth { + case 8: + bytes.buffer_write_byte(&img.pixels, u8(value)) + case 16: + vb := transmute([2]u8) u16(value) + bytes.buffer_write(&img.pixels, vb[:]) + } + + if len(img.pixels.buf) == cap(img.pixels.buf) { + break + } + } + + if len(img.pixels.buf) < cap(img.pixels.buf) { + err = Format_Error.Buffer_Too_Small + return + } + } + + err = Format_Error.None + return +} + +// Automatically try to select an appropriate format to save to based on `img.channel` and `img.depth` +autoselect_pbm_format_from_image :: proc(img: ^Image, prefer_binary := true, force_black_and_white := false, pfm_scale := f32(1.0)) -> (res: Info, ok: bool) { + /* + PBM (P1, P4): Portable Bit Map, stores black and white images (1 channel) + PGM (P2, P5): Portable Gray Map, stores greyscale images (1 channel, 1 or 2 bytes per value) + PPM (P3, P6): Portable Pixel Map, stores colour images (3 channel, 1 or 2 bytes per value) + PAM (P7 ): Portable Arbitrary Map, stores arbitrary channel images (1 or 2 bytes per value) + PFM (Pf, PF): Portable Float Map, stores floating-point images (Pf: 1 channel, PF: 3 channel) + + ASCII :: Formats{.P1, .P2, .P3} + */ + using res.header + + width = img.width + height = img.height + channels = img.channels + depth = img.depth + maxval = 255 if img.depth == 8 else 65535 + little_endian = true if ODIN_ENDIAN == .Little else false + + // Assume we'll find a suitable format + ok = true + + switch img.channels { + case 1: + // Must be Portable Float Map + if img.depth == 32 { + format = .Pf + return + } + + if force_black_and_white { + // Portable Bit Map + format = .P4 if prefer_binary else .P1 + maxval = 1 + return + } else { + // Portable Gray Map + format = .P5 if prefer_binary else .P2 + return + } + + case 3: + // Must be Portable Float Map + if img.depth == 32 { + format = .PF + return + } + + // Portable Pixel Map + format = .P6 if prefer_binary else .P3 + return + + case: + // Portable Arbitrary Map + if img.depth == 8 || img.depth == 16 { + format = .P7 + scale = pfm_scale + return + } + } + + // We couldn't find a suitable format + return {}, false +} + +@(init, private) +_register :: proc() { + loader :: proc(data: []byte, options: image.Options, allocator: mem.Allocator) -> (img: ^Image, err: Error) { + return load_from_bytes(data, allocator) + } + destroyer :: proc(img: ^Image) { + _ = destroy(img) + } + image.register(.NetPBM, loader, destroyer) +} \ No newline at end of file diff --git a/core/image/png/example.odin b/core/image/png/example.odin index f4eb5128e..17436c260 100644 --- a/core/image/png/example.odin +++ b/core/image/png/example.odin @@ -207,7 +207,7 @@ write_image_as_ppm :: proc(filename: string, image: ^image.Image) -> (success: b } mode: int = 0 - when ODIN_OS == "linux" || ODIN_OS == "darwin" { + when ODIN_OS == .Linux || ODIN_OS == .Darwin { // NOTE(justasd): 644 (owner read, write; group read; others read) mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH } diff --git a/core/image/png/helpers.odin b/core/image/png/helpers.odin index ecc0183bc..0ebf0b20b 100644 --- a/core/image/png/helpers.odin +++ b/core/image/png/helpers.odin @@ -242,17 +242,16 @@ srgb :: proc(c: image.PNG_Chunk) -> (res: sRGB, ok: bool) { } plte :: proc(c: image.PNG_Chunk) -> (res: PLTE, ok: bool) { - if c.header.type != .PLTE { + if c.header.type != .PLTE || c.header.length % 3 != 0 || c.header.length > 768 { return {}, false } - i := 0; j := 0; ok = true - for j < int(c.header.length) { - res.entries[i] = {c.data[j], c.data[j+1], c.data[j+2]} - i += 1; j += 3 + plte := mem.slice_data_cast([]image.RGB_Pixel, c.data[:]) + for color, i in plte { + res.entries[i] = color } - res.used = u16(i) - return + res.used = u16(len(plte)) + return res, true } splt :: proc(c: image.PNG_Chunk) -> (res: sPLT, ok: bool) { @@ -439,18 +438,18 @@ when false { flags: int = O_WRONLY|O_CREATE|O_TRUNC if len(image.pixels) == 0 || len(image.pixels) < image.width * image.height * int(image.channels) { - return E_PNG.Invalid_Image_Dimensions + return .Invalid_Image_Dimensions } mode: int = 0 - when ODIN_OS == "linux" || ODIN_OS == "darwin" { + when ODIN_OS == .Linux || ODIN_OS == .Darwin { // NOTE(justasd): 644 (owner read, write; group read; others read) mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH } fd, fderr := open(filename, flags, mode) if fderr != 0 { - return E_General.Cannot_Open_File + return .Cannot_Open_File } defer close(fd) @@ -473,7 +472,7 @@ when false { case 3: ihdr.color_type = Color_Type{.Color} case 4: ihdr.color_type = Color_Type{.Color, .Alpha} case:// Unhandled - return E_PNG.Unknown_Color_Type + return .Unknown_Color_Type } h := make_chunk(ihdr, .IHDR) write_chunk(fd, h) diff --git a/core/image/png/png.odin b/core/image/png/png.odin index bff0afde3..35fdb58d8 100644 --- a/core/image/png/png.odin +++ b/core/image/png/png.odin @@ -18,23 +18,16 @@ import "core:compress/zlib" import "core:image" import "core:os" -import "core:strings" import "core:hash" import "core:bytes" import "core:io" import "core:mem" import "core:intrinsics" -/* - 67_108_864 pixels max by default. - Maximum allowed dimensions are capped at 65535 * 65535. -*/ -MAX_DIMENSIONS :: min(#config(PNG_MAX_DIMENSIONS, 8192 * 8192), 65535 * 65535) +// Limit chunk sizes. +// By default: IDAT = 8k x 8k x 16-bits + 8k filter bytes. +// The total number of pixels defaults to 64 Megapixel and can be tuned in image/common.odin. -/* - Limit chunk sizes. - By default: IDAT = 8k x 8k x 16-bits + 8k filter bytes. -*/ _MAX_IDAT_DEFAULT :: ( 8192 /* Width */ * 8192 /* Height */ * 2 /* 16-bit */) + 8192 /* Filter bytes */ _MAX_IDAT :: (65535 /* Width */ * 65535 /* Height */ * 2 /* 16-bit */) + 65535 /* Filter bytes */ @@ -64,7 +57,7 @@ Row_Filter :: enum u8 { Paeth = 4, } -PLTE_Entry :: [3]u8 +PLTE_Entry :: image.RGB_Pixel PLTE :: struct #packed { entries: [256]PLTE_Entry, @@ -244,7 +237,7 @@ append_chunk :: proc(list: ^[dynamic]image.PNG_Chunk, src: image.PNG_Chunk, allo append(list, c) if len(list) != length + 1 { // Resize during append failed. - return mem.Allocator_Error.Out_Of_Memory + return .Unable_To_Allocate_Or_Resize } return @@ -259,7 +252,7 @@ read_header :: proc(ctx: ^$C) -> (image.PNG_IHDR, Error) { header := (^image.PNG_IHDR)(raw_data(c.data))^ // Validate IHDR using header - if width == 0 || height == 0 || u128(width) * u128(height) > MAX_DIMENSIONS { + if width == 0 || height == 0 || u128(width) * u128(height) > image.MAX_DIMENSIONS { return {}, .Invalid_Image_Dimensions } @@ -324,13 +317,12 @@ read_header :: proc(ctx: ^$C) -> (image.PNG_IHDR, Error) { } chunk_type_to_name :: proc(type: ^image.PNG_Chunk_Type) -> string { - t := transmute(^u8)type - return strings.string_from_ptr(t, 4) + return string(([^]u8)(type)[:4]) } -load_from_slice :: proc(slice: []u8, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) { +load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) { ctx := &compress.Context_Memory_Input{ - input_data = slice, + input_data = data, } /* @@ -350,10 +342,9 @@ load_from_file :: proc(filename: string, options := Options{}, allocator := cont defer delete(data) if ok { - return load_from_slice(data, options) + return load_from_bytes(data, options) } else { - img = new(Image) - return img, compress.General_Error.File_Not_Found + return nil, .Unable_To_Read_File } } @@ -366,6 +357,10 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a options -= {.info} } + if .return_header in options && .return_metadata in options { + options -= {.return_header} + } + if .alpha_drop_if_present in options && .alpha_add_if_missing in options { return {}, compress.General_Error.Incompatible_Options } @@ -377,13 +372,14 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a if img == nil { img = new(Image) } + img.which = .PNG info := new(image.PNG_Info) img.metadata = info signature, io_error := compress.read_data(ctx, Signature) if io_error != .None || signature != .PNG { - return img, .Invalid_PNG_Signature + return img, .Invalid_Signature } idat: []u8 @@ -392,7 +388,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a idat_length := u64(0) - c: image.PNG_Chunk + c: image.PNG_Chunk ch: image.PNG_Chunk_Header e: io.Error @@ -473,6 +469,10 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a } info.header = h + if .return_header in options && .return_metadata not_in options && .do_not_decompress_image not_in options { + return img, nil + } + case .PLTE: seen_plte = true // PLTE must appear before IDAT and can't appear for color types 0, 4. @@ -540,9 +540,6 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a seen_iend = true case .bKGD: - - // TODO: Make sure that 16-bit bKGD + tRNS chunks return u16 instead of u16be - c = read_chunk(ctx) or_return seen_bkgd = true if .return_metadata in options { @@ -594,23 +591,36 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a */ final_image_channels += 1 - seen_trns = true + + if .Paletted in header.color_type { + if len(c.data) > 256 { + return img, .TNRS_Invalid_Length + } + } else if .Color in header.color_type { + if len(c.data) != 6 { + return img, .TNRS_Invalid_Length + } + } else if len(c.data) != 2 { + return img, .TNRS_Invalid_Length + } + if info.header.bit_depth < 8 && .Paletted not_in info.header.color_type { // Rescale tRNS data so key matches intensity - dsc := depth_scale_table + dsc := depth_scale_table scale := dsc[info.header.bit_depth] if scale != 1 { key := mem.slice_data_cast([]u16be, c.data)[0] * u16be(scale) c.data = []u8{0, u8(key & 255)} } } + trns = c - case .iDOT, .CbGI: + case .iDOT, .CgBI: /* iPhone PNG bastardization that doesn't adhere to spec with broken IDAT chunk. - We're not going to add support for it. If you have the misfortunte of coming + We're not going to add support for it. If you have the misfortune of coming across one of these files, use a utility to defry it. */ return img, .Image_Does_Not_Adhere_to_Spec @@ -635,6 +645,10 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a return img, .IDAT_Missing } + if .Paletted in header.color_type && !seen_plte { + return img, .PLTE_Missing + } + /* Calculate the expected output size, to help `inflate` make better decisions about the output buffer. We'll also use it to check the returned buffer size is what we expected it to be. @@ -683,15 +697,6 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a return {}, defilter_error } - /* - Now we'll handle the relocoring of paletted images, handling of tRNS chunks, - and we'll expand grayscale images to RGB(A). - - For the sake of convenience we return only RGB(A) images. In the future we - may supply an option to return Gray/Gray+Alpha as-is, in which case RGB(A) - will become the default. - */ - if .Paletted in header.color_type && .do_not_expand_indexed in options { return img, nil } @@ -699,7 +704,10 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a return img, nil } - + /* + Now we're going to optionally apply various post-processing stages, + to for example expand grayscale, apply a palette, premultiply alpha, etc. + */ raw_image_channels := img.channels out_image_channels := 3 @@ -737,7 +745,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a dest_raw_size := compute_buffer_size(int(header.width), int(header.height), out_image_channels, 8) t := bytes.Buffer{} if !resize(&t.buf, dest_raw_size) { - return {}, mem.Allocator_Error.Out_Of_Memory + return {}, .Unable_To_Allocate_Or_Resize } i := 0; j := 0 @@ -818,7 +826,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a dest_raw_size := compute_buffer_size(int(header.width), int(header.height), out_image_channels, 16) t := bytes.Buffer{} if !resize(&t.buf, dest_raw_size) { - return {}, mem.Allocator_Error.Out_Of_Memory + return {}, .Unable_To_Allocate_Or_Resize } p16 := mem.slice_data_cast([]u16, temp.buf[:]) @@ -1017,7 +1025,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a dest_raw_size := compute_buffer_size(int(header.width), int(header.height), out_image_channels, 8) t := bytes.Buffer{} if !resize(&t.buf, dest_raw_size) { - return {}, mem.Allocator_Error.Out_Of_Memory + return {}, .Unable_To_Allocate_Or_Resize } p := mem.slice_data_cast([]u8, temp.buf[:]) @@ -1204,7 +1212,6 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a return img, nil } - filter_paeth :: #force_inline proc(left, up, up_left: u8) -> u8 { aa, bb, cc := i16(left), i16(up), i16(up_left) p := aa + bb - cc @@ -1526,7 +1533,7 @@ defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^image.PNG_IH num_bytes := compute_buffer_size(width, height, channels, depth == 16 ? 16 : 8) if !resize(&img.pixels.buf, num_bytes) { - return mem.Allocator_Error.Out_Of_Memory + return .Unable_To_Allocate_Or_Resize } filter_ok: bool @@ -1568,7 +1575,7 @@ defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^image.PNG_IH temp: bytes.Buffer temp_len := compute_buffer_size(x, y, channels, depth == 16 ? 16 : 8) if !resize(&temp.buf, temp_len) { - return mem.Allocator_Error.Out_Of_Memory + return .Unable_To_Allocate_Or_Resize } params := Filter_Params{ @@ -1630,4 +1637,10 @@ defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^image.PNG_IH return nil } -load :: proc{load_from_file, load_from_slice, load_from_context} +load :: proc{load_from_file, load_from_bytes, load_from_context} + + +@(init, private) +_register :: proc() { + image.register(.PNG, load_from_bytes, destroy) +} \ No newline at end of file diff --git a/core/image/qoi/qoi.odin b/core/image/qoi/qoi.odin new file mode 100644 index 000000000..29a17d4f4 --- /dev/null +++ b/core/image/qoi/qoi.odin @@ -0,0 +1,411 @@ +/* + Copyright 2022 Jeroen van Rijn . + Made available under Odin's BSD-3 license. + + List of contributors: + Jeroen van Rijn: Initial implementation. +*/ + + +// package qoi implements a QOI image reader +// +// The QOI specification is at https://qoiformat.org. +package qoi + +import "core:image" +import "core:compress" +import "core:bytes" +import "core:os" + +Error :: image.Error +Image :: image.Image +Options :: image.Options + +RGB_Pixel :: image.RGB_Pixel +RGBA_Pixel :: image.RGBA_Pixel + +save_to_memory :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator + + if img == nil { + return .Invalid_Input_Image + } + + if output == nil { + return .Invalid_Output + } + + pixels := img.width * img.height + if pixels == 0 || pixels > image.MAX_DIMENSIONS { + return .Invalid_Input_Image + } + + // QOI supports only 8-bit images with 3 or 4 channels. + if img.depth != 8 || img.channels < 3 || img.channels > 4 { + return .Invalid_Input_Image + } + + if img.channels * pixels != len(img.pixels.buf) { + return .Invalid_Input_Image + } + + written := 0 + + // Calculate and allocate maximum size. We'll reclaim space to actually written output at the end. + max_size := pixels * (img.channels + 1) + size_of(image.QOI_Header) + size_of(u64be) + + if !resize(&output.buf, max_size) { + return .Unable_To_Allocate_Or_Resize + } + + header := image.QOI_Header{ + magic = image.QOI_Magic, + width = u32be(img.width), + height = u32be(img.height), + channels = u8(img.channels), + color_space = .Linear if .qoi_all_channels_linear in options else .sRGB, + } + header_bytes := transmute([size_of(image.QOI_Header)]u8)header + + copy(output.buf[written:], header_bytes[:]) + written += size_of(image.QOI_Header) + + /* + Encode loop starts here. + */ + seen: [64]RGBA_Pixel + pix := RGBA_Pixel{0, 0, 0, 255} + prev := pix + + seen[qoi_hash(pix)] = pix + + input := img.pixels.buf[:] + run := u8(0) + + for len(input) > 0 { + if img.channels == 4 { + pix = (^RGBA_Pixel)(raw_data(input))^ + } else { + pix.rgb = (^RGB_Pixel)(raw_data(input))^ + } + input = input[img.channels:] + + if pix == prev { + run += 1 + // As long as the pixel matches the last one, accumulate the run total. + // If we reach the max run length or the end of the image, write the run. + if run == 62 || len(input) == 0 { + // Encode and write run + output.buf[written] = u8(QOI_Opcode_Tag.RUN) | (run - 1) + written += 1 + run = 0 + } + } else { + if run > 0 { + // The pixel differs from the previous one, but we still need to write the pending run. + // Encode and write run + output.buf[written] = u8(QOI_Opcode_Tag.RUN) | (run - 1) + written += 1 + run = 0 + } + + index := qoi_hash(pix) + + if seen[index] == pix { + // Write indexed pixel + output.buf[written] = u8(QOI_Opcode_Tag.INDEX) | index + written += 1 + } else { + // Add pixel to index + seen[index] = pix + + // If the alpha matches the previous pixel's alpha, we don't need to write a full RGBA literal. + if pix.a == prev.a { + // Delta + d := pix.rgb - prev.rgb + + // DIFF, biased and modulo 256 + _d := d + 2 + + // LUMA, biased and modulo 256 + _l := RGB_Pixel{ d.r - d.g + 8, d.g + 32, d.b - d.g + 8 } + + if _d.r < 4 && _d.g < 4 && _d.b < 4 { + // Delta is between -2 and 1 inclusive + output.buf[written] = u8(QOI_Opcode_Tag.DIFF) | _d.r << 4 | _d.g << 2 | _d.b + written += 1 + } else if _l.r < 16 && _l.g < 64 && _l.b < 16 { + // Biased luma is between {-8..7, -32..31, -8..7} + output.buf[written ] = u8(QOI_Opcode_Tag.LUMA) | _l.g + output.buf[written + 1] = _l.r << 4 | _l.b + written += 2 + } else { + // Write RGB literal + output.buf[written] = u8(QOI_Opcode_Tag.RGB) + pix_bytes := transmute([4]u8)pix + copy(output.buf[written + 1:], pix_bytes[:3]) + written += 4 + } + } else { + // Write RGBA literal + output.buf[written] = u8(QOI_Opcode_Tag.RGBA) + pix_bytes := transmute([4]u8)pix + copy(output.buf[written + 1:], pix_bytes[:]) + written += 5 + } + } + } + prev = pix + } + + trailer := []u8{0, 0, 0, 0, 0, 0, 0, 1} + copy(output.buf[written:], trailer[:]) + written += len(trailer) + + resize(&output.buf, written) + return nil +} + +save_to_file :: proc(output: string, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator + + out := &bytes.Buffer{} + defer bytes.buffer_destroy(out) + + save_to_memory(out, img, options) or_return + write_ok := os.write_entire_file(output, out.buf[:]) + + return nil if write_ok else .Unable_To_Write_File +} + +save :: proc{save_to_memory, save_to_file} + +load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) { + ctx := &compress.Context_Memory_Input{ + input_data = data, + } + + img, err = load_from_context(ctx, options, allocator) + return img, err +} + +load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) { + context.allocator = allocator + + data, ok := os.read_entire_file(filename) + defer delete(data) + + if ok { + return load_from_bytes(data, options) + } else { + return nil, .Unable_To_Read_File + } +} + +@(optimization_mode="speed") +load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) { + context.allocator = allocator + options := options + + if .info in options { + options |= {.return_metadata, .do_not_decompress_image} + options -= {.info} + } + + if .return_header in options && .return_metadata in options { + options -= {.return_header} + } + + header := image.read_data(ctx, image.QOI_Header) or_return + if header.magic != image.QOI_Magic { + return img, .Invalid_Signature + } + + if img == nil { + img = new(Image) + } + img.which = .QOI + + if .return_metadata in options { + info := new(image.QOI_Info) + info.header = header + img.metadata = info + } + + if header.channels != 3 && header.channels != 4 { + return img, .Invalid_Number_Of_Channels + } + + if header.color_space != .sRGB && header.color_space != .Linear { + return img, .Invalid_Color_Space + } + + if header.width == 0 || header.height == 0 { + return img, .Invalid_Image_Dimensions + } + + total_pixels := header.width * header.height + if total_pixels > image.MAX_DIMENSIONS { + return img, .Image_Dimensions_Too_Large + } + + img.width = int(header.width) + img.height = int(header.height) + img.channels = 4 if .alpha_add_if_missing in options else int(header.channels) + img.depth = 8 + + if .do_not_decompress_image in options { + img.channels = int(header.channels) + return + } + + bytes_needed := image.compute_buffer_size(int(header.width), int(header.height), img.channels, 8) + + if !resize(&img.pixels.buf, bytes_needed) { + return img, .Unable_To_Allocate_Or_Resize + } + + /* + Decode loop starts here. + */ + seen: [64]RGBA_Pixel + pix := RGBA_Pixel{0, 0, 0, 255} + seen[qoi_hash(pix)] = pix + pixels := img.pixels.buf[:] + + decode: for len(pixels) > 0 { + data := image.read_u8(ctx) or_return + + tag := QOI_Opcode_Tag(data) + #partial switch tag { + case .RGB: + pix.rgb = image.read_data(ctx, RGB_Pixel) or_return + + #no_bounds_check { + seen[qoi_hash(pix)] = pix + } + + case .RGBA: + pix = image.read_data(ctx, RGBA_Pixel) or_return + + #no_bounds_check { + seen[qoi_hash(pix)] = pix + } + + case: + // 2-bit tag + tag = QOI_Opcode_Tag(data & QOI_Opcode_Mask) + #partial switch tag { + case .INDEX: + pix = seen[data & 63] + + case .DIFF: + diff_r := ((data >> 4) & 3) - 2 + diff_g := ((data >> 2) & 3) - 2 + diff_b := ((data >> 0) & 3) - 2 + + pix += {diff_r, diff_g, diff_b, 0} + + #no_bounds_check { + seen[qoi_hash(pix)] = pix + } + + case .LUMA: + data2 := image.read_u8(ctx) or_return + + diff_g := (data & 63) - 32 + diff_r := diff_g - 8 + ((data2 >> 4) & 15) + diff_b := diff_g - 8 + (data2 & 15) + + pix += {diff_r, diff_g, diff_b, 0} + + #no_bounds_check { + seen[qoi_hash(pix)] = pix + } + + case .RUN: + if length := int(data & 63) + 1; (length * img.channels) > len(pixels) { + return img, .Corrupt + } else { + #no_bounds_check for in 0.. (index: u8) { + i1 := u16(pixel.r) * 3 + i2 := u16(pixel.g) * 5 + i3 := u16(pixel.b) * 7 + i4 := u16(pixel.a) * 11 + + return u8((i1 + i2 + i3 + i4) & 63) +} + +@(init, private) +_register :: proc() { + image.register(.QOI, load_from_bytes, destroy) +} \ No newline at end of file diff --git a/core/image/tga/tga.odin b/core/image/tga/tga.odin new file mode 100644 index 000000000..67a088eb5 --- /dev/null +++ b/core/image/tga/tga.odin @@ -0,0 +1,101 @@ +/* + Copyright 2022 Jeroen van Rijn . + Made available under Odin's BSD-3 license. + + List of contributors: + Jeroen van Rijn: Initial implementation. +*/ + + +// package tga implements a TGA image writer for 8-bit RGB and RGBA images. +package tga + +import "core:mem" +import "core:image" +import "core:bytes" +import "core:os" + +Error :: image.Error +Image :: image.Image +Options :: image.Options + +RGB_Pixel :: image.RGB_Pixel +RGBA_Pixel :: image.RGBA_Pixel + +save_to_memory :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator + + if img == nil { + return .Invalid_Input_Image + } + + if output == nil { + return .Invalid_Output + } + + pixels := img.width * img.height + if pixels == 0 || pixels > image.MAX_DIMENSIONS || img.width > 65535 || img.height > 65535 { + return .Invalid_Input_Image + } + + // Our TGA writer supports only 8-bit images with 3 or 4 channels. + if img.depth != 8 || img.channels < 3 || img.channels > 4 { + return .Invalid_Input_Image + } + + if img.channels * pixels != len(img.pixels.buf) { + return .Invalid_Input_Image + } + + written := 0 + + // Calculate and allocate necessary space. + necessary := pixels * img.channels + size_of(image.TGA_Header) + + if !resize(&output.buf, necessary) { + return .Unable_To_Allocate_Or_Resize + } + + header := image.TGA_Header{ + data_type_code = 0x02, // Color, uncompressed. + dimensions = {u16le(img.width), u16le(img.height)}, + bits_per_pixel = u8(img.depth * img.channels), + image_descriptor = 1 << 5, // Origin is top left. + } + header_bytes := transmute([size_of(image.TGA_Header)]u8)header + + copy(output.buf[written:], header_bytes[:]) + written += size_of(image.TGA_Header) + + /* + Encode loop starts here. + */ + if img.channels == 3 { + pix := mem.slice_data_cast([]RGB_Pixel, img.pixels.buf[:]) + out := mem.slice_data_cast([]RGB_Pixel, output.buf[written:]) + for p, i in pix { + out[i] = p.bgr + } + } else if img.channels == 4 { + pix := mem.slice_data_cast([]RGBA_Pixel, img.pixels.buf[:]) + out := mem.slice_data_cast([]RGBA_Pixel, output.buf[written:]) + for p, i in pix { + out[i] = p.bgra + } + } + return nil +} + +save_to_file :: proc(output: string, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator + + out := &bytes.Buffer{} + defer bytes.buffer_destroy(out) + + save_to_memory(out, img, options) or_return + write_ok := os.write_entire_file(output, out.buf[:]) + + return nil if write_ok else .Unable_To_Write_File +} + +save :: proc{save_to_memory, save_to_file} \ No newline at end of file diff --git a/core/image/which.odin b/core/image/which.odin new file mode 100644 index 000000000..ab608174f --- /dev/null +++ b/core/image/which.odin @@ -0,0 +1,179 @@ +package image + +import "core:os" + +Which_File_Type :: enum { + Unknown, + + BMP, + DjVu, // AT&T DjVu file format + EXR, + FLIF, + GIF, + HDR, // Radiance RGBE HDR + ICNS, // Apple Icon Image + JPEG, + JPEG_2000, + JPEG_XL, + NetPBM, // NetPBM family + PIC, // Softimage PIC + PNG, // Portable Network Graphics + PSD, // Photoshop PSD + QOI, // Quite Okay Image + SGI_RGB, // Silicon Graphics Image RGB file format + Sun_Rast, // Sun Raster Graphic + TGA, // Targa Truevision + TIFF, // Tagged Image File Format + WebP, + XBM, // X BitMap +} + +which :: proc{ + which_bytes, + which_file, +} + +which_bytes :: proc(data: []byte) -> Which_File_Type { + test_tga :: proc(s: string) -> bool { + get8 :: #force_inline proc(s: ^string) -> u8 { + v := s[0] + s^ = s[1:] + return v + } + get16le :: #force_inline proc(s: ^string) -> u16 { + v := u16(s[0]) | u16(s[1])<<16 + s^ = s[2:] + return v + } + s := s + s = s[1:] // skip offset + + color_type := get8(&s) + if color_type > 1 { + return false + } + image_type := get8(&s) // image type + if color_type == 1 { // Colormap (Paletted) Image + if image_type != 1 && image_type != 9 { // color type requires 1 or 9 + return false + } + s = s[4:] // skip index of first colormap + bpcme := get8(&s) // check bits per colormap entry + if bpcme != 8 && bpcme != 15 && bpcme != 16 && bpcme != 24 && bpcme != 32 { + return false + } + s = s[4:] // skip image origin (x, y) + } else { // Normal image without colormap + if image_type != 2 && image_type != 3 && image_type != 10 && image_type != 11 { + return false + } + s = s[9:] // skip colormap specification + } + if get16le(&s) < 1 || get16le(&s) < 1 { // test width and height + return false + } + bpp := get8(&s) // bits per pixel + if color_type == 1 && bpp != 8 && bpp != 16 { + return false + } + if bpp != 8 && bpp != 15 && bpp != 16 && bpp != 24 && bpp != 32 { + return false + } + return true + } + + header: [128]byte + copy(header[:], data) + s := string(header[:]) + + switch { + case s[:2] == "BM": + return .BMP + case s[:8] == "AT&TFORM": + switch s[12:16] { + case "DJVU", "DJVM": + return .DjVu + } + case s[:4] == "\x76\x2f\x31\x01": + return .EXR + case s[:6] == "GIF87a", s[:6] == "GIF89a": + return .GIF + case s[6:10] == "JFIF", s[6:10] == "Exif": + return .JPEG + case s[:3] == "\xff\xd8\xff": + switch s[4] { + case 0xdb, 0xee, 0xe1, 0xe0: + return .JPEG + } + switch { + case s[:12] == "\xff\xd8\xff\xe0\x00\x10\x4a\x46\x49\x46\x00\x01": + return .JPEG + } + case s[:4] == "\xff\x4f\xff\x51", s[:12] == "\x00\x00\x00\x0c\x6a\x50\x20\x20\x0d\x0a\x87\x0a": + return .JPEG_2000 + case s[:12] == "\x00\x00\x00\x0c\x4a\x58\x4c\x20\x0d\x0a\x87\x0a": + return .JPEG_XL + case s[0] == 'P': + switch s[2] { + case '\t', '\n', '\r': + switch s[1] { + case '1', '4': // PBM + return .NetPBM + case '2', '5': // PGM + return .NetPBM + case '3', '6': // PPM + return .NetPBM + case '7': // PAM + return .NetPBM + case 'F', 'f': // PFM + return .NetPBM + } + } + case s[:8] == "\x89PNG\r\n\x1a\n": + return .PNG + case s[:4] == "qoif": + return .QOI + case s[:2] == "\x01\xda": + return .SGI_RGB + case s[:4] == "\x59\xA6\x6A\x95": + return .Sun_Rast + case s[:4] == "MM\x2a\x00", s[:4] == "II\x00\x2A": + return .TIFF + case s[:4] == "RIFF" && s[8:12] == "WEBP": + return .WebP + case s[:8] == "#define ": + return .XBM + + case s[:11] == "#?RADIANCE\n", s[:7] == "#?RGBE\n": + return .HDR + case s[:4] == "\x38\x42\x50\x53": + return .PSD + case s[:4] != "\x53\x80\xF6\x34" && s[88:92] == "PICT": + return .PIC + case s[:4] == "\x69\x63\x6e\x73": + return .ICNS + case s[:4] == "\x46\x4c\x49\x46": + return .FLIF + case: + // More complex formats + if test_tga(s) { + return .TGA + } + + + } + return .Unknown +} + + +which_file :: proc(path: string) -> Which_File_Type { + f, err := os.open(path) + if err != 0 { + return .Unknown + } + header: [128]byte + os.read(f, header[:]) + file_type := which_bytes(header[:]) + os.close(f) + return file_type +} \ No newline at end of file diff --git a/core/intrinsics/intrinsics.odin b/core/intrinsics/intrinsics.odin index 803b04d17..22b5d953d 100644 --- a/core/intrinsics/intrinsics.odin +++ b/core/intrinsics/intrinsics.odin @@ -6,12 +6,14 @@ package intrinsics is_package_imported :: proc(package_name: string) -> bool --- // Types -simd_vector :: proc($N: int, $T: typeid) -> type/#simd[N]T soa_struct :: proc($N: int, $T: typeid) -> type/#soa[N]T // Volatile volatile_load :: proc(dst: ^$T) -> T --- -volatile_store :: proc(dst: ^$T, val: T) -> T --- +volatile_store :: proc(dst: ^$T, val: T) --- + +non_temporal_load :: proc(dst: ^$T) -> T --- +non_temporal_store :: proc(dst: ^$T, val: T) --- // Trapping debug_trap :: proc() --- @@ -23,24 +25,32 @@ alloca :: proc(size, align: int) -> [^]u8 --- cpu_relax :: proc() --- read_cycle_counter :: proc() -> i64 --- -count_ones :: proc(x: $T) -> T where type_is_integer(T) --- -count_zeros :: proc(x: $T) -> T where type_is_integer(T) --- -count_trailing_zeros :: proc(x: $T) -> T where type_is_integer(T) --- -count_leading_zeros :: proc(x: $T) -> T where type_is_integer(T) --- -reverse_bits :: proc(x: $T) -> T where type_is_integer(T) --- +count_ones :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) --- +count_zeros :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) --- +count_trailing_zeros :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) --- +count_leading_zeros :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) --- +reverse_bits :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) --- byte_swap :: proc(x: $T) -> T where type_is_integer(T) || type_is_float(T) --- overflow_add :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok --- overflow_sub :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok --- overflow_mul :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok --- -sqrt :: proc(x: $T) -> T where type_is_float(T) --- +sqrt :: proc(x: $T) -> T where type_is_float(T) || (type_is_simd_vector(T) && type_is_float(type_elem_type(T))) --- + +fused_mul_add :: proc(a, b, c: $T) -> T where type_is_float(T) || (type_is_simd_vector(T) && type_is_float(type_elem_type(T))) --- mem_copy :: proc(dst, src: rawptr, len: int) --- mem_copy_non_overlapping :: proc(dst, src: rawptr, len: int) --- mem_zero :: proc(ptr: rawptr, len: int) --- mem_zero_volatile :: proc(ptr: rawptr, len: int) --- +// prefer [^]T operations if possible +ptr_offset :: proc(ptr: ^$T, offset: int) -> ^T --- +ptr_sub :: proc(a, b: ^$T) -> int --- + +unaligned_load :: proc(src: ^$T) -> T --- +unaligned_store :: proc(dst: ^$T, val: T) -> T --- fixed_point_mul :: proc(lhs, rhs: $T, #const scale: uint) -> T where type_is_integer(T) --- fixed_point_div :: proc(lhs, rhs: $T, #const scale: uint) -> T where type_is_integer(T) --- @@ -60,77 +70,47 @@ syscall :: proc(id: uintptr, args: ..uintptr) -> uintptr --- // Atomics -atomic_fence :: proc() --- -atomic_fence_acq :: proc() --- -atomic_fence_rel :: proc() --- -atomic_fence_acqrel :: proc() --- +Atomic_Memory_Order :: enum { + Relaxed = 0, // Unordered + Consume = 1, // Monotonic + Acquire = 2, + Release = 3, + Acq_Rel = 4, + Seq_Cst = 5, +} -atomic_store :: proc(dst: ^$T, val: T) --- -atomic_store_rel :: proc(dst: ^$T, val: T) --- -atomic_store_relaxed :: proc(dst: ^$T, val: T) --- -atomic_store_unordered :: proc(dst: ^$T, val: T) --- +atomic_type_is_lock_free :: proc($T: typeid) -> bool --- + +atomic_thread_fence :: proc(order: Atomic_Memory_Order) --- +atomic_signal_fence :: proc(order: Atomic_Memory_Order) --- + +atomic_store :: proc(dst: ^$T, val: T) --- +atomic_store_explicit :: proc(dst: ^$T, val: T, order: Atomic_Memory_Order) --- atomic_load :: proc(dst: ^$T) -> T --- -atomic_load_acq :: proc(dst: ^$T) -> T --- -atomic_load_relaxed :: proc(dst: ^$T) -> T --- -atomic_load_unordered :: proc(dst: ^$T) -> T --- +atomic_load_explicit :: proc(dst: ^$T, order: Atomic_Memory_Order) -> T --- -atomic_add :: proc(dst; ^$T, val: T) -> T --- -atomic_add_acq :: proc(dst; ^$T, val: T) -> T --- -atomic_add_rel :: proc(dst; ^$T, val: T) -> T --- -atomic_add_acqrel :: proc(dst; ^$T, val: T) -> T --- -atomic_add_relaxed :: proc(dst; ^$T, val: T) -> T --- -atomic_sub :: proc(dst; ^$T, val: T) -> T --- -atomic_sub_acq :: proc(dst; ^$T, val: T) -> T --- -atomic_sub_rel :: proc(dst; ^$T, val: T) -> T --- -atomic_sub_acqrel :: proc(dst; ^$T, val: T) -> T --- -atomic_sub_relaxed :: proc(dst; ^$T, val: T) -> T --- -atomic_and :: proc(dst; ^$T, val: T) -> T --- -atomic_and_acq :: proc(dst; ^$T, val: T) -> T --- -atomic_and_rel :: proc(dst; ^$T, val: T) -> T --- -atomic_and_acqrel :: proc(dst; ^$T, val: T) -> T --- -atomic_and_relaxed :: proc(dst; ^$T, val: T) -> T --- -atomic_nand :: proc(dst; ^$T, val: T) -> T --- -atomic_nand_acq :: proc(dst; ^$T, val: T) -> T --- -atomic_nand_rel :: proc(dst; ^$T, val: T) -> T --- -atomic_nand_acqrel :: proc(dst; ^$T, val: T) -> T --- -atomic_nand_relaxed :: proc(dst; ^$T, val: T) -> T --- -atomic_or :: proc(dst; ^$T, val: T) -> T --- -atomic_or_acq :: proc(dst; ^$T, val: T) -> T --- -atomic_or_rel :: proc(dst; ^$T, val: T) -> T --- -atomic_or_acqrel :: proc(dst; ^$T, val: T) -> T --- -atomic_or_relaxed :: proc(dst; ^$T, val: T) -> T --- -atomic_xor :: proc(dst; ^$T, val: T) -> T --- -atomic_xor_acq :: proc(dst; ^$T, val: T) -> T --- -atomic_xor_rel :: proc(dst; ^$T, val: T) -> T --- -atomic_xor_acqrel :: proc(dst; ^$T, val: T) -> T --- -atomic_xor_relaxed :: proc(dst; ^$T, val: T) -> T --- +// fetch then operator +atomic_add :: proc(dst: ^$T, val: T) -> T --- +atomic_add_explicit :: proc(dst: ^$T, val: T, order: Atomic_Memory_Order) -> T --- +atomic_sub :: proc(dst: ^$T, val: T) -> T --- +atomic_sub_explicit :: proc(dst: ^$T, val: T, order: Atomic_Memory_Order) -> T --- +atomic_and :: proc(dst: ^$T, val: T) -> T --- +atomic_and_explicit :: proc(dst: ^$T, val: T, order: Atomic_Memory_Order) -> T --- +atomic_nand :: proc(dst: ^$T, val: T) -> T --- +atomic_nand_explicit :: proc(dst: ^$T, val: T, order: Atomic_Memory_Order) -> T --- +atomic_or :: proc(dst: ^$T, val: T) -> T --- +atomic_or_explicit :: proc(dst: ^$T, val: T, order: Atomic_Memory_Order) -> T --- +atomic_xor :: proc(dst: ^$T, val: T) -> T --- +atomic_xor_explicit :: proc(dst: ^$T, val: T, order: Atomic_Memory_Order) -> T --- +atomic_exchange :: proc(dst: ^$T, val: T) -> T --- +atomic_exchange_explicit :: proc(dst: ^$T, val: T, order: Atomic_Memory_Order) -> T --- -atomic_xchg :: proc(dst; ^$T, val: T) -> T --- -atomic_xchg_acq :: proc(dst; ^$T, val: T) -> T --- -atomic_xchg_rel :: proc(dst; ^$T, val: T) -> T --- -atomic_xchg_acqrel :: proc(dst; ^$T, val: T) -> T --- -atomic_xchg_relaxed :: proc(dst; ^$T, val: T) -> T --- +atomic_compare_exchange_strong :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok --- +atomic_compare_exchange_strong_explicit :: proc(dst: ^$T, old, new: T, success, failure: Atomic_Memory_Order) -> (T, bool) #optional_ok --- +atomic_compare_exchange_weak :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok --- +atomic_compare_exchange_weak_explicit :: proc(dst: ^$T, old, new: T, success, failure: Atomic_Memory_Order) -> (T, bool) #optional_ok --- -atomic_cxchg :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok --- -atomic_cxchg_acq :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok --- -atomic_cxchg_rel :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok --- -atomic_cxchg_acqrel :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok --- -atomic_cxchg_relaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok --- -atomic_cxchg_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok --- -atomic_cxchg_failacq :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok --- -atomic_cxchg_acq_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok --- -atomic_cxchg_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok --- - -atomic_cxchgweak :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok --- -atomic_cxchgweak_acq :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok --- -atomic_cxchgweak_rel :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok --- -atomic_cxchgweak_acqrel :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok --- -atomic_cxchgweak_relaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok --- -atomic_cxchgweak_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok --- -atomic_cxchgweak_failacq :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok --- -atomic_cxchgweak_acq_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok --- -atomic_cxchgweak_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok --- // Constant type tests @@ -148,22 +128,24 @@ type_is_string :: proc($T: typeid) -> bool --- type_is_typeid :: proc($T: typeid) -> bool --- type_is_any :: proc($T: typeid) -> bool --- -type_is_endian_platform :: proc($T: typeid) -> bool --- -type_is_endian_little :: proc($T: typeid) -> bool --- -type_is_endian_big :: proc($T: typeid) -> bool --- -type_is_unsigned :: proc($T: typeid) -> bool --- -type_is_numeric :: proc($T: typeid) -> bool --- -type_is_ordered :: proc($T: typeid) -> bool --- -type_is_ordered_numeric :: proc($T: typeid) -> bool --- -type_is_indexable :: proc($T: typeid) -> bool --- -type_is_sliceable :: proc($T: typeid) -> bool --- -type_is_comparable :: proc($T: typeid) -> bool --- -type_is_simple_compare :: proc($T: typeid) -> bool --- // easily compared using memcmp (== and !=) -type_is_dereferenceable :: proc($T: typeid) -> bool --- -type_is_valid_map_key :: proc($T: typeid) -> bool --- +type_is_endian_platform :: proc($T: typeid) -> bool --- +type_is_endian_little :: proc($T: typeid) -> bool --- +type_is_endian_big :: proc($T: typeid) -> bool --- +type_is_unsigned :: proc($T: typeid) -> bool --- +type_is_numeric :: proc($T: typeid) -> bool --- +type_is_ordered :: proc($T: typeid) -> bool --- +type_is_ordered_numeric :: proc($T: typeid) -> bool --- +type_is_indexable :: proc($T: typeid) -> bool --- +type_is_sliceable :: proc($T: typeid) -> bool --- +type_is_comparable :: proc($T: typeid) -> bool --- +type_is_simple_compare :: proc($T: typeid) -> bool --- // easily compared using memcmp (== and !=) +type_is_dereferenceable :: proc($T: typeid) -> bool --- +type_is_valid_map_key :: proc($T: typeid) -> bool --- +type_is_valid_matrix_elements :: proc($T: typeid) -> bool --- type_is_named :: proc($T: typeid) -> bool --- type_is_pointer :: proc($T: typeid) -> bool --- +type_is_multi_pointer :: proc($T: typeid) -> bool --- type_is_array :: proc($T: typeid) -> bool --- type_is_enumerated_array :: proc($T: typeid) -> bool --- type_is_slice :: proc($T: typeid) -> bool --- @@ -175,6 +157,7 @@ type_is_enum :: proc($T: typeid) -> bool --- type_is_proc :: proc($T: typeid) -> bool --- type_is_bit_set :: proc($T: typeid) -> bool --- type_is_simd_vector :: proc($T: typeid) -> bool --- +type_is_matrix :: proc($T: typeid) -> bool --- type_has_nil :: proc($T: typeid) -> bool --- @@ -182,6 +165,7 @@ type_is_specialization_of :: proc($T, $S: typeid) -> bool --- type_is_variant_of :: proc($U, $V: typeid) -> bool where type_is_union(U) --- type_has_field :: proc($T: typeid, $name: string) -> bool --- +type_field_type :: proc($T: typeid, $name: string) -> typeid --- type_proc_parameter_count :: proc($T: typeid) -> int where type_is_proc(T) --- type_proc_return_count :: proc($T: typeid) -> int where type_is_proc(T) --- @@ -189,15 +173,127 @@ type_proc_return_count :: proc($T: typeid) -> int where type_is_proc(T) --- type_proc_parameter_type :: proc($T: typeid, index: int) -> typeid where type_is_proc(T) --- type_proc_return_type :: proc($T: typeid, index: int) -> typeid where type_is_proc(T) --- +type_struct_field_count :: proc($T: typeid) -> int where type_is_struct(T) --- + type_polymorphic_record_parameter_count :: proc($T: typeid) -> typeid --- type_polymorphic_record_parameter_value :: proc($T: typeid, index: int) -> $V --- +type_is_specialized_polymorphic_record :: proc($T: typeid) -> bool --- +type_is_unspecialized_polymorphic_record :: proc($T: typeid) -> bool --- + +type_is_subtype_of :: proc($T, $U: typeid) -> bool --- type_field_index_of :: proc($T: typeid, $name: string) -> uintptr --- type_equal_proc :: proc($T: typeid) -> (equal: proc "contextless" (rawptr, rawptr) -> bool) where type_is_comparable(T) --- type_hasher_proc :: proc($T: typeid) -> (hasher: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr) where type_is_comparable(T) --- +constant_utf16_cstring :: proc($literal: string) -> [^]u16 --- + +// SIMD related +simd_add :: proc(a, b: #simd[N]T) -> #simd[N]T --- +simd_sub :: proc(a, b: #simd[N]T) -> #simd[N]T --- +simd_mul :: proc(a, b: #simd[N]T) -> #simd[N]T --- +simd_div :: proc(a, b: #simd[N]T) -> #simd[N]T where type_is_float(T) --- + +// Keeps Odin's Behaviour +// (x << y) if y <= mask else 0 +simd_shl :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T --- +simd_shr :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T --- + +// Similar to C's Behaviour +// x << (y & mask) +simd_shl_masked :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T --- +simd_shr_masked :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T --- + +simd_add_sat :: proc(a, b: #simd[N]T) -> #simd[N]T --- +simd_sub_sat :: proc(a, b: #simd[N]T) -> #simd[N]T --- + +simd_and :: proc(a, b: #simd[N]T) -> #simd[N]T --- +simd_or :: proc(a, b: #simd[N]T) -> #simd[N]T --- +simd_xor :: proc(a, b: #simd[N]T) -> #simd[N]T --- +simd_and_not :: proc(a, b: #simd[N]T) -> #simd[N]T --- + +simd_neg :: proc(a: #simd[N]T) -> #simd[N]T --- + +simd_abs :: proc(a: #simd[N]T) -> #simd[N]T --- + +simd_min :: proc(a, b: #simd[N]T) -> #simd[N]T --- +simd_max :: proc(a, b: #simd[N]T) -> #simd[N]T --- +simd_clamp :: proc(v, min, max: #simd[N]T) -> #simd[N]T --- + +// Return an unsigned integer of the same size as the input type +// NOT A BOOLEAN +// element-wise: +// false => 0x00...00 +// true => 0xff...ff +simd_lanes_eq :: proc(a, b: #simd[N]T) -> #simd[N]Integer --- +simd_lanes_ne :: proc(a, b: #simd[N]T) -> #simd[N]Integer --- +simd_lanes_lt :: proc(a, b: #simd[N]T) -> #simd[N]Integer --- +simd_lanes_le :: proc(a, b: #simd[N]T) -> #simd[N]Integer --- +simd_lanes_gt :: proc(a, b: #simd[N]T) -> #simd[N]Integer --- +simd_lanes_ge :: proc(a, b: #simd[N]T) -> #simd[N]Integer --- + +simd_extract :: proc(a: #simd[N]T, idx: uint) -> T --- +simd_replace :: proc(a: #simd[N]T, idx: uint, elem: T) -> #simd[N]T --- + +simd_reduce_add_ordered :: proc(a: #simd[N]T) -> T --- +simd_reduce_mul_ordered :: proc(a: #simd[N]T) -> T --- +simd_reduce_min :: proc(a: #simd[N]T) -> T --- +simd_reduce_max :: proc(a: #simd[N]T) -> T --- +simd_reduce_and :: proc(a: #simd[N]T) -> T --- +simd_reduce_or :: proc(a: #simd[N]T) -> T --- +simd_reduce_xor :: proc(a: #simd[N]T) -> T --- + +simd_shuffle :: proc(a, b: #simd[N]T, indices: ..int) -> #simd[len(indices)]T --- +simd_select :: proc(cond: #simd[N]boolean_or_integer, true, false: #simd[N]T) -> #simd[N]T --- + +// Lane-wise operations +simd_ceil :: proc(a: #simd[N]any_float) -> #simd[N]any_float --- +simd_floor :: proc(a: #simd[N]any_float) -> #simd[N]any_float --- +simd_trunc :: proc(a: #simd[N]any_float) -> #simd[N]any_float --- +// rounding to the nearest integral value; if two values are equally near, rounds to the even one +simd_nearest :: proc(a: #simd[N]any_float) -> #simd[N]any_float --- + +simd_to_bits :: proc(v: #simd[N]T) -> #simd[N]Integer where size_of(T) == size_of(Integer), type_is_unsigned(Integer) --- + +// equivalent a swizzle with descending indices, e.g. reserve(a, 3, 2, 1, 0) +simd_reverse :: proc(a: #simd[N]T) -> #simd[N]T --- + +simd_rotate_left :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T --- +simd_rotate_right :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T --- + + +// WASM targets only +wasm_memory_grow :: proc(index, delta: uintptr) -> int --- +wasm_memory_size :: proc(index: uintptr) -> int --- + +// `timeout_ns` is maximum number of nanoseconds the calling thread will be blocked for +// A negative value will be blocked forever +// Return value: +// 0 - indicates that the thread blocked and then was woken up +// 1 - the loaded value from `ptr` did not match `expected`, the thread did not block +// 2 - the thread blocked, but the timeout +wasm_memory_atomic_wait32 :: proc(ptr: ^u32, expected: u32, timeout_ns: i64) -> u32 --- +wasm_memory_atomic_notify32 :: proc(ptr: ^u32, waiters: u32) -> (waiters_woken_up: u32) --- + +// x86 Targets (i386, amd64) +x86_cpuid :: proc(ax, cx: u32) -> (eax, ebc, ecx, edx: u32) --- +x86_xgetbv :: proc(cx: u32) -> (eax, edx: u32) --- + + +// Darwin targets only +objc_object :: struct{} +objc_selector :: struct{} +objc_class :: struct{} +objc_id :: ^objc_object +objc_SEL :: ^objc_selector +objc_Class :: ^objc_class + +objc_find_selector :: proc($name: string) -> objc_SEL --- +objc_register_selector :: proc($name: string) -> objc_SEL --- +objc_find_class :: proc($name: string) -> objc_Class --- +objc_register_class :: proc($name: string) -> objc_Class --- // Internal compiler use only diff --git a/core/io/io.odin b/core/io/io.odin index b4757f8e5..3ad34d607 100644 --- a/core/io/io.odin +++ b/core/io/io.odin @@ -1,9 +1,12 @@ +// package io provides basic interfaces for generic data stream primitives. +// The purpose of this package is wrap existing data structures and their +// operations into an abstracted stream interface. package io import "core:intrinsics" -import "core:runtime" import "core:unicode/utf8" +// Seek whence values Seek_From :: enum { Start = 0, // seek relative to the origin of the file Current = 1, // seek relative to the current offset @@ -139,6 +142,10 @@ destroy :: proc(s: Stream) -> Error { return .Empty } +// read reads up to len(p) bytes into s. It returns the number of bytes read and any error if occurred. +// +// When read encounters an .EOF or error after successfully reading n > 0 bytes, it returns the number of +// bytes read along with the error. read :: proc(s: Reader, p: []byte, n_read: ^int = nil) -> (n: int, err: Error) { if s.stream_vtable != nil && s.impl_read != nil { n, err = s->impl_read(p) @@ -150,6 +157,7 @@ read :: proc(s: Reader, p: []byte, n_read: ^int = nil) -> (n: int, err: Error) { return 0, .Empty } +// write writes up to len(p) bytes into s. It returns the number of bytes written and any error if occurred. write :: proc(s: Writer, p: []byte, n_written: ^int = nil) -> (n: int, err: Error) { if s.stream_vtable != nil && s.impl_write != nil { n, err = s->impl_write(p) @@ -161,6 +169,13 @@ write :: proc(s: Writer, p: []byte, n_written: ^int = nil) -> (n: int, err: Erro return 0, .Empty } +// seek sets the offset of the next read or write to offset. +// +// .Start means seek relative to the origin of the file. +// .Current means seek relative to the current offset. +// .End means seek relative to the end. +// +// seek returns the new offset to the start of the file/stream, and any error if occurred. seek :: proc(s: Seeker, offset: i64, whence: Seek_From) -> (n: i64, err: Error) { if s.stream_vtable != nil && s.impl_seek != nil { return s->impl_seek(offset, whence) @@ -168,6 +183,8 @@ seek :: proc(s: Seeker, offset: i64, whence: Seek_From) -> (n: i64, err: Error) return 0, .Empty } +// The behaviour of close after the first call is stream implementation defined. +// Different streams may document their own behaviour. close :: proc(s: Closer) -> Error { if s.stream_vtable != nil && s.impl_close != nil { return s->impl_close() @@ -184,6 +201,7 @@ flush :: proc(s: Flusher) -> Error { return .None } +// size returns the size of the stream. If the stream does not support querying its size, 0 will be returned. size :: proc(s: Stream) -> i64 { if s.stream_vtable == nil { return 0 @@ -214,7 +232,12 @@ size :: proc(s: Stream) -> i64 { - +// read_at reads len(p) bytes into p starting with the provided offset in the underlying Reader_At stream r. +// It returns the number of bytes read and any error if occurred. +// +// When read_at returns n < len(p), it returns a non-nil Error explaining why. +// +// If n == len(p), err may be either nil or .EOF read_at :: proc(r: Reader_At, p: []byte, offset: i64, n_read: ^int = nil) -> (n: int, err: Error) { defer if n_read != nil { n_read^ += n @@ -230,11 +253,7 @@ read_at :: proc(r: Reader_At, p: []byte, offset: i64, n_read: ^int = nil) -> (n: return 0, .Empty } - curr_offset: i64 - curr_offset, err = r->impl_seek(offset, .Current) - if err != nil { - return 0, err - } + curr_offset := r->impl_seek(offset, .Current) or_return n, err = r->impl_read(p) _, err1 := r->impl_seek(curr_offset, .Start) @@ -245,6 +264,11 @@ read_at :: proc(r: Reader_At, p: []byte, offset: i64, n_read: ^int = nil) -> (n: } +// write_at writes len(p) bytes into p starting with the provided offset in the underlying Writer_At stream w. +// It returns the number of bytes written and any error if occurred. +// +// If write_at is writing to a Writer_At which has a seek offset, then write_at should not affect the underlying +// seek offset. write_at :: proc(w: Writer_At, p: []byte, offset: i64, n_written: ^int = nil) -> (n: int, err: Error) { defer if n_written != nil { n_written^ += n @@ -294,6 +318,7 @@ read_from :: proc(w: Reader_From, r: Reader) -> (n: i64, err: Error) { } +// read_byte reads and returns the next byte from r. read_byte :: proc(r: Byte_Reader, n_read: ^int = nil) -> (b: byte, err: Error) { defer if err == nil && n_read != nil { n_read^ += 1 @@ -347,6 +372,7 @@ _write_byte :: proc(w: Byte_Writer, c: byte, n_written: ^int = nil) -> (err: Err return err } +// read_rune reads a single UTF-8 encoded Unicode codepoint and returns the rune and its size in bytes. read_rune :: proc(br: Rune_Reader, n_read: ^int = nil) -> (ch: rune, size: int, err: Error) { defer if err == nil && n_read != nil { n_read^ += size @@ -405,10 +431,12 @@ unread_rune :: proc(s: Rune_Scanner) -> Error { } +// write_string writes the contents of the string s to w. write_string :: proc(s: Writer, str: string, n_written: ^int = nil) -> (n: int, err: Error) { return write(s, transmute([]byte)str, n_written) } +// write_rune writes a UTF-8 encoded rune to w. write_rune :: proc(s: Writer, r: rune, n_written: ^int = nil) -> (size: int, err: Error) { defer if err == nil && n_written != nil { n_written^ += size @@ -430,12 +458,16 @@ write_rune :: proc(s: Writer, r: rune, n_written: ^int = nil) -> (size: int, err } - +// read_full expected exactly len(buf) bytes from r into buf. read_full :: proc(r: Reader, buf: []byte) -> (n: int, err: Error) { return read_at_least(r, buf, len(buf)) } +// read_at_least reads from r into buf until it has read at least min bytes. It returns the number +// of bytes copied and an error if fewer bytes were read. `.EOF` is only returned if no bytes were read. +// `.Unexpected_EOF` is returned when an `.EOF ` is returned by the passed Reader after reading +// fewer than min bytes. If len(buf) is less than min, `.Short_Buffer` is returned. read_at_least :: proc(r: Reader, buf: []byte, min: int) -> (n: int, err: Error) { if len(buf) < min { return 0, .Short_Buffer @@ -515,7 +547,7 @@ _copy_buffer :: proc(dst: Writer, src: Reader, buf: []byte) -> (written: i64, er } } // NOTE(bill): alloca is fine here - buf = transmute([]byte)runtime.Raw_Slice{intrinsics.alloca(size, 2*align_of(rawptr)), size} + buf = intrinsics.alloca(size, 2*align_of(rawptr))[:size] } for { nr, er := read(src, buf) diff --git a/core/io/util.odin b/core/io/util.odin index 9d253807b..46aa97919 100644 --- a/core/io/util.odin +++ b/core/io/util.odin @@ -247,6 +247,30 @@ write_quoted_string :: proc(w: Writer, str: string, quote: byte = '"', n_written return } +// writer append a quoted rune into the byte buffer, return the written size +write_quoted_rune :: proc(w: Writer, r: rune) -> (n: int) { + _write_byte :: #force_inline proc(w: Writer, c: byte) -> int { + err := write_byte(w, c) + return 1 if err == nil else 0 + } + + quote := byte('\'') + n += _write_byte(w, quote) + buf, width := utf8.encode_rune(r) + if width == 1 && r == utf8.RUNE_ERROR { + n += _write_byte(w, '\\') + n += _write_byte(w, 'x') + n += _write_byte(w, DIGITS_LOWER[buf[0]>>4]) + n += _write_byte(w, DIGITS_LOWER[buf[0]&0xf]) + } else { + i, _ := write_escaped_rune(w, r, quote) + n += i + } + n += _write_byte(w, quote) + return +} + + Tee_Reader :: struct { diff --git a/core/log/file_console_logger.odin b/core/log/file_console_logger.odin index cc019617f..7f0d3b07a 100644 --- a/core/log/file_console_logger.odin +++ b/core/log/file_console_logger.odin @@ -67,7 +67,7 @@ file_console_logger_proc :: proc(logger_data: rawptr, level: Level, text: string h = data.file_handle } backing: [1024]byte //NOTE(Hoej): 1024 might be too much for a header backing, unless somebody has really long paths. - buf := strings.builder_from_slice(backing[:]) + buf := strings.builder_from_bytes(backing[:]) do_level_header(options, level, &buf) diff --git a/core/math/big/common.odin b/core/math/big/common.odin index 2b34a9163..74a641d83 100644 --- a/core/math/big/common.odin +++ b/core/math/big/common.odin @@ -172,7 +172,7 @@ Error :: enum int { Unimplemented = 127, } -Error_String :: #partial [Error]string{ +Error_String :: #sparse[Error]string{ .Okay = "Okay", .Out_Of_Memory = "Out of memory", .Invalid_Pointer = "Invalid pointer", @@ -182,6 +182,7 @@ Error_String :: #partial [Error]string{ .Max_Iterations_Reached = "Max iterations reached", .Buffer_Overflow = "Buffer overflow", .Integer_Overflow = "Integer overflow", + .Integer_Underflow = "Integer underflow", .Division_by_Zero = "Division by zero", .Math_Domain_Error = "Math domain error", diff --git a/core/math/ease/ease.odin b/core/math/ease/ease.odin new file mode 100644 index 000000000..0bd7c3641 --- /dev/null +++ b/core/math/ease/ease.odin @@ -0,0 +1,495 @@ +// easing procedures and flux easing used for animations +package ease + +import "core:math" +import "core:intrinsics" +import "core:time" + +@(private) PI_2 :: math.PI / 2 + +// converted to odin from https://github.com/warrenm/AHEasing +// with additional enum based call + +// Modeled after the parabola y = x^2 +quadratic_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + return p * p +} + +// Modeled after the parabola y = -x^2 + 2x +quadratic_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + return -(p * (p - 2)) +} + +// Modeled after the piecewise quadratic +// y = (1/2)((2x)^2) ; [0, 0.5) +// y = -(1/2)((2x-1)*(2x-3) - 1) ; [0.5, 1] +quadratic_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + if p < 0.5 { + return 2 * p * p + } else { + return (-2 * p * p) + (4 * p) - 1 + } +} + +// Modeled after the cubic y = x^3 +cubic_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + return p * p * p +} + +// Modeled after the cubic y = (x - 1)^3 + 1 +cubic_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + f := p - 1 + return f * f * f + 1 +} + +// Modeled after the piecewise cubic +// y = (1/2)((2x)^3) ; [0, 0.5) +// y = (1/2)((2x-2)^3 + 2) ; [0.5, 1] +cubic_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + if p < 0.5 { + return 4 * p * p * p + } else { + f := (2 * p) - 2 + return 0.5 * f * f * f + 1 + } +} + +// Modeled after the quartic x^4 +quartic_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + return p * p * p * p +} + +// Modeled after the quartic y = 1 - (x - 1)^4 +quartic_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + f := p - 1 + return f * f * f * (1 - p) + 1 +} + +// Modeled after the piecewise quartic +// y = (1/2)((2x)^4) ; [0, 0.5) +// y = -(1/2)((2x-2)^4 - 2) ; [0.5, 1] +quartic_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + if p < 0.5 { + return 8 * p * p * p * p + } else { + f := p - 1 + return -8 * f * f * f * f + 1 + } +} + +// Modeled after the quintic y = x^5 +quintic_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + return p * p * p * p * p +} + +// Modeled after the quintic y = (x - 1)^5 + 1 +quintic_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + f := p - 1 + return f * f * f * f * f + 1 +} + +// Modeled after the piecewise quintic +// y = (1/2)((2x)^5) ; [0, 0.5) +// y = (1/2)((2x-2)^5 + 2) ; [0.5, 1] +quintic_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + if p < 0.5 { + return 16 * p * p * p * p * p + } else { + f := (2 * p) - 2 + return 0.5 * f * f * f * f * f + 1 + } +} + +// Modeled after quarter-cycle of sine wave +sine_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + return math.sin((p - 1) * PI_2) + 1 +} + +// Modeled after quarter-cycle of sine wave (different phase) +sine_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + return math.sin(p * PI_2) +} + +// Modeled after half sine wave +sine_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + return 0.5 * (1 - math.cos(p * math.PI)) +} + +// Modeled after shifted quadrant IV of unit circle +circular_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + return 1 - math.sqrt(1 - (p * p)) +} + +// Modeled after shifted quadrant II of unit circle +circular_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + return math.sqrt((2 - p) * p) +} + +// Modeled after the piecewise circular function +// y = (1/2)(1 - sqrt(1 - 4x^2)) ; [0, 0.5) +// y = (1/2)(sqrt(-(2x - 3)*(2x - 1)) + 1) ; [0.5, 1] +circular_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + if p < 0.5 { + return 0.5 * (1 - math.sqrt(1 - 4 * (p * p))) + } else { + return 0.5 * (math.sqrt(-((2 * p) - 3) * ((2 * p) - 1)) + 1) + } +} + +// Modeled after the exponential function y = 2^(10(x - 1)) +exponential_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + return p == 0.0 ? p : math.pow(2, 10 * (p - 1)) +} + +// Modeled after the exponential function y = -2^(-10x) + 1 +exponential_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + return p == 1.0 ? p : 1 - math.pow(2, -10 * p) +} + +// Modeled after the piecewise exponential +// y = (1/2)2^(10(2x - 1)) ; [0,0.5) +// y = -(1/2)*2^(-10(2x - 1))) + 1 ; [0.5,1] +exponential_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + if p == 0.0 || p == 1.0 { + return p + } + + if p < 0.5 { + return 0.5 * math.pow(2, (20 * p) - 10) + } else { + return -0.5 * math.pow(2, (-20 * p) + 10) + 1 + } +} + +// Modeled after the damped sine wave y = sin(13pi/2*x)*pow(2, 10 * (x - 1)) +elastic_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + return math.sin(13 * PI_2 * p) * math.pow(2, 10 * (p - 1)) +} + +// Modeled after the damped sine wave y = sin(-13pi/2*(x + 1))*pow(2, -10x) + 1 +elastic_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + return math.sin(-13 * PI_2 * (p + 1)) * math.pow(2, -10 * p) + 1 +} + +// Modeled after the piecewise exponentially-damped sine wave: +// y = (1/2)*sin(13pi/2*(2*x))*pow(2, 10 * ((2*x) - 1)) ; [0,0.5) +// y = (1/2)*(sin(-13pi/2*((2x-1)+1))*pow(2,-10(2*x-1)) + 2) ; [0.5, 1] +elastic_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + if p < 0.5 { + return 0.5 * math.sin(13 * PI_2 * (2 * p)) * math.pow(2, 10 * ((2 * p) - 1)) + } else { + return 0.5 * (math.sin(-13 * PI_2 * ((2 * p - 1) + 1)) * math.pow(2, -10 * (2 * p - 1)) + 2) + } +} + +// Modeled after the overshooting cubic y = x^3-x*sin(x*pi) +back_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + return p * p * p - p * math.sin(p * math.PI) +} + +// Modeled after overshooting cubic y = 1-((1-x)^3-(1-x)*sin((1-x)*pi)) +back_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + f := 1 - p + return 1 - (f * f * f - f * math.sin(f * math.PI)) +} + +// Modeled after the piecewise overshooting cubic function: +// y = (1/2)*((2x)^3-(2x)*sin(2*x*pi)) ; [0, 0.5) +// y = (1/2)*(1-((1-x)^3-(1-x)*sin((1-x)*pi))+1) ; [0.5, 1] +back_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + if p < 0.5 { + f := 2 * p + return 0.5 * (f * f * f - f * math.sin(f * math.PI)) + } else { + f := (1 - (2*p - 1)) + return 0.5 * (1 - (f * f * f - f * math.sin(f * math.PI))) + 0.5 + } +} + +bounce_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + return 1 - bounce_out(1 - p) +} + +bounce_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + if p < 4/11.0 { + return (121 * p * p)/16.0 + } else if p < 8/11.0 { + return (363/40.0 * p * p) - (99/10.0 * p) + 17/5.0 + } else if p < 9/10.0 { + return (4356/361.0 * p * p) - (35442/1805.0 * p) + 16061/1805.0 + } else { + return (54/5.0 * p * p) - (513/25.0 * p) + 268/25.0 + } +} + +bounce_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + if p < 0.5 { + return 0.5 * bounce_in(p*2) + } else { + return 0.5 * bounce_out(p * 2 - 1) + 0.5 + } +} + +// additional enum variant + +Ease :: enum { + Linear, + + Quadratic_In, + Quadratic_Out, + Quadratic_In_Out, + + Cubic_In, + Cubic_Out, + Cubic_In_Out, + + Quartic_In, + Quartic_Out, + Quartic_In_Out, + + Quintic_In, + Quintic_Out, + Quintic_In_Out, + + Sine_In, + Sine_Out, + Sine_In_Out, + + Circular_In, + Circular_Out, + Circular_In_Out, + + Exponential_In, + Exponential_Out, + Exponential_In_Out, + + Elastic_In, + Elastic_Out, + Elastic_In_Out, + + Back_In, + Back_Out, + Back_In_Out, + + Bounce_In, + Bounce_Out, + Bounce_In_Out, +} + +ease :: proc "contextless" (type: Ease, p: $T) -> T + where intrinsics.type_is_float(T) { + switch type { + case .Linear: return p + + case .Quadratic_In: return quadratic_in(p) + case .Quadratic_Out: return quadratic_out(p) + case .Quadratic_In_Out: return quadratic_in_out(p) + + case .Cubic_In: return cubic_in(p) + case .Cubic_Out: return cubic_out(p) + case .Cubic_In_Out: return cubic_in_out(p) + + case .Quartic_In: return quartic_in(p) + case .Quartic_Out: return quartic_out(p) + case .Quartic_In_Out: return quartic_in_out(p) + + case .Quintic_In: return quintic_in(p) + case .Quintic_Out: return quintic_out(p) + case .Quintic_In_Out: return quintic_in_out(p) + + case .Sine_In: return sine_in(p) + case .Sine_Out: return sine_out(p) + case .Sine_In_Out: return sine_in_out(p) + + case .Circular_In: return circular_in(p) + case .Circular_Out: return circular_out(p) + case .Circular_In_Out: return circular_in_out(p) + + case .Exponential_In: return exponential_in(p) + case .Exponential_Out: return exponential_out(p) + case .Exponential_In_Out: return exponential_in_out(p) + + case .Elastic_In: return elastic_in(p) + case .Elastic_Out: return elastic_out(p) + case .Elastic_In_Out: return elastic_in_out(p) + + case .Back_In: return back_in(p) + case .Back_Out: return back_out(p) + case .Back_In_Out: return back_in_out(p) + + case .Bounce_In: return bounce_in(p) + case .Bounce_Out: return bounce_out(p) + case .Bounce_In_Out: return bounce_in_out(p) + } + + // in case type was invalid + return 0 +} +Flux_Map :: struct($T: typeid) { + values: map[^T]Flux_Tween(T), + keys_to_be_deleted: [dynamic]^T, +} + +Flux_Tween :: struct($T: typeid) { + value: ^T, + start: T, + diff: T, + goal: T, + + delay: f64, // in seconds + duration: time.Duration, + + progress: f64, + rate: f64, + type: Ease, + + inited: bool, + + // callbacks, data can be set, will be pushed to callback + data: rawptr, // by default gets set to value input + on_start: proc(flux: ^Flux_Map(T), data: rawptr), + on_update: proc(flux: ^Flux_Map(T), data: rawptr), + on_complete: proc(flux: ^Flux_Map(T), data: rawptr), +} + +// init flux map to a float type and a wanted cap +flux_init :: proc($T: typeid, value_capacity := 8) -> Flux_Map(T) where intrinsics.type_is_float(T) { + return { + values = make(map[^T]Flux_Tween(T), value_capacity), + keys_to_be_deleted = make([dynamic]^T, 0, value_capacity) + } +} + +// delete map content +flux_destroy :: proc(flux: Flux_Map($T)) where intrinsics.type_is_float(T) { + delete(flux.values) + delete(flux.keys_to_be_deleted) +} + +// clear map content, stops all animations +flux_clear :: proc(flux: ^Flux_Map($T)) where intrinsics.type_is_float(T) { + clear(&flux.values) +} + +// append / overwrite existing tween value to parameters +// rest is initialized in flux_tween_init, inside update +// return value can be used to set callbacks +flux_to :: proc( + flux: ^Flux_Map($T), + value: ^T, + goal: T, + type: Ease = .Quadratic_Out, + duration: time.Duration = time.Second, + delay: f64 = 0, +) -> (tween: ^Flux_Tween(T)) where intrinsics.type_is_float(T) { + if res, ok := &flux.values[value]; ok { + tween = res + } else { + flux.values[value] = {} + tween = &flux.values[value] + } + + tween^ = { + value = value, + goal = goal, + duration = duration, + delay = delay, + type = type, + data = value, + } + + return +} + +// init internal properties +flux_tween_init :: proc(tween: ^Flux_Tween($T), duration: time.Duration) where intrinsics.type_is_float(T) { + tween.inited = true + tween.start = tween.value^ + tween.diff = tween.goal - tween.value^ + s := time.duration_seconds(duration) + tween.rate = duration > 0 ? 1.0 / s : 0 + tween.progress = duration > 0 ? 0 : 1 +} + +// update all tweens, wait for their delay if one exists +// calls callbacks in all stages, when they're filled +// deletes tween from the map after completion +flux_update :: proc(flux: ^Flux_Map($T), dt: f64) where intrinsics.type_is_float(T) { + clear(&flux.keys_to_be_deleted) + + for key, tween in &flux.values { + delay_remainder := f64(0) + + // Update delay if necessary. + if tween.delay > 0 { + tween.delay -= dt + + if tween.delay < 0 { + // We finished the delay, but in doing so consumed part of this frame's `dt` budget. + // Keep track of it so we can apply it to this tween without affecting others. + delay_remainder = tween.delay + // We're done with this delay. + tween.delay = 0 + } + } + + // We either had no delay, or the delay has been consumed. + if tween.delay <= 0 { + if !tween.inited { + flux_tween_init(&tween, tween.duration) + + if tween.on_start != nil { + tween.on_start(flux, tween.data) + } + } + + // If part of the `dt` budget was consumed this frame, then `delay_remainder` will be + // that remainder, a negative value. Adding it to `dt` applies what's left of the `dt` + // to the tween so it advances properly, instead of too much or little. + tween.progress += tween.rate * (dt + delay_remainder) + x := tween.progress >= 1 ? 1 : ease(tween.type, tween.progress) + tween.value^ = tween.start + tween.diff * T(x) + + if tween.on_update != nil { + tween.on_update(flux, tween.data) + } + + if tween.progress >= 1 { + // append keys to array that will be deleted after the loop + append(&flux.keys_to_be_deleted, key) + + if tween.on_complete != nil { + tween.on_complete(flux, tween.data) + } + } + } + } + + // loop through keys that should be deleted from the map + if len(flux.keys_to_be_deleted) != 0 { + for key in flux.keys_to_be_deleted { + delete_key(&flux.values, key) + } + } +} + +// stop a specific key inside the map +// returns true when it successfully removed the key +flux_stop :: proc(flux: ^Flux_Map($T), key: ^T) -> bool where intrinsics.type_is_float(T) { + if key in flux.values { + delete_key(&flux.values, key) + return true + } + + return false +} + +// returns the amount of time left for the tween animation, if the key exists in the map +// returns 0 if the tween doesnt exist on the map +flux_tween_time_left :: proc(flux: Flux_Map($T), key: ^T) -> f64 { + if tween, ok := flux.values[key]; ok { + return ((1 - tween.progress) * tween.rate) + tween.delay + } else { + return 0 + } +} diff --git a/core/math/linalg/extended.odin b/core/math/linalg/extended.odin index c2bf5552a..24b7a90fc 100644 --- a/core/math/linalg/extended.odin +++ b/core/math/linalg/extended.odin @@ -426,14 +426,14 @@ distance :: proc(p0, p1: $V/[$N]$E) -> E where IS_NUMERIC(E) { } reflect :: proc(I, N: $T) -> (out: T) where IS_ARRAY(T), IS_FLOAT(ELEM_TYPE(T)) { - b := n * (2 * dot(n, i)) - return i - b + b := N * (2 * dot(N, I)) + return I - b } refract :: proc(I, N: $T) -> (out: T) where IS_ARRAY(T), IS_FLOAT(ELEM_TYPE(T)) { - dv := dot(n, i) + dv := dot(N, I) k := 1 - eta*eta - (1 - dv*dv) - a := i * eta - b := n * eta*dv*math.sqrt(k) + a := I * eta + b := N * eta*dv*math.sqrt(k) return (a - b) * E(int(k >= 0)) } diff --git a/core/math/linalg/glsl/linalg_glsl.odin b/core/math/linalg/glsl/linalg_glsl.odin index 7bc68b964..74753f66f 100644 --- a/core/math/linalg/glsl/linalg_glsl.odin +++ b/core/math/linalg/glsl/linalg_glsl.odin @@ -473,6 +473,25 @@ floor_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {floor(x.x), floor(x.y), fl floor_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {floor(x.x), floor(x.y), floor(x.z), floor(x.w)} } + +round :: proc{ + round_f32, + round_f64, + round_vec2, + round_vec3, + round_vec4, + round_dvec2, + round_dvec3, + round_dvec4, +} +round_vec2 :: proc "c" (x: vec2) -> vec2 { return {round(x.x), round(x.y)} } +round_vec3 :: proc "c" (x: vec3) -> vec3 { return {round(x.x), round(x.y), round(x.z)} } +round_vec4 :: proc "c" (x: vec4) -> vec4 { return {round(x.x), round(x.y), round(x.z), round(x.w)} } +round_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {round(x.x), round(x.y)} } +round_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {round(x.x), round(x.y), round(x.z)} } +round_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {round(x.x), round(x.y), round(x.z), round(x.w)} } + + ceil :: proc{ ceil_f32, ceil_f64, diff --git a/core/math/linalg/glsl/linalg_glsl_math.odin b/core/math/linalg/glsl/linalg_glsl_math.odin index 68f43a2f7..968a3fa5e 100644 --- a/core/math/linalg/glsl/linalg_glsl_math.odin +++ b/core/math/linalg/glsl/linalg_glsl_math.odin @@ -23,6 +23,7 @@ log_f32 :: proc "c" (x: f32) -> f32 { return math.ln(x) } exp2_f32 :: proc "c" (x: f32) -> f32 { return math.pow(f32(2), x) } sign_f32 :: proc "c" (x: f32) -> f32 { return math.sign(x) } floor_f32 :: proc "c" (x: f32) -> f32 { return math.floor(x) } +round_f32 :: proc "c" (x: f32) -> f32 { return math.round(x) } ceil_f32 :: proc "c" (x: f32) -> f32 { return math.ceil(x) } mod_f32 :: proc "c" (x, y: f32) -> f32 { return math.mod(x, y) } fract_f32 :: proc "c" (x: f32) -> f32 { @@ -53,6 +54,7 @@ log_f64 :: proc "c" (x: f64) -> f64 { return math.ln(x) } exp2_f64 :: proc "c" (x: f64) -> f64 { return math.pow(f64(2), x) } sign_f64 :: proc "c" (x: f64) -> f64 { return math.sign(x) } floor_f64 :: proc "c" (x: f64) -> f64 { return math.floor(x) } +round_f64 :: proc "c" (x: f64) -> f64 { return math.round(x) } ceil_f64 :: proc "c" (x: f64) -> f64 { return math.ceil(x) } mod_f64 :: proc "c" (x, y: f64) -> f64 { return math.mod(x, y) } fract_f64 :: proc "c" (x: f64) -> f64 { diff --git a/core/math/linalg/hlsl/linalg_hlsl.odin b/core/math/linalg/hlsl/linalg_hlsl.odin index 4391975ba..3f73dcd1f 100644 --- a/core/math/linalg/hlsl/linalg_hlsl.odin +++ b/core/math/linalg/hlsl/linalg_hlsl.odin @@ -551,6 +551,23 @@ floor_double2 :: proc "c" (x: double2) -> double2 { return {floor(x.x), floor(x. floor_double3 :: proc "c" (x: double3) -> double3 { return {floor(x.x), floor(x.y), floor(x.z)} } floor_double4 :: proc "c" (x: double4) -> double4 { return {floor(x.x), floor(x.y), floor(x.z), floor(x.w)} } +round :: proc{ + round_float, + round_double, + round_float2, + round_float3, + round_float4, + round_double2, + round_double3, + round_double4, +} +round_float2 :: proc "c" (x: float2) -> float2 { return {round(x.x), round(x.y)} } +round_float3 :: proc "c" (x: float3) -> float3 { return {round(x.x), round(x.y), round(x.z)} } +round_float4 :: proc "c" (x: float4) -> float4 { return {round(x.x), round(x.y), round(x.z), round(x.w)} } +round_double2 :: proc "c" (x: double2) -> double2 { return {round(x.x), round(x.y)} } +round_double3 :: proc "c" (x: double3) -> double3 { return {round(x.x), round(x.y), round(x.z)} } +round_double4 :: proc "c" (x: double4) -> double4 { return {round(x.x), round(x.y), round(x.z), round(x.w)} } + ceil :: proc{ ceil_float, @@ -570,6 +587,69 @@ ceil_double3 :: proc "c" (x: double3) -> double3 { return {ceil(x.x), ceil(x.y), ceil_double4 :: proc "c" (x: double4) -> double4 { return {ceil(x.x), ceil(x.y), ceil(x.z), ceil(x.w)} } +isfinite_float :: proc "c" (x: float) -> bool { return !isinf_float(x) } +isfinite_float2 :: proc "c" (x: float2) -> bool2 { return {isfinite_float(x.x), isfinite_float(x.y)} } +isfinite_float3 :: proc "c" (x: float3) -> bool3 { return {isfinite_float(x.x), isfinite_float(x.y), isfinite_float(x.z)} } +isfinite_float4 :: proc "c" (x: float4) -> bool4 { return {isfinite_float(x.x), isfinite_float(x.y), isfinite_float(x.z), isfinite_float(x.w)} } +isfinite_double :: proc "c" (x: double) -> bool { return !isinf_double(x) } +isfinite_double2 :: proc "c" (x: double2) -> bool2 { return {isfinite_double(x.x), isfinite_double(x.y)} } +isfinite_double3 :: proc "c" (x: double3) -> bool3 { return {isfinite_double(x.x), isfinite_double(x.y), isfinite_double(x.z)} } +isfinite_double4 :: proc "c" (x: double4) -> bool4 { return {isfinite_double(x.x), isfinite_double(x.y), isfinite_double(x.z), isfinite_double(x.w)} } + +// isfinite is the opposite of isinf and returns true if the number is neither positive-infinite or negative-infinite +isfinite :: proc{ + isfinite_float, + isfinite_float2, + isfinite_float3, + isfinite_float4, + isfinite_double, + isfinite_double2, + isfinite_double3, + isfinite_double4, +} + + +isinf_float :: proc "c" (x: float) -> bool { return x * 0.5 == x } +isinf_float2 :: proc "c" (x: float2) -> bool2 { return {isinf_float(x.x), isinf_float(x.y)} } +isinf_float3 :: proc "c" (x: float3) -> bool3 { return {isinf_float(x.x), isinf_float(x.y), isinf_float(x.z)} } +isinf_float4 :: proc "c" (x: float4) -> bool4 { return {isinf_float(x.x), isinf_float(x.y), isinf_float(x.z), isinf_float(x.w)} } +isinf_double :: proc "c" (x: double) -> bool { return x * 0.5 == x } +isinf_double2 :: proc "c" (x: double2) -> bool2 { return {isinf_double(x.x), isinf_double(x.y)} } +isinf_double3 :: proc "c" (x: double3) -> bool3 { return {isinf_double(x.x), isinf_double(x.y), isinf_double(x.z)} } +isinf_double4 :: proc "c" (x: double4) -> bool4 { return {isinf_double(x.x), isinf_double(x.y), isinf_double(x.z), isinf_double(x.w)} } + +// isinf is the opposite of isfinite and returns true if the number is either positive-infinite or negative-infinite +isinf :: proc{ + isinf_float, + isinf_float2, + isinf_float3, + isinf_float4, + isinf_double, + isinf_double2, + isinf_double3, + isinf_double4, +} + + +isnan_float2 :: proc "c" (x: float2) -> bool2 { return {isnan_float(x.x), isnan_float(x.y)} } +isnan_float3 :: proc "c" (x: float3) -> bool3 { return {isnan_float(x.x), isnan_float(x.y), isnan_float(x.z)} } +isnan_float4 :: proc "c" (x: float4) -> bool4 { return {isnan_float(x.x), isnan_float(x.y), isnan_float(x.z), isnan_float(x.w)} } +isnan_double2 :: proc "c" (x: double2) -> bool2 { return {isnan_double(x.x), isnan_double(x.y)} } +isnan_double3 :: proc "c" (x: double3) -> bool3 { return {isnan_double(x.x), isnan_double(x.y), isnan_double(x.z)} } +isnan_double4 :: proc "c" (x: double4) -> bool4 { return {isnan_double(x.x), isnan_double(x.y), isnan_double(x.z), isnan_double(x.w)} } + +// isnan returns true if the input value is the special case of Not-A-Number +isnan :: proc{ + isnan_float, + isnan_float2, + isnan_float3, + isnan_float4, + isnan_double, + isnan_double2, + isnan_double3, + isnan_double4, +} + fmod :: proc{ fmod_float, fmod_double, diff --git a/core/math/linalg/hlsl/linalg_hlsl_math.odin b/core/math/linalg/hlsl/linalg_hlsl_math.odin index d884c3d31..91c542b59 100644 --- a/core/math/linalg/hlsl/linalg_hlsl_math.odin +++ b/core/math/linalg/hlsl/linalg_hlsl_math.odin @@ -26,7 +26,9 @@ log10_float :: proc "c" (x: float) -> float { return math.log(x, 10) } exp2_float :: proc "c" (x: float) -> float { return math.pow(float(2), x) } sign_float :: proc "c" (x: float) -> float { return math.sign(x) } floor_float :: proc "c" (x: float) -> float { return math.floor(x) } +round_float :: proc "c" (x: float) -> float { return math.round(x) } ceil_float :: proc "c" (x: float) -> float { return math.ceil(x) } +isnan_float :: proc "c" (x: float) -> bool { return math.classify(x) == .NaN} fmod_float :: proc "c" (x, y: float) -> float { return math.mod(x, y) } frac_float :: proc "c" (x: float) -> float { if x >= 0 { @@ -35,6 +37,7 @@ frac_float :: proc "c" (x: float) -> float { return math.trunc(-x) + x } + cos_double :: proc "c" (x: double) -> double { return math.cos(x) } sin_double :: proc "c" (x: double) -> double { return math.sin(x) } tan_double :: proc "c" (x: double) -> double { return math.tan(x) } @@ -59,7 +62,9 @@ log10_double :: proc "c" (x: double) -> double { return math.log(x, 10) exp2_double :: proc "c" (x: double) -> double { return math.pow(double(2), x) } sign_double :: proc "c" (x: double) -> double { return math.sign(x) } floor_double :: proc "c" (x: double) -> double { return math.floor(x) } +round_double :: proc "c" (x: double) -> double { return math.round(x) } ceil_double :: proc "c" (x: double) -> double { return math.ceil(x) } +isnan_double :: proc "c" (x: double) -> bool { return math.classify(x) == .NaN} fmod_double :: proc "c" (x, y: double) -> double { return math.mod(x, y) } frac_double :: proc "c" (x: double) -> double { if x >= 0 { diff --git a/core/math/linalg/specific.odin b/core/math/linalg/specific.odin index cb007bd91..c4ecb194f 100644 --- a/core/math/linalg/specific.odin +++ b/core/math/linalg/specific.odin @@ -476,24 +476,24 @@ quaternion_angle_axis :: proc{ angle_from_quaternion_f16 :: proc(q: Quaternionf16) -> f16 { if abs(q.w) > math.SQRT_THREE*0.5 { - return math.asin(q.x*q.x + q.y*q.y + q.z*q.z) * 2 + return math.asin(math.sqrt(q.x*q.x + q.y*q.y + q.z*q.z)) * 2 } - return math.cos(q.x) * 2 + return math.acos(q.w) * 2 } angle_from_quaternion_f32 :: proc(q: Quaternionf32) -> f32 { if abs(q.w) > math.SQRT_THREE*0.5 { - return math.asin(q.x*q.x + q.y*q.y + q.z*q.z) * 2 + return math.asin(math.sqrt(q.x*q.x + q.y*q.y + q.z*q.z)) * 2 } - return math.cos(q.x) * 2 + return math.acos(q.w) * 2 } angle_from_quaternion_f64 :: proc(q: Quaternionf64) -> f64 { if abs(q.w) > math.SQRT_THREE*0.5 { - return math.asin(q.x*q.x + q.y*q.y + q.z*q.z) * 2 + return math.asin(math.sqrt(q.x*q.x + q.y*q.y + q.z*q.z)) * 2 } - return math.cos(q.x) * 2 + return math.acos(q.w) * 2 } angle_from_quaternion :: proc{ angle_from_quaternion_f16, diff --git a/core/math/math.odin b/core/math/math.odin index b81598da9..88cba965a 100644 --- a/core/math/math.odin +++ b/core/math/math.odin @@ -396,7 +396,7 @@ trunc_f16 :: proc "contextless" (x: f16) -> f16 { e := (x >> shift) & mask - bias if e < shift { - x &= ~(1 << (shift-e)) - 1 + x &~= 1 << (shift-e) - 1 } return transmute(f16)x } @@ -428,7 +428,7 @@ trunc_f32 :: proc "contextless" (x: f32) -> f32 { e := (x >> shift) & mask - bias if e < shift { - x &= ~(1 << (shift-e)) - 1 + x &~= 1 << (shift-e) - 1 } return transmute(f32)x } @@ -460,7 +460,7 @@ trunc_f64 :: proc "contextless" (x: f64) -> f64 { e := (x >> shift) & mask - bias if e < shift { - x &= ~(1 << (shift-e)) - 1 + x &~= 1 << (shift-e) - 1 } return transmute(f64)x } @@ -473,6 +473,7 @@ trunc_f64 :: proc "contextless" (x: f64) -> f64 { } trunc_f64le :: proc "contextless" (x: f64le) -> f64le { return #force_inline f64le(trunc_f64(f64(x))) } trunc_f64be :: proc "contextless" (x: f64be) -> f64be { return #force_inline f64be(trunc_f64(f64(x))) } +// Removes the fractional part of the value, i.e. rounds towards zero. trunc :: proc{ trunc_f16, trunc_f16le, trunc_f16be, trunc_f32, trunc_f32le, trunc_f32be, @@ -958,7 +959,7 @@ classify_f16 :: proc "contextless" (x: f16) -> Float_Class { return .Neg_Zero } return .Zero - case x*0.5 == x: + case x*0.25 == x: if x < 0 { return .Neg_Inf } @@ -1027,6 +1028,8 @@ classify_f64 :: proc "contextless" (x: f64) -> Float_Class { } classify_f64le :: proc "contextless" (x: f64le) -> Float_Class { return #force_inline classify_f64(f64(x)) } classify_f64be :: proc "contextless" (x: f64be) -> Float_Class { return #force_inline classify_f64(f64(x)) } +// Returns the `Float_Class` of the value, i.e. whether normal, subnormal, zero, negative zero, NaN, infinity or +// negative infinity. classify :: proc{ classify_f16, classify_f16le, classify_f16be, classify_f32, classify_f32le, classify_f32be, @@ -1203,7 +1206,7 @@ prod :: proc "contextless" (x: $T/[]$E) -> (res: E) return } -cumsum_inplace :: proc "contextless" (x: $T/[]$E) -> T +cumsum_inplace :: proc "contextless" (x: $T/[]$E) where intrinsics.type_is_numeric(E) { for i in 1.. T where intrinsics.type_is_float(T) { } asin :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) { - return atan2(x, 1 + sqrt(1 - x*x)) + return atan2(x, sqrt(1 - x*x)) } acos :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) { @@ -1715,4 +1718,22 @@ F32_BIAS :: 0x7f F64_MASK :: 0x7ff F64_SHIFT :: 64 - 12 -F64_BIAS :: 0x3ff \ No newline at end of file +F64_BIAS :: 0x3ff + +INF_F16 :f16: 0h7C00 +NEG_INF_F16 :f16: 0hFC00 + +SNAN_F16 :f16: 0h7C01 +QNAN_F16 :f16: 0h7E01 + +INF_F32 :f32: 0h7F80_0000 +NEG_INF_F32 :f32: 0hFF80_0000 + +SNAN_F32 :f32: 0hFF80_0001 +QNAN_F32 :f32: 0hFFC0_0001 + +INF_F64 :f64: 0h7FF0_0000_0000_0000 +NEG_INF_F64 :f64: 0hFFF0_0000_0000_0000 + +SNAN_F64 :f64: 0h7FF0_0000_0000_0001 +QNAN_F64 :f64: 0h7FF8_0000_0000_0001 diff --git a/core/math/noise/internal.odin b/core/math/noise/internal.odin new file mode 100644 index 000000000..5837f9235 --- /dev/null +++ b/core/math/noise/internal.odin @@ -0,0 +1,734 @@ +/* + OpenSimplex2 noise implementation. + + Ported from https://github.com/KdotJPG/OpenSimplex2. + Copyright 2022 Yuki2 (https://github.com/NoahR02) +*/ +//+private +package math_noise + +/* + Private implementation details follow. +*/ + +PRIME_X :: i64(0x5205402B9270C86F) +PRIME_Y :: i64(0x598CD327003817B5) +PRIME_Z :: i64(0x5BCC226E9FA0BACB) +PRIME_W :: i64(0x56CC5227E58F554B) + +HASH_MULTIPLIER :: i64(0x53A3F72DEEC546F5) +SEED_FLIP_3D :: i64(-0x52D547B2E96ED629) +SEED_OFFSET_4D :: i64(0xE83DC3E0DA7164D) + +ROOT_2_OVER_2 :: f64(0.7071067811865476) +SKEW_2D :: f64(0.366025403784439) +UNSKEW_2D :: f64(-0.21132486540518713) +ROOT_3_OVER_3 :: f64(0.577350269189626) + +FALLBACK_ROTATE_3D :: f64(2.0) / f64(3.0) +ROTATE_3D_ORTHOGONALIZER :: f64(UNSKEW_2D) + +SKEW_4D :: f32(0hbe0d8369) +UNSKEW_4D :: f32(0.309016994374947) +LATTICE_STEP_4D :: f32(0.2) + +N_GRADS_2D_EXPONENT :: 7 +N_GRADS_3D_EXPONENT :: 8 +N_GRADS_4D_EXPONENT :: 9 +N_GRADS_2D :: 1 << N_GRADS_2D_EXPONENT +N_GRADS_3D :: 1 << N_GRADS_3D_EXPONENT +N_GRADS_4D :: 1 << N_GRADS_4D_EXPONENT + +NORMALIZER_2D :: f64(0.01001634121365712) +NORMALIZER_3D :: f64(0.07969837668935331) +NORMALIZER_4D :: f64(0.0220065933241897) +RSQUARED_2D :: f32(0.5) +RSQUARED_3D :: f32(0.6) +RSQUARED_4D :: f32(0.6) + +GRADIENTS_2D := [N_GRADS_2D * 2]f32{ + 0h4218d2da, 0h42b87975, 0h42b87975, 0h4218d2da, 0h42b87975, 0hc218d2da, 0h4218d2da, 0hc2b87975, + 0hc218d2da, 0hc2b87975, 0hc2b87975, 0hc218d2da, 0hc2b87975, 0h4218d2da, 0hc218d2da, 0h42b87975, + 0h4150804d, 0h42c5f72a, 0h42731b78, 0h429e696c, 0h429e696c, 0h42731b78, 0h42c5f72a, 0h4150804d, + 0h42c5f72a, 0hc150804d, 0h429e696c, 0hc2731b78, 0h42731b78, 0hc29e696c, 0h4150804d, 0hc2c5f72a, + 0hc150804d, 0hc2c5f72a, 0hc2731b78, 0hc29e696c, 0hc29e696c, 0hc2731b78, 0hc2c5f72a, 0hc150804d, + 0hc2c5f72a, 0h4150804d, 0hc29e696c, 0h42731b78, 0hc2731b78, 0h429e696c, 0hc150804d, 0h42c5f72a, + 0h4218d2da, 0h42b87975, 0h42b87975, 0h4218d2da, 0h42b87975, 0hc218d2da, 0h4218d2da, 0hc2b87975, + 0hc218d2da, 0hc2b87975, 0hc2b87975, 0hc218d2da, 0hc2b87975, 0h4218d2da, 0hc218d2da, 0h42b87975, + 0h4150804d, 0h42c5f72a, 0h42731b78, 0h429e696c, 0h429e696c, 0h42731b78, 0h42c5f72a, 0h4150804d, + 0h42c5f72a, 0hc150804d, 0h429e696c, 0hc2731b78, 0h42731b78, 0hc29e696c, 0h4150804d, 0hc2c5f72a, + 0hc150804d, 0hc2c5f72a, 0hc2731b78, 0hc29e696c, 0hc29e696c, 0hc2731b78, 0hc2c5f72a, 0hc150804d, + 0hc2c5f72a, 0h4150804d, 0hc29e696c, 0h42731b78, 0hc2731b78, 0h429e696c, 0hc150804d, 0h42c5f72a, + 0h4218d2da, 0h42b87975, 0h42b87975, 0h4218d2da, 0h42b87975, 0hc218d2da, 0h4218d2da, 0hc2b87975, + 0hc218d2da, 0hc2b87975, 0hc2b87975, 0hc218d2da, 0hc2b87975, 0h4218d2da, 0hc218d2da, 0h42b87975, + 0h4150804d, 0h42c5f72a, 0h42731b78, 0h429e696c, 0h429e696c, 0h42731b78, 0h42c5f72a, 0h4150804d, + 0h42c5f72a, 0hc150804d, 0h429e696c, 0hc2731b78, 0h42731b78, 0hc29e696c, 0h4150804d, 0hc2c5f72a, + 0hc150804d, 0hc2c5f72a, 0hc2731b78, 0hc29e696c, 0hc29e696c, 0hc2731b78, 0hc2c5f72a, 0hc150804d, + 0hc2c5f72a, 0h4150804d, 0hc29e696c, 0h42731b78, 0hc2731b78, 0h429e696c, 0hc150804d, 0h42c5f72a, + 0h4218d2da, 0h42b87975, 0h42b87975, 0h4218d2da, 0h42b87975, 0hc218d2da, 0h4218d2da, 0hc2b87975, + 0hc218d2da, 0hc2b87975, 0hc2b87975, 0hc218d2da, 0hc2b87975, 0h4218d2da, 0hc218d2da, 0h42b87975, + 0h4150804d, 0h42c5f72a, 0h42731b78, 0h429e696c, 0h429e696c, 0h42731b78, 0h42c5f72a, 0h4150804d, + 0h42c5f72a, 0hc150804d, 0h429e696c, 0hc2731b78, 0h42731b78, 0hc29e696c, 0h4150804d, 0hc2c5f72a, + 0hc150804d, 0hc2c5f72a, 0hc2731b78, 0hc29e696c, 0hc29e696c, 0hc2731b78, 0hc2c5f72a, 0hc150804d, + 0hc2c5f72a, 0h4150804d, 0hc29e696c, 0h42731b78, 0hc2731b78, 0h429e696c, 0hc150804d, 0h42c5f72a, + 0h4218d2da, 0h42b87975, 0h42b87975, 0h4218d2da, 0h42b87975, 0hc218d2da, 0h4218d2da, 0hc2b87975, + 0hc218d2da, 0hc2b87975, 0hc2b87975, 0hc218d2da, 0hc2b87975, 0h4218d2da, 0hc218d2da, 0h42b87975, + 0h4150804d, 0h42c5f72a, 0h42731b78, 0h429e696c, 0h429e696c, 0h42731b78, 0h42c5f72a, 0h4150804d, + 0h42c5f72a, 0hc150804d, 0h429e696c, 0hc2731b78, 0h42731b78, 0hc29e696c, 0h4150804d, 0hc2c5f72a, + 0hc150804d, 0hc2c5f72a, 0hc2731b78, 0hc29e696c, 0hc29e696c, 0hc2731b78, 0hc2c5f72a, 0hc150804d, + 0hc2c5f72a, 0h4150804d, 0hc29e696c, 0h42731b78, 0hc2731b78, 0h429e696c, 0hc150804d, 0h42c5f72a, + 0h4218d2da, 0h42b87975, 0h42b87975, 0h4218d2da, 0h42b87975, 0hc218d2da, 0h4218d2da, 0hc2b87975, + 0hc218d2da, 0hc2b87975, 0hc2b87975, 0hc218d2da, 0hc2b87975, 0h4218d2da, 0hc218d2da, 0h42b87975, +} + +GRADIENTS_3D := [N_GRADS_3D * 4]f32{ + 0h41df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0h41df5103, 0h4148c1c5, 0h00000000, + 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 0h00000000, + 0hc1df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0h41df5103, 0h4148c1c5, 0h00000000, + 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 0h00000000, + 0hc148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, + 0h00000000, 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, + 0hc148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, + 0h00000000, 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, + 0hc1df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0hc1df5103, 0h4148c1c5, 0h00000000, + 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, 0h00000000, + 0hc1df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0hc1df5103, 0h00000000, + 0hc16b5146, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc16b5146, 0h00000000, + 0hc1df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0h41df5103, 0h00000000, + 0hc21ae5b8, 0h00000000, 0h416b5146, 0h00000000, 0hc16b5146, 0h00000000, 0h421ae5b8, 0h00000000, + 0hc148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, + 0h00000000, 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, + 0hc148c1c5, 0h41df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0h41df5103, 0h00000000, + 0h00000000, 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, + 0h41df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0hc1df5103, 0h4148c1c5, 0h00000000, + 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, 0h00000000, + 0h41df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0hc1df5103, 0h00000000, + 0h421ae5b8, 0h00000000, 0hc16b5146, 0h00000000, 0h416b5146, 0h00000000, 0hc21ae5b8, 0h00000000, + 0h41df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0h41df5103, 0h00000000, + 0h416b5146, 0h00000000, 0h421ae5b8, 0h00000000, 0h421ae5b8, 0h00000000, 0h416b5146, 0h00000000, + 0h41df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0h41df5103, 0h4148c1c5, 0h00000000, + 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 0h00000000, + 0hc1df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0h41df5103, 0h4148c1c5, 0h00000000, + 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 0h00000000, + 0hc148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, + 0h00000000, 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, + 0hc148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, + 0h00000000, 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, + 0hc1df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0hc1df5103, 0h4148c1c5, 0h00000000, + 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, 0h00000000, + 0hc1df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0hc1df5103, 0h00000000, + 0hc16b5146, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc16b5146, 0h00000000, + 0hc1df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0h41df5103, 0h00000000, + 0hc21ae5b8, 0h00000000, 0h416b5146, 0h00000000, 0hc16b5146, 0h00000000, 0h421ae5b8, 0h00000000, + 0hc148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, + 0h00000000, 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, + 0hc148c1c5, 0h41df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0h41df5103, 0h00000000, + 0h00000000, 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, + 0h41df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0hc1df5103, 0h4148c1c5, 0h00000000, + 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, 0h00000000, + 0h41df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0hc1df5103, 0h00000000, + 0h421ae5b8, 0h00000000, 0hc16b5146, 0h00000000, 0h416b5146, 0h00000000, 0hc21ae5b8, 0h00000000, + 0h41df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0h41df5103, 0h00000000, + 0h416b5146, 0h00000000, 0h421ae5b8, 0h00000000, 0h421ae5b8, 0h00000000, 0h416b5146, 0h00000000, + 0h41df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0h41df5103, 0h4148c1c5, 0h00000000, + 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 0h00000000, + 0hc1df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0h41df5103, 0h4148c1c5, 0h00000000, + 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 0h00000000, + 0hc148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, + 0h00000000, 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, + 0hc148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, + 0h00000000, 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, + 0hc1df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0hc1df5103, 0h4148c1c5, 0h00000000, + 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, 0h00000000, + 0hc1df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0hc1df5103, 0h00000000, + 0hc16b5146, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc16b5146, 0h00000000, + 0hc1df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0h41df5103, 0h00000000, + 0hc21ae5b8, 0h00000000, 0h416b5146, 0h00000000, 0hc16b5146, 0h00000000, 0h421ae5b8, 0h00000000, + 0hc148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, + 0h00000000, 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, + 0hc148c1c5, 0h41df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0h41df5103, 0h00000000, + 0h00000000, 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, + 0h41df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0hc1df5103, 0h4148c1c5, 0h00000000, + 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, 0h00000000, + 0h41df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0hc1df5103, 0h00000000, + 0h421ae5b8, 0h00000000, 0hc16b5146, 0h00000000, 0h416b5146, 0h00000000, 0hc21ae5b8, 0h00000000, + 0h41df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0h41df5103, 0h00000000, + 0h416b5146, 0h00000000, 0h421ae5b8, 0h00000000, 0h421ae5b8, 0h00000000, 0h416b5146, 0h00000000, + 0h41df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0h41df5103, 0h4148c1c5, 0h00000000, + 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 0h00000000, + 0hc1df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0h41df5103, 0h4148c1c5, 0h00000000, + 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 0h00000000, + 0hc148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, + 0h00000000, 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, + 0hc148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, + 0h00000000, 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, + 0hc1df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0hc1df5103, 0h4148c1c5, 0h00000000, + 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, 0h00000000, + 0hc1df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0hc1df5103, 0h00000000, + 0hc16b5146, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc16b5146, 0h00000000, + 0hc1df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0h41df5103, 0h00000000, + 0hc21ae5b8, 0h00000000, 0h416b5146, 0h00000000, 0hc16b5146, 0h00000000, 0h421ae5b8, 0h00000000, + 0hc148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, + 0h00000000, 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, + 0hc148c1c5, 0h41df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0h41df5103, 0h00000000, + 0h00000000, 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, + 0h41df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0hc1df5103, 0h4148c1c5, 0h00000000, + 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, 0h00000000, + 0h41df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0hc1df5103, 0h00000000, + 0h421ae5b8, 0h00000000, 0hc16b5146, 0h00000000, 0h416b5146, 0h00000000, 0hc21ae5b8, 0h00000000, + 0h41df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0h41df5103, 0h00000000, + 0h416b5146, 0h00000000, 0h421ae5b8, 0h00000000, 0h421ae5b8, 0h00000000, 0h416b5146, 0h00000000, + 0h41df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0h41df5103, 0h4148c1c5, 0h00000000, + 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 0h00000000, + 0hc1df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0h41df5103, 0h4148c1c5, 0h00000000, + 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 0h00000000, + 0hc148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, + 0h00000000, 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, + 0hc148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, + 0h00000000, 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, + 0hc1df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0hc1df5103, 0h4148c1c5, 0h00000000, + 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, 0h00000000, + 0hc1df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0hc1df5103, 0h00000000, + 0hc16b5146, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc16b5146, 0h00000000, + 0hc1df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0h41df5103, 0h00000000, + 0hc21ae5b8, 0h00000000, 0h416b5146, 0h00000000, 0hc16b5146, 0h00000000, 0h421ae5b8, 0h00000000, + 0hc148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, + 0h00000000, 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, + 0hc148c1c5, 0h41df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0h41df5103, 0h00000000, + 0h00000000, 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, + 0h41df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0hc1df5103, 0h4148c1c5, 0h00000000, + 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, 0h00000000, + 0h41df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0hc1df5103, 0h00000000, + 0h421ae5b8, 0h00000000, 0hc16b5146, 0h00000000, 0h416b5146, 0h00000000, 0hc21ae5b8, 0h00000000, + 0h41df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0h41df5103, 0h00000000, + 0h416b5146, 0h00000000, 0h421ae5b8, 0h00000000, 0h421ae5b8, 0h00000000, 0h416b5146, 0h00000000, + 0h41df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0h41df5103, 0h4148c1c5, 0h00000000, + 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 0h00000000, + 0hc1df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0h41df5103, 0h4148c1c5, 0h00000000, + 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 0h00000000, + 0hc148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, + 0h00000000, 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, + 0hc148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, + 0h00000000, 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, +} + +GRADIENTS_4D := [N_GRADS_4D * 4]f32{ + 0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41d2a716, 0hc208695c, 0hc19194b0, 0h40de6d7d, 0h41b6d966, + 0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41b6d966, 0hc22076c5, 0h406d72bf, 0h406d72bf, 0h41a58418, + 0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h422076c5, 0hc1b6d966, 0hc0de6d7d, 0h419194b0, 0h4208695c, + 0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h4208695c, 0hc1d2a716, 0h416b8e00, 0h416b8e00, 0h41f50507, + 0hc1f50507, 0hc16b8e00, 0h41d2a716, 0hc16b8e00, 0hc208695c, 0hc19194b0, 0h41b6d966, 0h40de6d7d, + 0hc208695c, 0h40de6d7d, 0h41b6d966, 0hc19194b0, 0hc22076c5, 0h406d72bf, 0h41a58418, 0h406d72bf, + 0hc1a58418, 0hc06d72bf, 0h422076c5, 0hc06d72bf, 0hc1b6d966, 0hc0de6d7d, 0h4208695c, 0h419194b0, + 0hc1b6d966, 0h419194b0, 0h4208695c, 0hc0de6d7d, 0hc1d2a716, 0h416b8e00, 0h41f50507, 0h416b8e00, + 0hc1f50507, 0h41d2a716, 0hc16b8e00, 0hc16b8e00, 0hc208695c, 0h41b6d966, 0hc19194b0, 0h40de6d7d, + 0hc208695c, 0h41b6d966, 0h40de6d7d, 0hc19194b0, 0hc22076c5, 0h41a58418, 0h406d72bf, 0h406d72bf, + 0hc1a58418, 0h422076c5, 0hc06d72bf, 0hc06d72bf, 0hc1b6d966, 0h4208695c, 0hc0de6d7d, 0h419194b0, + 0hc1b6d966, 0h4208695c, 0h419194b0, 0hc0de6d7d, 0hc1d2a716, 0h41f50507, 0h416b8e00, 0h416b8e00, + 0h41d2a716, 0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41b6d966, 0hc208695c, 0hc19194b0, 0h40de6d7d, + 0h41b6d966, 0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41a58418, 0hc22076c5, 0h406d72bf, 0h406d72bf, + 0h422076c5, 0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h4208695c, 0hc1b6d966, 0hc0de6d7d, 0h419194b0, + 0h4208695c, 0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h41f50507, 0hc1d2a716, 0h416b8e00, 0h416b8e00, + 0hc208ee18, 0hc18a0670, 0hc18a0670, 0hc18a0670, 0hc20e2b7a, 0hc19d18ee, 0hc19d18ee, 0h40b05c85, + 0hc20e2b7a, 0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0hc19d18ee, + 0hc21c1252, 0hc1b8e69d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0h40024b8d, + 0hc21c1252, 0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc235739c, 0hbfc4b564, 0hbfc4b564, 0hbfc4b564, + 0hc18a0670, 0hc208ee18, 0hc18a0670, 0hc18a0670, 0hc19d18ee, 0hc20e2b7a, 0hc19d18ee, 0h40b05c85, + 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0h40b05c85, 0hc20e2b7a, 0hc19d18ee, 0hc19d18ee, + 0hc1b8e69d, 0hc21c1252, 0h40024b8d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0hc1b8e69d, 0h40024b8d, + 0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0hbfc4b564, 0hc235739c, 0hbfc4b564, 0hbfc4b564, + 0hc18a0670, 0hc18a0670, 0hc208ee18, 0hc18a0670, 0hc19d18ee, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, + 0hc19d18ee, 0h40b05c85, 0hc20e2b7a, 0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0hc19d18ee, + 0hc1b8e69d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc21c1252, 0h40024b8d, + 0h40024b8d, 0h40024b8d, 0hc21c1252, 0hc1b8e69d, 0hbfc4b564, 0hbfc4b564, 0hc235739c, 0hbfc4b564, + 0hc18a0670, 0hc18a0670, 0hc18a0670, 0hc208ee18, 0hc19d18ee, 0hc19d18ee, 0h40b05c85, 0hc20e2b7a, + 0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0hc19d18ee, 0hc20e2b7a, + 0hc1b8e69d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0h40024b8d, 0hc21c1252, + 0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc21c1252, 0hbfc4b564, 0hbfc4b564, 0hbfc4b564, 0hc235739c, + 0hc16b8e00, 0hc1f50507, 0hc16b8e00, 0h41d2a716, 0hc19194b0, 0hc208695c, 0h40de6d7d, 0h41b6d966, + 0h40de6d7d, 0hc208695c, 0hc19194b0, 0h41b6d966, 0h406d72bf, 0hc22076c5, 0h406d72bf, 0h41a58418, + 0hc06d72bf, 0hc1a58418, 0hc06d72bf, 0h422076c5, 0hc0de6d7d, 0hc1b6d966, 0h419194b0, 0h4208695c, + 0h419194b0, 0hc1b6d966, 0hc0de6d7d, 0h4208695c, 0h416b8e00, 0hc1d2a716, 0h416b8e00, 0h41f50507, + 0hc16b8e00, 0hc16b8e00, 0hc1f50507, 0h41d2a716, 0hc19194b0, 0h40de6d7d, 0hc208695c, 0h41b6d966, + 0h40de6d7d, 0hc19194b0, 0hc208695c, 0h41b6d966, 0h406d72bf, 0h406d72bf, 0hc22076c5, 0h41a58418, + 0hc06d72bf, 0hc06d72bf, 0hc1a58418, 0h422076c5, 0hc0de6d7d, 0h419194b0, 0hc1b6d966, 0h4208695c, + 0h419194b0, 0hc0de6d7d, 0hc1b6d966, 0h4208695c, 0h416b8e00, 0h416b8e00, 0hc1d2a716, 0h41f50507, + 0hc16b8e00, 0hc1f50507, 0h41d2a716, 0hc16b8e00, 0hc19194b0, 0hc208695c, 0h41b6d966, 0h40de6d7d, + 0h40de6d7d, 0hc208695c, 0h41b6d966, 0hc19194b0, 0h406d72bf, 0hc22076c5, 0h41a58418, 0h406d72bf, + 0hc06d72bf, 0hc1a58418, 0h422076c5, 0hc06d72bf, 0hc0de6d7d, 0hc1b6d966, 0h4208695c, 0h419194b0, + 0h419194b0, 0hc1b6d966, 0h4208695c, 0hc0de6d7d, 0h416b8e00, 0hc1d2a716, 0h41f50507, 0h416b8e00, + 0hc16b8e00, 0hc16b8e00, 0h41d2a716, 0hc1f50507, 0hc19194b0, 0h40de6d7d, 0h41b6d966, 0hc208695c, + 0h40de6d7d, 0hc19194b0, 0h41b6d966, 0hc208695c, 0h406d72bf, 0h406d72bf, 0h41a58418, 0hc22076c5, + 0hc06d72bf, 0hc06d72bf, 0h422076c5, 0hc1a58418, 0hc0de6d7d, 0h419194b0, 0h4208695c, 0hc1b6d966, + 0h419194b0, 0hc0de6d7d, 0h4208695c, 0hc1b6d966, 0h416b8e00, 0h416b8e00, 0h41f50507, 0hc1d2a716, + 0hc16b8e00, 0h41d2a716, 0hc1f50507, 0hc16b8e00, 0hc19194b0, 0h41b6d966, 0hc208695c, 0h40de6d7d, + 0h40de6d7d, 0h41b6d966, 0hc208695c, 0hc19194b0, 0h406d72bf, 0h41a58418, 0hc22076c5, 0h406d72bf, + 0hc06d72bf, 0h422076c5, 0hc1a58418, 0hc06d72bf, 0hc0de6d7d, 0h4208695c, 0hc1b6d966, 0h419194b0, + 0h419194b0, 0h4208695c, 0hc1b6d966, 0hc0de6d7d, 0h416b8e00, 0h41f50507, 0hc1d2a716, 0h416b8e00, + 0hc16b8e00, 0h41d2a716, 0hc16b8e00, 0hc1f50507, 0hc19194b0, 0h41b6d966, 0h40de6d7d, 0hc208695c, + 0h40de6d7d, 0h41b6d966, 0hc19194b0, 0hc208695c, 0h406d72bf, 0h41a58418, 0h406d72bf, 0hc22076c5, + 0hc06d72bf, 0h422076c5, 0hc06d72bf, 0hc1a58418, 0hc0de6d7d, 0h4208695c, 0h419194b0, 0hc1b6d966, + 0h419194b0, 0h4208695c, 0hc0de6d7d, 0hc1b6d966, 0h416b8e00, 0h41f50507, 0h416b8e00, 0hc1d2a716, + 0h41d2a716, 0hc16b8e00, 0hc1f50507, 0hc16b8e00, 0h41b6d966, 0hc19194b0, 0hc208695c, 0h40de6d7d, + 0h41b6d966, 0h40de6d7d, 0hc208695c, 0hc19194b0, 0h41a58418, 0h406d72bf, 0hc22076c5, 0h406d72bf, + 0h422076c5, 0hc06d72bf, 0hc1a58418, 0hc06d72bf, 0h4208695c, 0hc0de6d7d, 0hc1b6d966, 0h419194b0, + 0h4208695c, 0h419194b0, 0hc1b6d966, 0hc0de6d7d, 0h41f50507, 0h416b8e00, 0hc1d2a716, 0h416b8e00, + 0h41d2a716, 0hc16b8e00, 0hc16b8e00, 0hc1f50507, 0h41b6d966, 0hc19194b0, 0h40de6d7d, 0hc208695c, + 0h41b6d966, 0h40de6d7d, 0hc19194b0, 0hc208695c, 0h41a58418, 0h406d72bf, 0h406d72bf, 0hc22076c5, + 0h422076c5, 0hc06d72bf, 0hc06d72bf, 0hc1a58418, 0h4208695c, 0hc0de6d7d, 0h419194b0, 0hc1b6d966, + 0h4208695c, 0h419194b0, 0hc0de6d7d, 0hc1b6d966, 0h41f50507, 0h416b8e00, 0h416b8e00, 0hc1d2a716, + 0h3fc4b564, 0h3fc4b564, 0h3fc4b564, 0h4235739c, 0hc0024b8d, 0hc0024b8d, 0h41b8e69d, 0h421c1252, + 0hc0024b8d, 0h41b8e69d, 0hc0024b8d, 0h421c1252, 0hc0b05c85, 0h419d18ee, 0h419d18ee, 0h420e2b7a, + 0h41b8e69d, 0hc0024b8d, 0hc0024b8d, 0h421c1252, 0h419d18ee, 0hc0b05c85, 0h419d18ee, 0h420e2b7a, + 0h419d18ee, 0h419d18ee, 0hc0b05c85, 0h420e2b7a, 0h418a0670, 0h418a0670, 0h418a0670, 0h4208ee18, + 0h3fc4b564, 0h3fc4b564, 0h4235739c, 0h3fc4b564, 0hc0024b8d, 0h40024b8d, 0h421c1252, 0h41b8e69d, + 0hc0024b8d, 0h41b8e69d, 0h421c1252, 0hc0024b8d, 0hc0b05c85, 0h419d18ee, 0h420e2b7a, 0h419d18ee, + 0h41b8e69d, 0hc0024b8d, 0h421c1252, 0hc0024b8d, 0h419d18ee, 0hc0b05c85, 0h420e2b7a, 0h419d18ee, + 0h419d18ee, 0h419d18ee, 0h420e2b7a, 0hc0b05c85, 0h418a0670, 0h418a0670, 0h4208ee18, 0h418a0670, + 0h3fc4b564, 0h4235739c, 0h3fc4b564, 0h3fc4b564, 0hc0024b8d, 0h421c1252, 0hc0024b8d, 0h41b8e69d, + 0hc0024b8d, 0h421c1252, 0h41b8e69d, 0hc0024b8d, 0hc0b05c85, 0h420e2b7a, 0h419d18ee, 0h419d18ee, + 0h41b8e69d, 0h421c1252, 0hc0024b8d, 0hc0024b8d, 0h419d18ee, 0h420e2b7a, 0hc0b05c85, 0h419d18ee, + 0h419d18ee, 0h420e2b7a, 0h419d18ee, 0hc0b05c85, 0h418a0670, 0h4208ee18, 0h418a0670, 0h418a0670, + 0h4235739c, 0h3fc4b564, 0h3fc4b564, 0h3fc4b564, 0h421c1252, 0hc0024b8d, 0hc0024b8d, 0h41b8e69d, + 0h421c1252, 0hc0024b8d, 0h41b8e69d, 0hc0024b8d, 0h420e2b7a, 0hc0b05c85, 0h419d18ee, 0h419d18ee, + 0h421c1252, 0h41b8e69d, 0hc0024b8d, 0hc0024b8d, 0h420e2b7a, 0h419d18ee, 0hc0b05c85, 0h419d18ee, + 0h420e2b7a, 0h419d18ee, 0h419d18ee, 0hc0b05c85, 0h4208ee18, 0h418a0670, 0h418a0670, 0h418a0670, + 0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41d2a716, 0hc208695c, 0hc19194b0, 0h40de6d7d, 0h41b6d966, + 0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41b6d966, 0hc22076c5, 0h406d72bf, 0h406d72bf, 0h41a58418, + 0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h422076c5, 0hc1b6d966, 0hc0de6d7d, 0h419194b0, 0h4208695c, + 0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h4208695c, 0hc1d2a716, 0h416b8e00, 0h416b8e00, 0h41f50507, + 0hc1f50507, 0hc16b8e00, 0h41d2a716, 0hc16b8e00, 0hc208695c, 0hc19194b0, 0h41b6d966, 0h40de6d7d, + 0hc208695c, 0h40de6d7d, 0h41b6d966, 0hc19194b0, 0hc22076c5, 0h406d72bf, 0h41a58418, 0h406d72bf, + 0hc1a58418, 0hc06d72bf, 0h422076c5, 0hc06d72bf, 0hc1b6d966, 0hc0de6d7d, 0h4208695c, 0h419194b0, + 0hc1b6d966, 0h419194b0, 0h4208695c, 0hc0de6d7d, 0hc1d2a716, 0h416b8e00, 0h41f50507, 0h416b8e00, + 0hc1f50507, 0h41d2a716, 0hc16b8e00, 0hc16b8e00, 0hc208695c, 0h41b6d966, 0hc19194b0, 0h40de6d7d, + 0hc208695c, 0h41b6d966, 0h40de6d7d, 0hc19194b0, 0hc22076c5, 0h41a58418, 0h406d72bf, 0h406d72bf, + 0hc1a58418, 0h422076c5, 0hc06d72bf, 0hc06d72bf, 0hc1b6d966, 0h4208695c, 0hc0de6d7d, 0h419194b0, + 0hc1b6d966, 0h4208695c, 0h419194b0, 0hc0de6d7d, 0hc1d2a716, 0h41f50507, 0h416b8e00, 0h416b8e00, + 0h41d2a716, 0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41b6d966, 0hc208695c, 0hc19194b0, 0h40de6d7d, + 0h41b6d966, 0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41a58418, 0hc22076c5, 0h406d72bf, 0h406d72bf, + 0h422076c5, 0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h4208695c, 0hc1b6d966, 0hc0de6d7d, 0h419194b0, + 0h4208695c, 0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h41f50507, 0hc1d2a716, 0h416b8e00, 0h416b8e00, + 0hc208ee18, 0hc18a0670, 0hc18a0670, 0hc18a0670, 0hc20e2b7a, 0hc19d18ee, 0hc19d18ee, 0h40b05c85, + 0hc20e2b7a, 0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0hc19d18ee, + 0hc21c1252, 0hc1b8e69d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0h40024b8d, + 0hc21c1252, 0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc235739c, 0hbfc4b564, 0hbfc4b564, 0hbfc4b564, + 0hc18a0670, 0hc208ee18, 0hc18a0670, 0hc18a0670, 0hc19d18ee, 0hc20e2b7a, 0hc19d18ee, 0h40b05c85, + 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0h40b05c85, 0hc20e2b7a, 0hc19d18ee, 0hc19d18ee, + 0hc1b8e69d, 0hc21c1252, 0h40024b8d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0hc1b8e69d, 0h40024b8d, + 0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0hbfc4b564, 0hc235739c, 0hbfc4b564, 0hbfc4b564, + 0hc18a0670, 0hc18a0670, 0hc208ee18, 0hc18a0670, 0hc19d18ee, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, + 0hc19d18ee, 0h40b05c85, 0hc20e2b7a, 0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0hc19d18ee, + 0hc1b8e69d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc21c1252, 0h40024b8d, + 0h40024b8d, 0h40024b8d, 0hc21c1252, 0hc1b8e69d, 0hbfc4b564, 0hbfc4b564, 0hc235739c, 0hbfc4b564, + 0hc18a0670, 0hc18a0670, 0hc18a0670, 0hc208ee18, 0hc19d18ee, 0hc19d18ee, 0h40b05c85, 0hc20e2b7a, + 0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0hc19d18ee, 0hc20e2b7a, + 0hc1b8e69d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0h40024b8d, 0hc21c1252, + 0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc21c1252, 0hbfc4b564, 0hbfc4b564, 0hbfc4b564, 0hc235739c, + 0hc16b8e00, 0hc1f50507, 0hc16b8e00, 0h41d2a716, 0hc19194b0, 0hc208695c, 0h40de6d7d, 0h41b6d966, + 0h40de6d7d, 0hc208695c, 0hc19194b0, 0h41b6d966, 0h406d72bf, 0hc22076c5, 0h406d72bf, 0h41a58418, + 0hc06d72bf, 0hc1a58418, 0hc06d72bf, 0h422076c5, 0hc0de6d7d, 0hc1b6d966, 0h419194b0, 0h4208695c, + 0h419194b0, 0hc1b6d966, 0hc0de6d7d, 0h4208695c, 0h416b8e00, 0hc1d2a716, 0h416b8e00, 0h41f50507, + 0hc16b8e00, 0hc16b8e00, 0hc1f50507, 0h41d2a716, 0hc19194b0, 0h40de6d7d, 0hc208695c, 0h41b6d966, + 0h40de6d7d, 0hc19194b0, 0hc208695c, 0h41b6d966, 0h406d72bf, 0h406d72bf, 0hc22076c5, 0h41a58418, + 0hc06d72bf, 0hc06d72bf, 0hc1a58418, 0h422076c5, 0hc0de6d7d, 0h419194b0, 0hc1b6d966, 0h4208695c, + 0h419194b0, 0hc0de6d7d, 0hc1b6d966, 0h4208695c, 0h416b8e00, 0h416b8e00, 0hc1d2a716, 0h41f50507, + 0hc16b8e00, 0hc1f50507, 0h41d2a716, 0hc16b8e00, 0hc19194b0, 0hc208695c, 0h41b6d966, 0h40de6d7d, + 0h40de6d7d, 0hc208695c, 0h41b6d966, 0hc19194b0, 0h406d72bf, 0hc22076c5, 0h41a58418, 0h406d72bf, + 0hc06d72bf, 0hc1a58418, 0h422076c5, 0hc06d72bf, 0hc0de6d7d, 0hc1b6d966, 0h4208695c, 0h419194b0, + 0h419194b0, 0hc1b6d966, 0h4208695c, 0hc0de6d7d, 0h416b8e00, 0hc1d2a716, 0h41f50507, 0h416b8e00, + 0hc16b8e00, 0hc16b8e00, 0h41d2a716, 0hc1f50507, 0hc19194b0, 0h40de6d7d, 0h41b6d966, 0hc208695c, + 0h40de6d7d, 0hc19194b0, 0h41b6d966, 0hc208695c, 0h406d72bf, 0h406d72bf, 0h41a58418, 0hc22076c5, + 0hc06d72bf, 0hc06d72bf, 0h422076c5, 0hc1a58418, 0hc0de6d7d, 0h419194b0, 0h4208695c, 0hc1b6d966, + 0h419194b0, 0hc0de6d7d, 0h4208695c, 0hc1b6d966, 0h416b8e00, 0h416b8e00, 0h41f50507, 0hc1d2a716, + 0hc16b8e00, 0h41d2a716, 0hc1f50507, 0hc16b8e00, 0hc19194b0, 0h41b6d966, 0hc208695c, 0h40de6d7d, + 0h40de6d7d, 0h41b6d966, 0hc208695c, 0hc19194b0, 0h406d72bf, 0h41a58418, 0hc22076c5, 0h406d72bf, + 0hc06d72bf, 0h422076c5, 0hc1a58418, 0hc06d72bf, 0hc0de6d7d, 0h4208695c, 0hc1b6d966, 0h419194b0, + 0h419194b0, 0h4208695c, 0hc1b6d966, 0hc0de6d7d, 0h416b8e00, 0h41f50507, 0hc1d2a716, 0h416b8e00, + 0hc16b8e00, 0h41d2a716, 0hc16b8e00, 0hc1f50507, 0hc19194b0, 0h41b6d966, 0h40de6d7d, 0hc208695c, + 0h40de6d7d, 0h41b6d966, 0hc19194b0, 0hc208695c, 0h406d72bf, 0h41a58418, 0h406d72bf, 0hc22076c5, + 0hc06d72bf, 0h422076c5, 0hc06d72bf, 0hc1a58418, 0hc0de6d7d, 0h4208695c, 0h419194b0, 0hc1b6d966, + 0h419194b0, 0h4208695c, 0hc0de6d7d, 0hc1b6d966, 0h416b8e00, 0h41f50507, 0h416b8e00, 0hc1d2a716, + 0h41d2a716, 0hc16b8e00, 0hc1f50507, 0hc16b8e00, 0h41b6d966, 0hc19194b0, 0hc208695c, 0h40de6d7d, + 0h41b6d966, 0h40de6d7d, 0hc208695c, 0hc19194b0, 0h41a58418, 0h406d72bf, 0hc22076c5, 0h406d72bf, + 0h422076c5, 0hc06d72bf, 0hc1a58418, 0hc06d72bf, 0h4208695c, 0hc0de6d7d, 0hc1b6d966, 0h419194b0, + 0h4208695c, 0h419194b0, 0hc1b6d966, 0hc0de6d7d, 0h41f50507, 0h416b8e00, 0hc1d2a716, 0h416b8e00, + 0h41d2a716, 0hc16b8e00, 0hc16b8e00, 0hc1f50507, 0h41b6d966, 0hc19194b0, 0h40de6d7d, 0hc208695c, + 0h41b6d966, 0h40de6d7d, 0hc19194b0, 0hc208695c, 0h41a58418, 0h406d72bf, 0h406d72bf, 0hc22076c5, + 0h422076c5, 0hc06d72bf, 0hc06d72bf, 0hc1a58418, 0h4208695c, 0hc0de6d7d, 0h419194b0, 0hc1b6d966, + 0h4208695c, 0h419194b0, 0hc0de6d7d, 0hc1b6d966, 0h41f50507, 0h416b8e00, 0h416b8e00, 0hc1d2a716, + 0h3fc4b564, 0h3fc4b564, 0h3fc4b564, 0h4235739c, 0hc0024b8d, 0hc0024b8d, 0h41b8e69d, 0h421c1252, + 0hc0024b8d, 0h41b8e69d, 0hc0024b8d, 0h421c1252, 0hc0b05c85, 0h419d18ee, 0h419d18ee, 0h420e2b7a, + 0h41b8e69d, 0hc0024b8d, 0hc0024b8d, 0h421c1252, 0h419d18ee, 0hc0b05c85, 0h419d18ee, 0h420e2b7a, + 0h419d18ee, 0h419d18ee, 0hc0b05c85, 0h420e2b7a, 0h418a0670, 0h418a0670, 0h418a0670, 0h4208ee18, + 0h3fc4b564, 0h3fc4b564, 0h4235739c, 0h3fc4b564, 0hc0024b8d, 0h40024b8d, 0h421c1252, 0h41b8e69d, + 0hc0024b8d, 0h41b8e69d, 0h421c1252, 0hc0024b8d, 0hc0b05c85, 0h419d18ee, 0h420e2b7a, 0h419d18ee, + 0h41b8e69d, 0hc0024b8d, 0h421c1252, 0hc0024b8d, 0h419d18ee, 0hc0b05c85, 0h420e2b7a, 0h419d18ee, + 0h419d18ee, 0h419d18ee, 0h420e2b7a, 0hc0b05c85, 0h418a0670, 0h418a0670, 0h4208ee18, 0h418a0670, + 0h3fc4b564, 0h4235739c, 0h3fc4b564, 0h3fc4b564, 0hc0024b8d, 0h421c1252, 0hc0024b8d, 0h41b8e69d, + 0hc0024b8d, 0h421c1252, 0h41b8e69d, 0hc0024b8d, 0hc0b05c85, 0h420e2b7a, 0h419d18ee, 0h419d18ee, + 0h41b8e69d, 0h421c1252, 0hc0024b8d, 0hc0024b8d, 0h419d18ee, 0h420e2b7a, 0hc0b05c85, 0h419d18ee, + 0h419d18ee, 0h420e2b7a, 0h419d18ee, 0hc0b05c85, 0h418a0670, 0h4208ee18, 0h418a0670, 0h418a0670, + 0h4235739c, 0h3fc4b564, 0h3fc4b564, 0h3fc4b564, 0h421c1252, 0hc0024b8d, 0hc0024b8d, 0h41b8e69d, + 0h421c1252, 0hc0024b8d, 0h41b8e69d, 0hc0024b8d, 0h420e2b7a, 0hc0b05c85, 0h419d18ee, 0h419d18ee, + 0h421c1252, 0h41b8e69d, 0hc0024b8d, 0hc0024b8d, 0h420e2b7a, 0h419d18ee, 0hc0b05c85, 0h419d18ee, + 0h420e2b7a, 0h419d18ee, 0h419d18ee, 0hc0b05c85, 0h4208ee18, 0h418a0670, 0h418a0670, 0h418a0670, + 0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41d2a716, 0hc208695c, 0hc19194b0, 0h40de6d7d, 0h41b6d966, + 0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41b6d966, 0hc22076c5, 0h406d72bf, 0h406d72bf, 0h41a58418, + 0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h422076c5, 0hc1b6d966, 0hc0de6d7d, 0h419194b0, 0h4208695c, + 0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h4208695c, 0hc1d2a716, 0h416b8e00, 0h416b8e00, 0h41f50507, + 0hc1f50507, 0hc16b8e00, 0h41d2a716, 0hc16b8e00, 0hc208695c, 0hc19194b0, 0h41b6d966, 0h40de6d7d, + 0hc208695c, 0h40de6d7d, 0h41b6d966, 0hc19194b0, 0hc22076c5, 0h406d72bf, 0h41a58418, 0h406d72bf, + 0hc1a58418, 0hc06d72bf, 0h422076c5, 0hc06d72bf, 0hc1b6d966, 0hc0de6d7d, 0h4208695c, 0h419194b0, + 0hc1b6d966, 0h419194b0, 0h4208695c, 0hc0de6d7d, 0hc1d2a716, 0h416b8e00, 0h41f50507, 0h416b8e00, + 0hc1f50507, 0h41d2a716, 0hc16b8e00, 0hc16b8e00, 0hc208695c, 0h41b6d966, 0hc19194b0, 0h40de6d7d, + 0hc208695c, 0h41b6d966, 0h40de6d7d, 0hc19194b0, 0hc22076c5, 0h41a58418, 0h406d72bf, 0h406d72bf, + 0hc1a58418, 0h422076c5, 0hc06d72bf, 0hc06d72bf, 0hc1b6d966, 0h4208695c, 0hc0de6d7d, 0h419194b0, + 0hc1b6d966, 0h4208695c, 0h419194b0, 0hc0de6d7d, 0hc1d2a716, 0h41f50507, 0h416b8e00, 0h416b8e00, + 0h41d2a716, 0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41b6d966, 0hc208695c, 0hc19194b0, 0h40de6d7d, + 0h41b6d966, 0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41a58418, 0hc22076c5, 0h406d72bf, 0h406d72bf, + 0h422076c5, 0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h4208695c, 0hc1b6d966, 0hc0de6d7d, 0h419194b0, + 0h4208695c, 0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h41f50507, 0hc1d2a716, 0h416b8e00, 0h416b8e00, + 0hc208ee18, 0hc18a0670, 0hc18a0670, 0hc18a0670, 0hc20e2b7a, 0hc19d18ee, 0hc19d18ee, 0h40b05c85, + 0hc20e2b7a, 0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0hc19d18ee, + 0hc21c1252, 0hc1b8e69d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0h40024b8d, + 0hc21c1252, 0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc235739c, 0hbfc4b564, 0hbfc4b564, 0hbfc4b564, + 0hc18a0670, 0hc208ee18, 0hc18a0670, 0hc18a0670, 0hc19d18ee, 0hc20e2b7a, 0hc19d18ee, 0h40b05c85, + 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0h40b05c85, 0hc20e2b7a, 0hc19d18ee, 0hc19d18ee, + 0hc1b8e69d, 0hc21c1252, 0h40024b8d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0hc1b8e69d, 0h40024b8d, + 0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0hbfc4b564, 0hc235739c, 0hbfc4b564, 0hbfc4b564, + 0hc18a0670, 0hc18a0670, 0hc208ee18, 0hc18a0670, 0hc19d18ee, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, + 0hc19d18ee, 0h40b05c85, 0hc20e2b7a, 0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0hc19d18ee, + 0hc1b8e69d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc21c1252, 0h40024b8d, + 0h40024b8d, 0h40024b8d, 0hc21c1252, 0hc1b8e69d, 0hbfc4b564, 0hbfc4b564, 0hc235739c, 0hbfc4b564, + 0hc18a0670, 0hc18a0670, 0hc18a0670, 0hc208ee18, 0hc19d18ee, 0hc19d18ee, 0h40b05c85, 0hc20e2b7a, + 0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0hc19d18ee, 0hc20e2b7a, + 0hc1b8e69d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0h40024b8d, 0hc21c1252, + 0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc21c1252, 0hbfc4b564, 0hbfc4b564, 0hbfc4b564, 0hc235739c, + 0hc16b8e00, 0hc1f50507, 0hc16b8e00, 0h41d2a716, 0hc19194b0, 0hc208695c, 0h40de6d7d, 0h41b6d966, + 0h40de6d7d, 0hc208695c, 0hc19194b0, 0h41b6d966, 0h406d72bf, 0hc22076c5, 0h406d72bf, 0h41a58418, + 0hc06d72bf, 0hc1a58418, 0hc06d72bf, 0h422076c5, 0hc0de6d7d, 0hc1b6d966, 0h419194b0, 0h4208695c, + 0h419194b0, 0hc1b6d966, 0hc0de6d7d, 0h4208695c, 0h416b8e00, 0hc1d2a716, 0h416b8e00, 0h41f50507, + 0hc16b8e00, 0hc16b8e00, 0hc1f50507, 0h41d2a716, 0hc19194b0, 0h40de6d7d, 0hc208695c, 0h41b6d966, + 0h40de6d7d, 0hc19194b0, 0hc208695c, 0h41b6d966, 0h406d72bf, 0h406d72bf, 0hc22076c5, 0h41a58418, + 0hc06d72bf, 0hc06d72bf, 0hc1a58418, 0h422076c5, 0hc0de6d7d, 0h419194b0, 0hc1b6d966, 0h4208695c, + 0h419194b0, 0hc0de6d7d, 0hc1b6d966, 0h4208695c, 0h416b8e00, 0h416b8e00, 0hc1d2a716, 0h41f50507, + 0hc16b8e00, 0hc1f50507, 0h41d2a716, 0hc16b8e00, 0hc19194b0, 0hc208695c, 0h41b6d966, 0h40de6d7d, + 0h40de6d7d, 0hc208695c, 0h41b6d966, 0hc19194b0, 0h406d72bf, 0hc22076c5, 0h41a58418, 0h406d72bf, + 0hc06d72bf, 0hc1a58418, 0h422076c5, 0hc06d72bf, 0hc0de6d7d, 0hc1b6d966, 0h4208695c, 0h419194b0, + 0h419194b0, 0hc1b6d966, 0h4208695c, 0hc0de6d7d, 0h416b8e00, 0hc1d2a716, 0h41f50507, 0h416b8e00, + 0hc16b8e00, 0hc16b8e00, 0h41d2a716, 0hc1f50507, 0hc19194b0, 0h40de6d7d, 0h41b6d966, 0hc208695c, + 0h40de6d7d, 0hc19194b0, 0h41b6d966, 0hc208695c, 0h406d72bf, 0h406d72bf, 0h41a58418, 0hc22076c5, + 0hc06d72bf, 0hc06d72bf, 0h422076c5, 0hc1a58418, 0hc0de6d7d, 0h419194b0, 0h4208695c, 0hc1b6d966, + 0h419194b0, 0hc0de6d7d, 0h4208695c, 0hc1b6d966, 0h416b8e00, 0h416b8e00, 0h41f50507, 0hc1d2a716, + 0hc16b8e00, 0h41d2a716, 0hc1f50507, 0hc16b8e00, 0hc19194b0, 0h41b6d966, 0hc208695c, 0h40de6d7d, + 0h40de6d7d, 0h41b6d966, 0hc208695c, 0hc19194b0, 0h406d72bf, 0h41a58418, 0hc22076c5, 0h406d72bf, + 0hc06d72bf, 0h422076c5, 0hc1a58418, 0hc06d72bf, 0hc0de6d7d, 0h4208695c, 0hc1b6d966, 0h419194b0, + 0h419194b0, 0h4208695c, 0hc1b6d966, 0hc0de6d7d, 0h416b8e00, 0h41f50507, 0hc1d2a716, 0h416b8e00, + 0hc16b8e00, 0h41d2a716, 0hc16b8e00, 0hc1f50507, 0hc19194b0, 0h41b6d966, 0h40de6d7d, 0hc208695c, + 0h40de6d7d, 0h41b6d966, 0hc19194b0, 0hc208695c, 0h406d72bf, 0h41a58418, 0h406d72bf, 0hc22076c5, + 0hc06d72bf, 0h422076c5, 0hc06d72bf, 0hc1a58418, 0hc0de6d7d, 0h4208695c, 0h419194b0, 0hc1b6d966, + 0h419194b0, 0h4208695c, 0hc0de6d7d, 0hc1b6d966, 0h416b8e00, 0h41f50507, 0h416b8e00, 0hc1d2a716, + 0h41d2a716, 0hc16b8e00, 0hc1f50507, 0hc16b8e00, 0h41b6d966, 0hc19194b0, 0hc208695c, 0h40de6d7d, + 0h41b6d966, 0h40de6d7d, 0hc208695c, 0hc19194b0, 0h41a58418, 0h406d72bf, 0hc22076c5, 0h406d72bf, + 0h422076c5, 0hc06d72bf, 0hc1a58418, 0hc06d72bf, 0h4208695c, 0hc0de6d7d, 0hc1b6d966, 0h419194b0, + 0h4208695c, 0h419194b0, 0hc1b6d966, 0hc0de6d7d, 0h41f50507, 0h416b8e00, 0hc1d2a716, 0h416b8e00, + 0h41d2a716, 0hc16b8e00, 0hc16b8e00, 0hc1f50507, 0h41b6d966, 0hc19194b0, 0h40de6d7d, 0hc208695c, + 0h41b6d966, 0h40de6d7d, 0hc19194b0, 0hc208695c, 0h41a58418, 0h406d72bf, 0h406d72bf, 0hc22076c5, + 0h422076c5, 0hc06d72bf, 0hc06d72bf, 0hc1a58418, 0h4208695c, 0hc0de6d7d, 0h419194b0, 0hc1b6d966, + 0h4208695c, 0h419194b0, 0hc0de6d7d, 0hc1b6d966, 0h41f50507, 0h416b8e00, 0h416b8e00, 0hc1d2a716, + 0h3fc4b564, 0h3fc4b564, 0h3fc4b564, 0h4235739c, 0hc0024b8d, 0hc0024b8d, 0h41b8e69d, 0h421c1252, + 0hc0024b8d, 0h41b8e69d, 0hc0024b8d, 0h421c1252, 0hc0b05c85, 0h419d18ee, 0h419d18ee, 0h420e2b7a, + 0h41b8e69d, 0hc0024b8d, 0hc0024b8d, 0h421c1252, 0h419d18ee, 0hc0b05c85, 0h419d18ee, 0h420e2b7a, + 0h419d18ee, 0h419d18ee, 0hc0b05c85, 0h420e2b7a, 0h418a0670, 0h418a0670, 0h418a0670, 0h4208ee18, + 0h3fc4b564, 0h3fc4b564, 0h4235739c, 0h3fc4b564, 0hc0024b8d, 0h40024b8d, 0h421c1252, 0h41b8e69d, + 0hc0024b8d, 0h41b8e69d, 0h421c1252, 0hc0024b8d, 0hc0b05c85, 0h419d18ee, 0h420e2b7a, 0h419d18ee, + 0h41b8e69d, 0hc0024b8d, 0h421c1252, 0hc0024b8d, 0h419d18ee, 0hc0b05c85, 0h420e2b7a, 0h419d18ee, + 0h419d18ee, 0h419d18ee, 0h420e2b7a, 0hc0b05c85, 0h418a0670, 0h418a0670, 0h4208ee18, 0h418a0670, + 0h3fc4b564, 0h4235739c, 0h3fc4b564, 0h3fc4b564, 0hc0024b8d, 0h421c1252, 0hc0024b8d, 0h41b8e69d, + 0hc0024b8d, 0h421c1252, 0h41b8e69d, 0hc0024b8d, 0hc0b05c85, 0h420e2b7a, 0h419d18ee, 0h419d18ee, + 0h41b8e69d, 0h421c1252, 0hc0024b8d, 0hc0024b8d, 0h419d18ee, 0h420e2b7a, 0hc0b05c85, 0h419d18ee, + 0h419d18ee, 0h420e2b7a, 0h419d18ee, 0hc0b05c85, 0h418a0670, 0h4208ee18, 0h418a0670, 0h418a0670, + 0h4235739c, 0h3fc4b564, 0h3fc4b564, 0h3fc4b564, 0h421c1252, 0hc0024b8d, 0hc0024b8d, 0h41b8e69d, + 0h421c1252, 0hc0024b8d, 0h41b8e69d, 0hc0024b8d, 0h420e2b7a, 0hc0b05c85, 0h419d18ee, 0h419d18ee, + 0h421c1252, 0h41b8e69d, 0hc0024b8d, 0hc0024b8d, 0h420e2b7a, 0h419d18ee, 0hc0b05c85, 0h419d18ee, + 0h420e2b7a, 0h419d18ee, 0h419d18ee, 0hc0b05c85, 0h4208ee18, 0h418a0670, 0h418a0670, 0h418a0670, + 0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41d2a716, 0hc208695c, 0hc19194b0, 0h40de6d7d, 0h41b6d966, + 0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41b6d966, 0hc22076c5, 0h406d72bf, 0h406d72bf, 0h41a58418, + 0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h422076c5, 0hc1b6d966, 0hc0de6d7d, 0h419194b0, 0h4208695c, + 0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h4208695c, 0hc1d2a716, 0h416b8e00, 0h416b8e00, 0h41f50507, + 0hc1f50507, 0hc16b8e00, 0h41d2a716, 0hc16b8e00, 0hc208695c, 0hc19194b0, 0h41b6d966, 0h40de6d7d, + 0hc208695c, 0h40de6d7d, 0h41b6d966, 0hc19194b0, 0hc22076c5, 0h406d72bf, 0h41a58418, 0h406d72bf, + 0hc1a58418, 0hc06d72bf, 0h422076c5, 0hc06d72bf, 0hc1b6d966, 0hc0de6d7d, 0h4208695c, 0h419194b0, + 0hc1b6d966, 0h419194b0, 0h4208695c, 0hc0de6d7d, 0hc1d2a716, 0h416b8e00, 0h41f50507, 0h416b8e00, + 0hc1f50507, 0h41d2a716, 0hc16b8e00, 0hc16b8e00, 0hc208695c, 0h41b6d966, 0hc19194b0, 0h40de6d7d, + 0hc208695c, 0h41b6d966, 0h40de6d7d, 0hc19194b0, 0hc22076c5, 0h41a58418, 0h406d72bf, 0h406d72bf, + 0hc1a58418, 0h422076c5, 0hc06d72bf, 0hc06d72bf, 0hc1b6d966, 0h4208695c, 0hc0de6d7d, 0h419194b0, + 0hc1b6d966, 0h4208695c, 0h419194b0, 0hc0de6d7d, 0hc1d2a716, 0h41f50507, 0h416b8e00, 0h416b8e00, + 0h41d2a716, 0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41b6d966, 0hc208695c, 0hc19194b0, 0h40de6d7d, + 0h41b6d966, 0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41a58418, 0hc22076c5, 0h406d72bf, 0h406d72bf, + 0h422076c5, 0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h4208695c, 0hc1b6d966, 0hc0de6d7d, 0h419194b0, + 0h4208695c, 0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h41f50507, 0hc1d2a716, 0h416b8e00, 0h416b8e00, +} + +/* + 2D Simplex noise base. +*/ +_internal_noise_2d_unskewed_base :: proc(seed: i64, coord: Vec2) -> (value: f32) { + // Get base points and offsets. + base := [2]i64{fast_floor(coord.x), fast_floor(coord.y)} + i := [2]f32{f32(coord.x - f64(base.x)), f32(coord.y - f64(base.y))} + + // Prime pre-multiplication for hash. + bp := base * [2]i64{PRIME_X, PRIME_Y} + + // Unskew. + t := f32(i.x + i.y) * f32(UNSKEW_2D) + d0 := i + [2]f32{t, t} + + // First vertex. + a0 := RSQUARED_2D - d0.x * d0.x - d0.y * d0.y + if a0 > 0 { + value = (a0 * a0) * (a0 * a0) * grad(seed, [2]i64{bp.x, bp.y}, d0) + } + + // Second vertex. + a1 := f32(2 * (1 + 2 * UNSKEW_2D) * (1 / UNSKEW_2D + 2)) * t + f32(-2 * (1 + 2 * UNSKEW_2D) * (1 + 2 * UNSKEW_2D)) + a0 + if a1 > 0 { + d1 := d0 - [2]f32{f32(1 + 2 * UNSKEW_2D), f32(1 + 2 * UNSKEW_2D)} + value += (a1 * a1) * (a1 * a1) * grad(seed, [2]i64{bp.x + PRIME_X, bp.y + PRIME_Y}, d1) + } + + // Third vertex. + if d0.y > d0.x { + d2 := d0 - [2]f32{f32(UNSKEW_2D), f32(UNSKEW_2D + 1)} + a2 := RSQUARED_2D - d2.x * d2.x - d2.y * d2.y + if(a2 > 0) { + value += (a2 * a2) * (a2 * a2) * grad(seed, [2]i64{bp.x, bp.y + PRIME_Y}, d2) + } + } else { + d2 := d0 - [2]f32{f32(UNSKEW_2D + 1), f32(UNSKEW_2D)} + a2 := RSQUARED_2D - d2.x * d2.x - d2.y * d2.y + if(a2 > 0) { + value += (a2 * a2) * (a2 * a2) * grad(seed, [2]i64{bp.x + PRIME_X, bp.y}, d2) + } + } + + return +} + + +/* + Generate overlapping cubic lattices for 3D OpenSimplex2 noise. +*/ +_internal_noise_3d_unrotated_base :: proc(seed: i64, coord: Vec3) -> (value: f32) { + seed := seed + // Get base points and offsets. + // xr, yr, zr := coord.x, coord.y, coord.z + + rb := [3]i64{fast_round(coord.x), fast_round(coord.y), fast_round(coord.z)} + ri := [3]f32{f32(coord.x - f64(rb.x)), f32(coord.y - f64(rb.y)), f32(coord.z - f64(rb.z))} + + // -1 if positive, 1 if negative. + i_sign := [3]i64{i64(-1.0 - ri.x) | 1, i64(-1.0 - ri.y) | 1, i64(-1.0 - ri.z) | 1} + f_sign := [3]f32{f32(i_sign.x), f32(i_sign.y), f32(i_sign.z)} + + // Compute absolute values, using the above as a shortcut. This was faster in my tests for some reason. + a0 := f_sign * -ri + + // Prime pre-multiplication for hash. + rbp := rb * [3]i64{PRIME_X, PRIME_Y, PRIME_Z} + + // Loop: Pick an edge on each lattice copy. + a := (RSQUARED_3D - ri.x * ri.x) - (ri.y * ri.y + ri.z * ri.z) + + l := 0 + for { + defer l += 1 + + // Closest point on cube. + if a > 0 { + a2 := a * a; a4 := a2 * a2 + value += a4 * grad(seed, rbp, ri) + } + + // Second-closest point. + if a0.x >= a0.y && a0.x >= a0.z { + b := a + a0.x + a0.x + if b > 1 { + b -= 1 + b2 := b * b; b4 := b2 * b2 + value += b4 * grad(seed, [3]i64{rbp.x - i_sign.x * PRIME_X, rbp.y, rbp.z}, [3]f32{ri.x + f_sign.x, ri.y, ri.z}) + } + } else if a0.y > a0.x && a0.y >= a0.z { + b := a + a0.y + a0.y + if b > 1 { + b -= 1 + b2 := b * b; b4 := b2 * b2 + value += b4 * grad(seed, [3]i64{rbp.x, rbp.y - i_sign.y * PRIME_Y, rbp.z}, [3]f32{ri.x, ri.y + f_sign.y, ri.z}) + } + } else { + b := a + a0.z + a0.z + if b > 1 { + b -= 1 + b2 := b * b; b4 := b2 * b2 + value += b4 * grad(seed, [3]i64{rbp.x, rbp.y, rbp.z - i_sign.z * PRIME_Z}, [3]f32{ri.x, ri.y, ri.z + f_sign.z}) + } + } + + // Break from loop if we're done, skipping updates below. + if l == 1 { + break + } + + // Update absolute value. + a0 = 0.5 - a0 + + // Update relative coordinate. + ri = a0 * f_sign + + // Update falloff. + a += (0.75 - a0.x) - (a0.y + a0.z) + + // Update prime for hash. + rbp += [3]i64{i_sign.x >> 1, i_sign.y >> 1, i_sign.z >> 1} & {PRIME_X, PRIME_Y, PRIME_Z} + + // Update the reverse sign indicators. + i_sign = -i_sign + f_sign = -f_sign + + // And finally update the seed for the other lattice copy. + seed ~= SEED_FLIP_3D + } + + return value +} + +/* + 4D OpenSimplex2 noise base. +*/ +_internal_noise_4d_unskewed_base :: proc(seed: i64, coord: Vec4) -> (value: f32) { + seed := seed + + // Get base points and offsets + base := [4]i64{fast_floor(coord.x), fast_floor(coord.y), fast_floor(coord.z), fast_floor(coord.w)} + si := [4]f32{f32(coord.x - f64(base.x)), f32(coord.y - f64(base.y)), f32(coord.z - f64(base.z)), f32(coord.w - f64(base.w))} + + // Determine which lattice we can be confident has a contributing point its corresponding cell's base simplex. + // We only look at the spaces between the diagonal planes. This proved effective in all of my tests. + si_sum := (si.x + si.y) + (si.z + si.w) + starting_lattice := i64(si_sum * 1.25) + + // Offset for seed based on first lattice copy. + seed += starting_lattice * SEED_OFFSET_4D + + // Offset for lattice point relative positions (skewed) + starting_lattice_offset := f32(starting_lattice) * -LATTICE_STEP_4D + si += starting_lattice_offset + + // Prep for vertex contributions. + ssi := (si_sum + starting_lattice_offset * 4) * UNSKEW_4D + + // Prime pre-multiplication for hash. + svp := base * [4]i64{PRIME_X, PRIME_Y, PRIME_Z, PRIME_W} + + // Five points to add, total, from five copies of the A4 lattice. + for i : i64 = 0; ; i += 1 { + + // Next point is the closest vertex on the 4-simplex whose base vertex is the aforementioned vertex. + score := 1.0 + ssi * (-1.0 / UNSKEW_4D) // Seems slightly faster than 1.0-xsi-ysi-zsi-wsi + if si.x >= si.x && si.x >= si.z && si.x >= si.w && si.x >= score { + svp.x += PRIME_X + si.x -= 1 + ssi -= UNSKEW_4D + } + else if si.y > si.x && si.y >= si.z && si.y >= si.w && si.y >= score { + svp.y += PRIME_Y + si.y -= 1 + ssi -= UNSKEW_4D + } + else if si.z > si.x && si.z > si.y && si.z >= si.w && si.z >= score { + svp.z += PRIME_Z + si.z -= 1 + ssi -= UNSKEW_4D + } + else if si.w > si.x && si.w > si.y && si.w > si.z && si.w >= score { + svp.w += PRIME_W + si.w -= 1 + ssi -= UNSKEW_4D + } + + // gradient contribution with falloff. + d := si + ssi + a := (d.x * d.x + d.y * d.y) + (d.z * d.z + d.w * d.w) + + if a < RSQUARED_4D { + a -= RSQUARED_4D + a *= a; a4 := a * a + value += a4 * grad(seed, svp, d) + } + + // Break from loop if we're done, skipping updates below. + if i == 4 { + break + } + + // Update for next lattice copy shifted down by <-0.2, -0.2, -0.2, -0.2>. + si += LATTICE_STEP_4D + ssi += LATTICE_STEP_4D * 4 * UNSKEW_4D + seed -= SEED_OFFSET_4D + + // Because we don't always start on the same lattice copy, there's a special reset case. + if i == starting_lattice { + svp -= {PRIME_X, PRIME_Y, PRIME_Z, PRIME_W} + seed += SEED_OFFSET_4D * 5 + } + } + return +} + +/* + Utility functions +*/ +@(optimization_mode="speed") +grad_2d :: proc(seed: i64, svp: [2]i64, delta: [2]f32) -> (value: f32) { + hash := seed ~ svp.x ~ svp.y + hash *= HASH_MULTIPLIER + hash ~= hash >> (64 - N_GRADS_2D_EXPONENT + 1) + + gi := hash & ((N_GRADS_2D - 1) << 1) + return GRADIENTS_2D[gi] * delta.x + GRADIENTS_2D[gi | 1] * delta.y +} + +@(optimization_mode="speed") +grad_3d :: proc(seed: i64, rvp: [3]i64, delta: [3]f32) -> (value: f32) { + hash := (seed ~ rvp.x) ~ (rvp.y ~ rvp.z) + hash *= HASH_MULTIPLIER + hash ~= hash >> (64 - N_GRADS_3D_EXPONENT + 2) + + gi := hash & ((N_GRADS_3D - 1) << 2) + return GRADIENTS_3D[gi] * delta.x + GRADIENTS_3D[gi | 1] * delta.y + GRADIENTS_3D[gi | 2] * delta.z +} + +@(optimization_mode="speed") +grad_4d :: proc(seed: i64, svp: [4]i64, delta: [4]f32) -> (value: f32) { + hash := seed ~ (svp.x ~ svp.y) ~ (svp.z ~ svp.w) + hash *= HASH_MULTIPLIER + hash ~= hash >> (64 - N_GRADS_4D_EXPONENT + 2) + + gi := hash & ((N_GRADS_4D - 1) << 2) + return (GRADIENTS_4D[gi] * delta.x + GRADIENTS_4D[gi | 1] * delta.y) + (GRADIENTS_4D[gi | 2] * delta.z + GRADIENTS_4D[gi | 3] * delta.w) +} + +grad :: proc {grad_2d, grad_3d, grad_4d} + +@(optimization_mode="speed") +fast_floor :: proc(x: f64) -> (floored: i64) { + xi := i64(x) + return x < f64(xi) ? xi - 1 : xi +} + +@(optimization_mode="speed") +fast_round :: proc(x: f64) -> (rounded: i64) { + return x < 0 ? i64(x - 0.5) : i64(x + 0.5) +} \ No newline at end of file diff --git a/core/math/noise/opensimplex2.odin b/core/math/noise/opensimplex2.odin new file mode 100644 index 000000000..d90dafdf5 --- /dev/null +++ b/core/math/noise/opensimplex2.odin @@ -0,0 +1,171 @@ +/* + OpenSimplex2 noise implementation. + + Ported from https://github.com/KdotJPG/OpenSimplex2. + Copyright 2022 Yuki2 (https://github.com/NoahR02) +*/ +package math_noise + +/* + Input coordinate vectors +*/ +Vec2 :: [2]f64 +Vec3 :: [3]f64 +Vec4 :: [4]f64 + +/* + Noise Evaluators +*/ + +/* + 2D Simplex noise, standard lattice orientation. +*/ +noise_2d :: proc(seed: i64, coord: Vec2) -> (value: f32) { + // Get points for A2* lattice + skew := SKEW_2D * (coord.x + coord.y) + skewed := coord + skew + + return _internal_noise_2d_unskewed_base(seed, skewed) +} + +/* + 2D Simplex noise, with Y pointing down the main diagonal. + Might be better for a 2D sandbox style game, where Y is vertical. + Probably slightly less optimal for heightmaps or continent maps, + unless your map is centered around an equator. It's a subtle + difference, but the option is here to make it an easy choice. +*/ +noise_2d_improve_x :: proc(seed: i64, coord: Vec2) -> (value: f32) { + // Skew transform and rotation baked into one. + xx := coord.x * ROOT_2_OVER_2 + yy := coord.y * (ROOT_2_OVER_2 * (1 + 2 * SKEW_2D)) + return _internal_noise_2d_unskewed_base(seed, Vec2{yy + xx, yy - xx}) +} + + +/* + 3D OpenSimplex2 noise, with better visual isotropy in (X, Y). + Recommended for 3D terrain and time-varied animations. + The Z coordinate should always be the "different" coordinate in whatever your use case is. + If Y is vertical in world coordinates, call `noise_3d_improve_xz(x, z, Y)` or use `noise_3d_xz_before_y`. + If Z is vertical in world coordinates, call `noise_3d_improve_xz(x, y, Z)`. + For a time varied animation, call `noise_3d_improve_xz(x, y, T)`. +*/ +noise_3d_improve_xy :: proc(seed: i64, coord: Vec3) -> (value: f32) { + /* + Re-orient the cubic lattices without skewing, so Z points up the main lattice diagonal, + and the planes formed by XY are moved far out of alignment with the cube faces. + Orthonormal rotation. Not a skew transform. + */ + xy := coord.x + coord.y + s2 := xy * ROTATE_3D_ORTHOGONALIZER + zz := coord.z * ROOT_3_OVER_3 + + r := Vec3{coord.x + s2 + zz, coord.y + s2 + zz, xy * -ROOT_3_OVER_3 + zz} + + // Evaluate both lattices to form a BCC lattice. + return _internal_noise_3d_unrotated_base(seed, r) +} + +/* + 3D OpenSimplex2 noise, with better visual isotropy in (X, Z). + Recommended for 3D terrain and time-varied animations. + The Y coordinate should always be the "different" coordinate in whatever your use case is. + If Y is vertical in world coordinates, call `noise_3d_improve_xz(x, Y, z)`. + If Z is vertical in world coordinates, call `noise_3d_improve_xz(x, Z, y)` or use `noise_3d_improve_xy`. + For a time varied animation, call `noise_3d_improve_xz(x, T, y)` or use `noise_3d_improve_xy`. +*/ +noise_3d_improve_xz :: proc(seed: i64, coord: Vec3) -> (value: f32) { + /* + Re-orient the cubic lattices without skewing, so Y points up the main lattice diagonal, + and the planes formed by XZ are moved far out of alignment with the cube faces. + Orthonormal rotation. Not a skew transform. + */ + xz := coord.x + coord.z + s2 := xz * ROTATE_3D_ORTHOGONALIZER + yy := coord.y * ROOT_3_OVER_3 + + r := Vec3{coord.x + s2 + yy, xz * -ROOT_3_OVER_3 + yy, coord.z + s2 + yy} + + // Evaluate both lattices to form a BCC lattice. + return _internal_noise_3d_unrotated_base(seed, r) +} + +/* + 3D OpenSimplex2 noise, fallback rotation option + Use `noise_3d_improve_xy` or `noise_3d_improve_xz` instead, wherever appropriate. + They have less diagonal bias. This function's best use is as a fallback. +*/ +noise_3d_fallback :: proc(seed: i64, coord: Vec3) -> (value: f32) { + /* + Re-orient the cubic lattices via rotation, to produce a familiar look. + Orthonormal rotation. Not a skew transform. + */ + bias := FALLBACK_ROTATE_3D * (coord.x + coord.y + coord.z) + biased := bias - coord + // Evaluate both lattices to form a BCC lattice. + return _internal_noise_3d_unrotated_base(seed, biased) +} + + +/* + 4D OpenSimplex2 noise, with XYZ oriented like `noise_3d_improve_xy` + and W for an extra degree of freedom. W repeats eventually. + Recommended for time-varied animations which texture a 3D object (W=time) + in a space where Z is vertical. +*/ +noise_4d_improve_xyz_improve_xy :: proc(seed: i64, coord: Vec4) -> (value: f32) { + xy := coord.x + coord.y + s2 := xy * -0.21132486540518699998 + zz := coord.z * 0.28867513459481294226 + ww := coord.w * 0.2236067977499788 + + xr, yr : f64 = coord.x + (zz + ww + s2), coord.y + (zz + ww + s2) + zr : f64 = xy * -0.57735026918962599998 + (zz + ww) + wr : f64 = coord.z * -0.866025403784439 + ww + + return _internal_noise_4d_unskewed_base(seed, Vec4{xr, yr, zr, wr}) +} + +/* + 4D OpenSimplex2 noise, with XYZ oriented like `noise_3d_improve_xz` + and W for an extra degree of freedom. W repeats eventually. + Recommended for time-varied animations which texture a 3D object (W=time) + in a space where Y is vertical. +*/ +noise_4d_improve_xyz_improve_xz :: proc(seed: i64, coord: Vec4) -> (value: f32) { + xz := coord.x + coord.z + s2 := xz * -0.21132486540518699998 + yy := coord.y * 0.28867513459481294226 + ww := coord.w * 0.2236067977499788 + + xr, zr : f64 = coord.x + (yy + ww + s2), coord.z + (yy + ww + s2) + yr := xz * -0.57735026918962599998 + (yy + ww) + wr := coord.y * -0.866025403784439 + ww + + return _internal_noise_4d_unskewed_base(seed, Vec4{xr, yr, zr, wr}) +} + +/* + 4D OpenSimplex2 noise, with XYZ oriented like `noise_3d_fallback` + and W for an extra degree of freedom. W repeats eventually. + Recommended for time-varied animations which texture a 3D object (W=time) + where there isn't a clear distinction between horizontal and vertical +*/ +noise_4d_improve_xyz :: proc(seed: i64, coord: Vec4) -> (value: f32) { + xyz := coord.x + coord.y + coord.z + ww := coord.w * 0.2236067977499788 + s2 := xyz * -0.16666666666666666 + ww + + skewed := Vec4{coord.x + s2, coord.y + s2, coord.z + s2, -0.5 * xyz + ww} + return _internal_noise_4d_unskewed_base(seed, skewed) +} + +/* + 4D OpenSimplex2 noise, fallback lattice orientation. +*/ +noise_4d_fallback :: proc(seed: i64, coord: Vec4) -> (value: f32) { + // Get points for A4 lattice + skew := f64(SKEW_4D) * (coord.x + coord.y + coord.z + coord.w) + return _internal_noise_4d_unskewed_base(seed, coord + skew) +} \ No newline at end of file diff --git a/core/math/rand/distributions.odin b/core/math/rand/distributions.odin new file mode 100644 index 000000000..ada89afad --- /dev/null +++ b/core/math/rand/distributions.odin @@ -0,0 +1,312 @@ +package rand + +import "core:math" + +float64_uniform :: float64_range +float32_uniform :: float32_range + +// Triangular Distribution +// See: http://wikipedia.org/wiki/Triangular_distribution +float64_triangular :: proc(lo, hi: f64, mode: Maybe(f64), r: ^Rand = nil) -> f64 { + if hi-lo == 0 { + return lo + } + lo, hi := lo, hi + u := float64(r) + c := f64(0.5) if mode == nil else clamp((mode.?-lo) / (hi-lo), 0, 1) + if u > c { + u = 1-u + c = 1-c + lo, hi = hi, lo + } + return lo + (hi - lo) * math.sqrt(u * c) + +} +// Triangular Distribution +// See: http://wikipedia.org/wiki/Triangular_distribution +float32_triangular :: proc(lo, hi: f32, mode: Maybe(f32), r: ^Rand = nil) -> f32 { + if hi-lo == 0 { + return lo + } + lo, hi := lo, hi + u := float32(r) + c := f32(0.5) if mode == nil else clamp((mode.?-lo) / (hi-lo), 0, 1) + if u > c { + u = 1-u + c = 1-c + lo, hi = hi, lo + } + return lo + (hi - lo) * math.sqrt(u * c) +} + + +// Normal/Gaussian Distribution +float64_normal :: proc(mean, stddev: f64, r: ^Rand = nil) -> f64 { + return norm_float64(r) * stddev + mean +} +// Normal/Gaussian Distribution +float32_normal :: proc(mean, stddev: f32, r: ^Rand = nil) -> f32 { + return f32(float64_normal(f64(mean), f64(stddev), r)) +} + + +// Log Normal Distribution +float64_log_normal :: proc(mean, stddev: f64, r: ^Rand = nil) -> f64 { + return math.exp(float64_normal(mean, stddev, r)) +} +// Log Normal Distribution +float32_log_normal :: proc(mean, stddev: f32, r: ^Rand = nil) -> f32 { + return f32(float64_log_normal(f64(mean), f64(stddev), r)) +} + + +// Exponential Distribution +// `lambda` is 1.0/(desired mean). It should be non-zero. +// Return values range from +// 0 to positive infinity if lambda > 0 +// negative infinity to 0 if lambda <= 0 +float64_exponential :: proc(lambda: f64, r: ^Rand = nil) -> f64 { + return - math.ln(1 - float64(r)) / lambda +} +// Exponential Distribution +// `lambda` is 1.0/(desired mean). It should be non-zero. +// Return values range from +// 0 to positive infinity if lambda > 0 +// negative infinity to 0 if lambda <= 0 +float32_exponential :: proc(lambda: f32, r: ^Rand = nil) -> f32 { + return f32(float64_exponential(f64(lambda), r)) +} + + +// Gamma Distribution (NOT THE GAMMA FUNCTION) +// +// Required: alpha > 0 and beta > 0 +// +// math.pow(x, alpha-1) * math.exp(-x / beta) +// pdf(x) = -------------------------------------------- +// math.gamma(alpha) * math.pow(beta, alpha) +// +// mean is alpha*beta, variance is math.pow(alpha*beta, 2) +float64_gamma :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 { + if alpha <= 0 || beta <= 0 { + panic(#procedure + ": alpha and beta must be > 0.0") + } + + LOG4 :: 1.3862943611198906188344642429163531361510002687205105082413600189 + SG_MAGIC_CONST :: 2.5040773967762740733732583523868748412194809812852436493487 + + switch { + case alpha > 1: + // R.C.H. Cheng, "The generation of Gamma variables with non-integral shape parameters", Applied Statistics, (1977), 26, No. 1, p71-74 + + ainv := math.sqrt(2 * alpha - 1) + bbb := alpha - LOG4 + ccc := alpha + ainv + for { + u1 := float64(r) + if !(1e-7 < u1 && u1 < 0.9999999) { + continue + } + u2 := 1 - float64(r) + v := math.ln(u1 / (1 - u1)) / ainv + x := alpha * math.exp(v) + z := u1 * u1 * u2 + t := bbb + ccc*v - x + if t + SG_MAGIC_CONST - 4.5 * z >= 0 || t >= math.ln(z) { + return x * beta + } + } + case alpha == 1: + // float64_exponential(1/beta) + return -math.ln(1 - float64(r)) * beta + case: + // ALGORITHM GS of Statistical Computing - Kennedy & Gentle + x: f64 + for { + u := float64(r) + b := (math.e + alpha) / math.e + p := b * u + if p <= 1 { + x = math.pow(p, 1/alpha) + } else { + x = -math.ln((b - p) / alpha) + } + u1 := float64(r) + if p > 1 { + if u1 <= math.pow(x, alpha-1) { + break + } + } else if u1 <= math.exp(-x) { + break + } + } + return x * beta + } +} +// Gamma Distribution (NOT THE GAMMA FUNCTION) +// +// Required: alpha > 0 and beta > 0 +// +// math.pow(x, alpha-1) * math.exp(-x / beta) +// pdf(x) = -------------------------------------------- +// math.gamma(alpha) * math.pow(beta, alpha) +// +// mean is alpha*beta, variance is math.pow(alpha*beta, 2) +float32_gamma :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 { + return f32(float64_gamma(f64(alpha), f64(beta), r)) +} + + +// Beta Distribution +// +// Required: alpha > 0 and beta > 0 +// +// Return values range between 0 and 1 +float64_beta :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 { + if alpha <= 0 || beta <= 0 { + panic(#procedure + ": alpha and beta must be > 0.0") + } + // Knuth Vol 2 Ed 3 pg 134 "the beta distribution" + y := float64_gamma(alpha, 1.0, r) + if y != 0 { + return y / (y + float64_gamma(beta, 1.0, r)) + } + return 0 +} +// Beta Distribution +// +// Required: alpha > 0 and beta > 0 +// +// Return values range between 0 and 1 +float32_beta :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 { + return f32(float64_beta(f64(alpha), f64(beta), r)) +} + + +// Pareto distribution, `alpha` is the shape parameter. +// https://wikipedia.org/wiki/Pareto_distribution +float64_pareto :: proc(alpha: f64, r: ^Rand = nil) -> f64 { + return math.pow(1 - float64(r), -1.0 / alpha) +} +// Pareto distribution, `alpha` is the shape parameter. +// https://wikipedia.org/wiki/Pareto_distribution +float32_pareto :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 { + return f32(float64_pareto(f64(alpha), r)) +} + + +// Weibull distribution, `alpha` is the scale parameter, `beta` is the shape parameter. +float64_weibull :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 { + u := 1 - float64(r) + return alpha * math.pow(-math.ln(u), 1.0/beta) +} +// Weibull distribution, `alpha` is the scale parameter, `beta` is the shape parameter. +float32_weibull :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 { + return f32(float64_weibull(f64(alpha), f64(beta), r)) +} + + +// Circular Data (von Mises) Distribution +// `mean_angle` is the in mean angle between 0 and 2pi radians +// `kappa` is the concentration parameter which must be >= 0 +// When `kappa` is zero, the Distribution is a uniform Distribution over the range 0 to 2pi +float64_von_mises :: proc(mean_angle, kappa: f64, r: ^Rand = nil) -> f64 { + // Fisher, N.I., "Statistical Analysis of Circular Data", Cambridge University Press, 1993. + + mu := mean_angle + if kappa <= 1e-6 { + return math.TAU * float64(r) + } + + s := 0.5 / kappa + t := s + math.sqrt(1 + s*s) + z: f64 + for { + u1 := float64(r) + z = math.cos(math.TAU * 0.5 * u1) + + d := z / (t + z) + u2 := float64(r) + if u2 < 1 - d*d || u2 <= (1-d)*math.exp(d) { + break + } + } + + q := 1.0 / t + f := (q + z) / (1 + q*z) + u3 := float64(r) + if u3 > 0.5 { + return math.mod(mu + math.acos(f), math.TAU) + } else { + return math.mod(mu - math.acos(f), math.TAU) + } +} +// Circular Data (von Mises) Distribution +// `mean_angle` is the in mean angle between 0 and 2pi radians +// `kappa` is the concentration parameter which must be >= 0 +// When `kappa` is zero, the Distribution is a uniform Distribution over the range 0 to 2pi +float32_von_mises :: proc(mean_angle, kappa: f32, r: ^Rand = nil) -> f32 { + return f32(float64_von_mises(f64(mean_angle), f64(kappa), r)) +} + + +// Cauchy-Lorentz Distribution +// `x_0` is the location, `gamma` is the scale where `gamma` > 0 +float64_cauchy_lorentz :: proc(x_0, gamma: f64, r: ^Rand = nil) -> f64 { + assert(gamma > 0) + + // Calculated from the inverse CDF + + return math.tan(math.PI * (float64(r) - 0.5))*gamma + x_0 +} +// Cauchy-Lorentz Distribution +// `x_0` is the location, `gamma` is the scale where `gamma` > 0 +float32_cauchy_lorentz :: proc(x_0, gamma: f32, r: ^Rand = nil) -> f32 { + return f32(float64_cauchy_lorentz(f64(x_0), f64(gamma), r)) +} + + +// Log Cauchy-Lorentz Distribution +// `x_0` is the location, `gamma` is the scale where `gamma` > 0 +float64_log_cauchy_lorentz :: proc(x_0, gamma: f64, r: ^Rand = nil) -> f64 { + assert(gamma > 0) + return math.exp(math.tan(math.PI * (float64(r) - 0.5))*gamma + x_0) +} +// Log Cauchy-Lorentz Distribution +// `x_0` is the location, `gamma` is the scale where `gamma` > 0 +float32_log_cauchy_lorentz :: proc(x_0, gamma: f32, r: ^Rand = nil) -> f32 { + return f32(float64_log_cauchy_lorentz(f64(x_0), f64(gamma), r)) +} + + +// Laplace Distribution +// `b` is the scale where `b` > 0 +float64_laplace :: proc(mean, b: f64, r: ^Rand = nil) -> f64 { + assert(b > 0) + p := float64(r)-0.5 + return -math.sign(p)*math.ln(1 - 2*abs(p))*b + mean +} +// Laplace Distribution +// `b` is the scale where `b` > 0 +float32_laplace :: proc(mean, b: f32, r: ^Rand = nil) -> f32 { + return f32(float64_laplace(f64(mean), f64(b), r)) +} + + +// Gompertz Distribution +// `eta` is the shape, `b` is the scale +// Both `eta` and `b` must be > 0 +float64_gompertz :: proc(eta, b: f64, r: ^Rand = nil) -> f64 { + if eta <= 0 || b <= 0 { + panic(#procedure + ": eta and b must be > 0.0") + } + + p := float64(r) + return math.ln(1 - math.ln(1 - p)/eta)/b +} +// Gompertz Distribution +// `eta` is the shape, `b` is the scale +// Both `eta` and `b` must be > 0 +float32_gompertz :: proc(eta, b: f32, r: ^Rand = nil) -> f32 { + return f32(float64_gompertz(f64(eta), f64(b), r)) +} diff --git a/core/math/rand/exp.odin b/core/math/rand/exp.odin new file mode 100644 index 000000000..c0f92e99c --- /dev/null +++ b/core/math/rand/exp.odin @@ -0,0 +1,214 @@ +package rand + +import "core:math" + +// exp_float64 returns a exponential distribution in the range (0, max(f64)], +// with an exponential distribution who rate parameter is 1 (lambda) and whose mean +// is 1 (1/lambda). +// +// To produce a distribution with a differetn rate parameter, divide the result by +// the desired rate parameter +// +// "The Ziggurat Method for Generating Random Variables" +// Authors: George Marsaglia, Wai Wan Tsang +// Submitted: 2000-04-15. Published: 2000-10-02. +// https://www.jstatsoft.org/index.php/jss/article/view/v005i08/ziggurat.pdf [pdf] +// https://www.jstatsoft.org/article/view/v005i08 [web page] +// +exp_float64 :: proc(r: ^Rand = nil) -> f64 { + re :: 7.69711747013104972 + + @(static) + ke := [256]u32{ + 0xe290a139, 0x0, 0x9beadebc, 0xc377ac71, 0xd4ddb990, + 0xde893fb8, 0xe4a8e87c, 0xe8dff16a, 0xebf2deab, 0xee49a6e8, + 0xf0204efd, 0xf19bdb8e, 0xf2d458bb, 0xf3da104b, 0xf4b86d78, + 0xf577ad8a, 0xf61de83d, 0xf6afb784, 0xf730a573, 0xf7a37651, + 0xf80a5bb6, 0xf867189d, 0xf8bb1b4f, 0xf9079062, 0xf94d70ca, + 0xf98d8c7d, 0xf9c8928a, 0xf9ff175b, 0xfa319996, 0xfa6085f8, + 0xfa8c3a62, 0xfab5084e, 0xfadb36c8, 0xfaff0410, 0xfb20a6ea, + 0xfb404fb4, 0xfb5e2951, 0xfb7a59e9, 0xfb95038c, 0xfbae44ba, + 0xfbc638d8, 0xfbdcf892, 0xfbf29a30, 0xfc0731df, 0xfc1ad1ed, + 0xfc2d8b02, 0xfc3f6c4d, 0xfc5083ac, 0xfc60ddd1, 0xfc708662, + 0xfc7f8810, 0xfc8decb4, 0xfc9bbd62, 0xfca9027c, 0xfcb5c3c3, + 0xfcc20864, 0xfccdd70a, 0xfcd935e3, 0xfce42ab0, 0xfceebace, + 0xfcf8eb3b, 0xfd02c0a0, 0xfd0c3f59, 0xfd156b7b, 0xfd1e48d6, + 0xfd26daff, 0xfd2f2552, 0xfd372af7, 0xfd3eeee5, 0xfd4673e7, + 0xfd4dbc9e, 0xfd54cb85, 0xfd5ba2f2, 0xfd62451b, 0xfd68b415, + 0xfd6ef1da, 0xfd750047, 0xfd7ae120, 0xfd809612, 0xfd8620b4, + 0xfd8b8285, 0xfd90bcf5, 0xfd95d15e, 0xfd9ac10b, 0xfd9f8d36, + 0xfda43708, 0xfda8bf9e, 0xfdad2806, 0xfdb17141, 0xfdb59c46, + 0xfdb9a9fd, 0xfdbd9b46, 0xfdc170f6, 0xfdc52bd8, 0xfdc8ccac, + 0xfdcc542d, 0xfdcfc30b, 0xfdd319ef, 0xfdd6597a, 0xfdd98245, + 0xfddc94e5, 0xfddf91e6, 0xfde279ce, 0xfde54d1f, 0xfde80c52, + 0xfdeab7de, 0xfded5034, 0xfdefd5be, 0xfdf248e3, 0xfdf4aa06, + 0xfdf6f984, 0xfdf937b6, 0xfdfb64f4, 0xfdfd818d, 0xfdff8dd0, + 0xfe018a08, 0xfe03767a, 0xfe05536c, 0xfe07211c, 0xfe08dfc9, + 0xfe0a8fab, 0xfe0c30fb, 0xfe0dc3ec, 0xfe0f48b1, 0xfe10bf76, + 0xfe122869, 0xfe1383b4, 0xfe14d17c, 0xfe1611e7, 0xfe174516, + 0xfe186b2a, 0xfe19843e, 0xfe1a9070, 0xfe1b8fd6, 0xfe1c8289, + 0xfe1d689b, 0xfe1e4220, 0xfe1f0f26, 0xfe1fcfbc, 0xfe2083ed, + 0xfe212bc3, 0xfe21c745, 0xfe225678, 0xfe22d95f, 0xfe234ffb, + 0xfe23ba4a, 0xfe241849, 0xfe2469f2, 0xfe24af3c, 0xfe24e81e, + 0xfe25148b, 0xfe253474, 0xfe2547c7, 0xfe254e70, 0xfe25485a, + 0xfe25356a, 0xfe251586, 0xfe24e88f, 0xfe24ae64, 0xfe2466e1, + 0xfe2411df, 0xfe23af34, 0xfe233eb4, 0xfe22c02c, 0xfe22336b, + 0xfe219838, 0xfe20ee58, 0xfe20358c, 0xfe1f6d92, 0xfe1e9621, + 0xfe1daef0, 0xfe1cb7ac, 0xfe1bb002, 0xfe1a9798, 0xfe196e0d, + 0xfe1832fd, 0xfe16e5fe, 0xfe15869d, 0xfe141464, 0xfe128ed3, + 0xfe10f565, 0xfe0f478c, 0xfe0d84b1, 0xfe0bac36, 0xfe09bd73, + 0xfe07b7b5, 0xfe059a40, 0xfe03644c, 0xfe011504, 0xfdfeab88, + 0xfdfc26e9, 0xfdf98629, 0xfdf6c83b, 0xfdf3ec01, 0xfdf0f04a, + 0xfdedd3d1, 0xfdea953d, 0xfde7331e, 0xfde3abe9, 0xfddffdfb, + 0xfddc2791, 0xfdd826cd, 0xfdd3f9a8, 0xfdcf9dfc, 0xfdcb1176, + 0xfdc65198, 0xfdc15bb3, 0xfdbc2ce2, 0xfdb6c206, 0xfdb117be, + 0xfdab2a63, 0xfda4f5fd, 0xfd9e7640, 0xfd97a67a, 0xfd908192, + 0xfd8901f2, 0xfd812182, 0xfd78d98e, 0xfd7022bb, 0xfd66f4ed, + 0xfd5d4732, 0xfd530f9c, 0xfd48432b, 0xfd3cd59a, 0xfd30b936, + 0xfd23dea4, 0xfd16349e, 0xfd07a7a3, 0xfcf8219b, 0xfce7895b, + 0xfcd5c220, 0xfcc2aadb, 0xfcae1d5e, 0xfc97ed4e, 0xfc7fe6d4, + 0xfc65ccf3, 0xfc495762, 0xfc2a2fc8, 0xfc07ee19, 0xfbe213c1, + 0xfbb8051a, 0xfb890078, 0xfb5411a5, 0xfb180005, 0xfad33482, + 0xfa839276, 0xfa263b32, 0xf9b72d1c, 0xf930a1a2, 0xf889f023, + 0xf7b577d2, 0xf69c650c, 0xf51530f0, 0xf2cb0e3c, 0xeeefb15d, + 0xe6da6ecf, + } + @(static) + we := [256]f32{ + 2.0249555e-09, 1.486674e-11, 2.4409617e-11, 3.1968806e-11, + 3.844677e-11, 4.4228204e-11, 4.9516443e-11, 5.443359e-11, + 5.905944e-11, 6.344942e-11, 6.7643814e-11, 7.1672945e-11, + 7.556032e-11, 7.932458e-11, 8.298079e-11, 8.654132e-11, + 9.0016515e-11, 9.3415074e-11, 9.674443e-11, 1.0001099e-10, + 1.03220314e-10, 1.06377254e-10, 1.09486115e-10, 1.1255068e-10, + 1.1557435e-10, 1.1856015e-10, 1.2151083e-10, 1.2442886e-10, + 1.2731648e-10, 1.3017575e-10, 1.3300853e-10, 1.3581657e-10, + 1.3860142e-10, 1.4136457e-10, 1.4410738e-10, 1.4683108e-10, + 1.4953687e-10, 1.5222583e-10, 1.54899e-10, 1.5755733e-10, + 1.6020171e-10, 1.6283301e-10, 1.6545203e-10, 1.6805951e-10, + 1.7065617e-10, 1.732427e-10, 1.7581973e-10, 1.7838787e-10, + 1.8094774e-10, 1.8349985e-10, 1.8604476e-10, 1.8858298e-10, + 1.9111498e-10, 1.9364126e-10, 1.9616223e-10, 1.9867835e-10, + 2.0119004e-10, 2.0369768e-10, 2.0620168e-10, 2.087024e-10, + 2.1120022e-10, 2.136955e-10, 2.1618855e-10, 2.1867974e-10, + 2.2116936e-10, 2.2365775e-10, 2.261452e-10, 2.2863202e-10, + 2.311185e-10, 2.3360494e-10, 2.360916e-10, 2.3857874e-10, + 2.4106667e-10, 2.4355562e-10, 2.4604588e-10, 2.485377e-10, + 2.5103128e-10, 2.5352695e-10, 2.560249e-10, 2.585254e-10, + 2.6102867e-10, 2.6353494e-10, 2.6604446e-10, 2.6855745e-10, + 2.7107416e-10, 2.7359479e-10, 2.761196e-10, 2.7864877e-10, + 2.8118255e-10, 2.8372119e-10, 2.8626485e-10, 2.888138e-10, + 2.9136826e-10, 2.939284e-10, 2.9649452e-10, 2.9906677e-10, + 3.016454e-10, 3.0423064e-10, 3.0682268e-10, 3.0942177e-10, + 3.1202813e-10, 3.1464195e-10, 3.1726352e-10, 3.19893e-10, + 3.2253064e-10, 3.251767e-10, 3.2783135e-10, 3.3049485e-10, + 3.3316744e-10, 3.3584938e-10, 3.3854083e-10, 3.4124212e-10, + 3.4395342e-10, 3.46675e-10, 3.4940711e-10, 3.5215003e-10, + 3.5490397e-10, 3.5766917e-10, 3.6044595e-10, 3.6323455e-10, + 3.660352e-10, 3.6884823e-10, 3.7167386e-10, 3.745124e-10, + 3.773641e-10, 3.802293e-10, 3.8310827e-10, 3.860013e-10, + 3.8890866e-10, 3.918307e-10, 3.9476775e-10, 3.9772008e-10, + 4.0068804e-10, 4.0367196e-10, 4.0667217e-10, 4.09689e-10, + 4.1272286e-10, 4.1577405e-10, 4.1884296e-10, 4.2192994e-10, + 4.250354e-10, 4.281597e-10, 4.313033e-10, 4.3446652e-10, + 4.3764986e-10, 4.408537e-10, 4.4407847e-10, 4.4732465e-10, + 4.5059267e-10, 4.5388301e-10, 4.571962e-10, 4.6053267e-10, + 4.6389292e-10, 4.6727755e-10, 4.70687e-10, 4.741219e-10, + 4.7758275e-10, 4.810702e-10, 4.845848e-10, 4.8812715e-10, + 4.9169796e-10, 4.9529775e-10, 4.989273e-10, 5.0258725e-10, + 5.0627835e-10, 5.100013e-10, 5.1375687e-10, 5.1754584e-10, + 5.21369e-10, 5.2522725e-10, 5.2912136e-10, 5.330522e-10, + 5.370208e-10, 5.4102806e-10, 5.45075e-10, 5.491625e-10, + 5.532918e-10, 5.5746385e-10, 5.616799e-10, 5.6594107e-10, + 5.7024857e-10, 5.746037e-10, 5.7900773e-10, 5.834621e-10, + 5.8796823e-10, 5.925276e-10, 5.971417e-10, 6.018122e-10, + 6.065408e-10, 6.113292e-10, 6.1617933e-10, 6.2109295e-10, + 6.260722e-10, 6.3111916e-10, 6.3623595e-10, 6.4142497e-10, + 6.4668854e-10, 6.5202926e-10, 6.5744976e-10, 6.6295286e-10, + 6.6854156e-10, 6.742188e-10, 6.79988e-10, 6.858526e-10, + 6.9181616e-10, 6.978826e-10, 7.04056e-10, 7.103407e-10, + 7.167412e-10, 7.2326256e-10, 7.2990985e-10, 7.366886e-10, + 7.4360473e-10, 7.5066453e-10, 7.5787476e-10, 7.6524265e-10, + 7.7277595e-10, 7.80483e-10, 7.883728e-10, 7.9645507e-10, + 8.047402e-10, 8.1323964e-10, 8.219657e-10, 8.309319e-10, + 8.401528e-10, 8.496445e-10, 8.594247e-10, 8.6951274e-10, + 8.799301e-10, 8.9070046e-10, 9.018503e-10, 9.134092e-10, + 9.254101e-10, 9.378904e-10, 9.508923e-10, 9.644638e-10, + 9.786603e-10, 9.935448e-10, 1.0091913e-09, 1.025686e-09, + 1.0431306e-09, 1.0616465e-09, 1.08138e-09, 1.1025096e-09, + 1.1252564e-09, 1.1498986e-09, 1.1767932e-09, 1.206409e-09, + 1.2393786e-09, 1.276585e-09, 1.3193139e-09, 1.3695435e-09, + 1.4305498e-09, 1.508365e-09, 1.6160854e-09, 1.7921248e-09, + } + @(static) + fe := [256]f32{ + 1, 0.9381437, 0.90046996, 0.87170434, 0.8477855, 0.8269933, + 0.8084217, 0.7915276, 0.77595687, 0.7614634, 0.7478686, + 0.7350381, 0.72286767, 0.71127474, 0.70019263, 0.6895665, + 0.67935055, 0.6695063, 0.66000086, 0.65080583, 0.6418967, + 0.63325197, 0.6248527, 0.6166822, 0.60872537, 0.60096896, + 0.5934009, 0.58601034, 0.5787874, 0.57172304, 0.5648092, + 0.5580383, 0.5514034, 0.5448982, 0.5385169, 0.53225386, + 0.5261042, 0.52006316, 0.5141264, 0.50828975, 0.5025495, + 0.496902, 0.49134386, 0.485872, 0.48048335, 0.4751752, + 0.46994483, 0.46478975, 0.45970762, 0.45469615, 0.44975325, + 0.44487688, 0.44006512, 0.43531612, 0.43062815, 0.42599955, + 0.42142874, 0.4169142, 0.41245446, 0.40804818, 0.403694, + 0.3993907, 0.39513698, 0.39093173, 0.38677382, 0.38266218, + 0.37859577, 0.37457356, 0.37059465, 0.3666581, 0.362763, + 0.35890847, 0.35509375, 0.351318, 0.3475805, 0.34388044, + 0.34021714, 0.3365899, 0.33299807, 0.32944095, 0.32591796, + 0.3224285, 0.3189719, 0.31554767, 0.31215525, 0.30879408, + 0.3054636, 0.3021634, 0.29889292, 0.2956517, 0.29243928, + 0.28925523, 0.28609908, 0.28297043, 0.27986884, 0.27679393, + 0.2737453, 0.2707226, 0.2677254, 0.26475343, 0.26180625, + 0.25888354, 0.25598502, 0.2531103, 0.25025907, 0.24743107, + 0.24462597, 0.24184346, 0.23908329, 0.23634516, 0.23362878, + 0.23093392, 0.2282603, 0.22560766, 0.22297576, 0.22036438, + 0.21777324, 0.21520215, 0.21265087, 0.21011916, 0.20760682, + 0.20511365, 0.20263945, 0.20018397, 0.19774707, 0.19532852, + 0.19292815, 0.19054577, 0.1881812, 0.18583426, 0.18350479, + 0.1811926, 0.17889754, 0.17661946, 0.17435817, 0.17211354, + 0.1698854, 0.16767362, 0.16547804, 0.16329853, 0.16113494, + 0.15898713, 0.15685499, 0.15473837, 0.15263714, 0.15055119, + 0.14848037, 0.14642459, 0.14438373, 0.14235765, 0.14034624, + 0.13834943, 0.13636707, 0.13439907, 0.13244532, 0.13050574, + 0.1285802, 0.12666863, 0.12477092, 0.12288698, 0.12101672, + 0.119160056, 0.1173169, 0.115487166, 0.11367077, 0.11186763, + 0.11007768, 0.10830083, 0.10653701, 0.10478614, 0.10304816, + 0.101323, 0.09961058, 0.09791085, 0.09622374, 0.09454919, + 0.09288713, 0.091237515, 0.08960028, 0.087975375, 0.08636274, + 0.08476233, 0.083174095, 0.081597984, 0.08003395, 0.07848195, + 0.076941945, 0.07541389, 0.07389775, 0.072393484, 0.07090106, + 0.069420435, 0.06795159, 0.066494495, 0.06504912, 0.063615434, + 0.062193416, 0.060783047, 0.059384305, 0.057997175, + 0.05662164, 0.05525769, 0.053905312, 0.052564494, 0.051235236, + 0.049917534, 0.048611384, 0.047316793, 0.046033762, 0.0447623, + 0.043502413, 0.042254124, 0.041017443, 0.039792392, + 0.038578995, 0.037377283, 0.036187284, 0.035009038, + 0.033842582, 0.032687962, 0.031545233, 0.030414443, 0.02929566, + 0.02818895, 0.027094385, 0.026012046, 0.024942026, 0.023884421, + 0.022839336, 0.021806888, 0.020787204, 0.019780423, 0.0187867, + 0.0178062, 0.016839107, 0.015885621, 0.014945968, 0.014020392, + 0.013109165, 0.012212592, 0.011331013, 0.01046481, 0.009614414, + 0.008780315, 0.007963077, 0.0071633533, 0.006381906, + 0.0056196423, 0.0048776558, 0.004157295, 0.0034602648, + 0.0027887989, 0.0021459677, 0.0015362998, 0.0009672693, + 0.00045413437, + } + + for { + j := uint32(r) + i := j & 0xFF + x := f64(j) * f64(we[i]) + if j < ke[i] { + return x + } + if i == 0 { + return re - math.ln(float64(r)) + } + if fe[i]+f32(float64(r))*(fe[i-1]-fe[i]) < f32(math.exp(-x)) { + return x + } + } +} \ No newline at end of file diff --git a/core/math/rand/normal.odin b/core/math/rand/normal.odin index 4a77543ba..a9edd0f19 100644 --- a/core/math/rand/normal.odin +++ b/core/math/rand/normal.odin @@ -2,6 +2,12 @@ package rand import "core:math" + +// norm_float64 returns a normally distributed f64 in the range -max(f64) through +max(f64) inclusive, +// with a standard normal distribution with a mean of 0 and standard deviation of 1. +// +// sample = norm_float64() * std_dev + mean +// // // Normal distribution // @@ -11,12 +17,6 @@ import "core:math" // https://www.jstatsoft.org/index.php/jss/article/view/v005i08/ziggurat.pdf [pdf] // https://www.jstatsoft.org/article/view/v005i08 [web page] // - -// norm_float64 returns a normally distributed f64 in the range -max(f64) through +max(f64) inclusive, -// with a standard normal distribution with a mean of 0 and standard deviation of 1. -// -// sample = norm_float64() * std_dev + mean -// norm_float64 :: proc(r: ^Rand = nil) -> f64 { rn :: 3.442619855899 @@ -49,7 +49,6 @@ norm_float64 :: proc(r: ^Rand = nil) -> f64 { 0x7da61a1e, 0x7d72a0fb, 0x7d30e097, 0x7cd9b4ab, 0x7c600f1a, 0x7ba90bdc, 0x7a722176, 0x77d664e5, } - @(static) wn := [128]f32{ 1.7290405e-09, 1.2680929e-10, 1.6897518e-10, 1.9862688e-10, @@ -85,7 +84,6 @@ norm_float64 :: proc(r: ^Rand = nil) -> f64 { 1.2601323e-09, 1.2857697e-09, 1.3146202e-09, 1.347784e-09, 1.3870636e-09, 1.4357403e-09, 1.5008659e-09, 1.6030948e-09, } - @(static) fn := [128]f32{ 1.00000000, 0.9635997, 0.9362827, 0.9130436, 0.89228165, diff --git a/core/math/rand/rand.odin b/core/math/rand/rand.odin index 9bd30c216..f7dfcb3b8 100644 --- a/core/math/rand/rand.odin +++ b/core/math/rand/rand.odin @@ -1,15 +1,16 @@ package rand +import "core:intrinsics" + Rand :: struct { state: u64, inc: u64, + is_system: bool, } @(private) -_GLOBAL_SEED_DATA := 1234567890 -@(private) -global_rand := create(u64(uintptr(&_GLOBAL_SEED_DATA))) +global_rand := create(u64(intrinsics.read_cycle_counter())) set_global_seed :: proc(seed: u64) { init(&global_rand, seed) @@ -29,6 +30,16 @@ init :: proc(r: ^Rand, seed: u64) { _random(r) } +init_as_system :: proc(r: ^Rand) { + if !#defined(_system_random) { + panic(#procedure + " is not supported on this platform yet") + } + r.state = 0 + r.inc = 0 + r.is_system = true +} + +@(private) _random :: proc(r: ^Rand) -> u32 { r := r if r == nil { @@ -36,6 +47,12 @@ _random :: proc(r: ^Rand) -> u32 { // enforce the global random state if necessary with `nil` r = &global_rand } + when #defined(_system_random) { + if r.is_system { + return _system_random() + } + } + old_state := r.state r.state = old_state * 6364136223846793005 + (r.inc|1) xor_shifted := u32(((old_state>>18) ~ old_state) >> 27) @@ -70,7 +87,7 @@ int31_max :: proc(n: i32, r: ^Rand = nil) -> i32 { if n&(n-1) == 0 { return int31(r) & (n-1) } - max := i32((1<<31) - 1 - (1<<31)&u32(n)) + max := i32((1<<31) - 1 - (1<<31)%u32(n)) v := int31(r) for v > max { v = int31(r) @@ -85,7 +102,7 @@ int63_max :: proc(n: i64, r: ^Rand = nil) -> i64 { if n&(n-1) == 0 { return int63(r) & (n-1) } - max := i64((1<<63) - 1 - (1<<63)&u64(n)) + max := i64((1<<63) - 1 - (1<<63)%u64(n)) v := int63(r) for v > max { v = int63(r) @@ -100,7 +117,7 @@ int127_max :: proc(n: i128, r: ^Rand = nil) -> i128 { if n&(n-1) == 0 { return int127(r) & (n-1) } - max := i128((1<<63) - 1 - (1<<63)&u128(n)) + max := i128((1<<127) - 1 - (1<<127)%u128(n)) v := int127(r) for v > max { v = int127(r) @@ -119,13 +136,14 @@ int_max :: proc(n: int, r: ^Rand = nil) -> int { } } +// Uniform random distribution [0, 1) float64 :: proc(r: ^Rand = nil) -> f64 { return f64(int63_max(1<<53, r)) / (1 << 53) } +// Uniform random distribution [0, 1) float32 :: proc(r: ^Rand = nil) -> f32 { return f32(float64(r)) } float64_range :: proc(lo, hi: f64, r: ^Rand = nil) -> f64 { return (hi-lo)*float64(r) + lo } float32_range :: proc(lo, hi: f32, r: ^Rand = nil) -> f32 { return (hi-lo)*float32(r) + lo } - read :: proc(p: []byte, r: ^Rand = nil) -> (n: int) { pos := i8(0) val := i64(0) @@ -142,8 +160,8 @@ read :: proc(p: []byte, r: ^Rand = nil) -> (n: int) { } // perm returns a slice of n ints in a pseudo-random permutation of integers in the range [0, n) -perm :: proc(n: int, r: ^Rand = nil) -> []int { - m := make([]int, n) +perm :: proc(n: int, r: ^Rand = nil, allocator := context.allocator) -> []int { + m := make([]int, n, allocator) for i := 0; i < n; i += 1 { j := int_max(i+1, r) m[i] = m[j] diff --git a/core/math/rand/system_darwin.odin b/core/math/rand/system_darwin.odin new file mode 100644 index 000000000..f51e4473e --- /dev/null +++ b/core/math/rand/system_darwin.odin @@ -0,0 +1,21 @@ +package rand + +import "core:sys/darwin" + +_system_random :: proc() -> u32 { + for { + value: u32 + ret := darwin.syscall_getentropy(([^]u8)(&value), 4) + if ret < 0 { + switch ret { + case -4: // EINTR + continue + case -78: // ENOSYS + panic("getentropy not available in kernel") + case: + panic("getentropy failed") + } + } + return value + } +} \ No newline at end of file diff --git a/core/math/rand/system_linux.odin b/core/math/rand/system_linux.odin new file mode 100644 index 000000000..bfdc8872b --- /dev/null +++ b/core/math/rand/system_linux.odin @@ -0,0 +1,27 @@ +package rand + +import "core:sys/unix" + +_system_random :: proc() -> u32 { + for { + value: u32 + ret := unix.sys_getrandom(([^]u8)(&value), 4, 0) + if ret < 0 { + switch ret { + case -4: // EINTR + // Call interupted by a signal handler, just retry the request. + continue + case -38: // ENOSYS + // The kernel is apparently prehistoric (< 3.17 circa 2014) + // and does not support getrandom. + panic("getrandom not available in kernel") + case: + // All other failures are things that should NEVER happen + // unless the kernel interface changes (ie: the Linux + // developers break userland). + panic("getrandom failed") + } + } + return value + } +} \ No newline at end of file diff --git a/core/math/rand/system_windows.odin b/core/math/rand/system_windows.odin new file mode 100644 index 000000000..ee9cd0294 --- /dev/null +++ b/core/math/rand/system_windows.odin @@ -0,0 +1,12 @@ +package rand + +import win32 "core:sys/windows" + +_system_random :: proc() -> u32 { + value: u32 + status := win32.BCryptGenRandom(nil, ([^]u8)(&value), 4, win32.BCRYPT_USE_SYSTEM_PREFERRED_RNG) + if status < 0 { + panic("BCryptGenRandom failed") + } + return value +} \ No newline at end of file diff --git a/core/mem/alloc.odin b/core/mem/alloc.odin index bdd2899a9..7416ebdb7 100644 --- a/core/mem/alloc.odin +++ b/core/mem/alloc.odin @@ -55,6 +55,11 @@ Allocator :: struct { DEFAULT_ALIGNMENT :: 2*align_of(rawptr) +DEFAULT_PAGE_SIZE :: + 64 * 1024 when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64 else + 16 * 1024 when ODIN_OS == .Darwin && ODIN_ARCH == .arm64 else + 4 * 1024 + alloc :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr { if size == 0 { return nil diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin index b8bd9a065..e2a0cc0fc 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -52,15 +52,16 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, switch mode { case .Alloc: - total_size := size + alignment + #no_bounds_check end := &arena.data[arena.offset] + + ptr := align_forward(end, uintptr(alignment)) + + total_size := size + ptr_sub((^byte)(ptr), (^byte)(end)) if arena.offset + total_size > len(arena.data) { return nil, .Out_Of_Memory } - #no_bounds_check end := &arena.data[arena.offset] - - ptr := align_forward(end, uintptr(alignment)) arena.offset += total_size arena.peak_used = max(arena.peak_used, arena.offset) zero(ptr, size) @@ -662,6 +663,7 @@ dynamic_pool_destroy :: proc(using pool: ^Dynamic_Pool) { dynamic_pool_free_all(pool) delete(unused_blocks) delete(used_blocks) + delete(out_band_allocations) zero(pool, size_of(pool^)) } @@ -746,6 +748,8 @@ dynamic_pool_reset :: proc(using pool: ^Dynamic_Pool) { free(a, block_allocator) } clear(&out_band_allocations) + + bytes_left = 0 // Make new allocations call `cycle_new_block` again. } dynamic_pool_free_all :: proc(using pool: ^Dynamic_Pool) { @@ -855,7 +859,7 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, result: []byte err: Allocator_Error - if mode == .Free && old_memory not_in data.allocation_map { + if mode == .Free && old_memory != nil && old_memory not_in data.allocation_map { append(&data.bad_free_array, Tracking_Allocator_Bad_Free_Entry{ memory = old_memory, location = loc, @@ -883,6 +887,10 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, } case .Free: delete_key(&data.allocation_map, old_memory) + case .Free_All: + if data.clear_on_free_all { + clear_map(&data.allocation_map) + } case .Resize: if old_memory != result_ptr { delete_key(&data.allocation_map, old_memory) @@ -895,11 +903,6 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, location = loc, } - case .Free_All: - if data.clear_on_free_all { - clear_map(&data.allocation_map) - } - case .Query_Features: set := (^Allocator_Mode_Set)(old_memory) if set != nil { diff --git a/core/mem/doc.odin b/core/mem/doc.odin new file mode 100644 index 000000000..fe53dee83 --- /dev/null +++ b/core/mem/doc.odin @@ -0,0 +1,34 @@ +/* +package mem implements various types of allocators. + + +An example of how to use the `Tracking_Allocator` to track subsequent allocations +in your program and report leaks and bad frees: + +```odin +package foo + +import "core:mem" +import "core:fmt" + +_main :: proc() { + do stuff +} + +main :: proc() { + track: mem.Tracking_Allocator + mem.tracking_allocator_init(&track, context.allocator) + context.allocator = mem.tracking_allocator(&track) + + _main() + + for _, leak in track.allocation_map { + fmt.printf("%v leaked %v bytes\n", leak.location, leak.size) + } + for bad_free in track.bad_free_array { + fmt.printf("%v allocation %p was freed badly\n", bad_free.location, bad_free.memory) + } +} +``` +*/ +package mem \ No newline at end of file diff --git a/core/mem/mem.odin b/core/mem/mem.odin index 8eb877e75..7295a2afa 100644 --- a/core/mem/mem.odin +++ b/core/mem/mem.odin @@ -3,6 +3,12 @@ package mem import "core:runtime" import "core:intrinsics" +Byte :: 1 +Kilobyte :: 1024 * Byte +Megabyte :: 1024 * Kilobyte +Gigabyte :: 1024 * Megabyte +Terabyte :: 1024 * Gigabyte + set :: proc "contextless" (data: rawptr, value: byte, len: int) -> rawptr { return runtime.memset(data, i32(value), len) } @@ -16,14 +22,16 @@ zero_explicit :: proc "contextless" (data: rawptr, len: int) -> rawptr { // equivalent semantics to those provided by the C11 Annex K 3.7.4.1 // memset_s call. intrinsics.mem_zero_volatile(data, len) // Use the volatile mem_zero - intrinsics.atomic_fence() // Prevent reordering + intrinsics.atomic_thread_fence(.Seq_Cst) // Prevent reordering return data } -zero_item :: proc "contextless" (item: $P/^$T) { +zero_item :: proc "contextless" (item: $P/^$T) -> P { intrinsics.mem_zero(item, size_of(T)) + return item } -zero_slice :: proc "contextless" (data: $T/[]$E) { +zero_slice :: proc "contextless" (data: $T/[]$E) -> T { zero(raw_data(data), size_of(E)*len(data)) + return data } @@ -101,6 +109,12 @@ check_zero_ptr :: proc(ptr: rawptr, len: int) -> bool { case ptr == nil: return true } + switch len { + case 1: return (^u8)(ptr)^ == 0 + case 2: return intrinsics.unaligned_load((^u16)(ptr)) == 0 + case 4: return intrinsics.unaligned_load((^u32)(ptr)) == 0 + case 8: return intrinsics.unaligned_load((^u64)(ptr)) == 0 + } start := uintptr(ptr) start_aligned := align_forward_uintptr(start, align_of(uintptr)) @@ -144,7 +158,7 @@ slice_ptr :: proc "contextless" (ptr: ^$T, len: int) -> []T { return ([^]T)(ptr)[:len] } -byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byte { +byte_slice :: #force_inline proc "contextless" (data: rawptr, #any_int len: int) -> []byte { return ([^]u8)(data)[:max(len, 0)] } @@ -166,7 +180,7 @@ slice_data_cast :: proc "contextless" ($T: typeid/[]$A, slice: $S/[]$B) -> T { slice_to_components :: proc "contextless" (slice: $E/[]$T) -> (data: ^T, len: int) { s := transmute(Raw_Slice)slice - return s.data, s.len + return (^T)(s.data), s.len } buffer_from_slice :: proc "contextless" (backing: $T/[]$E) -> [dynamic]E { @@ -192,11 +206,6 @@ any_to_bytes :: proc "contextless" (val: any) -> []byte { } -kilobytes :: proc "contextless" (x: int) -> int { return (x) * 1024 } -megabytes :: proc "contextless" (x: int) -> int { return kilobytes(x) * 1024 } -gigabytes :: proc "contextless" (x: int) -> int { return megabytes(x) * 1024 } -terabytes :: proc "contextless" (x: int) -> int { return gigabytes(x) * 1024 } - is_power_of_two :: proc "contextless" (x: uintptr) -> bool { if x <= 0 { return false diff --git a/core/mem/raw.odin b/core/mem/raw.odin index 9eef4f6e3..2bce2d7aa 100644 --- a/core/mem/raw.odin +++ b/core/mem/raw.odin @@ -20,20 +20,12 @@ make_any :: proc "contextless" (data: rawptr, id: typeid) -> any { return transmute(any)Raw_Any{data, id} } -raw_array_data :: proc "contextless" (a: $P/^($T/[$N]$E)) -> ^E { - return (^E)(a) -} -raw_string_data :: proc "contextless" (s: $T/string) -> ^byte { - return (transmute(Raw_String)s).data -} -raw_slice_data :: proc "contextless" (a: $T/[]$E) -> ^E { - return cast(^E)(transmute(Raw_Slice)a).data -} -raw_dynamic_array_data :: proc "contextless" (a: $T/[dynamic]$E) -> ^E { - return cast(^E)(transmute(Raw_Dynamic_Array)a).data -} - -raw_data :: proc{raw_array_data, raw_string_data, raw_slice_data, raw_dynamic_array_data} +raw_array_data :: runtime.raw_array_data +raw_simd_data :: runtime.raw_simd_data +raw_string_data :: runtime.raw_string_data +raw_slice_data :: runtime.raw_slice_data +raw_dynamic_array_data :: runtime.raw_dynamic_array_data +raw_data :: runtime.raw_data Poly_Raw_Map_Entry :: struct($Key, $Value: typeid) { diff --git a/core/mem/virtual/virtual.odin b/core/mem/virtual/virtual.odin index 38c654254..21ab5ef21 100644 --- a/core/mem/virtual/virtual.odin +++ b/core/mem/virtual/virtual.odin @@ -6,25 +6,25 @@ DEFAULT_PAGE_SIZE := uint(4096) Allocator_Error :: mem.Allocator_Error -reserve :: proc(size: uint) -> (data: []byte, err: Allocator_Error) { +reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) { return _reserve(size) } -commit :: proc(data: rawptr, size: uint) -> Allocator_Error { +commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error { return _commit(data, size) } -reserve_and_commit :: proc(size: uint) -> (data: []byte, err: Allocator_Error) { +reserve_and_commit :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) { data = reserve(size) or_return commit(raw_data(data), size) or_return return } -decommit :: proc(data: rawptr, size: uint) { +decommit :: proc "contextless" (data: rawptr, size: uint) { _decommit(data, size) } -release :: proc(data: rawptr, size: uint) { +release :: proc "contextless" (data: rawptr, size: uint) { _release(data, size) } @@ -36,7 +36,7 @@ Protect_Flag :: enum u32 { Protect_Flags :: distinct bit_set[Protect_Flag; u32] Protect_No_Access :: Protect_Flags{} -protect :: proc(data: rawptr, size: uint, flags: Protect_Flags) -> bool { +protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool { return _protect(data, size, flags) } @@ -82,11 +82,13 @@ memory_block_alloc :: proc(committed, reserved: uint, flags: Memory_Block_Flags) pmblock := platform_memory_alloc(0, total_size) or_return pmblock.block.base = ([^]byte)(uintptr(pmblock) + base_offset) - commit(pmblock.block.base, committed) or_return + commit_err := platform_memory_commit(pmblock, uint(base_offset) + committed) + assert(commit_err == nil) + // Should be zeroed assert(pmblock.block.used == 0) assert(pmblock.block.prev == nil) - if (do_protection) { + if do_protection { protect(rawptr(uintptr(pmblock) + protect_offset), page_size, Protect_No_Access) } @@ -105,7 +107,7 @@ memory_block_alloc :: proc(committed, reserved: uint, flags: Memory_Block_Flags) } alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int) -> (data: []byte, err: Allocator_Error) { - calc_alignment_offset :: proc(block: ^Memory_Block, alignment: uintptr) -> uint { + calc_alignment_offset :: proc "contextless" (block: ^Memory_Block, alignment: uintptr) -> uint { alignment_offset := uint(0) ptr := uintptr(block.base[block.used:]) mask := alignment-1 @@ -115,23 +117,37 @@ alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int) return alignment_offset } - + do_commit_if_necessary :: proc(block: ^Memory_Block, size: uint) -> (err: Allocator_Error) { + if block.committed - block.used < size { + pmblock := (^Platform_Memory_Block)(block) + base_offset := uint(uintptr(pmblock.block.base) - uintptr(pmblock)) + platform_total_commit := base_offset + block.used + size + + assert(pmblock.committed <= pmblock.reserved) + assert(pmblock.committed < platform_total_commit) + + platform_memory_commit(pmblock, platform_total_commit) or_return + + pmblock.committed = platform_total_commit + block.committed = pmblock.committed - base_offset + } + return nil + } + + alignment_offset := calc_alignment_offset(block, uintptr(alignment)) - size := uint(min_size) + alignment_offset - + if block.used + size > block.reserved { err = .Out_Of_Memory return } - - ptr := block.base[block.used:] - ptr = ptr[alignment_offset:] - + assert(block.committed <= block.reserved) + do_commit_if_necessary(block, size) or_return + + data = block.base[block.used+alignment_offset:][:min_size] block.used += size - assert(block.used <= block.reserved) - - return ptr[:min_size], nil + return } diff --git a/core/mem/virtual/virtual_linux.odin b/core/mem/virtual/virtual_linux.odin index 71a56e499..2f6fbdd01 100644 --- a/core/mem/virtual/virtual_linux.odin +++ b/core/mem/virtual/virtual_linux.odin @@ -37,9 +37,9 @@ MADV_WIPEONFORK :: 18 MADV_KEEPONFORK :: 19 MADV_HWPOISON :: 100 -mmap :: proc "contextless" (addr: rawptr, length: uint, prot: c.int, flags: c.int, fd: c.int, offset: uintptr) -> rawptr { +mmap :: proc "contextless" (addr: rawptr, length: uint, prot: c.int, flags: c.int, fd: c.int, offset: uintptr) -> int { res := intrinsics.syscall(unix.SYS_mmap, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flags), uintptr(fd), offset) - return rawptr(res) + return int(res) } munmap :: proc "contextless" (addr: rawptr, length: uint) -> c.int { @@ -58,16 +58,15 @@ madvise :: proc "contextless" (addr: rawptr, length: uint, advice: c.int) -> c.i } -_reserve :: proc(size: uint) -> (data: []byte, err: Allocator_Error) { - MAP_FAILED := rawptr(~uintptr(0)) +_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) { result := mmap(nil, size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) - if result == MAP_FAILED { + if result < 0 && result > -4096 { return nil, .Out_Of_Memory } - return ([^]byte)(result)[:size], nil + return ([^]byte)(uintptr(result))[:size], nil } -_commit :: proc(data: rawptr, size: uint) -> Allocator_Error { +_commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error { result := mprotect(data, size, PROT_READ|PROT_WRITE) if result != 0 { // TODO(bill): Handle error value correctly @@ -75,14 +74,14 @@ _commit :: proc(data: rawptr, size: uint) -> Allocator_Error { } return nil } -_decommit :: proc(data: rawptr, size: uint) { +_decommit :: proc "contextless" (data: rawptr, size: uint) { mprotect(data, size, PROT_NONE) madvise(data, size, MADV_FREE) } -_release :: proc(data: rawptr, size: uint) { +_release :: proc "contextless" (data: rawptr, size: uint) { munmap(data, size) } -_protect :: proc(data: rawptr, size: uint, flags: Protect_Flags) -> bool { +_protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool { pflags: c.int pflags = PROT_NONE if .Read in flags { pflags |= PROT_READ } diff --git a/core/mem/virtual/virtual_platform.odin b/core/mem/virtual/virtual_platform.odin index c4211ba5e..367346f63 100644 --- a/core/mem/virtual/virtual_platform.odin +++ b/core/mem/virtual/virtual_platform.odin @@ -1,15 +1,16 @@ //+private package mem_virtual -import sync "core:sync/sync2" +import "core:sync" Platform_Memory_Block :: struct { - block: Memory_Block, - reserved: uint, + block: Memory_Block, + committed: uint, + reserved: uint, prev, next: ^Platform_Memory_Block, } -platform_memory_alloc :: proc(to_commit, to_reserve: uint) -> (block: ^Platform_Memory_Block, err: Allocator_Error) { +platform_memory_alloc :: proc "contextless" (to_commit, to_reserve: uint) -> (block: ^Platform_Memory_Block, err: Allocator_Error) { to_commit, to_reserve := to_commit, to_reserve to_reserve = max(to_commit, to_reserve) @@ -20,12 +21,13 @@ platform_memory_alloc :: proc(to_commit, to_reserve: uint) -> (block: ^Platform_ commit(raw_data(data), to_commit) block = (^Platform_Memory_Block)(raw_data(data)) - block.reserved = to_reserve + block.committed = to_commit + block.reserved = to_reserve return } -platform_memory_free :: proc(block: ^Platform_Memory_Block) { +platform_memory_free :: proc "contextless" (block: ^Platform_Memory_Block) { if block != nil { release(block, block.reserved) } @@ -52,3 +54,17 @@ platform_memory_init :: proc() { global_platform_memory_block_sentinel_set = true } } + +platform_memory_commit :: proc "contextless" (block: ^Platform_Memory_Block, to_commit: uint) -> (err: Allocator_Error) { + if to_commit < block.committed { + return nil + } + if to_commit > block.reserved { + return .Out_Of_Memory + } + + + commit(block, to_commit) or_return + block.committed = to_commit + return nil +} diff --git a/core/mem/virtual/virtual_windows.odin b/core/mem/virtual/virtual_windows.odin index 623e8d469..ef0bf6f1a 100644 --- a/core/mem/virtual/virtual_windows.odin +++ b/core/mem/virtual/virtual_windows.odin @@ -62,7 +62,7 @@ foreign Kernel32 { } -_reserve :: proc(size: uint) -> (data: []byte, err: Allocator_Error) { +_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) { result := VirtualAlloc(nil, size, MEM_RESERVE, PAGE_READWRITE) if result == nil { err = .Out_Of_Memory @@ -72,7 +72,7 @@ _reserve :: proc(size: uint) -> (data: []byte, err: Allocator_Error) { return } -_commit :: proc(data: rawptr, size: uint) -> Allocator_Error { +_commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error { result := VirtualAlloc(data, size, MEM_COMMIT, PAGE_READWRITE) if result == nil { switch err := GetLastError(); err { @@ -85,13 +85,13 @@ _commit :: proc(data: rawptr, size: uint) -> Allocator_Error { } return nil } -_decommit :: proc(data: rawptr, size: uint) { +_decommit :: proc "contextless" (data: rawptr, size: uint) { VirtualFree(data, size, MEM_DECOMMIT) } -_release :: proc(data: rawptr, size: uint) { +_release :: proc "contextless" (data: rawptr, size: uint) { VirtualFree(data, 0, MEM_RELEASE) } -_protect :: proc(data: rawptr, size: uint, flags: Protect_Flags) -> bool { +_protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool { pflags: u32 pflags = PAGE_NOACCESS switch flags { diff --git a/core/odin/ast/ast.odin b/core/odin/ast/ast.odin index 9db57541b..f4aa67446 100644 --- a/core/odin/ast/ast.odin +++ b/core/odin/ast/ast.odin @@ -34,7 +34,7 @@ Node :: struct { pos: tokenizer.Pos, end: tokenizer.Pos, state_flags: Node_State_Flags, - derived: any, + derived: Any_Node, } Comment_Group :: struct { @@ -88,9 +88,11 @@ File :: struct { Expr :: struct { using expr_base: Node, + derived_expr: Any_Expr, } Stmt :: struct { using stmt_base: Node, + derived_stmt: Any_Stmt, } Decl :: struct { using decl_base: Stmt, @@ -151,6 +153,7 @@ Comp_Lit :: struct { open: tokenizer.Pos, elems: []^Expr, close: tokenizer.Pos, + tag: ^Expr, } @@ -540,7 +543,7 @@ unparen_expr :: proc(expr: ^Expr) -> (val: ^Expr) { return } for { - e, ok := val.derived.(Paren_Expr) + e, ok := val.derived.(^Paren_Expr) if !ok || e.expr == nil { break } @@ -705,13 +708,19 @@ Struct_Type :: struct { name_count: int, } +Union_Type_Kind :: enum u8 { + Normal, + maybe, + no_nil, + shared_nil, +} + Union_Type :: struct { using node: Expr, tok_pos: tokenizer.Pos, poly_params: ^Field_List, align: ^Expr, - is_maybe: bool, - is_no_nil: bool, + kind: Union_Type_Kind, where_token: tokenizer.Token, where_clauses: []^Expr, variants: []^Expr, @@ -757,4 +766,173 @@ Matrix_Type :: struct { row_count: ^Expr, column_count: ^Expr, elem: ^Expr, -} \ No newline at end of file +} + + +Any_Node :: union { + ^Package, + ^File, + ^Comment_Group, + + ^Bad_Expr, + ^Ident, + ^Implicit, + ^Undef, + ^Basic_Lit, + ^Basic_Directive, + ^Ellipsis, + ^Proc_Lit, + ^Comp_Lit, + ^Tag_Expr, + ^Unary_Expr, + ^Binary_Expr, + ^Paren_Expr, + ^Selector_Expr, + ^Implicit_Selector_Expr, + ^Selector_Call_Expr, + ^Index_Expr, + ^Deref_Expr, + ^Slice_Expr, + ^Matrix_Index_Expr, + ^Call_Expr, + ^Field_Value, + ^Ternary_If_Expr, + ^Ternary_When_Expr, + ^Or_Else_Expr, + ^Or_Return_Expr, + ^Type_Assertion, + ^Type_Cast, + ^Auto_Cast, + ^Inline_Asm_Expr, + + ^Proc_Group, + + ^Typeid_Type, + ^Helper_Type, + ^Distinct_Type, + ^Poly_Type, + ^Proc_Type, + ^Pointer_Type, + ^Multi_Pointer_Type, + ^Array_Type, + ^Dynamic_Array_Type, + ^Struct_Type, + ^Union_Type, + ^Enum_Type, + ^Bit_Set_Type, + ^Map_Type, + ^Relative_Type, + ^Matrix_Type, + + ^Bad_Stmt, + ^Empty_Stmt, + ^Expr_Stmt, + ^Tag_Stmt, + ^Assign_Stmt, + ^Block_Stmt, + ^If_Stmt, + ^When_Stmt, + ^Return_Stmt, + ^Defer_Stmt, + ^For_Stmt, + ^Range_Stmt, + ^Inline_Range_Stmt, + ^Case_Clause, + ^Switch_Stmt, + ^Type_Switch_Stmt, + ^Branch_Stmt, + ^Using_Stmt, + + ^Bad_Decl, + ^Value_Decl, + ^Package_Decl, + ^Import_Decl, + ^Foreign_Block_Decl, + ^Foreign_Import_Decl, + + ^Attribute, + ^Field, + ^Field_List, +} + + +Any_Expr :: union { + ^Bad_Expr, + ^Ident, + ^Implicit, + ^Undef, + ^Basic_Lit, + ^Basic_Directive, + ^Ellipsis, + ^Proc_Lit, + ^Comp_Lit, + ^Tag_Expr, + ^Unary_Expr, + ^Binary_Expr, + ^Paren_Expr, + ^Selector_Expr, + ^Implicit_Selector_Expr, + ^Selector_Call_Expr, + ^Index_Expr, + ^Deref_Expr, + ^Slice_Expr, + ^Matrix_Index_Expr, + ^Call_Expr, + ^Field_Value, + ^Ternary_If_Expr, + ^Ternary_When_Expr, + ^Or_Else_Expr, + ^Or_Return_Expr, + ^Type_Assertion, + ^Type_Cast, + ^Auto_Cast, + ^Inline_Asm_Expr, + + ^Proc_Group, + + ^Typeid_Type, + ^Helper_Type, + ^Distinct_Type, + ^Poly_Type, + ^Proc_Type, + ^Pointer_Type, + ^Multi_Pointer_Type, + ^Array_Type, + ^Dynamic_Array_Type, + ^Struct_Type, + ^Union_Type, + ^Enum_Type, + ^Bit_Set_Type, + ^Map_Type, + ^Relative_Type, + ^Matrix_Type, +} + + +Any_Stmt :: union { + ^Bad_Stmt, + ^Empty_Stmt, + ^Expr_Stmt, + ^Tag_Stmt, + ^Assign_Stmt, + ^Block_Stmt, + ^If_Stmt, + ^When_Stmt, + ^Return_Stmt, + ^Defer_Stmt, + ^For_Stmt, + ^Range_Stmt, + ^Inline_Range_Stmt, + ^Case_Clause, + ^Switch_Stmt, + ^Type_Switch_Stmt, + ^Branch_Stmt, + ^Using_Stmt, + + ^Bad_Decl, + ^Value_Decl, + ^Package_Decl, + ^Import_Decl, + ^Foreign_Block_Decl, + ^Foreign_Import_Decl, +} diff --git a/core/odin/ast/clone.odin b/core/odin/ast/clone.odin index 1e3058678..400c064f5 100644 --- a/core/odin/ast/clone.odin +++ b/core/odin/ast/clone.odin @@ -1,16 +1,25 @@ package odin_ast +import "core:intrinsics" import "core:mem" import "core:fmt" +import "core:reflect" import "core:odin/tokenizer" +_ :: intrinsics new :: proc($T: typeid, pos, end: tokenizer.Pos) -> ^T { n, _ := mem.new(T) n.pos = pos n.end = end - n.derived = n^ + n.derived = n base: ^Node = n // dummy check _ = base // "Use" type to make -vet happy + when intrinsics.type_has_field(T, "derived_expr") { + n.derived_expr = n + } + when intrinsics.type_has_field(T, "derived_stmt") { + n.derived_stmt = n + } return n } @@ -59,232 +68,257 @@ clone_node :: proc(node: ^Node) -> ^Node { return nil } - size := size_of(Node) + size := size_of(Node) align := align_of(Node) - ti := type_info_of(node.derived.id) + ti := reflect.union_variant_type_info(node.derived) if ti != nil { - size = ti.size - align = ti.align + elem := ti.variant.(reflect.Type_Info_Pointer).elem + size = elem.size + align = elem.align } - switch in node.derived { - case Package, File: + #partial switch in node.derived { + case ^Package, ^File: panic("Cannot clone this node type") } res := cast(^Node)mem.alloc(size, align) src: rawptr = node if node.derived != nil { - src = node.derived.data + src = (^rawptr)(&node.derived)^ } mem.copy(res, src, size) - res.derived.data = rawptr(res) - res.derived.id = node.derived.id + res_ptr_any: any + res_ptr_any.data = &res + res_ptr_any.id = ti.id - switch r in &res.derived { - case Bad_Expr: - case Ident: - case Implicit: - case Undef: - case Basic_Lit: + reflect.set_union_value(res.derived, res_ptr_any) - case Ellipsis: + res_ptr := reflect.deref(res_ptr_any) + + if de := reflect.struct_field_value_by_name(res_ptr, "derived_expr", true); de != nil { + reflect.set_union_value(de, res_ptr_any) + } + if ds := reflect.struct_field_value_by_name(res_ptr, "derived_stmt", true); ds != nil { + reflect.set_union_value(ds, res_ptr_any) + } + + if res.derived != nil do switch r in res.derived { + case ^Package, ^File: + case ^Bad_Expr: + case ^Ident: + case ^Implicit: + case ^Undef: + case ^Basic_Lit: + case ^Basic_Directive: + case ^Comment_Group: + + case ^Ellipsis: r.expr = clone(r.expr) - case Proc_Lit: + case ^Proc_Lit: r.type = auto_cast clone(r.type) r.body = clone(r.body) - case Comp_Lit: + case ^Comp_Lit: r.type = clone(r.type) r.elems = clone(r.elems) - case Tag_Expr: + case ^Tag_Expr: r.expr = clone(r.expr) - case Unary_Expr: + case ^Unary_Expr: r.expr = clone(r.expr) - case Binary_Expr: + case ^Binary_Expr: r.left = clone(r.left) r.right = clone(r.right) - case Paren_Expr: + case ^Paren_Expr: r.expr = clone(r.expr) - case Selector_Expr: + case ^Selector_Expr: r.expr = clone(r.expr) r.field = auto_cast clone(r.field) - case Implicit_Selector_Expr: + case ^Implicit_Selector_Expr: r.field = auto_cast clone(r.field) - case Selector_Call_Expr: + case ^Selector_Call_Expr: r.expr = clone(r.expr) r.call = auto_cast clone(r.call) - case Index_Expr: + case ^Index_Expr: r.expr = clone(r.expr) r.index = clone(r.index) - case Matrix_Index_Expr: + case ^Matrix_Index_Expr: r.expr = clone(r.expr) r.row_index = clone(r.row_index) r.column_index = clone(r.column_index) - case Deref_Expr: + case ^Deref_Expr: r.expr = clone(r.expr) - case Slice_Expr: + case ^Slice_Expr: r.expr = clone(r.expr) r.low = clone(r.low) r.high = clone(r.high) - case Call_Expr: + case ^Call_Expr: r.expr = clone(r.expr) r.args = clone(r.args) - case Field_Value: + case ^Field_Value: r.field = clone(r.field) r.value = clone(r.value) - case Ternary_If_Expr: + case ^Ternary_If_Expr: r.x = clone(r.x) r.cond = clone(r.cond) r.y = clone(r.y) - case Ternary_When_Expr: + case ^Ternary_When_Expr: r.x = clone(r.x) r.cond = clone(r.cond) r.y = clone(r.y) - case Or_Else_Expr: + case ^Or_Else_Expr: r.x = clone(r.x) r.y = clone(r.y) - case Or_Return_Expr: + case ^Or_Return_Expr: r.expr = clone(r.expr) - case Type_Assertion: + case ^Type_Assertion: r.expr = clone(r.expr) r.type = clone(r.type) - case Type_Cast: + case ^Type_Cast: r.type = clone(r.type) r.expr = clone(r.expr) - case Auto_Cast: + case ^Auto_Cast: r.expr = clone(r.expr) - case Inline_Asm_Expr: + case ^Inline_Asm_Expr: r.param_types = clone(r.param_types) r.return_type = clone(r.return_type) r.constraints_string = clone(r.constraints_string) r.asm_string = clone(r.asm_string) - case Bad_Stmt: + case ^Bad_Stmt: // empty - case Empty_Stmt: + case ^Empty_Stmt: // empty - case Expr_Stmt: + case ^Expr_Stmt: r.expr = clone(r.expr) - case Tag_Stmt: + case ^Tag_Stmt: r.stmt = clone(r.stmt) - case Assign_Stmt: + case ^Assign_Stmt: r.lhs = clone(r.lhs) r.rhs = clone(r.rhs) - case Block_Stmt: + case ^Block_Stmt: r.label = clone(r.label) r.stmts = clone(r.stmts) - case If_Stmt: + case ^If_Stmt: r.label = clone(r.label) r.init = clone(r.init) r.cond = clone(r.cond) r.body = clone(r.body) r.else_stmt = clone(r.else_stmt) - case When_Stmt: + case ^When_Stmt: r.cond = clone(r.cond) r.body = clone(r.body) r.else_stmt = clone(r.else_stmt) - case Return_Stmt: + case ^Return_Stmt: r.results = clone(r.results) - case Defer_Stmt: + case ^Defer_Stmt: r.stmt = clone(r.stmt) - case For_Stmt: + case ^For_Stmt: r.label = clone(r.label) r.init = clone(r.init) r.cond = clone(r.cond) r.post = clone(r.post) r.body = clone(r.body) - case Range_Stmt: + case ^Range_Stmt: r.label = clone(r.label) r.vals = clone(r.vals) r.expr = clone(r.expr) r.body = clone(r.body) - case Case_Clause: + case ^Inline_Range_Stmt: + r.label = clone(r.label) + r.val0 = clone(r.val0) + r.val1 = clone(r.val1) + r.expr = clone(r.expr) + r.body = clone(r.body) + case ^Case_Clause: r.list = clone(r.list) r.body = clone(r.body) - case Switch_Stmt: + case ^Switch_Stmt: r.label = clone(r.label) r.init = clone(r.init) r.cond = clone(r.cond) r.body = clone(r.body) - case Type_Switch_Stmt: + case ^Type_Switch_Stmt: r.label = clone(r.label) r.tag = clone(r.tag) r.expr = clone(r.expr) r.body = clone(r.body) - case Branch_Stmt: + case ^Branch_Stmt: r.label = auto_cast clone(r.label) - case Using_Stmt: + case ^Using_Stmt: r.list = clone(r.list) - case Bad_Decl: - case Value_Decl: + case ^Bad_Decl: + case ^Value_Decl: r.attributes = clone(r.attributes) r.names = clone(r.names) r.type = clone(r.type) r.values = clone(r.values) - case Package_Decl: - case Import_Decl: - case Foreign_Block_Decl: + case ^Package_Decl: + case ^Import_Decl: + case ^Foreign_Block_Decl: r.attributes = clone(r.attributes) r.foreign_library = clone(r.foreign_library) r.body = clone(r.body) - case Foreign_Import_Decl: + case ^Foreign_Import_Decl: r.name = auto_cast clone(r.name) - case Proc_Group: + case ^Proc_Group: r.args = clone(r.args) - case Attribute: + case ^Attribute: r.elems = clone(r.elems) - case Field: + case ^Field: r.names = clone(r.names) r.type = clone(r.type) r.default_value = clone(r.default_value) - case Field_List: + case ^Field_List: r.list = clone(r.list) - case Typeid_Type: + case ^Typeid_Type: r.specialization = clone(r.specialization) - case Helper_Type: + case ^Helper_Type: r.type = clone(r.type) - case Distinct_Type: + case ^Distinct_Type: r.type = clone(r.type) - case Poly_Type: + case ^Poly_Type: r.type = auto_cast clone(r.type) r.specialization = clone(r.specialization) - case Proc_Type: + case ^Proc_Type: r.params = auto_cast clone(r.params) r.results = auto_cast clone(r.results) - case Pointer_Type: + case ^Pointer_Type: r.elem = clone(r.elem) - case Multi_Pointer_Type: + case ^Multi_Pointer_Type: r.elem = clone(r.elem) - case Array_Type: + case ^Array_Type: r.len = clone(r.len) r.elem = clone(r.elem) - case Dynamic_Array_Type: + case ^Dynamic_Array_Type: r.elem = clone(r.elem) - case Struct_Type: + case ^Struct_Type: r.poly_params = auto_cast clone(r.poly_params) r.align = clone(r.align) r.fields = auto_cast clone(r.fields) - case Union_Type: + case ^Union_Type: r.poly_params = auto_cast clone(r.poly_params) r.align = clone(r.align) r.variants = clone(r.variants) - case Enum_Type: + case ^Enum_Type: r.base_type = clone(r.base_type) r.fields = clone(r.fields) - case Bit_Set_Type: + case ^Bit_Set_Type: r.elem = clone(r.elem) r.underlying = clone(r.underlying) - case Map_Type: + case ^Map_Type: r.key = clone(r.key) r.value = clone(r.value) - case Matrix_Type: + case ^Matrix_Type: r.row_count = clone(r.row_count) r.column_count = clone(r.column_count) r.elem = clone(r.elem) + case ^Relative_Type: + r.tag = clone(r.tag) + r.type = clone(r.type) case: - fmt.panicf("Unhandled node kind: %T", r) + fmt.panicf("Unhandled node kind: %v", r) } return res diff --git a/core/odin/ast/walk.odin b/core/odin/ast/walk.odin index d0d17cc9e..b4eaf8140 100644 --- a/core/odin/ast/walk.odin +++ b/core/odin/ast/walk.odin @@ -52,71 +52,74 @@ walk :: proc(v: ^Visitor, node: ^Node) { } } - v := v + if v == nil || node == nil { + return + } + if v = v->visit(node); v == nil { return } switch n in &node.derived { - case File: + case ^File: if n.docs != nil { walk(v, n.docs) } walk_stmt_list(v, n.decls[:]) - case Package: + case ^Package: for _, f in n.files { walk(v, f) } - case Comment_Group: + case ^Comment_Group: // empty - case Bad_Expr: - case Ident: - case Implicit: - case Undef: - case Basic_Lit: - case Basic_Directive: - case Ellipsis: + case ^Bad_Expr: + case ^Ident: + case ^Implicit: + case ^Undef: + case ^Basic_Lit: + case ^Basic_Directive: + case ^Ellipsis: if n.expr != nil { walk(v, n.expr) } - case Proc_Lit: + case ^Proc_Lit: walk(v, n.type) walk(v, n.body) walk_expr_list(v, n.where_clauses) - case Comp_Lit: + case ^Comp_Lit: if n.type != nil { walk(v, n.type) } walk_expr_list(v, n.elems) - case Tag_Expr: + case ^Tag_Expr: walk(v, n.expr) - case Unary_Expr: + case ^Unary_Expr: walk(v, n.expr) - case Binary_Expr: + case ^Binary_Expr: walk(v, n.left) walk(v, n.right) - case Paren_Expr: + case ^Paren_Expr: walk(v, n.expr) - case Selector_Expr: + case ^Selector_Expr: walk(v, n.expr) walk(v, n.field) - case Implicit_Selector_Expr: + case ^Implicit_Selector_Expr: walk(v, n.field) - case Selector_Call_Expr: + case ^Selector_Call_Expr: walk(v, n.expr) walk(v, n.call) - case Index_Expr: + case ^Index_Expr: walk(v, n.expr) walk(v, n.index) - case Matrix_Index_Expr: + case ^Matrix_Index_Expr: walk(v, n.expr) walk(v, n.row_index) walk(v, n.column_index) - case Deref_Expr: + case ^Deref_Expr: walk(v, n.expr) - case Slice_Expr: + case ^Slice_Expr: walk(v, n.expr) if n.low != nil { walk(v, n.low) @@ -124,57 +127,57 @@ walk :: proc(v: ^Visitor, node: ^Node) { if n.high != nil { walk(v, n.high) } - case Call_Expr: + case ^Call_Expr: walk(v, n.expr) walk_expr_list(v, n.args) - case Field_Value: + case ^Field_Value: walk(v, n.field) walk(v, n.value) - case Ternary_If_Expr: + case ^Ternary_If_Expr: walk(v, n.x) walk(v, n.cond) walk(v, n.y) - case Ternary_When_Expr: + case ^Ternary_When_Expr: walk(v, n.x) walk(v, n.cond) walk(v, n.y) - case Or_Else_Expr: + case ^Or_Else_Expr: walk(v, n.x) walk(v, n.y) - case Or_Return_Expr: + case ^Or_Return_Expr: walk(v, n.expr) - case Type_Assertion: + case ^Type_Assertion: walk(v, n.expr) if n.type != nil { walk(v, n.type) } - case Type_Cast: + case ^Type_Cast: walk(v, n.type) walk(v, n.expr) - case Auto_Cast: + case ^Auto_Cast: walk(v, n.expr) - case Inline_Asm_Expr: + case ^Inline_Asm_Expr: walk_expr_list(v, n.param_types) walk(v, n.return_type) walk(v, n.constraints_string) walk(v, n.asm_string) - case Bad_Stmt: - case Empty_Stmt: - case Expr_Stmt: + case ^Bad_Stmt: + case ^Empty_Stmt: + case ^Expr_Stmt: walk(v, n.expr) - case Tag_Stmt: + case ^Tag_Stmt: walk(v, n.stmt) - case Assign_Stmt: + case ^Assign_Stmt: walk_expr_list(v, n.lhs) walk_expr_list(v, n.rhs) - case Block_Stmt: + case ^Block_Stmt: if n.label != nil { walk(v, n.label) } walk_stmt_list(v, n.stmts) - case If_Stmt: + case ^If_Stmt: if n.label != nil { walk(v, n.label) } @@ -186,17 +189,17 @@ walk :: proc(v: ^Visitor, node: ^Node) { if n.else_stmt != nil { walk(v, n.else_stmt) } - case When_Stmt: + case ^When_Stmt: walk(v, n.cond) walk(v, n.body) if n.else_stmt != nil { walk(v, n.else_stmt) } - case Return_Stmt: + case ^Return_Stmt: walk_expr_list(v, n.results) - case Defer_Stmt: + case ^Defer_Stmt: walk(v, n.stmt) - case For_Stmt: + case ^For_Stmt: if n.label != nil { walk(v, n.label) } @@ -210,7 +213,7 @@ walk :: proc(v: ^Visitor, node: ^Node) { walk(v, n.post) } walk(v, n.body) - case Range_Stmt: + case ^Range_Stmt: if n.label != nil { walk(v, n.label) } @@ -221,7 +224,7 @@ walk :: proc(v: ^Visitor, node: ^Node) { } walk(v, n.expr) walk(v, n.body) - case Inline_Range_Stmt: + case ^Inline_Range_Stmt: if n.label != nil { walk(v, n.label) } @@ -233,10 +236,10 @@ walk :: proc(v: ^Visitor, node: ^Node) { } walk(v, n.expr) walk(v, n.body) - case Case_Clause: + case ^Case_Clause: walk_expr_list(v, n.list) walk_stmt_list(v, n.body) - case Switch_Stmt: + case ^Switch_Stmt: if n.label != nil { walk(v, n.label) } @@ -247,7 +250,7 @@ walk :: proc(v: ^Visitor, node: ^Node) { walk(v, n.cond) } walk(v, n.body) - case Type_Switch_Stmt: + case ^Type_Switch_Stmt: if n.label != nil { walk(v, n.label) } @@ -258,16 +261,16 @@ walk :: proc(v: ^Visitor, node: ^Node) { walk(v, n.expr) } walk(v, n.body) - case Branch_Stmt: + case ^Branch_Stmt: if n.label != nil { walk(v, n.label) } - case Using_Stmt: + case ^Using_Stmt: walk_expr_list(v, n.list) - case Bad_Decl: - case Value_Decl: + case ^Bad_Decl: + case ^Value_Decl: if n.docs != nil { walk(v, n.docs) } @@ -280,21 +283,21 @@ walk :: proc(v: ^Visitor, node: ^Node) { if n.comment != nil { walk(v, n.comment) } - case Package_Decl: + case ^Package_Decl: if n.docs != nil { walk(v, n.docs) } if n.comment != nil { walk(v, n.comment) } - case Import_Decl: + case ^Import_Decl: if n.docs != nil { walk(v, n.docs) } if n.comment != nil { walk(v, n.comment) } - case Foreign_Block_Decl: + case ^Foreign_Block_Decl: if n.docs != nil { walk(v, n.docs) } @@ -303,7 +306,7 @@ walk :: proc(v: ^Visitor, node: ^Node) { walk(v, n.foreign_library) } walk(v, n.body) - case Foreign_Import_Decl: + case ^Foreign_Import_Decl: if n.docs != nil { walk(v, n.docs) } @@ -313,11 +316,11 @@ walk :: proc(v: ^Visitor, node: ^Node) { walk(v, n.comment) } - case Proc_Group: + case ^Proc_Group: walk_expr_list(v, n.args) - case Attribute: + case ^Attribute: walk_expr_list(v, n.elems) - case Field: + case ^Field: if n.docs != nil { walk(v, n.docs) } @@ -331,31 +334,31 @@ walk :: proc(v: ^Visitor, node: ^Node) { if n.comment != nil { walk(v, n.comment) } - case Field_List: + case ^Field_List: for x in n.list { walk(v, x) } - case Typeid_Type: + case ^Typeid_Type: if n.specialization != nil { walk(v, n.specialization) } - case Helper_Type: + case ^Helper_Type: walk(v, n.type) - case Distinct_Type: + case ^Distinct_Type: walk(v, n.type) - case Poly_Type: + case ^Poly_Type: walk(v, n.type) if n.specialization != nil { walk(v, n.specialization) } - case Proc_Type: + case ^Proc_Type: walk(v, n.params) walk(v, n.results) - case Pointer_Type: + case ^Pointer_Type: walk(v, n.elem) - case Multi_Pointer_Type: + case ^Multi_Pointer_Type: walk(v, n.elem) - case Array_Type: + case ^Array_Type: if n.tag != nil { walk(v, n.tag) } @@ -363,12 +366,12 @@ walk :: proc(v: ^Visitor, node: ^Node) { walk(v, n.len) } walk(v, n.elem) - case Dynamic_Array_Type: + case ^Dynamic_Array_Type: if n.tag != nil { walk(v, n.tag) } walk(v, n.elem) - case Struct_Type: + case ^Struct_Type: if n.poly_params != nil { walk(v, n.poly_params) } @@ -377,7 +380,7 @@ walk :: proc(v: ^Visitor, node: ^Node) { } walk_expr_list(v, n.where_clauses) walk(v, n.fields) - case Union_Type: + case ^Union_Type: if n.poly_params != nil { walk(v, n.poly_params) } @@ -386,23 +389,23 @@ walk :: proc(v: ^Visitor, node: ^Node) { } walk_expr_list(v, n.where_clauses) walk_expr_list(v, n.variants) - case Enum_Type: + case ^Enum_Type: if n.base_type != nil { walk(v, n.base_type) } walk_expr_list(v, n.fields) - case Bit_Set_Type: + case ^Bit_Set_Type: walk(v, n.elem) if n.underlying != nil { walk(v, n.underlying) } - case Map_Type: + case ^Map_Type: walk(v, n.key) walk(v, n.value) - case Relative_Type: + case ^Relative_Type: walk(v, n.tag) walk(v, n.type) - case Matrix_Type: + case ^Matrix_Type: walk(v, n.row_count) walk(v, n.column_count) walk(v, n.elem) diff --git a/core/odin/doc-format/doc_format.odin b/core/odin/doc-format/doc_format.odin index 59eafdc09..62682004d 100644 --- a/core/odin/doc-format/doc_format.odin +++ b/core/odin/doc-format/doc_format.odin @@ -11,7 +11,7 @@ String :: distinct Array(byte) Version_Type_Major :: 0 Version_Type_Minor :: 2 -Version_Type_Patch :: 3 +Version_Type_Patch :: 4 Version_Type :: struct { major, minor, patch: u8, @@ -77,9 +77,15 @@ Pkg :: struct { flags: Pkg_Flags, docs: String, files: Array(File_Index), - entities: Array(Entity_Index), + entries: Array(Scope_Entry), } +Scope_Entry :: struct { + name: String, + entity: Entity_Index, +} + + Entity_Kind :: enum u32le { Invalid = 0, Constant = 1, @@ -89,6 +95,7 @@ Entity_Kind :: enum u32le { Proc_Group = 5, Import_Name = 6, Library_Name = 7, + Builtin = 8, } Entity_Flag :: enum u32le { @@ -105,6 +112,9 @@ Entity_Flag :: enum u32le { Type_Alias = 20, + Builtin_Pkg_Builtin = 30, + Builtin_Pkg_Intrinsics = 31, + Var_Thread_Local = 40, Var_Static = 41, diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index e8c2c848d..52ecb4781 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -183,6 +183,7 @@ parse_file :: proc(p: ^Parser, file: ^ast.File) -> bool { pd.name = pkg_name.text pd.comment = p.line_comment p.file.pkg_decl = pd + p.file.docs = docs expect_semicolon(p, pd) @@ -195,10 +196,10 @@ parse_file :: proc(p: ^Parser, file: ^ast.File) -> bool { for p.curr_tok.kind != .EOF { stmt := parse_stmt(p) if stmt != nil { - if _, ok := stmt.derived.(ast.Empty_Stmt); !ok { + if _, ok := stmt.derived.(^ast.Empty_Stmt); !ok { append(&p.file.decls, stmt) - if es, es_ok := stmt.derived.(ast.Expr_Stmt); es_ok && es.expr != nil { - if _, pl_ok := es.expr.derived.(ast.Proc_Lit); pl_ok { + if es, es_ok := stmt.derived.(^ast.Expr_Stmt); es_ok && es.expr != nil { + if _, pl_ok := es.expr.derived.(^ast.Proc_Lit); pl_ok { error(p, stmt.pos, "procedure literal evaluated but not used") } } @@ -428,9 +429,21 @@ expect_closing_brace_of_field_list :: proc(p: ^Parser) -> tokenizer.Token { str := tokenizer.token_to_string(token) error(p, end_of_line_pos(p, p.prev_tok), "expected a comma, got %s", str) } - return expect_token(p, .Close_Brace) + expect_brace := expect_token(p, .Close_Brace) + + if expect_brace.kind != .Close_Brace { + for p.curr_tok.kind != .Close_Brace && p.curr_tok.kind != .EOF && !is_non_inserted_semicolon(p.curr_tok) { + advance_token(p) + } + return p.curr_tok + } + + return expect_brace } +is_non_inserted_semicolon :: proc(tok: tokenizer.Token) -> bool { + return tok.kind == .Semicolon && tok.text != "\n" +} is_blank_ident :: proc{ is_blank_ident_string, @@ -447,7 +460,7 @@ is_blank_ident_token :: proc(tok: tokenizer.Token) -> bool { return false } is_blank_ident_node :: proc(node: ^ast.Node) -> bool { - if ident, ok := node.derived.(ast.Ident); ok { + if ident, ok := node.derived.(^ast.Ident); ok { return is_blank_ident(ident.name) } return true @@ -490,34 +503,34 @@ is_semicolon_optional_for_node :: proc(p: ^Parser, node: ^ast.Node) -> bool { return true } - switch n in node.derived { - case ast.Empty_Stmt, ast.Block_Stmt: + #partial switch n in node.derived { + case ^ast.Empty_Stmt, ^ast.Block_Stmt: return true - case ast.If_Stmt, ast.When_Stmt, - ast.For_Stmt, ast.Range_Stmt, ast.Inline_Range_Stmt, - ast.Switch_Stmt, ast.Type_Switch_Stmt: + case ^ast.If_Stmt, ^ast.When_Stmt, + ^ast.For_Stmt, ^ast.Range_Stmt, ^ast.Inline_Range_Stmt, + ^ast.Switch_Stmt, ^ast.Type_Switch_Stmt: return true - case ast.Helper_Type: + case ^ast.Helper_Type: return is_semicolon_optional_for_node(p, n.type) - case ast.Distinct_Type: + case ^ast.Distinct_Type: return is_semicolon_optional_for_node(p, n.type) - case ast.Pointer_Type: + case ^ast.Pointer_Type: return is_semicolon_optional_for_node(p, n.elem) - case ast.Struct_Type, ast.Union_Type, ast.Enum_Type: + case ^ast.Struct_Type, ^ast.Union_Type, ^ast.Enum_Type: // Require semicolon within a procedure body return p.curr_proc == nil - case ast.Proc_Lit: + case ^ast.Proc_Lit: return true - case ast.Package_Decl, ast.Import_Decl, ast.Foreign_Import_Decl: + case ^ast.Package_Decl, ^ast.Import_Decl, ^ast.Foreign_Import_Decl: return true - case ast.Foreign_Block_Decl: + case ^ast.Foreign_Block_Decl: return is_semicolon_optional_for_node(p, n.body) - case ast.Value_Decl: + case ^ast.Value_Decl: if n.is_mutable { return false } @@ -629,10 +642,10 @@ parse_stmt_list :: proc(p: ^Parser) -> []^ast.Stmt { p.curr_tok.kind != .EOF { stmt := parse_stmt(p) if stmt != nil { - if _, ok := stmt.derived.(ast.Empty_Stmt); !ok { + if _, ok := stmt.derived.(^ast.Empty_Stmt); !ok { append(&list, stmt) - if es, es_ok := stmt.derived.(ast.Expr_Stmt); es_ok && es.expr != nil { - if _, pl_ok := es.expr.derived.(ast.Proc_Lit); pl_ok { + if es, es_ok := stmt.derived.(^ast.Expr_Stmt); es_ok && es.expr != nil { + if _, pl_ok := es.expr.derived.(^ast.Proc_Lit); pl_ok { error(p, stmt.pos, "procedure literal evaluated but not used") } } @@ -710,7 +723,7 @@ convert_stmt_to_expr :: proc(p: ^Parser, stmt: ^ast.Stmt, kind: string) -> ^ast. if stmt == nil { return nil } - if es, ok := stmt.derived.(ast.Expr_Stmt); ok { + if es, ok := stmt.derived.(^ast.Expr_Stmt); ok { return es.expr } error(p, stmt.pos, "expected %s, found a simple statement", kind) @@ -852,7 +865,7 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt { if p.curr_tok.kind != .Semicolon { cond = parse_simple_stmt(p, {Stmt_Allow_Flag.In}) - if as, ok := cond.derived.(ast.Assign_Stmt); ok && as.op.kind == .In { + if as, ok := cond.derived.(^ast.Assign_Stmt); ok && as.op.kind == .In { is_range = true } } @@ -894,7 +907,7 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt { if is_range { - assign_stmt := cond.derived.(ast.Assign_Stmt) + assign_stmt := cond.derived.(^ast.Assign_Stmt) vals := assign_stmt.lhs[:] rhs: ^ast.Expr @@ -975,7 +988,7 @@ parse_switch_stmt :: proc(p: ^Parser) -> ^ast.Stmt { tag = as } else { tag = parse_simple_stmt(p, {Stmt_Allow_Flag.In}) - if as, ok := tag.derived.(ast.Assign_Stmt); ok && as.op.kind == .In { + if as, ok := tag.derived.(^ast.Assign_Stmt); ok && as.op.kind == .In { is_type_switch = true } else if parse_control_statement_semicolon_separator(p) { init = tag @@ -1062,14 +1075,14 @@ parse_attribute :: proc(p: ^Parser, tok: tokenizer.Token, open_kind, close_kind: skip_possible_newline(p) decl := parse_stmt(p) - switch d in &decl.derived { - case ast.Value_Decl: + #partial switch d in decl.derived_stmt { + case ^ast.Value_Decl: if d.docs == nil { d.docs = docs } append(&d.attributes, attribute) - case ast.Foreign_Block_Decl: + case ^ast.Foreign_Block_Decl: if d.docs == nil { d.docs = docs } append(&d.attributes, attribute) - case ast.Foreign_Import_Decl: + case ^ast.Foreign_Import_Decl: if d.docs == nil { d.docs = docs } append(&d.attributes, attribute) case: @@ -1083,11 +1096,11 @@ parse_attribute :: proc(p: ^Parser, tok: tokenizer.Token, open_kind, close_kind: parse_foreign_block_decl :: proc(p: ^Parser) -> ^ast.Stmt { decl := parse_stmt(p) - switch in decl.derived { - case ast.Empty_Stmt, ast.Bad_Stmt, ast.Bad_Decl: + #partial switch in decl.derived_stmt { + case ^ast.Empty_Stmt, ^ast.Bad_Stmt, ^ast.Bad_Decl: // Ignore return nil - case ast.When_Stmt, ast.Value_Decl: + case ^ast.When_Stmt, ^ast.Value_Decl: return decl } @@ -1291,13 +1304,13 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt { case .Defer: tok := advance_token(p) stmt := parse_stmt(p) - switch s in stmt.derived { - case ast.Empty_Stmt: + #partial switch s in stmt.derived_stmt { + case ^ast.Empty_Stmt: error(p, s.pos, "empty statement after defer (e.g. ';')") - case ast.Defer_Stmt: + case ^ast.Defer_Stmt: error(p, s.pos, "you cannot defer a defer statement") stmt = s.stmt - case ast.Return_Stmt: + case ^ast.Return_Stmt: error(p, s.pos, "you cannot defer a return statement") } ds := ast.new(ast.Defer_Stmt, tok.pos, stmt.end) @@ -1312,7 +1325,7 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt { } results: [dynamic]^ast.Expr - for p.curr_tok.kind != .Semicolon { + for p.curr_tok.kind != .Semicolon && p.curr_tok.kind != .Close_Brace { result := parse_expr(p, false) append(&results, result) if p.curr_tok.kind != .Comma || @@ -1369,8 +1382,8 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt { expect_token_after(p, .Colon, "identifier list") decl := parse_value_decl(p, list, docs) if decl != nil { - switch d in &decl.derived { - case ast.Value_Decl: + #partial switch d in decl.derived_stmt { + case ^ast.Value_Decl: d.is_using = true return decl } @@ -1401,9 +1414,9 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt { return stmt case "partial": stmt := parse_stmt(p) - switch s in &stmt.derived { - case ast.Switch_Stmt: s.partial = true - case ast.Type_Switch_Stmt: s.partial = true + #partial switch s in stmt.derived_stmt { + case ^ast.Switch_Stmt: s.partial = true + case ^ast.Type_Switch_Stmt: s.partial = true case: error(p, stmt.pos, "#partial can only be applied to a switch statement") } return stmt @@ -1548,11 +1561,11 @@ parse_body :: proc(p: ^Parser) -> ^ast.Block_Stmt { } convert_stmt_to_body :: proc(p: ^Parser, stmt: ^ast.Stmt) -> ^ast.Stmt { - switch s in stmt.derived { - case ast.Block_Stmt: + #partial switch s in stmt.derived_stmt { + case ^ast.Block_Stmt: error(p, stmt.pos, "expected a normal statement rather than a block statement") return stmt - case ast.Empty_Stmt: + case ^ast.Empty_Stmt: error(p, stmt.pos, "expected a non-empty statement") } @@ -1629,10 +1642,10 @@ convert_to_ident_list :: proc(p: ^Parser, list: []Expr_And_Flags, ignore_flags, id: ^ast.Expr = ident.expr - switch n in ident.expr.derived { - case ast.Ident: - case ast.Bad_Expr: - case ast.Poly_Type: + #partial switch n in ident.expr.derived_expr { + case ^ast.Ident: + case ^ast.Bad_Expr: + case ^ast.Poly_Type: if allow_poly_names { if n.specialization == nil { break @@ -1794,21 +1807,21 @@ check_procedure_name_list :: proc(p: ^Parser, names: []^ast.Expr) -> bool { return false } - _, first_is_polymorphic := names[0].derived.(ast.Poly_Type) + _, first_is_polymorphic := names[0].derived.(^ast.Poly_Type) any_polymorphic_names := first_is_polymorphic for i := 1; i < len(names); i += 1 { name := names[i] if first_is_polymorphic { - if _, ok := name.derived.(ast.Poly_Type); ok { + if _, ok := name.derived.(^ast.Poly_Type); ok { any_polymorphic_names = true } else { error(p, name.pos, "mixture of polymorphic and non-polymorphic identifiers") return any_polymorphic_names } } else { - if _, ok := name.derived.(ast.Poly_Type); ok { + if _, ok := name.derived.(^ast.Poly_Type); ok { any_polymorphic_names = true error(p, name.pos, "mixture of polymorphic and non-polymorphic identifiers") return any_polymorphic_names @@ -1873,7 +1886,7 @@ parse_field_list :: proc(p: ^Parser, follow: tokenizer.Token_Kind, allowed_flags if type == nil { return false } - _, ok := type.derived.(ast.Ellipsis) + _, ok := type.derived.(^ast.Ellipsis) return ok } @@ -1891,7 +1904,7 @@ parse_field_list :: proc(p: ^Parser, follow: tokenizer.Token_Kind, allowed_flags type = parse_var_type(p, allowed_flags) tt := ast.unparen_expr(type) if is_signature && !any_polymorphic_names { - if ti, ok := tt.derived.(ast.Typeid_Type); ok && ti.specialization != nil { + if ti, ok := tt.derived.(^ast.Typeid_Type); ok && ti.specialization != nil { error(p, tt.pos, "specialization of typeid is not allowed without polymorphic names") } } @@ -1967,7 +1980,7 @@ parse_field_list :: proc(p: ^Parser, follow: tokenizer.Token_Kind, allowed_flags p.curr_tok.kind != .EOF { prefix_flags := parse_field_prefixes(p) param := parse_var_type(p, allowed_flags & {.Typeid_Token, .Ellipsis}) - if _, ok := param.derived.(ast.Ellipsis); ok { + if _, ok := param.derived.(^ast.Ellipsis); ok { if seen_ellipsis { error(p, param.pos, "extra variadic parameter after ellipsis") } @@ -1994,8 +2007,8 @@ parse_field_list :: proc(p: ^Parser, follow: tokenizer.Token_Kind, allowed_flags names := make([]^ast.Expr, 1) names[0] = ast.new(ast.Ident, tok.pos, end_pos(tok)) - switch ident in &names[0].derived { - case ast.Ident: + #partial switch ident in names[0].derived_expr { + case ^ast.Ident: ident.name = tok.text case: unreachable() @@ -2125,12 +2138,12 @@ parse_proc_type :: proc(p: ^Parser, tok: tokenizer.Token) -> ^ast.Proc_Type { loop: for param in params.list { if param.type != nil { - if _, ok := param.type.derived.(ast.Poly_Type); ok { + if _, ok := param.type.derived.(^ast.Poly_Type); ok { is_generic = true break loop } for name in param.names { - if _, ok := name.derived.(ast.Poly_Type); ok { + if _, ok := name.derived.(^ast.Poly_Type); ok { is_generic = true break loop } @@ -2167,13 +2180,13 @@ parse_inlining_operand :: proc(p: ^Parser, lhs: bool, tok: tokenizer.Token) -> ^ } } - switch e in &ast.unparen_expr(expr).derived { - case ast.Proc_Lit: + #partial switch e in ast.unparen_expr(expr).derived_expr { + case ^ast.Proc_Lit: if e.inlining != .None && e.inlining != pi { error(p, expr.pos, "both 'inline' and 'no_inline' cannot be applied to a procedure literal") } e.inlining = pi - case ast.Call_Expr: + case ^ast.Call_Expr: if e.inlining != .None && e.inlining != pi { error(p, expr.pos, "both 'inline' and 'no_inline' cannot be applied to a procedure call") } @@ -2264,22 +2277,40 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { bd.name = name.text original_type := parse_type(p) type := ast.unparen_expr(original_type) - switch t in &type.derived { - case ast.Array_Type: t.tag = bd - case ast.Dynamic_Array_Type: t.tag = bd + #partial switch t in type.derived_expr { + case ^ast.Array_Type: t.tag = bd + case ^ast.Dynamic_Array_Type: t.tag = bd case: error(p, original_type.pos, "expected an array type after #%s", name.text) } return original_type case "partial": + tag := ast.new(ast.Basic_Directive, tok.pos, end_pos(name)) + tag.tok = tok + tag.name = name.text + original_expr := parse_expr(p, lhs) + expr := ast.unparen_expr(original_expr) + #partial switch t in expr.derived_expr { + case ^ast.Comp_Lit: + t.tag = tag + case ^ast.Array_Type: + t.tag = tag + error(p, tok.pos, "#%s has been replaced with #sparse for non-contiguous enumerated array types", name.text) + case: + error(p, tok.pos, "expected a compound literal after #%s", name.text) + + } + return original_expr + + case "sparse": tag := ast.new(ast.Basic_Directive, tok.pos, end_pos(name)) tag.tok = tok tag.name = name.text original_type := parse_type(p) type := ast.unparen_expr(original_type) - switch t in &type.derived { - case ast.Array_Type: + #partial switch t in type.derived_expr { + case ^ast.Array_Type: t.tag = tag case: error(p, tok.pos, "expected an enumerated array type after #%s", name.text) @@ -2319,7 +2350,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { return rt case "force_inline", "force_no_inline": - return parse_inlining_operand(p, lhs, tok) + return parse_inlining_operand(p, lhs, name) case: expr := parse_expr(p, lhs) te := ast.new(ast.Tag_Expr, tok.pos, expr.pos) @@ -2600,8 +2631,9 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { tok := expect_token(p, .Union) poly_params: ^ast.Field_List align: ^ast.Expr - is_maybe: bool - is_no_nil: bool + is_maybe: bool + is_no_nil: bool + is_shared_nil: bool if allow_token(p, .Open_Paren) { param_count: int @@ -2633,12 +2665,34 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { error(p, tag.pos, "duplicate union tag '#%s'", tag.text) } is_no_nil = true + case "shared_nil": + if is_shared_nil { + error(p, tag.pos, "duplicate union tag '#%s'", tag.text) + } + is_shared_nil = true case: error(p, tag.pos, "invalid union tag '#%s", tag.text) } } p.expr_level = prev_level + if is_no_nil && is_maybe { + error(p, p.curr_tok.pos, "#maybe and #no_nil cannot be applied together") + } + if is_no_nil && is_shared_nil { + error(p, p.curr_tok.pos, "#shared_nil and #no_nil cannot be applied together") + } + if is_shared_nil && is_maybe { + error(p, p.curr_tok.pos, "#maybe and #shared_nil cannot be applied together") + } + + union_kind := ast.Union_Type_Kind.Normal + switch { + case is_maybe: union_kind = .maybe + case is_no_nil: union_kind = .no_nil + case is_shared_nil: union_kind = .shared_nil + } + where_token: tokenizer.Token where_clauses: []^ast.Expr @@ -2659,7 +2713,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { variants: [dynamic]^ast.Expr for p.curr_tok.kind != .Close_Brace && p.curr_tok.kind != .EOF { type := parse_type(p) - if _, ok := type.derived.(ast.Bad_Expr); !ok { + if _, ok := type.derived.(^ast.Bad_Expr); !ok { append(&variants, type) } if !allow_token(p, .Comma) { @@ -2669,14 +2723,15 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { close := expect_closing_brace_of_field_list(p) + + ut := ast.new(ast.Union_Type, tok.pos, end_pos(close)) ut.poly_params = poly_params ut.variants = variants[:] ut.align = align ut.where_token = where_token ut.where_clauses = where_clauses - ut.is_maybe = is_maybe - ut.is_no_nil = is_no_nil + ut.kind = union_kind return ut @@ -2834,19 +2889,19 @@ is_literal_type :: proc(expr: ^ast.Expr) -> bool { if val == nil { return false } - switch _ in val.derived { - case ast.Bad_Expr, - ast.Ident, - ast.Selector_Expr, - ast.Array_Type, - ast.Struct_Type, - ast.Union_Type, - ast.Enum_Type, - ast.Dynamic_Array_Type, - ast.Map_Type, - ast.Bit_Set_Type, - ast.Matrix_Type, - ast.Call_Expr: + #partial switch _ in val.derived_expr { + case ^ast.Bad_Expr, + ^ast.Ident, + ^ast.Selector_Expr, + ^ast.Array_Type, + ^ast.Struct_Type, + ^ast.Union_Type, + ^ast.Enum_Type, + ^ast.Dynamic_Array_Type, + ^ast.Map_Type, + ^ast.Bit_Set_Type, + ^ast.Matrix_Type, + ^ast.Call_Expr: return true } return false @@ -2968,7 +3023,7 @@ parse_call_expr :: proc(p: ^Parser, operand: ^ast.Expr) -> ^ast.Expr { ce.close = close.pos o := ast.unparen_expr(operand) - if se, ok := o.derived.(ast.Selector_Expr); ok && se.op.kind == .Arrow_Right { + if se, ok := o.derived.(^ast.Selector_Expr); ok && se.op.kind == .Arrow_Right { sce := ast.new(ast.Selector_Call_Expr, ce.pos, ce.end) sce.expr = o sce.call = ce @@ -3398,13 +3453,13 @@ parse_simple_stmt :: proc(p: ^Parser, flags: Stmt_Allow_Flags) -> ^ast.Stmt { stmt := parse_stmt(p) if stmt != nil { - switch n in &stmt.derived { - case ast.Block_Stmt: n.label = label - case ast.If_Stmt: n.label = label - case ast.For_Stmt: n.label = label - case ast.Switch_Stmt: n.label = label - case ast.Type_Switch_Stmt: n.label = label - case ast.Range_Stmt: n.label = label + #partial switch n in stmt.derived_stmt { + case ^ast.Block_Stmt: n.label = label + case ^ast.If_Stmt: n.label = label + case ^ast.For_Stmt: n.label = label + case ^ast.Switch_Stmt: n.label = label + case ^ast.Type_Switch_Stmt: n.label = label + case ^ast.Range_Stmt: n.label = label } } diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index abda44fa2..63a3b543d 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -151,7 +151,7 @@ print :: proc(p: ^Printer, file: ^ast.File) -> string { fix_lines(p) - builder := strings.make_builder(0, mem.megabytes(5), p.allocator) + builder := strings.builder_make(0, 5 * mem.Megabyte, p.allocator) last_line := 0 diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin index 023583bde..66166aa81 100644 --- a/core/odin/printer/visit.odin +++ b/core/odin/printer/visit.odin @@ -71,7 +71,7 @@ push_comment :: proc(p: ^Printer, comment: tokenizer.Token) -> int { return 0 } else { - builder := strings.make_builder(context.temp_allocator) + builder := strings.builder_make(context.temp_allocator) c_len := len(comment.text) trim_space := true @@ -90,12 +90,12 @@ push_comment :: proc(p: ^Printer, comment: tokenizer.Token) -> int { continue case c == '\r' && comment.text[min(c_len - 1, i + 1)] == '\n': append(&multilines, strings.to_string(builder)) - builder = strings.make_builder(context.temp_allocator) + builder = strings.builder_make(context.temp_allocator) trim_space = true i += 1 case c == '\n': append(&multilines, strings.to_string(builder)) - builder = strings.make_builder(context.temp_allocator) + builder = strings.builder_make(context.temp_allocator) trim_space = true case c == '/' && comment.text[min(c_len - 1, i + 1)] == '*': strings.write_string(&builder, "/*") @@ -342,16 +342,16 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) { return } - switch v in &decl.derived { - case Expr_Stmt: + #partial switch v in decl.derived_stmt { + case ^Expr_Stmt: move_line(p, decl.pos) visit_expr(p, v.expr) if p.config.semicolons { push_generic_token(p, .Semicolon, 0) } - case When_Stmt: + case ^When_Stmt: visit_stmt(p, cast(^Stmt)decl) - case Foreign_Import_Decl: + case ^Foreign_Import_Decl: if len(v.attributes) > 0 { sort.sort(sort_attribute(&v.attributes)) move_line(p, v.attributes[0].pos) @@ -370,7 +370,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) { for path in v.fullpaths { push_ident_token(p, path, 0) } - case Foreign_Block_Decl: + case ^Foreign_Block_Decl: if len(v.attributes) > 0 { sort.sort(sort_attribute(&v.attributes)) move_line(p, v.attributes[0].pos) @@ -383,7 +383,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) { visit_expr(p, v.foreign_library) visit_stmt(p, v.body) - case Import_Decl: + case ^Import_Decl: move_line(p, decl.pos) if v.name.text != "" { @@ -395,7 +395,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) { push_ident_token(p, v.fullpath, 1) } - case Value_Decl: + case ^Value_Decl: if len(v.attributes) > 0 { sort.sort(sort_attribute(&v.attributes)) move_line(p, v.attributes[0].pos) @@ -446,10 +446,10 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) { add_semicolon := true for value in v.values { - switch a in value.derived { - case Union_Type, Enum_Type, Struct_Type: + #partial switch a in value.derived { + case ^Union_Type, ^Enum_Type, ^Struct_Type: add_semicolon = false || called_in_stmt - case Proc_Lit: + case ^Proc_Lit: add_semicolon = false } } @@ -516,23 +516,34 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener return } - switch v in stmt.derived { - case Import_Decl: - visit_decl(p, cast(^Decl)stmt, true) - return - case Value_Decl: - visit_decl(p, cast(^Decl)stmt, true) - return - case Foreign_Import_Decl: - visit_decl(p, cast(^Decl)stmt, true) - return - case Foreign_Block_Decl: - visit_decl(p, cast(^Decl)stmt, true) - return - } - switch v in stmt.derived { - case Using_Stmt: + switch v in stmt.derived_stmt { + case ^Bad_Stmt: + case ^Bad_Decl: + case ^Package_Decl: + + case ^Empty_Stmt: + push_generic_token(p, .Semicolon, 0) + case ^Tag_Stmt: + push_generic_token(p, .Hash, 1) + push_generic_token(p, v.op.kind, 1, v.op.text) + visit_stmt(p, v.stmt) + + + case ^Import_Decl: + visit_decl(p, cast(^Decl)stmt, true) + return + case ^Value_Decl: + visit_decl(p, cast(^Decl)stmt, true) + return + case ^Foreign_Import_Decl: + visit_decl(p, cast(^Decl)stmt, true) + return + case ^Foreign_Block_Decl: + visit_decl(p, cast(^Decl)stmt, true) + return + + case ^Using_Stmt: move_line(p, v.pos) push_generic_token(p, .Using, 1) @@ -542,7 +553,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener if p.config.semicolons { push_generic_token(p, .Semicolon, 0) } - case Block_Stmt: + case ^Block_Stmt: move_line(p, v.pos) if v.pos.line == v.end.line { @@ -572,7 +583,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener visit_end_brace(p, v.end) } } - case If_Stmt: + case ^If_Stmt: move_line(p, v.pos) if v.label != nil { @@ -595,7 +606,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener uses_do := false - if check_stmt, ok := v.body.derived.(Block_Stmt); ok && check_stmt.uses_do { + if check_stmt, ok := v.body.derived.(^Block_Stmt); ok && check_stmt.uses_do { uses_do = true } @@ -626,7 +637,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener visit_stmt(p, v.else_stmt) } - case Switch_Stmt: + case ^Switch_Stmt: move_line(p, v.pos) if v.label != nil { @@ -654,7 +665,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener visit_expr(p, v.cond) visit_stmt(p, v.body) - case Case_Clause: + case ^Case_Clause: move_line(p, v.pos) if !p.config.indent_cases { @@ -678,7 +689,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener if !p.config.indent_cases { indent(p) } - case Type_Switch_Stmt: + case ^Type_Switch_Stmt: move_line(p, v.pos) hint_current_line(p, {.Switch_Stmt}) @@ -696,7 +707,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener visit_stmt(p, v.tag) visit_stmt(p, v.body) - case Assign_Stmt: + case ^Assign_Stmt: move_line(p, v.pos) hint_current_line(p, {.Assign}) @@ -710,13 +721,13 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener if block_stmt && p.config.semicolons { push_generic_token(p, .Semicolon, 0) } - case Expr_Stmt: + case ^Expr_Stmt: move_line(p, v.pos) visit_expr(p, v.expr) if block_stmt && p.config.semicolons { push_generic_token(p, .Semicolon, 0) } - case For_Stmt: + case ^For_Stmt: // this should be simplified move_line(p, v.pos) @@ -753,7 +764,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener visit_stmt(p, v.body) - case Inline_Range_Stmt: + case ^Inline_Range_Stmt: move_line(p, v.pos) if v.label != nil { @@ -779,7 +790,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener visit_expr(p, v.expr) visit_stmt(p, v.body) - case Range_Stmt: + case ^Range_Stmt: move_line(p, v.pos) if v.label != nil { @@ -805,7 +816,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener visit_expr(p, v.expr) visit_stmt(p, v.body) - case Return_Stmt: + case ^Return_Stmt: move_line(p, v.pos) push_generic_token(p, .Return, 1) @@ -817,7 +828,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener if block_stmt && p.config.semicolons { push_generic_token(p, .Semicolon, 0) } - case Defer_Stmt: + case ^Defer_Stmt: move_line(p, v.pos) push_generic_token(p, .Defer, 0) @@ -826,7 +837,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener if p.config.semicolons { push_generic_token(p, .Semicolon, 0) } - case When_Stmt: + case ^When_Stmt: move_line(p, v.pos) push_generic_token(p, .When, 1) visit_expr(p, v.cond) @@ -846,7 +857,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener visit_stmt(p, v.else_stmt) } - case Branch_Stmt: + case ^Branch_Stmt: move_line(p, v.pos) push_generic_token(p, v.tok.kind, 0) @@ -918,8 +929,15 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) { set_source_position(p, expr.pos) - switch v in expr.derived { - case Inline_Asm_Expr: + switch v in expr.derived_expr { + case ^Bad_Expr: + + case ^Tag_Expr: + push_generic_token(p, .Hash, 1) + push_generic_token(p, v.op.kind, 1, v.op.text) + visit_expr(p, v.expr) + + case ^Inline_Asm_Expr: push_generic_token(p, v.tok.kind, 1, v.tok.text) push_generic_token(p, .Open_Paren, 1) @@ -936,42 +954,42 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) { push_generic_token(p, .Comma, 0) visit_expr(p, v.constraints_string) push_generic_token(p, .Close_Brace, 0) - case Undef: + case ^Undef: push_generic_token(p, .Undef, 1) - case Auto_Cast: + case ^Auto_Cast: push_generic_token(p, v.op.kind, 1) visit_expr(p, v.expr) - case Ternary_If_Expr: + case ^Ternary_If_Expr: visit_expr(p, v.x) push_generic_token(p, v.op1.kind, 1) visit_expr(p, v.cond) push_generic_token(p, v.op2.kind, 1) visit_expr(p, v.y) - case Ternary_When_Expr: + case ^Ternary_When_Expr: visit_expr(p, v.x) push_generic_token(p, v.op1.kind, 1) visit_expr(p, v.cond) push_generic_token(p, v.op2.kind, 1) visit_expr(p, v.y) - case Or_Else_Expr: + case ^Or_Else_Expr: visit_expr(p, v.x) push_generic_token(p, v.token.kind, 1) visit_expr(p, v.y) - case Or_Return_Expr: + case ^Or_Return_Expr: visit_expr(p, v.expr) push_generic_token(p, v.token.kind, 1) - case Selector_Call_Expr: + case ^Selector_Call_Expr: visit_expr(p, v.call.expr) push_generic_token(p, .Open_Paren, 1) visit_exprs(p, v.call.args, {.Add_Comma}) push_generic_token(p, .Close_Paren, 0) - case Ellipsis: + case ^Ellipsis: push_generic_token(p, .Ellipsis, 1) visit_expr(p, v.expr) - case Relative_Type: + case ^Relative_Type: visit_expr(p, v.tag) visit_expr(p, v.type) - case Slice_Expr: + case ^Slice_Expr: visit_expr(p, v.expr) push_generic_token(p, .Open_Bracket, 0) visit_expr(p, v.low) @@ -981,37 +999,37 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) { visit_expr(p, v.high) } push_generic_token(p, .Close_Bracket, 0) - case Ident: + case ^Ident: if .Enforce_Poly_Names in options { push_generic_token(p, .Dollar, 1) push_ident_token(p, v.name, 0) } else { push_ident_token(p, v.name, 1) } - case Deref_Expr: + case ^Deref_Expr: visit_expr(p, v.expr) push_generic_token(p, v.op.kind, 0) - case Type_Cast: + case ^Type_Cast: push_generic_token(p, v.tok.kind, 1) push_generic_token(p, .Open_Paren, 0) visit_expr(p, v.type) push_generic_token(p, .Close_Paren, 0) merge_next_token(p) visit_expr(p, v.expr) - case Basic_Directive: + case ^Basic_Directive: push_generic_token(p, v.tok.kind, 1) push_ident_token(p, v.name, 0) - case Distinct_Type: + case ^Distinct_Type: push_generic_token(p, .Distinct, 1) visit_expr(p, v.type) - case Dynamic_Array_Type: + case ^Dynamic_Array_Type: visit_expr(p, v.tag) push_generic_token(p, .Open_Bracket, 1) push_generic_token(p, .Dynamic, 0) push_generic_token(p, .Close_Bracket, 0) merge_next_token(p) visit_expr(p, v.elem) - case Bit_Set_Type: + case ^Bit_Set_Type: push_generic_token(p, .Bit_Set, 1) push_generic_token(p, .Open_Bracket, 0) @@ -1023,13 +1041,16 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) { } push_generic_token(p, .Close_Bracket, 0) - case Union_Type: + case ^Union_Type: push_generic_token(p, .Union, 1) push_poly_params(p, v.poly_params) - if v.is_maybe { - push_ident_token(p, "#maybe", 1) + switch v.kind { + case .Normal: + case .maybe: push_ident_token(p, "#maybe", 1) + case .no_nil: push_ident_token(p, "#no_nil", 1) + case .shared_nil: push_ident_token(p, "#shared_nil", 1) } push_where_clauses(p, v.where_clauses) @@ -1045,7 +1066,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) { visit_exprs(p, v.variants, {.Add_Comma, .Trailing}) visit_end_brace(p, v.end) } - case Enum_Type: + case ^Enum_Type: push_generic_token(p, .Enum, 1) hint_current_line(p, {.Enum}) @@ -1068,7 +1089,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) { } set_source_position(p, v.end) - case Struct_Type: + case ^Struct_Type: push_generic_token(p, .Struct, 1) hint_current_line(p, {.Struct}) @@ -1103,7 +1124,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) { } set_source_position(p, v.end) - case Proc_Lit: + case ^Proc_Lit: switch v.inlining { case .None: case .Inline: @@ -1112,7 +1133,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) { push_ident_token(p, "#force_no_inline", 0) } - visit_proc_type(p, v.type^, true) + visit_proc_type(p, v.type, true) push_where_clauses(p, v.where_clauses) @@ -1122,16 +1143,16 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) { } else { push_generic_token(p, .Undef, 1) } - case Proc_Type: + case ^Proc_Type: visit_proc_type(p, v) - case Basic_Lit: + case ^Basic_Lit: push_generic_token(p, v.tok.kind, 1, v.tok.text) - case Binary_Expr: + case ^Binary_Expr: visit_binary_expr(p, v) - case Implicit_Selector_Expr: + case ^Implicit_Selector_Expr: push_generic_token(p, .Period, 1) push_ident_token(p, v.field.name, 0) - case Call_Expr: + case ^Call_Expr: visit_expr(p, v.expr) push_format_token(p, @@ -1146,27 +1167,34 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) { visit_call_exprs(p, v.args, v.ellipsis.kind == .Ellipsis) push_generic_token(p, .Close_Paren, 0) - case Typeid_Type: + case ^Typeid_Type: push_generic_token(p, .Typeid, 1) if v.specialization != nil { push_generic_token(p, .Quo, 0) visit_expr(p, v.specialization) } - case Selector_Expr: + case ^Selector_Expr: visit_expr(p, v.expr) push_generic_token(p, v.op.kind, 0) visit_expr(p, v.field) - case Paren_Expr: + case ^Paren_Expr: push_generic_token(p, .Open_Paren, 1) visit_expr(p, v.expr) push_generic_token(p, .Close_Paren, 0) - case Index_Expr: + case ^Index_Expr: visit_expr(p, v.expr) push_generic_token(p, .Open_Bracket, 0) visit_expr(p, v.index) push_generic_token(p, .Close_Bracket, 0) - case Proc_Group: + case ^Matrix_Index_Expr: + visit_expr(p, v.expr) + push_generic_token(p, .Open_Bracket, 0) + visit_expr(p, v.row_index) + push_generic_token(p, .Comma, 0) + visit_expr(p, v.column_index) + push_generic_token(p, .Close_Bracket, 0) + case ^Proc_Group: push_generic_token(p, v.tok.kind, 1) if len(v.args) != 0 && v.pos.line != v.args[len(v.args) - 1].pos.line { @@ -1181,7 +1209,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) { push_generic_token(p, .Close_Brace, 0) } - case Comp_Lit: + case ^Comp_Lit: if v.type != nil { visit_expr(p, v.type) } @@ -1198,18 +1226,18 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) { push_generic_token(p, .Close_Brace, 0) } - case Unary_Expr: + case ^Unary_Expr: push_generic_token(p, v.op.kind, 1) merge_next_token(p) visit_expr(p, v.expr) - case Field_Value: + case ^Field_Value: visit_expr(p, v.field) push_generic_token(p, .Eq, 1) visit_expr(p, v.value) - case Type_Assertion: + case ^Type_Assertion: visit_expr(p, v.expr) - if unary, ok := v.type.derived.(Unary_Expr); ok && unary.op.text == "?" { + if unary, ok := v.type.derived.(^Unary_Expr); ok && unary.op.text == "?" { push_generic_token(p, .Period, 0) visit_expr(p, v.type) } else { @@ -1219,13 +1247,13 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) { push_generic_token(p, .Close_Paren, 0) } - case Pointer_Type: + case ^Pointer_Type: push_generic_token(p, .Pointer, 1) merge_next_token(p) visit_expr(p, v.elem) - case Implicit: + case ^Implicit: push_generic_token(p, v.tok.kind, 1) - case Poly_Type: + case ^Poly_Type: push_generic_token(p, .Dollar, 1) merge_next_token(p) visit_expr(p, v.type) @@ -1235,22 +1263,35 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) { merge_next_token(p) visit_expr(p, v.specialization) } - case Array_Type: + case ^Array_Type: visit_expr(p, v.tag) push_generic_token(p, .Open_Bracket, 1) visit_expr(p, v.len) push_generic_token(p, .Close_Bracket, 0) merge_next_token(p) visit_expr(p, v.elem) - case Map_Type: + case ^Map_Type: push_generic_token(p, .Map, 1) push_generic_token(p, .Open_Bracket, 0) visit_expr(p, v.key) push_generic_token(p, .Close_Bracket, 0) merge_next_token(p) visit_expr(p, v.value) - case Helper_Type: + case ^Helper_Type: visit_expr(p, v.type) + case ^Multi_Pointer_Type: + push_generic_token(p, .Open_Bracket, 1) + push_generic_token(p, .Pointer, 0) + push_generic_token(p, .Close_Bracket, 0) + visit_expr(p, v.elem) + case ^Matrix_Type: + push_generic_token(p, .Matrix, 1) + push_generic_token(p, .Open_Bracket, 0) + visit_expr(p, v.row_count) + push_generic_token(p, .Comma, 0) + visit_expr(p, v.column_count) + push_generic_token(p, .Close_Bracket, 0) + visit_expr(p, v.elem) case: panic(fmt.aprint(expr.derived)) } @@ -1348,7 +1389,7 @@ visit_field_list :: proc(p: ^Printer, list: ^ast.Field_List, options := List_Opt } } -visit_proc_type :: proc(p: ^Printer, proc_type: ast.Proc_Type, is_proc_lit := false) { +visit_proc_type :: proc(p: ^Printer, proc_type: ^ast.Proc_Type, is_proc_lit := false) { if is_proc_lit { push_format_token(p, Format_Token { kind = .Proc, @@ -1392,7 +1433,7 @@ visit_proc_type :: proc(p: ^Printer, proc_type: ast.Proc_Type, is_proc_lit := fa } else if len(proc_type.results.list) == 1 { for name in proc_type.results.list[0].names { - if ident, ok := name.derived.(ast.Ident); ok { + if ident, ok := name.derived.(^ast.Ident); ok { if ident.name != "_" { use_parens = true } @@ -1410,19 +1451,19 @@ visit_proc_type :: proc(p: ^Printer, proc_type: ast.Proc_Type, is_proc_lit := fa } } -visit_binary_expr :: proc(p: ^Printer, binary: ast.Binary_Expr) { +visit_binary_expr :: proc(p: ^Printer, binary: ^ast.Binary_Expr) { move_line(p, binary.left.pos) - if v, ok := binary.left.derived.(ast.Binary_Expr); ok { + if v, ok := binary.left.derived.(^ast.Binary_Expr); ok { visit_binary_expr(p, v) } else { visit_expr(p, binary.left) } either_implicit_selector := false - if _, ok := binary.left.derived.(ast.Implicit_Selector_Expr); ok { + if _, ok := binary.left.derived.(^ast.Implicit_Selector_Expr); ok { either_implicit_selector = true - } else if _, ok := binary.right.derived.(ast.Implicit_Selector_Expr); ok { + } else if _, ok := binary.right.derived.(^ast.Implicit_Selector_Expr); ok { either_implicit_selector = true } @@ -1439,7 +1480,7 @@ visit_binary_expr :: proc(p: ^Printer, binary: ast.Binary_Expr) { move_line(p, binary.right.pos) - if v, ok := binary.right.derived.(ast.Binary_Expr); ok { + if v, ok := binary.right.derived.(^ast.Binary_Expr); ok { visit_binary_expr(p, v) } else { visit_expr(p, binary.right) @@ -1499,7 +1540,7 @@ visit_signature_list :: proc(p: ^Printer, list: ^ast.Field_List, remove_blank := named := false for name in field.names { - if ident, ok := name.derived.(ast.Ident); ok { + if ident, ok := name.derived.(^ast.Ident); ok { //for some reason the parser uses _ to mean empty if ident.name != "_" || !remove_blank { named = true diff --git a/core/os/dir_freebsd.odin b/core/os/dir_freebsd.odin new file mode 100644 index 000000000..c664ffb34 --- /dev/null +++ b/core/os/dir_freebsd.odin @@ -0,0 +1,72 @@ +package os + +import "core:mem" + +read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) { + dirp: Dir + dirp, err = _fdopendir(fd) + if err != ERROR_NONE { + return + } + + defer _closedir(dirp) + + dirpath: string + dirpath, err = absolute_path_from_handle(fd) + + if err != ERROR_NONE { + return + } + + defer delete(dirpath) + + n := n + size := n + if n <= 0 { + n = -1 + size = 100 + } + + dfi := make([dynamic]File_Info, 0, size, allocator) + + for { + entry: Dirent + end_of_stream: bool + entry, err, end_of_stream = _readdir(dirp) + if err != ERROR_NONE { + for fi_ in dfi { + file_info_delete(fi_, allocator) + } + delete(dfi) + return + } else if end_of_stream { + break + } + + fi_: File_Info + filename := cast(string)(transmute(cstring)mem.Raw_Cstring{ data = &entry.name[0] }) + + if filename == "." || filename == ".." { + continue + } + + fullpath := make([]byte, len(dirpath)+1+len(filename)) + copy(fullpath, dirpath) + copy(fullpath[len(dirpath):], "/") + copy(fullpath[len(dirpath)+1:], filename) + defer delete(fullpath, context.temp_allocator) + + fi_, err = stat(string(fullpath), allocator) + if err != ERROR_NONE { + for fi__ in dfi { + file_info_delete(fi__, allocator) + } + delete(dfi) + return + } + + append(&dfi, fi_) + } + + return dfi[:], ERROR_NONE +} diff --git a/core/os/dir_openbsd.odin b/core/os/dir_openbsd.odin new file mode 100644 index 000000000..465fd35ae --- /dev/null +++ b/core/os/dir_openbsd.odin @@ -0,0 +1,71 @@ +package os + +import "core:strings" +import "core:mem" + +read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) { + dirp: Dir + dirp, err = _fdopendir(fd) + if err != ERROR_NONE { + return + } + + defer _closedir(dirp) + + // XXX OpenBSD + dirpath: string + dirpath, err = absolute_path_from_handle(fd) + + if err != ERROR_NONE { + return + } + + defer delete(dirpath) + + n := n + size := n + if n <= 0 { + n = -1 + size = 100 + } + + dfi := make([dynamic]File_Info, 0, size, allocator) + + for { + entry: Dirent + end_of_stream: bool + entry, err, end_of_stream = _readdir(dirp) + if err != ERROR_NONE { + for fi_ in dfi { + file_info_delete(fi_, allocator) + } + delete(dfi) + return + } else if end_of_stream { + break + } + + fi_: File_Info + filename := cast(string)(transmute(cstring)mem.Raw_Cstring{ data = &entry.name[0] }) + + if filename == "." || filename == ".." { + continue + } + + fullpath := strings.join( []string{ dirpath, filename }, "/", context.temp_allocator) + defer delete(fullpath, context.temp_allocator) + + fi_, err = stat(fullpath, allocator) + if err != ERROR_NONE { + for fi__ in dfi { + file_info_delete(fi__, allocator) + } + delete(dfi) + return + } + + append(&dfi, fi_) + } + + return dfi[:], ERROR_NONE +} diff --git a/core/os/dir_windows.odin b/core/os/dir_windows.odin index ff7e53293..89a09d403 100644 --- a/core/os/dir_windows.odin +++ b/core/os/dir_windows.odin @@ -13,7 +13,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F if d.cFileName[0] == '.' && d.cFileName[1] == '.' && d.cFileName[2] == 0 { return } - path := strings.concatenate({base_path, `\`, win32.utf16_to_utf8(d.cFileName[:])}) + path := strings.concatenate({base_path, `\`, win32.utf16_to_utf8(d.cFileName[:]) or_else ""}) fi.fullpath = path fi.name = basename(path) fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow) @@ -82,6 +82,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F wpath_search[len(wpath)+2] = 0 path := cleanpath_from_buf(wpath) + defer delete(path) find_data := &win32.WIN32_FIND_DATAW{} find_handle := win32.FindFirstFileW(raw_data(wpath_search), find_data) diff --git a/core/os/env_windows.odin b/core/os/env_windows.odin index 74981bc6e..6e14127ed 100644 --- a/core/os/env_windows.odin +++ b/core/os/env_windows.odin @@ -11,24 +11,24 @@ lookup_env :: proc(key: string, allocator := context.allocator) -> (value: strin return } wkey := win32.utf8_to_wstring(key) - b := make([dynamic]u16, 100, context.temp_allocator) - for { - n := win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b))) - if n == 0 { - err := win32.GetLastError() - if err == u32(ERROR_ENVVAR_NOT_FOUND) { - return "", false - } + n := win32.GetEnvironmentVariableW(wkey, nil, 0) + if n == 0 { + err := win32.GetLastError() + if err == u32(ERROR_ENVVAR_NOT_FOUND) { + return "", false } - - if n <= u32(len(b)) { - value = win32.utf16_to_utf8(b[:n], allocator) - found = true - return - } - - resize(&b, len(b)*2) } + b := make([dynamic]u16, n, context.temp_allocator) + n = win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b))) + if n == 0 { + err := win32.GetLastError() + if err == u32(ERROR_ENVVAR_NOT_FOUND) { + return "", false + } + } + value, _ = win32.utf16_to_utf8(b[:n], allocator) + found = true + return } @@ -76,7 +76,7 @@ environ :: proc(allocator := context.allocator) -> []string { if i <= from { break } - append(&r, win32.utf16_to_utf8(envs[from:i], allocator)) + append(&r, win32.utf16_to_utf8(envs[from:i], allocator) or_else "") from = i + 1 } } diff --git a/core/os/file_windows.odin b/core/os/file_windows.odin index 419f8bbc2..8019b0440 100644 --- a/core/os/file_windows.odin +++ b/core/os/file_windows.odin @@ -20,13 +20,13 @@ open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errn case O_RDWR: access = win32.FILE_GENERIC_READ | win32.FILE_GENERIC_WRITE } + if mode&O_CREATE != 0 { + access |= win32.FILE_GENERIC_WRITE + } if mode&O_APPEND != 0 { access &~= win32.FILE_GENERIC_WRITE access |= win32.FILE_APPEND_DATA } - if mode&O_CREATE != 0 { - access |= win32.FILE_GENERIC_WRITE - } share_mode := win32.FILE_SHARE_READ|win32.FILE_SHARE_WRITE sa: ^win32.SECURITY_ATTRIBUTES = nil @@ -106,19 +106,23 @@ read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Errno) { BUF_SIZE :: 386 buf16: [BUF_SIZE]u16 buf8: [4*BUF_SIZE]u8 - + for n < len(b) && err == 0 { - max_read := u32(min(BUF_SIZE, len(b)/4)) + min_read := max(len(b)/4, 1 if len(b) > 0 else 0) + max_read := u32(min(BUF_SIZE, min_read)) + if max_read == 0 { + break + } single_read_length: u32 ok := win32.ReadConsoleW(handle, &buf16[0], max_read, &single_read_length, nil) if !ok { err = Errno(win32.GetLastError()) } - + buf8_len := utf16.decode_to_utf8(buf8[:], buf16[:single_read_length]) src := buf8[:buf8_len] - + ctrl_z := false for i := 0; i < len(src) && n+i < len(b); i += 1 { x := src[i] @@ -129,9 +133,16 @@ read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Errno) { b[n] = x n += 1 } - if ctrl_z || single_read_length < len(buf16) { + if ctrl_z || single_read_length < max_read { break } + + // NOTE(bill): if the last two values were a newline, then it is expected that + // this is the end of the input + if n >= 2 && single_read_length == max_read && string(b[n-2:n]) == "\r\n" { + break + } + } return @@ -309,9 +320,6 @@ stderr := get_std_handle(uint(win32.STD_ERROR_HANDLE)) get_std_handle :: proc "contextless" (h: uint) -> Handle { fd := win32.GetStdHandle(win32.DWORD(h)) - when size_of(uintptr) == 8 { - win32.SetHandleInformation(fd, win32.HANDLE_FLAG_INHERIT, 0) - } return Handle(fd) } @@ -357,7 +365,7 @@ get_current_directory :: proc(allocator := context.allocator) -> string { win32.ReleaseSRWLockExclusive(&cwd_lock) - return win32.utf16_to_utf8(dir_buf_wstr, allocator) + return win32.utf16_to_utf8(dir_buf_wstr, allocator) or_else "" } set_current_directory :: proc(path: string) -> (err: Errno) { @@ -376,20 +384,33 @@ set_current_directory :: proc(path: string) -> (err: Errno) { -change_directory :: proc(path: string) -> Errno { +change_directory :: proc(path: string) -> (err: Errno) { wpath := win32.utf8_to_wstring(path, context.temp_allocator) - return Errno(win32.SetCurrentDirectoryW(wpath)) + + if !win32.SetCurrentDirectoryW(wpath) { + err = Errno(win32.GetLastError()) + } + return } -make_directory :: proc(path: string, mode: u32) -> Errno { +make_directory :: proc(path: string, mode: u32 = 0) -> (err: Errno) { + // Mode is unused on Windows, but is needed on *nix wpath := win32.utf8_to_wstring(path, context.temp_allocator) - return Errno(win32.CreateDirectoryW(wpath, nil)) + + if !win32.CreateDirectoryW(wpath, nil) { + err = Errno(win32.GetLastError()) + } + return } -remove_directory :: proc(path: string) -> Errno { +remove_directory :: proc(path: string) -> (err: Errno) { wpath := win32.utf8_to_wstring(path, context.temp_allocator) - return Errno(win32.RemoveDirectoryW(wpath)) + + if !win32.RemoveDirectoryW(wpath) { + err = Errno(win32.GetLastError()) + } + return } @@ -399,7 +420,7 @@ is_abs :: proc(path: string) -> bool { if len(path) > 0 && path[0] == '/' { return true } - when ODIN_OS == "windows" { + when ODIN_OS == .Windows { if len(path) > 2 { switch path[0] { case 'A'..='Z', 'a'..='z': @@ -455,23 +476,31 @@ fix_long_path :: proc(path: string) -> string { } -link :: proc(old_name, new_name: string) -> Errno { +link :: proc(old_name, new_name: string) -> (err: Errno) { n := win32.utf8_to_wstring(fix_long_path(new_name)) o := win32.utf8_to_wstring(fix_long_path(old_name)) return Errno(win32.CreateHardLinkW(n, o, nil)) } -unlink :: proc(path: string) -> Errno { +unlink :: proc(path: string) -> (err: Errno) { wpath := win32.utf8_to_wstring(path, context.temp_allocator) - return Errno(win32.DeleteFileW(wpath)) + + if !win32.DeleteFileW(wpath) { + err = Errno(win32.GetLastError()) + } + return } -rename :: proc(old_path, new_path: string) -> Errno { +rename :: proc(old_path, new_path: string) -> (err: Errno) { from := win32.utf8_to_wstring(old_path, context.temp_allocator) to := win32.utf8_to_wstring(new_path, context.temp_allocator) - return Errno(win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING)) + + if !win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING) { + err = Errno(win32.GetLastError()) + } + return } diff --git a/core/os/os.odin b/core/os/os.odin index 83158be80..5e71e720e 100644 --- a/core/os/os.odin +++ b/core/os/os.odin @@ -9,6 +9,10 @@ OS :: ODIN_OS ARCH :: ODIN_ARCH ENDIAN :: ODIN_ENDIAN +SEEK_SET :: 0 +SEEK_CUR :: 1 +SEEK_END :: 2 + write_string :: proc(fd: Handle, str: string) -> (int, Errno) { return write(fd, transmute([]byte)str) } @@ -139,7 +143,7 @@ write_entire_file :: proc(name: string, data: []byte, truncate := true) -> (succ } mode: int = 0 - when OS == "linux" || OS == "darwin" { + when OS == .Linux || OS == .Darwin { // NOTE(justasd): 644 (owner read, write; group read; others read) mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH } @@ -206,11 +210,19 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode, } } - aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int) -> ([]byte, mem.Allocator_Error) { + aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int) -> (new_memory: []byte, err: mem.Allocator_Error) { if p == nil { return nil, nil } - return aligned_alloc(new_size, new_alignment, p) + + new_memory = aligned_alloc(new_size, new_alignment, p) or_return + + // NOTE: heap_resize does not zero the new memory, so we do it + if new_size > old_size { + new_region := mem.raw_data(new_memory[old_size:]) + mem.zero(new_region, new_size - old_size) + } + return } switch mode { diff --git a/core/os/os2/env_linux.odin b/core/os/os2/env_linux.odin new file mode 100644 index 000000000..1833ac4dc --- /dev/null +++ b/core/os/os2/env_linux.odin @@ -0,0 +1,28 @@ +//+private +package os2 + +_get_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { + //TODO + return +} + +_set_env :: proc(key, value: string) -> bool { + //TODO + return false +} + +_unset_env :: proc(key: string) -> bool { + //TODO + return false +} + +_clear_env :: proc() { + //TODO +} + +_environ :: proc(allocator := context.allocator) -> []string { + //TODO + return nil +} + + diff --git a/core/os/os2/env_windows.odin b/core/os/os2/env_windows.odin index af04db858..f58922fac 100644 --- a/core/os/os2/env_windows.odin +++ b/core/os/os2/env_windows.odin @@ -1,32 +1,37 @@ //+private package os2 -import "core:mem" import win32 "core:sys/windows" +import "core:runtime" -_lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { +_lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) { if key == "" { return } wkey := win32.utf8_to_wstring(key) - b := make([dynamic]u16, 100, context.temp_allocator) - for { - n := win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b))) - if n == 0 { - err := win32.GetLastError() - if err == win32.ERROR_ENVVAR_NOT_FOUND { - return "", false - } - } - if n <= u32(len(b)) { - value = win32.utf16_to_utf8(b[:n], allocator) - found = true - return + n := win32.GetEnvironmentVariableW(wkey, nil, 0) + if n == 0 { + err := win32.GetLastError() + if err == win32.ERROR_ENVVAR_NOT_FOUND { + return "", false } - - resize(&b, len(b)*2) + return "", true } + b := make([]u16, n+1, _temp_allocator()) + + n = win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b))) + if n == 0 { + err := win32.GetLastError() + if err == win32.ERROR_ENVVAR_NOT_FOUND { + return "", false + } + return "", false + } + + value = win32.utf16_to_utf8(b[:n], allocator) or_else "" + found = true + return } _set_env :: proc(key, value: string) -> bool { @@ -42,7 +47,7 @@ _unset_env :: proc(key: string) -> bool { } _clear_env :: proc() { - envs := environ(context.temp_allocator) + envs := environ(_temp_allocator()) for env in envs { for j in 1.. (cname: cstring, allocated: bool) { + if len(name) > _CSTRING_NAME_HEAP_THRESHOLD { + cname = strings.clone_to_cstring(name) + allocated = true + return + } + cname = strings.clone_to_cstring(name, context.temp_allocator) + return +} diff --git a/core/os/os2/file_stream.odin b/core/os/os2/file_stream.odin index 0962ed59c..5e3db9370 100644 --- a/core/os/os2/file_stream.odin +++ b/core/os/os2/file_stream.odin @@ -2,12 +2,20 @@ package os2 import "core:io" -file_to_stream :: proc(fd: Handle) -> (s: io.Stream) { - s.stream_data = rawptr(uintptr(fd)) +to_stream :: proc(f: ^File) -> (s: io.Stream) { + s.stream_data = f s.stream_vtable = _file_stream_vtable return } +to_writer :: proc(f: ^File) -> (s: io.Writer) { + return {to_stream(f)} +} +to_reader :: proc(f: ^File) -> (s: io.Reader) { + return {to_stream(f)} +} + + @(private) error_to_io_error :: proc(ferr: Error) -> io.Error { if ferr == nil { @@ -20,66 +28,66 @@ error_to_io_error :: proc(ferr: Error) -> io.Error { @(private) _file_stream_vtable := &io.Stream_VTable{ impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { - fd := Handle(uintptr(s.stream_data)) + f := (^File)(s.stream_data) ferr: Error - n, ferr = read(fd, p) + n, ferr = read(f, p) err = error_to_io_error(ferr) return }, impl_read_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) { - fd := Handle(uintptr(s.stream_data)) + f := (^File)(s.stream_data) ferr: Error - n, ferr = read_at(fd, p, offset) + n, ferr = read_at(f, p, offset) err = error_to_io_error(ferr) return }, impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) { - fd := Handle(uintptr(s.stream_data)) + f := (^File)(s.stream_data) ferr: Error - n, ferr = write_to(fd, w) + n, ferr = write_to(f, w) err = error_to_io_error(ferr) return }, impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { - fd := Handle(uintptr(s.stream_data)) + f := (^File)(s.stream_data) ferr: Error - n, ferr = write(fd, p) + n, ferr = write(f, p) err = error_to_io_error(ferr) return }, impl_write_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) { - fd := Handle(uintptr(s.stream_data)) + f := (^File)(s.stream_data) ferr: Error - n, ferr = write_at(fd, p, offset) + n, ferr = write_at(f, p, offset) err = error_to_io_error(ferr) return }, impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) { - fd := Handle(uintptr(s.stream_data)) + f := (^File)(s.stream_data) ferr: Error - n, ferr = read_from(fd, r) + n, ferr = read_from(f, r) err = error_to_io_error(ferr) return }, impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) { - fd := Handle(uintptr(s.stream_data)) - n, ferr := seek(fd, offset, Seek_From(whence)) + f := (^File)(s.stream_data) + n, ferr := seek(f, offset, Seek_From(whence)) err := error_to_io_error(ferr) return n, err }, impl_size = proc(s: io.Stream) -> i64 { - fd := Handle(uintptr(s.stream_data)) - sz, _ := file_size(fd) + f := (^File)(s.stream_data) + sz, _ := file_size(f) return sz }, impl_flush = proc(s: io.Stream) -> io.Error { - fd := Handle(uintptr(s.stream_data)) - ferr := flush(fd) + f := (^File)(s.stream_data) + ferr := flush(f) return error_to_io_error(ferr) }, impl_close = proc(s: io.Stream) -> io.Error { - fd := Handle(uintptr(s.stream_data)) - ferr := close(fd) + f := (^File)(s.stream_data) + ferr := close(f) return error_to_io_error(ferr) }, } diff --git a/core/os/os2/file_util.odin b/core/os/os2/file_util.odin index 77f6545ac..9f9064244 100644 --- a/core/os/os2/file_util.odin +++ b/core/os/os2/file_util.odin @@ -4,25 +4,25 @@ import "core:mem" import "core:strconv" import "core:unicode/utf8" -write_string :: proc(fd: Handle, s: string) -> (n: int, err: Error) { - return write(fd, transmute([]byte)s) +write_string :: proc(f: ^File, s: string) -> (n: int, err: Error) { + return write(f, transmute([]byte)s) } -write_byte :: proc(fd: Handle, b: byte) -> (n: int, err: Error) { - return write(fd, []byte{b}) +write_byte :: proc(f: ^File, b: byte) -> (n: int, err: Error) { + return write(f, []byte{b}) } -write_rune :: proc(fd: Handle, r: rune) -> (n: int, err: Error) { +write_rune :: proc(f: ^File, r: rune) -> (n: int, err: Error) { if r < utf8.RUNE_SELF { - return write_byte(fd, byte(r)) + return write_byte(f, byte(r)) } b: [4]byte b, n = utf8.encode_rune(r) - return write(fd, b[:n]) + return write(f, b[:n]) } -write_encoded_rune :: proc(fd: Handle, r: rune) -> (n: int, err: Error) { +write_encoded_rune :: proc(f: ^File, r: rune) -> (n: int, err: Error) { wrap :: proc(m: int, merr: Error, n: ^int, err: ^Error) -> bool { n^ += m if merr != nil { @@ -32,49 +32,49 @@ write_encoded_rune :: proc(fd: Handle, r: rune) -> (n: int, err: Error) { return false } - if wrap(write_byte(fd, '\''), &n, &err) { return } + if wrap(write_byte(f, '\''), &n, &err) { return } switch r { - case '\a': if wrap(write_string(fd, "\\a"), &n, &err) { return } - case '\b': if wrap(write_string(fd, "\\b"), &n, &err) { return } - case '\e': if wrap(write_string(fd, "\\e"), &n, &err) { return } - case '\f': if wrap(write_string(fd, "\\f"), &n, &err) { return } - case '\n': if wrap(write_string(fd, "\\n"), &n, &err) { return } - case '\r': if wrap(write_string(fd, "\\r"), &n, &err) { return } - case '\t': if wrap(write_string(fd, "\\t"), &n, &err) { return } - case '\v': if wrap(write_string(fd, "\\v"), &n, &err) { return } + case '\a': if wrap(write_string(f, "\\a"), &n, &err) { return } + case '\b': if wrap(write_string(f, "\\b"), &n, &err) { return } + case '\e': if wrap(write_string(f, "\\e"), &n, &err) { return } + case '\f': if wrap(write_string(f, "\\f"), &n, &err) { return } + case '\n': if wrap(write_string(f, "\\n"), &n, &err) { return } + case '\r': if wrap(write_string(f, "\\r"), &n, &err) { return } + case '\t': if wrap(write_string(f, "\\t"), &n, &err) { return } + case '\v': if wrap(write_string(f, "\\v"), &n, &err) { return } case: if r < 32 { - if wrap(write_string(fd, "\\x"), &n, &err) { return } + if wrap(write_string(f, "\\x"), &n, &err) { return } b: [2]byte s := strconv.append_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil) switch len(s) { - case 0: if wrap(write_string(fd, "00"), &n, &err) { return } - case 1: if wrap(write_rune(fd, '0'), &n, &err) { return } - case 2: if wrap(write_string(fd, s), &n, &err) { return } + case 0: if wrap(write_string(f, "00"), &n, &err) { return } + case 1: if wrap(write_rune(f, '0'), &n, &err) { return } + case 2: if wrap(write_string(f, s), &n, &err) { return } } } else { - if wrap(write_rune(fd, r), &n, &err) { return } + if wrap(write_rune(f, r), &n, &err) { return } } } - _ = wrap(write_byte(fd, '\''), &n, &err) + _ = wrap(write_byte(f, '\''), &n, &err) return } -write_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (n: int, err: Error) { +write_ptr :: proc(f: ^File, data: rawptr, len: int) -> (n: int, err: Error) { s := transmute([]byte)mem.Raw_Slice{data, len} - return write(fd, s) + return write(f, s) } -read_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (n: int, err: Error) { +read_ptr :: proc(f: ^File, data: rawptr, len: int) -> (n: int, err: Error) { s := transmute([]byte)mem.Raw_Slice{data, len} - return read(fd, s) + return read(f, s) } -read_entire_file :: proc(name: string, allocator := context.allocator) -> ([]byte, Error) { +read_entire_file :: proc(name: string, allocator := context.allocator) -> (data: []byte, err: Error) { f, ferr := open(name) if ferr != nil { return nil, ferr @@ -91,15 +91,17 @@ read_entire_file :: proc(name: string, allocator := context.allocator) -> ([]byt // TODO(bill): Is this correct logic? total: int - data := make([]byte, size, allocator) + data = make([]byte, size, allocator) or_return for { - n, err := read(f, data[total:]) + n: int + n, err = read(f, data[total:]) total += n if err != nil { if err == .EOF { err = nil } - return data[:total], err + data = data[:total] + return } } } @@ -109,7 +111,7 @@ write_entire_file :: proc(name: string, data: []byte, perm: File_Mode, truncate if truncate { flags |= O_TRUNC } - f, err := open_file(name, flags, perm) + f, err := open(name, flags, perm) if err != nil { return err } diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin index cfc9feebf..7589ed799 100644 --- a/core/os/os2/file_windows.odin +++ b/core/os/os2/file_windows.odin @@ -2,135 +2,728 @@ package os2 import "core:io" +import "core:mem" +import "core:sync" +import "core:runtime" +import "core:strings" import "core:time" +import "core:unicode/utf16" +import win32 "core:sys/windows" -_create :: proc(name: string) -> (Handle, Error) { - return 0, nil +INVALID_HANDLE :: ~uintptr(0) + +S_IWRITE :: 0o200 +_ERROR_BAD_NETPATH :: 53 +MAX_RW :: 1<<30 + +_file_allocator :: proc() -> runtime.Allocator { + return heap_allocator() } -_open :: proc(name: string) -> (Handle, Error) { - return 0, nil -} - -_open_file :: proc(name: string, flag: int, perm: File_Mode) -> (Handle, Error) { - return 0, nil -} - -_close :: proc(fd: Handle) -> Error { - return nil -} - -_name :: proc(fd: Handle, allocator := context.allocator) -> string { - return "" -} - -_seek :: proc(fd: Handle, offset: i64, whence: Seek_From) -> (ret: i64, err: Error) { - return -} - -_read :: proc(fd: Handle, p: []byte) -> (n: int, err: Error) { - return -} - -_read_at :: proc(fd: Handle, p: []byte, offset: i64) -> (n: int, err: Error) { - return -} - -_read_from :: proc(fd: Handle, r: io.Reader) -> (n: i64, err: Error) { - return -} - -_write :: proc(fd: Handle, p: []byte) -> (n: int, err: Error) { - return -} - -_write_at :: proc(fd: Handle, p: []byte, offset: i64) -> (n: int, err: Error) { - return -} - -_write_to :: proc(fd: Handle, w: io.Writer) -> (n: i64, err: Error) { - return -} - -_file_size :: proc(fd: Handle) -> (n: i64, err: Error) { - return +_temp_allocator :: proc() -> runtime.Allocator { + // TODO(bill): make this not depend on the context allocator + return context.temp_allocator } -_sync :: proc(fd: Handle) -> Error { - return nil +_File_Kind :: enum u8 { + File, + Console, + Pipe, } -_flush :: proc(fd: Handle) -> Error { - return nil +_File :: struct { + fd: rawptr, + name: string, + wname: win32.wstring, + kind: _File_Kind, + + allocator: runtime.Allocator, + + rw_mutex: sync.RW_Mutex, // read write calls + p_mutex: sync.Mutex, // pread pwrite calls } -_truncate :: proc(fd: Handle, size: i64) -> Maybe(Path_Error) { - return nil +_handle :: proc(f: ^File) -> win32.HANDLE { + return win32.HANDLE(_fd(f)) } -_remove :: proc(name: string) -> Maybe(Path_Error) { - return nil +_open_internal :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (handle: uintptr, err: Error) { + if len(name) == 0 { + err = .Not_Exist + return + } + + path := _fix_long_path(name) + access: u32 + switch flags & {.Read, .Write} { + case {.Read}: access = win32.FILE_GENERIC_READ + case {.Write}: access = win32.FILE_GENERIC_WRITE + case {.Read, .Write}: access = win32.FILE_GENERIC_READ | win32.FILE_GENERIC_WRITE + } + + if .Create in flags { + access |= win32.FILE_GENERIC_WRITE + } + if .Append in flags { + access &~= win32.FILE_GENERIC_WRITE + access |= win32.FILE_APPEND_DATA + } + share_mode := u32(win32.FILE_SHARE_READ | win32.FILE_SHARE_WRITE) + sa: ^win32.SECURITY_ATTRIBUTES + if .Close_On_Exec not_in flags { + sa = &win32.SECURITY_ATTRIBUTES{} + sa.nLength = size_of(win32.SECURITY_ATTRIBUTES) + sa.bInheritHandle = true + } + + create_mode: u32 = win32.OPEN_EXISTING + switch { + case flags & {.Create, .Excl} == {.Create, .Excl}: + create_mode = win32.CREATE_NEW + case flags & {.Create, .Trunc} == {.Create, .Trunc}: + create_mode = win32.CREATE_ALWAYS + case flags & {.Create} == {.Create}: + create_mode = win32.OPEN_ALWAYS + case flags & {.Trunc} == {.Trunc}: + create_mode = win32.TRUNCATE_EXISTING + } + + attrs: u32 = win32.FILE_ATTRIBUTE_NORMAL + if perm & S_IWRITE == 0 { + attrs = win32.FILE_ATTRIBUTE_READONLY + if create_mode == win32.CREATE_ALWAYS { + // NOTE(bill): Open has just asked to create a file in read-only mode. + // If the file already exists, to make it akin to a *nix open call, + // the call preserves the existing permissions. + h := win32.CreateFileW(path, access, share_mode, sa, win32.TRUNCATE_EXISTING, win32.FILE_ATTRIBUTE_NORMAL, nil) + if h == win32.INVALID_HANDLE { + switch e := win32.GetLastError(); e { + case win32.ERROR_FILE_NOT_FOUND, _ERROR_BAD_NETPATH, win32.ERROR_PATH_NOT_FOUND: + // file does not exist, create the file + case 0: + return uintptr(h), nil + case: + return 0, Platform_Error(e) + } + } + } + } + h := win32.CreateFileW(path, access, share_mode, sa, create_mode, attrs, nil) + if h == win32.INVALID_HANDLE { + return 0, _get_platform_error() + } + return uintptr(h), nil } -_rename :: proc(old_path, new_path: string) -> Maybe(Path_Error) { + +_open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (f: ^File, err: Error) { + flags := flags if flags != nil else {.Read} + handle := _open_internal(name, flags + {.Close_On_Exec}, perm) or_return + return _new_file(handle, name), nil +} + +_new_file :: proc(handle: uintptr, name: string) -> ^File { + if handle == INVALID_HANDLE { + return nil + } + f := new(File, _file_allocator()) + + f.impl.allocator = _file_allocator() + f.impl.fd = rawptr(fd) + f.impl.name = strings.clone(name, f.impl.allocator) + f.impl.wname = win32.utf8_to_wstring(name, f.impl.allocator) + + handle := _handle(f) + kind := _File_Kind.File + if m: u32; win32.GetConsoleMode(handle, &m) { + kind = .Console + } + if win32.GetFileType(handle) == win32.FILE_TYPE_PIPE { + kind = .Pipe + } + f.impl.kind = kind + + return f +} + +_fd :: proc(f: ^File) -> uintptr { + if f == nil { + return INVALID_HANDLE + } + return uintptr(f.impl.fd) +} + +_destroy :: proc(f: ^File) -> Error { + if f == nil { + return nil + } + + a := f.impl.allocator + free(f.impl.wname, a) + delete(f.impl.name, a) + free(f, a) return nil } -_link :: proc(old_name, new_name: string) -> Maybe(Link_Error) { +_close :: proc(f: ^File) -> Error { + if f == nil { + return nil + } + if !win32.CloseHandle(win32.HANDLE(f.impl.fd)) { + return .Closed + } + return _destroy(f) +} + +_name :: proc(f: ^File) -> string { + return f.impl.name if f != nil else "" +} + +_seek :: proc(f: ^File, offset: i64, whence: Seek_From) -> (ret: i64, err: Error) { + handle := _handle(f) + if handle == win32.INVALID_HANDLE { + return 0, .Invalid_File + } + if f.impl.kind == .Pipe { + return 0, .Invalid_File + } + + sync.guard(&f.impl.rw_mutex) + + w: u32 + switch whence { + case .Start: w = win32.FILE_BEGIN + case .Current: w = win32.FILE_CURRENT + case .End: w = win32.FILE_END + } + hi := i32(offset>>32) + lo := i32(offset) + + dw_ptr := win32.SetFilePointer(handle, lo, &hi, w) + if dw_ptr == win32.INVALID_SET_FILE_POINTER { + return 0, _get_platform_error() + } + return i64(hi)<<32 + i64(dw_ptr), nil +} + +_read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) { + read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Error) { + if len(b) == 0 { + return 0, nil + } + + // TODO(bill): should this be moved to `_File` instead? + BUF_SIZE :: 386 + buf16: [BUF_SIZE]u16 + buf8: [4*BUF_SIZE]u8 + + for n < len(b) && err == nil { + min_read := max(len(b)/4, 1 if len(b) > 0 else 0) + max_read := u32(min(BUF_SIZE, min_read)) + if max_read == 0 { + break + } + + single_read_length: u32 + ok := win32.ReadConsoleW(handle, &buf16[0], max_read, &single_read_length, nil) + if !ok { + err = _get_platform_error() + } + + buf8_len := utf16.decode_to_utf8(buf8[:], buf16[:single_read_length]) + src := buf8[:buf8_len] + + ctrl_z := false + for i := 0; i < len(src) && n+i < len(b); i += 1 { + x := src[i] + if x == 0x1a { // ctrl-z + ctrl_z = true + break + } + b[n] = x + n += 1 + } + if ctrl_z || single_read_length < max_read { + break + } + + // NOTE(bill): if the last two values were a newline, then it is expected that + // this is the end of the input + if n >= 2 && single_read_length == max_read && string(b[n-2:n]) == "\r\n" { + break + } + } + + return + } + + handle := _handle(f) + + single_read_length: win32.DWORD + total_read: int + length := len(p) + + sync.shared_guard(&f.impl.rw_mutex) // multiple readers + + if sync.guard(&f.impl.p_mutex) { + to_read := min(win32.DWORD(length), MAX_RW) + ok: win32.BOOL + if f.impl.kind == .Console { + n, err := read_console(handle, p[total_read:][:to_read]) + total_read += n + if err != nil { + return int(total_read), err + } + } else { + ok = win32.ReadFile(handle, &p[total_read], to_read, &single_read_length, nil) + } + + if single_read_length > 0 && ok { + total_read += int(single_read_length) + } else { + err = _get_platform_error() + } + } + + return int(total_read), nil +} + +_read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) { + pread :: proc(f: ^File, data: []byte, offset: i64) -> (n: int, err: Error) { + buf := data + if len(buf) > MAX_RW { + buf = buf[:MAX_RW] + + } + curr_offset := seek(f, offset, .Current) or_return + defer seek(f, curr_offset, .Start) + + o := win32.OVERLAPPED{ + OffsetHigh = u32(offset>>32), + Offset = u32(offset), + } + + // TODO(bill): Determine the correct behaviour for consoles + + h := _handle(f) + done: win32.DWORD + if !win32.ReadFile(h, raw_data(buf), u32(len(buf)), &done, &o) { + err = _get_platform_error() + done = 0 + } + n = int(done) + return + } + + sync.guard(&f.impl.p_mutex) + + p, offset := p, offset + for len(p) > 0 { + m := pread(f, p, offset) or_return + n += m + p = p[m:] + offset += i64(m) + } + return +} + +_read_from :: proc(f: ^File, r: io.Reader) -> (n: i64, err: Error) { + // TODO(bill) + return +} + +_write :: proc(f: ^File, p: []byte) -> (n: int, err: Error) { + if len(p) == 0 { + return + } + + single_write_length: win32.DWORD + total_write: i64 + length := i64(len(p)) + + handle := _handle(f) + + sync.guard(&f.impl.rw_mutex) + for total_write < length { + remaining := length - total_write + to_write := win32.DWORD(min(i32(remaining), MAX_RW)) + + e := win32.WriteFile(handle, &p[total_write], to_write, &single_write_length, nil) + if single_write_length <= 0 || !e { + n = int(total_write) + err = _get_platform_error() + return + } + total_write += i64(single_write_length) + } + return int(total_write), nil +} + +_write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) { + pwrite :: proc(f: ^File, data: []byte, offset: i64) -> (n: int, err: Error) { + buf := data + if len(buf) > MAX_RW { + buf = buf[:MAX_RW] + + } + curr_offset := seek(f, offset, .Current) or_return + defer seek(f, curr_offset, .Start) + + o := win32.OVERLAPPED{ + OffsetHigh = u32(offset>>32), + Offset = u32(offset), + } + + h := _handle(f) + done: win32.DWORD + if !win32.WriteFile(h, raw_data(buf), u32(len(buf)), &done, &o) { + err = _get_platform_error() + done = 0 + } + n = int(done) + return + } + + sync.guard(&f.impl.p_mutex) + p, offset := p, offset + for len(p) > 0 { + m := pwrite(f, p, offset) or_return + n += m + p = p[m:] + offset += i64(m) + } + return +} + +_write_to :: proc(f: ^File, w: io.Writer) -> (n: i64, err: Error) { + // TODO(bill) + return +} + +_file_size :: proc(f: ^File) -> (n: i64, err: Error) { + length: win32.LARGE_INTEGER + handle := _handle(f) + if !win32.GetFileSizeEx(handle, &length) { + err = _get_platform_error() + } + n = i64(length) + return +} + + +_sync :: proc(f: ^File) -> Error { + return _flush(f) +} + +_flush :: proc(f: ^File) -> Error { + handle := _handle(f) + if !win32.FlushFileBuffers(handle) { + return _get_platform_error() + } return nil } -_symlink :: proc(old_name, new_name: string) -> Maybe(Link_Error) { +_truncate :: proc(f: ^File, size: i64) -> Error { + if f == nil { + return nil + } + curr_off := seek(f, 0, .Current) or_return + defer seek(f, curr_off, .Start) + seek(f, size, .Start) or_return + handle := _handle(f) + if !win32.SetEndOfFile(handle) { + return _get_platform_error() + } return nil } -_read_link :: proc(name: string) -> (string, Maybe(Path_Error)) { +_remove :: proc(name: string) -> Error { + p := _fix_long_path(name) + err, err1: Error + if !win32.DeleteFileW(p) { + err = _get_platform_error() + } + if err == nil { + return nil + } + if !win32.RemoveDirectoryW(p) { + err1 = _get_platform_error() + } + if err1 == nil { + return nil + } + + if err != err1 { + a := win32.GetFileAttributesW(p) + if a == ~u32(0) { + err = _get_platform_error() + } else { + if a & win32.FILE_ATTRIBUTE_DIRECTORY != 0 { + err = err1 + } else if a & win32.FILE_ATTRIBUTE_READONLY != 0 { + if win32.SetFileAttributesW(p, a &~ win32.FILE_ATTRIBUTE_READONLY) { + err = nil + if !win32.DeleteFileW(p) { + err = _get_platform_error() + } + } + } + } + } + + return err +} + +_rename :: proc(old_path, new_path: string) -> Error { + from := _fix_long_path(old_path) + to := _fix_long_path(new_path) + if win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING) { + return nil + } + return _get_platform_error() + +} + + +_link :: proc(old_name, new_name: string) -> Error { + o := _fix_long_path(old_name) + n := _fix_long_path(new_name) + if win32.CreateHardLinkW(n, o, nil) { + return nil + } + return _get_platform_error() +} + +_symlink :: proc(old_name, new_name: string) -> Error { + return .Unsupported +} + +_open_sym_link :: proc(p: [^]u16) -> (handle: win32.HANDLE, err: Error) { + attrs := u32(win32.FILE_FLAG_BACKUP_SEMANTICS) + attrs |= win32.FILE_FLAG_OPEN_REPARSE_POINT + handle = win32.CreateFileW(p, 0, 0, nil, win32.OPEN_EXISTING, attrs, nil) + if handle == win32.INVALID_HANDLE { + return nil, _get_platform_error() + } + return + +} + +_normalize_link_path :: proc(p: []u16, allocator: runtime.Allocator) -> (str: string, err: Error) { + has_prefix :: proc(p: []u16, str: string) -> bool { + if len(p) < len(str) { + return false + } + // assume ascii + for i in 0.. bool { + return has_prefix(p, `\??\`) + } + + if !has_unc_prefix(p) { + return win32.utf16_to_utf8(p, allocator) + } + + ws := p[4:] + switch { + case len(ws) >= 2 && ws[1] == ':': + return win32.utf16_to_utf8(ws, allocator) + case has_prefix(ws, `UNC\`): + ws[3] = '\\' // override data in buffer + return win32.utf16_to_utf8(ws[3:], allocator) + } + + + handle := _open_sym_link(raw_data(p)) or_return + defer win32.CloseHandle(handle) + + n := win32.GetFinalPathNameByHandleW(handle, nil, 0, win32.VOLUME_NAME_DOS) + if n == 0 { + return "", _get_platform_error() + } + buf := make([]u16, n+1, _temp_allocator()) + n = win32.GetFinalPathNameByHandleW(handle, raw_data(buf), u32(len(buf)), win32.VOLUME_NAME_DOS) + if n == 0 { + return "", _get_platform_error() + } + + ws = buf[:n] + if has_unc_prefix(ws) { + ws = ws[4:] + if len(ws) > 3 && has_prefix(ws, `UNC`) { + ws[2] = '\\' + return win32.utf16_to_utf8(ws[2:], allocator) + } + return win32.utf16_to_utf8(ws, allocator) + } + return "", .Invalid_Path +} + +_read_link :: proc(name: string, allocator: runtime.Allocator) -> (s: string, err: Error) { + MAXIMUM_REPARSE_DATA_BUFFER_SIZE :: 16 * 1024 + + @thread_local + rdb_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]byte + + p := _fix_long_path(name) + handle := _open_sym_link(p) or_return + defer win32.CloseHandle(handle) + + bytes_returned: u32 + if !win32.DeviceIoControl(handle, win32.FSCTL_GET_REPARSE_POINT, nil, 0, &rdb_buf[0], len(rdb_buf)-1, &bytes_returned, nil) { + err = _get_platform_error() + return + } + mem.zero_slice(rdb_buf[:min(bytes_returned+1, len(rdb_buf))]) + + + rdb := (^win32.REPARSE_DATA_BUFFER)(&rdb_buf[0]) + switch rdb.ReparseTag { + case win32.IO_REPARSE_TAG_SYMLINK: + rb := (^win32.SYMBOLIC_LINK_REPARSE_BUFFER)(&rdb.rest) + pb := win32.wstring(&rb.PathBuffer) + pb[rb.SubstituteNameOffset+rb.SubstituteNameLength] = 0 + p := pb[rb.SubstituteNameOffset:][:rb.SubstituteNameLength] + if rb.Flags & win32.SYMLINK_FLAG_RELATIVE != 0 { + return win32.utf16_to_utf8(p, allocator) + } + return _normalize_link_path(p, allocator) + + case win32.IO_REPARSE_TAG_MOUNT_POINT: + rb := (^win32.MOUNT_POINT_REPARSE_BUFFER)(&rdb.rest) + pb := win32.wstring(&rb.PathBuffer) + pb[rb.SubstituteNameOffset+rb.SubstituteNameLength] = 0 + p := pb[rb.SubstituteNameOffset:][:rb.SubstituteNameLength] + return _normalize_link_path(p, allocator) + } + // Path wasn't a symlink/junction but another reparse point kind return "", nil } -_chdir :: proc(fd: Handle) -> Error { +_fchdir :: proc(f: ^File) -> Error { + if f == nil { + return nil + } + if !win32.SetCurrentDirectoryW(f.impl.wname) { + return _get_platform_error() + } return nil } -_chmod :: proc(fd: Handle, mode: File_Mode) -> Error { +_fchmod :: proc(f: ^File, mode: File_Mode) -> Error { + if f == nil { + return nil + } + d: win32.BY_HANDLE_FILE_INFORMATION + if !win32.GetFileInformationByHandle(_handle(f), &d) { + return _get_platform_error() + } + attrs := d.dwFileAttributes + if mode & S_IWRITE != 0 { + attrs &~= win32.FILE_ATTRIBUTE_READONLY + } else { + attrs |= win32.FILE_ATTRIBUTE_READONLY + } + + info: win32.FILE_BASIC_INFO + info.FileAttributes = attrs + if !win32.SetFileInformationByHandle(_handle(f), .FileBasicInfo, &info, size_of(d)) { + return _get_platform_error() + } return nil } -_chown :: proc(fd: Handle, uid, gid: int) -> Error { +_fchown :: proc(f: ^File, uid, gid: int) -> Error { + return .Unsupported +} + +_chdir :: proc(name: string) -> Error { + p := _fix_long_path(name) + if !win32.SetCurrentDirectoryW(p) { + return _get_platform_error() + } return nil } +_chmod :: proc(name: string, mode: File_Mode) -> Error { + f := open(name, {.Write}) or_return + defer close(f) + return _fchmod(f, mode) +} + +_chown :: proc(name: string, uid, gid: int) -> Error { + return .Unsupported +} _lchown :: proc(name: string, uid, gid: int) -> Error { - return nil + return .Unsupported } -_chtimes :: proc(name: string, atime, mtime: time.Time) -> Maybe(Path_Error) { +_chtimes :: proc(name: string, atime, mtime: time.Time) -> Error { + f := open(name, {.Write}) or_return + defer close(f) + return _fchtimes(f, atime, mtime) +} +_fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error { + if f == nil { + return nil + } + d: win32.BY_HANDLE_FILE_INFORMATION + if !win32.GetFileInformationByHandle(_handle(f), &d) { + return _get_platform_error() + } + + to_windows_time :: #force_inline proc(t: time.Time) -> win32.LARGE_INTEGER { + // a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (UTC) + return win32.LARGE_INTEGER(time.time_to_unix_nano(t) * 100 + 116444736000000000) + } + + atime, mtime := atime, mtime + if time.time_to_unix_nano(atime) < time.time_to_unix_nano(mtime) { + atime = mtime + } + + info: win32.FILE_BASIC_INFO + info.LastAccessTime = to_windows_time(atime) + info.LastWriteTime = to_windows_time(mtime) + if !win32.SetFileInformationByHandle(_handle(f), .FileBasicInfo, &info, size_of(d)) { + return _get_platform_error() + } return nil } + _exists :: proc(path: string) -> bool { - return false + wpath := _fix_long_path(path) + attribs := win32.GetFileAttributesW(wpath) + return i32(attribs) != win32.INVALID_FILE_ATTRIBUTES } _is_file :: proc(path: string) -> bool { + wpath := _fix_long_path(path) + attribs := win32.GetFileAttributesW(wpath) + if i32(attribs) != win32.INVALID_FILE_ATTRIBUTES { + return attribs & win32.FILE_ATTRIBUTE_DIRECTORY == 0 + } return false } _is_dir :: proc(path: string) -> bool { + wpath := _fix_long_path(path) + attribs := win32.GetFileAttributesW(wpath) + if i32(attribs) != win32.INVALID_FILE_ATTRIBUTES { + return attribs & win32.FILE_ATTRIBUTE_DIRECTORY != 0 + } return false } - - -_path_error_delete :: proc(perr: Maybe(Path_Error)) { - -} - -_link_error_delete :: proc(lerr: Maybe(Link_Error)) { - -} diff --git a/core/os/os2/heap_linux.odin b/core/os/os2/heap_linux.odin new file mode 100644 index 000000000..5fdad5329 --- /dev/null +++ b/core/os/os2/heap_linux.odin @@ -0,0 +1,722 @@ +//+private +package os2 + +import "core:sys/unix" +import "core:sync" +import "core:mem" + +// NOTEs +// +// All allocations below DIRECT_MMAP_THRESHOLD exist inside of memory "Regions." A region +// consists of a Region_Header and the memory that will be divided into allocations to +// send to the user. The memory is an array of "Allocation_Headers" which are 8 bytes. +// Allocation_Headers are used to navigate the memory in the region. The "next" member of +// the Allocation_Header points to the next header, and the space between the headers +// can be used to send to the user. This space between is referred to as "blocks" in the +// code. The indexes in the header refer to these blocks instead of bytes. This allows us +// to index all the memory in the region with a u16. +// +// When an allocation request is made, it will use the first free block that can contain +// the entire block. If there is an excess number of blocks (as specified by the constant +// BLOCK_SEGMENT_THRESHOLD), this extra space will be segmented and left in the free_list. +// +// To keep the implementation simple, there can never exist 2 free blocks adjacent to each +// other. Any freeing will result in attempting to merge the blocks before and after the +// newly free'd blocks. +// +// Any request for size above the DIRECT_MMAP_THRESHOLD will result in the allocation +// getting its own individual mmap. Individual mmaps will still get an Allocation_Header +// that contains the size with the last bit set to 1 to indicate it is indeed a direct +// mmap allocation. + +// Why not brk? +// glibc's malloc utilizes a mix of the brk and mmap system calls. This implementation +// does *not* utilize the brk system call to avoid possible conflicts with foreign C +// code. Just because we aren't directly using libc, there is nothing stopping the user +// from doing it. + +// What's with all the #no_bounds_check? +// When memory is returned from mmap, it technically doesn't get written ... well ... anywhere +// until that region is written to by *you*. So, when a new region is created, we call mmap +// to get a pointer to some memory, and we claim that memory is a ^Region. Therefor, the +// region itself is never formally initialized by the compiler as this would result in writing +// zeros to memory that we can already assume are 0. This would also have the effect of +// actually commiting this data to memory whether it gets used or not. + + +// +// Some variables to play with +// + +// Minimum blocks used for any one allocation +MINIMUM_BLOCK_COUNT :: 2 + +// Number of extra blocks beyond the requested amount where we would segment. +// E.g. (blocks) |H0123456| 7 available +// |H01H0123| Ask for 2, now 4 available +BLOCK_SEGMENT_THRESHOLD :: 4 + +// Anything above this threshold will get its own memory map. Since regions +// are indexed by 16 bit integers, this value should not surpass max(u16) * 6 +DIRECT_MMAP_THRESHOLD_USER :: int(max(u16)) + +// The point at which we convert direct mmap to region. This should be a decent +// amount less than DIRECT_MMAP_THRESHOLD to avoid jumping in and out of regions. +MMAP_TO_REGION_SHRINK_THRESHOLD :: DIRECT_MMAP_THRESHOLD - PAGE_SIZE * 4 + +// free_list is dynamic and is initialized in the begining of the region memory +// when the region is initialized. Once resized, it can be moved anywhere. +FREE_LIST_DEFAULT_CAP :: 32 + + +// +// Other constants that should not be touched +// + +// This universally seems to be 4096 outside of uncommon archs. +PAGE_SIZE :: 4096 + +// just rounding up to nearest PAGE_SIZE +DIRECT_MMAP_THRESHOLD :: (DIRECT_MMAP_THRESHOLD_USER-1) + PAGE_SIZE - (DIRECT_MMAP_THRESHOLD_USER-1) % PAGE_SIZE + +// Regions must be big enough to hold DIRECT_MMAP_THRESHOLD - 1 as well +// as end right on a page boundary as to not waste space. +SIZE_OF_REGION :: DIRECT_MMAP_THRESHOLD + 4 * int(PAGE_SIZE) + +// size of user memory blocks +BLOCK_SIZE :: size_of(Allocation_Header) + +// number of allocation sections (call them blocks) of the region used for allocations +BLOCKS_PER_REGION :: u16((SIZE_OF_REGION - size_of(Region_Header)) / BLOCK_SIZE) + +// minimum amount of space that can used by any individual allocation (includes header) +MINIMUM_ALLOCATION :: (MINIMUM_BLOCK_COUNT * BLOCK_SIZE) + BLOCK_SIZE + +// This is used as a boolean value for Region_Header.local_addr. +CURRENTLY_ACTIVE :: (^^Region)(~uintptr(0)) + +FREE_LIST_ENTRIES_PER_BLOCK :: BLOCK_SIZE / size_of(u16) + +MMAP_FLAGS :: unix.MAP_ANONYMOUS | unix.MAP_PRIVATE +MMAP_PROT :: unix.PROT_READ | unix.PROT_WRITE + + +@thread_local _local_region: ^Region +global_regions: ^Region + + +// There is no way of correctly setting the last bit of free_idx or +// the last bit of requested, so we can safely use it as a flag to +// determine if we are interacting with a direct mmap. +REQUESTED_MASK :: 0x7FFFFFFFFFFFFFFF +IS_DIRECT_MMAP :: 0x8000000000000000 + +// Special free_idx value that does not index the free_list. +NOT_FREE :: 0x7FFF +Allocation_Header :: struct #raw_union { + using _: struct { + // Block indicies + idx: u16, + prev: u16, + next: u16, + free_idx: u16, + }, + requested: u64, +} + +Region_Header :: struct #align 16 { + next_region: ^Region, // points to next region in global_heap (linked list) + local_addr: ^^Region, // tracks region ownership via address of _local_region + reset_addr: ^^Region, // tracks old local addr for reset + free_list: []u16, + free_list_len: u16, + free_blocks: u16, // number of free blocks in region (includes headers) + last_used: u16, // farthest back block that has been used (need zeroing?) + _reserved: u16, +} + +Region :: struct { + hdr: Region_Header, + memory: [BLOCKS_PER_REGION]Allocation_Header, +} + +_heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode, + size, alignment: int, + old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, mem.Allocator_Error) { + // + // NOTE(tetra, 2020-01-14): The heap doesn't respect alignment. + // Instead, we overallocate by `alignment + size_of(rawptr) - 1`, and insert + // padding. We also store the original pointer returned by heap_alloc right before + // the pointer we return to the user. + // + + aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil) -> ([]byte, mem.Allocator_Error) { + a := max(alignment, align_of(rawptr)) + space := size + a - 1 + + allocated_mem: rawptr + if old_ptr != nil { + original_old_ptr := mem.ptr_offset((^rawptr)(old_ptr), -1)^ + allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr)) + } else { + allocated_mem = heap_alloc(space+size_of(rawptr)) + } + aligned_mem := rawptr(mem.ptr_offset((^u8)(allocated_mem), size_of(rawptr))) + + ptr := uintptr(aligned_mem) + aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a) + diff := int(aligned_ptr - ptr) + if (size + diff) > space { + return nil, .Out_Of_Memory + } + + aligned_mem = rawptr(aligned_ptr) + mem.ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem + + return mem.byte_slice(aligned_mem, size), nil + } + + aligned_free :: proc(p: rawptr) { + if p != nil { + heap_free(mem.ptr_offset((^rawptr)(p), -1)^) + } + } + + aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int) -> (new_memory: []byte, err: mem.Allocator_Error) { + if p == nil { + return nil, nil + } + + return aligned_alloc(new_size, new_alignment, p) + } + + switch mode { + case .Alloc: + return aligned_alloc(size, alignment) + + case .Free: + aligned_free(old_memory) + + case .Free_All: + return nil, .Mode_Not_Implemented + + case .Resize: + if old_memory == nil { + return aligned_alloc(size, alignment) + } + return aligned_resize(old_memory, old_size, size, alignment) + + case .Query_Features: + set := (^mem.Allocator_Mode_Set)(old_memory) + if set != nil { + set^ = {.Alloc, .Free, .Resize, .Query_Features} + } + return nil, nil + + case .Query_Info: + return nil, .Mode_Not_Implemented + } + + return nil, nil +} + +heap_alloc :: proc(size: int) -> rawptr { + if size >= DIRECT_MMAP_THRESHOLD { + return _direct_mmap_alloc(size) + } + + // atomically check if the local region has been stolen + if _local_region != nil { + res := sync.atomic_compare_exchange_strong_explicit( + &_local_region.hdr.local_addr, + &_local_region, + CURRENTLY_ACTIVE, + .Acquire, + .Relaxed, + ) + if res != &_local_region { + // At this point, the region has been stolen and res contains the unexpected value + expected := res + if res != CURRENTLY_ACTIVE { + expected = res + res = sync.atomic_compare_exchange_strong_explicit( + &_local_region.hdr.local_addr, + expected, + CURRENTLY_ACTIVE, + .Acquire, + .Relaxed, + ) + } + if res != expected { + _local_region = nil + } + } + } + + size := size + size = _round_up_to_nearest(size, BLOCK_SIZE) + blocks_needed := u16(max(MINIMUM_BLOCK_COUNT, size / BLOCK_SIZE)) + + // retrieve a region if new thread or stolen + if _local_region == nil { + _local_region, _ = _region_retrieve_with_space(blocks_needed) + if _local_region == nil { + return nil + } + } + defer sync.atomic_store_explicit(&_local_region.hdr.local_addr, &_local_region, .Release) + + // At this point we have a usable region. Let's find the user some memory + idx: u16 + local_region_idx := _region_get_local_idx() + back_idx := -1 + infinite: for { + for i := 0; i < int(_local_region.hdr.free_list_len); i += 1 { + idx = _local_region.hdr.free_list[i] + #no_bounds_check if _get_block_count(_local_region.memory[idx]) >= blocks_needed { + break infinite + } + } + sync.atomic_store_explicit(&_local_region.hdr.local_addr, &_local_region, .Release) + _local_region, back_idx = _region_retrieve_with_space(blocks_needed, local_region_idx, back_idx) + } + user_ptr, used := _region_get_block(_local_region, idx, blocks_needed) + _local_region.hdr.free_blocks -= (used + 1) + + // If this memory was ever used before, it now needs to be zero'd. + if idx < _local_region.hdr.last_used { + mem.zero(user_ptr, int(used) * BLOCK_SIZE) + } else { + _local_region.hdr.last_used = idx + used + } + + return user_ptr +} + +heap_resize :: proc(old_memory: rawptr, new_size: int) -> rawptr #no_bounds_check { + alloc := _get_allocation_header(old_memory) + if alloc.requested & IS_DIRECT_MMAP > 0 { + return _direct_mmap_resize(alloc, new_size) + } + + if new_size > DIRECT_MMAP_THRESHOLD { + return _direct_mmap_from_region(alloc, new_size) + } + + return _region_resize(alloc, new_size) +} + +heap_free :: proc(memory: rawptr) { + alloc := _get_allocation_header(memory) + if alloc.requested & IS_DIRECT_MMAP == IS_DIRECT_MMAP { + _direct_mmap_free(alloc) + return + } + + assert(alloc.free_idx == NOT_FREE) + + _region_find_and_assign_local(alloc) + _region_local_free(alloc) + sync.atomic_store_explicit(&_local_region.hdr.local_addr, &_local_region, .Release) +} + +// +// Regions +// +_new_region :: proc() -> ^Region #no_bounds_check { + res := unix.sys_mmap(nil, uint(SIZE_OF_REGION), MMAP_PROT, MMAP_FLAGS, -1, 0) + if res < 0 { + return nil + } + new_region := (^Region)(uintptr(res)) + + new_region.hdr.local_addr = CURRENTLY_ACTIVE + new_region.hdr.reset_addr = &_local_region + + free_list_blocks := _round_up_to_nearest(FREE_LIST_DEFAULT_CAP, FREE_LIST_ENTRIES_PER_BLOCK) + _region_assign_free_list(new_region, &new_region.memory[1], u16(free_list_blocks) * FREE_LIST_ENTRIES_PER_BLOCK) + + // + 2 to account for free_list's allocation header + first_user_block := len(new_region.hdr.free_list) / FREE_LIST_ENTRIES_PER_BLOCK + 2 + + // first allocation header (this is a free list) + new_region.memory[0].next = u16(first_user_block) + new_region.memory[0].free_idx = NOT_FREE + new_region.memory[first_user_block].idx = u16(first_user_block) + new_region.memory[first_user_block].next = BLOCKS_PER_REGION - 1 + + // add the first user block to the free list + new_region.hdr.free_list[0] = u16(first_user_block) + new_region.hdr.free_list_len = 1 + new_region.hdr.free_blocks = _get_block_count(new_region.memory[first_user_block]) + 1 + + for r := sync.atomic_compare_exchange_strong(&global_regions, nil, new_region); + r != nil; + r = sync.atomic_compare_exchange_strong(&r.hdr.next_region, nil, new_region) {} + + return new_region +} + +_region_resize :: proc(alloc: ^Allocation_Header, new_size: int, alloc_is_free_list: bool = false) -> rawptr #no_bounds_check { + assert(alloc.free_idx == NOT_FREE) + + old_memory := mem.ptr_offset(alloc, 1) + + old_block_count := _get_block_count(alloc^) + new_block_count := u16( + max(MINIMUM_BLOCK_COUNT, _round_up_to_nearest(new_size, BLOCK_SIZE) / BLOCK_SIZE), + ) + if new_block_count < old_block_count { + if new_block_count - old_block_count >= MINIMUM_BLOCK_COUNT { + _region_find_and_assign_local(alloc) + _region_segment(_local_region, alloc, new_block_count, alloc.free_idx) + new_block_count = _get_block_count(alloc^) + sync.atomic_store_explicit(&_local_region.hdr.local_addr, &_local_region, .Release) + } + // need to zero anything within the new block that that lies beyond new_size + extra_bytes := int(new_block_count * BLOCK_SIZE) - new_size + extra_bytes_ptr := mem.ptr_offset((^u8)(alloc), new_size + BLOCK_SIZE) + mem.zero(extra_bytes_ptr, extra_bytes) + return old_memory + } + + if !alloc_is_free_list { + _region_find_and_assign_local(alloc) + } + defer if !alloc_is_free_list { + sync.atomic_store_explicit(&_local_region.hdr.local_addr, &_local_region, .Release) + } + + // First, let's see if we can grow in place. + if alloc.next != BLOCKS_PER_REGION - 1 && _local_region.memory[alloc.next].free_idx != NOT_FREE { + next_alloc := _local_region.memory[alloc.next] + total_available := old_block_count + _get_block_count(next_alloc) + 1 + if total_available >= new_block_count { + alloc.next = next_alloc.next + _local_region.memory[alloc.next].prev = alloc.idx + if total_available - new_block_count > BLOCK_SEGMENT_THRESHOLD { + _region_segment(_local_region, alloc, new_block_count, next_alloc.free_idx) + } else { + _region_free_list_remove(_local_region, next_alloc.free_idx) + } + mem.zero(&_local_region.memory[next_alloc.idx], int(alloc.next - next_alloc.idx) * BLOCK_SIZE) + _local_region.hdr.last_used = max(alloc.next, _local_region.hdr.last_used) + _local_region.hdr.free_blocks -= (_get_block_count(alloc^) - old_block_count) + if alloc_is_free_list { + _region_assign_free_list(_local_region, old_memory, _get_block_count(alloc^)) + } + return old_memory + } + } + + // If we made it this far, we need to resize, copy, zero and free. + region_iter := _local_region + local_region_idx := _region_get_local_idx() + back_idx := -1 + idx: u16 + infinite: for { + for i := 0; i < len(region_iter.hdr.free_list); i += 1 { + idx = region_iter.hdr.free_list[i] + if _get_block_count(region_iter.memory[idx]) >= new_block_count { + break infinite + } + } + if region_iter != _local_region { + sync.atomic_store_explicit( + ®ion_iter.hdr.local_addr, + region_iter.hdr.reset_addr, + .Release, + ) + } + region_iter, back_idx = _region_retrieve_with_space(new_block_count, local_region_idx, back_idx) + } + if region_iter != _local_region { + sync.atomic_store_explicit( + ®ion_iter.hdr.local_addr, + region_iter.hdr.reset_addr, + .Release, + ) + } + + // copy from old memory + new_memory, used_blocks := _region_get_block(region_iter, idx, new_block_count) + mem.copy(new_memory, old_memory, int(old_block_count * BLOCK_SIZE)) + + // zero any new memory + addon_section := mem.ptr_offset((^Allocation_Header)(new_memory), old_block_count) + new_blocks := used_blocks - old_block_count + mem.zero(addon_section, int(new_blocks) * BLOCK_SIZE) + + region_iter.hdr.free_blocks -= (used_blocks + 1) + + // Set free_list before freeing. + if alloc_is_free_list { + _region_assign_free_list(_local_region, new_memory, used_blocks) + } + + // free old memory + _region_local_free(alloc) + return new_memory +} + +_region_local_free :: proc(alloc: ^Allocation_Header) #no_bounds_check { + alloc := alloc + add_to_free_list := true + + _local_region.hdr.free_blocks += _get_block_count(alloc^) + 1 + + // try to merge with prev + if alloc.idx > 0 && _local_region.memory[alloc.prev].free_idx != NOT_FREE { + _local_region.memory[alloc.prev].next = alloc.next + _local_region.memory[alloc.next].prev = alloc.prev + alloc = &_local_region.memory[alloc.prev] + add_to_free_list = false + } + + // try to merge with next + if alloc.next < BLOCKS_PER_REGION - 1 && _local_region.memory[alloc.next].free_idx != NOT_FREE { + old_next := alloc.next + alloc.next = _local_region.memory[old_next].next + _local_region.memory[alloc.next].prev = alloc.idx + + if add_to_free_list { + _local_region.hdr.free_list[_local_region.memory[old_next].free_idx] = alloc.idx + alloc.free_idx = _local_region.memory[old_next].free_idx + } else { + // NOTE: We have aleady merged with prev, and now merged with next. + // Now, we are actually going to remove from the free_list. + _region_free_list_remove(_local_region, _local_region.memory[old_next].free_idx) + } + add_to_free_list = false + } + + // This is the only place where anything is appended to the free list. + if add_to_free_list { + fl := _local_region.hdr.free_list + alloc.free_idx = _local_region.hdr.free_list_len + fl[alloc.free_idx] = alloc.idx + _local_region.hdr.free_list_len += 1 + if int(_local_region.hdr.free_list_len) == len(fl) { + free_alloc := _get_allocation_header(mem.raw_data(_local_region.hdr.free_list)) + _region_resize(free_alloc, len(fl) * 2 * size_of(fl[0]), true) + } + } +} + +_region_assign_free_list :: proc(region: ^Region, memory: rawptr, blocks: u16) { + raw_free_list := transmute(mem.Raw_Slice)region.hdr.free_list + raw_free_list.len = int(blocks) * FREE_LIST_ENTRIES_PER_BLOCK + raw_free_list.data = memory + region.hdr.free_list = transmute([]u16)(raw_free_list) +} + +_region_retrieve_with_space :: proc(blocks: u16, local_idx: int = -1, back_idx: int = -1) -> (^Region, int) { + r: ^Region + idx: int + for r = global_regions; r != nil; r = r.hdr.next_region { + if idx == local_idx || idx < back_idx || r.hdr.free_blocks < blocks { + idx += 1 + continue + } + idx += 1 + local_addr: ^^Region = sync.atomic_load(&r.hdr.local_addr) + if local_addr != CURRENTLY_ACTIVE { + res := sync.atomic_compare_exchange_strong_explicit( + &r.hdr.local_addr, + local_addr, + CURRENTLY_ACTIVE, + .Acquire, + .Relaxed, + ) + if res == local_addr { + r.hdr.reset_addr = local_addr + return r, idx + } + } + } + + return _new_region(), idx +} + +_region_retrieve_from_addr :: proc(addr: rawptr) -> ^Region { + r: ^Region + for r = global_regions; r != nil; r = r.hdr.next_region { + if _region_contains_mem(r, addr) { + return r + } + } + unreachable() +} + +_region_get_block :: proc(region: ^Region, idx, blocks_needed: u16) -> (rawptr, u16) #no_bounds_check { + alloc := ®ion.memory[idx] + + assert(alloc.free_idx != NOT_FREE) + assert(alloc.next > 0) + + block_count := _get_block_count(alloc^) + if block_count - blocks_needed > BLOCK_SEGMENT_THRESHOLD { + _region_segment(region, alloc, blocks_needed, alloc.free_idx) + } else { + _region_free_list_remove(region, alloc.free_idx) + } + + alloc.free_idx = NOT_FREE + return mem.ptr_offset(alloc, 1), _get_block_count(alloc^) +} + +_region_segment :: proc(region: ^Region, alloc: ^Allocation_Header, blocks, new_free_idx: u16) #no_bounds_check { + old_next := alloc.next + alloc.next = alloc.idx + blocks + 1 + region.memory[old_next].prev = alloc.next + + // Initialize alloc.next allocation header here. + region.memory[alloc.next].prev = alloc.idx + region.memory[alloc.next].next = old_next + region.memory[alloc.next].idx = alloc.next + region.memory[alloc.next].free_idx = new_free_idx + + // Replace our original spot in the free_list with new segment. + region.hdr.free_list[new_free_idx] = alloc.next +} + +_region_get_local_idx :: proc() -> int { + idx: int + for r := global_regions; r != nil; r = r.hdr.next_region { + if r == _local_region { + return idx + } + idx += 1 + } + + return -1 +} + +_region_find_and_assign_local :: proc(alloc: ^Allocation_Header) { + // Find the region that contains this memory + if !_region_contains_mem(_local_region, alloc) { + _local_region = _region_retrieve_from_addr(alloc) + } + + // At this point, _local_region is set correctly. Spin until acquired + res: ^^Region + for res != &_local_region { + res = sync.atomic_compare_exchange_strong_explicit( + &_local_region.hdr.local_addr, + &_local_region, + CURRENTLY_ACTIVE, + .Acquire, + .Relaxed, + ) + } +} + +_region_contains_mem :: proc(r: ^Region, memory: rawptr) -> bool #no_bounds_check { + if r == nil { + return false + } + mem_int := uintptr(memory) + return mem_int >= uintptr(&r.memory[0]) && mem_int <= uintptr(&r.memory[BLOCKS_PER_REGION - 1]) +} + +_region_free_list_remove :: proc(region: ^Region, free_idx: u16) #no_bounds_check { + // pop, swap and update allocation hdr + if n := region.hdr.free_list_len - 1; free_idx != n { + region.hdr.free_list[free_idx] = region.hdr.free_list[n] + alloc_idx := region.hdr.free_list[free_idx] + region.memory[alloc_idx].free_idx = free_idx + } + region.hdr.free_list_len -= 1 +} + +// +// Direct mmap +// +_direct_mmap_alloc :: proc(size: int) -> rawptr { + mmap_size := _round_up_to_nearest(size + BLOCK_SIZE, PAGE_SIZE) + new_allocation := unix.sys_mmap(nil, uint(mmap_size), MMAP_PROT, MMAP_FLAGS, -1, 0) + if new_allocation < 0 && new_allocation > -4096 { + return nil + } + + alloc := (^Allocation_Header)(uintptr(new_allocation)) + alloc.requested = u64(size) // NOTE: requested = requested size + alloc.requested += IS_DIRECT_MMAP + return rawptr(mem.ptr_offset(alloc, 1)) +} + +_direct_mmap_resize :: proc(alloc: ^Allocation_Header, new_size: int) -> rawptr { + old_requested := int(alloc.requested & REQUESTED_MASK) + old_mmap_size := _round_up_to_nearest(old_requested + BLOCK_SIZE, PAGE_SIZE) + new_mmap_size := _round_up_to_nearest(new_size + BLOCK_SIZE, PAGE_SIZE) + if int(new_mmap_size) < MMAP_TO_REGION_SHRINK_THRESHOLD { + return _direct_mmap_to_region(alloc, new_size) + } else if old_requested == new_size { + return mem.ptr_offset(alloc, 1) + } + + new_allocation := unix.sys_mremap( + alloc, + uint(old_mmap_size), + uint(new_mmap_size), + unix.MREMAP_MAYMOVE, + ) + if new_allocation < 0 && new_allocation > -4096 { + return nil + } + + new_header := (^Allocation_Header)(uintptr(new_allocation)) + new_header.requested = u64(new_size) + new_header.requested += IS_DIRECT_MMAP + + if new_mmap_size > old_mmap_size { + // new section may not be pointer aligned, so cast to ^u8 + new_section := mem.ptr_offset((^u8)(new_header), old_requested + BLOCK_SIZE) + mem.zero(new_section, new_mmap_size - old_mmap_size) + } + return mem.ptr_offset(new_header, 1) + +} + +_direct_mmap_from_region :: proc(alloc: ^Allocation_Header, new_size: int) -> rawptr { + new_memory := _direct_mmap_alloc(new_size) + if new_memory != nil { + old_memory := mem.ptr_offset(alloc, 1) + mem.copy(new_memory, old_memory, int(_get_block_count(alloc^)) * BLOCK_SIZE) + } + _region_find_and_assign_local(alloc) + _region_local_free(alloc) + sync.atomic_store_explicit(&_local_region.hdr.local_addr, &_local_region, .Release) + return new_memory +} + +_direct_mmap_to_region :: proc(alloc: ^Allocation_Header, new_size: int) -> rawptr { + new_memory := heap_alloc(new_size) + if new_memory != nil { + mem.copy(new_memory, mem.ptr_offset(alloc, -1), new_size) + _direct_mmap_free(alloc) + } + return new_memory +} + +_direct_mmap_free :: proc(alloc: ^Allocation_Header) { + requested := int(alloc.requested & REQUESTED_MASK) + mmap_size := _round_up_to_nearest(requested + BLOCK_SIZE, PAGE_SIZE) + unix.sys_munmap(alloc, uint(mmap_size)) +} + +// +// Util +// + +_get_block_count :: #force_inline proc(alloc: Allocation_Header) -> u16 { + return alloc.next - alloc.idx - 1 +} + +_get_allocation_header :: #force_inline proc(raw_mem: rawptr) -> ^Allocation_Header { + return mem.ptr_offset((^Allocation_Header)(raw_mem), -1) +} + +_round_up_to_nearest :: #force_inline proc(size, round: int) -> int { + return (size-1) + round - (size-1) % round +} diff --git a/core/os/os2/heap_windows.odin b/core/os/os2/heap_windows.odin index 85ea2c56f..90f0ae110 100644 --- a/core/os/os2/heap_windows.odin +++ b/core/os/os2/heap_windows.odin @@ -99,7 +99,7 @@ _heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode, return nil, nil case .Query_Info: - return nil, nil + return nil, .Mode_Not_Implemented } return nil, nil diff --git a/core/os/os2/path.odin b/core/os/os2/path.odin index ee7d6e6f2..c27015862 100644 --- a/core/os/os2/path.odin +++ b/core/os/os2/path.odin @@ -1,5 +1,7 @@ package os2 +import "core:runtime" + Path_Separator :: _Path_Separator // OS-Specific Path_List_Separator :: _Path_List_Separator // OS-Specific @@ -7,21 +9,21 @@ is_path_separator :: proc(c: byte) -> bool { return _is_path_separator(c) } -mkdir :: proc(name: string, perm: File_Mode) -> Maybe(Path_Error) { +mkdir :: proc(name: string, perm: File_Mode) -> Error { return _mkdir(name, perm) } -mkdir_all :: proc(path: string, perm: File_Mode) -> Maybe(Path_Error) { +mkdir_all :: proc(path: string, perm: File_Mode) -> Error { return _mkdir_all(path, perm) } -remove_all :: proc(path: string) -> Maybe(Path_Error) { +remove_all :: proc(path: string) -> Error { return _remove_all(path) } -getwd :: proc(allocator := context.allocator) -> (dir: string, err: Error) { +getwd :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { return _getwd(allocator) } setwd :: proc(dir: string) -> (err: Error) { diff --git a/core/os/os2/path_linux.odin b/core/os/os2/path_linux.odin new file mode 100644 index 000000000..2f59d1f13 --- /dev/null +++ b/core/os/os2/path_linux.odin @@ -0,0 +1,247 @@ +//+private +package os2 + +import "core:strings" +import "core:strconv" +import "core:runtime" +import "core:sys/unix" + +_Path_Separator :: '/' +_Path_List_Separator :: ':' + +_S_IFMT :: 0o170000 // Type of file mask +_S_IFIFO :: 0o010000 // Named pipe (fifo) +_S_IFCHR :: 0o020000 // Character special +_S_IFDIR :: 0o040000 // Directory +_S_IFBLK :: 0o060000 // Block special +_S_IFREG :: 0o100000 // Regular +_S_IFLNK :: 0o120000 // Symbolic link +_S_IFSOCK :: 0o140000 // Socket + +_OPENDIR_FLAGS :: _O_RDONLY|_O_NONBLOCK|_O_DIRECTORY|_O_LARGEFILE|_O_CLOEXEC + +_is_path_separator :: proc(c: byte) -> bool { + return c == '/' +} + +_mkdir :: proc(path: string, perm: File_Mode) -> Error { + // NOTE: These modes would require sys_mknod, however, that would require + // additional arguments to this function. + if perm & (File_Mode_Named_Pipe | File_Mode_Device | File_Mode_Char_Device | File_Mode_Sym_Link) != 0 { + return .Invalid_Argument + } + + path_cstr, allocated := _name_to_cstring(path) + defer if allocated { + delete(path_cstr) + } + return _ok_or_error(unix.sys_mkdir(path_cstr, int(perm & 0o777))) +} + +_mkdir_all :: proc(path: string, perm: File_Mode) -> Error { + _mkdirat :: proc(dfd: int, path: []u8, perm: int, has_created: ^bool) -> Error { + if len(path) == 0 { + return _ok_or_error(unix.sys_close(dfd)) + } + i: int + for /**/; i < len(path) - 1 && path[i] != '/'; i += 1 {} + path[i] = 0 + new_dfd := unix.sys_openat(dfd, cstring(&path[0]), _OPENDIR_FLAGS) + switch new_dfd { + case -ENOENT: + if res := unix.sys_mkdirat(dfd, cstring(&path[0]), perm); res < 0 { + return _get_platform_error(res) + } + has_created^ = true + if new_dfd = unix.sys_openat(dfd, cstring(&path[0]), _OPENDIR_FLAGS); new_dfd < 0 { + return _get_platform_error(new_dfd) + } + fallthrough + case 0: + if res := unix.sys_close(dfd); res < 0 { + return _get_platform_error(res) + } + // skip consecutive '/' + for i += 1; i < len(path) && path[i] == '/'; i += 1 {} + return _mkdirat(new_dfd, path[i:], perm, has_created) + case: + return _get_platform_error(new_dfd) + } + unreachable() + } + + if perm & (File_Mode_Named_Pipe | File_Mode_Device | File_Mode_Char_Device | File_Mode_Sym_Link) != 0 { + return .Invalid_Argument + } + + // need something we can edit, and use to generate cstrings + allocated: bool + path_bytes: []u8 + if len(path) > _CSTRING_NAME_HEAP_THRESHOLD { + allocated = true + path_bytes = make([]u8, len(path) + 1) + } else { + path_bytes = make([]u8, len(path) + 1, context.temp_allocator) + } + defer if allocated { + delete(path_bytes) + } + + // NULL terminate the byte slice to make it a valid cstring + copy(path_bytes, path) + path_bytes[len(path)] = 0 + + dfd: int + if path_bytes[0] == '/' { + dfd = unix.sys_open("/", _OPENDIR_FLAGS) + path_bytes = path_bytes[1:] + } else { + dfd = unix.sys_open(".", _OPENDIR_FLAGS) + } + if dfd < 0 { + return _get_platform_error(dfd) + } + + has_created: bool + _mkdirat(dfd, path_bytes, int(perm & 0o777), &has_created) or_return + if has_created { + return nil + } + return .Exist + //return has_created ? nil : .Exist +} + +dirent64 :: struct { + d_ino: u64, + d_off: u64, + d_reclen: u16, + d_type: u8, + d_name: [1]u8, +} + +_remove_all :: proc(path: string) -> Error { + DT_DIR :: 4 + + _remove_all_dir :: proc(dfd: int) -> Error { + n := 64 + buf := make([]u8, n) + defer delete(buf) + + loop: for { + getdents_res := unix.sys_getdents64(dfd, &buf[0], n) + switch getdents_res { + case -EINVAL: + delete(buf) + n *= 2 + buf = make([]u8, n) + continue loop + case -4096..<0: + return _get_platform_error(getdents_res) + case 0: + break loop + } + + d: ^dirent64 + + for i := 0; i < getdents_res; i += int(d.d_reclen) { + d = (^dirent64)(rawptr(&buf[i])) + d_name_cstr := cstring(&d.d_name[0]) + + buf_len := uintptr(d.d_reclen) - offset_of(d.d_name) + + /* check for current directory (.) */ + #no_bounds_check if buf_len > 1 && d.d_name[0] == '.' && d.d_name[1] == 0 { + continue + } + + /* check for parent directory (..) */ + #no_bounds_check if buf_len > 2 && d.d_name[0] == '.' && d.d_name[1] == '.' && d.d_name[2] == 0 { + continue + } + + unlink_res: int + + switch d.d_type { + case DT_DIR: + new_dfd := unix.sys_openat(dfd, d_name_cstr, _OPENDIR_FLAGS) + if new_dfd < 0 { + return _get_platform_error(new_dfd) + } + defer unix.sys_close(new_dfd) + _remove_all_dir(new_dfd) or_return + unlink_res = unix.sys_unlinkat(dfd, d_name_cstr, int(unix.AT_REMOVEDIR)) + case: + unlink_res = unix.sys_unlinkat(dfd, d_name_cstr) + } + + if unlink_res < 0 { + return _get_platform_error(unlink_res) + } + } + } + return nil + } + + path_cstr, allocated := _name_to_cstring(path) + defer if allocated { + delete(path_cstr) + } + + fd := unix.sys_open(path_cstr, _OPENDIR_FLAGS) + switch fd { + case -ENOTDIR: + return _ok_or_error(unix.sys_unlink(path_cstr)) + case -4096..<0: + return _get_platform_error(fd) + } + + defer unix.sys_close(fd) + _remove_all_dir(fd) or_return + return _ok_or_error(unix.sys_rmdir(path_cstr)) +} + +_getwd :: proc(allocator: runtime.Allocator) -> (string, Error) { + // NOTE(tetra): I would use PATH_MAX here, but I was not able to find + // an authoritative value for it across all systems. + // The largest value I could find was 4096, so might as well use the page size. + // NOTE(jason): Avoiding libc, so just use 4096 directly + PATH_MAX :: 4096 + buf := make([dynamic]u8, PATH_MAX, allocator) + for { + #no_bounds_check res := unix.sys_getcwd(&buf[0], uint(len(buf))) + + if res >= 0 { + return strings.string_from_nul_terminated_ptr(&buf[0], len(buf)), nil + } + if res != -ERANGE { + return "", _get_platform_error(res) + } + resize(&buf, len(buf)+PATH_MAX) + } + unreachable() +} + +_setwd :: proc(dir: string) -> Error { + dir_cstr, allocated := _name_to_cstring(dir) + defer if allocated { + delete(dir_cstr) + } + return _ok_or_error(unix.sys_chdir(dir_cstr)) +} + +_get_full_path :: proc(fd: int, allocator := context.allocator) -> string { + PROC_FD_PATH :: "/proc/self/fd/" + + buf: [32]u8 + copy(buf[:], PROC_FD_PATH) + + strconv.itoa(buf[len(PROC_FD_PATH):], fd) + + fullpath: string + err: Error + if fullpath, err = _read_link_cstr(cstring(&buf[0]), allocator); err != nil || fullpath[0] != '/' { + return "" + } + return fullpath +} + diff --git a/core/os/os2/path_windows.odin b/core/os/os2/path_windows.odin index 607f56968..2dc667822 100644 --- a/core/os/os2/path_windows.odin +++ b/core/os/os2/path_windows.odin @@ -1,6 +1,10 @@ //+private package os2 +import win32 "core:sys/windows" +import "core:runtime" +import "core:strings" + _Path_Separator :: '\\' _Path_List_Separator :: ';' @@ -8,24 +12,151 @@ _is_path_separator :: proc(c: byte) -> bool { return c == '\\' || c == '/' } -_mkdir :: proc(name: string, perm: File_Mode) -> Maybe(Path_Error) { +_mkdir :: proc(name: string, perm: File_Mode) -> Error { + if !win32.CreateDirectoryW(_fix_long_path(name), nil) { + return _get_platform_error() + } return nil } -_mkdir_all :: proc(path: string, perm: File_Mode) -> Maybe(Path_Error) { - // TODO(bill): _mkdir_all for windows +_mkdir_all :: proc(path: string, perm: File_Mode) -> Error { + fix_root_directory :: proc(p: string) -> (s: string, allocated: bool, err: runtime.Allocator_Error) { + if len(p) == len(`\\?\c:`) { + if is_path_separator(p[0]) && is_path_separator(p[1]) && p[2] == '?' && is_path_separator(p[3]) && p[5] == ':' { + s = strings.concatenate_safe({p, `\`}, _file_allocator()) or_return + allocated = true + return + } + } + return p, false, nil + } + + dir, err := stat(path, _temp_allocator()) + if err == nil { + if dir.is_dir { + return nil + } + return .Exist + } + + i := len(path) + for i > 0 && is_path_separator(path[i-1]) { + i -= 1 + } + + j := i + for j > 0 && !is_path_separator(path[j-1]) { + j -= 1 + } + + if j > 1 { + new_path, allocated := fix_root_directory(path[:j-1]) or_return + defer if allocated { + delete(new_path, _file_allocator()) + } + mkdir_all(new_path, perm) or_return + } + + err = mkdir(path, perm) + if err != nil { + dir1, err1 := lstat(path, _temp_allocator()) + if err1 == nil && dir1.is_dir { + return nil + } + return err + } return nil } -_remove_all :: proc(path: string) -> Maybe(Path_Error) { +_remove_all :: proc(path: string) -> Error { // TODO(bill): _remove_all for windows return nil } -_getwd :: proc(allocator := context.allocator) -> (dir: string, err: Error) { +_getwd :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { + // TODO(bill) return "", nil } _setwd :: proc(dir: string) -> (err: Error) { + // TODO(bill) return nil } + + +can_use_long_paths: bool + +@(init) +init_long_path_support :: proc() { + // TODO(bill): init_long_path_support + // ADD THIS SHIT + // registry_path := win32.L(`Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem\LongPathsEnabled`) + can_use_long_paths = false +} + + +_fix_long_path_slice :: proc(path: string) -> []u16 { + return win32.utf8_to_utf16(_fix_long_path_internal(path)) +} + +_fix_long_path :: proc(path: string) -> win32.wstring { + return win32.utf8_to_wstring(_fix_long_path_internal(path)) +} + + +_fix_long_path_internal :: proc(path: string) -> string { + if can_use_long_paths { + return path + } + + // When using win32 to create a directory, the path + // cannot be too long that you cannot append an 8.3 + // file name, because MAX_PATH is 260, 260-12 = 248 + if len(path) < 248 { + return path + } + + // UNC paths do not need to be modified + if len(path) >= 2 && path[:2] == `\\` { + return path + } + + if !_is_abs(path) { // relative path + return path + } + + PREFIX :: `\\?` + path_buf := make([]byte, len(PREFIX)+len(path)+1, _temp_allocator()) + copy(path_buf, PREFIX) + n := len(path) + r, w := 0, len(PREFIX) + for r < n { + switch { + case is_path_separator(path[r]): + r += 1 + case path[r] == '.' && (r+1 == n || is_path_separator(path[r+1])): + // \.\ + r += 1 + case r+1 < n && path[r] == '.' && path[r+1] == '.' && (r+2 == n || is_path_separator(path[r+2])): + // Skip \..\ paths + return path + case: + path_buf[w] = '\\' + w += 1 + for r < n && !is_path_separator(path[r]) { + path_buf[w] = path[r] + r += 1 + w += 1 + } + } + } + + // Root directories require a trailing \ + if w == len(`\\?\c:`) { + path_buf[w] = '\\' + w += 1 + } + + return string(path_buf[:w]) + +} diff --git a/core/os/os2/pipe.odin b/core/os/os2/pipe.odin index c38f03f03..62f7ddf10 100644 --- a/core/os/os2/pipe.odin +++ b/core/os/os2/pipe.odin @@ -1,5 +1,5 @@ package os2 -pipe :: proc() -> (r, w: Handle, err: Error) { +pipe :: proc() -> (r, w: ^File, err: Error) { return _pipe() } diff --git a/core/os/os2/pipe_linux.odin b/core/os/os2/pipe_linux.odin new file mode 100644 index 000000000..b66ff9663 --- /dev/null +++ b/core/os/os2/pipe_linux.odin @@ -0,0 +1,7 @@ +//+private +package os2 + +_pipe :: proc() -> (r, w: ^File, err: Error) { + return nil, nil, nil +} + diff --git a/core/os/os2/pipe_windows.odin b/core/os/os2/pipe_windows.odin index 5570ca282..bab8b44f5 100644 --- a/core/os/os2/pipe_windows.odin +++ b/core/os/os2/pipe_windows.odin @@ -3,11 +3,11 @@ package os2 import win32 "core:sys/windows" -_pipe :: proc() -> (r, w: Handle, err: Error) { +_pipe :: proc() -> (r, w: ^File, err: Error) { p: [2]win32.HANDLE if !win32.CreatePipe(&p[0], &p[1], nil, 0) { - return 0, 0, Platform_Error{i32(win32.GetLastError())} + return nil, nil, _get_platform_error() } - return Handle(p[0]), Handle(p[1]), nil + return new_file(uintptr(p[0]), ""), new_file(uintptr(p[1]), ""), nil } diff --git a/core/os/os2/process.odin b/core/os/os2/process.odin index 028951fe3..db47e2f5b 100644 --- a/core/os/os2/process.odin +++ b/core/os/os2/process.odin @@ -1,6 +1,6 @@ package os2 -import sync "core:sync/sync2" +import "core:sync" import "core:time" import "core:runtime" @@ -46,7 +46,7 @@ Process :: struct { Process_Attributes :: struct { dir: string, env: []string, - files: []Handle, + files: []^File, sys: ^Process_Attributes_OS_Specific, } diff --git a/core/os/os2/stat.odin b/core/os/os2/stat.odin index 19f1453ff..24a01fb0a 100644 --- a/core/os/os2/stat.odin +++ b/core/os/os2/stat.odin @@ -1,6 +1,7 @@ package os2 import "core:time" +import "core:runtime" File_Info :: struct { fullpath: string, @@ -13,26 +14,26 @@ File_Info :: struct { access_time: time.Time, } -file_info_slice_delete :: proc(infos: []File_Info, allocator := context.allocator) { +file_info_slice_delete :: proc(infos: []File_Info, allocator: runtime.Allocator) { for i := len(infos)-1; i >= 0; i -= 1 { file_info_delete(infos[i], allocator) } delete(infos, allocator) } -file_info_delete :: proc(fi: File_Info, allocator := context.allocator) { +file_info_delete :: proc(fi: File_Info, allocator: runtime.Allocator) { delete(fi.fullpath, allocator) } -fstat :: proc(fd: Handle, allocator := context.allocator) -> (File_Info, Maybe(Path_Error)) { - return _fstat(fd, allocator) +fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) { + return _fstat(f, allocator) } -stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Maybe(Path_Error)) { +stat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) { return _stat(name, allocator) } -lstat :: proc(name: string, allocator := context.allocator) -> (File_Info, Maybe(Path_Error)) { +lstat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) { return _lstat(name, allocator) } diff --git a/core/os/os2/stat_linux.odin b/core/os/os2/stat_linux.odin new file mode 100644 index 000000000..b627cef15 --- /dev/null +++ b/core/os/os2/stat_linux.odin @@ -0,0 +1,152 @@ +//+private +package os2 + +import "core:time" +import "core:runtime" +import "core:sys/unix" +import "core:path/filepath" + +// File type +S_IFMT :: 0o170000 // Type of file mask +S_IFIFO :: 0o010000 // Named pipe (fifo) +S_IFCHR :: 0o020000 // Character special +S_IFDIR :: 0o040000 // Directory +S_IFBLK :: 0o060000 // Block special +S_IFREG :: 0o100000 // Regular +S_IFLNK :: 0o120000 // Symbolic link +S_IFSOCK :: 0o140000 // Socket + +// File mode +// Read, write, execute/search by owner +S_IRWXU :: 0o0700 // RWX mask for owner +S_IRUSR :: 0o0400 // R for owner +S_IWUSR :: 0o0200 // W for owner +S_IXUSR :: 0o0100 // X for owner + + // Read, write, execute/search by group +S_IRWXG :: 0o0070 // RWX mask for group +S_IRGRP :: 0o0040 // R for group +S_IWGRP :: 0o0020 // W for group +S_IXGRP :: 0o0010 // X for group + + // Read, write, execute/search by others +S_IRWXO :: 0o0007 // RWX mask for other +S_IROTH :: 0o0004 // R for other +S_IWOTH :: 0o0002 // W for other +S_IXOTH :: 0o0001 // X for other + +S_ISUID :: 0o4000 // Set user id on execution +S_ISGID :: 0o2000 // Set group id on execution +S_ISVTX :: 0o1000 // Directory restrcted delete + + +S_ISLNK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFLNK } +S_ISREG :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFREG } +S_ISDIR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFDIR } +S_ISCHR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFCHR } +S_ISBLK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFBLK } +S_ISFIFO :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFIFO } +S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK } + +F_OK :: 0 // Test for file existance +X_OK :: 1 // Test for execute permission +W_OK :: 2 // Test for write permission +R_OK :: 4 // Test for read permission + +@private +Unix_File_Time :: struct { + seconds: i64, + nanoseconds: i64, +} + +@private +_Stat :: struct { + device_id: u64, // ID of device containing file + serial: u64, // File serial number + nlink: u64, // Number of hard links + mode: u32, // Mode of the file + uid: u32, // User ID of the file's owner + gid: u32, // Group ID of the file's group + _padding: i32, // 32 bits of padding + rdev: u64, // Device ID, if device + size: i64, // Size of the file, in bytes + block_size: i64, // Optimal bllocksize for I/O + blocks: i64, // Number of 512-byte blocks allocated + + last_access: Unix_File_Time, // Time of last access + modified: Unix_File_Time, // Time of last modification + status_change: Unix_File_Time, // Time of last status change + + _reserve1, + _reserve2, + _reserve3: i64, +} + + +_fstat :: proc(f: ^File, allocator := context.allocator) -> (File_Info, Error) { + return _fstat_internal(f.impl.fd, allocator) +} + +_fstat_internal :: proc(fd: int, allocator: runtime.Allocator) -> (File_Info, Error) { + s: _Stat + result := unix.sys_fstat(fd, &s) + if result < 0 { + return {}, _get_platform_error(result) + } + + // TODO: As of Linux 4.11, the new statx syscall can retrieve creation_time + fi := File_Info { + fullpath = _get_full_path(fd, allocator), + name = "", + size = s.size, + mode = 0, + is_dir = S_ISDIR(s.mode), + modification_time = time.Time {s.modified.seconds}, + access_time = time.Time {s.last_access.seconds}, + creation_time = time.Time{0}, // regular stat does not provide this + } + + fi.name = filepath.base(fi.fullpath) + return fi, nil +} + +// NOTE: _stat and _lstat are using _fstat to avoid a race condition when populating fullpath +_stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Error) { + name_cstr, allocated := _name_to_cstring(name) + defer if allocated { + delete(name_cstr) + } + + fd := unix.sys_open(name_cstr, _O_RDONLY) + if fd < 0 { + return {}, _get_platform_error(fd) + } + defer unix.sys_close(fd) + return _fstat_internal(fd, allocator) +} + +_lstat :: proc(name: string, allocator := context.allocator) -> (File_Info, Error) { + name_cstr, allocated := _name_to_cstring(name) + defer if allocated { + delete(name_cstr) + } + fd := unix.sys_open(name_cstr, _O_RDONLY | _O_PATH | _O_NOFOLLOW) + if fd < 0 { + return {}, _get_platform_error(fd) + } + defer unix.sys_close(fd) + return _fstat_internal(fd, allocator) +} + +_same_file :: proc(fi1, fi2: File_Info) -> bool { + return fi1.fullpath == fi2.fullpath +} + +_stat_internal :: proc(name: string) -> (s: _Stat, res: int) { + name_cstr, allocated := _name_to_cstring(name) + defer if allocated { + delete(name_cstr) + } + res = unix.sys_stat(name_cstr, &s) + return +} diff --git a/core/os/os2/stat_windows.odin b/core/os/os2/stat_windows.odin index f46a9435c..5de5269d7 100644 --- a/core/os/os2/stat_windows.odin +++ b/core/os/os2/stat_windows.odin @@ -1,21 +1,22 @@ //+private package os2 +import "core:runtime" import "core:time" +import "core:strings" import win32 "core:sys/windows" -_fstat :: proc(fd: Handle, allocator := context.allocator) -> (File_Info, Maybe(Path_Error)) { - if fd == 0 { - return {}, Path_Error{err = .Invalid_Argument} +_fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) { + if f == nil || f.impl.fd == nil { + return {}, nil } - context.allocator = allocator - path, err := _cleanpath_from_handle(fd) + path, err := _cleanpath_from_handle(f, allocator) if err != nil { return {}, err } - h := win32.HANDLE(fd) + h := _handle(f) switch win32.GetFileType(h) { case win32.FILE_TYPE_PIPE, win32.FILE_TYPE_CHAR: fi: File_Info @@ -25,13 +26,13 @@ _fstat :: proc(fd: Handle, allocator := context.allocator) -> (File_Info, Maybe( return fi, nil } - return _file_info_from_get_file_information_by_handle(path, h) + return _file_info_from_get_file_information_by_handle(path, h, allocator) } -_stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Maybe(Path_Error)) { - return internal_stat(name, win32.FILE_FLAG_BACKUP_SEMANTICS) +_stat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) { + return internal_stat(name, win32.FILE_FLAG_BACKUP_SEMANTICS, allocator) } -_lstat :: proc(name: string, allocator := context.allocator) -> (File_Info, Maybe(Path_Error)) { - return internal_stat(name, win32.FILE_FLAG_BACKUP_SEMANTICS|win32.FILE_FLAG_OPEN_REPARSE_POINT) +_lstat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) { + return internal_stat(name, win32.FILE_FLAG_BACKUP_SEMANTICS|win32.FILE_FLAG_OPEN_REPARSE_POINT, allocator) } _same_file :: proc(fi1, fi2: File_Info) -> bool { return fi1.fullpath == fi2.fullpath @@ -39,50 +40,38 @@ _same_file :: proc(fi1, fi2: File_Info) -> bool { -_stat_errno :: proc(errno: win32.DWORD) -> Path_Error { - return Path_Error{err = Platform_Error{i32(errno)}} -} - -full_path_from_name :: proc(name: string, allocator := context.allocator) -> (path: string, err: Maybe(Path_Error)) { - context.allocator = allocator - +full_path_from_name :: proc(name: string, allocator: runtime.Allocator) -> (path: string, err: Error) { name := name if name == "" { name = "." } - p := win32.utf8_to_utf16(name, context.temp_allocator) - buf := make([dynamic]u16, 100) - for { - n := win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil) - if n == 0 { - delete(buf) - return "", _stat_errno(win32.GetLastError()) - } - if n <= u32(len(buf)) { - return win32.utf16_to_utf8(buf[:n]), nil - } - resize(&buf, len(buf)*2) - } + p := win32.utf8_to_utf16(name, _temp_allocator()) - return + n := win32.GetFullPathNameW(raw_data(p), 0, nil, nil) + if n == 0 { + return "", _get_platform_error() + } + buf := make([]u16, n+1, _temp_allocator()) + n = win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil) + if n == 0 { + return "", _get_platform_error() + } + return win32.utf16_to_utf8(buf[:n], allocator) } -internal_stat :: proc(name: string, create_file_attributes: u32, allocator := context.allocator) -> (fi: File_Info, e: Maybe(Path_Error)) { +internal_stat :: proc(name: string, create_file_attributes: u32, allocator: runtime.Allocator) -> (fi: File_Info, e: Error) { if len(name) == 0 { - return {}, Path_Error{err = .Not_Exist} + return {}, .Not_Exist } - context.allocator = allocator - - - wname := win32.utf8_to_wstring(_fix_long_path(name), context.temp_allocator) + wname := _fix_long_path(name) fa: win32.WIN32_FILE_ATTRIBUTE_DATA ok := win32.GetFileAttributesExW(wname, win32.GetFileExInfoStandard, &fa) if ok && fa.dwFileAttributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 { // Not a symlink - return _file_info_from_win32_file_attribute_data(&fa, name) + return _file_info_from_win32_file_attribute_data(&fa, name, allocator) } err := 0 if ok else win32.GetLastError() @@ -91,21 +80,21 @@ internal_stat :: proc(name: string, create_file_attributes: u32, allocator := co fd: win32.WIN32_FIND_DATAW sh := win32.FindFirstFileW(wname, &fd) if sh == win32.INVALID_HANDLE_VALUE { - e = Path_Error{err = Platform_Error{i32(win32.GetLastError())}} + e = _get_platform_error() return } win32.FindClose(sh) - return _file_info_from_win32_find_data(&fd, name) + return _file_info_from_win32_find_data(&fd, name, allocator) } h := win32.CreateFileW(wname, 0, 0, nil, win32.OPEN_EXISTING, create_file_attributes, nil) if h == win32.INVALID_HANDLE_VALUE { - e = Path_Error{err = Platform_Error{i32(win32.GetLastError())}} + e = _get_platform_error() return } defer win32.CloseHandle(h) - return _file_info_from_get_file_information_by_handle(name, h) + return _file_info_from_get_file_information_by_handle(name, h, allocator) } @@ -130,56 +119,40 @@ _cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 { } -_cleanpath_from_handle :: proc(fd: Handle) -> (string, Maybe(Path_Error)) { - if fd == 0 { - return "", Path_Error{err = .Invalid_Argument} +_cleanpath_from_handle :: proc(f: ^File, allocator: runtime.Allocator) -> (string, Error) { + if f == nil || f.impl.fd == nil { + return "", nil } - h := win32.HANDLE(fd) + h := _handle(f) - MAX_PATH := win32.DWORD(260) + 1 - buf: []u16 - for { - buf = make([]u16, MAX_PATH, context.temp_allocator) - err := win32.GetFinalPathNameByHandleW(h, raw_data(buf), MAX_PATH, 0) - switch err { - case win32.ERROR_PATH_NOT_FOUND, win32.ERROR_INVALID_PARAMETER: - return "", _stat_errno(err) - case win32.ERROR_NOT_ENOUGH_MEMORY: - MAX_PATH = MAX_PATH*2 + 1 - continue - } - break + n := win32.GetFinalPathNameByHandleW(h, nil, 0, 0) + if n == 0 { + return "", _get_platform_error() } - return _cleanpath_from_buf(buf), nil + buf := make([]u16, max(n, 260)+1, _temp_allocator()) + n = win32.GetFinalPathNameByHandleW(h, raw_data(buf), u32(len(buf)), 0) + return _cleanpath_from_buf(buf[:n], allocator) } -_cleanpath_from_handle_u16 :: proc(fd: Handle) -> ([]u16, Maybe(Path_Error)) { - if fd == 0 { - return nil, Path_Error{err = .Invalid_Argument} +_cleanpath_from_handle_u16 :: proc(f: ^File) -> ([]u16, Error) { + if f == nil || f.impl.fd == nil { + return nil, nil } - h := win32.HANDLE(fd) + h := _handle(f) - MAX_PATH := win32.DWORD(260) + 1 - buf: []u16 - for { - buf = make([]u16, MAX_PATH, context.temp_allocator) - err := win32.GetFinalPathNameByHandleW(h, raw_data(buf), MAX_PATH, 0) - switch err { - case win32.ERROR_PATH_NOT_FOUND, win32.ERROR_INVALID_PARAMETER: - return nil, _stat_errno(err) - case win32.ERROR_NOT_ENOUGH_MEMORY: - MAX_PATH = MAX_PATH*2 + 1 - continue - } - break + n := win32.GetFinalPathNameByHandleW(h, nil, 0, 0) + if n == 0 { + return nil, _get_platform_error() } - return _cleanpath_strip_prefix(buf), nil + buf := make([]u16, max(n, 260)+1, _temp_allocator()) + n = win32.GetFinalPathNameByHandleW(h, raw_data(buf), u32(len(buf)), 0) + return _cleanpath_strip_prefix(buf[:n]), nil } -_cleanpath_from_buf :: proc(buf: []u16) -> string { +_cleanpath_from_buf :: proc(buf: []u16, allocator: runtime.Allocator) -> (string, runtime.Allocator_Error) { buf := buf buf = _cleanpath_strip_prefix(buf) - return win32.utf16_to_utf8(buf, context.allocator) + return win32.utf16_to_utf8(buf, allocator) } @@ -221,15 +194,15 @@ file_type_mode :: proc(h: win32.HANDLE) -> File_Mode { -_file_mode_from_file_attributes :: proc(FileAttributes: win32.DWORD, h: win32.HANDLE, ReparseTag: win32.DWORD) -> (mode: File_Mode) { - if FileAttributes & win32.FILE_ATTRIBUTE_READONLY != 0 { +_file_mode_from_file_attributes :: proc(file_attributes: win32.DWORD, h: win32.HANDLE, ReparseTag: win32.DWORD) -> (mode: File_Mode) { + if file_attributes & win32.FILE_ATTRIBUTE_READONLY != 0 { mode |= 0o444 } else { mode |= 0o666 } is_sym := false - if FileAttributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 { + if file_attributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 { is_sym = false } else { is_sym = ReparseTag == win32.IO_REPARSE_TAG_SYMLINK || ReparseTag == win32.IO_REPARSE_TAG_MOUNT_POINT @@ -238,7 +211,7 @@ _file_mode_from_file_attributes :: proc(FileAttributes: win32.DWORD, h: win32.HA if is_sym { mode |= File_Mode_Sym_Link } else { - if FileAttributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 { + if file_attributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 { mode |= 0o111 | File_Mode_Dir } @@ -251,7 +224,7 @@ _file_mode_from_file_attributes :: proc(FileAttributes: win32.DWORD, h: win32.HA } -_file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_DATA, name: string) -> (fi: File_Info, e: Maybe(Path_Error)) { +_file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_DATA, name: string, allocator: runtime.Allocator) -> (fi: File_Info, e: Error) { fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow) fi.mode |= _file_mode_from_file_attributes(d.dwFileAttributes, nil, 0) @@ -261,14 +234,14 @@ _file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime)) fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime)) - fi.fullpath, e = full_path_from_name(name) + fi.fullpath, e = full_path_from_name(name, allocator) fi.name = basename(fi.fullpath) return } -_file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string) -> (fi: File_Info, e: Maybe(Path_Error)) { +_file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string, allocator: runtime.Allocator) -> (fi: File_Info, e: Error) { fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow) fi.mode |= _file_mode_from_file_attributes(d.dwFileAttributes, nil, 0) @@ -278,17 +251,17 @@ _file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime)) fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime)) - fi.fullpath, e = full_path_from_name(name) + fi.fullpath, e = full_path_from_name(name, allocator) fi.name = basename(fi.fullpath) return } -_file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HANDLE) -> (File_Info, Maybe(Path_Error)) { +_file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HANDLE, allocator: runtime.Allocator) -> (File_Info, Error) { d: win32.BY_HANDLE_FILE_INFORMATION if !win32.GetFileInformationByHandle(h, &d) { - return {}, _stat_errno(win32.GetLastError()) + return {}, _get_platform_error() } @@ -296,7 +269,7 @@ _file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HA if !win32.GetFileInformationByHandleEx(h, .FileAttributeTagInfo, &ti, size_of(ti)) { err := win32.GetLastError() if err != win32.ERROR_INVALID_PARAMETER { - return {}, _stat_errno(err) + return {}, Platform_Error(err) } // Indicate this is a symlink on FAT file systems ti.ReparseTag = 0 @@ -318,58 +291,83 @@ _file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HA return fi, nil } -_is_abs :: proc(path: string) -> bool { - if len(path) > 0 && path[0] == '/' { - return true + + +reserved_names := [?]string{ + "CON", "PRN", "AUX", "NUL", + "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", + "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", +} + +_is_reserved_name :: proc(path: string) -> bool { + if len(path) == 0 { + return false } - if len(path) > 2 { - switch path[0] { - case 'A'..='Z', 'a'..='z': - return path[1] == ':' && is_path_separator(path[2]) + for reserved in reserved_names { + if strings.equal_fold(path, reserved) { + return true } } return false } -_fix_long_path :: proc(path: string) -> string { - if len(path) < 248 { - return path - } +_is_UNC :: proc(path: string) -> bool { + return _volume_name_len(path) > 2 +} - if len(path) >= 2 && path[:2] == `\\` { - return path - } - if !_is_abs(path) { - return path - } +_volume_name_len :: proc(path: string) -> int { + if ODIN_OS == .Windows { + if len(path) < 2 { + return 0 + } + c := path[0] + if path[1] == ':' { + switch c { + case 'a'..='z', 'A'..='Z': + return 2 + } + } - prefix :: `\\?` - - path_buf := make([]byte, len(prefix)+len(path)+len(`\`), context.temp_allocator) - copy(path_buf, prefix) - n := len(path) - r, w := 0, len(prefix) - for r < n { - switch { - case is_path_separator(path[r]): - r += 1 - case path[r] == '.' && (r+1 == n || is_path_separator(path[r+1])): - r += 1 - case r+1 < n && path[r] == '.' && path[r+1] == '.' && (r+2 == n || is_path_separator(path[r+2])): - return path - case: - path_buf[w] = '\\' - w += 1 - for ; r < n && !is_path_separator(path[r]); r += 1 { - path_buf[w] = path[r] - w += 1 + // URL: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx + if l := len(path); l >= 5 && _is_path_separator(path[0]) && _is_path_separator(path[1]) && + !_is_path_separator(path[2]) && path[2] != '.' { + for n := 3; n < l-1; n += 1 { + if _is_path_separator(path[n]) { + n += 1 + if !_is_path_separator(path[n]) { + if path[n] == '.' { + break + } + } + for ; n < l; n += 1 { + if _is_path_separator(path[n]) { + break + } + } + return n + } + break } } } - - if w == len(`\\?\c:`) { - path_buf[w] = '\\' - w += 1 - } - return string(path_buf[:w]) + return 0 } + + +_is_abs :: proc(path: string) -> bool { + if _is_reserved_name(path) { + return true + } + l := _volume_name_len(path) + if l == 0 { + return false + } + + path := path + path = path[l:] + if path == "" { + return false + } + return is_path_separator(path[0]) +} + diff --git a/core/os/os2/temp_file.odin b/core/os/os2/temp_file.odin index 8ff0e1656..b05c186a0 100644 --- a/core/os/os2/temp_file.odin +++ b/core/os/os2/temp_file.odin @@ -1,14 +1,15 @@ package os2 +import "core:runtime" -create_temp :: proc(dir, pattern: string) -> (Handle, Error) { +create_temp :: proc(dir, pattern: string) -> (^File, Error) { return _create_temp(dir, pattern) } -mkdir_temp :: proc(dir, pattern: string, allocator := context.allocator) -> (string, Error) { - return _mkdir_temp(dir, pattern) +mkdir_temp :: proc(dir, pattern: string, allocator: runtime.Allocator) -> (string, Error) { + return _mkdir_temp(dir, pattern, allocator) } -temp_dir :: proc(allocator := context.allocator) -> string { +temp_dir :: proc(allocator: runtime.Allocator) -> (string, Error) { return _temp_dir(allocator) } diff --git a/core/os/os2/temp_file_linux.odin b/core/os/os2/temp_file_linux.odin new file mode 100644 index 000000000..201fb0e93 --- /dev/null +++ b/core/os/os2/temp_file_linux.odin @@ -0,0 +1,20 @@ +//+private +package os2 + +import "core:runtime" + + +_create_temp :: proc(dir, pattern: string) -> (^File, Error) { + //TODO + return nil, nil +} + +_mkdir_temp :: proc(dir, pattern: string, allocator: runtime.Allocator) -> (string, Error) { + //TODO + return "", nil +} + +_temp_dir :: proc(allocator: runtime.Allocator) -> (string, Error) { + //TODO + return "", nil +} diff --git a/core/os/os2/temp_file_windows.odin b/core/os/os2/temp_file_windows.odin index 43f9f43b4..08837f7f0 100644 --- a/core/os/os2/temp_file_windows.odin +++ b/core/os/os2/temp_file_windows.odin @@ -1,29 +1,29 @@ //+private package os2 +import "core:runtime" import win32 "core:sys/windows" -_create_temp :: proc(dir, pattern: string) -> (Handle, Error) { - return 0, nil +_create_temp :: proc(dir, pattern: string) -> (^File, Error) { + return nil, nil } -_mkdir_temp :: proc(dir, pattern: string, allocator := context.allocator) -> (string, Error) { +_mkdir_temp :: proc(dir, pattern: string, allocator: runtime.Allocator) -> (string, Error) { return "", nil } -_temp_dir :: proc(allocator := context.allocator) -> string { - b := make([dynamic]u16, u32(win32.MAX_PATH), context.temp_allocator) - for { - n := win32.GetTempPathW(u32(len(b)), raw_data(b)) - if n > u32(len(b)) { - resize(&b, int(n)) - continue - } - if n == 3 && b[1] == ':' && b[2] == '\\' { - - } else if n > 0 && b[n-1] == '\\' { - n -= 1 - } - return win32.utf16_to_utf8(b[:n], allocator) +_temp_dir :: proc(allocator: runtime.Allocator) -> (string, runtime.Allocator_Error) { + n := win32.GetTempPathW(0, nil) + if n == 0 { + return "", nil } + b := make([]u16, max(win32.MAX_PATH, n), _temp_allocator()) + n = win32.GetTempPathW(u32(len(b)), raw_data(b)) + + if n == 3 && b[1] == ':' && b[2] == '\\' { + + } else if n > 0 && b[n-1] == '\\' { + n -= 1 + } + return win32.utf16_to_utf8(b[:n], allocator) } diff --git a/core/os/os2/user.odin b/core/os/os2/user.odin index 6dd99c621..1fb653b85 100644 --- a/core/os/os2/user.odin +++ b/core/os/os2/user.odin @@ -1,18 +1,19 @@ package os2 import "core:strings" +import "core:runtime" -user_cache_dir :: proc(allocator := context.allocator) -> (dir: string, is_defined: bool) { - switch ODIN_OS { - case "windows": +user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { + #partial switch ODIN_OS { + case .Windows: dir = get_env("LocalAppData") if dir != "" { - dir = strings.clone(dir, allocator) + dir = strings.clone_safe(dir, allocator) or_return } - case "darwin": + case .Darwin: dir = get_env("HOME") if dir != "" { - dir = strings.concatenate({dir, "/Library/Caches"}, allocator) + dir = strings.concatenate_safe({dir, "/Library/Caches"}, allocator) or_return } case: // All other UNIX systems dir = get_env("XDG_CACHE_HOME") @@ -21,24 +22,26 @@ user_cache_dir :: proc(allocator := context.allocator) -> (dir: string, is_defin if dir == "" { return } - dir = strings.concatenate({dir, "/.cache"}, allocator) + dir = strings.concatenate_safe({dir, "/.cache"}, allocator) or_return } } - is_defined = dir != "" + if dir == "" { + err = .Invalid_Path + } return } -user_config_dir :: proc(allocator := context.allocator) -> (dir: string, is_defined: bool) { - switch ODIN_OS { - case "windows": +user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { + #partial switch ODIN_OS { + case .Windows: dir = get_env("AppData") if dir != "" { - dir = strings.clone(dir, allocator) + dir = strings.clone_safe(dir, allocator) or_return } - case "darwin": + case .Darwin: dir = get_env("HOME") if dir != "" { - dir = strings.concatenate({dir, "/Library/Application Support"}, allocator) + dir = strings.concatenate_safe({dir, "/Library/Application Support"}, allocator) or_return } case: // All other UNIX systems dir = get_env("XDG_CACHE_HOME") @@ -47,22 +50,24 @@ user_config_dir :: proc(allocator := context.allocator) -> (dir: string, is_defi if dir == "" { return } - dir = strings.concatenate({dir, "/.config"}, allocator) + dir = strings.concatenate_safe({dir, "/.config"}, allocator) or_return } } - is_defined = dir != "" + if dir == "" { + err = .Invalid_Path + } return } -user_home_dir :: proc() -> (dir: string, is_defined: bool) { +user_home_dir :: proc() -> (dir: string, err: Error) { env := "HOME" - switch ODIN_OS { - case "windows": + #partial switch ODIN_OS { + case .Windows: env = "USERPROFILE" } if v := get_env(env); v != "" { - return v, true + return v, nil } - return "", false + return "", .Invalid_Path } diff --git a/core/os/os_darwin.odin b/core/os/os_darwin.odin index b32453a5d..b5e67558c 100644 --- a/core/os/os_darwin.odin +++ b/core/os/os_darwin.odin @@ -163,9 +163,6 @@ O_SYNC :: 0x0080 O_ASYNC :: 0x0040 O_CLOEXEC :: 0x1000000 -SEEK_SET :: 0 -SEEK_CUR :: 1 -SEEK_END :: 2 SEEK_DATA :: 3 SEEK_HOLE :: 4 SEEK_MAX :: SEEK_HOLE @@ -260,13 +257,13 @@ S_ISUID :: 0o4000 // Set user id on execution S_ISGID :: 0o2000 // Set group id on execution S_ISVTX :: 0o1000 // Directory restrcted delete -S_ISLNK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFLNK } -S_ISREG :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFREG } -S_ISDIR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFDIR } -S_ISCHR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFCHR } -S_ISBLK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFBLK } -S_ISFIFO :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFIFO } -S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK } +S_ISLNK :: #force_inline proc(m: u16) -> bool { return (m & S_IFMT) == S_IFLNK } +S_ISREG :: #force_inline proc(m: u16) -> bool { return (m & S_IFMT) == S_IFREG } +S_ISDIR :: #force_inline proc(m: u16) -> bool { return (m & S_IFMT) == S_IFDIR } +S_ISCHR :: #force_inline proc(m: u16) -> bool { return (m & S_IFMT) == S_IFCHR } +S_ISBLK :: #force_inline proc(m: u16) -> bool { return (m & S_IFMT) == S_IFBLK } +S_ISFIFO :: #force_inline proc(m: u16) -> bool { return (m & S_IFMT) == S_IFIFO } +S_ISSOCK :: #force_inline proc(m: u16) -> bool { return (m & S_IFMT) == S_IFSOCK } R_OK :: 4 // Test for read permission W_OK :: 2 // Test for write permission @@ -279,7 +276,7 @@ foreign libc { @(link_name="__error") __error :: proc() -> ^int --- @(link_name="open") _unix_open :: proc(path: cstring, flags: i32, mode: u16) -> Handle --- - @(link_name="close") _unix_close :: proc(handle: Handle) --- + @(link_name="close") _unix_close :: proc(handle: Handle) -> c.int --- @(link_name="read") _unix_read :: proc(handle: Handle, buffer: rawptr, count: int) -> int --- @(link_name="write") _unix_write :: proc(handle: Handle, buffer: rawptr, count: int) -> int --- @(link_name="lseek") _unix_lseek :: proc(fs: Handle, offset: int, whence: int) -> int --- @@ -290,13 +287,21 @@ foreign libc { @(link_name="fstat64") _unix_fstat :: proc(fd: Handle, stat: ^OS_Stat) -> c.int --- @(link_name="readlink") _unix_readlink :: proc(path: cstring, buf: ^byte, bufsiz: c.size_t) -> c.ssize_t --- @(link_name="access") _unix_access :: proc(path: cstring, mask: int) -> int --- - @(link_name="fdopendir$INODE64") _unix_fdopendir :: proc(fd: Handle) -> Dir --- + + @(link_name="fdopendir$INODE64") _unix_fdopendir_amd64 :: proc(fd: Handle) -> Dir --- + @(link_name="readdir_r$INODE64") _unix_readdir_r_amd64 :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int --- + @(link_name="fdopendir") _unix_fdopendir_arm64 :: proc(fd: Handle) -> Dir --- + @(link_name="readdir_r") _unix_readdir_r_arm64 :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int --- + @(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int --- @(link_name="rewinddir") _unix_rewinddir :: proc(dirp: Dir) --- - @(link_name="readdir_r$INODE64") _unix_readdir_r :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int --- - @(link_name="fcntl") _unix_fcntl :: proc(fd: Handle, cmd: c.int, buf: ^byte) -> c.int --- - @(link_name="fchmod") _unix_fchmod :: proc(fildes: Handle, mode: u16) -> c.int ---; + @(link_name="__fcntl") _unix__fcntl :: proc(fd: Handle, cmd: c.int, buf: ^byte) -> c.int --- + + @(link_name="rename") _unix_rename :: proc(old: cstring, new: cstring) -> c.int --- + @(link_name="remove") _unix_remove :: proc(path: cstring) -> c.int --- + + @(link_name="fchmod") _unix_fchmod :: proc(fd: Handle, mode: u16) -> c.int --- @(link_name="malloc") _unix_malloc :: proc(size: int) -> rawptr --- @(link_name="calloc") _unix_calloc :: proc(num, size: int) -> rawptr --- @@ -305,13 +310,22 @@ foreign libc { @(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring --- @(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring --- @(link_name="chdir") _unix_chdir :: proc(buf: cstring) -> c.int --- + @(link_name="mkdir") _unix_mkdir :: proc(buf: cstring, mode: u16) -> c.int --- @(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr --- - @(link_name="strerror") _darwin_string_error :: proc(num : c.int) -> cstring ---; + @(link_name="strerror") _darwin_string_error :: proc(num : c.int) -> cstring --- @(link_name="exit") _unix_exit :: proc(status: c.int) -> ! --- } +when ODIN_ARCH != .arm64 { + _unix_fdopendir :: proc {_unix_fdopendir_amd64} + _unix_readdir_r :: proc {_unix_readdir_r_amd64} +} else { + _unix_fdopendir :: proc {_unix_fdopendir_arm64} + _unix_readdir_r :: proc {_unix_readdir_r_arm64} +} + foreign dl { @(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: int) -> rawptr --- @(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr --- @@ -324,18 +338,17 @@ get_last_error :: proc() -> int { } get_last_error_string :: proc() -> string { - return cast(string)_darwin_string_error(cast(c.int)get_last_error()); + return cast(string)_darwin_string_error(cast(c.int)get_last_error()) } open :: proc(path: string, flags: int = O_RDWR, mode: int = 0) -> (Handle, Errno) { - cstr := strings.clone_to_cstring(path) + cstr := strings.clone_to_cstring(path, context.temp_allocator) handle := _unix_open(cstr, i32(flags), u16(mode)) - delete(cstr) if handle == -1 { return INVALID_HANDLE, 1 } -when ODIN_OS == "darwin" && ODIN_ARCH == "arm64" { +when ODIN_OS == .Darwin && ODIN_ARCH == .arm64 { if mode != 0 { err := fchmod(handle, cast(u16)mode) if err != 0 { @@ -348,12 +361,12 @@ when ODIN_OS == "darwin" && ODIN_ARCH == "arm64" { return handle, 0 } -fchmod :: proc(fildes: Handle, mode: u16) -> Errno { - return cast(Errno)_unix_fchmod(fildes, mode) +fchmod :: proc(fd: Handle, mode: u16) -> Errno { + return cast(Errno)_unix_fchmod(fd, mode) } -close :: proc(fd: Handle) { - _unix_close(fd) +close :: proc(fd: Handle) -> bool { + return _unix_close(fd) == 0 } write :: proc(fd: Handle, data: []u8) -> (int, Errno) { @@ -412,6 +425,70 @@ is_path_separator :: proc(r: rune) -> bool { return r == '/' } +is_file_handle :: proc(fd: Handle) -> bool { + s, err := _fstat(fd) + if err != ERROR_NONE { + return false + } + return S_ISREG(s.mode) +} + +is_file_path :: proc(path: string, follow_links: bool = true) -> bool { + s: OS_Stat + err: Errno + if follow_links { + s, err = _stat(path) + } else { + s, err = _lstat(path) + } + if err != ERROR_NONE { + return false + } + return S_ISREG(s.mode) +} + + +is_dir_handle :: proc(fd: Handle) -> bool { + s, err := _fstat(fd) + if err != ERROR_NONE { + return false + } + return S_ISDIR(s.mode) +} + +is_dir_path :: proc(path: string, follow_links: bool = true) -> bool { + s: OS_Stat + err: Errno + if follow_links { + s, err = _stat(path) + } else { + s, err = _lstat(path) + } + if err != ERROR_NONE { + return false + } + return S_ISDIR(s.mode) +} + +is_file :: proc {is_file_path, is_file_handle} +is_dir :: proc {is_dir_path, is_dir_handle} + +exists :: proc(path: string) -> bool { + cpath := strings.clone_to_cstring(path, context.temp_allocator) + res := _unix_access(cpath, O_RDONLY) + return res == 0 +} + +rename :: proc(old: string, new: string) -> bool { + old_cstr := strings.clone_to_cstring(old, context.temp_allocator) + new_cstr := strings.clone_to_cstring(new, context.temp_allocator) + return _unix_rename(old_cstr, new_cstr) != -1 +} + +remove :: proc(path: string) -> bool { + path_cstr := strings.clone_to_cstring(path, context.temp_allocator) + return _unix_remove(path_cstr) != -1 +} @private _stat :: proc(path: string) -> (OS_Stat, Errno) { @@ -474,7 +551,7 @@ _rewinddir :: proc(dirp: Dir) { _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) { result: ^Dirent rc := _unix_readdir_r(dirp, &entry, &result) - + if rc != 0 { err = Errno(get_last_error()) return @@ -514,7 +591,7 @@ _readlink :: proc(path: string) -> (string, Errno) { absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) { buf : [256]byte - res := _unix_fcntl(fd, F_GETPATH, &buf[0]) + res := _unix__fcntl(fd, F_GETPATH, &buf[0]) if res != 0 { return "", Errno(get_last_error()) } @@ -553,19 +630,26 @@ heap_alloc :: proc(size: int) -> rawptr { return _unix_calloc(1, size) } heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr { + // NOTE: _unix_realloc doesn't guarantee new memory will be zeroed on + // POSIX platforms. Ensure your caller takes this into account. return _unix_realloc(ptr, new_size) } heap_free :: proc(ptr: rawptr) { _unix_free(ptr) } -getenv :: proc(name: string) -> (string, bool) { - path_str := strings.clone_to_cstring(name, context.temp_allocator) +lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { + path_str := strings.clone_to_cstring(key, context.temp_allocator) cstr := _unix_getenv(path_str) if cstr == nil { return "", false } - return string(cstr), true + return strings.clone(string(cstr), allocator), true +} + +get_env :: proc(key: string, allocator := context.allocator) -> (value: string) { + value, _ = lookup_env(key, allocator) + return } get_current_directory :: proc() -> string { @@ -593,7 +677,17 @@ set_current_directory :: proc(path: string) -> (err: Errno) { return ERROR_NONE } +make_directory :: proc(path: string, mode: u16 = 0o775) -> Errno { + path_cstr := strings.clone_to_cstring(path, context.temp_allocator) + res := _unix_mkdir(path_cstr, mode) + if res == -1 { + return Errno(get_last_error()) + } + return ERROR_NONE +} + exit :: proc "contextless" (code: int) -> ! { + runtime._cleanup_runtime_contextless() _unix_exit(i32(code)) } diff --git a/core/os/os_freebsd.odin b/core/os/os_freebsd.odin index e9314b468..adf4f246f 100644 --- a/core/os/os_freebsd.odin +++ b/core/os/os_freebsd.odin @@ -7,465 +7,700 @@ import "core:runtime" import "core:strings" import "core:c" -Handle :: distinct i32; -File_Time :: distinct u64; -Errno :: distinct i32; -Syscall :: distinct i32; +Handle :: distinct i32 +File_Time :: distinct u64 +Errno :: distinct i32 -INVALID_HANDLE :: ~Handle(0); +INVALID_HANDLE :: ~Handle(0) -ERROR_NONE: Errno : 0; -EPERM: Errno : 1; -ENOENT: Errno : 2; -ESRCH: Errno : 3; -EINTR: Errno : 4; -EIO: Errno : 5; -ENXIO: Errno : 6; -E2BIG: Errno : 7; -ENOEXEC: Errno : 8; -EBADF: Errno : 9; -ECHILD: Errno : 10; -EBEADLK: Errno : 11; -ENOMEM: Errno : 12; -EACCESS: Errno : 13; -EFAULT: Errno : 14; -ENOTBLK: Errno : 15; -EBUSY: Errno : 16; -EEXIST: Errno : 17; -EXDEV: Errno : 18; -ENODEV: Errno : 19; -ENOTDIR: Errno : 20; -EISDIR: Errno : 21; -EINVAL: Errno : 22; -ENFILE: Errno : 23; -EMFILE: Errno : 24; -ENOTTY: Errno : 25; -ETXTBSY: Errno : 26; -EFBIG: Errno : 27; -ENOSPC: Errno : 28; -ESPIPE: Errno : 29; -EROFS: Errno : 30; -EMLINK: Errno : 31; -EPIPE: Errno : 32; -EDOM: Errno : 33; -ERANGE: Errno : 34; /* Result too large */ -EAGAIN: Errno : 35; -EINPROGRESS: Errno : 36; -EALREADY: Errno : 37; -ENOTSOCK: Errno : 38; -EDESTADDRREQ: Errno : 39; -EMSGSIZE: Errno : 40; -EPROTOTYPE: Errno : 41; -ENOPROTOOPT: Errno : 42; -EPROTONOSUPPORT: Errno : 43; -ESOCKTNOSUPPORT: Errno : 44; -EOPNOTSUPP: Errno : 45; -EPFNOSUPPORT: Errno : 46; -EAFNOSUPPORT: Errno : 47; -EADDRINUSE: Errno : 48; -EADDRNOTAVAIL: Errno : 49; -ENETDOWN: Errno : 50; -ENETUNREACH: Errno : 51; -ENETRESET: Errno : 52; -ECONNABORTED: Errno : 53; -ECONNRESET: Errno : 54; -ENOBUFS: Errno : 55; -EISCONN: Errno : 56; -ENOTCONN: Errno : 57; -ESHUTDOWN: Errno : 58; -ETIMEDOUT: Errno : 60; -ECONNREFUSED: Errno : 61; -ELOOP: Errno : 62; -ENAMETOOLING: Errno : 63; -EHOSTDOWN: Errno : 64; -EHOSTUNREACH: Errno : 65; -ENOTEMPTY: Errno : 66; -EPROCLIM: Errno : 67; -EUSERS: Errno : 68; -EDQUOT: Errno : 69; -ESTALE: Errno : 70; -EBADRPC: Errno : 72; -ERPCMISMATCH: Errno : 73; -EPROGUNAVAIL: Errno : 74; -EPROGMISMATCH: Errno : 75; -EPROCUNAVAIL: Errno : 76; -ENOLCK: Errno : 77; -ENOSYS: Errno : 78; -EFTYPE: Errno : 79; -EAUTH: Errno : 80; -ENEEDAUTH: Errno : 81; -EIDRM: Errno : 82; -ENOMSG: Errno : 83; -EOVERFLOW: Errno : 84; -ECANCELED: Errno : 85; -EILSEQ: Errno : 86; -ENOATTR: Errno : 87; -EDOOFUS: Errno : 88; -EBADMSG: Errno : 89; -EMULTIHOP: Errno : 90; -ENOLINK: Errno : 91; -EPROTO: Errno : 92; -ENOTCAPABLE: Errno : 93; -ECAPMODE: Errno : 94; -ENOTRECOVERABLE: Errno : 95; -EOWNERDEAD: Errno : 96; +ERROR_NONE: Errno : 0 +EPERM: Errno : 1 +ENOENT: Errno : 2 +ESRCH: Errno : 3 +EINTR: Errno : 4 +EIO: Errno : 5 +ENXIO: Errno : 6 +E2BIG: Errno : 7 +ENOEXEC: Errno : 8 +EBADF: Errno : 9 +ECHILD: Errno : 10 +EBEADLK: Errno : 11 +ENOMEM: Errno : 12 +EACCESS: Errno : 13 +EFAULT: Errno : 14 +ENOTBLK: Errno : 15 +EBUSY: Errno : 16 +EEXIST: Errno : 17 +EXDEV: Errno : 18 +ENODEV: Errno : 19 +ENOTDIR: Errno : 20 +EISDIR: Errno : 21 +EINVAL: Errno : 22 +ENFILE: Errno : 23 +EMFILE: Errno : 24 +ENOTTY: Errno : 25 +ETXTBSY: Errno : 26 +EFBIG: Errno : 27 +ENOSPC: Errno : 28 +ESPIPE: Errno : 29 +EROFS: Errno : 30 +EMLINK: Errno : 31 +EPIPE: Errno : 32 +EDOM: Errno : 33 +ERANGE: Errno : 34 /* Result too large */ +EAGAIN: Errno : 35 +EINPROGRESS: Errno : 36 +EALREADY: Errno : 37 +ENOTSOCK: Errno : 38 +EDESTADDRREQ: Errno : 39 +EMSGSIZE: Errno : 40 +EPROTOTYPE: Errno : 41 +ENOPROTOOPT: Errno : 42 +EPROTONOSUPPORT: Errno : 43 +ESOCKTNOSUPPORT: Errno : 44 +EOPNOTSUPP: Errno : 45 +EPFNOSUPPORT: Errno : 46 +EAFNOSUPPORT: Errno : 47 +EADDRINUSE: Errno : 48 +EADDRNOTAVAIL: Errno : 49 +ENETDOWN: Errno : 50 +ENETUNREACH: Errno : 51 +ENETRESET: Errno : 52 +ECONNABORTED: Errno : 53 +ECONNRESET: Errno : 54 +ENOBUFS: Errno : 55 +EISCONN: Errno : 56 +ENOTCONN: Errno : 57 +ESHUTDOWN: Errno : 58 +ETIMEDOUT: Errno : 60 +ECONNREFUSED: Errno : 61 +ELOOP: Errno : 62 +ENAMETOOLING: Errno : 63 +EHOSTDOWN: Errno : 64 +EHOSTUNREACH: Errno : 65 +ENOTEMPTY: Errno : 66 +EPROCLIM: Errno : 67 +EUSERS: Errno : 68 +EDQUOT: Errno : 69 +ESTALE: Errno : 70 +EBADRPC: Errno : 72 +ERPCMISMATCH: Errno : 73 +EPROGUNAVAIL: Errno : 74 +EPROGMISMATCH: Errno : 75 +EPROCUNAVAIL: Errno : 76 +ENOLCK: Errno : 77 +ENOSYS: Errno : 78 +EFTYPE: Errno : 79 +EAUTH: Errno : 80 +ENEEDAUTH: Errno : 81 +EIDRM: Errno : 82 +ENOMSG: Errno : 83 +EOVERFLOW: Errno : 84 +ECANCELED: Errno : 85 +EILSEQ: Errno : 86 +ENOATTR: Errno : 87 +EDOOFUS: Errno : 88 +EBADMSG: Errno : 89 +EMULTIHOP: Errno : 90 +ENOLINK: Errno : 91 +EPROTO: Errno : 92 +ENOTCAPABLE: Errno : 93 +ECAPMODE: Errno : 94 +ENOTRECOVERABLE: Errno : 95 +EOWNERDEAD: Errno : 96 -O_RDONLY :: 0x00000; -O_WRONLY :: 0x00001; -O_RDWR :: 0x00002; -O_CREATE :: 0x00040; -O_EXCL :: 0x00080; -O_NOCTTY :: 0x00100; -O_TRUNC :: 0x00200; -O_NONBLOCK :: 0x00800; -O_APPEND :: 0x00400; -O_SYNC :: 0x01000; -O_ASYNC :: 0x02000; -O_CLOEXEC :: 0x80000; +O_RDONLY :: 0x00000 +O_WRONLY :: 0x00001 +O_RDWR :: 0x00002 +O_CREATE :: 0x00040 +O_EXCL :: 0x00080 +O_NOCTTY :: 0x00100 +O_TRUNC :: 0x00200 +O_NONBLOCK :: 0x00800 +O_APPEND :: 0x00400 +O_SYNC :: 0x01000 +O_ASYNC :: 0x02000 +O_CLOEXEC :: 0x80000 -SEEK_SET :: 0; -SEEK_CUR :: 1; -SEEK_END :: 2; -SEEK_DATA :: 3; -SEEK_HOLE :: 4; -SEEK_MAX :: SEEK_HOLE; +SEEK_DATA :: 3 +SEEK_HOLE :: 4 +SEEK_MAX :: SEEK_HOLE // NOTE: These are OS specific! // Do not mix these up! -RTLD_LAZY :: 0x001; -RTLD_NOW :: 0x002; -//RTLD_BINDING_MASK :: 0x3; // Called MODEMASK in dlfcn.h -RTLD_GLOBAL :: 0x100; -RTLD_LOCAL :: 0x000; -RTLD_TRACE :: 0x200; -RTLD_NODELETE :: 0x01000; -RTLD_NOLOAD :: 0x02000; +RTLD_LAZY :: 0x001 +RTLD_NOW :: 0x002 +//RTLD_BINDING_MASK :: 0x3 // Called MODEMASK in dlfcn.h +RTLD_GLOBAL :: 0x100 +RTLD_LOCAL :: 0x000 +RTLD_TRACE :: 0x200 +RTLD_NODELETE :: 0x01000 +RTLD_NOLOAD :: 0x02000 -args := _alloc_command_line_arguments(); +MAX_PATH :: 1024 + +args := _alloc_command_line_arguments() Unix_File_Time :: struct { - seconds: i64, + seconds: time_t, nanoseconds: c.long, } -pid_t :: u32; +dev_t :: u64 +ino_t :: u64 +nlink_t :: u64 +off_t :: i64 +mode_t :: u16 +pid_t :: u32 +uid_t :: u32 +gid_t :: u32 +blkcnt_t :: i64 +blksize_t :: i32 +fflags_t :: u32 + +when ODIN_ARCH == .amd64 /* LP64 */ { + time_t :: i64 +} else { + time_t :: i32 +} + OS_Stat :: struct { - device_id: u64, - serial: u64, - nlink: u64, - mode: u32, + device_id: dev_t, + serial: ino_t, + nlink: nlink_t, + mode: mode_t, _padding0: i16, - uid: u32, - gid: u32, + uid: uid_t, + gid: gid_t, _padding1: i32, - rdev: u64, + rdev: dev_t, last_access: Unix_File_Time, modified: Unix_File_Time, status_change: Unix_File_Time, birthtime: Unix_File_Time, - size: i64, - blocks: i64, - block_size: i32, + size: off_t, + blocks: blkcnt_t, + block_size: blksize_t, - flags: u32, + flags: fflags_t, gen: u64, - lspare: i64, + lspare: [10]u64, } + +// since FreeBSD v12 +Dirent :: struct { + ino: ino_t, + off: off_t, + reclen: u16, + type: u8, + _pad0: u8, + namlen: u16, + _pad1: u16, + name: [256]byte, +} + +Dir :: distinct rawptr // DIR* + // File type -S_IFMT :: 0o170000; // Type of file mask -S_IFIFO :: 0o010000; // Named pipe (fifo) -S_IFCHR :: 0o020000; // Character special -S_IFDIR :: 0o040000; // Directory -S_IFBLK :: 0o060000; // Block special -S_IFREG :: 0o100000; // Regular -S_IFLNK :: 0o120000; // Symbolic link -S_IFSOCK :: 0o140000; // Socket -//S_ISVTX :: 0o001000; // Save swapped text even after use +S_IFMT :: 0o170000 // Type of file mask +S_IFIFO :: 0o010000 // Named pipe (fifo) +S_IFCHR :: 0o020000 // Character special +S_IFDIR :: 0o040000 // Directory +S_IFBLK :: 0o060000 // Block special +S_IFREG :: 0o100000 // Regular +S_IFLNK :: 0o120000 // Symbolic link +S_IFSOCK :: 0o140000 // Socket +//S_ISVTX :: 0o001000 // Save swapped text even after use // File mode // Read, write, execute/search by owner -S_IRWXU :: 0o0700; // RWX mask for owner -S_IRUSR :: 0o0400; // R for owner -S_IWUSR :: 0o0200; // W for owner -S_IXUSR :: 0o0100; // X for owner +S_IRWXU :: 0o0700 // RWX mask for owner +S_IRUSR :: 0o0400 // R for owner +S_IWUSR :: 0o0200 // W for owner +S_IXUSR :: 0o0100 // X for owner // Read, write, execute/search by group -S_IRWXG :: 0o0070; // RWX mask for group -S_IRGRP :: 0o0040; // R for group -S_IWGRP :: 0o0020; // W for group -S_IXGRP :: 0o0010; // X for group +S_IRWXG :: 0o0070 // RWX mask for group +S_IRGRP :: 0o0040 // R for group +S_IWGRP :: 0o0020 // W for group +S_IXGRP :: 0o0010 // X for group // Read, write, execute/search by others -S_IRWXO :: 0o0007; // RWX mask for other -S_IROTH :: 0o0004; // R for other -S_IWOTH :: 0o0002; // W for other -S_IXOTH :: 0o0001; // X for other +S_IRWXO :: 0o0007 // RWX mask for other +S_IROTH :: 0o0004 // R for other +S_IWOTH :: 0o0002 // W for other +S_IXOTH :: 0o0001 // X for other -S_ISUID :: 0o4000; // Set user id on execution -S_ISGID :: 0o2000; // Set group id on execution -S_ISVTX :: 0o1000; // Directory restrcted delete +S_ISUID :: 0o4000 // Set user id on execution +S_ISGID :: 0o2000 // Set group id on execution +S_ISVTX :: 0o1000 // Directory restrcted delete -S_ISLNK :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFLNK; -S_ISREG :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFREG; -S_ISDIR :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFDIR; -S_ISCHR :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFCHR; -S_ISBLK :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFBLK; -S_ISFIFO :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFIFO; -S_ISSOCK :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFSOCK; +S_ISLNK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFLNK } +S_ISREG :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFREG } +S_ISDIR :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFDIR } +S_ISCHR :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFCHR } +S_ISBLK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFBLK } +S_ISFIFO :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFIFO } +S_ISSOCK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFSOCK } -F_OK :: 0; // Test for file existance -X_OK :: 1; // Test for execute permission -W_OK :: 2; // Test for write permission -R_OK :: 4; // Test for read permission +F_OK :: 0 // Test for file existance +X_OK :: 1 // Test for execute permission +W_OK :: 2 // Test for write permission +R_OK :: 4 // Test for read permission foreign libc { - @(link_name="__error") __errno_location :: proc() -> ^int ---; - @(link_name="syscall") syscall :: proc(number: Syscall, #c_vararg args: ..any) -> int ---; + @(link_name="__error") __errno_location :: proc() -> ^int --- - @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle ---; - @(link_name="close") _unix_close :: proc(fd: Handle) -> c.int ---; - @(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---; - @(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---; - @(link_name="lseek64") _unix_seek :: proc(fd: Handle, offset: i64, whence: c.int) -> i64 ---; - @(link_name="gettid") _unix_gettid :: proc() -> u64 ---; - @(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int ---; - @(link_name="stat64") _unix_stat :: proc(path: cstring, stat: ^OS_Stat) -> c.int ---; - @(link_name="fstat") _unix_fstat :: proc(fd: Handle, stat: ^OS_Stat) -> c.int ---; - @(link_name="access") _unix_access :: proc(path: cstring, mask: c.int) -> c.int ---; + @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle --- + @(link_name="close") _unix_close :: proc(fd: Handle) -> c.int --- + @(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t --- + @(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t --- + @(link_name="lseek") _unix_seek :: proc(fd: Handle, offset: i64, whence: c.int) -> i64 --- + @(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int --- + @(link_name="stat") _unix_stat :: proc(path: cstring, stat: ^OS_Stat) -> c.int --- + @(link_name="lstat") _unix_lstat :: proc(path: cstring, sb: ^OS_Stat) -> c.int --- + @(link_name="fstat") _unix_fstat :: proc(fd: Handle, stat: ^OS_Stat) -> c.int --- + @(link_name="readlink") _unix_readlink :: proc(path: cstring, buf: ^byte, bufsiz: c.size_t) -> c.ssize_t --- + @(link_name="access") _unix_access :: proc(path: cstring, mask: c.int) -> c.int --- + @(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring --- + @(link_name="chdir") _unix_chdir :: proc(buf: cstring) -> c.int --- + @(link_name="rename") _unix_rename :: proc(old, new: cstring) -> c.int --- + @(link_name="unlink") _unix_unlink :: proc(path: cstring) -> c.int --- + @(link_name="rmdir") _unix_rmdir :: proc(path: cstring) -> c.int --- + @(link_name="mkdir") _unix_mkdir :: proc(path: cstring, mode: mode_t) -> c.int --- + + @(link_name="fdopendir") _unix_fdopendir :: proc(fd: Handle) -> Dir --- + @(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int --- + @(link_name="rewinddir") _unix_rewinddir :: proc(dirp: Dir) --- + @(link_name="readdir_r") _unix_readdir_r :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int --- - @(link_name="malloc") _unix_malloc :: proc(size: c.size_t) -> rawptr ---; - @(link_name="calloc") _unix_calloc :: proc(num, size: c.size_t) -> rawptr ---; - @(link_name="free") _unix_free :: proc(ptr: rawptr) ---; - @(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---; - @(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---; - @(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring ---; - @(link_name="chdir") _unix_chdir :: proc(buf: cstring) -> c.int ---; + @(link_name="malloc") _unix_malloc :: proc(size: c.size_t) -> rawptr --- + @(link_name="calloc") _unix_calloc :: proc(num, size: c.size_t) -> rawptr --- + @(link_name="free") _unix_free :: proc(ptr: rawptr) --- + @(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr --- + + @(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring --- + @(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr --- - @(link_name="exit") _unix_exit :: proc(status: c.int) -> ! ---; + @(link_name="exit") _unix_exit :: proc(status: c.int) -> ! --- } foreign dl { - @(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: c.int) -> rawptr ---; - @(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr ---; - @(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> c.int ---; - @(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---; + @(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: c.int) -> rawptr --- + @(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr --- + @(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> c.int --- + @(link_name="dlerror") _unix_dlerror :: proc() -> cstring --- - @(link_name="pthread_getthreadid_np") pthread_getthreadid_np :: proc() -> c.int ---; + @(link_name="pthread_getthreadid_np") pthread_getthreadid_np :: proc() -> c.int --- } is_path_separator :: proc(r: rune) -> bool { - return r == '/'; + return r == '/' } get_last_error :: proc() -> int { - return __errno_location()^; + return __errno_location()^ } open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) { - cstr := strings.clone_to_cstring(path); - handle := _unix_open(cstr, c.int(flags), c.int(mode)); - delete(cstr); + cstr := strings.clone_to_cstring(path, context.temp_allocator) + handle := _unix_open(cstr, c.int(flags), c.int(mode)) if handle == -1 { - return INVALID_HANDLE, Errno(get_last_error()); + return INVALID_HANDLE, Errno(get_last_error()) } - return handle, ERROR_NONE; + return handle, ERROR_NONE } close :: proc(fd: Handle) -> Errno { - result := _unix_close(fd); + result := _unix_close(fd) if result == -1 { - return Errno(get_last_error()); + return Errno(get_last_error()) } - return ERROR_NONE; + return ERROR_NONE } read :: proc(fd: Handle, data: []byte) -> (int, Errno) { - bytes_read := _unix_read(fd, &data[0], c.size_t(len(data))); + bytes_read := _unix_read(fd, &data[0], c.size_t(len(data))) if bytes_read == -1 { - return -1, Errno(get_last_error()); + return -1, Errno(get_last_error()) } - return int(bytes_read), ERROR_NONE; + return int(bytes_read), ERROR_NONE } write :: proc(fd: Handle, data: []byte) -> (int, Errno) { if len(data) == 0 { - return 0, ERROR_NONE; + return 0, ERROR_NONE } - bytes_written := _unix_write(fd, &data[0], c.size_t(len(data))); + bytes_written := _unix_write(fd, &data[0], c.size_t(len(data))) if bytes_written == -1 { - return -1, Errno(get_last_error()); + return -1, Errno(get_last_error()) } - return int(bytes_written), ERROR_NONE; + return int(bytes_written), ERROR_NONE } seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { - res := _unix_seek(fd, offset, c.int(whence)); + res := _unix_seek(fd, offset, c.int(whence)) if res == -1 { - return -1, Errno(get_last_error()); + return -1, Errno(get_last_error()) } - return res, ERROR_NONE; + return res, ERROR_NONE } file_size :: proc(fd: Handle) -> (i64, Errno) { - s, err := fstat(fd); + s, err := fstat(fd) if err != ERROR_NONE { - return -1, err; + return -1, err } - return s.size, ERROR_NONE; + return s.size, ERROR_NONE } -stdin: Handle = 0; -stdout: Handle = 1; -stderr: Handle = 2; - -last_write_time :: proc(fd: Handle) -> (File_Time, Errno) { - s, err := fstat(fd); - if err != ERROR_NONE { - return 0, err; +rename :: proc(old_path, new_path: string) -> Errno { + old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator) + new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator) + res := _unix_rename(old_path_cstr, new_path_cstr) + if res == -1 { + return Errno(get_last_error()) } - modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds; - return File_Time(modified), ERROR_NONE; + return ERROR_NONE +} + +remove :: proc(path: string) -> Errno { + path_cstr := strings.clone_to_cstring(path, context.temp_allocator) + res := _unix_unlink(path_cstr) + if res == -1 { + return Errno(get_last_error()) + } + return ERROR_NONE +} + +make_directory :: proc(path: string, mode: mode_t = 0o775) -> Errno { + path_cstr := strings.clone_to_cstring(path, context.temp_allocator) + res := _unix_mkdir(path_cstr, mode) + if res == -1 { + return Errno(get_last_error()) + } + return ERROR_NONE +} + +remove_directory :: proc(path: string) -> Errno { + path_cstr := strings.clone_to_cstring(path, context.temp_allocator) + res := _unix_rmdir(path_cstr) + if res == -1 { + return Errno(get_last_error()) + } + return ERROR_NONE +} + +is_file_handle :: proc(fd: Handle) -> bool { + s, err := _fstat(fd) + if err != ERROR_NONE { + return false + } + return S_ISREG(s.mode) +} + +is_file_path :: proc(path: string, follow_links: bool = true) -> bool { + s: OS_Stat + err: Errno + if follow_links { + s, err = _stat(path) + } else { + s, err = _lstat(path) + } + if err != ERROR_NONE { + return false + } + return S_ISREG(s.mode) +} + +is_dir_handle :: proc(fd: Handle) -> bool { + s, err := _fstat(fd) + if err != ERROR_NONE { + return false + } + return S_ISDIR(s.mode) +} + +is_dir_path :: proc(path: string, follow_links: bool = true) -> bool { + s: OS_Stat + err: Errno + if follow_links { + s, err = _stat(path) + } else { + s, err = _lstat(path) + } + if err != ERROR_NONE { + return false + } + return S_ISDIR(s.mode) +} + +is_file :: proc {is_file_path, is_file_handle} +is_dir :: proc {is_dir_path, is_dir_handle} + +// NOTE(bill): Uses startup to initialize it + +stdin: Handle = 0 +stdout: Handle = 1 +stderr: Handle = 2 + +/* TODO(zangent): Implement these! +last_write_time :: proc(fd: Handle) -> File_Time {} +last_write_time_by_name :: proc(name: string) -> File_Time {} +*/ +last_write_time :: proc(fd: Handle) -> (File_Time, Errno) { + s, err := _fstat(fd) + if err != ERROR_NONE { + return 0, err + } + modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds + return File_Time(modified), ERROR_NONE } last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) { - s, err := stat(name); + s, err := _stat(name) if err != ERROR_NONE { - return 0, err; + return 0, err } - modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds; - return File_Time(modified), ERROR_NONE; + modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds + return File_Time(modified), ERROR_NONE } -stat :: proc(path: string) -> (OS_Stat, Errno) { - cstr := strings.clone_to_cstring(path); - defer delete(cstr); - - s: OS_Stat; - result := _unix_stat(cstr, &s); +@private +_stat :: proc(path: string) -> (OS_Stat, Errno) { + cstr := strings.clone_to_cstring(path, context.temp_allocator) + s: OS_Stat = --- + result := _unix_lstat(cstr, &s) if result == -1 { - return s, Errno(get_last_error()); + return s, Errno(get_last_error()) } - return s, ERROR_NONE; + return s, ERROR_NONE } -fstat :: proc(fd: Handle) -> (OS_Stat, Errno) { - s: OS_Stat; - result := _unix_fstat(fd, &s); - if result == -1 { - return s, Errno(get_last_error()); +@private +_lstat :: proc(path: string) -> (OS_Stat, Errno) { + cstr := strings.clone_to_cstring(path, context.temp_allocator) + + // deliberately uninitialized + s: OS_Stat = --- + res := _unix_lstat(cstr, &s) + if res == -1 { + return s, Errno(get_last_error()) } - return s, ERROR_NONE; + return s, ERROR_NONE +} + +@private +_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) { + s: OS_Stat = --- + result := _unix_fstat(fd, &s) + if result == -1 { + return s, Errno(get_last_error()) + } + return s, ERROR_NONE +} + +@private +_fdopendir :: proc(fd: Handle) -> (Dir, Errno) { + dirp := _unix_fdopendir(fd) + if dirp == cast(Dir)nil { + return nil, Errno(get_last_error()) + } + return dirp, ERROR_NONE +} + +@private +_closedir :: proc(dirp: Dir) -> Errno { + rc := _unix_closedir(dirp) + if rc != 0 { + return Errno(get_last_error()) + } + return ERROR_NONE +} + +@private +_rewinddir :: proc(dirp: Dir) { + _unix_rewinddir(dirp) +} + +@private +_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) { + result: ^Dirent + rc := _unix_readdir_r(dirp, &entry, &result) + + if rc != 0 { + err = Errno(get_last_error()) + return + } + err = ERROR_NONE + + if result == nil { + end_of_stream = true + return + } + + return +} + +@private +_readlink :: proc(path: string) -> (string, Errno) { + path_cstr := strings.clone_to_cstring(path, context.temp_allocator) + + bufsz : uint = MAX_PATH + buf := make([]byte, MAX_PATH) + for { + rc := _unix_readlink(path_cstr, &(buf[0]), bufsz) + if rc == -1 { + delete(buf) + return "", Errno(get_last_error()) + } else if rc == int(bufsz) { + bufsz += MAX_PATH + delete(buf) + buf = make([]byte, bufsz) + } else { + return strings.string_from_ptr(&buf[0], rc), ERROR_NONE + } + } + unreachable() +} + +// XXX FreeBSD +absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) { + return "", Errno(ENOSYS) +} + +absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) { + rel := rel + if rel == "" { + rel = "." + } + + rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator) + + path_ptr := _unix_realpath(rel_cstr, nil) + if path_ptr == nil { + return "", Errno(get_last_error()) + } + defer _unix_free(path_ptr) + + path_cstr := transmute(cstring)path_ptr + path = strings.clone( string(path_cstr) ) + + return path, ERROR_NONE } access :: proc(path: string, mask: int) -> (bool, Errno) { - cstr := strings.clone_to_cstring(path); - defer delete(cstr); - result := _unix_access(cstr, c.int(mask)); + cstr := strings.clone_to_cstring(path, context.temp_allocator) + result := _unix_access(cstr, c.int(mask)) if result == -1 { - return false, Errno(get_last_error()); + return false, Errno(get_last_error()) } - return true, ERROR_NONE; + return true, ERROR_NONE } heap_alloc :: proc(size: int) -> rawptr { - assert(size >= 0); - return _unix_calloc(1, c.size_t(size)); + assert(size >= 0) + return _unix_calloc(1, c.size_t(size)) } heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr { - return _unix_realloc(ptr, c.size_t(new_size)); + // NOTE: _unix_realloc doesn't guarantee new memory will be zeroed on + // POSIX platforms. Ensure your caller takes this into account. + return _unix_realloc(ptr, c.size_t(new_size)) } heap_free :: proc(ptr: rawptr) { - _unix_free(ptr); + _unix_free(ptr) } -getenv :: proc(name: string) -> (string, bool) { - path_str := strings.clone_to_cstring(name); - defer delete(path_str); - cstr := _unix_getenv(path_str); +lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { + path_str := strings.clone_to_cstring(key, context.temp_allocator) + cstr := _unix_getenv(path_str) if cstr == nil { - return "", false; + return "", false } - return string(cstr), true; + return strings.clone(string(cstr), allocator), true +} + +get_env :: proc(key: string, allocator := context.allocator) -> (value: string) { + value, _ = lookup_env(key, allocator) + return } get_current_directory :: proc() -> string { // NOTE(tetra): I would use PATH_MAX here, but I was not able to find // an authoritative value for it across all systems. // The largest value I could find was 4096, so might as well use the page size. - page_size := get_page_size(); - buf := make([dynamic]u8, page_size); + page_size := get_page_size() + buf := make([dynamic]u8, page_size) #no_bounds_check for { - cwd := _unix_getcwd(cstring(&buf[0]), c.size_t(len(buf))); + cwd := _unix_getcwd(cstring(&buf[0]), c.size_t(len(buf))) if cwd != nil { - return string(cwd); + return string(cwd) } if Errno(get_last_error()) != ERANGE { - return ""; + return "" } - resize(&buf, len(buf)+page_size); + resize(&buf, len(buf)+page_size) } - unreachable(); + unreachable() } set_current_directory :: proc(path: string) -> (err: Errno) { - cstr := strings.clone_to_cstring(path, context.temp_allocator); - res := _unix_chdir(cstr); - if res == -1 do return Errno(get_last_error()); - return ERROR_NONE; + cstr := strings.clone_to_cstring(path, context.temp_allocator) + res := _unix_chdir(cstr) + if res == -1 do return Errno(get_last_error()) + return ERROR_NONE } exit :: proc "contextless" (code: int) -> ! { - _unix_exit(c.int(code)); + runtime._cleanup_runtime_contextless() + _unix_exit(c.int(code)) } current_thread_id :: proc "contextless" () -> int { - return cast(int) pthread_getthreadid_np(); + return cast(int) pthread_getthreadid_np() } dlopen :: proc(filename: string, flags: int) -> rawptr { - cstr := strings.clone_to_cstring(filename); - defer delete(cstr); - handle := _unix_dlopen(cstr, c.int(flags)); - return handle; + cstr := strings.clone_to_cstring(filename, context.temp_allocator) + handle := _unix_dlopen(cstr, c.int(flags)) + return handle } dlsym :: proc(handle: rawptr, symbol: string) -> rawptr { - assert(handle != nil); - cstr := strings.clone_to_cstring(symbol); - defer delete(cstr); - proc_handle := _unix_dlsym(handle, cstr); - return proc_handle; + assert(handle != nil) + cstr := strings.clone_to_cstring(symbol, context.temp_allocator) + proc_handle := _unix_dlsym(handle, cstr) + return proc_handle } dlclose :: proc(handle: rawptr) -> bool { - assert(handle != nil); - return _unix_dlclose(handle) == 0; + assert(handle != nil) + return _unix_dlclose(handle) == 0 } dlerror :: proc() -> string { - return string(_unix_dlerror()); + return string(_unix_dlerror()) } get_page_size :: proc() -> int { // NOTE(tetra): The page size never changes, so why do anything complicated // if we don't have to. - @static page_size := -1; - if page_size != -1 do return page_size; + @static page_size := -1 + if page_size != -1 do return page_size - page_size = int(_unix_getpagesize()); - return page_size; + page_size = int(_unix_getpagesize()) + return page_size } _alloc_command_line_arguments :: proc() -> []string { - res := make([]string, len(runtime.args__)); + res := make([]string, len(runtime.args__)) for arg, i in runtime.args__ { - res[i] = string(arg); + res[i] = string(arg) } - return res; + return res } - diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin index 1c796f1b8..de3a22187 100644 --- a/core/os/os_linux.odin +++ b/core/os/os_linux.odin @@ -11,6 +11,7 @@ import "core:intrinsics" import "core:sys/unix" Handle :: distinct i32 +Pid :: distinct i32 File_Time :: distinct u64 Errno :: distinct i32 @@ -150,6 +151,8 @@ ERFKILL: Errno : 132 /* Operation not possible due to RF-kill */ EHWPOISON: Errno : 133 /* Memory page has hardware error */ +ADDR_NO_RANDOMIZE :: 0x40000 + O_RDONLY :: 0x00000 O_WRONLY :: 0x00001 O_RDWR :: 0x00002 @@ -164,9 +167,6 @@ O_ASYNC :: 0x02000 O_CLOEXEC :: 0x80000 -SEEK_SET :: 0 -SEEK_CUR :: 1 -SEEK_END :: 2 SEEK_DATA :: 3 SEEK_HOLE :: 4 SEEK_MAX :: SEEK_HOLE @@ -266,15 +266,28 @@ X_OK :: 1 // Test for execute permission W_OK :: 2 // Test for write permission R_OK :: 4 // Test for read permission -AT_FDCWD :: -100 +AT_FDCWD :: ~uintptr(99) /* -100 */ AT_REMOVEDIR :: uintptr(0x200) AT_SYMLINK_NOFOLLOW :: uintptr(0x100) +_unix_personality :: proc(persona: u64) -> int { + return int(intrinsics.syscall(unix.SYS_personality, uintptr(persona))) +} + +_unix_fork :: proc() -> Pid { + when ODIN_ARCH != .arm64 { + res := int(intrinsics.syscall(unix.SYS_fork)) + } else { + res := int(intrinsics.syscall(unix.SYS_clone, unix.SIGCHLD)) + } + return -1 if res < 0 else Pid(res) +} + _unix_open :: proc(path: cstring, flags: int, mode: int = 0o000) -> Handle { - when ODIN_ARCH != "arm64" { + when ODIN_ARCH != .arm64 { res := int(intrinsics.syscall(unix.SYS_open, uintptr(rawptr(path)), uintptr(flags), uintptr(mode))) } else { // NOTE: arm64 does not have open - res := int(intrinsics.syscall(unix.SYS_openat, uintptr(AT_FDCWD), uintptr(rawptr(path), uintptr(flags), uintptr(mode)))) + res := int(intrinsics.syscall(unix.SYS_openat, AT_FDCWD, uintptr(rawptr(path)), uintptr(flags), uintptr(mode))) } return -1 if res < 0 else Handle(res) } @@ -292,7 +305,7 @@ _unix_write :: proc(fd: Handle, buf: rawptr, size: uint) -> int { } _unix_seek :: proc(fd: Handle, offset: i64, whence: int) -> i64 { - when ODIN_ARCH == "amd64" || ODIN_ARCH == "arm64" { + when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 { return i64(intrinsics.syscall(unix.SYS_lseek, uintptr(fd), uintptr(offset), uintptr(whence))) } else { low := uintptr(offset & 0xFFFFFFFF) @@ -304,17 +317,17 @@ _unix_seek :: proc(fd: Handle, offset: i64, whence: int) -> i64 { } _unix_stat :: proc(path: cstring, stat: ^OS_Stat) -> int { - when ODIN_ARCH == "amd64" { + when ODIN_ARCH == .amd64 { return int(intrinsics.syscall(unix.SYS_stat, uintptr(rawptr(path)), uintptr(stat))) - } else when ODIN_ARCH != "arm64" { + } else when ODIN_ARCH != .arm64 { return int(intrinsics.syscall(unix.SYS_stat64, uintptr(rawptr(path)), uintptr(stat))) } else { // NOTE: arm64 does not have stat - return int(intrinsics.syscall(unix.SYS_fstatat, uintptr(AT_FDCWD), uintptr(rawptr(path)), uintptr(stat), 0)) + return int(intrinsics.syscall(unix.SYS_fstatat, AT_FDCWD, uintptr(rawptr(path)), uintptr(stat), 0)) } } _unix_fstat :: proc(fd: Handle, stat: ^OS_Stat) -> int { - when ODIN_ARCH == "amd64" || ODIN_ARCH == "arm64" { + when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 { return int(intrinsics.syscall(unix.SYS_fstat, uintptr(fd), uintptr(stat))) } else { return int(intrinsics.syscall(unix.SYS_fstat64, uintptr(fd), uintptr(stat))) @@ -322,28 +335,28 @@ _unix_fstat :: proc(fd: Handle, stat: ^OS_Stat) -> int { } _unix_lstat :: proc(path: cstring, stat: ^OS_Stat) -> int { - when ODIN_ARCH == "amd64" { + when ODIN_ARCH == .amd64 { return int(intrinsics.syscall(unix.SYS_lstat, uintptr(rawptr(path)), uintptr(stat))) - } else when ODIN_ARCH != "arm64" { + } else when ODIN_ARCH != .arm64 { return int(intrinsics.syscall(unix.SYS_lstat64, uintptr(rawptr(path)), uintptr(stat))) } else { // NOTE: arm64 does not have any lstat - return int(intrinsics.syscall(unix.SYS_fstatat, uintptr(AT_FDCWD), uintptr(rawptr(path)), uintptr(stat), AT_SYMLINK_NOFOLLOW)) + return int(intrinsics.syscall(unix.SYS_fstatat, AT_FDCWD, uintptr(rawptr(path)), uintptr(stat), AT_SYMLINK_NOFOLLOW)) } } _unix_readlink :: proc(path: cstring, buf: rawptr, bufsiz: uint) -> int { - when ODIN_ARCH != "arm64" { + when ODIN_ARCH != .arm64 { return int(intrinsics.syscall(unix.SYS_readlink, uintptr(rawptr(path)), uintptr(buf), uintptr(bufsiz))) } else { // NOTE: arm64 does not have readlink - return int(intrinsics.syscall(unix.SYS_readlinkat, uintptr(AT_FDCWD), uintptr(rawptr(path)), uintptr(buf), uintptr(bufsiz))) + return int(intrinsics.syscall(unix.SYS_readlinkat, AT_FDCWD, uintptr(rawptr(path)), uintptr(buf), uintptr(bufsiz))) } } _unix_access :: proc(path: cstring, mask: int) -> int { - when ODIN_ARCH != "arm64" { + when ODIN_ARCH != .arm64 { return int(intrinsics.syscall(unix.SYS_access, uintptr(rawptr(path)), uintptr(mask))) } else { // NOTE: arm64 does not have access - return int(intrinsics.syscall(unix.SYS_faccessat, uintptr(AT_FDCWD), uintptr(rawptr(path)), uintptr(mask))) + return int(intrinsics.syscall(unix.SYS_faccessat, AT_FDCWD, uintptr(rawptr(path)), uintptr(mask))) } } @@ -356,34 +369,34 @@ _unix_chdir :: proc(path: cstring) -> int { } _unix_rename :: proc(old, new: cstring) -> int { - when ODIN_ARCH != "arm64" { + when ODIN_ARCH != .arm64 { return int(intrinsics.syscall(unix.SYS_rename, uintptr(rawptr(old)), uintptr(rawptr(new)))) } else { // NOTE: arm64 does not have rename - return int(intrinsics.syscall(unix.SYS_renameat, uintptr(AT_FDCWD), uintptr(rawptr(old)), uintptr(rawptr(new)))) + return int(intrinsics.syscall(unix.SYS_renameat, AT_FDCWD, uintptr(rawptr(old)), uintptr(rawptr(new)))) } } _unix_unlink :: proc(path: cstring) -> int { - when ODIN_ARCH != "arm64" { + when ODIN_ARCH != .arm64 { return int(intrinsics.syscall(unix.SYS_unlink, uintptr(rawptr(path)))) } else { // NOTE: arm64 does not have unlink - return int(intrinsics.syscall(unix.SYS_unlinkat, uintptr(AT_FDCWD), uintptr(rawptr(path), 0))) + return int(intrinsics.syscall(unix.SYS_unlinkat, AT_FDCWD, uintptr(rawptr(path)), 0)) } } _unix_rmdir :: proc(path: cstring) -> int { - when ODIN_ARCH != "arm64" { + when ODIN_ARCH != .arm64 { return int(intrinsics.syscall(unix.SYS_rmdir, uintptr(rawptr(path)))) } else { // NOTE: arm64 does not have rmdir - return int(intrinsics.syscall(unix.SYS_unlinkat, uintptr(AT_FDCWD), uintptr(rawptr(path)), AT_REMOVEDIR)) + return int(intrinsics.syscall(unix.SYS_unlinkat, AT_FDCWD, uintptr(rawptr(path)), AT_REMOVEDIR)) } } _unix_mkdir :: proc(path: cstring, mode: u32) -> int { - when ODIN_ARCH != "arm64" { + when ODIN_ARCH != .arm64 { return int(intrinsics.syscall(unix.SYS_mkdir, uintptr(rawptr(path)), uintptr(mode))) } else { // NOTE: arm64 does not have mkdir - return int(intrinsics.syscall(unix.SYS_mkdirat, uintptr(AT_FDCWD), uintptr(rawptr(path)), uintptr(mode))) + return int(intrinsics.syscall(unix.SYS_mkdirat, AT_FDCWD, uintptr(rawptr(path)), uintptr(mode))) } } @@ -402,6 +415,7 @@ foreign libc { @(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr --- @(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring --- + @(link_name="putenv") _unix_putenv :: proc(cstring) -> c.int --- @(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr --- @(link_name="exit") _unix_exit :: proc(status: c.int) -> ! --- @@ -431,10 +445,25 @@ get_last_error :: proc() -> int { return __errno_location()^ } +personality :: proc(persona: u64) -> (Errno) { + res := _unix_personality(persona) + if res == -1 { + return _get_errno(res) + } + return ERROR_NONE +} + +fork :: proc() -> (Pid, Errno) { + pid := _unix_fork() + if pid == -1 { + return -1, _get_errno(int(pid)) + } + return pid, ERROR_NONE +} + open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) { - cstr := strings.clone_to_cstring(path) + cstr := strings.clone_to_cstring(path, context.temp_allocator) handle := _unix_open(cstr, flags, mode) - defer delete(cstr) if handle < 0 { return INVALID_HANDLE, _get_errno(int(handle)) } @@ -473,11 +502,13 @@ seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { } file_size :: proc(fd: Handle) -> (i64, Errno) { - s, err := _fstat(fd) - if err != ERROR_NONE { - return 0, err - } - return max(s.size, 0), ERROR_NONE + // deliberately uninitialized; the syscall fills this buffer for us + s: OS_Stat = --- + result := _unix_fstat(fd, &s) + if result < 0 { + return 0, _get_errno(result) + } + return max(s.size, 0), ERROR_NONE } rename :: proc(old_path, new_path: string) -> Errno { @@ -549,6 +580,11 @@ is_dir_path :: proc(path: string, follow_links: bool = true) -> bool { is_file :: proc {is_file_path, is_file_handle} is_dir :: proc {is_dir_path, is_dir_handle} +exists :: proc(path: string) -> bool { + cpath := strings.clone_to_cstring(path, context.temp_allocator) + res := _unix_access(cpath, O_RDONLY) + return res == 0 +} // NOTE(bill): Uses startup to initialize it @@ -580,10 +616,10 @@ last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) { @private _stat :: proc(path: string) -> (OS_Stat, Errno) { - cstr := strings.clone_to_cstring(path) - defer delete(cstr) + cstr := strings.clone_to_cstring(path, context.temp_allocator) - s: OS_Stat + // deliberately uninitialized; the syscall fills this buffer for us + s: OS_Stat = --- result := _unix_stat(cstr, &s) if result < 0 { return s, _get_errno(result) @@ -593,10 +629,10 @@ _stat :: proc(path: string) -> (OS_Stat, Errno) { @private _lstat :: proc(path: string) -> (OS_Stat, Errno) { - cstr := strings.clone_to_cstring(path) - defer delete(cstr) + cstr := strings.clone_to_cstring(path, context.temp_allocator) - s: OS_Stat + // deliberately uninitialized; the syscall fills this buffer for us + s: OS_Stat = --- result := _unix_lstat(cstr, &s) if result < 0 { return s, _get_errno(result) @@ -606,7 +642,8 @@ _lstat :: proc(path: string) -> (OS_Stat, Errno) { @private _fstat :: proc(fd: Handle) -> (OS_Stat, Errno) { - s: OS_Stat + // deliberately uninitialized; the syscall fills this buffer for us + s: OS_Stat = --- result := _unix_fstat(fd, &s) if result < 0 { return s, _get_errno(result) @@ -659,8 +696,7 @@ _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) @private _readlink :: proc(path: string) -> (string, Errno) { - path_cstr := strings.clone_to_cstring(path) - defer delete(path_cstr) + path_cstr := strings.clone_to_cstring(path, context.temp_allocator) bufsz : uint = 256 buf := make([]byte, bufsz) @@ -696,8 +732,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) { rel = "." } - rel_cstr := strings.clone_to_cstring(rel) - defer delete(rel_cstr) + rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator) path_ptr := _unix_realpath(rel_cstr, nil) if path_ptr == nil { @@ -712,8 +747,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) { } access :: proc(path: string, mask: int) -> (bool, Errno) { - cstr := strings.clone_to_cstring(path) - defer delete(cstr) + cstr := strings.clone_to_cstring(path, context.temp_allocator) result := _unix_access(cstr, mask) if result < 0 { return false, _get_errno(result) @@ -727,6 +761,8 @@ heap_alloc :: proc(size: int) -> rawptr { } heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr { + // NOTE: _unix_realloc doesn't guarantee new memory will be zeroed on + // POSIX platforms. Ensure your caller takes this into account. return _unix_realloc(ptr, c.size_t(new_size)) } @@ -734,14 +770,37 @@ heap_free :: proc(ptr: rawptr) { _unix_free(ptr) } -getenv :: proc(name: string) -> (string, bool) { - path_str := strings.clone_to_cstring(name) - defer delete(path_str) +lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { + path_str := strings.clone_to_cstring(key, context.temp_allocator) + // NOTE(tetra): Lifetime of 'cstr' is unclear, but _unix_free(cstr) segfaults. cstr := _unix_getenv(path_str) if cstr == nil { return "", false } - return string(cstr), true + return strings.clone(string(cstr), allocator), true +} + +get_env :: proc(key: string, allocator := context.allocator) -> (value: string) { + value, _ = lookup_env(key, allocator) + return +} + +set_env :: proc(key, value: string) -> Errno { + s := strings.concatenate({key, "=", value, "\x00"}, context.temp_allocator) + res := _unix_putenv(strings.unsafe_string_to_cstring(s)) + if res < 0 { + return Errno(get_last_error()) + } + return ERROR_NONE +} + +unset_env :: proc(key: string) -> Errno { + s := strings.clone_to_cstring(key, context.temp_allocator) + res := _unix_putenv(s) + if res < 0 { + return Errno(get_last_error()) + } + return ERROR_NONE } get_current_directory :: proc() -> string { @@ -774,6 +833,7 @@ set_current_directory :: proc(path: string) -> (err: Errno) { } exit :: proc "contextless" (code: int) -> ! { + runtime._cleanup_runtime_contextless() _unix_exit(c.int(code)) } @@ -782,15 +842,13 @@ current_thread_id :: proc "contextless" () -> int { } dlopen :: proc(filename: string, flags: int) -> rawptr { - cstr := strings.clone_to_cstring(filename) - defer delete(cstr) + cstr := strings.clone_to_cstring(filename, context.temp_allocator) handle := _unix_dlopen(cstr, c.int(flags)) return handle } dlsym :: proc(handle: rawptr, symbol: string) -> rawptr { assert(handle != nil) - cstr := strings.clone_to_cstring(symbol) - defer delete(cstr) + cstr := strings.clone_to_cstring(symbol, context.temp_allocator) proc_handle := _unix_dlsym(handle, cstr) return proc_handle } diff --git a/core/os/os_openbsd.odin b/core/os/os_openbsd.odin new file mode 100644 index 000000000..9a3dbd874 --- /dev/null +++ b/core/os/os_openbsd.odin @@ -0,0 +1,708 @@ +package os + +foreign import libc "system:c" + +import "core:strings" +import "core:c" +import "core:runtime" + +Handle :: distinct i32 +Pid :: distinct i32 +File_Time :: distinct u64 +Errno :: distinct i32 + +INVALID_HANDLE :: ~Handle(0) + +ERROR_NONE: Errno: 0 + +EPERM: Errno: 1 +ENOENT: Errno: 2 +ESRCH: Errno: 3 +EINTR: Errno: 4 +EIO: Errno: 5 +ENXIO: Errno: 6 +E2BIG: Errno: 7 +ENOEXEC: Errno: 8 +EBADF: Errno: 9 +ECHILD: Errno: 10 +EDEADLK: Errno: 11 +ENOMEM: Errno: 12 +EACCES: Errno: 13 +EFAULT: Errno: 14 +ENOTBLK: Errno: 15 +EBUSY: Errno: 16 +EEXIST: Errno: 17 +EXDEV: Errno: 18 +ENODEV: Errno: 19 +ENOTDIR: Errno: 20 +EISDIR: Errno: 21 +EINVAL: Errno: 22 +ENFILE: Errno: 23 +EMFILE: Errno: 24 +ENOTTY: Errno: 25 +ETXTBSY: Errno: 26 +EFBIG: Errno: 27 +ENOSPC: Errno: 28 +ESPIPE: Errno: 29 +EROFS: Errno: 30 +EMLINK: Errno: 31 +EPIPE: Errno: 32 +EDOM: Errno: 33 +ERANGE: Errno: 34 +EAGAIN: Errno: 35 +EWOULDBLOCK: Errno: EAGAIN +EINPROGRESS: Errno: 36 +EALREADY: Errno: 37 +ENOTSOCK: Errno: 38 +EDESTADDRREQ: Errno: 39 +EMSGSIZE: Errno: 40 +EPROTOTYPE: Errno: 41 +ENOPROTOOPT: Errno: 42 +EPROTONOSUPPORT: Errno: 43 +ESOCKTNOSUPPORT: Errno: 44 +EOPNOTSUPP: Errno: 45 +EPFNOSUPPORT: Errno: 46 +EAFNOSUPPORT: Errno: 47 +EADDRINUSE: Errno: 48 +EADDRNOTAVAIL: Errno: 49 +ENETDOWN: Errno: 50 +ENETUNREACH: Errno: 51 +ENETRESET: Errno: 52 +ECONNABORTED: Errno: 53 +ECONNRESET: Errno: 54 +ENOBUFS: Errno: 55 +EISCONN: Errno: 56 +ENOTCONN: Errno: 57 +ESHUTDOWN: Errno: 58 +ETOOMANYREFS: Errno: 59 +ETIMEDOUT: Errno: 60 +ECONNREFUSED: Errno: 61 +ELOOP: Errno: 62 +ENAMETOOLONG: Errno: 63 +EHOSTDOWN: Errno: 64 +EHOSTUNREACH: Errno: 65 +ENOTEMPTY: Errno: 66 +EPROCLIM: Errno: 67 +EUSERS: Errno: 68 +EDQUOT: Errno: 69 +ESTALE: Errno: 70 +EREMOTE: Errno: 71 +EBADRPC: Errno: 72 +ERPCMISMATCH: Errno: 73 +EPROGUNAVAIL: Errno: 74 +EPROGMISMATCH: Errno: 75 +EPROCUNAVAIL: Errno: 76 +ENOLCK: Errno: 77 +ENOSYS: Errno: 78 +EFTYPE: Errno: 79 +EAUTH: Errno: 80 +ENEEDAUTH: Errno: 81 +EIPSEC: Errno: 82 +ENOATTR: Errno: 83 +EILSEQ: Errno: 84 +ENOMEDIUM: Errno: 85 +EMEDIUMTYPE: Errno: 86 +EOVERFLOW: Errno: 87 +ECANCELED: Errno: 88 +EIDRM: Errno: 89 +ENOMSG: Errno: 90 +ENOTSUP: Errno: 91 +EBADMSG: Errno: 92 +ENOTRECOVERABLE: Errno: 93 +EOWNERDEAD: Errno: 94 +EPROTO: Errno: 95 + +O_RDONLY :: 0x00000 +O_WRONLY :: 0x00001 +O_RDWR :: 0x00002 +O_NONBLOCK :: 0x00004 +O_APPEND :: 0x00008 +O_ASYNC :: 0x00040 +O_SYNC :: 0x00080 +O_CREATE :: 0x00200 +O_TRUNC :: 0x00400 +O_EXCL :: 0x00800 +O_NOCTTY :: 0x08000 +O_CLOEXEC :: 0x10000 + +RTLD_LAZY :: 0x001 +RTLD_NOW :: 0x002 +RTLD_LOCAL :: 0x000 +RTLD_GLOBAL :: 0x100 +RTLD_TRACE :: 0x200 +RTLD_NODELETE :: 0x400 + +MAX_PATH :: 1024 + +// "Argv" arguments converted to Odin strings +args := _alloc_command_line_arguments() + +pid_t :: i32 +time_t :: i64 +mode_t :: u32 +dev_t :: i32 +ino_t :: u64 +nlink_t :: u32 +uid_t :: u32 +gid_t :: u32 +off_t :: i64 +blkcnt_t :: u64 +blksize_t :: i32 + +Unix_File_Time :: struct { + seconds: time_t, + nanoseconds: c.long, +} + +OS_Stat :: struct { + mode: mode_t, // inode protection mode + device_id: dev_t, // inode's device + serial: ino_t, // inode's number + nlink: nlink_t, // number of hard links + uid: uid_t, // user ID of the file's owner + gid: gid_t, // group ID of the file's group + rdev: dev_t, // device type + + last_access: Unix_File_Time, // time of last access + modified: Unix_File_Time, // time of last data modification + status_change: Unix_File_Time, // time of last file status change + + size: off_t, // file size, in bytes + blocks: blkcnt_t, // blocks allocated for file + block_size: blksize_t, // optimal blocksize for I/O + + flags: u32, // user defined flags for file + gen: u32, // file generation number + birthtime: Unix_File_Time, // time of file creation +} + +MAXNAMLEN :: 255 + +// NOTE(laleksic, 2021-01-21): Comment and rename these to match OS_Stat above +Dirent :: struct { + ino: ino_t, // file number of entry + off: off_t, // offset after this entry + reclen: u16, // length of this record + type: u8, // file type + namlen: u8, // length of string in name + _padding: [4]u8, + name: [MAXNAMLEN + 1]byte, // name +} + +Dir :: distinct rawptr // DIR* + +// File type +S_IFMT :: 0o170000 // Type of file mask +S_IFIFO :: 0o010000 // Named pipe (fifo) +S_IFCHR :: 0o020000 // Character special +S_IFDIR :: 0o040000 // Directory +S_IFBLK :: 0o060000 // Block special +S_IFREG :: 0o100000 // Regular +S_IFLNK :: 0o120000 // Symbolic link +S_IFSOCK :: 0o140000 // Socket +S_ISVTX :: 0o001000 // Save swapped text even after use + +// File mode + // Read, write, execute/search by owner +S_IRWXU :: 0o0700 // RWX mask for owner +S_IRUSR :: 0o0400 // R for owner +S_IWUSR :: 0o0200 // W for owner +S_IXUSR :: 0o0100 // X for owner + + // Read, write, execute/search by group +S_IRWXG :: 0o0070 // RWX mask for group +S_IRGRP :: 0o0040 // R for group +S_IWGRP :: 0o0020 // W for group +S_IXGRP :: 0o0010 // X for group + + // Read, write, execute/search by others +S_IRWXO :: 0o0007 // RWX mask for other +S_IROTH :: 0o0004 // R for other +S_IWOTH :: 0o0002 // W for other +S_IXOTH :: 0o0001 // X for other + +S_ISUID :: 0o4000 // Set user id on execution +S_ISGID :: 0o2000 // Set group id on execution +S_ISTXT :: 0o1000 // Sticky bit + +S_ISLNK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFLNK } +S_ISREG :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFREG } +S_ISDIR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFDIR } +S_ISCHR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFCHR } +S_ISBLK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFBLK } +S_ISFIFO :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFIFO } +S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK } + +F_OK :: 0x00 // Test for file existance +X_OK :: 0x01 // Test for execute permission +W_OK :: 0x02 // Test for write permission +R_OK :: 0x04 // Test for read permission + +AT_FDCWD :: -100 +AT_EACCESS :: 0x01 +AT_SYMLINK_NOFOLLOW :: 0x02 +AT_SYMLINK_FOLLOW :: 0x04 +AT_REMOVEDIR :: 0x08 + +@(default_calling_convention="c") +foreign libc { + @(link_name="__errno") __errno :: proc() -> ^int --- + + @(link_name="fork") _unix_fork :: proc() -> pid_t --- + @(link_name="getthrid") _unix_getthrid :: proc() -> int --- + + @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle --- + @(link_name="close") _unix_close :: proc(fd: Handle) -> c.int --- + @(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t --- + @(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t --- + @(link_name="lseek") _unix_seek :: proc(fd: Handle, offset: off_t, whence: c.int) -> off_t --- + @(link_name="stat") _unix_stat :: proc(path: cstring, sb: ^OS_Stat) -> c.int --- + @(link_name="fstat") _unix_fstat :: proc(fd: Handle, sb: ^OS_Stat) -> c.int --- + @(link_name="lstat") _unix_lstat :: proc(path: cstring, sb: ^OS_Stat) -> c.int --- + @(link_name="readlink") _unix_readlink :: proc(path: cstring, buf: ^byte, bufsiz: c.size_t) -> c.ssize_t --- + @(link_name="access") _unix_access :: proc(path: cstring, mask: c.int) -> c.int --- + @(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring --- + @(link_name="chdir") _unix_chdir :: proc(path: cstring) -> c.int --- + @(link_name="rename") _unix_rename :: proc(old, new: cstring) -> c.int --- + @(link_name="unlink") _unix_unlink :: proc(path: cstring) -> c.int --- + @(link_name="rmdir") _unix_rmdir :: proc(path: cstring) -> c.int --- + @(link_name="mkdir") _unix_mkdir :: proc(path: cstring, mode: mode_t) -> c.int --- + + @(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int --- + @(link_name="fdopendir") _unix_fdopendir :: proc(fd: Handle) -> Dir --- + @(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int --- + @(link_name="rewinddir") _unix_rewinddir :: proc(dirp: Dir) --- + @(link_name="readdir_r") _unix_readdir_r :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int --- + + @(link_name="malloc") _unix_malloc :: proc(size: c.size_t) -> rawptr --- + @(link_name="calloc") _unix_calloc :: proc(num, size: c.size_t) -> rawptr --- + @(link_name="free") _unix_free :: proc(ptr: rawptr) --- + @(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr --- + + @(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring --- + @(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr --- + + @(link_name="exit") _unix_exit :: proc(status: c.int) -> ! --- + + @(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: c.int) -> rawptr --- + @(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr --- + @(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> c.int --- + @(link_name="dlerror") _unix_dlerror :: proc() -> cstring --- +} + +is_path_separator :: proc(r: rune) -> bool { + return r == '/' +} + +get_last_error :: proc() -> int { + return __errno()^ +} + +fork :: proc() -> (Pid, Errno) { + pid := _unix_fork() + if pid == -1 { + return Pid(-1), Errno(get_last_error()) + } + return Pid(pid), ERROR_NONE +} + +open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) { + cstr := strings.clone_to_cstring(path, context.temp_allocator) + handle := _unix_open(cstr, c.int(flags), c.int(mode)) + if handle == -1 { + return INVALID_HANDLE, Errno(get_last_error()) + } + return handle, ERROR_NONE +} + +close :: proc(fd: Handle) -> Errno { + result := _unix_close(fd) + if result == -1 { + return Errno(get_last_error()) + } + return ERROR_NONE +} + +read :: proc(fd: Handle, data: []byte) -> (int, Errno) { + bytes_read := _unix_read(fd, &data[0], c.size_t(len(data))) + if bytes_read == -1 { + return -1, Errno(get_last_error()) + } + return int(bytes_read), ERROR_NONE +} + +write :: proc(fd: Handle, data: []byte) -> (int, Errno) { + if len(data) == 0 { + return 0, ERROR_NONE + } + bytes_written := _unix_write(fd, &data[0], c.size_t(len(data))) + if bytes_written == -1 { + return -1, Errno(get_last_error()) + } + return int(bytes_written), ERROR_NONE +} + +seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { + res := _unix_seek(fd, offset, c.int(whence)) + if res == -1 { + return -1, Errno(get_last_error()) + } + return res, ERROR_NONE +} + +file_size :: proc(fd: Handle) -> (i64, Errno) { + s, err := _fstat(fd) + if err != ERROR_NONE { + return -1, err + } + return s.size, ERROR_NONE +} + +rename :: proc(old_path, new_path: string) -> Errno { + old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator) + new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator) + res := _unix_rename(old_path_cstr, new_path_cstr) + if res == -1 { + return Errno(get_last_error()) + } + return ERROR_NONE +} + +remove :: proc(path: string) -> Errno { + path_cstr := strings.clone_to_cstring(path, context.temp_allocator) + res := _unix_unlink(path_cstr) + if res == -1 { + return Errno(get_last_error()) + } + return ERROR_NONE +} + +make_directory :: proc(path: string, mode: mode_t = 0o775) -> Errno { + path_cstr := strings.clone_to_cstring(path, context.temp_allocator) + res := _unix_mkdir(path_cstr, mode) + if res == -1 { + return Errno(get_last_error()) + } + return ERROR_NONE +} + +remove_directory :: proc(path: string) -> Errno { + path_cstr := strings.clone_to_cstring(path, context.temp_allocator) + res := _unix_rmdir(path_cstr) + if res == -1 { + return Errno(get_last_error()) + } + return ERROR_NONE +} + +is_file_handle :: proc(fd: Handle) -> bool { + s, err := _fstat(fd) + if err != ERROR_NONE { + return false + } + return S_ISREG(s.mode) +} + +is_file_path :: proc(path: string, follow_links: bool = true) -> bool { + s: OS_Stat + err: Errno + if follow_links { + s, err = _stat(path) + } else { + s, err = _lstat(path) + } + if err != ERROR_NONE { + return false + } + return S_ISREG(s.mode) +} + +is_dir_handle :: proc(fd: Handle) -> bool { + s, err := _fstat(fd) + if err != ERROR_NONE { + return false + } + return S_ISDIR(s.mode) +} + +is_dir_path :: proc(path: string, follow_links: bool = true) -> bool { + s: OS_Stat + err: Errno + if follow_links { + s, err = _stat(path) + } else { + s, err = _lstat(path) + } + if err != ERROR_NONE { + return false + } + return S_ISDIR(s.mode) +} + +is_file :: proc {is_file_path, is_file_handle} +is_dir :: proc {is_dir_path, is_dir_handle} + +// NOTE(bill): Uses startup to initialize it + +stdin: Handle = 0 +stdout: Handle = 1 +stderr: Handle = 2 + +/* TODO(zangent): Implement these! +last_write_time :: proc(fd: Handle) -> File_Time {} +last_write_time_by_name :: proc(name: string) -> File_Time {} +*/ +last_write_time :: proc(fd: Handle) -> (File_Time, Errno) { + s, err := _fstat(fd) + if err != ERROR_NONE { + return 0, err + } + modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds + return File_Time(modified), ERROR_NONE +} + +last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) { + s, err := _stat(name) + if err != ERROR_NONE { + return 0, err + } + modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds + return File_Time(modified), ERROR_NONE +} + +@private +_stat :: proc(path: string) -> (OS_Stat, Errno) { + cstr := strings.clone_to_cstring(path, context.temp_allocator) + + // deliberately uninitialized + s: OS_Stat = --- + res := _unix_stat(cstr, &s) + if res == -1 { + return s, Errno(get_last_error()) + } + return s, ERROR_NONE +} + +@private +_lstat :: proc(path: string) -> (OS_Stat, Errno) { + cstr := strings.clone_to_cstring(path, context.temp_allocator) + + // deliberately uninitialized + s: OS_Stat = --- + res := _unix_lstat(cstr, &s) + if res == -1 { + return s, Errno(get_last_error()) + } + return s, ERROR_NONE +} + +@private +_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) { + // deliberately uninitialized + s: OS_Stat = --- + res := _unix_fstat(fd, &s) + if res == -1 { + return s, Errno(get_last_error()) + } + return s, ERROR_NONE +} + +@private +_fdopendir :: proc(fd: Handle) -> (Dir, Errno) { + dirp := _unix_fdopendir(fd) + if dirp == cast(Dir)nil { + return nil, Errno(get_last_error()) + } + return dirp, ERROR_NONE +} + +@private +_closedir :: proc(dirp: Dir) -> Errno { + rc := _unix_closedir(dirp) + if rc != 0 { + return Errno(get_last_error()) + } + return ERROR_NONE +} + +@private +_rewinddir :: proc(dirp: Dir) { + _unix_rewinddir(dirp) +} + +@private +_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) { + result: ^Dirent + rc := _unix_readdir_r(dirp, &entry, &result) + + if rc != 0 { + err = Errno(get_last_error()) + return + } + err = ERROR_NONE + + if result == nil { + end_of_stream = true + return + } + + return +} + +@private +_readlink :: proc(path: string) -> (string, Errno) { + path_cstr := strings.clone_to_cstring(path, context.temp_allocator) + + bufsz : uint = MAX_PATH + buf := make([]byte, MAX_PATH) + for { + rc := _unix_readlink(path_cstr, &(buf[0]), bufsz) + if rc == -1 { + delete(buf) + return "", Errno(get_last_error()) + } else if rc == int(bufsz) { + bufsz += MAX_PATH + delete(buf) + buf = make([]byte, bufsz) + } else { + return strings.string_from_ptr(&buf[0], rc), ERROR_NONE + } + } + unreachable() +} + +// XXX OpenBSD +absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) { + return "", Errno(ENOSYS) +} + +absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) { + rel := rel + if rel == "" { + rel = "." + } + + rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator) + + path_ptr := _unix_realpath(rel_cstr, nil) + if path_ptr == nil { + return "", Errno(get_last_error()) + } + defer _unix_free(path_ptr) + + path_cstr := transmute(cstring)path_ptr + path = strings.clone( string(path_cstr) ) + + return path, ERROR_NONE +} + +access :: proc(path: string, mask: int) -> (bool, Errno) { + cstr := strings.clone_to_cstring(path, context.temp_allocator) + res := _unix_access(cstr, c.int(mask)) + if res == -1 { + return false, Errno(get_last_error()) + } + return true, ERROR_NONE +} + +heap_alloc :: proc(size: int) -> rawptr { + assert(size >= 0) + return _unix_calloc(1, c.size_t(size)) +} + +heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr { + // NOTE: _unix_realloc doesn't guarantee new memory will be zeroed on + // POSIX platforms. Ensure your caller takes this into account. + return _unix_realloc(ptr, c.size_t(new_size)) +} + +heap_free :: proc(ptr: rawptr) { + _unix_free(ptr) +} + +lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { + path_str := strings.clone_to_cstring(key, context.temp_allocator) + cstr := _unix_getenv(path_str) + if cstr == nil { + return "", false + } + return strings.clone(string(cstr), allocator), true +} + +get_env :: proc(key: string, allocator := context.allocator) -> (value: string) { + value, _ = lookup_env(key, allocator) + return +} + +get_current_directory :: proc() -> string { + buf := make([dynamic]u8, MAX_PATH) + for { + cwd := _unix_getcwd(cstring(raw_data(buf)), c.size_t(len(buf))) + if cwd != nil { + return string(cwd) + } + if Errno(get_last_error()) != ERANGE { + return "" + } + resize(&buf, len(buf) + MAX_PATH) + } + unreachable() +} + +set_current_directory :: proc(path: string) -> (err: Errno) { + cstr := strings.clone_to_cstring(path, context.temp_allocator) + res := _unix_chdir(cstr) + if res == -1 { + return Errno(get_last_error()) + } + return ERROR_NONE +} + +exit :: proc "contextless" (code: int) -> ! { + runtime._cleanup_runtime_contextless() + _unix_exit(c.int(code)) +} + +current_thread_id :: proc "contextless" () -> int { + return _unix_getthrid() +} + +dlopen :: proc(filename: string, flags: int) -> rawptr { + cstr := strings.clone_to_cstring(filename, context.temp_allocator) + handle := _unix_dlopen(cstr, c.int(flags)) + return handle +} +dlsym :: proc(handle: rawptr, symbol: string) -> rawptr { + assert(handle != nil) + cstr := strings.clone_to_cstring(symbol, context.temp_allocator) + proc_handle := _unix_dlsym(handle, cstr) + return proc_handle +} +dlclose :: proc(handle: rawptr) -> bool { + assert(handle != nil) + return _unix_dlclose(handle) == 0 +} +dlerror :: proc() -> string { + return string(_unix_dlerror()) +} + +get_page_size :: proc() -> int { + // NOTE(tetra): The page size never changes, so why do anything complicated + // if we don't have to. + @static page_size := -1 + if page_size != -1 { + return page_size + } + + page_size = int(_unix_getpagesize()) + return page_size +} + + +_alloc_command_line_arguments :: proc() -> []string { + res := make([]string, len(runtime.args__)) + for arg, i in runtime.args__ { + res[i] = string(arg) + } + return res +} diff --git a/core/os/os_wasi.odin b/core/os/os_wasi.odin index d2ba166bd..7bab1b949 100644 --- a/core/os/os_wasi.odin +++ b/core/os/os_wasi.odin @@ -1,6 +1,7 @@ package os import "core:sys/wasm/wasi" +import "core:runtime" Handle :: distinct i32 Errno :: distinct i32 @@ -93,5 +94,6 @@ heap_free :: proc(ptr: rawptr) { exit :: proc "contextless" (code: int) -> ! { + runtime._cleanup_runtime_contextless() wasi.proc_exit(wasi.exitcode_t(code)) } \ No newline at end of file diff --git a/core/os/os_windows.odin b/core/os/os_windows.odin index e6efb89df..fe9496e4c 100644 --- a/core/os/os_windows.odin +++ b/core/os/os_windows.odin @@ -2,6 +2,7 @@ package os import win32 "core:sys/windows" +import "core:runtime" Handle :: distinct uintptr File_Time :: distinct u64 @@ -128,6 +129,7 @@ get_page_size :: proc() -> int { exit :: proc "contextless" (code: int) -> ! { + runtime._cleanup_runtime_contextless() win32.ExitProcess(win32.DWORD(code)) } diff --git a/core/os/stat.odin b/core/os/stat.odin index 6f4c8b0ef..1b64ad33b 100644 --- a/core/os/stat.odin +++ b/core/os/stat.odin @@ -2,7 +2,6 @@ package os import "core:time" - File_Info :: struct { fullpath: string, name: string, diff --git a/core/os/stat_unix.odin b/core/os/stat_unix.odin index 08c6f53c4..dae7ab2fb 100644 --- a/core/os/stat_unix.odin +++ b/core/os/stat_unix.odin @@ -1,4 +1,4 @@ -//+build linux, darwin, freebsd +//+build linux, darwin, freebsd, openbsd package os import "core:time" @@ -61,7 +61,7 @@ _make_time_from_unix_file_time :: proc(uft: Unix_File_Time) -> time.Time { _fill_file_info_from_stat :: proc(fi: ^File_Info, s: OS_Stat) { fi.size = s.size fi.mode = cast(File_Mode)s.mode - fi.is_dir = S_ISDIR(u32(s.mode)) + fi.is_dir = S_ISDIR(s.mode) // NOTE(laleksic, 2021-01-21): Not really creation time, but closest we can get (maybe better to leave it 0?) fi.creation_time = _make_time_from_unix_file_time(s.status_change) @@ -119,7 +119,6 @@ lstat :: proc(name: string, allocator := context.allocator) -> (fi: File_Info, e } stat :: proc(name: string, allocator := context.allocator) -> (fi: File_Info, err: Errno) { - context.allocator = allocator s: OS_Stat diff --git a/core/os/stat_windows.odin b/core/os/stat_windows.odin index 2d9f98fd4..79bb8c42e 100644 --- a/core/os/stat_windows.odin +++ b/core/os/stat_windows.odin @@ -20,7 +20,7 @@ full_path_from_name :: proc(name: string, allocator := context.allocator) -> (pa return "", Errno(win32.GetLastError()) } if n <= u32(len(buf)) { - return win32.utf16_to_utf8(buf[:n], allocator), ERROR_NONE + return win32.utf16_to_utf8(buf[:n], allocator) or_else "", ERROR_NONE } resize(&buf, len(buf)*2) } @@ -80,7 +80,7 @@ stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Errno) return _stat(name, attrs, allocator) } -fstat :: proc(fd: Handle, allocator := context.allocator) -> (File_Info, Errno) { +fstat :: proc(fd: Handle, allocator := context.allocator) -> (fi: File_Info, errno: Errno) { if fd == 0 { return {}, ERROR_INVALID_HANDLE } @@ -94,14 +94,14 @@ fstat :: proc(fd: Handle, allocator := context.allocator) -> (File_Info, Errno) h := win32.HANDLE(fd) switch win32.GetFileType(h) { case win32.FILE_TYPE_PIPE, win32.FILE_TYPE_CHAR: - fi: File_Info - fi.fullpath = path fi.name = basename(path) fi.mode |= file_type_mode(h) - return fi, ERROR_NONE + errno = ERROR_NONE + case: + fi, errno = file_info_from_get_file_information_by_handle(path, h) } - - return file_info_from_get_file_information_by_handle(path, h) + fi.fullpath = path + return } @@ -132,26 +132,11 @@ cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 { @(private) cleanpath_from_handle :: proc(fd: Handle) -> (string, Errno) { - if fd == 0 { - return "", ERROR_INVALID_HANDLE + buf, err := cleanpath_from_handle_u16(fd) + if err != 0 { + return "", err } - h := win32.HANDLE(fd) - - MAX_PATH := win32.DWORD(260) + 1 - buf: []u16 - for { - buf = make([]u16, MAX_PATH, context.temp_allocator) - err := win32.GetFinalPathNameByHandleW(h, raw_data(buf), MAX_PATH, 0) - switch Errno(err) { - case ERROR_PATH_NOT_FOUND, ERROR_INVALID_PARAMETER: - return "", Errno(err) - case ERROR_NOT_ENOUGH_MEMORY: - MAX_PATH = MAX_PATH*2 + 1 - continue - } - break - } - return cleanpath_from_buf(buf), ERROR_NONE + return win32.utf16_to_utf8(buf, context.allocator) or_else "", err } @(private) cleanpath_from_handle_u16 :: proc(fd: Handle) -> ([]u16, Errno) { @@ -160,27 +145,19 @@ cleanpath_from_handle_u16 :: proc(fd: Handle) -> ([]u16, Errno) { } h := win32.HANDLE(fd) - MAX_PATH := win32.DWORD(260) + 1 - buf: []u16 - for { - buf = make([]u16, MAX_PATH, context.temp_allocator) - err := win32.GetFinalPathNameByHandleW(h, raw_data(buf), MAX_PATH, 0) - switch Errno(err) { - case ERROR_PATH_NOT_FOUND, ERROR_INVALID_PARAMETER: - return nil, Errno(err) - case ERROR_NOT_ENOUGH_MEMORY: - MAX_PATH = MAX_PATH*2 + 1 - continue - } - break + n := win32.GetFinalPathNameByHandleW(h, nil, 0, 0) + if n == 0 { + return nil, Errno(win32.GetLastError()) } - return cleanpath_strip_prefix(buf), ERROR_NONE + buf := make([]u16, max(n, win32.DWORD(260))+1, context.temp_allocator) + buf_len := win32.GetFinalPathNameByHandleW(h, raw_data(buf), n, 0) + return buf[:buf_len], ERROR_NONE } @(private) cleanpath_from_buf :: proc(buf: []u16) -> string { buf := buf buf = cleanpath_strip_prefix(buf) - return win32.utf16_to_utf8(buf, context.allocator) + return win32.utf16_to_utf8(buf, context.allocator) or_else "" } @(private) diff --git a/core/os/stream.odin b/core/os/stream.odin index 5cf5c8405..2c6e1d47f 100644 --- a/core/os/stream.odin +++ b/core/os/stream.odin @@ -19,7 +19,7 @@ _file_stream_vtable := &io.Stream_VTable{ return }, impl_read_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) { - when ODIN_OS == "windows" || ODIN_OS == "wasi" { + when ODIN_OS == .Windows || ODIN_OS == .WASI { fd := Handle(uintptr(s.stream_data)) os_err: Errno n, os_err = read_at(fd, p, offset) @@ -33,7 +33,7 @@ _file_stream_vtable := &io.Stream_VTable{ return }, impl_write_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) { - when ODIN_OS == "windows" || ODIN_OS == "wasi" { + when ODIN_OS == .Windows || ODIN_OS == .WASI { fd := Handle(uintptr(s.stream_data)) os_err: Errno n, os_err = write_at(fd, p, offset) @@ -53,7 +53,7 @@ _file_stream_vtable := &io.Stream_VTable{ return sz }, impl_flush = proc(s: io.Stream) -> io.Error { - when ODIN_OS == "windows" { + when ODIN_OS == .Windows { fd := Handle(uintptr(s.stream_data)) flush(fd) } else { diff --git a/core/path/filepath/match.odin b/core/path/filepath/match.odin index cba44953d..c932f202a 100644 --- a/core/path/filepath/match.odin +++ b/core/path/filepath/match.odin @@ -89,7 +89,7 @@ scan_chunk :: proc(pattern: string) -> (star: bool, chunk, rest: string) { scan_loop: for i = 0; i < len(pattern); i += 1 { switch pattern[i] { case '\\': - when ODIN_OS != "windows" { + when ODIN_OS != .Windows { if i+1 < len(pattern) { i += 1 } @@ -161,7 +161,7 @@ match_chunk :: proc(chunk, s: string) -> (rest: string, ok: bool, err: Match_Err chunk = chunk[1:] case '\\': - when ODIN_OS != "windows" { + when ODIN_OS != .Windows { chunk = chunk[1:] if len(chunk) == 0 { err = .Syntax_Error @@ -188,7 +188,7 @@ get_escape :: proc(chunk: string) -> (r: rune, next_chunk: string, err: Match_Er return } chunk := chunk - if chunk[0] == '\\' && ODIN_OS != "windows" { + if chunk[0] == '\\' && ODIN_OS != .Windows { chunk = chunk[1:] if len(chunk) == 0 { err = .Syntax_Error @@ -220,19 +220,21 @@ get_escape :: proc(chunk: string) -> (r: rune, next_chunk: string, err: Match_Er // glob :: proc(pattern: string, allocator := context.allocator) -> (matches: []string, err: Match_Error) { + context.allocator = allocator + if !has_meta(pattern) { // TODO(bill): os.lstat on here to check for error - m := make([]string, 1, allocator) + m := make([]string, 1) m[0] = pattern return m[:], .None } - temp_buf: [8]byte - dir, file := split(pattern) volume_len := 0 - when ODIN_OS == "windows" { + when ODIN_OS == .Windows { + temp_buf: [8]byte volume_len, dir = clean_glob_path_windows(dir, temp_buf[:]) + } else { dir = clean_glob_path(dir) } @@ -247,7 +249,7 @@ glob :: proc(pattern: string, allocator := context.allocator) -> (matches: []str if err != .None { return } - dmatches := make([dynamic]string, 0, 0, allocator) + dmatches := make([dynamic]string, 0, 0) for d in m { dmatches, err = _glob(d, file, &dmatches) if err != .None { @@ -259,15 +261,17 @@ glob :: proc(pattern: string, allocator := context.allocator) -> (matches: []str } return } -_glob :: proc(dir, pattern: string, matches: ^[dynamic]string) -> (m: [dynamic]string, e: Match_Error) { +_glob :: proc(dir, pattern: string, matches: ^[dynamic]string, allocator := context.allocator) -> (m: [dynamic]string, e: Match_Error) { + context.allocator = allocator + if matches != nil { m = matches^ } else { - m = make([dynamic]string, 0, 0, context.allocator) + m = make([dynamic]string, 0, 0) } - d, derr := os.open(dir) + d, derr := os.open(dir, os.O_RDONLY) if derr != 0 { return } @@ -276,6 +280,7 @@ _glob :: proc(dir, pattern: string, matches: ^[dynamic]string) -> (m: [dynamic]s { file_info, ferr := os.fstat(d) defer os.file_info_delete(file_info) + if ferr != 0 { return } @@ -300,7 +305,7 @@ _glob :: proc(dir, pattern: string, matches: ^[dynamic]string) -> (m: [dynamic]s n := fi.name matched := match(pattern, n) or_return if matched { - append(&m, join(dir, n)) + append(&m, join({dir, n})) } } return @@ -308,7 +313,7 @@ _glob :: proc(dir, pattern: string, matches: ^[dynamic]string) -> (m: [dynamic]s @(private) has_meta :: proc(path: string) -> bool { - when ODIN_OS == "windows" { + when ODIN_OS == .Windows { CHARS :: `*?[` } else { CHARS :: `*?[\` diff --git a/core/path/filepath/path.odin b/core/path/filepath/path.odin index 39cd80a47..32e4a8a37 100644 --- a/core/path/filepath/path.odin +++ b/core/path/filepath/path.odin @@ -1,14 +1,16 @@ // The path/filepath package uses either forward slashes or backslashes depending on the operating system -// To process paths usch as URLs that depend on forward slashes regardless of the OS, use the path package +// To process paths such as URLs that depend on forward slashes regardless of the OS, use the path package package filepath import "core:strings" +SEPARATOR_CHARS :: `/\` + // is_separator checks whether the byte is a valid separator character is_separator :: proc(c: byte) -> bool { switch c { case '/': return true - case '\\': return ODIN_OS == "windows" + case '\\': return ODIN_OS == .Windows } return false } @@ -32,7 +34,7 @@ volume_name :: proc(path: string) -> string { } volume_name_len :: proc(path: string) -> int { - if ODIN_OS == "windows" { + if ODIN_OS == .Windows { if len(path) < 2 { return 0 } @@ -69,6 +71,16 @@ volume_name_len :: proc(path: string) -> int { return 0 } +/* + Gets the file name and extension from a path. + + i.e: + 'path/to/name.tar.gz' -> 'name.tar.gz' + 'path/to/name.txt' -> 'name.txt' + 'path/to/name' -> 'name' + + Returns "." if the path is an empty string. +*/ base :: proc(path: string) -> string { if path == "" { return "." @@ -94,6 +106,118 @@ base :: proc(path: string) -> string { return path } +/* + Gets the name of a file from a path. + + The stem of a file is such that stem(path) + ext(path) = base(path). + + Only the last dot is considered when splitting the file extension. + See `short_stem`. + + i.e: + 'name.tar.gz' -> 'name.tar' + 'name.txt' -> 'name' + + Returns an empty string if there is no stem. e.g: '.gitignore'. + Returns an empty string if there's a trailing path separator. +*/ +stem :: proc(path: string) -> string { + if len(path) > 0 && is_separator(path[len(path) - 1]) { + // NOTE(tetra): Trailing separator + return "" + } + + // NOTE(tetra): Get the basename + path := path + if i := strings.last_index_any(path, SEPARATOR_CHARS); i != -1 { + path = path[i+1:] + } + + if i := strings.last_index_byte(path, '.'); i != -1 { + return path[:i] + } + + return path +} + +/* + Gets the name of a file from a path. + + The short stem is such that short_stem(path) + long_ext(path) = base(path). + + The first dot is used to split off the file extension, unlike `stem` which uses the last dot. + + i.e: + 'name.tar.gz' -> 'name' + 'name.txt' -> 'name' + + Returns an empty string if there is no stem. e.g: '.gitignore'. + Returns an empty string if there's a trailing path separator. +*/ +short_stem :: proc(path: string) -> string { + s := stem(path) + if i := strings.index_byte(s, '.'); i != -1 { + return s[:i] + } + return s +} + +/* + Gets the file extension from a path, including the dot. + + The file extension is such that stem(path) + ext(path) = base(path). + + Only the last dot is considered when splitting the file extension. + See `long_ext`. + + i.e: + 'name.tar.gz' -> '.gz' + 'name.txt' -> '.txt' + + Returns an empty string if there is no dot. + Returns an empty string if there is a trailing path separator. +*/ +ext :: proc(path: string) -> string { + for i := len(path)-1; i >= 0 && !is_separator(path[i]); i -= 1 { + if path[i] == '.' { + return path[i:] + } + } + return "" +} + +/* + Gets the file extension from a path, including the dot. + + The long file extension is such that short_stem(path) + long_ext(path) = base(path). + + The first dot is used to split off the file extension, unlike `ext` which uses the last dot. + + i.e: + 'name.tar.gz' -> '.tar.gz' + 'name.txt' -> '.txt' + + Returns an empty string if there is no dot. + Returns an empty string if there is a trailing path separator. +*/ +long_ext :: proc(path: string) -> string { + if len(path) > 0 && is_separator(path[len(path) - 1]) { + // NOTE(tetra): Trailing separator + return "" + } + + // NOTE(tetra): Get the basename + path := path + if i := strings.last_index_any(path, SEPARATOR_CHARS); i != -1 { + path = path[i+1:] + } + + if i := strings.index_byte(path, '.'); i != -1 { + return path[i:] + } + + return "" +} clean :: proc(path: string, allocator := context.allocator) -> string { context.allocator = allocator @@ -122,6 +246,7 @@ clean :: proc(path: string, allocator := context.allocator) -> string { vol_and_path = original_path, vol_len = vol_len, } + defer lazy_buffer_destroy(out) r, dot_dot := 0, 0 if rooted { @@ -170,7 +295,6 @@ clean :: proc(path: string, allocator := context.allocator) -> string { cleaned, new_allocation := from_slash(s) if new_allocation { delete(s) - lazy_buffer_destroy(out) } return cleaned } @@ -189,15 +313,6 @@ to_slash :: proc(path: string, allocator := context.allocator) -> (new_path: str return strings.replace_all(path, SEPARATOR_STRING, "/", allocator) } -ext :: proc(path: string) -> string { - for i := len(path)-1; i >= 0 && !is_separator(path[i]); i -= 1 { - if path[i] == '.' { - return path[i:] - } - } - return "" -} - Relative_Error :: enum { None, @@ -284,13 +399,14 @@ rel :: proc(base_path, target_path: string, allocator := context.allocator) -> ( } dir :: proc(path: string, allocator := context.allocator) -> string { + context.allocator = allocator vol := volume_name(path) i := len(path) - 1 for i >= len(vol) && !is_separator(path[i]) { i -= 1 } - dir := clean(path[len(vol) : i+1], allocator) - defer delete(dir, allocator) + dir := clean(path[len(vol) : i+1]) + defer delete(dir) if dir == "." && len(vol) > 2 { return strings.clone(vol) } @@ -299,6 +415,11 @@ dir :: proc(path: string, allocator := context.allocator) -> string { +// Splits the PATH-like `path` string, returning an array of its separated components (delete after use). +// For Windows the separator is `;`, for Unix it's `:`. +// An empty string returns nil. A non-empty string with no separators returns a 1-element array. +// Any empty components will be included, e.g. `a::b` will return a 3-element array, as will `::`. +// Separators within pairs of double-quotes will be ignored and stripped, e.g. `"a:b"c:d` will return []{`a:bc`, `d`}. split_list :: proc(path: string, allocator := context.allocator) -> []string { if path == "" { return nil @@ -321,7 +442,7 @@ split_list :: proc(path: string, allocator := context.allocator) -> []string { } start, quote = 0, false - list := make([]string, count, allocator) + list := make([]string, count + 1, allocator) index := 0 for i := 0; i < len(path); i += 1 { c := path[i] @@ -335,6 +456,7 @@ split_list :: proc(path: string, allocator := context.allocator) -> []string { } } assert(index == count) + list[index] = path[start:] for s0, i in list { s, new := strings.replace_all(s0, `"`, ``, allocator) diff --git a/core/path/filepath/path_unix.odin b/core/path/filepath/path_unix.odin index 1db528a2f..8faf6097c 100644 --- a/core/path/filepath/path_unix.odin +++ b/core/path/filepath/path_unix.odin @@ -1,7 +1,7 @@ -//+build linux, darwin, freebsd +//+build linux, darwin, freebsd, openbsd package filepath -when ODIN_OS == "darwin" { +when ODIN_OS == .Darwin { foreign import libc "System.framework" } else { foreign import libc "system:c" @@ -38,7 +38,7 @@ abs :: proc(path: string, allocator := context.allocator) -> (string, bool) { return path_str, true } -join :: proc(elems: ..string, allocator := context.allocator) -> string { +join :: proc(elems: []string, allocator := context.allocator) -> string { for e, i in elems { if e != "" { p := strings.join(elems[i:], SEPARATOR_STRING, context.temp_allocator) @@ -54,11 +54,16 @@ foreign libc { @(link_name="free") _unix_free :: proc(ptr: rawptr) --- } -when ODIN_OS == "darwin" { +when ODIN_OS == .Darwin { @(private) foreign libc { @(link_name="__error") __error :: proc() -> ^i32 --- } +} else when ODIN_OS == .OpenBSD { + @(private) + foreign libc { + @(link_name="__errno") __error :: proc() -> ^i32 --- + } } else { @(private) foreign libc { diff --git a/core/path/filepath/path_windows.odin b/core/path/filepath/path_windows.odin index 25b2ae500..cdfe3ddbb 100644 --- a/core/path/filepath/path_windows.odin +++ b/core/path/filepath/path_windows.odin @@ -68,7 +68,7 @@ temp_full_path :: proc(name: string) -> (path: string, err: os.Errno) { return "", os.Errno(win32.GetLastError()) } if n <= u32(len(buf)) { - return win32.utf16_to_utf8(buf[:n], ta), os.ERROR_NONE + return win32.utf16_to_utf8(buf[:n], ta) or_else "", os.ERROR_NONE } resize(&buf, len(buf)*2) } @@ -88,7 +88,7 @@ abs :: proc(path: string, allocator := context.allocator) -> (string, bool) { } -join :: proc(elems: ..string, allocator := context.allocator) -> string { +join :: proc(elems: []string, allocator := context.allocator) -> string { for e, i in elems { if e != "" { return join_non_empty(elems[i:], allocator) diff --git a/core/path/filepath/walk.odin b/core/path/filepath/walk.odin index 29d4fd5b1..dad63cc09 100644 --- a/core/path/filepath/walk.odin +++ b/core/path/filepath/walk.odin @@ -71,7 +71,7 @@ _walk :: proc(info: os.File_Info, walk_proc: Walk_Proc) -> (err: os.Errno, skip_ @(private) read_dir :: proc(dir_name: string, allocator := context.temp_allocator) -> ([]os.File_Info, os.Errno) { - f, err := os.open(dir_name) + f, err := os.open(dir_name, os.O_RDONLY) if err != 0 { return nil, err } diff --git a/core/path/slashpath/path.odin b/core/path/slashpath/path.odin index 8ac10e655..865f619bf 100644 --- a/core/path/slashpath/path.odin +++ b/core/path/slashpath/path.odin @@ -146,7 +146,7 @@ clean :: proc(path: string, allocator := context.allocator) -> string { } // join joins numerous path elements into a single path -join :: proc(elems: ..string, allocator := context.allocator) -> string { +join :: proc(elems: []string, allocator := context.allocator) -> string { context.allocator = allocator for elem, i in elems { if elem != "" { diff --git a/core/reflect/reflect.odin b/core/reflect/reflect.odin index 7f64d0974..27a83e680 100644 --- a/core/reflect/reflect.odin +++ b/core/reflect/reflect.odin @@ -234,7 +234,7 @@ is_nil :: proc(v: any) -> bool { return true } data := as_bytes(v) - if data != nil { + if data == nil { return true } for v in data { @@ -365,6 +365,19 @@ index :: proc(val: any, i: int, loc := #caller_location) -> any { return nil } +deref :: proc(val: any) -> any { + if val != nil { + ti := type_info_base(type_info_of(val.id)) + if info, ok := ti.variant.(Type_Info_Pointer); ok { + return any{ + (^rawptr)(val.data)^, + info.elem.id, + } + } + } + return val +} + // Struct_Tag represents the type of the string of a struct field @@ -641,7 +654,7 @@ union_variant_type_info :: proc(a: any) -> ^Type_Info { } type_info_union_is_pure_maybe :: proc(info: runtime.Type_Info_Union) -> bool { - return info.maybe && len(info.variants) == 1 && is_pointer(info.variants[0]) + return len(info.variants) == 1 && is_pointer(info.variants[0]) } union_variant_typeid :: proc(a: any) -> typeid { @@ -680,7 +693,6 @@ union_variant_typeid :: proc(a: any) -> typeid { return nil } panic("expected a union to reflect.union_variant_typeid") - } get_union_variant_raw_tag :: proc(a: any) -> i64 { @@ -1042,6 +1054,7 @@ as_u64 :: proc(a: any) -> (value: u64, valid: bool) { case Type_Info_Float: valid = true switch v in a { + case f16: value = u64(v) case f32: value = u64(v) case f64: value = u64(v) case f32le: value = u64(v) @@ -1147,6 +1160,7 @@ as_f64 :: proc(a: any) -> (value: f64, valid: bool) { case Type_Info_Float: valid = true switch v in a { + case f16: value = f64(v) case f32: value = f64(v) case f64: value = (v) case f32le: value = f64(v) diff --git a/core/reflect/types.odin b/core/reflect/types.odin index 74778013a..7be7ff812 100644 --- a/core/reflect/types.odin +++ b/core/reflect/types.odin @@ -256,6 +256,17 @@ is_multi_pointer :: proc(info: ^Type_Info) -> bool { _, ok := type_info_base(info).variant.(Type_Info_Multi_Pointer) return ok } +is_pointer_internally :: proc(info: ^Type_Info) -> bool { + if info == nil { return false } + #partial switch v in info.variant { + case Type_Info_Pointer, Type_Info_Multi_Pointer, + Type_Info_Procedure: + return true + case Type_Info_String: + return v.is_cstring + } + return false +} is_procedure :: proc(info: ^Type_Info) -> bool { if info == nil { return false } _, ok := type_info_base(info).variant.(Type_Info_Procedure) @@ -334,11 +345,11 @@ is_relative_slice :: proc(info: ^Type_Info) -> bool { -write_typeid_builder :: proc(buf: ^strings.Builder, id: typeid) { - write_type(buf, type_info_of(id)) +write_typeid_builder :: proc(buf: ^strings.Builder, id: typeid, n_written: ^int = nil) -> (n: int, err: io.Error) { + return write_type_writer(strings.to_writer(buf), type_info_of(id)) } -write_typeid_writer :: proc(writer: io.Writer, id: typeid) { - write_type(writer, type_info_of(id)) +write_typeid_writer :: proc(writer: io.Writer, id: typeid, n_written: ^int = nil) -> (n: int, err: io.Error) { + return write_type_writer(writer, type_info_of(id), n_written) } write_typeid :: proc{ @@ -472,6 +483,9 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info, n_written: ^int = nil) - write_type(w, info.elem, &n) or_return case Type_Info_Enumerated_Array: + if info.is_sparse { + io.write_string(w, "#sparse", &n) or_return + } io.write_string(w, "[", &n) or_return write_type(w, info.index, &n) or_return io.write_string(w, "]", &n) or_return @@ -528,9 +542,8 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info, n_written: ^int = nil) - case Type_Info_Union: io.write_string(w, "union ", &n) or_return - if info.maybe { - io.write_string(w, "#maybe ", &n) or_return - } + if info.no_nil { io.write_string(w, "#no_nil ", &n) or_return } + if info.shared_nil { io.write_string(w, "#shared_nil ", &n) or_return } if info.custom_align { io.write_string(w, "#align ", &n) or_return io.write_i64(w, i64(ti.align), 10, &n) or_return @@ -560,11 +573,11 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info, n_written: ^int = nil) - write_type(w, info.elem, &n) or_return case is_rune(info.elem): io.write_encoded_rune(w, rune(info.lower), true, &n) or_return - io.write_string(w, "..", &n) or_return + io.write_string(w, "..=", &n) or_return io.write_encoded_rune(w, rune(info.upper), true, &n) or_return case: io.write_i64(w, info.lower, 10, &n) or_return - io.write_string(w, "..", &n) or_return + io.write_string(w, "..=", &n) or_return io.write_i64(w, info.upper, 10, &n) or_return } if info.underlying != nil { diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 424650828..8fb3d7210 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -33,6 +33,11 @@ Calling_Convention :: enum u8 { None = 6, Naked = 7, + + _ = 8, // reserved + + Win64 = 9, + SysV = 10, } Type_Info_Enum_Value :: distinct i64 @@ -95,6 +100,7 @@ Type_Info_Enumerated_Array :: struct { count: int, min_value: Type_Info_Enum_Value, max_value: Type_Info_Enum_Value, + is_sparse: bool, } Type_Info_Dynamic_Array :: struct {elem: ^Type_Info, elem_size: int} Type_Info_Slice :: struct {elem: ^Type_Info, elem_size: int} @@ -129,7 +135,7 @@ Type_Info_Union :: struct { custom_align: bool, no_nil: bool, - maybe: bool, + shared_nil: bool, } Type_Info_Enum :: struct { base: ^Type_Info, @@ -261,6 +267,19 @@ type_table: []Type_Info args__: []cstring +when ODIN_OS == .Windows { + // NOTE(Jeroen): If we're a Windows DLL, fwdReason will be populated. + // This tells a DLL if it's first loaded, about to be unloaded, or a thread is joining/exiting. + + DLL_Forward_Reason :: enum u32 { + Process_Detach = 0, // About to unload DLL + Process_Attach = 1, // Entry point + Thread_Attach = 2, + Thread_Detach = 3, + } + dll_forward_reason: DLL_Forward_Reason +} + // IMPORTANT NOTE(bill): Must be in this order (as the compiler relies upon it) @@ -345,7 +364,6 @@ Context :: struct { assertion_failure_proc: Assertion_Failure_Proc, logger: Logger, - user_data: any, user_ptr: rawptr, user_index: int, @@ -386,6 +404,37 @@ Raw_Cstring :: struct { } +/* + // Defined internally by the compiler + Odin_OS_Type :: enum int { + Unknown, + Windows, + Darwin, + Linux, + Essence, + FreeBSD, + OpenBSD, + WASI, + JS, + Freestanding, + } +*/ +Odin_OS_Type :: type_of(ODIN_OS) + +/* + // Defined internally by the compiler + Odin_Arch_Type :: enum int { + Unknown, + amd64, + i386, + arm32, + arm64, + wasm32, + wasm64, + } +*/ +Odin_Arch_Type :: type_of(ODIN_ARCH) + /* // Defined internally by the compiler Odin_Build_Mode_Type :: enum int { @@ -417,7 +466,7 @@ Odin_Endian_Type :: type_of(ODIN_ENDIAN) // This is probably only useful for freestanding targets foreign { @(link_name="__$startup_runtime") - _startup_runtime :: proc() --- + _startup_runtime :: proc "odin" () --- } @(link_name="__$cleanup_runtime") @@ -425,6 +474,11 @@ _cleanup_runtime :: proc() { default_temp_allocator_destroy(&global_default_temp_allocator_data) } +_cleanup_runtime_contextless :: proc "contextless" () { + context = default_context() + _cleanup_runtime() +} + ///////////////////////////// ///////////////////////////// @@ -474,16 +528,18 @@ __type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info #no_bounds_check return &type_table[n] } -typeid_base :: proc "contextless" (id: typeid) -> typeid { - ti := type_info_of(id) - ti = type_info_base(ti) - return ti.id +when !ODIN_DISALLOW_RTTI { + typeid_base :: proc "contextless" (id: typeid) -> typeid { + ti := type_info_of(id) + ti = type_info_base(ti) + return ti.id + } + typeid_core :: proc "contextless" (id: typeid) -> typeid { + ti := type_info_core(type_info_of(id)) + return ti.id + } + typeid_base_without_enum :: typeid_core } -typeid_core :: proc "contextless" (id: typeid) -> typeid { - ti := type_info_core(type_info_of(id)) - return ti.id -} -typeid_base_without_enum :: typeid_core @@ -523,7 +579,7 @@ __init_context :: proc "contextless" (c: ^Context) { return } - // NOTE(bill): Do not initialize these procedures with a call as they are not defined with the "contexless" calling convention + // NOTE(bill): Do not initialize these procedures with a call as they are not defined with the "contextless" calling convention c.allocator.procedure = default_allocator_proc c.allocator.data = nil @@ -539,7 +595,7 @@ __init_context :: proc "contextless" (c: ^Context) { } default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code_Location) -> ! { - when ODIN_OS == "freestanding" { + when ODIN_OS == .Freestanding { // Do nothing } else { print_caller_location(loc) diff --git a/core/runtime/core_builtin.odin b/core/runtime/core_builtin.odin index 3bafc0b1d..4f698a270 100644 --- a/core/runtime/core_builtin.odin +++ b/core/runtime/core_builtin.odin @@ -3,7 +3,17 @@ package runtime import "core:intrinsics" @builtin -Maybe :: union($T: typeid) #maybe {T} +Maybe :: union($T: typeid) {T} + + +@builtin +container_of :: #force_inline proc "contextless" (ptr: $P/^$Field_Type, $T: typeid, $field_name: string) -> ^T + where intrinsics.type_has_field(T, field_name), + intrinsics.type_field_type(T, field_name) == Field_Type { + offset :: offset_of_by_string(T, field_name) + return (^T)(uintptr(ptr) - offset) if ptr != nil else nil +} + @thread_local global_default_temp_allocator_data: Default_Temp_Allocator @@ -119,6 +129,9 @@ reserve :: proc{reserve_dynamic_array, reserve_map} @builtin resize :: proc{resize_dynamic_array} +// Shrinks the capacity of a dynamic array or map down to the current length, or the given capacity. +@builtin +shrink :: proc{shrink_dynamic_array, shrink_map} @builtin free :: proc{mem_free} @@ -274,12 +287,30 @@ clear_map :: proc "contextless" (m: ^$T/map[$K]$V) { } @builtin -reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int) { +reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int, loc := #caller_location) { if m != nil { - __dynamic_map_reserve(__get_map_header(m), capacity) + __dynamic_map_reserve(__get_map_header(m), capacity, loc) } } +/* + Shrinks the capacity of a map down to the current length, or the given capacity. + + If `new_cap` is negative, then `len(m)` is used. + + Returns false if `cap(m) < new_cap`, or the allocator report failure. + + If `len(m) < new_cap`, then `len(m)` will be left unchanged. +*/ +@builtin +shrink_map :: proc(m: ^$T/map[$K]$V, new_cap := -1, loc := #caller_location) -> (did_shrink: bool) { + if m != nil { + new_cap := new_cap if new_cap >= 0 else len(m) + return __dynamic_map_shrink(__get_map_header(m), new_cap, loc) + } + return +} + // The delete_key built-in procedure deletes the element with the specified key (m[key]) from the map. // If m is nil, or there is no such element, this procedure is a no-op @builtin @@ -382,16 +413,17 @@ append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) { @builtin -insert_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #caller_location) -> (ok: bool) #no_bounds_check { +inject_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #caller_location) -> (ok: bool) #no_bounds_check { if array == nil { return } - n := len(array) + n := max(len(array), index) m :: 1 - resize(array, n+m, loc) - if n+m <= len(array) { + new_size := n + m + + if resize(array, new_size, loc) { when size_of(E) != 0 { - copy(array[index+m:], array[index:]) + copy(array[index + m:], array[index:]) array[index] = arg } ok = true @@ -400,7 +432,7 @@ insert_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #calle } @builtin -insert_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool) #no_bounds_check { +inject_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool) #no_bounds_check { if array == nil { return } @@ -409,12 +441,13 @@ insert_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #c return } - n := len(array) + n := max(len(array), index) m := len(args) - resize(array, n+m, loc) - if n+m <= len(array) { + new_size := n + m + + if resize(array, new_size, loc) { when size_of(E) != 0 { - copy(array[index+m:], array[index:]) + copy(array[index + m:], array[index:]) copy(array[index:], args) } ok = true @@ -423,27 +456,72 @@ insert_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #c } @builtin -insert_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string, loc := #caller_location) -> (ok: bool) #no_bounds_check { +inject_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string, loc := #caller_location) -> (ok: bool) #no_bounds_check { if array == nil { return } - if len(args) == 0 { + if len(arg) == 0 { ok = true return } - n := len(array) - m := len(args) - resize(array, n+m, loc) - if n+m <= len(array) { + n := max(len(array), index) + m := len(arg) + new_size := n + m + + if resize(array, new_size, loc) { copy(array[index+m:], array[index:]) + copy(array[index:], arg) + ok = true + } + return +} + +@builtin inject_at :: proc{inject_at_elem, inject_at_elems, inject_at_elem_string} + + + +@builtin +assign_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #caller_location) -> (ok: bool) #no_bounds_check { + if index < len(array) { + array[index] = arg + ok = true + } else if resize(array, index+1, loc) { + array[index] = arg + ok = true + } + return +} + + +@builtin +assign_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool) #no_bounds_check { + if index+len(args) < len(array) { + copy(array[index:], args) + ok = true + } else if resize(array, index+1+len(args), loc) { copy(array[index:], args) ok = true } return } -@builtin insert_at :: proc{insert_at_elem, insert_at_elems, insert_at_elem_string} + +@builtin +assign_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string, loc := #caller_location) -> (ok: bool) #no_bounds_check { + if len(args) == 0 { + ok = true + } else if index+len(args) < len(array) { + copy(array[index:], args) + ok = true + } else if resize(array, index+1+len(args), loc) { + copy(array[index:], args) + ok = true + } + return +} + +@builtin assign_at :: proc{assign_at_elem, assign_at_elems, assign_at_elem_string} @@ -523,6 +601,54 @@ resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller return true } +/* + Shrinks the capacity of a dynamic array down to the current length, or the given capacity. + + If `new_cap` is negative, then `len(array)` is used. + + Returns false if `cap(array) < new_cap`, or the allocator report failure. + + If `len(array) < new_cap`, then `len(array)` will be left unchanged. +*/ +shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, new_cap := -1, loc := #caller_location) -> (did_shrink: bool) { + if array == nil { + return + } + a := (^Raw_Dynamic_Array)(array) + + new_cap := new_cap if new_cap >= 0 else a.len + + if new_cap > a.cap { + return + } + + if a.allocator.procedure == nil { + a.allocator = context.allocator + } + assert(a.allocator.procedure != nil) + + old_size := a.cap * size_of(E) + new_size := new_cap * size_of(E) + + new_data, err := a.allocator.procedure( + a.allocator.data, + .Resize, + new_size, + align_of(E), + a.data, + old_size, + loc, + ) + if err != nil { + return + } + + a.data = raw_data(new_data) + a.len = min(new_cap, a.len) + a.cap = new_cap + return true +} + @builtin map_insert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location) -> (ptr: ^V) { key, value := key, value @@ -587,26 +713,30 @@ card :: proc(s: $S/bit_set[$E; $U]) -> int { @builtin -raw_array_data :: proc "contextless" (a: $P/^($T/[$N]$E)) -> ^E { - return (^E)(a) +raw_array_data :: proc "contextless" (a: $P/^($T/[$N]$E)) -> [^]E { + return ([^]E)(a) } @builtin -raw_slice_data :: proc "contextless" (s: $S/[]$E) -> ^E { +raw_simd_data :: proc "contextless" (a: $P/^($T/#simd[$N]$E)) -> [^]E { + return ([^]E)(a) +} +@builtin +raw_slice_data :: proc "contextless" (s: $S/[]$E) -> [^]E { ptr := (transmute(Raw_Slice)s).data - return (^E)(ptr) + return ([^]E)(ptr) } @builtin -raw_dynamic_array_data :: proc "contextless" (s: $S/[dynamic]$E) -> ^E { +raw_dynamic_array_data :: proc "contextless" (s: $S/[dynamic]$E) -> [^]E { ptr := (transmute(Raw_Dynamic_Array)s).data - return (^E)(ptr) + return ([^]E)(ptr) } @builtin -raw_string_data :: proc "contextless" (s: $S/string) -> ^u8 { +raw_string_data :: proc "contextless" (s: $S/string) -> [^]u8 { return (transmute(Raw_String)s).data } @builtin -raw_data :: proc{raw_array_data, raw_slice_data, raw_dynamic_array_data, raw_string_data} +raw_data :: proc{raw_array_data, raw_slice_data, raw_dynamic_array_data, raw_string_data, raw_simd_data} @@ -618,13 +748,15 @@ assert :: proc(condition: bool, message := "", loc := #caller_location) { // to improve performance to make the CPU not // execute speculatively, making it about an order of // magnitude faster - proc(message: string, loc: Source_Code_Location) { + @(cold) + internal :: proc(message: string, loc: Source_Code_Location) { p := context.assertion_failure_proc if p == nil { p = default_assertion_failure_proc } p("runtime assertion", message, loc) - }(message, loc) + } + internal(message, loc) } } diff --git a/core/runtime/default_allocators_nil.odin b/core/runtime/default_allocators_nil.odin index ccb4a3381..04dea0e19 100644 --- a/core/runtime/default_allocators_nil.odin +++ b/core/runtime/default_allocators_nil.odin @@ -32,7 +32,7 @@ nil_allocator :: proc() -> Allocator { -when ODIN_OS == "freestanding" { +when ODIN_OS == .Freestanding { default_allocator_proc :: nil_allocator_proc default_allocator :: nil_allocator } \ No newline at end of file diff --git a/core/runtime/default_allocators_windows.odin b/core/runtime/default_allocators_windows.odin index 9cabbcce8..45d4d3e6a 100644 --- a/core/runtime/default_allocators_windows.odin +++ b/core/runtime/default_allocators_windows.odin @@ -17,7 +17,7 @@ when ODIN_DEFAULT_TO_NIL_ALLOCATOR { _windows_default_free(old_memory) case .Free_All: - // NOTE(tetra): Do nothing. + return nil, .Mode_Not_Implemented case .Resize: data, err = _windows_default_resize(old_memory, old_size, size, alignment) @@ -29,7 +29,7 @@ when ODIN_DEFAULT_TO_NIL_ALLOCATOR { } case .Query_Info: - // Do nothing + return nil, .Mode_Not_Implemented } return diff --git a/core/runtime/default_temporary_allocator.odin b/core/runtime/default_temporary_allocator.odin index 01143e222..52f781980 100644 --- a/core/runtime/default_temporary_allocator.odin +++ b/core/runtime/default_temporary_allocator.odin @@ -3,7 +3,7 @@ package runtime DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE: int : #config(DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE, 1<<22) -when ODIN_OS == "freestanding" || ODIN_OS == "js" || ODIN_DEFAULT_TO_NIL_ALLOCATOR { +when ODIN_OS == .Freestanding || ODIN_OS == .JS || ODIN_DEFAULT_TO_NIL_ALLOCATOR { Default_Temp_Allocator :: struct {} default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backup_allocator := context.allocator) {} @@ -185,7 +185,7 @@ when ODIN_OS == "freestanding" || ODIN_OS == "js" || ODIN_DEFAULT_TO_NIL_ALLOCAT } case .Query_Info: - // Nothing to give + return nil, .Mode_Not_Implemented } return diff --git a/core/runtime/dynamic_array_internal.odin b/core/runtime/dynamic_array_internal.odin index 6f800de7a..d39c2dd0b 100644 --- a/core/runtime/dynamic_array_internal.odin +++ b/core/runtime/dynamic_array_internal.odin @@ -41,6 +41,35 @@ __dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, cap: return false } +__dynamic_array_shrink :: proc(array_: rawptr, elem_size, elem_align: int, new_cap: int, loc := #caller_location) -> (did_shrink: bool) { + array := (^Raw_Dynamic_Array)(array_) + + // NOTE(tetra, 2020-01-26): We set the allocator before earlying-out below, because user code is usually written + // assuming that appending/reserving will set the allocator, if it is not already set. + if array.allocator.procedure == nil { + array.allocator = context.allocator + } + assert(array.allocator.procedure != nil) + + if new_cap > array.cap { + return + } + + old_size := array.cap * elem_size + new_size := new_cap * elem_size + allocator := array.allocator + + new_data, err := allocator.procedure(allocator.data, .Resize, new_size, elem_align, array.data, old_size, loc) + if err != nil { + return + } + + array.data = raw_data(new_data) + array.len = min(new_cap, array.len) + array.cap = new_cap + return true +} + __dynamic_array_resize :: proc(array_: rawptr, elem_size, elem_align: int, len: int, loc := #caller_location) -> bool { array := (^Raw_Dynamic_Array)(array_) @@ -65,7 +94,7 @@ __dynamic_array_append :: proc(array_: rawptr, elem_size, elem_align: int, ok := true - if array.cap <= array.len+item_count { + if array.cap < array.len+item_count { cap := 2 * array.cap + max(8, item_count) ok = __dynamic_array_reserve(array, elem_size, elem_align, cap, loc) } @@ -86,7 +115,7 @@ __dynamic_array_append_nothing :: proc(array_: rawptr, elem_size, elem_align: in array := (^Raw_Dynamic_Array)(array_) ok := true - if array.cap <= array.len+1 { + if array.cap < array.len+1 { cap := 2 * array.cap + max(8, 1) ok = __dynamic_array_reserve(array, elem_size, elem_align, cap, loc) } diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin index 4d4c51d6a..fee0f570f 100644 --- a/core/runtime/dynamic_map_internal.odin +++ b/core/runtime/dynamic_map_internal.odin @@ -239,6 +239,16 @@ __dynamic_map_reserve :: proc(using header: Map_Header, cap: int, loc := #caller } } +__dynamic_map_shrink :: proc(using header: Map_Header, cap: int, loc := #caller_location) -> (did_shrink: bool) { + c := context + if m.entries.allocator.procedure != nil { + c.allocator = m.entries.allocator + } + context = c + + return __dynamic_array_shrink(&m.entries, entry_size, entry_align, cap, loc) +} + __dynamic_map_rehash :: proc(using header: Map_Header, new_count: int, loc := #caller_location) { #force_inline __dynamic_map_reserve(header, new_count, loc) } diff --git a/core/runtime/entry_unix.odin b/core/runtime/entry_unix.odin index dd1e06625..9f7d219c3 100644 --- a/core/runtime/entry_unix.odin +++ b/core/runtime/entry_unix.odin @@ -1,5 +1,5 @@ //+private -//+build linux, darwin, freebsd +//+build linux, darwin, freebsd, openbsd package runtime import "core:intrinsics" @@ -30,4 +30,4 @@ when ODIN_BUILD_MODE == .Dynamic { #force_no_inline _cleanup_runtime() return 0 } -} \ No newline at end of file +} diff --git a/core/runtime/entry_windows.odin b/core/runtime/entry_windows.odin index 35a6bb421..a315c1209 100644 --- a/core/runtime/entry_windows.odin +++ b/core/runtime/entry_windows.odin @@ -8,21 +8,25 @@ when ODIN_BUILD_MODE == .Dynamic { @(link_name="DllMain", linkage="strong", require) DllMain :: proc "stdcall" (hinstDLL: rawptr, fdwReason: u32, lpReserved: rawptr) -> b32 { context = default_context() - switch fdwReason { - case 1: // DLL_PROCESS_ATTACH + + // Populate Windows DLL-specific global + dll_forward_reason = DLL_Forward_Reason(fdwReason) + + switch dll_forward_reason { + case .Process_Attach: #force_no_inline _startup_runtime() intrinsics.__entry_point() - case 0: // DLL_PROCESS_DETACH + case .Process_Detach: #force_no_inline _cleanup_runtime() - case 2: // DLL_THREAD_ATTACH + case .Thread_Attach: break - case 3: // DLL_THREAD_DETACH + case .Thread_Detach: break } return true } } else when !ODIN_TEST && !ODIN_NO_ENTRY_POINT { - when ODIN_ARCH == "i386" || ODIN_NO_CRT { + when ODIN_ARCH == .i386 || ODIN_NO_CRT { @(link_name="mainCRTStartup", linkage="strong", require) mainCRTStartup :: proc "stdcall" () -> i32 { context = default_context() diff --git a/core/runtime/error_checks.odin b/core/runtime/error_checks.odin index 7f1aeb2d7..0d0b39072 100644 --- a/core/runtime/error_checks.odin +++ b/core/runtime/error_checks.odin @@ -1,7 +1,7 @@ package runtime bounds_trap :: proc "contextless" () -> ! { - when ODIN_OS == "windows" { + when ODIN_OS == .Windows { windows_trap_array_bounds() } else { trap() @@ -9,7 +9,7 @@ bounds_trap :: proc "contextless" () -> ! { } type_assertion_trap :: proc "contextless" () -> ! { - when ODIN_OS == "windows" { + when ODIN_OS == .Windows { windows_trap_type_assertion() } else { trap() @@ -21,11 +21,12 @@ bounds_check_error :: proc "contextless" (file: string, line, column: i32, index if 0 <= index && index < count { return } + @(cold) handle_error :: proc "contextless" (file: string, line, column: i32, index, count: int) { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Index ") print_i64(i64(index)) - print_string(" is out of bounds range 0:") + print_string(" is out of range 0..<") print_i64(i64(count)) print_byte('\n') bounds_trap() @@ -35,11 +36,11 @@ bounds_check_error :: proc "contextless" (file: string, line, column: i32, index slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, hi: int, len: int) -> ! { print_caller_location(Source_Code_Location{file, line, column, ""}) - print_string(" Invalid slice indices: ") + print_string(" Invalid slice indices ") print_i64(i64(lo)) print_string(":") print_i64(i64(hi)) - print_string(":") + print_string(" is out of range 0..<") print_i64(i64(len)) print_byte('\n') bounds_trap() @@ -47,7 +48,7 @@ slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, h multi_pointer_slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, hi: int) -> ! { print_caller_location(Source_Code_Location{file, line, column, ""}) - print_string(" Invalid slice indices: ") + print_string(" Invalid slice indices ") print_i64(i64(lo)) print_string(":") print_i64(i64(hi)) @@ -81,13 +82,14 @@ dynamic_array_expr_error :: proc "contextless" (file: string, line, column: i32, if 0 <= low && low <= high && high <= max { return } + @(cold) handle_error :: proc "contextless" (file: string, line, column: i32, low, high, max: int) { print_caller_location(Source_Code_Location{file, line, column, ""}) - print_string(" Invalid dynamic array values: ") + print_string(" Invalid dynamic array indices ") print_i64(i64(low)) print_string(":") print_i64(i64(high)) - print_string(":") + print_string(" is out of range 0..<") print_i64(i64(max)) print_byte('\n') bounds_trap() @@ -97,17 +99,18 @@ dynamic_array_expr_error :: proc "contextless" (file: string, line, column: i32, matrix_bounds_check_error :: proc "contextless" (file: string, line, column: i32, row_index, column_index, row_count, column_count: int) { - if 0 <= row_index && row_index < row_count && + if 0 <= row_index && row_index < row_count && 0 <= column_index && column_index < column_count { return } + @(cold) handle_error :: proc "contextless" (file: string, line, column: i32, row_index, column_index, row_count, column_count: int) { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Matrix indices [") print_i64(i64(row_index)) print_string(", ") print_i64(i64(column_index)) - print_string(" is out of bounds range [0..<") + print_string(" is out of range [0..<") print_i64(i64(row_count)) print_string(", 0..<") print_i64(i64(column_count)) @@ -119,71 +122,101 @@ matrix_bounds_check_error :: proc "contextless" (file: string, line, column: i32 } -type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid) { - if ok { - return - } - handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid) { - print_caller_location(Source_Code_Location{file, line, column, ""}) - print_string(" Invalid type assertion from ") - print_typeid(from) - print_string(" to ") - print_typeid(to) - print_byte('\n') - type_assertion_trap() - } - handle_error(file, line, column, from, to) -} - -type_assertion_check2 :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid, from_data: rawptr) { - if ok { - return +when ODIN_DISALLOW_RTTI { + type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column: i32) { + if ok { + return + } + @(cold) + handle_error :: proc "contextless" (file: string, line, column: i32) { + print_caller_location(Source_Code_Location{file, line, column, ""}) + print_string(" Invalid type assertion\n") + type_assertion_trap() + } + handle_error(file, line, column) } - variant_type :: proc "contextless" (id: typeid, data: rawptr) -> typeid { - if id == nil || data == nil { + type_assertion_check2 :: proc "contextless" (ok: bool, file: string, line, column: i32) { + if ok { + return + } + @(cold) + handle_error :: proc "contextless" (file: string, line, column: i32) { + print_caller_location(Source_Code_Location{file, line, column, ""}) + print_string(" Invalid type assertion\n") + type_assertion_trap() + } + handle_error(file, line, column) + } +} else { + type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid) { + if ok { + return + } + @(cold) + handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid) { + print_caller_location(Source_Code_Location{file, line, column, ""}) + print_string(" Invalid type assertion from ") + print_typeid(from) + print_string(" to ") + print_typeid(to) + print_byte('\n') + type_assertion_trap() + } + handle_error(file, line, column, from, to) + } + + type_assertion_check2 :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid, from_data: rawptr) { + if ok { + return + } + + variant_type :: proc "contextless" (id: typeid, data: rawptr) -> typeid { + if id == nil || data == nil { + return id + } + ti := type_info_base(type_info_of(id)) + #partial switch v in ti.variant { + case Type_Info_Any: + return (^any)(data).id + case Type_Info_Union: + tag_ptr := uintptr(data) + v.tag_offset + idx := 0 + switch v.tag_type.size { + case 1: idx = int((^u8)(tag_ptr)^) - 1 + case 2: idx = int((^u16)(tag_ptr)^) - 1 + case 4: idx = int((^u32)(tag_ptr)^) - 1 + case 8: idx = int((^u64)(tag_ptr)^) - 1 + case 16: idx = int((^u128)(tag_ptr)^) - 1 + } + if idx < 0 { + return nil + } else if idx < len(v.variants) { + return v.variants[idx].id + } + } return id } - ti := type_info_base(type_info_of(id)) - #partial switch v in ti.variant { - case Type_Info_Any: - return (^any)(data).id - case Type_Info_Union: - tag_ptr := uintptr(data) + v.tag_offset - idx := 0 - switch v.tag_type.size { - case 1: idx = int((^u8)(tag_ptr)^) - 1 - case 2: idx = int((^u16)(tag_ptr)^) - 1 - case 4: idx = int((^u32)(tag_ptr)^) - 1 - case 8: idx = int((^u64)(tag_ptr)^) - 1 - case 16: idx = int((^u128)(tag_ptr)^) - 1 - } - if idx < 0 { - return nil - } else if idx < len(v.variants) { - return v.variants[idx].id + + @(cold) + handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) { + + actual := variant_type(from, from_data) + + print_caller_location(Source_Code_Location{file, line, column, ""}) + print_string(" Invalid type assertion from ") + print_typeid(from) + print_string(" to ") + print_typeid(to) + if actual != from { + print_string(", actual type: ") + print_typeid(actual) } + print_byte('\n') + type_assertion_trap() } - return id + handle_error(file, line, column, from, to, from_data) } - - handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) { - - actual := variant_type(from, from_data) - - print_caller_location(Source_Code_Location{file, line, column, ""}) - print_string(" Invalid type assertion from ") - print_typeid(from) - print_string(" to ") - print_typeid(to) - if actual != from { - print_string(", actual type: ") - print_typeid(actual) - } - print_byte('\n') - type_assertion_trap() - } - handle_error(file, line, column, from, to, from_data) } @@ -191,6 +224,7 @@ make_slice_error_loc :: #force_inline proc "contextless" (loc := #caller_locatio if 0 <= len { return } + @(cold) handle_error :: proc "contextless" (loc: Source_Code_Location, len: int) { print_caller_location(loc) print_string(" Invalid slice length for make: ") @@ -205,6 +239,7 @@ make_dynamic_array_error_loc :: #force_inline proc "contextless" (using loc := # if 0 <= len && len <= cap { return } + @(cold) handle_error :: proc "contextless" (loc: Source_Code_Location, len, cap: int) { print_caller_location(loc) print_string(" Invalid dynamic array parameters for make: ") @@ -221,6 +256,7 @@ make_map_expr_error_loc :: #force_inline proc "contextless" (loc := #caller_loca if 0 <= cap { return } + @(cold) handle_error :: proc "contextless" (loc: Source_Code_Location, cap: int) { print_caller_location(loc) print_string(" Invalid map capacity for make: ") diff --git a/core/runtime/internal.odin b/core/runtime/internal.odin index 7b283a132..30798f623 100644 --- a/core/runtime/internal.odin +++ b/core/runtime/internal.odin @@ -3,7 +3,7 @@ package runtime import "core:intrinsics" @(private="file") -IS_WASM :: ODIN_ARCH == "wasm32" || ODIN_ARCH == "wasm64" +IS_WASM :: ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64 @(private) RUNTIME_LINKAGE :: "strong" when ( @@ -37,10 +37,8 @@ bswap_64 :: proc "contextless" (x: u64) -> u64 { bswap_128 :: proc "contextless" (x: u128) -> u128 { z := transmute([4]u32)x - z[0] = bswap_32(z[3]) - z[1] = bswap_32(z[2]) - z[2] = bswap_32(z[1]) - z[3] = bswap_32(z[0]) + z[0], z[3] = bswap_32(z[3]), bswap_32(z[0]) + z[1], z[2] = bswap_32(z[2]), bswap_32(z[1]) return transmute(u128)z } diff --git a/core/runtime/print.odin b/core/runtime/print.odin index 8a14eba08..89c196fc2 100644 --- a/core/runtime/print.odin +++ b/core/runtime/print.odin @@ -143,18 +143,36 @@ print_int :: proc "contextless" (x: int) { print_i64(i64(x)) } print_caller_location :: proc "contextless" (using loc: Source_Code_Location) { print_string(file_path) - print_byte('(') - print_u64(u64(line)) - print_byte(':') - print_u64(u64(column)) - print_byte(')') + when ODIN_ERROR_POS_STYLE == .Default { + print_byte('(') + print_u64(u64(line)) + print_byte(':') + print_u64(u64(column)) + print_byte(')') + } else when ODIN_ERROR_POS_STYLE == .Unix { + print_byte(':') + print_u64(u64(line)) + print_byte(':') + print_u64(u64(column)) + print_byte(':') + } else { + #panic("unhandled ODIN_ERROR_POS_STYLE") + } } print_typeid :: proc "contextless" (id: typeid) { - if id == nil { - print_string("nil") + when ODIN_DISALLOW_RTTI { + if id == nil { + print_string("nil") + } else { + print_string("") + } } else { - ti := type_info_of(id) - print_type(ti) + if id == nil { + print_string("nil") + } else { + ti := type_info_of(id) + print_type(ti) + } } } print_type :: proc "contextless" (ti: ^Type_Info) { @@ -250,6 +268,9 @@ print_type :: proc "contextless" (ti: ^Type_Info) { print_type(info.elem) case Type_Info_Enumerated_Array: + if info.is_sparse { + print_string("#sparse") + } print_byte('[') print_type(info.index) print_byte(']') diff --git a/core/runtime/procs.odin b/core/runtime/procs.odin index 961f6376f..782efa773 100644 --- a/core/runtime/procs.odin +++ b/core/runtime/procs.odin @@ -1,10 +1,10 @@ package runtime -when ODIN_NO_CRT && ODIN_OS == "windows" { +when ODIN_NO_CRT && ODIN_OS == .Windows { foreign import lib "system:NtDll.lib" @(private="file") - @(default_calling_convention="std") + @(default_calling_convention="stdcall") foreign lib { RtlMoveMemory :: proc(dst, src: rawptr, length: int) --- RtlFillMemory :: proc(dst: rawptr, length: int, fill: i32) --- @@ -25,7 +25,7 @@ when ODIN_NO_CRT && ODIN_OS == "windows" { RtlMoveMemory(dst, src, len) return dst } -} else when ODIN_NO_CRT || (ODIN_ARCH == "wasm32" || ODIN_ARCH == "wasm64") { +} else when ODIN_NO_CRT || (ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64) { @(link_name="memset", linkage="strong", require) memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr { if ptr != nil && len != 0 { diff --git a/core/runtime/procs_darwin.odin b/core/runtime/procs_darwin.odin new file mode 100644 index 000000000..b54a28dcc --- /dev/null +++ b/core/runtime/procs_darwin.odin @@ -0,0 +1,21 @@ +//+private +package runtime + +foreign import "system:Foundation.framework" + +import "core:intrinsics" + +objc_id :: ^intrinsics.objc_object +objc_Class :: ^intrinsics.objc_class +objc_SEL :: ^intrinsics.objc_selector + +foreign Foundation { + objc_lookUpClass :: proc "c" (name: cstring) -> objc_Class --- + sel_registerName :: proc "c" (name: cstring) -> objc_SEL --- + objc_allocateClassPair :: proc "c" (superclass: objc_Class, name: cstring, extraBytes: uint) --- + + objc_msgSend :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) --- + objc_msgSend_fpret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) -> f64 --- + objc_msgSend_fp2ret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) -> complex128 --- + objc_msgSend_stret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) --- +} diff --git a/core/simd/simd.odin b/core/simd/simd.odin new file mode 100644 index 000000000..a0a4df28d --- /dev/null +++ b/core/simd/simd.odin @@ -0,0 +1,187 @@ +package simd + +import "core:builtin" +import "core:intrinsics" + +// 128-bit vector aliases +u8x16 :: #simd[16]u8 +i8x16 :: #simd[16]i8 +u16x8 :: #simd[8]u16 +i16x8 :: #simd[8]i16 +u32x4 :: #simd[4]u32 +i32x4 :: #simd[4]i32 +u64x2 :: #simd[2]u64 +i64x2 :: #simd[2]i64 +f32x4 :: #simd[4]f32 +f64x2 :: #simd[2]f64 + +boolx16 :: #simd[16]bool +b8x16 :: #simd[16]b8 +b16x8 :: #simd[8]b16 +b32x4 :: #simd[4]b32 +b64x2 :: #simd[2]b64 + +// 256-bit vector aliases +u8x32 :: #simd[32]u8 +i8x32 :: #simd[32]i8 +u16x16 :: #simd[16]u16 +i16x16 :: #simd[16]i16 +u32x8 :: #simd[8]u32 +i32x8 :: #simd[8]i32 +u64x4 :: #simd[4]u64 +i64x4 :: #simd[4]i64 +f32x8 :: #simd[8]f32 +f64x4 :: #simd[4]f64 + +boolx32 :: #simd[32]bool +b8x32 :: #simd[32]b8 +b16x16 :: #simd[16]b16 +b32x8 :: #simd[8]b32 +b64x4 :: #simd[4]b64 + +// 512-bit vector aliases +u8x64 :: #simd[64]u8 +i8x64 :: #simd[64]i8 +u16x32 :: #simd[32]u16 +i16x32 :: #simd[32]i16 +u32x16 :: #simd[16]u32 +i32x16 :: #simd[16]i32 +u64x8 :: #simd[8]u64 +i64x8 :: #simd[8]i64 +f32x16 :: #simd[16]f32 +f64x8 :: #simd[8]f64 + +boolx64 :: #simd[64]bool +b8x64 :: #simd[64]b8 +b16x32 :: #simd[32]b16 +b32x16 :: #simd[16]b32 +b64x8 :: #simd[8]b64 + + +add :: intrinsics.simd_add +sub :: intrinsics.simd_sub +mul :: intrinsics.simd_mul +div :: intrinsics.simd_div // floats only + +// Keeps Odin's Behaviour +// (x << y) if y <= mask else 0 +shl :: intrinsics.simd_shl +shr :: intrinsics.simd_shr + +// Similar to C's Behaviour +// x << (y & mask) +shl_masked :: intrinsics.simd_shl_masked +shr_masked :: intrinsics.simd_shr_masked + +// Saturation Arithmetic +add_sat :: intrinsics.simd_add_sat +sub_sat :: intrinsics.simd_sub_sat + +and :: intrinsics.simd_and +or :: intrinsics.simd_or +xor :: intrinsics.simd_xor +and_not :: intrinsics.simd_and_not + +neg :: intrinsics.simd_neg + +abs :: intrinsics.simd_abs + +min :: intrinsics.simd_min +max :: intrinsics.simd_max +clamp :: intrinsics.simd_clamp + +// Return an unsigned integer of the same size as the input type +// NOT A BOOLEAN +// element-wise: +// false => 0x00...00 +// true => 0xff...ff +lanes_eq :: intrinsics.simd_lanes_eq +lanes_ne :: intrinsics.simd_lanes_ne +lanes_lt :: intrinsics.simd_lanes_lt +lanes_le :: intrinsics.simd_lanes_le +lanes_gt :: intrinsics.simd_lanes_gt +lanes_ge :: intrinsics.simd_lanes_ge + +// extract :: proc(a: #simd[N]T, idx: uint) -> T +extract :: intrinsics.simd_extract +// replace :: proc(a: #simd[N]T, idx: uint, elem: T) -> #simd[N]T +replace :: intrinsics.simd_replace + +reduce_add_ordered :: intrinsics.simd_reduce_add_ordered +reduce_mul_ordered :: intrinsics.simd_reduce_mul_ordered +reduce_min :: intrinsics.simd_reduce_min +reduce_max :: intrinsics.simd_reduce_max +reduce_and :: intrinsics.simd_reduce_and +reduce_or :: intrinsics.simd_reduce_or +reduce_xor :: intrinsics.simd_reduce_xor + +// swizzle :: proc(a: #simd[N]T, indices: ..int) -> #simd[len(indices)]T +swizzle :: builtin.swizzle + +// shuffle :: proc(a, b: #simd[N]T, indices: #simd[max 2*N]u32) -> #simd[len(indices)]T +shuffle :: intrinsics.simd_shuffle + +// select :: proc(cond: #simd[N]boolean_or_integer, true, false: #simd[N]T) -> #simd[N]T +select :: intrinsics.simd_select + + +sqrt :: intrinsics.sqrt +ceil :: intrinsics.simd_ceil +floor :: intrinsics.simd_floor +trunc :: intrinsics.simd_trunc +nearest :: intrinsics.simd_nearest + +to_bits :: intrinsics.simd_to_bits + +lanes_reverse :: intrinsics.simd_lanes_reverse + +lanes_rotate_left :: intrinsics.simd_lanes_rotate_left +lanes_rotate_right :: intrinsics.simd_lanes_rotate_right + +count_ones :: intrinsics.count_ones +count_zeros :: intrinsics.count_zeros +count_trailing_zeros :: intrinsics.count_trailing_zeros +count_leading_zeros :: intrinsics.count_leading_zeros +reverse_bits :: intrinsics.reverse_bits + +fused_mul_add :: intrinsics.fused_mul_add +fma :: intrinsics.fused_mul_add + +to_array_ptr :: #force_inline proc "contextless" (v: ^#simd[$LANES]$E) -> ^[LANES]E { + return (^[LANES]E)(v) +} +to_array :: #force_inline proc "contextless" (v: #simd[$LANES]$E) -> [LANES]E { + return transmute([LANES]E)(v) +} +from_array :: #force_inline proc "contextless" (v: $A/[$LANES]$E) -> #simd[LANES]E { + return transmute(#simd[LANES]E)v +} + +from_slice :: proc($T: typeid/#simd[$LANES]$E, slice: []E) -> T { + assert(len(slice) >= LANES, "slice length must be a least the number of lanes") + array: [LANES]E + #no_bounds_check for i in 0.. T where intrinsics.type_is_integer(E) { + return xor(v, T(~E(0))) +} + +copysign :: #force_inline proc "contextless" (v, sign: $T/#simd[$LANES]$E) -> T where intrinsics.type_is_float(E) { + neg_zero := to_bits(T(-0.0)) + sign_bit := to_bits(sign) & neg_zero + magnitude := to_bits(v) &~ neg_zero + return transmute(T)(sign_bit|magnitude) +} + +signum :: #force_inline proc "contextless" (v: $T/#simd[$LANES]$E) -> T where intrinsics.type_is_float(E) { + is_nan := lanes_ne(v, v) + return select(is_nan, v, copysign(T(1), v)) +} + +recip :: #force_inline proc "contextless" (v: $T/#simd[$LANES]$E) -> T where intrinsics.type_is_float(E) { + return T(1) / v +} diff --git a/core/simd/x86/abm.odin b/core/simd/x86/abm.odin new file mode 100644 index 000000000..79b806242 --- /dev/null +++ b/core/simd/x86/abm.odin @@ -0,0 +1,24 @@ +//+build i386, amd64 +package simd_x86 + +import "core:intrinsics" + +@(require_results, enable_target_feature="lzcnt") +_lzcnt_u32 :: #force_inline proc "c" (x: u32) -> u32 { + return intrinsics.count_leading_zeros(x) +} +@(require_results, enable_target_feature="popcnt") +_popcnt32 :: #force_inline proc "c" (x: u32) -> i32 { + return i32(intrinsics.count_ones(x)) +} + +when ODIN_ARCH == .amd64 { + @(require_results, enable_target_feature="lzcnt") + _lzcnt_u64 :: #force_inline proc "c" (x: u64) -> u64 { + return intrinsics.count_leading_zeros(x) + } + @(require_results, enable_target_feature="popcnt") + _popcnt64 :: #force_inline proc "c" (x: u64) -> i32 { + return i32(intrinsics.count_ones(x)) + } +} \ No newline at end of file diff --git a/core/simd/x86/adx.odin b/core/simd/x86/adx.odin new file mode 100644 index 000000000..d03cffcff --- /dev/null +++ b/core/simd/x86/adx.odin @@ -0,0 +1,56 @@ +//+build i386, amd64 +package simd_x86 + +@(require_results) +_addcarry_u32 :: #force_inline proc "c" (c_in: u8, a: u32, b: u32, out: ^u32) -> u8 { + x, y := llvm_addcarry_u32(c_in, a, b) + out^ = y + return x +} +@(require_results) +_addcarryx_u32 :: #force_inline proc "c" (c_in: u8, a: u32, b: u32, out: ^u32) -> u8 { + return llvm_addcarryx_u32(c_in, a, b, out) +} +@(require_results) +_subborrow_u32 :: #force_inline proc "c" (c_in: u8, a: u32, b: u32, out: ^u32) -> u8 { + x, y := llvm_subborrow_u32(c_in, a, b) + out^ = y + return x +} + +when ODIN_ARCH == .amd64 { + @(require_results) + _addcarry_u64 :: #force_inline proc "c" (c_in: u8, a: u64, b: u64, out: ^u64) -> u8 { + x, y := llvm_addcarry_u64(c_in, a, b) + out^ = y + return x + } + @(require_results) + _addcarryx_u64 :: #force_inline proc "c" (c_in: u8, a: u64, b: u64, out: ^u64) -> u8 { + return llvm_addcarryx_u64(c_in, a, b, out) + } + @(require_results) + _subborrow_u64 :: #force_inline proc "c" (c_in: u8, a: u64, b: u64, out: ^u64) -> u8 { + x, y := llvm_subborrow_u64(c_in, a, b) + out^ = y + return x + } +} + +@(private, default_calling_convention="c") +foreign _ { + @(link_name="llvm.x86.addcarry.32") + llvm_addcarry_u32 :: proc(a: u8, b: u32, c: u32) -> (u8, u32) --- + @(link_name="llvm.x86.addcarryx.u32") + llvm_addcarryx_u32 :: proc(a: u8, b: u32, c: u32, d: rawptr) -> u8 --- + @(link_name="llvm.x86.subborrow.32") + llvm_subborrow_u32 :: proc(a: u8, b: u32, c: u32) -> (u8, u32) --- + + // amd64 only + @(link_name="llvm.x86.addcarry.64") + llvm_addcarry_u64 :: proc(a: u8, b: u64, c: u64) -> (u8, u64) --- + @(link_name="llvm.x86.addcarryx.u64") + llvm_addcarryx_u64 :: proc(a: u8, b: u64, c: u64, d: rawptr) -> u8 --- + @(link_name="llvm.x86.subborrow.64") + llvm_subborrow_u64 :: proc(a: u8, b: u64, c: u64) -> (u8, u64) --- +} diff --git a/core/simd/x86/cmpxchg16b.odin b/core/simd/x86/cmpxchg16b.odin new file mode 100644 index 000000000..d575dd9df --- /dev/null +++ b/core/simd/x86/cmpxchg16b.odin @@ -0,0 +1,8 @@ +//+build amd64 +package simd_x86 + +import "core:intrinsics" + +cmpxchg16b :: #force_inline proc "c" (dst: ^u128, old, new: u128, $success, $failure: intrinsics.Atomic_Memory_Order) -> (val: u128) { + return intrinsics.atomic_compare_exchange_strong_explicit(dst, old, new, success, failure) +} \ No newline at end of file diff --git a/core/simd/x86/cpu.odin b/core/simd/x86/cpu.odin new file mode 100644 index 000000000..14e90c0f0 --- /dev/null +++ b/core/simd/x86/cpu.odin @@ -0,0 +1,94 @@ +//+build i386, amd64 +package simd_x86 + +import "core:intrinsics" + +// cpuid :: proc(ax, cx: u32) -> (eax, ebc, ecx, edx: u32) --- +cpuid :: intrinsics.x86_cpuid + +// xgetbv :: proc(cx: u32) -> (eax, edx: u32) --- +xgetbv :: intrinsics.x86_xgetbv + + +CPU_Feature :: enum u64 { + aes, // AES hardware implementation (AES NI) + adx, // Multi-precision add-carry instruction extensions + avx, // Advanced vector extension + avx2, // Advanced vector extension 2 + bmi1, // Bit manipulation instruction set 1 + bmi2, // Bit manipulation instruction set 2 + erms, // Enhanced REP for MOVSB and STOSB + fma, // Fused-multiply-add instructions + os_xsave, // OS supports XSAVE/XRESTOR for saving/restoring XMM registers. + pclmulqdq, // PCLMULQDQ instruction - most often used for AES-GCM + popcnt, // Hamming weight instruction POPCNT. + rdrand, // RDRAND instruction (on-chip random number generator) + rdseed, // RDSEED instruction (on-chip random number generator) + sse2, // Streaming SIMD extension 2 (always available on amd64) + sse3, // Streaming SIMD extension 3 + ssse3, // Supplemental streaming SIMD extension 3 + sse41, // Streaming SIMD extension 4 and 4.1 + sse42, // Streaming SIMD extension 4 and 4.2 +} + +CPU_Features :: distinct bit_set[CPU_Feature; u64] + +cpu_features: Maybe(CPU_Features) + +@(init, private) +init_cpu_features :: proc "c" () { + is_set :: #force_inline proc "c" (hwc: u32, value: u32) -> bool { + return hwc&value != 0 + } + try_set :: #force_inline proc "c" (set: ^CPU_Features, feature: CPU_Feature, hwc: u32, value: u32) { + if is_set(hwc, value) { + set^ += {feature} + } + } + + max_id, _, _, _ := cpuid(0, 0) + if max_id < 1 { + return + } + + set: CPU_Features + + _, _, ecx1, edx1 := cpuid(1, 0) + + try_set(&set, .sse2, 26, edx1) + try_set(&set, .sse3, 0, ecx1) + try_set(&set, .pclmulqdq, 1, ecx1) + try_set(&set, .ssse3, 9, ecx1) + try_set(&set, .fma, 12, ecx1) + try_set(&set, .sse41, 19, ecx1) + try_set(&set, .sse42, 20, ecx1) + try_set(&set, .popcnt, 23, ecx1) + try_set(&set, .aes, 25, ecx1) + try_set(&set, .os_xsave, 27, ecx1) + try_set(&set, .rdrand, 30, ecx1) + + os_supports_avx := false + if .os_xsave in set { + eax, _ := xgetbv(0) + os_supports_avx = is_set(1, eax) && is_set(2, eax) + } + if os_supports_avx { + try_set(&set, .avx, 28, ecx1) + } + + if max_id < 7 { + return + } + + _, ebx7, _, _ := cpuid(7, 0) + try_set(&set, .bmi1, 3, ebx7) + if os_supports_avx { + try_set(&set, .avx2, 5, ebx7) + } + try_set(&set, .bmi2, 8, ebx7) + try_set(&set, .erms, 9, ebx7) + try_set(&set, .rdseed, 18, ebx7) + try_set(&set, .adx, 19, ebx7) + + cpu_features = set +} diff --git a/core/simd/x86/fxsr.odin b/core/simd/x86/fxsr.odin new file mode 100644 index 000000000..cd78de7d4 --- /dev/null +++ b/core/simd/x86/fxsr.odin @@ -0,0 +1,36 @@ +//+build i386, amd64 +package simd_x86 + +@(enable_target_feature="fxsr") +_fxsave :: #force_inline proc "c" (mem_addr: rawptr) { + fxsave(mem_addr) +} +@(enable_target_feature="fxsr") +_fxrstor :: #force_inline proc "c" (mem_addr: rawptr) { + fxrstor(mem_addr) +} + +when ODIN_ARCH == .amd64 { + @(enable_target_feature="fxsr") + _fxsave64 :: #force_inline proc "c" (mem_addr: rawptr) { + fxsave64(mem_addr) + } + @(enable_target_feature="fxsr") + _fxrstor64 :: #force_inline proc "c" (mem_addr: rawptr) { + fxrstor64(mem_addr) + } +} + +@(private, default_calling_convention="c") +foreign _ { + @(link_name="llvm.x86.fxsave") + fxsave :: proc(p: rawptr) --- + @(link_name="llvm.x86.fxrstor") + fxrstor :: proc(p: rawptr) --- + + // amd64 only + @(link_name="llvm.x86.fxsave64") + fxsave64 :: proc(p: rawptr) --- + @(link_name="llvm.x86.fxrstor64") + fxrstor64 :: proc(p: rawptr) --- +} \ No newline at end of file diff --git a/core/simd/x86/pclmulqdq.odin b/core/simd/x86/pclmulqdq.odin new file mode 100644 index 000000000..692fb7ce1 --- /dev/null +++ b/core/simd/x86/pclmulqdq.odin @@ -0,0 +1,13 @@ +//+build i386, amd64 +package simd_x86 + +@(require_results, enable_target_feature="pclmulqdq") +_mm_clmulepi64_si128 :: #force_inline proc "c" (a, b: __m128i, $IMM8: u8) -> __m128i { + return pclmulqdq(a, b, u8(IMM8)) +} + +@(private, default_calling_convention="c") +foreign _ { + @(link_name="llvm.x86.pclmulqdq") + pclmulqdq :: proc(a, round_key: __m128i, #const imm8: u8) -> __m128i --- +} \ No newline at end of file diff --git a/core/simd/x86/rdtsc.odin b/core/simd/x86/rdtsc.odin new file mode 100644 index 000000000..54024c3f2 --- /dev/null +++ b/core/simd/x86/rdtsc.odin @@ -0,0 +1,20 @@ +//+build i386, amd64 +package simd_x86 + +@(require_results) +_rdtsc :: #force_inline proc "c" () -> u64 { + return rdtsc() +} + +@(require_results) +__rdtscp :: #force_inline proc "c" (aux: ^u32) -> u64 { + return rdtscp(aux) +} + +@(private, default_calling_convention="c") +foreign _ { + @(link_name="llvm.x86.rdtsc") + rdtsc :: proc() -> u64 --- + @(link_name="llvm.x86.rdtscp") + rdtscp :: proc(aux: rawptr) -> u64 --- +} \ No newline at end of file diff --git a/core/simd/x86/sha.odin b/core/simd/x86/sha.odin new file mode 100644 index 000000000..f015f4b8a --- /dev/null +++ b/core/simd/x86/sha.odin @@ -0,0 +1,49 @@ +//+build i386, amd64 +package simd_x86 + +@(require_results, enable_target_feature="sha") +_mm_sha1msg1_epu32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)sha1msg1(transmute(i32x4)a, transmute(i32x4)b) +} +@(require_results, enable_target_feature="sha") +_mm_sha1msg2_epu32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)sha1msg2(transmute(i32x4)a, transmute(i32x4)b) +} +@(require_results, enable_target_feature="sha") +_mm_sha1nexte_epu32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)sha1nexte(transmute(i32x4)a, transmute(i32x4)b) +} +@(require_results, enable_target_feature="sha") +_mm_sha1rnds4_epu32 :: #force_inline proc "c" (a, b: __m128i, $FUNC: u32) -> __m128i where 0 <= FUNC, FUNC <= 3 { + return transmute(__m128i)sha1rnds4(transmute(i32x4)a, transmute(i32x4)b, u8(FUNC & 0xff)) +} +@(require_results, enable_target_feature="sha") +_mm_sha256msg1_epu32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)sha256msg1(transmute(i32x4)a, transmute(i32x4)b) +} +@(require_results, enable_target_feature="sha") +_mm_sha256msg2_epu32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)sha256msg2(transmute(i32x4)a, transmute(i32x4)b) +} +@(require_results, enable_target_feature="sha") +_mm_sha256rnds2_epu32 :: #force_inline proc "c" (a, b, k: __m128i) -> __m128i { + return transmute(__m128i)sha256rnds2(transmute(i32x4)a, transmute(i32x4)b, transmute(i32x4)k) +} + +@(private, default_calling_convention="c") +foreign _ { + @(link_name="llvm.x86.sha1msg1") + sha1msg1 :: proc(a, b: i32x4) -> i32x4 --- + @(link_name="llvm.x86.sha1msg2") + sha1msg2 :: proc(a, b: i32x4) -> i32x4 --- + @(link_name="llvm.x86.sha1nexte") + sha1nexte :: proc(a, b: i32x4) -> i32x4 --- + @(link_name="llvm.x86.sha1rnds4") + sha1rnds4 :: proc(a, b: i32x4, #const c: u8) -> i32x4 --- + @(link_name="llvm.x86.sha256msg1") + sha256msg1 :: proc(a, b: i32x4) -> i32x4 --- + @(link_name="llvm.x86.sha256msg2") + sha256msg2 :: proc(a, b: i32x4) -> i32x4 --- + @(link_name="llvm.x86.sha256rnds2") + sha256rnds2 :: proc(a, b, k: i32x4) -> i32x4 --- +} \ No newline at end of file diff --git a/core/simd/x86/sse.odin b/core/simd/x86/sse.odin new file mode 100644 index 000000000..3efdeccba --- /dev/null +++ b/core/simd/x86/sse.odin @@ -0,0 +1,618 @@ +//+build i386, amd64 +package simd_x86 + +import "core:intrinsics" +import "core:simd" + +// _MM_SHUFFLE(z, y, x, w) -> (z<<6 | y<<4 | x<<2 | w) +_MM_SHUFFLE :: intrinsics.simd_x86__MM_SHUFFLE + +_MM_HINT_T0 :: 3 +_MM_HINT_T1 :: 2 +_MM_HINT_T2 :: 1 +_MM_HINT_NTA :: 0 +_MM_HINT_ET0 :: 7 +_MM_HINT_ET1 :: 6 + + +_MM_EXCEPT_INVALID :: 0x0001 +_MM_EXCEPT_DENORM :: 0x0002 +_MM_EXCEPT_DIV_ZERO :: 0x0004 +_MM_EXCEPT_OVERFLOW :: 0x0008 +_MM_EXCEPT_UNDERFLOW :: 0x0010 +_MM_EXCEPT_INEXACT :: 0x0020 +_MM_EXCEPT_MASK :: 0x003f + +_MM_MASK_INVALID :: 0x0080 +_MM_MASK_DENORM :: 0x0100 +_MM_MASK_DIV_ZERO :: 0x0200 +_MM_MASK_OVERFLOW :: 0x0400 +_MM_MASK_UNDERFLOW :: 0x0800 +_MM_MASK_INEXACT :: 0x1000 +_MM_MASK_MASK :: 0x1f80 + +_MM_ROUND_NEAREST :: 0x0000 +_MM_ROUND_DOWN :: 0x2000 +_MM_ROUND_UP :: 0x4000 +_MM_ROUND_TOWARD_ZERO :: 0x6000 + +_MM_ROUND_MASK :: 0x6000 + +_MM_FLUSH_ZERO_MASK :: 0x8000 +_MM_FLUSH_ZERO_ON :: 0x8000 +_MM_FLUSH_ZERO_OFF :: 0x0000 + + +@(require_results, enable_target_feature="sse") +_mm_add_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return addss(a, b) +} +@(require_results, enable_target_feature="sse") +_mm_add_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return simd.add(a, b) +} + +@(require_results, enable_target_feature="sse") +_mm_sub_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return subss(a, b) +} +@(require_results, enable_target_feature="sse") +_mm_sub_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return simd.sub(a, b) +} + +@(require_results, enable_target_feature="sse") +_mm_mul_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return mulss(a, b) +} +@(require_results, enable_target_feature="sse") +_mm_mul_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return simd.mul(a, b) +} + +@(require_results, enable_target_feature="sse") +_mm_div_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return divss(a, b) +} +@(require_results, enable_target_feature="sse") +_mm_div_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return simd.div(a, b) +} + +@(require_results, enable_target_feature="sse") +_mm_sqrt_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return sqrtss(a) +} +@(require_results, enable_target_feature="sse") +_mm_sqrt_ps :: #force_inline proc "c" (a: __m128) -> __m128 { + return sqrtps(a) +} + +@(require_results, enable_target_feature="sse") +_mm_rcp_ss :: #force_inline proc "c" (a: __m128) -> __m128 { + return rcpss(a) +} +@(require_results, enable_target_feature="sse") +_mm_rcp_ps :: #force_inline proc "c" (a: __m128) -> __m128 { + return rcpps(a) +} + +@(require_results, enable_target_feature="sse") +_mm_rsqrt_ss :: #force_inline proc "c" (a: __m128) -> __m128 { + return rsqrtss(a) +} +@(require_results, enable_target_feature="sse") +_mm_rsqrt_ps :: #force_inline proc "c" (a: __m128) -> __m128 { + return rsqrtps(a) +} + +@(require_results, enable_target_feature="sse") +_mm_min_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return minss(a, b) +} +@(require_results, enable_target_feature="sse") +_mm_min_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return minps(a, b) +} + +@(require_results, enable_target_feature="sse") +_mm_max_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return maxss(a, b) +} +@(require_results, enable_target_feature="sse") +_mm_max_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return maxps(a, b) +} + +@(require_results, enable_target_feature="sse") +_mm_and_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return transmute(__m128)simd.and(transmute(__m128i)a, transmute(__m128i)b) +} +@(require_results, enable_target_feature="sse") +_mm_andnot_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return transmute(__m128)simd.and_not(transmute(__m128i)a, transmute(__m128i)b) +} +@(require_results, enable_target_feature="sse") +_mm_or_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return transmute(__m128)simd.or(transmute(__m128i)a, transmute(__m128i)b) +} +@(require_results, enable_target_feature="sse") +_mm_xor_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return transmute(__m128)simd.xor(transmute(__m128i)a, transmute(__m128i)b) +} + + +@(require_results, enable_target_feature="sse") +_mm_cmpeq_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return cmpss(a, b, 0) +} +@(require_results, enable_target_feature="sse") +_mm_cmplt_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return cmpss(a, b, 1) +} +@(require_results, enable_target_feature="sse") +_mm_cmple_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return cmpss(a, b, 2) +} +@(require_results, enable_target_feature="sse") +_mm_cmpgt_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return simd.shuffle(a, cmpss(b, a, 1), 4, 1, 2, 3) +} +@(require_results, enable_target_feature="sse") +_mm_cmpge_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return simd.shuffle(a, cmpss(b, a, 2), 4, 1, 2, 3) +} +@(require_results, enable_target_feature="sse") +_mm_cmpneq_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return cmpss(a, b, 4) +} +@(require_results, enable_target_feature="sse") +_mm_cmpnlt_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return cmpss(a, b, 5) +} +@(require_results, enable_target_feature="sse") +_mm_cmpnle_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return cmpss(a, b, 6) +} +@(require_results, enable_target_feature="sse") +_mm_cmpngt_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return simd.shuffle(a, cmpss(b, a, 5), 4, 1, 2, 3) +} +@(require_results, enable_target_feature="sse") +_mm_cmpnge_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return simd.shuffle(a, cmpss(b, a, 6), 4, 1, 2, 3) +} +@(require_results, enable_target_feature="sse") +_mm_cmpord_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return cmpss(a, b, 7) +} +@(require_results, enable_target_feature="sse") +_mm_cmpunord_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return cmpss(a, b, 3) +} + + +@(require_results, enable_target_feature="sse") +_mm_cmpeq_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return cmpps(a, b, 0) +} +@(require_results, enable_target_feature="sse") +_mm_cmplt_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return cmpps(a, b, 1) +} +@(require_results, enable_target_feature="sse") +_mm_cmple_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return cmpps(a, b, 2) +} +@(require_results, enable_target_feature="sse") +_mm_cmpgt_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return cmpps(b, a, 1) +} +@(require_results, enable_target_feature="sse") +_mm_cmpge_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return cmpps(b, a, 2) +} +@(require_results, enable_target_feature="sse") +_mm_cmpneq_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return cmpps(a, b, 4) +} +@(require_results, enable_target_feature="sse") +_mm_cmpnlt_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return cmpps(a, b, 5) +} +@(require_results, enable_target_feature="sse") +_mm_cmpnle_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return cmpps(a, b, 6) +} +@(require_results, enable_target_feature="sse") +_mm_cmpngt_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return cmpps(b, a, 5) +} +@(require_results, enable_target_feature="sse") +_mm_cmpnge_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return cmpps(b, a, 6) +} +@(require_results, enable_target_feature="sse") +_mm_cmpord_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return cmpps(b, a, 7) +} +@(require_results, enable_target_feature="sse") +_mm_cmpunord_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return cmpps(b, a, 3) +} + + +@(require_results, enable_target_feature="sse") +_mm_comieq_ss :: #force_inline proc "c" (a, b: __m128) -> b32 { + return comieq_ss(a, b) +} +@(require_results, enable_target_feature="sse") +_mm_comilt_ss :: #force_inline proc "c" (a, b: __m128) -> b32 { + return comilt_ss(a, b) +} +@(require_results, enable_target_feature="sse") +_mm_comile_ss :: #force_inline proc "c" (a, b: __m128) -> b32 { + return comile_ss(a, b) +} +@(require_results, enable_target_feature="sse") +_mm_comigt_ss :: #force_inline proc "c" (a, b: __m128) -> b32 { + return comigt_ss(a, b) +} +@(require_results, enable_target_feature="sse") +_mm_comige_ss :: #force_inline proc "c" (a, b: __m128) -> b32 { + return comige_ss(a, b) +} +@(require_results, enable_target_feature="sse") +_mm_comineq_ss :: #force_inline proc "c" (a, b: __m128) -> b32 { + return comineq_ss(a, b) +} + +@(require_results, enable_target_feature="sse") +_mm_ucomieq_ss :: #force_inline proc "c" (a, b: __m128) -> b32 { + return ucomieq_ss(a, b) +} +@(require_results, enable_target_feature="sse") +_mm_ucomilt_ss :: #force_inline proc "c" (a, b: __m128) -> b32 { + return ucomilt_ss(a, b) +} +@(require_results, enable_target_feature="sse") +_mm_ucomile_ss :: #force_inline proc "c" (a, b: __m128) -> b32 { + return ucomile_ss(a, b) +} +@(require_results, enable_target_feature="sse") +_mm_ucomigt_ss :: #force_inline proc "c" (a, b: __m128) -> b32 { + return ucomigt_ss(a, b) +} +@(require_results, enable_target_feature="sse") +_mm_ucomige_ss :: #force_inline proc "c" (a, b: __m128) -> b32 { + return ucomige_ss(a, b) +} +@(require_results, enable_target_feature="sse") +_mm_ucomineq_ss :: #force_inline proc "c" (a, b: __m128) -> b32 { + return ucomineq_ss(a, b) +} + +@(require_results, enable_target_feature="sse") +_mm_cvtss_si32 :: #force_inline proc "c" (a: __m128) -> i32 { + return cvtss2si(a) +} +_mm_cvt_ss2si :: _mm_cvtss_si32 +_mm_cvttss_si32 :: _mm_cvtss_si32 + +@(require_results, enable_target_feature="sse") +_mm_cvtss_f32 :: #force_inline proc "c" (a: __m128) -> f32 { + return simd.extract(a, 0) +} + +@(require_results, enable_target_feature="sse") +_mm_cvtsi32_ss :: #force_inline proc "c" (a: __m128, b: i32) -> __m128 { + return cvtsi2ss(a, b) +} +_mm_cvt_si2ss :: _mm_cvtsi32_ss + + +@(require_results, enable_target_feature="sse") +_mm_set_ss :: #force_inline proc "c" (a: f32) -> __m128 { + return __m128{a, 0, 0, 0} +} +@(require_results, enable_target_feature="sse") +_mm_set1_ps :: #force_inline proc "c" (a: f32) -> __m128 { + return __m128(a) +} +_mm_set_ps1 :: _mm_set1_ps + +@(require_results, enable_target_feature="sse") +_mm_set_ps :: #force_inline proc "c" (a, b, c, d: f32) -> __m128 { + return __m128{d, c, b, a} +} +@(require_results, enable_target_feature="sse") +_mm_setr_ps :: #force_inline proc "c" (a, b, c, d: f32) -> __m128 { + return __m128{a, b, c, d} +} + +@(require_results, enable_target_feature="sse") +_mm_setzero_ps :: #force_inline proc "c" () -> __m128 { + return __m128{0, 0, 0, 0} +} + +@(require_results, enable_target_feature="sse") +_mm_shuffle_ps :: #force_inline proc "c" (a, b: __m128, $MASK: u32) -> __m128 { + return simd.shuffle( + a, b, + u32(MASK) & 0b11, + (u32(MASK)>>2) & 0b11, + ((u32(MASK)>>4) & 0b11)+4, + ((u32(MASK)>>6) & 0b11)+4) +} + + +@(require_results, enable_target_feature="sse") +_mm_unpackhi_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return simd.shuffle(a, b, 2, 6, 3, 7) +} +@(require_results, enable_target_feature="sse") +_mm_unpacklo_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return simd.shuffle(a, b, 0, 4, 1, 5) +} + +@(require_results, enable_target_feature="sse") +_mm_movehl_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return simd.shuffle(a, b, 6, 7, 2, 3) +} +@(require_results, enable_target_feature="sse") +_mm_movelh_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return simd.shuffle(a, b, 0, 1, 4, 5) +} + +@(require_results, enable_target_feature="sse") +_mm_movemask_ps :: #force_inline proc "c" (a: __m128) -> u32 { + return movmskps(a) +} + +@(require_results, enable_target_feature="sse") +_mm_load_ss :: #force_inline proc "c" (p: ^f32) -> __m128 { + return __m128{p^, 0, 0, 0} +} +@(require_results, enable_target_feature="sse") +_mm_load1_ps :: #force_inline proc "c" (p: ^f32) -> __m128 { + a := p^ + return __m128(a) +} +_mm_load_ps1 :: _mm_load1_ps + +@(require_results, enable_target_feature="sse") +_mm_load_ps :: #force_inline proc "c" (p: [^]f32) -> __m128 { + return (^__m128)(p)^ +} + +@(require_results, enable_target_feature="sse") +_mm_loadu_ps :: #force_inline proc "c" (p: [^]f32) -> __m128 { + dst := _mm_undefined_ps() + intrinsics.mem_copy_non_overlapping(&dst, p, size_of(__m128)) + return dst +} + +@(require_results, enable_target_feature="sse") +_mm_loadr_ps :: #force_inline proc "c" (p: [^]f32) -> __m128 { + return simd.lanes_reverse(_mm_load_ps(p)) +} + +@(require_results, enable_target_feature="sse") +_mm_loadu_si64 :: #force_inline proc "c" (mem_addr: rawptr) -> __m128i { + a := intrinsics.unaligned_load((^i64)(mem_addr)) + return __m128i{a, 0} +} + +@(enable_target_feature="sse") +_mm_store_ss :: #force_inline proc "c" (p: ^f32, a: __m128) { + p^ = simd.extract(a, 0) +} + +@(enable_target_feature="sse") +_mm_store1_ps :: #force_inline proc "c" (p: [^]f32, a: __m128) { + b := simd.swizzle(a, 0, 0, 0, 0) + (^__m128)(p)^ = b +} +_mm_store_ps1 :: _mm_store1_ps + + +@(enable_target_feature="sse") +_mm_store_ps :: #force_inline proc "c" (p: [^]f32, a: __m128) { + (^__m128)(p)^ = a +} +@(enable_target_feature="sse") +_mm_storeu_ps :: #force_inline proc "c" (p: [^]f32, a: __m128) { + b := a + intrinsics.mem_copy_non_overlapping(p, &b, size_of(__m128)) +} +@(enable_target_feature="sse") +_mm_storer_ps :: #force_inline proc "c" (p: [^]f32, a: __m128) { + (^__m128)(p)^ = simd.lanes_reverse(a) +} + + +@(require_results, enable_target_feature="sse") +_mm_move_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return simd.shuffle(a, b, 4, 1, 2, 3) +} + +@(enable_target_feature="sse") +_mm_sfence :: #force_inline proc "c" () { + sfence() +} + +@(require_results, enable_target_feature="sse") +_mm_getcsr :: #force_inline proc "c" () -> (result: u32) { + stmxcsr(&result) + return result +} + +@(enable_target_feature="sse") +_mm_setcsr :: #force_inline proc "c" (val: u32) { + val := val + ldmxcsr(&val) +} + +@(require_results, enable_target_feature="sse") +_MM_GET_EXCEPTION_MASK :: #force_inline proc "c" () -> u32 { + return _mm_getcsr() & _MM_MASK_MASK +} +@(require_results, enable_target_feature="sse") +_MM_GET_EXCEPTION_STATE :: #force_inline proc "c" () -> u32 { + return _mm_getcsr() & _MM_EXCEPT_MASK +} +@(require_results, enable_target_feature="sse") +_MM_GET_FLUSH_ZERO_MODE :: #force_inline proc "c" () -> u32 { + return _mm_getcsr() & _MM_FLUSH_ZERO_MASK +} +@(require_results, enable_target_feature="sse") +_MM_GET_ROUNDING_MODE :: #force_inline proc "c" () -> u32 { + return _mm_getcsr() & _MM_ROUND_MASK +} + +@(enable_target_feature="sse") +_MM_SET_EXCEPTION_MASK :: #force_inline proc "c" (x: u32) { + _mm_setcsr((_mm_getcsr() &~ _MM_MASK_MASK) | x) +} +@(enable_target_feature="sse") +_MM_SET_EXCEPTION_STATE :: #force_inline proc "c" (x: u32) { + _mm_setcsr((_mm_getcsr() &~ _MM_EXCEPT_MASK) | x) +} +@(enable_target_feature="sse") +_MM_SET_FLUSH_ZERO_MODE :: #force_inline proc "c" (x: u32) { + _mm_setcsr((_mm_getcsr() &~ _MM_FLUSH_ZERO_MASK) | x) +} +@(enable_target_feature="sse") +_MM_SET_ROUNDING_MODE :: #force_inline proc "c" (x: u32) { + _mm_setcsr((_mm_getcsr() &~ _MM_ROUND_MASK) | x) +} + +@(enable_target_feature="sse") +_mm_prefetch :: #force_inline proc "c" (p: rawptr, $STRATEGY: u32) { + prefetch(p, (STRATEGY>>2)&1, STRATEGY&3, 1) +} + + +@(require_results, enable_target_feature="sse") +_mm_undefined_ps :: #force_inline proc "c" () -> __m128 { + return _mm_set1_ps(0) +} + +@(enable_target_feature="sse") +_MM_TRANSPOSE4_PS :: #force_inline proc "c" (row0, row1, row2, row3: ^__m128) { + tmp0 := _mm_unpacklo_ps(row0^, row1^) + tmp1 := _mm_unpacklo_ps(row2^, row3^) + tmp2 := _mm_unpackhi_ps(row0^, row1^) + tmp3 := _mm_unpackhi_ps(row2^, row3^) + + row0^ = _mm_movelh_ps(tmp0, tmp2) + row1^ = _mm_movelh_ps(tmp2, tmp0) + row2^ = _mm_movelh_ps(tmp1, tmp3) + row3^ = _mm_movelh_ps(tmp3, tmp1) +} + +@(enable_target_feature="sse") +_mm_stream_ps :: #force_inline proc "c" (addr: [^]f32, a: __m128) { + intrinsics.non_temporal_store((^__m128)(addr), a) +} + +when ODIN_ARCH == .amd64 { + @(require_results, enable_target_feature="sse") + _mm_cvtss_si64 :: #force_inline proc "c"(a: __m128) -> i64 { + return cvtss2si64(a) + } + @(require_results, enable_target_feature="sse") + _mm_cvttss_si64 :: #force_inline proc "c"(a: __m128) -> i64 { + return cvttss2si64(a) + } + @(require_results, enable_target_feature="sse") + _mm_cvtsi64_ss :: #force_inline proc "c"(a: __m128, b: i64) -> __m128 { + return cvtsi642ss(a, b) + } +} + + +@(private, default_calling_convention="c") +foreign _ { + @(link_name="llvm.x86.sse.add.ss") + addss :: proc(a, b: __m128) -> __m128 --- + @(link_name="llvm.x86.sse.sub.ss") + subss :: proc(a, b: __m128) -> __m128 --- + @(link_name="llvm.x86.sse.mul.ss") + mulss :: proc(a, b: __m128) -> __m128 --- + @(link_name="llvm.x86.sse.div.ss") + divss :: proc(a, b: __m128) -> __m128 --- + @(link_name="llvm.x86.sse.sqrt.ss") + sqrtss :: proc(a: __m128) -> __m128 --- + @(link_name="llvm.x86.sse.sqrt.ps") + sqrtps :: proc(a: __m128) -> __m128 --- + @(link_name="llvm.x86.sse.rcp.ss") + rcpss :: proc(a: __m128) -> __m128 --- + @(link_name="llvm.x86.sse.rcp.ps") + rcpps :: proc(a: __m128) -> __m128 --- + @(link_name="llvm.x86.sse.rsqrt.ss") + rsqrtss :: proc(a: __m128) -> __m128 --- + @(link_name="llvm.x86.sse.rsqrt.ps") + rsqrtps :: proc(a: __m128) -> __m128 --- + @(link_name="llvm.x86.sse.min.ss") + minss :: proc(a, b: __m128) -> __m128 --- + @(link_name="llvm.x86.sse.min.ps") + minps :: proc(a, b: __m128) -> __m128 --- + @(link_name="llvm.x86.sse.max.ss") + maxss :: proc(a, b: __m128) -> __m128 --- + @(link_name="llvm.x86.sse.max.ps") + maxps :: proc(a, b: __m128) -> __m128 --- + @(link_name="llvm.x86.sse.movmsk.ps") + movmskps :: proc(a: __m128) -> u32 --- + @(link_name="llvm.x86.sse.cmp.ps") + cmpps :: proc(a, b: __m128, #const imm8: u8) -> __m128 --- + @(link_name="llvm.x86.sse.comieq.ss") + comieq_ss :: proc(a, b: __m128) -> b32 --- + @(link_name="llvm.x86.sse.comilt.ss") + comilt_ss :: proc(a, b: __m128) -> b32 --- + @(link_name="llvm.x86.sse.comile.ss") + comile_ss :: proc(a, b: __m128) -> b32 --- + @(link_name="llvm.x86.sse.comigt.ss") + comigt_ss :: proc(a, b: __m128) -> b32 --- + @(link_name="llvm.x86.sse.comige.ss") + comige_ss :: proc(a, b: __m128) -> b32 --- + @(link_name="llvm.x86.sse.comineq.ss") + comineq_ss :: proc(a, b: __m128) -> b32 --- + @(link_name="llvm.x86.sse.ucomieq.ss") + ucomieq_ss :: proc(a, b: __m128) -> b32 --- + @(link_name="llvm.x86.sse.ucomilt.ss") + ucomilt_ss :: proc(a, b: __m128) -> b32 --- + @(link_name="llvm.x86.sse.ucomile.ss") + ucomile_ss :: proc(a, b: __m128) -> b32 --- + @(link_name="llvm.x86.sse.ucomigt.ss") + ucomigt_ss :: proc(a, b: __m128) -> b32 --- + @(link_name="llvm.x86.sse.ucomige.ss") + ucomige_ss :: proc(a, b: __m128) -> b32 --- + @(link_name="llvm.x86.sse.ucomineq.ss") + ucomineq_ss :: proc(a, b: __m128) -> b32 --- + @(link_name="llvm.x86.sse.cvtss2si") + cvtss2si :: proc(a: __m128) -> i32 --- + @(link_name="llvm.x86.sse.cvttss2si") + cvttss2si :: proc(a: __m128) -> i32 --- + @(link_name="llvm.x86.sse.cvtsi2ss") + cvtsi2ss :: proc(a: __m128, b: i32) -> __m128 --- + @(link_name="llvm.x86.sse.sfence") + sfence :: proc() --- + @(link_name="llvm.x86.sse.stmxcsr") + stmxcsr :: proc(p: rawptr) --- + @(link_name="llvm.x86.sse.ldmxcsr") + ldmxcsr :: proc(p: rawptr) --- + @(link_name="llvm.prefetch") + prefetch :: proc(p: rawptr, #const rw, loc, ty: u32) --- + @(link_name="llvm.x86.sse.cmp.ss") + cmpss :: proc(a, b: __m128, #const imm8: u8) -> __m128 --- + + + // amd64 only + @(link_name="llvm.x86.sse.cvtss2si64") + cvtss2si64 :: proc(a: __m128) -> i64 --- + @(link_name="llvm.x86.sse.cvttss2si64") + cvttss2si64 :: proc(a: __m128) -> i64 --- + @(link_name="llvm.x86.sse.cvtsi642ss") + cvtsi642ss :: proc(a: __m128, b: i64) -> __m128 --- +} diff --git a/core/simd/x86/sse2.odin b/core/simd/x86/sse2.odin new file mode 100644 index 000000000..f33bd2195 --- /dev/null +++ b/core/simd/x86/sse2.odin @@ -0,0 +1,1191 @@ +//+build i386, amd64 +package simd_x86 + +import "core:intrinsics" +import "core:simd" + +@(enable_target_feature="sse2") +_mm_pause :: #force_inline proc "c" () { + pause() +} +@(enable_target_feature="sse2") +_mm_clflush :: #force_inline proc "c" (p: rawptr) { + clflush(p) +} +@(enable_target_feature="sse2") +_mm_lfence :: #force_inline proc "c" () { + lfence() +} +@(enable_target_feature="sse2") +_mm_mfence :: #force_inline proc "c" () { + mfence() +} + +@(require_results, enable_target_feature="sse2") +_mm_add_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)simd.add(transmute(i8x16)a, transmute(i8x16)b) +} +@(require_results, enable_target_feature="sse2") +_mm_add_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)simd.add(transmute(i16x8)a, transmute(i16x8)b) +} +@(require_results, enable_target_feature="sse2") +_mm_add_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)simd.add(transmute(i32x4)a, transmute(i32x4)b) +} +@(require_results, enable_target_feature="sse2") +_mm_add_epi64 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)simd.add(transmute(i64x2)a, transmute(i64x2)b) +} +@(require_results, enable_target_feature="sse2") +_mm_adds_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)simd.add_sat(transmute(i8x16)a, transmute(i8x16)b) +} +@(require_results, enable_target_feature="sse2") +_mm_adds_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)simd.add_sat(transmute(i16x8)a, transmute(i16x8)b) +} +@(require_results, enable_target_feature="sse2") +_mm_adds_epu8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)simd.add_sat(transmute(u8x16)a, transmute(u8x16)b) +} +@(require_results, enable_target_feature="sse2") +_mm_adds_epu16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)simd.add_sat(transmute(u16x8)a, transmute(u16x8)b) +} +@(require_results, enable_target_feature="sse2") +_mm_avg_epu8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)pavgb(transmute(u8x16)a, transmute(u8x16)b) +} +@(require_results, enable_target_feature="sse2") +_mm_avg_epu16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)pavgw(transmute(u16x8)a, transmute(u16x8)b) +} + +@(require_results, enable_target_feature="sse2") +_mm_madd_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)pmaddwd(transmute(i16x8)a, transmute(i16x8)b) +} +@(require_results, enable_target_feature="sse2") +_mm_max_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)pmaxsw(transmute(i16x8)a, transmute(i16x8)b) +} +@(require_results, enable_target_feature="sse2") +_mm_max_epu8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)pmaxub(transmute(u8x16)a, transmute(u8x16)b) +} +@(require_results, enable_target_feature="sse2") +_mm_min_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)pminsw(transmute(i16x8)a, transmute(i16x8)b) +} +@(require_results, enable_target_feature="sse2") +_mm_min_epu8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)pminub(transmute(u8x16)a, transmute(u8x16)b) +} + + +@(require_results, enable_target_feature="sse2") +_mm_mulhi_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)pmulhw(transmute(i16x8)a, transmute(i16x8)b) +} +@(require_results, enable_target_feature="sse2") +_mm_mulhi_epu16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)pmulhuw(transmute(u16x8)a, transmute(u16x8)b) +} +@(require_results, enable_target_feature="sse2") +_mm_mullo_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)simd.mul(transmute(i16x8)a, transmute(i16x8)b) +} +@(require_results, enable_target_feature="sse2") +_mm_mul_epu32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)pmuludq(transmute(u32x4)a, transmute(u32x4)b) +} +@(require_results, enable_target_feature="sse2") +_mm_sad_epu8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)psadbw(transmute(u8x16)a, transmute(u8x16)b) +} +@(require_results, enable_target_feature="sse2") +_mm_sub_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)simd.sub(transmute(i8x16)a, transmute(i8x16)b) +} +@(require_results, enable_target_feature="sse2") +_mm_sub_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)simd.sub(transmute(i16x8)a, transmute(i16x8)b) +} +@(require_results, enable_target_feature="sse2") +_mm_sub_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)simd.sub(transmute(i32x4)a, transmute(i32x4)b) +} +@(require_results, enable_target_feature="sse2") +_mm_sub_epi64 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)simd.sub(transmute(i64x2)a, transmute(i64x2)b) +} +@(require_results, enable_target_feature="sse2") +_mm_subs_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)simd.sub_sat(transmute(i8x16)a, transmute(i8x16)b) +} +@(require_results, enable_target_feature="sse2") +_mm_subs_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)simd.sub_sat(transmute(i16x8)a, transmute(i16x8)b) +} +@(require_results, enable_target_feature="sse2") +_mm_subs_epu8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)simd.sub_sat(transmute(u8x16)a, transmute(u8x16)b) +} +@(require_results, enable_target_feature="sse2") +_mm_subs_epu16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)simd.sub_sat(transmute(u16x8)a, transmute(u16x8)b) +} + + + +@(private) +@(require_results, enable_target_feature="sse2") +_mm_slli_si128_impl :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i { + shift :: IMM8 & 0xff + + return transmute(__m128i)simd.shuffle( + transmute(i8x16)a, + i8x16(0), + 0 when shift > 15 else (16 - shift + 0), + 1 when shift > 15 else (16 - shift + 1), + 2 when shift > 15 else (16 - shift + 2), + 3 when shift > 15 else (16 - shift + 3), + 4 when shift > 15 else (16 - shift + 4), + 5 when shift > 15 else (16 - shift + 5), + 6 when shift > 15 else (16 - shift + 6), + 7 when shift > 15 else (16 - shift + 7), + 8 when shift > 15 else (16 - shift + 8), + 9 when shift > 15 else (16 - shift + 9), + 10 when shift > 15 else (16 - shift + 10), + 11 when shift > 15 else (16 - shift + 11), + 12 when shift > 15 else (16 - shift + 12), + 13 when shift > 15 else (16 - shift + 13), + 14 when shift > 15 else (16 - shift + 14), + 15 when shift > 15 else (16 - shift + 15), + ) +} + +@(private) +@(require_results, enable_target_feature="sse2") +_mm_srli_si128_impl :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i { + shift :: IMM8 + return transmute(__m128i)simd.shuffle( + transmute(i8x16)a, + i8x16(0), + 0 + 16 when shift > 15 else (shift + 0), + 1 + 16 when shift > 15 else (shift + 1), + 2 + 16 when shift > 15 else (shift + 2), + 3 + 16 when shift > 15 else (shift + 3), + 4 + 16 when shift > 15 else (shift + 4), + 5 + 16 when shift > 15 else (shift + 5), + 6 + 16 when shift > 15 else (shift + 6), + 7 + 16 when shift > 15 else (shift + 7), + 8 + 16 when shift > 15 else (shift + 8), + 9 + 16 when shift > 15 else (shift + 9), + 10 + 16 when shift > 15 else (shift + 10), + 11 + 16 when shift > 15 else (shift + 11), + 12 + 16 when shift > 15 else (shift + 12), + 13 + 16 when shift > 15 else (shift + 13), + 14 + 16 when shift > 15 else (shift + 14), + 15 + 16 when shift > 15 else (shift + 15), + ) +} + + +@(require_results, enable_target_feature="sse2") +_mm_slli_si128 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i { + return _mm_slli_si128_impl(a, IMM8) +} +@(require_results, enable_target_feature="sse2") +_mm_bslli_si128 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i { + return _mm_slli_si128_impl(a, IMM8) +} + + +@(require_results, enable_target_feature="sse2") +_mm_bsrli_si128 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i { + return _mm_srli_si128_impl(a, IMM8) +} +@(require_results, enable_target_feature="sse2") +_mm_slli_epi16 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i { + return transmute(__m128i)pslliw(transmute(i16x8)a, IMM8) +} +@(require_results, enable_target_feature="sse2") +_mm_sll_epi16 :: #force_inline proc "c" (a, count: __m128i) -> __m128i { + return transmute(__m128i)psllw(transmute(i16x8)a, transmute(i16x8)count) +} +@(require_results, enable_target_feature="sse2") +_mm_slli_epi32 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i { + return transmute(__m128i)psllid(transmute(i32x4)a, IMM8) +} +@(require_results, enable_target_feature="sse2") +_mm_sll_epi32 :: #force_inline proc "c" (a, count: __m128i) -> __m128i { + return transmute(__m128i)pslld(transmute(i32x4)a, transmute(i32x4)count) +} +@(require_results, enable_target_feature="sse2") +_mm_slli_epi64 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i { + return transmute(__m128i)pslliq(transmute(i64x2)a, IMM8) +} +@(require_results, enable_target_feature="sse2") +_mm_sll_epi64 :: #force_inline proc "c" (a, count: __m128i) -> __m128i { + return transmute(__m128i)psllq(transmute(i64x2)a, transmute(i64x2)count) +} +@(require_results, enable_target_feature="sse2") +_mm_srai_epi16 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i { + return transmute(__m128i)psraiw(transmute(i16x8)a. IMM8) +} +@(require_results, enable_target_feature="sse2") +_mm_sra_epi16 :: #force_inline proc "c" (a, count: __m128i) -> __m128i { + return transmute(__m128i)psraw(transmute(i16x8)a, transmute(i16x8)count) +} +@(require_results, enable_target_feature="sse2") +_mm_srai_epi32 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i { + return transmute(__m128i)psraid(transmute(i32x4)a, IMM8) +} +@(require_results, enable_target_feature="sse2") +_mm_sra_epi32 :: #force_inline proc "c" (a, count: __m128i) -> __m128i { + return transmute(__m128i)psrad(transmute(i32x4)a, transmute(i32x4)count) +} + + +@(require_results, enable_target_feature="sse2") +_mm_srli_si128 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i { + return _mm_srli_si128_impl(a, IMM8) +} +@(require_results, enable_target_feature="sse2") +_mm_srli_epi16 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i { + return transmute(__m128i)psrliw(transmute(i16x8)a. IMM8) +} +@(require_results, enable_target_feature="sse2") +_mm_srl_epi16 :: #force_inline proc "c" (a, count: __m128i) -> __m128i { + return transmute(__m128i)psrlw(transmute(i16x8)a, transmute(i16x8)count) +} +@(require_results, enable_target_feature="sse2") +_mm_srli_epi32 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i { + return transmute(__m128i)psrlid(transmute(i32x4)a, IMM8) +} +@(require_results, enable_target_feature="sse2") +_mm_srl_epi32 :: #force_inline proc "c" (a, count: __m128i) -> __m128i { + return transmute(__m128i)psrld(transmute(i32x4)a, transmute(i32x4)count) +} +@(require_results, enable_target_feature="sse2") +_mm_srli_epi64 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i { + return transmute(__m128i)psrliq(transmute(i64x2)a, IMM8) +} +@(require_results, enable_target_feature="sse2") +_mm_srl_epi64 :: #force_inline proc "c" (a, count: __m128i) -> __m128i { + return transmute(__m128i)psrlq(transmute(i64x2)a, transmute(i64x2)count) +} + + +@(require_results, enable_target_feature="sse2") +_mm_and_si128 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return simd.and(a, b) +} +@(require_results, enable_target_feature="sse2") +_mm_andnot_si128 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return simd.and_not(b, a) +} +@(require_results, enable_target_feature="sse2") +_mm_or_si128 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return simd.or(a, b) +} +@(require_results, enable_target_feature="sse2") +_mm_xor_si128 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return simd.xor(a, b) +} +@(require_results, enable_target_feature="sse2") +_mm_cmpeq_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)simd.lanes_eq(transmute(i8x16)a, transmute(i8x16)b) +} +@(require_results, enable_target_feature="sse2") +_mm_cmpeq_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)simd.lanes_eq(transmute(i16x8)a, transmute(i16x8)b) +} +@(require_results, enable_target_feature="sse2") +_mm_cmpeq_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)simd.lanes_eq(transmute(i32x4)a, transmute(i32x4)b) +} +@(require_results, enable_target_feature="sse2") +_mm_cmpgt_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)simd.lanes_gt(transmute(i8x16)a, transmute(i8x16)b) +} +@(require_results, enable_target_feature="sse2") +_mm_cmpgt_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)simd.lanes_gt(transmute(i16x8)a, transmute(i16x8)b) +} +@(require_results, enable_target_feature="sse2") +_mm_cmpgt_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)simd.lanes_gt(transmute(i32x4)a, transmute(i32x4)b) +} +@(require_results, enable_target_feature="sse2") +_mm_cmplt_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)simd.lanes_lt(transmute(i8x16)a, transmute(i8x16)b) +} +@(require_results, enable_target_feature="sse2") +_mm_cmplt_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)simd.lanes_lt(transmute(i16x8)a, transmute(i16x8)b) +} +@(require_results, enable_target_feature="sse2") +_mm_cmplt_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)simd.lanes_lt(transmute(i32x4)a, transmute(i32x4)b) +} + + +@(require_results, enable_target_feature="sse2") +_mm_cvtepi32_pd :: #force_inline proc "c" (a: __m128i) -> __m128d { + v := transmute(i32x4)a + return cast(__m128d)simd.shuffle(v, v, 0, 1) +} +@(require_results, enable_target_feature="sse2") +_mm_cvtsi32_sd :: #force_inline proc "c" (a: __m128d, b: i32) -> __m128d { + return simd.replace(a, 0, f64(b)) +} +@(require_results, enable_target_feature="sse2") +_mm_cvtepi32_ps :: #force_inline proc "c" (a: __m128i) -> __m128 { + return cvtdq2ps(transmute(i32x4)a) +} +@(require_results, enable_target_feature="sse2") +_mm_cvtps_epi32 :: #force_inline proc "c" (a: __m128) -> __m128i { + return transmute(__m128i)cvtps2dq(a) +} +@(require_results, enable_target_feature="sse2") +_mm_cvtsi32_si128 :: #force_inline proc "c" (a: i32) -> __m128i { + return transmute(__m128i)i32x4{a, 0, 0, 0} +} +@(require_results, enable_target_feature="sse2") +_mm_cvtsi128_si32 :: #force_inline proc "c" (a: __m128i) -> i32 { + return simd.extract(transmute(i32x4)a, 0) +} + + + +@(require_results, enable_target_feature="sse2") +_mm_set_epi64x :: #force_inline proc "c" (e1, e0: i64) -> __m128i { + return transmute(__m128i)i64x2{e0, e1} +} +@(require_results, enable_target_feature="sse2") +_mm_set_epi32 :: #force_inline proc "c" (e3, e2, e1, e0: i32) -> __m128i { + return transmute(__m128i)i32x4{e0, e1, e2, e3} +} +@(require_results, enable_target_feature="sse2") +_mm_set_epi16 :: #force_inline proc "c" (e7, e6, e5, e4, e3, e2, e1, e0: i16) -> __m128i { + return transmute(__m128i)i16x8{e0, e1, e2, e3, e4, e5, e6, e7} +} +@(require_results, enable_target_feature="sse2") +_mm_set_epi8 :: #force_inline proc "c" (e15, e14, e13, e12, e11, e10, e9, e8, e7, e6, e5, e4, e3, e2, e1, e0: i8) -> __m128i { + return transmute(__m128i)i8x16{e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15} +} +@(require_results, enable_target_feature="sse2") +_mm_set1_epi64x :: #force_inline proc "c" (a: i64) -> __m128i { + return _mm_set_epi64x(a, a) +} +@(require_results, enable_target_feature="sse2") +_mm_set1_epi32 :: #force_inline proc "c" (a: i32) -> __m128i { + return _mm_set_epi32(a, a, a, a) +} +@(require_results, enable_target_feature="sse2") +_mm_set1_epi16 :: #force_inline proc "c" (a: i16) -> __m128i { + return _mm_set_epi16(a, a, a, a, a, a, a, a) +} +@(require_results, enable_target_feature="sse2") +_mm_set1_epi8 :: #force_inline proc "c" (a: i8) -> __m128i { + return _mm_set_epi8(a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a) +} +@(require_results, enable_target_feature="sse2") +_mm_setr_epi32 :: #force_inline proc "c" (e3, e2, e1, e0: i32) -> __m128i { + return _mm_set_epi32(e0, e1, e2, e3) +} +@(require_results, enable_target_feature="sse2") +_mm_setr_epi16 :: #force_inline proc "c" (e7, e6, e5, e4, e3, e2, e1, e0: i16) -> __m128i { + return _mm_set_epi16(e0, e1, e2, e3, e4, e5, e6, e7) +} +@(require_results, enable_target_feature="sse2") +_mm_setr_epi8 :: #force_inline proc "c" (e15, e14, e13, e12, e11, e10, e9, e8, e7, e6, e5, e4, e3, e2, e1, e0: i8) -> __m128i { + return _mm_set_epi8(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15) +} +@(require_results, enable_target_feature="sse2") +_mm_setzero_si128 :: #force_inline proc "c" () -> __m128i { + return _mm_set1_epi64x(0) +} + + +@(require_results, enable_target_feature="sse2") +_mm_loadl_epi64 :: #force_inline proc "c" (mem_addr: ^__m128i) -> __m128i { + return _mm_set_epi64x(0, intrinsics.unaligned_load((^i64)(mem_addr))) +} +@(require_results, enable_target_feature="sse2") +_mm_load_si128 :: #force_inline proc "c" (mem_addr: ^__m128i) -> __m128i { + return mem_addr^ +} +@(require_results, enable_target_feature="sse2") +_mm_loadu_si128 :: #force_inline proc "c" (mem_addr: ^__m128i) -> __m128i { + dst := _mm_undefined_si128() + intrinsics.mem_copy_non_overlapping(&dst, mem_addr, size_of(__m128i)) + return dst +} +@(enable_target_feature="sse2") +_mm_maskmoveu_si128 :: #force_inline proc "c" (a, mask: __m128i, mem_addr: rawptr) { + maskmovdqu(transmute(i8x16)a, transmute(i8x16)mask, mem_addr) +} +@(enable_target_feature="sse2") +_mm_store_si128 :: #force_inline proc "c" (mem_addr: ^__m128i, a: __m128i) { + mem_addr^ = a +} +@(enable_target_feature="sse2") +_mm_storeu_si128 :: #force_inline proc "c" (mem_addr: ^__m128i, a: __m128i) { + storeudq(mem_addr, a) +} +@(enable_target_feature="sse2") +_mm_storel_epi64 :: #force_inline proc "c" (mem_addr: ^__m128i, a: __m128i) { + a := a + intrinsics.mem_copy_non_overlapping(mem_addr, &a, 8) +} +@(enable_target_feature="sse2") +_mm_stream_si128 :: #force_inline proc "c" (mem_addr: ^__m128i, a: __m128i) { + intrinsics.non_temporal_store(mem_addr, a) +} +@(enable_target_feature="sse2") +_mm_stream_si32 :: #force_inline proc "c" (mem_addr: ^i32, a: i32) { + intrinsics.non_temporal_store(mem_addr, a) +} +@(require_results, enable_target_feature="sse2") +_mm_move_epi64 :: #force_inline proc "c" (a: __m128i) -> __m128i { + zero := _mm_setzero_si128() + return transmute(__m128i)simd.shuffle(transmute(i64x2)a, transmute(i64x2)zero, 0, 2) +} + + + + +@(require_results, enable_target_feature="sse2") +_mm_packs_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)packsswb(transmute(i16x8)a, transmute(i16x8)b) +} +@(require_results, enable_target_feature="sse2") +_mm_packs_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)packssdw(transmute(i32x4)a, transmute(i32x4)b) +} +@(require_results, enable_target_feature="sse2") +_mm_packus_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)packuswb(transmute(i16x8)a, transmute(i16x8)b) +} +@(require_results, enable_target_feature="sse2") +_mm_extract_epi16 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> i32 { + return i32(simd.extract(transmute(u16x8)a, IMM8)) +} +@(require_results, enable_target_feature="sse2") +_mm_insert_epi16 :: #force_inline proc "c" (a: __m128i, i: i32, $IMM8: u32) -> __m128i { + return i32(simd.replace(transmute(u16x8)a, IMM8, i16(i))) +} +@(require_results, enable_target_feature="sse2") +_mm_movemask_epi8 :: #force_inline proc "c" (a: __m128i) -> i32 { + return pmovmskb(transmute(i8x16)a) +} +@(require_results, enable_target_feature="sse2") +_mm_shuffle_epi32 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i { + v := transmute(i32x4)a + return transmute(__m128i)simd.shuffle( + v, + v, + IMM8 & 0b11, + (IMM8 >> 2) & 0b11, + (IMM8 >> 4) & 0b11, + (IMM8 >> 6) & 0b11, + ) +} +@(require_results, enable_target_feature="sse2") +_mm_shufflehi_epi16 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i { + v := transmute(i16x8)a + return transmute(__m128i)simd.shuffle( + v, + v, + 0, + 1, + 2, + 3, + (IMM8 & 0b11) + 4, + ((IMM8 >> 2) & 0b11) + 4, + ((IMM8 >> 4) & 0b11) + 4, + ((IMM8 >> 6) & 0b11) + 4, + ) +} +@(require_results, enable_target_feature="sse2") +_mm_shufflelo_epi16 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i { + v := transmute(i16x8)a + return transmute(__m128i)simd.shuffle( + v, + v, + IMM8 & 0b11, + (IMM8 >> 2) & 0b11, + (IMM8 >> 4) & 0b11, + (IMM8 >> 6) & 0b11, + 4, + 5, + 6, + 7, + ) +} +@(require_results, enable_target_feature="sse2") +_mm_unpackhi_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)simd.shuffle( + transmute(i8x16)a, + transmute(i8x16)b, + 8, 24, 9, 25, 10, 26, 11, 27, 12, 28, 13, 29, 14, 30, 15, 31, + ) +} +@(require_results, enable_target_feature="sse2") +_mm_unpackhi_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)simd.shuffle(transmute(i16x8)a, transmute(i16x8)b, 4, 12, 5, 13, 6, 14, 7, 15) +} +@(require_results, enable_target_feature="sse2") +_mm_unpackhi_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)simd.shuffle(transmute(i32x4)a, transmute(i32x4)b, 2, 6, 3, 7) +} +@(require_results, enable_target_feature="sse2") +_mm_unpackhi_epi64 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)simd.shuffle(transmute(i64x2)a, transmute(i64x2)b, 1, 3) +} +@(require_results, enable_target_feature="sse2") +_mm_unpacklo_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)simd.shuffle( + transmute(i8x16)a, + transmute(i8x16)b, + 0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23, + ) +} +@(require_results, enable_target_feature="sse2") +_mm_unpacklo_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)simd.shuffle(transmute(i16x8)a, transmute(i16x8)b, 0, 8, 1, 9, 2, 10, 3, 11) +} +@(require_results, enable_target_feature="sse2") +_mm_unpacklo_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)simd.shuffle(transmute(i32x4)a, transmute(i32x4)b, 0, 4, 1, 5) +} +@(require_results, enable_target_feature="sse2") +_mm_unpacklo_epi64 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)simd.shuffle(transmute(i64x2)a, transmute(i64x2)b, 0, 2) +} + + + + +@(require_results, enable_target_feature="sse2") +_mm_add_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return simd.replace(a, 0, _mm_cvtsd_f64(a) + _mm_cvtsd_f64(b)) +} +@(require_results, enable_target_feature="sse2") +_mm_add_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return simd.add(a, b) +} +@(require_results, enable_target_feature="sse2") +_mm_div_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return simd.replace(a, 0, _mm_cvtsd_f64(a) / _mm_cvtsd_f64(b)) +} +@(require_results, enable_target_feature="sse2") +_mm_div_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return simd.div(a, b) +} +@(require_results, enable_target_feature="sse2") +_mm_max_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return maxsd(a, b) +} +@(require_results, enable_target_feature="sse2") +_mm_max_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return maxpd(a, b) +} +@(require_results, enable_target_feature="sse2") +_mm_min_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return minsd(a, b) +} +@(require_results, enable_target_feature="sse2") +_mm_min_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return minpd(a, b) +} +@(require_results, enable_target_feature="sse2") +_mm_mul_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return simd.replace(a, 0, _mm_cvtsd_f64(a) * _mm_cvtsd_f64(b)) +} +@(require_results, enable_target_feature="sse2") +_mm_mul_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return simd.mul(a, b) +} +@(require_results, enable_target_feature="sse2") +_mm_sqrt_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return simd.replace(a, 0, _mm_cvtsd_f64(sqrtsd(b))) +} +@(require_results, enable_target_feature="sse2") +_mm_sqrt_pd :: #force_inline proc "c" (a: __m128d) -> __m128d { + return simd.sqrt(a) +} +@(require_results, enable_target_feature="sse2") +_mm_sub_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return simd.replace(a, 0, _mm_cvtsd_f64(a) - _mm_cvtsd_f64(b)) +} +@(require_results, enable_target_feature="sse2") +_mm_sub_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return simd.sub(a, b) +} +@(require_results, enable_target_feature="sse2") +_mm_and_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return transmute(__m128d)_mm_and_si128(transmute(__m128i)a, transmute(__m128i)b) +} +@(require_results, enable_target_feature="sse2") +_mm_andnot_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return transmute(__m128d)_mm_andnot_si128(transmute(__m128i)a, transmute(__m128i)b) +} +@(require_results, enable_target_feature="sse2") +_mm_or_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return transmute(__m128d)_mm_or_si128(transmute(__m128i)a, transmute(__m128i)b) +} +@(require_results, enable_target_feature="sse2") +_mm_xor_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return transmute(__m128d)_mm_xor_si128(transmute(__m128i)a, transmute(__m128i)b) +} + + + + +@(require_results, enable_target_feature="sse2") +_mm_cmpeq_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return cmpsd(a, b, 0) +} +@(require_results, enable_target_feature="sse2") +_mm_cmplt_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return cmpsd(a, b, 1) +} +@(require_results, enable_target_feature="sse2") +_mm_cmple_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return cmpsd(a, b, 2) +} +@(require_results, enable_target_feature="sse2") +_mm_cmpgt_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return simd.replace(_mm_cmplt_sd(b, a), 1, simd.extract(a, 1)) +} +@(require_results, enable_target_feature="sse2") +_mm_cmpge_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return simd.replace(_mm_cmple_sd(b, a), 1, simd.extract(a, 1)) +} +@(require_results, enable_target_feature="sse2") +_mm_cmpord_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return cmpsd(a, b, 7) +} +@(require_results, enable_target_feature="sse2") +_mm_cmpunord_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return cmpsd(a, b, 3) +} +@(require_results, enable_target_feature="sse2") +_mm_cmpneq_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return cmpsd(a, b, 4) +} +@(require_results, enable_target_feature="sse2") +_mm_cmpnlt_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return cmpsd(a, b, 5) +} +@(require_results, enable_target_feature="sse2") +_mm_cmpnle_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return cmpsd(a, b, 6) +} +@(require_results, enable_target_feature="sse2") +_mm_cmpngt_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return simd.replace(_mm_cmpnlt_sd(b, a), 1, simd.extract(a, 1)) +} +@(require_results, enable_target_feature="sse2") +_mm_cmpnge_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return simd.replace(_mm_cmpnle_sd(b, a), 1, simd.extract(a, 1)) +} +@(require_results, enable_target_feature="sse2") +_mm_cmpeq_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return cmppd(a, b, 0) +} +@(require_results, enable_target_feature="sse2") +_mm_cmplt_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return cmppd(a, b, 1) +} +@(require_results, enable_target_feature="sse2") +_mm_cmple_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return cmppd(a, b, 2) +} +@(require_results, enable_target_feature="sse2") +_mm_cmpgt_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return _mm_cmplt_pd(b, a) +} +@(require_results, enable_target_feature="sse2") +_mm_cmpge_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return _mm_cmple_pd(b, a) +} +@(require_results, enable_target_feature="sse2") +_mm_cmpord_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return cmppd(a, b, 7) +} +@(require_results, enable_target_feature="sse2") +_mm_cmpunord_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return cmppd(a, b, 3) +} +@(require_results, enable_target_feature="sse2") +_mm_cmpneq_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return cmppd(a, b, 4) +} +@(require_results, enable_target_feature="sse2") +_mm_cmpnlt_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return cmppd(a, b, 5) +} +@(require_results, enable_target_feature="sse2") +_mm_cmpnle_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return cmppd(a, b, 6) +} +@(require_results, enable_target_feature="sse2") +_mm_cmpngt_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return _mm_cmpnlt_pd(b, a) +} +@(require_results, enable_target_feature="sse2") +_mm_cmpnge_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return _mm_cmpnle_pd(b, a) +} +@(require_results, enable_target_feature="sse2") +_mm_comieq_sd :: #force_inline proc "c" (a, b: __m128d) -> i32 { + return comieqsd(a, b) +} +@(require_results, enable_target_feature="sse2") +_mm_comilt_sd :: #force_inline proc "c" (a, b: __m128d) -> i32 { + return comiltsd(a, b) +} +@(require_results, enable_target_feature="sse2") +_mm_comile_sd :: #force_inline proc "c" (a, b: __m128d) -> i32 { + return comilesd(a, b) +} +@(require_results, enable_target_feature="sse2") +_mm_comigt_sd :: #force_inline proc "c" (a, b: __m128d) -> i32 { + return comigtsd(a, b) +} +@(require_results, enable_target_feature="sse2") +_mm_comige_sd :: #force_inline proc "c" (a, b: __m128d) -> i32 { + return comigesd(a, b) +} +@(require_results, enable_target_feature="sse2") +_mm_comineq_sd :: #force_inline proc "c" (a, b: __m128d) -> i32 { + return comineqsd(a, b) +} +@(require_results, enable_target_feature="sse2") +_mm_ucomieq_sd :: #force_inline proc "c" (a, b: __m128d) -> i32 { + return ucomieqsd(a, b) +} +@(require_results, enable_target_feature="sse2") +_mm_ucomilt_sd :: #force_inline proc "c" (a, b: __m128d) -> i32 { + return ucomiltsd(a, b) +} +@(require_results, enable_target_feature="sse2") +_mm_ucomile_sd :: #force_inline proc "c" (a, b: __m128d) -> i32 { + return ucomilesd(a, b) +} +@(require_results, enable_target_feature="sse2") +_mm_ucomigt_sd :: #force_inline proc "c" (a, b: __m128d) -> i32 { + return ucomigtsd(a, b) +} +@(require_results, enable_target_feature="sse2") +_mm_ucomige_sd :: #force_inline proc "c" (a, b: __m128d) -> i32 { + return ucomigesd(a, b) +} +@(require_results, enable_target_feature="sse2") +_mm_ucomineq_sd :: #force_inline proc "c" (a, b: __m128d) -> i32 { + return ucomineqsd(a, b) +} + + + + + +@(require_results, enable_target_feature="sse2") +_mm_cvtpd_ps :: #force_inline proc "c" (a: __m128d) -> __m128 { + return cvtpd2ps(a) +} +@(require_results, enable_target_feature="sse2") +_mm_cvtps_pd :: #force_inline proc "c" (a: __m128) -> __m128d { + return cvtps2pd(a) +} +@(require_results, enable_target_feature="sse2") +_mm_cvtpd_epi32 :: #force_inline proc "c" (a: __m128d) -> __m128i { + return transmute(__m128i)cvtpd2dq(a) +} +@(require_results, enable_target_feature="sse2") +_mm_cvtsd_si32 :: #force_inline proc "c" (a: __m128d) -> i32 { + return cvtsd2si(a) +} +@(require_results, enable_target_feature="sse2") +_mm_cvtsd_ss :: #force_inline proc "c" (a, b: __m128d) -> __m128 { + return cvtsd2ss(a, b) +} +@(require_results, enable_target_feature="sse2") +_mm_cvtsd_f64 :: #force_inline proc "c" (a: __m128d) -> f64 { + return simd.extract(a, 0) +} +@(require_results, enable_target_feature="sse2") +_mm_cvtss_sd :: #force_inline proc "c" (a, b: __m128) -> __m128d { + return cvtss2sd(a, b) +} +@(require_results, enable_target_feature="sse2") +_mm_cvttpd_epi32 :: #force_inline proc "c" (a: __m128d) -> __m128i { + return transmute(__m128i)cvttpd2dq(a) +} +@(require_results, enable_target_feature="sse2") +_mm_cvttsd_si32 :: #force_inline proc "c" (a: __m128d) -> i32 { + return cvttsd2si(a) +} +@(require_results, enable_target_feature="sse2") +_mm_cvttps_epi32 :: #force_inline proc "c" (a: __m128) -> __m128i { + return transmute(__m128i)cvttps2dq(a) +} +@(require_results, enable_target_feature="sse2") +_mm_set_sd :: #force_inline proc "c" (a: f64) -> __m128d { + return _mm_set_pd(0.0, a) +} +@(require_results, enable_target_feature="sse2") +_mm_set1_pd :: #force_inline proc "c" (a: f64) -> __m128d { + return _mm_set_pd(a, a) +} +@(require_results, enable_target_feature="sse2") +_mm_set_pd1 :: #force_inline proc "c" (a: f64) -> __m128d { + return _mm_set_pd(a, a) +} +@(require_results, enable_target_feature="sse2") +_mm_set_pd :: #force_inline proc "c" (a: f64, b: f64) -> __m128d { + return __m128d{b, a} +} +@(require_results, enable_target_feature="sse2") +_mm_setr_pd :: #force_inline proc "c" (a: f64, b: f64) -> __m128d { + return _mm_set_pd(b, a) +} +@(require_results, enable_target_feature="sse2") +_mm_setzero_pd :: #force_inline proc "c" () -> __m128d { + return _mm_set_pd(0.0, 0.0) +} +@(require_results, enable_target_feature="sse2") +_mm_movemask_pd :: #force_inline proc "c" (a: __m128d) -> i32 { + return movmskpd(a) +} +@(require_results, enable_target_feature="sse2") +_mm_load_pd :: #force_inline proc "c" (mem_addr: ^f64) -> __m128d { + return (^__m128d)(mem_addr)^ +} +@(require_results, enable_target_feature="sse2") +_mm_load_sd :: #force_inline proc "c" (mem_addr: ^f64) -> __m128d { + return _mm_setr_pd(mem_addr^, 0.) +} +@(require_results, enable_target_feature="sse2") +_mm_loadh_pd :: #force_inline proc "c" (a: __m128d, mem_addr: ^f64) -> __m128d { + return _mm_setr_pd(simd.extract(a, 0), mem_addr^) +} +@(require_results, enable_target_feature="sse2") +_mm_loadl_pd :: #force_inline proc "c" (a: __m128d, mem_addr: ^f64) -> __m128d { + return _mm_setr_pd(mem_addr^, simd.extract(a, 1)) +} +@(enable_target_feature="sse2") +_mm_stream_pd :: #force_inline proc "c" (mem_addr: ^f64, a: __m128d) { + intrinsics.non_temporal_store((^__m128d)(mem_addr), a) +} +@(enable_target_feature="sse2") +_mm_store_sd :: #force_inline proc "c" (mem_addr: ^f64, a: __m128d) { + mem_addr^ = simd.extract(a, 0) +} +@(enable_target_feature="sse2") +_mm_store_pd :: #force_inline proc "c" (mem_addr: ^f64, a: __m128d) { + (^__m128d)(mem_addr)^ = a +} +@(enable_target_feature="sse2") +_mm_storeu_pd :: #force_inline proc "c" (mem_addr: ^f64, a: __m128d) { + storeupd(mem_addr, a) +} +@(enable_target_feature="sse2") +_mm_store1_pd :: #force_inline proc "c" (mem_addr: ^f64, a: __m128d) { + (^__m128d)(mem_addr)^ = simd.shuffle(a, a, 0, 0) +} +@(enable_target_feature="sse2") +_mm_store_pd1 :: #force_inline proc "c" (mem_addr: ^f64, a: __m128d) { + (^__m128d)(mem_addr)^ = simd.shuffle(a, a, 0, 0) +} +@(enable_target_feature="sse2") +_mm_storer_pd :: #force_inline proc "c" (mem_addr: ^f64, a: __m128d) { + (^__m128d)(mem_addr)^ = simd.shuffle(a, a, 1, 0) +} +@(enable_target_feature="sse2") +_mm_storeh_pd :: #force_inline proc "c" (mem_addr: ^f64, a: __m128d) { + mem_addr^ = simd.extract(a, 1) +} +@(enable_target_feature="sse2") +_mm_storel_pd :: #force_inline proc "c" (mem_addr: ^f64, a: __m128d) { + mem_addr^ = simd.extract(a, 0) +} +@(require_results, enable_target_feature="sse2") +_mm_load1_pd :: #force_inline proc "c" (mem_addr: ^f64) -> __m128d { + d := mem_addr^ + return _mm_setr_pd(d, d) +} +@(require_results, enable_target_feature="sse2") +_mm_load_pd1 :: #force_inline proc "c" (mem_addr: ^f64) -> __m128d { + return _mm_load1_pd(mem_addr) +} +@(require_results, enable_target_feature="sse2") +_mm_loadr_pd :: #force_inline proc "c" (mem_addr: ^f64) -> __m128d { + a := _mm_load_pd(mem_addr) + return simd.shuffle(a, a, 1, 0) +} +@(require_results, enable_target_feature="sse2") +_mm_loadu_pd :: #force_inline proc "c" (mem_addr: ^f64) -> __m128d { + dst := _mm_undefined_pd() + intrinsics.mem_copy_non_overlapping(&dst, mem_addr, size_of(__m128d)) + return dst +} +@(require_results, enable_target_feature="sse2") +_mm_shuffle_pd :: #force_inline proc "c" (a, b: __m128d, $MASK: u32) -> __m128d { + return simd.shuffle(a, b, MASK&0b1, ((MASK>>1)&0b1) + 2) +} +@(require_results, enable_target_feature="sse2") +_mm_move_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return _mm_setr_pd(simd.extract(b, 0), simd.extract(a, 1)) +} + + + + +@(require_results, enable_target_feature="sse2") +_mm_castpd_ps :: #force_inline proc "c" (a: __m128d) -> __m128 { + return transmute(__m128)a +} +@(require_results, enable_target_feature="sse2") +_mm_castpd_si128 :: #force_inline proc "c" (a: __m128d) -> __m128i { + return transmute(__m128i)a +} +@(require_results, enable_target_feature="sse2") +_mm_castps_pd :: #force_inline proc "c" (a: __m128) -> __m128d { + return transmute(__m128d)a +} +@(require_results, enable_target_feature="sse2") +_mm_castps_si128 :: #force_inline proc "c" (a: __m128) -> __m128i { + return transmute(__m128i)a +} +@(require_results, enable_target_feature="sse2") +_mm_castsi128_pd :: #force_inline proc "c" (a: __m128i) -> __m128d { + return transmute(__m128d)a +} +@(require_results, enable_target_feature="sse2") +_mm_castsi128_ps :: #force_inline proc "c" (a: __m128i) -> __m128 { + return transmute(__m128)a +} + + +@(require_results, enable_target_feature="sse2") +_mm_undefined_pd :: #force_inline proc "c" () -> __m128d { + return __m128d{0, 0} +} +@(require_results, enable_target_feature="sse2") +_mm_undefined_si128 :: #force_inline proc "c" () -> __m128i { + return __m128i{0, 0} +} +@(require_results, enable_target_feature="sse2") +_mm_unpackhi_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return simd.shuffle(a, b, 1, 3) +} +@(require_results, enable_target_feature="sse2") +_mm_unpacklo_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return simd.shuffle(a, b, 0, 2) +} + + +when ODIN_ARCH == .amd64 { + @(require_results, enable_target_feature="sse2") + _mm_cvtsd_si64 :: #force_inline proc "c" (a: __m128d) -> i64 { + return cvtsd2si64(a) + } + @(require_results, enable_target_feature="sse2") + _mm_cvtsd_si64x :: #force_inline proc "c" (a: __m128d) -> i64 { + return _mm_cvtsd_si64(a) + } + @(require_results, enable_target_feature="sse2") + _mm_cvttsd_si64 :: #force_inline proc "c" (a: __m128d) -> i64 { + return cvttsd2si64(a) + } + @(require_results, enable_target_feature="sse2") + _mm_cvttsd_si64x :: #force_inline proc "c" (a: __m128d) -> i64 { + return _mm_cvttsd_si64(a) + } + @(enable_target_feature="sse2") + _mm_stream_si64 :: #force_inline proc "c" (mem_addr: ^i64, a: i64) { + intrinsics.non_temporal_store(mem_addr, a) + } + @(require_results, enable_target_feature="sse2") + _mm_cvtsi64_si128 :: #force_inline proc "c" (a: i64) -> __m128i { + return _mm_set_epi64x(0, a) + } + @(require_results, enable_target_feature="sse2") + _mm_cvtsi64x_si128 :: #force_inline proc "c" (a: i64) -> __m128i { + return _mm_cvtsi64_si128(a) + } + @(require_results, enable_target_feature="sse2") + _mm_cvtsi128_si64 :: #force_inline proc "c" (a: __m128i) -> i64 { + return simd.extract(transmute(i64x2)a, 0) + } + @(require_results, enable_target_feature="sse2") + _mm_cvtsi128_si64x :: #force_inline proc "c" (a: __m128i) -> i64 { + return _mm_cvtsi128_si64(a) + } + @(require_results, enable_target_feature="sse2") + _mm_cvtsi64_sd :: #force_inline proc "c" (a: __m128d, b: i64) -> __m128d { + return simd.replace(a, 0, f64(b)) + } + @(require_results, enable_target_feature="sse2") + _mm_cvtsi64x_sd :: #force_inline proc "c" (a: __m128d, b: i64) -> __m128d { + return _mm_cvtsi64_sd(a, b) + } +} + + +@(private, default_calling_convention="c") +foreign _ { + @(link_name="llvm.x86.sse2.pause") + pause :: proc() --- + @(link_name="llvm.x86.sse2.clflush") + clflush :: proc(p: rawptr) --- + @(link_name="llvm.x86.sse2.lfence") + lfence :: proc() --- + @(link_name="llvm.x86.sse2.mfence") + mfence :: proc() --- + @(link_name="llvm.x86.sse2.pavg.b") + pavgb :: proc(a, b: u8x16) -> u8x16 --- + @(link_name="llvm.x86.sse2.pavg.w") + pavgw :: proc(a, b: u16x8) -> u16x8 --- + @(link_name="llvm.x86.sse2.pmadd.wd") + pmaddwd :: proc(a, b: i16x8) -> i32x4 --- + @(link_name="llvm.x86.sse2.pmaxs.w") + pmaxsw :: proc(a, b: i16x8) -> i16x8 --- + @(link_name="llvm.x86.sse2.pmaxu.b") + pmaxub :: proc(a, b: u8x16) -> u8x16 --- + @(link_name="llvm.x86.sse2.pmins.w") + pminsw :: proc(a, b: i16x8) -> i16x8 --- + @(link_name="llvm.x86.sse2.pminu.b") + pminub :: proc(a, b: u8x16) -> u8x16 --- + @(link_name="llvm.x86.sse2.pmulh.w") + pmulhw :: proc(a, b: i16x8) -> i16x8 --- + @(link_name="llvm.x86.sse2.pmulhu.w") + pmulhuw :: proc(a, b: u16x8) -> u16x8 --- + @(link_name="llvm.x86.sse2.pmulu.dq") + pmuludq :: proc(a, b: u32x4) -> u64x2 --- + @(link_name="llvm.x86.sse2.psad.bw") + psadbw :: proc(a, b: u8x16) -> u64x2 --- + @(link_name="llvm.x86.sse2.pslli.w") + pslliw :: proc(a: i16x8, #const imm8: u32) -> i16x8 --- + @(link_name="llvm.x86.sse2.psll.w") + psllw :: proc(a: i16x8, count: i16x8) -> i16x8 --- + @(link_name="llvm.x86.sse2.pslli.d") + psllid :: proc(a: i32x4, #const imm8: u32) -> i32x4 --- + @(link_name="llvm.x86.sse2.psll.d") + pslld :: proc(a: i32x4, count: i32x4) -> i32x4 --- + @(link_name="llvm.x86.sse2.pslli.q") + pslliq :: proc(a: i64x2, #const imm8: u32) -> i64x2 --- + @(link_name="llvm.x86.sse2.psll.q") + psllq :: proc(a: i64x2, count: i64x2) -> i64x2 --- + @(link_name="llvm.x86.sse2.psrai.w") + psraiw :: proc(a: i16x8, #const imm8: u32) -> i16x8 --- + @(link_name="llvm.x86.sse2.psra.w") + psraw :: proc(a: i16x8, count: i16x8) -> i16x8 --- + @(link_name="llvm.x86.sse2.psrai.d") + psraid :: proc(a: i32x4, #const imm8: u32) -> i32x4 --- + @(link_name="llvm.x86.sse2.psra.d") + psrad :: proc(a: i32x4, count: i32x4) -> i32x4 --- + @(link_name="llvm.x86.sse2.psrli.w") + psrliw :: proc(a: i16x8, #const imm8: u32) -> i16x8 --- + @(link_name="llvm.x86.sse2.psrl.w") + psrlw :: proc(a: i16x8, count: i16x8) -> i16x8 --- + @(link_name="llvm.x86.sse2.psrli.d") + psrlid :: proc(a: i32x4, #const imm8: u32) -> i32x4 --- + @(link_name="llvm.x86.sse2.psrl.d") + psrld :: proc(a: i32x4, count: i32x4) -> i32x4 --- + @(link_name="llvm.x86.sse2.psrli.q") + psrliq :: proc(a: i64x2, #const imm8: u32) -> i64x2 --- + @(link_name="llvm.x86.sse2.psrl.q") + psrlq :: proc(a: i64x2, count: i64x2) -> i64x2 --- + @(link_name="llvm.x86.sse2.cvtdq2ps") + cvtdq2ps :: proc(a: i32x4) -> __m128 --- + @(link_name="llvm.x86.sse2.cvtps2dq") + cvtps2dq :: proc(a: __m128) -> i32x4 --- + @(link_name="llvm.x86.sse2.maskmov.dqu") + maskmovdqu :: proc(a: i8x16, mask: i8x16, mem_addr: rawptr) --- + @(link_name="llvm.x86.sse2.packsswb.128") + packsswb :: proc(a, b: i16x8) -> i8x16 --- + @(link_name="llvm.x86.sse2.packssdw.128") + packssdw :: proc(a, b: i32x4) -> i16x8 --- + @(link_name="llvm.x86.sse2.packuswb.128") + packuswb :: proc(a, b: i16x8) -> u8x16 --- + @(link_name="llvm.x86.sse2.pmovmskb.128") + pmovmskb :: proc(a: i8x16) -> i32 --- + @(link_name="llvm.x86.sse2.max.sd") + maxsd :: proc(a, b: __m128d) -> __m128d --- + @(link_name="llvm.x86.sse2.max.pd") + maxpd :: proc(a, b: __m128d) -> __m128d --- + @(link_name="llvm.x86.sse2.min.sd") + minsd :: proc(a, b: __m128d) -> __m128d --- + @(link_name="llvm.x86.sse2.min.pd") + minpd :: proc(a, b: __m128d) -> __m128d --- + @(link_name="llvm.x86.sse2.sqrt.sd") + sqrtsd :: proc(a: __m128d) -> __m128d --- + @(link_name="llvm.x86.sse2.sqrt.pd") + sqrtpd :: proc(a: __m128d) -> __m128d --- + @(link_name="llvm.x86.sse2.cmp.sd") + cmpsd :: proc(a, b: __m128d, imm8: i8) -> __m128d --- + @(link_name="llvm.x86.sse2.cmp.pd") + cmppd :: proc(a, b: __m128d, imm8: i8) -> __m128d --- + @(link_name="llvm.x86.sse2.comieq.sd") + comieqsd :: proc(a, b: __m128d) -> i32 --- + @(link_name="llvm.x86.sse2.comilt.sd") + comiltsd :: proc(a, b: __m128d) -> i32 --- + @(link_name="llvm.x86.sse2.comile.sd") + comilesd :: proc(a, b: __m128d) -> i32 --- + @(link_name="llvm.x86.sse2.comigt.sd") + comigtsd :: proc(a, b: __m128d) -> i32 --- + @(link_name="llvm.x86.sse2.comige.sd") + comigesd :: proc(a, b: __m128d) -> i32 --- + @(link_name="llvm.x86.sse2.comineq.sd") + comineqsd :: proc(a, b: __m128d) -> i32 --- + @(link_name="llvm.x86.sse2.ucomieq.sd") + ucomieqsd :: proc(a, b: __m128d) -> i32 --- + @(link_name="llvm.x86.sse2.ucomilt.sd") + ucomiltsd :: proc(a, b: __m128d) -> i32 --- + @(link_name="llvm.x86.sse2.ucomile.sd") + ucomilesd :: proc(a, b: __m128d) -> i32 --- + @(link_name="llvm.x86.sse2.ucomigt.sd") + ucomigtsd :: proc(a, b: __m128d) -> i32 --- + @(link_name="llvm.x86.sse2.ucomige.sd") + ucomigesd :: proc(a, b: __m128d) -> i32 --- + @(link_name="llvm.x86.sse2.ucomineq.sd") + ucomineqsd :: proc(a, b: __m128d) -> i32 --- + @(link_name="llvm.x86.sse2.movmsk.pd") + movmskpd :: proc(a: __m128d) -> i32 --- + @(link_name="llvm.x86.sse2.cvtpd2ps") + cvtpd2ps :: proc(a: __m128d) -> __m128 --- + @(link_name="llvm.x86.sse2.cvtps2pd") + cvtps2pd :: proc(a: __m128) -> __m128d --- + @(link_name="llvm.x86.sse2.cvtpd2dq") + cvtpd2dq :: proc(a: __m128d) -> i32x4 --- + @(link_name="llvm.x86.sse2.cvtsd2si") + cvtsd2si :: proc(a: __m128d) -> i32 --- + @(link_name="llvm.x86.sse2.cvtsd2ss") + cvtsd2ss :: proc(a, b: __m128d) -> __m128 --- + @(link_name="llvm.x86.sse2.cvtss2sd") + cvtss2sd :: proc(a, b: __m128) -> __m128d --- + @(link_name="llvm.x86.sse2.cvttpd2dq") + cvttpd2dq :: proc(a: __m128d) -> i32x4 --- + @(link_name="llvm.x86.sse2.cvttsd2si") + cvttsd2si :: proc(a: __m128d) -> i32 --- + @(link_name="llvm.x86.sse2.cvttps2dq") + cvttps2dq :: proc(a: __m128) -> i32x4 --- + @(link_name="llvm.x86.sse2.storeu.dq") + storeudq :: proc(mem_addr: rawptr, a: __m128i) --- + @(link_name="llvm.x86.sse2.storeu.pd") + storeupd :: proc(mem_addr: rawptr, a: __m128d) --- + + // amd64 only + @(link_name="llvm.x86.sse2.cvtsd2si64") + cvtsd2si64 :: proc(a: __m128d) -> i64 --- + @(link_name="llvm.x86.sse2.cvttsd2si64") + cvttsd2si64 :: proc(a: __m128d) -> i64 --- +} diff --git a/core/simd/x86/sse3.odin b/core/simd/x86/sse3.odin new file mode 100644 index 000000000..7a3073c18 --- /dev/null +++ b/core/simd/x86/sse3.odin @@ -0,0 +1,68 @@ +//+build i386, amd64 +package simd_x86 + +import "core:intrinsics" +import "core:simd" + +@(require_results, enable_target_feature="sse3") +_mm_addsub_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return addsubps(a, b) +} +@(require_results, enable_target_feature="sse3") +_mm_addsub_pd :: #force_inline proc "c" (a: __m128d, b: __m128d) -> __m128d { + return addsubpd(a, b) +} +@(require_results, enable_target_feature="sse3") +_mm_hadd_pd :: #force_inline proc "c" (a: __m128d, b: __m128d) -> __m128d { + return haddpd(a, b) +} +@(require_results, enable_target_feature="sse3") +_mm_hadd_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return haddps(a, b) +} +@(require_results, enable_target_feature="sse3") +_mm_hsub_pd :: #force_inline proc "c" (a: __m128d, b: __m128d) -> __m128d { + return hsubpd(a, b) +} +@(require_results, enable_target_feature="sse3") +_mm_hsub_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return hsubps(a, b) +} +@(require_results, enable_target_feature="sse3") +_mm_lddqu_si128 :: #force_inline proc "c" (mem_addr: ^__m128i) -> __m128i { + return transmute(__m128i)lddqu(mem_addr) +} +@(require_results, enable_target_feature="sse3") +_mm_movedup_pd :: #force_inline proc "c" (a: __m128d) -> __m128d { + return simd.shuffle(a, a, 0, 0) +} +@(require_results, enable_target_feature="sse3") +_mm_loaddup_pd :: #force_inline proc "c" (mem_addr: [^]f64) -> __m128d { + return _mm_load1_pd(mem_addr) +} +@(require_results, enable_target_feature="sse3") +_mm_movehdup_ps :: #force_inline proc "c" (a: __m128) -> __m128 { + return simd.shuffle(a, a, 1, 1, 3, 3) +} +@(require_results, enable_target_feature="sse3") +_mm_moveldup_ps :: #force_inline proc "c" (a: __m128) -> __m128 { + return simd.shuffle(a, a, 0, 0, 2, 2) +} + +@(private, default_calling_convention="c") +foreign _ { + @(link_name = "llvm.x86.sse3.addsub.ps") + addsubps :: proc(a, b: __m128) -> __m128 --- + @(link_name = "llvm.x86.sse3.addsub.pd") + addsubpd :: proc(a: __m128d, b: __m128d) -> __m128d --- + @(link_name = "llvm.x86.sse3.hadd.pd") + haddpd :: proc(a: __m128d, b: __m128d) -> __m128d --- + @(link_name = "llvm.x86.sse3.hadd.ps") + haddps :: proc(a, b: __m128) -> __m128 --- + @(link_name = "llvm.x86.sse3.hsub.pd") + hsubpd :: proc(a: __m128d, b: __m128d) -> __m128d --- + @(link_name = "llvm.x86.sse3.hsub.ps") + hsubps :: proc(a, b: __m128) -> __m128 --- + @(link_name = "llvm.x86.sse3.ldu.dq") + lddqu :: proc(mem_addr: rawptr) -> i8x16 --- +} \ No newline at end of file diff --git a/core/simd/x86/sse41.odin b/core/simd/x86/sse41.odin new file mode 100644 index 000000000..b35be33f2 --- /dev/null +++ b/core/simd/x86/sse41.odin @@ -0,0 +1,352 @@ +//+build i386, amd64 +package simd_x86 + +import "core:simd" + +// SSE4 rounding constants +_MM_FROUND_TO_NEAREST_INT :: 0x00 +_MM_FROUND_TO_NEG_INF :: 0x01 +_MM_FROUND_TO_POS_INF :: 0x02 +_MM_FROUND_TO_ZERO :: 0x03 +_MM_FROUND_CUR_DIRECTION :: 0x04 +_MM_FROUND_RAISE_EXC :: 0x00 +_MM_FROUND_NO_EXC :: 0x08 +_MM_FROUND_NINT :: 0x00 +_MM_FROUND_FLOOR :: _MM_FROUND_RAISE_EXC | _MM_FROUND_TO_NEG_INF +_MM_FROUND_CEIL :: _MM_FROUND_RAISE_EXC | _MM_FROUND_TO_POS_INF +_MM_FROUND_TRUNC :: _MM_FROUND_RAISE_EXC | _MM_FROUND_TO_ZERO +_MM_FROUND_RINT :: _MM_FROUND_RAISE_EXC | _MM_FROUND_CUR_DIRECTION +_MM_FROUND_NEARBYINT :: _MM_FROUND_NO_EXC | _MM_FROUND_CUR_DIRECTION + + + +@(require_results, enable_target_feature="sse4.1") +_mm_blendv_epi8 :: #force_inline proc "c" (a, b, mask: __m128i) -> __m128i { + return transmute(__m128i)pblendvb(transmute(i8x16)a, transmute(i8x16)b, transmute(i8x16)mask) +} +@(require_results, enable_target_feature="sse4.1") +_mm_blend_epi16 :: #force_inline proc "c" (a, b: __m128i, $IMM8: u8) -> __m128i { + return transmute(__m128i)pblendw(transmute(i16x8)a, transmute(i16x8)b, IMM8) +} +@(require_results, enable_target_feature="sse4.1") +_mm_blendv_pd :: #force_inline proc "c" (a, b, mask: __m128d) -> __m128d { + return blendvpd(a, b, mask) +} +@(require_results, enable_target_feature="sse4.1") +_mm_blendv_ps :: #force_inline proc "c" (a, b, mask: __m128) -> __m128 { + return blendvps(a, b, mask) +} +@(require_results, enable_target_feature="sse4.1") +_mm_blend_pd :: #force_inline proc "c" (a, b: __m128d, $IMM2: u8) -> __m128d { + return blendpd(a, b, IMM2) +} +@(require_results, enable_target_feature="sse4.1") +_mm_blend_ps :: #force_inline proc "c" (a, b: __m128, $IMM4: u8) -> __m128 { + return blendps(a, b, IMM4) +} +@(require_results, enable_target_feature="sse4.1") +_mm_extract_ps :: #force_inline proc "c" (a: __m128, $IMM8: u32) -> i32 { + return transmute(i32)simd.extract(a, IMM8) +} +@(require_results, enable_target_feature="sse4.1") +_mm_extract_epi8 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> i32 { + return i32(simd.extract(transmute(u8x16)a, IMM8)) +} +@(require_results, enable_target_feature="sse4.1") +_mm_extract_epi32 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> i32 { + return simd.extract(transmute(i32x4)a, IMM8) +} +@(require_results, enable_target_feature="sse4.1") +_mm_insert_ps :: #force_inline proc "c" (a, b: __m128, $IMM8: u8) -> __m128 { + return insertps(a, b, IMM8) +} +@(require_results, enable_target_feature="sse4.1") +_mm_insert_epi8 :: #force_inline proc "c" (a: __m128i, i: i32, $IMM8: u32) -> __m128i { + return transmute(__m128i)simd.replace(transmute(i8x16)a, IMM8, i8(i)) +} +@(require_results, enable_target_feature="sse4.1") +_mm_insert_epi32 :: #force_inline proc "c" (a: __m128i, i: i32, $IMM8: u32) -> __m128i { + return transmute(__m128i)simd.replace(transmute(i32x4)a, IMM8, i) +} +@(require_results, enable_target_feature="sse4.1") +_mm_max_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)pmaxsb(transmute(i8x16)a, transmute(i8x16)b) +} +@(require_results, enable_target_feature="sse4.1") +_mm_max_epu16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)pmaxuw(transmute(u16x8)a, transmute(u16x8)b) +} +@(require_results, enable_target_feature="sse4.1") +_mm_max_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)pmaxsd(transmute(i32x4)a, transmute(i32x4)b) +} +@(require_results, enable_target_feature="sse4.1") +_mm_max_epu32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)pmaxud(transmute(u32x4)a, transmute(u32x4)b) +} +@(require_results, enable_target_feature="sse4.1") +_mm_min_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)pminsb(transmute(i8x16)a, transmute(i8x16)b) +} +@(require_results, enable_target_feature="sse4.1") +_mm_min_epu16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)pminuw(transmute(u16x8)a, transmute(u16x8)b) +} +@(require_results, enable_target_feature="sse4.1") +_mm_min_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)pminsd(transmute(i32x4)a, transmute(i32x4)b) +} +@(require_results, enable_target_feature="sse4.1") +_mm_min_epu32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)pminud(transmute(u32x4)a, transmute(u32x4)b) +} +@(require_results, enable_target_feature="sse4.1") +_mm_packus_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)packusdw(transmute(i32x4)a, transmute(i32x4)b) +} +@(require_results, enable_target_feature="sse4.1") +_mm_cmpeq_epi64 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)simd.lanes_eq(transmute(i64x2)a, transmute(i64x2)b) +} +@(require_results, enable_target_feature="sse4.1") +_mm_cvtepi8_epi16 :: #force_inline proc "c" (a: __m128i) -> __m128i { + x := transmute(i8x16)a + y := simd.shuffle(x, x, 0, 1, 2, 3, 4, 5, 6, 7) + return transmute(__m128i)i16x8(y) +} +@(require_results, enable_target_feature="sse4.1") +_mm_cvtepi8_epi32 :: #force_inline proc "c" (a: __m128i) -> __m128i { + x := transmute(i8x16)a + y := simd.shuffle(x, x, 0, 1, 2, 3) + return transmute(__m128i)i32x4(y) +} +@(require_results, enable_target_feature="sse4.1") +_mm_cvtepi8_epi64 :: #force_inline proc "c" (a: __m128i) -> __m128i { + x := transmute(i8x16)a + y := simd.shuffle(x, x, 0, 1) + return transmute(__m128i)i64x2(y) +} +@(require_results, enable_target_feature="sse4.1") +_mm_cvtepi16_epi32 :: #force_inline proc "c" (a: __m128i) -> __m128i { + x := transmute(i16x8)a + y := simd.shuffle(x, x, 0, 1, 2, 3) + return transmute(__m128i)i32x4(y) +} +@(require_results, enable_target_feature="sse4.1") +_mm_cvtepi16_epi64 :: #force_inline proc "c" (a: __m128i) -> __m128i { + x := transmute(i16x8)a + y := simd.shuffle(x, x, 0, 1) + return transmute(__m128i)i64x2(y) +} +@(require_results, enable_target_feature="sse4.1") +_mm_cvtepi32_epi64 :: #force_inline proc "c" (a: __m128i) -> __m128i { + x := transmute(i32x4)a + y := simd.shuffle(x, x, 0, 1) + return transmute(__m128i)i64x2(y) +} +@(require_results, enable_target_feature="sse4.1") +_mm_cvtepu8_epi16 :: #force_inline proc "c" (a: __m128i) -> __m128i { + x := transmute(u8x16)a + y := simd.shuffle(x, x, 0, 1, 2, 3, 4, 5, 6, 7) + return transmute(__m128i)i16x8(y) +} +@(require_results, enable_target_feature="sse4.1") +_mm_cvtepu8_epi32 :: #force_inline proc "c" (a: __m128i) -> __m128i { + x := transmute(u8x16)a + y := simd.shuffle(x, x, 0, 1, 2, 3) + return transmute(__m128i)i32x4(y) +} +@(require_results, enable_target_feature="sse4.1") +_mm_cvtepu8_epi64 :: #force_inline proc "c" (a: __m128i) -> __m128i { + x := transmute(u8x16)a + y := simd.shuffle(x, x, 0, 1) + return transmute(__m128i)i64x2(y) +} +@(require_results, enable_target_feature="sse4.1") +_mm_cvtepu16_epi32 :: #force_inline proc "c" (a: __m128i) -> __m128i { + x := transmute(u16x8)a + y := simd.shuffle(x, x, 0, 1, 2, 3) + return transmute(__m128i)i32x4(y) +} +@(require_results, enable_target_feature="sse4.1") +_mm_cvtepu16_epi64 :: #force_inline proc "c" (a: __m128i) -> __m128i { + x := transmute(u16x8)a + y := simd.shuffle(x, x, 0, 1) + return transmute(__m128i)i64x2(y) +} +@(require_results, enable_target_feature="sse4.1") +_mm_cvtepu32_epi64 :: #force_inline proc "c" (a: __m128i) -> __m128i { + x := transmute(u32x4)a + y := simd.shuffle(x, x, 0, 1) + return transmute(__m128i)i64x2(y) +} +@(require_results, enable_target_feature="sse4.1") +_mm_dp_pd :: #force_inline proc "c" (a, b: __m128d, $IMM8: u8) -> __m128d { + return dppd(a, b, IMM8) +} +@(require_results, enable_target_feature="sse4.1") +_mm_dp_ps :: #force_inline proc "c" (a, b: __m128, $IMM8: u8) -> __m128 { + return dpps(a, b, IMM8) +} +@(require_results, enable_target_feature="sse4.1") +_mm_floor_pd :: #force_inline proc "c" (a: __m128d) -> __m128d { + return simd.floor(a) +} +@(require_results, enable_target_feature="sse4.1") +_mm_floor_ps :: #force_inline proc "c" (a: __m128) -> __m128 { + return simd.floor(a) +} +@(require_results, enable_target_feature="sse4.1") +_mm_floor_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return roundsd(a, b, _MM_FROUND_FLOOR) +} +@(require_results, enable_target_feature="sse4.1") +_mm_floor_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return roundss(a, b, _MM_FROUND_FLOOR) +} +@(require_results, enable_target_feature="sse4.1") +_mm_ceil_pd :: #force_inline proc "c" (a: __m128d) -> __m128d { + return simd.ceil(a) +} +@(require_results, enable_target_feature="sse4.1") +_mm_ceil_ps :: #force_inline proc "c" (a: __m128) -> __m128 { + return simd.ceil(a) +} +@(require_results, enable_target_feature="sse4.1") +_mm_ceil_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { + return roundsd(a, b, _MM_FROUND_CEIL) +} +@(require_results, enable_target_feature="sse4.1") +_mm_ceil_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { + return roundss(a, b, _MM_FROUND_CEIL) +} +@(require_results, enable_target_feature="sse4.1") +_mm_round_pd :: #force_inline proc "c" (a: __m128d, $ROUNDING: i32) -> __m128d { + return roundpd(a, ROUNDING) +} +@(require_results, enable_target_feature="sse4.1") +_mm_round_ps :: #force_inline proc "c" (a: __m128, $ROUNDING: i32) -> __m128 { + return roundps(a, ROUNDING) +} +@(require_results, enable_target_feature="sse4.1") +_mm_round_sd :: #force_inline proc "c" (a, b: __m128d, $ROUNDING: i32) -> __m128d { + return roundsd(a, b, ROUNDING) +} +@(require_results, enable_target_feature="sse4.1") +_mm_round_ss :: #force_inline proc "c" (a, b: __m128, $ROUNDING: i32) -> __m128 { + return roundss(a, b, ROUNDING) +} +@(require_results, enable_target_feature="sse4.1") +_mm_minpos_epu16 :: #force_inline proc "c" (a: __m128i) -> __m128i { + return transmute(__m128i)phminposuw(transmute(u16x8)a) +} +@(require_results, enable_target_feature="sse4.1") +_mm_mul_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)pmuldq(transmute(i32x4)a, transmute(i32x4)b) +} +@(require_results, enable_target_feature="sse4.1") +_mm_mullo_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)simd.mul(transmute(i32x4)a, transmute(i32x4)b) +} +@(require_results, enable_target_feature="sse4.1") +_mm_mpsadbw_epu8 :: #force_inline proc "c" (a, b: __m128i, $IMM8: u8) -> __m128i { + return transmute(__m128i)mpsadbw(transmute(u8x16)a, transmute(u8x16)b, IMM8) +} +@(require_results, enable_target_feature="sse4.1") +_mm_testz_si128 :: #force_inline proc "c" (a: __m128i, mask: __m128i) -> i32 { + return ptestz(transmute(i64x2)a, transmute(i64x2)mask) +} +@(require_results, enable_target_feature="sse4.1") +_mm_testc_si128 :: #force_inline proc "c" (a: __m128i, mask: __m128i) -> i32 { + return ptestc(transmute(i64x2)a, transmute(i64x2)mask) +} +@(require_results, enable_target_feature="sse4.1") +_mm_testnzc_si128 :: #force_inline proc "c" (a: __m128i, mask: __m128i) -> i32 { + return ptestnzc(transmute(i64x2)a, transmute(i64x2)mask) +} +@(require_results, enable_target_feature="sse4.1") +_mm_test_all_zeros :: #force_inline proc "c" (a: __m128i, mask: __m128i) -> i32 { + return _mm_testz_si128(a, mask) +} +@(require_results, enable_target_feature="sse4.1") +_mm_test_all_ones :: #force_inline proc "c" (a: __m128i) -> i32 { + return _mm_testc_si128(a, _mm_cmpeq_epi32(a, a)) +} +@(require_results, enable_target_feature="sse4.1") +_mm_test_mix_ones_zeros :: #force_inline proc "c" (a: __m128i, mask: __m128i) -> i32 { + return _mm_testnzc_si128(a, mask) +} + + +when ODIN_ARCH == .amd64 { + @(require_results, enable_target_feature="sse4.1") + _mm_extract_epi64 :: #force_inline proc "c" (a: __m128i, $IMM1: u32) -> i64 { + return simd.extract(transmute(i64x2)a, IMM1) + } + + @(require_results, enable_target_feature="sse4.1") + _mm_insert_epi64 :: #force_inline proc "c" (a: __m128i, i: i64, $IMM1: u32) -> __m128i { + return transmute(__m128i)simd.replace(transmute(i64x2)a, IMM1, i) + } +} + + +@(private, default_calling_convention="c") +foreign _ { + @(link_name = "llvm.x86.sse41.pblendvb") + pblendvb :: proc(a, b: i8x16, mask: i8x16) -> i8x16 --- + @(link_name = "llvm.x86.sse41.blendvpd") + blendvpd :: proc(a, b, mask: __m128d) -> __m128d --- + @(link_name = "llvm.x86.sse41.blendvps") + blendvps :: proc(a, b, mask: __m128) -> __m128 --- + @(link_name = "llvm.x86.sse41.blendpd") + blendpd :: proc(a, b: __m128d, #const imm2: u8) -> __m128d --- + @(link_name = "llvm.x86.sse41.blendps") + blendps :: proc(a, b: __m128, #const imm4: u8) -> __m128 --- + @(link_name = "llvm.x86.sse41.pblendw") + pblendw :: proc(a: i16x8, b: i16x8, #const imm8: u8) -> i16x8 --- + @(link_name = "llvm.x86.sse41.insertps") + insertps :: proc(a, b: __m128, #const imm8: u8) -> __m128 --- + @(link_name = "llvm.x86.sse41.pmaxsb") + pmaxsb :: proc(a, b: i8x16) -> i8x16 --- + @(link_name = "llvm.x86.sse41.pmaxuw") + pmaxuw :: proc(a, b: u16x8) -> u16x8 --- + @(link_name = "llvm.x86.sse41.pmaxsd") + pmaxsd :: proc(a, b: i32x4) -> i32x4 --- + @(link_name = "llvm.x86.sse41.pmaxud") + pmaxud :: proc(a, b: u32x4) -> u32x4 --- + @(link_name = "llvm.x86.sse41.pminsb") + pminsb :: proc(a, b: i8x16) -> i8x16 --- + @(link_name = "llvm.x86.sse41.pminuw") + pminuw :: proc(a, b: u16x8) -> u16x8 --- + @(link_name = "llvm.x86.sse41.pminsd") + pminsd :: proc(a, b: i32x4) -> i32x4 --- + @(link_name = "llvm.x86.sse41.pminud") + pminud :: proc(a, b: u32x4) -> u32x4 --- + @(link_name = "llvm.x86.sse41.packusdw") + packusdw :: proc(a, b: i32x4) -> u16x8 --- + @(link_name = "llvm.x86.sse41.dppd") + dppd :: proc(a, b: __m128d, #const imm8: u8) -> __m128d --- + @(link_name = "llvm.x86.sse41.dpps") + dpps :: proc(a, b: __m128, #const imm8: u8) -> __m128 --- + @(link_name = "llvm.x86.sse41.round.pd") + roundpd :: proc(a: __m128d, rounding: i32) -> __m128d --- + @(link_name = "llvm.x86.sse41.round.ps") + roundps :: proc(a: __m128, rounding: i32) -> __m128 --- + @(link_name = "llvm.x86.sse41.round.sd") + roundsd :: proc(a, b: __m128d, rounding: i32) -> __m128d --- + @(link_name = "llvm.x86.sse41.round.ss") + roundss :: proc(a, b: __m128, rounding: i32) -> __m128 --- + @(link_name = "llvm.x86.sse41.phminposuw") + phminposuw :: proc(a: u16x8) -> u16x8 --- + @(link_name = "llvm.x86.sse41.pmuldq") + pmuldq :: proc(a, b: i32x4) -> i64x2 --- + @(link_name = "llvm.x86.sse41.mpsadbw") + mpsadbw :: proc(a, b: u8x16, #const imm8: u8) -> u16x8 --- + @(link_name = "llvm.x86.sse41.ptestz") + ptestz :: proc(a, mask: i64x2) -> i32 --- + @(link_name = "llvm.x86.sse41.ptestc") + ptestc :: proc(a, mask: i64x2) -> i32 --- + @(link_name = "llvm.x86.sse41.ptestnzc") + ptestnzc :: proc(a, mask: i64x2) -> i32 --- +} \ No newline at end of file diff --git a/core/simd/x86/sse42.odin b/core/simd/x86/sse42.odin new file mode 100644 index 000000000..62b4f0478 --- /dev/null +++ b/core/simd/x86/sse42.odin @@ -0,0 +1,149 @@ +//+build i386, amd64 +package simd_x86 + +import "core:simd" + +_SIDD_UBYTE_OPS :: 0b0000_0000 +_SIDD_UWORD_OPS :: 0b0000_0001 +_SIDD_SBYTE_OPS :: 0b0000_0010 +_SIDD_SWORD_OPS :: 0b0000_0011 + +_SIDD_CMP_EQUAL_ANY :: 0b0000_0000 +_SIDD_CMP_RANGES :: 0b0000_0100 +_SIDD_CMP_EQUAL_EACH :: 0b0000_1000 +_SIDD_CMP_EQUAL_ORDERED :: 0b0000_1100 + +_SIDD_POSITIVE_POLARITY :: 0b0000_0000 +_SIDD_NEGATIVE_POLARITY :: 0b0001_0000 +_SIDD_MASKED_POSITIVE_POLARITY :: 0b0010_0000 +_SIDD_MASKED_NEGATIVE_POLARITY :: 0b0011_0000 + +_SIDD_LEAST_SIGNIFICANT :: 0b0000_0000 +_SIDD_MOST_SIGNIFICANT :: 0b0100_0000 + +_SIDD_BIT_MASK :: 0b0000_0000 +_SIDD_UNIT_MASK :: 0b0100_0000 + +@(require_results, enable_target_feature="sse4.2") +_mm_cmpistrm :: #force_inline proc "c" (a: __m128i, b: __m128i, $IMM8: i8) -> __m128i { + return transmute(__m128i)pcmpistrm128(transmute(i8x16)a, transmute(i8x16)b, IMM8) +} +@(require_results, enable_target_feature="sse4.2") +_mm_cmpistri :: #force_inline proc "c" (a: __m128i, b: __m128i, $IMM8: i8) -> i32 { + return pcmpistri128(transmute(i8x16)a, transmute(i8x16)b, IMM8) +} +@(require_results, enable_target_feature="sse4.2") +_mm_cmpistrz :: #force_inline proc "c" (a: __m128i, b: __m128i, $IMM8: i8) -> i32 { + return pcmpistriz128(transmute(i8x16)a, transmute(i8x16)b, IMM8) +} +@(require_results, enable_target_feature="sse4.2") +_mm_cmpistrc :: #force_inline proc "c" (a: __m128i, b: __m128i, $IMM8: i8) -> i32 { + return pcmpistric128(transmute(i8x16)a, transmute(i8x16)b, IMM8) +} +@(require_results, enable_target_feature="sse4.2") +_mm_cmpistrs :: #force_inline proc "c" (a: __m128i, b: __m128i, $IMM8: i8) -> i32 { + return pcmpistris128(transmute(i8x16)a, transmute(i8x16)b, IMM8) +} +@(require_results, enable_target_feature="sse4.2") +_mm_cmpistro :: #force_inline proc "c" (a: __m128i, b: __m128i, $IMM8: i8) -> i32 { + return pcmpistrio128(transmute(i8x16)a, transmute(i8x16)b, IMM8) +} +@(require_results, enable_target_feature="sse4.2") +_mm_cmpistra :: #force_inline proc "c" (a: __m128i, b: __m128i, $IMM8: i8) -> i32 { + return pcmpistria128(transmute(i8x16)a, transmute(i8x16)b, IMM8) +} +@(require_results, enable_target_feature="sse4.2") +_mm_cmpestrm :: #force_inline proc "c" (a: __m128i, la: i32, b: __m128i, lb: i32, $IMM8: i8) -> __m128i { + return transmute(__m128i)pcmpestrm128(transmute(i8x16)a, la, transmute(i8x16)b, lb, IMM8) +} +@(require_results, enable_target_feature="sse4.2") +_mm_cmpestri :: #force_inline proc "c" (a: __m128i, la: i32, b: __m128i, lb: i32, $IMM8: i8) -> i32 { + return pcmpestri128(transmute(i8x16)a, la, transmute(i8x16)b, lb, IMM8) +} +@(require_results, enable_target_feature="sse4.2") +_mm_cmpestrz :: #force_inline proc "c" (a: __m128i, la: i32, b: __m128i, lb: i32, $IMM8: i8) -> i32 { + return pcmpestriz128(transmute(i8x16)a, la, transmute(i8x16)b, lb, IMM8) +} +@(require_results, enable_target_feature="sse4.2") +_mm_cmpestrc :: #force_inline proc "c" (a: __m128i, la: i32, b: __m128i, lb: i32, $IMM8: i8) -> i32 { + return pcmpestric128(transmute(i8x16)a, la, transmute(i8x16)b, lb, IMM8) +} +@(require_results, enable_target_feature="sse4.2") +_mm_cmpestrs :: #force_inline proc "c" (a: __m128i, la: i32, b: __m128i, lb: i32, $IMM8: i8) -> i32 { + return pcmpestris128(transmute(i8x16)a, la, transmute(i8x16)b, lb, IMM8) +} +@(require_results, enable_target_feature="sse4.2") +_mm_cmpestro :: #force_inline proc "c" (a: __m128i, la: i32, b: __m128i, lb: i32, $IMM8: i8) -> i32 { + return pcmpestrio128(transmute(i8x16)a, la, transmute(i8x16)b, lb, IMM8) +} +@(require_results, enable_target_feature="sse4.2") +_mm_cmpestra :: #force_inline proc "c" (a: __m128i, la: i32, b: __m128i, lb: i32, $IMM8: i8) -> i32 { + return pcmpestria128(transmute(i8x16)a, la, transmute(i8x16)b, lb, IMM8) +} +@(require_results, enable_target_feature="sse4.2") +_mm_crc32_u8 :: #force_inline proc "c" (crc: u32, v: u8) -> u32 { + return crc32_32_8(crc, v) +} +@(require_results, enable_target_feature="sse4.2") +_mm_crc32_u16 :: #force_inline proc "c" (crc: u32, v: u16) -> u32 { + return crc32_32_16(crc, v) +} +@(require_results, enable_target_feature="sse4.2") +_mm_crc32_u32 :: #force_inline proc "c" (crc: u32, v: u32) -> u32 { + return crc32_32_32(crc, v) +} +@(require_results, enable_target_feature="sse4.2") +_mm_cmpgt_epi64 :: #force_inline proc "c" (a: __m128i, b: __m128i) -> __m128i { + return transmute(__m128i)simd.lanes_gt(transmute(i64x2)a, transmute(i64x2)b) +} + +when ODIN_ARCH == .amd64 { + @(require_results, enable_target_feature="sse4.2") + _mm_crc32_u64 :: #force_inline proc "c" (crc: u64, v: u64) -> u64 { + return crc32_64_64(crc, v) + } +} + +@(private, default_calling_convention="c") +foreign _ { + // SSE 4.2 string and text comparison ops + @(link_name="llvm.x86.sse42.pcmpestrm128") + pcmpestrm128 :: proc(a: i8x16, la: i32, b: i8x16, lb: i32, #const imm8: i8) -> u8x16 --- + @(link_name="llvm.x86.sse42.pcmpestri128") + pcmpestri128 :: proc(a: i8x16, la: i32, b: i8x16, lb: i32, #const imm8: i8) -> i32 --- + @(link_name="llvm.x86.sse42.pcmpestriz128") + pcmpestriz128 :: proc(a: i8x16, la: i32, b: i8x16, lb: i32, #const imm8: i8) -> i32 --- + @(link_name="llvm.x86.sse42.pcmpestric128") + pcmpestric128 :: proc(a: i8x16, la: i32, b: i8x16, lb: i32, #const imm8: i8) -> i32 --- + @(link_name="llvm.x86.sse42.pcmpestris128") + pcmpestris128 :: proc(a: i8x16, la: i32, b: i8x16, lb: i32, #const imm8: i8) -> i32 --- + @(link_name="llvm.x86.sse42.pcmpestrio128") + pcmpestrio128 :: proc(a: i8x16, la: i32, b: i8x16, lb: i32, #const imm8: i8) -> i32 --- + @(link_name="llvm.x86.sse42.pcmpestria128") + pcmpestria128 :: proc(a: i8x16, la: i32, b: i8x16, lb: i32, #const imm8: i8) -> i32 --- + @(link_name="llvm.x86.sse42.pcmpistrm128") + pcmpistrm128 :: proc(a, b: i8x16, #const imm8: i8) -> i8x16 --- + @(link_name="llvm.x86.sse42.pcmpistri128") + pcmpistri128 :: proc(a, b: i8x16, #const imm8: i8) -> i32 --- + @(link_name="llvm.x86.sse42.pcmpistriz128") + pcmpistriz128 :: proc(a, b: i8x16, #const imm8: i8) -> i32 --- + @(link_name="llvm.x86.sse42.pcmpistric128") + pcmpistric128 :: proc(a, b: i8x16, #const imm8: i8) -> i32 --- + @(link_name="llvm.x86.sse42.pcmpistris128") + pcmpistris128 :: proc(a, b: i8x16, #const imm8: i8) -> i32 --- + @(link_name="llvm.x86.sse42.pcmpistrio128") + pcmpistrio128 :: proc(a, b: i8x16, #const imm8: i8) -> i32 --- + @(link_name="llvm.x86.sse42.pcmpistria128") + pcmpistria128 :: proc(a, b: i8x16, #const imm8: i8) -> i32 --- + // SSE 4.2 CRC instructions + @(link_name="llvm.x86.sse42.crc32.32.8") + crc32_32_8 :: proc(crc: u32, v: u8) -> u32 --- + @(link_name="llvm.x86.sse42.crc32.32.16") + crc32_32_16 :: proc(crc: u32, v: u16) -> u32 --- + @(link_name="llvm.x86.sse42.crc32.32.32") + crc32_32_32 :: proc(crc: u32, v: u32) -> u32 --- + + // AMD64 Only + @(link_name="llvm.x86.sse42.crc32.64.64") + crc32_64_64 :: proc(crc: u64, v: u64) -> u64 --- +} diff --git a/core/simd/x86/ssse3.odin b/core/simd/x86/ssse3.odin new file mode 100644 index 000000000..f11ef6774 --- /dev/null +++ b/core/simd/x86/ssse3.odin @@ -0,0 +1,140 @@ +//+build i386, amd64 +package simd_x86 + +import "core:intrinsics" +import "core:simd" +_ :: simd + +@(require_results, enable_target_feature="ssse3") +_mm_abs_epi8 :: #force_inline proc "c" (a: __m128i) -> __m128i { + return transmute(__m128i)pabsb128(transmute(i8x16)a) +} +@(require_results, enable_target_feature="ssse3") +_mm_abs_epi16 :: #force_inline proc "c" (a: __m128i) -> __m128i { + return transmute(__m128i)pabsw128(transmute(i16x8)a) +} +@(require_results, enable_target_feature="ssse3") +_mm_abs_epi32 :: #force_inline proc "c" (a: __m128i) -> __m128i { + return transmute(__m128i)pabsd128(transmute(i32x4)a) +} +@(require_results, enable_target_feature="ssse3") +_mm_shuffle_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)pshufb128(transmute(u8x16)a, transmute(u8x16)b) +} +@(require_results, enable_target_feature="ssse3") +_mm_alignr_epi8 :: #force_inline proc "c" (a, b: __m128i, $IMM8: u32) -> __m128i { + shift :: IMM8 + + // If palignr is shifting the pair of vectors more than the size of two + // lanes, emit zero. + if shift > 32 { + return _mm_set1_epi8(0) + } + a, b := a, b + if shift > 16 { + a, b = _mm_set1_epi8(0), a + } + + return transmute(__m128i)simd.shuffle( + transmute(i8x16)b, + transmute(i8x16)a, + 0 when shift > 32 else shift - 16 + 0 when shift > 16 else shift + 0, + 1 when shift > 32 else shift - 16 + 1 when shift > 16 else shift + 1, + 2 when shift > 32 else shift - 16 + 2 when shift > 16 else shift + 2, + 3 when shift > 32 else shift - 16 + 3 when shift > 16 else shift + 3, + 4 when shift > 32 else shift - 16 + 4 when shift > 16 else shift + 4, + 5 when shift > 32 else shift - 16 + 5 when shift > 16 else shift + 5, + 6 when shift > 32 else shift - 16 + 6 when shift > 16 else shift + 6, + 7 when shift > 32 else shift - 16 + 7 when shift > 16 else shift + 7, + 8 when shift > 32 else shift - 16 + 8 when shift > 16 else shift + 8, + 9 when shift > 32 else shift - 16 + 9 when shift > 16 else shift + 9, + 10 when shift > 32 else shift - 16 + 10 when shift > 16 else shift + 10, + 11 when shift > 32 else shift - 16 + 11 when shift > 16 else shift + 11, + 12 when shift > 32 else shift - 16 + 12 when shift > 16 else shift + 12, + 13 when shift > 32 else shift - 16 + 13 when shift > 16 else shift + 13, + 14 when shift > 32 else shift - 16 + 14 when shift > 16 else shift + 14, + 15 when shift > 32 else shift - 16 + 15 when shift > 16 else shift + 15, + ) +} + + +@(require_results, enable_target_feature="ssse3") +_mm_hadd_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)phaddw128(transmute(i16x8)a, transmute(i16x8)b) +} +@(require_results, enable_target_feature="ssse3") +_mm_hadds_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)phaddsw128(transmute(i16x8)a, transmute(i16x8)b) +} +@(require_results, enable_target_feature="ssse3") +_mm_hadd_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)phaddd128(transmute(i32x4)a, transmute(i32x4)b) +} +@(require_results, enable_target_feature="ssse3") +_mm_hsub_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)phsubw128(transmute(i16x8)a, transmute(i16x8)b) +} +@(require_results, enable_target_feature="ssse3") +_mm_hsubs_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)phsubsw128(transmute(i16x8)a, transmute(i16x8)b) +} +@(require_results, enable_target_feature="ssse3") +_mm_hsub_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)phsubd128(transmute(i32x4)a, transmute(i32x4)b) +} +@(require_results, enable_target_feature="ssse3") +_mm_maddubs_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)pmaddubsw128(transmute(u8x16)a, transmute(i8x16)b) +} +@(require_results, enable_target_feature="ssse3") +_mm_mulhrs_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)pmulhrsw128(transmute(i16x8)a, transmute(i16x8)b) +} +@(require_results, enable_target_feature="ssse3") +_mm_sign_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)psignb128(transmute(i8x16)a, transmute(i8x16)b) +} +@(require_results, enable_target_feature="ssse3") +_mm_sign_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)psignw128(transmute(i16x8)a, transmute(i16x8)b) +} +@(require_results, enable_target_feature="ssse3") +_mm_sign_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { + return transmute(__m128i)psignd128(transmute(i32x4)a, transmute(i32x4)b) +} + + + +@(private, default_calling_convention="c") +foreign _ { + @(link_name = "llvm.x86.ssse3.pabs.b.128") + pabsb128 :: proc(a: i8x16) -> u8x16 --- + @(link_name = "llvm.x86.ssse3.pabs.w.128") + pabsw128 :: proc(a: i16x8) -> u16x8 --- + @(link_name = "llvm.x86.ssse3.pabs.d.128") + pabsd128 :: proc(a: i32x4) -> u32x4 --- + @(link_name = "llvm.x86.ssse3.pshuf.b.128") + pshufb128 :: proc(a, b: u8x16) -> u8x16 --- + @(link_name = "llvm.x86.ssse3.phadd.w.128") + phaddw128 :: proc(a, b: i16x8) -> i16x8 --- + @(link_name = "llvm.x86.ssse3.phadd.sw.128") + phaddsw128 :: proc(a, b: i16x8) -> i16x8 --- + @(link_name = "llvm.x86.ssse3.phadd.d.128") + phaddd128 :: proc(a, b: i32x4) -> i32x4 --- + @(link_name = "llvm.x86.ssse3.phsub.w.128") + phsubw128 :: proc(a, b: i16x8) -> i16x8 --- + @(link_name = "llvm.x86.ssse3.phsub.sw.128") + phsubsw128 :: proc(a, b: i16x8) -> i16x8 --- + @(link_name = "llvm.x86.ssse3.phsub.d.128") + phsubd128 :: proc(a, b: i32x4) -> i32x4 --- + @(link_name = "llvm.x86.ssse3.pmadd.ub.sw.128") + pmaddubsw128 :: proc(a: u8x16, b: i8x16) -> i16x8 --- + @(link_name = "llvm.x86.ssse3.pmul.hr.sw.128") + pmulhrsw128 :: proc(a, b: i16x8) -> i16x8 --- + @(link_name = "llvm.x86.ssse3.psign.b.128") + psignb128 :: proc(a, b: i8x16) -> i8x16 --- + @(link_name = "llvm.x86.ssse3.psign.w.128") + psignw128 :: proc(a, b: i16x8) -> i16x8 --- + @(link_name = "llvm.x86.ssse3.psign.d.128") + psignd128 :: proc(a, b: i32x4) -> i32x4 --- +} \ No newline at end of file diff --git a/core/simd/x86/types.odin b/core/simd/x86/types.odin new file mode 100644 index 000000000..06a2cd41e --- /dev/null +++ b/core/simd/x86/types.odin @@ -0,0 +1,57 @@ +//+build i386, amd64 +package simd_x86 + +import "core:simd" + +bf16 :: u16 + +__m128i :: #simd[2]i64 +__m128 :: #simd[4]f32 +__m128d :: #simd[2]f64 + +__m256i :: #simd[4]i64 +__m256 :: #simd[8]f32 +__m256d :: #simd[4]f64 + +__m512i :: #simd[8]i64 +__m512 :: #simd[16]f32 +__m512d :: #simd[8]f64 + +__m128bh :: #simd[8]bf16 +__m256bh :: #simd[16]bf16 +__m512bh :: #simd[32]bf16 + + +/// The `__mmask64` type used in AVX-512 intrinsics, a 64-bit integer +__mmask64 :: u64 + +/// The `__mmask32` type used in AVX-512 intrinsics, a 32-bit integer +__mmask32 :: u32 + +/// The `__mmask16` type used in AVX-512 intrinsics, a 16-bit integer +__mmask16 :: u16 + +/// The `__mmask8` type used in AVX-512 intrinsics, a 8-bit integer +__mmask8 :: u8 + +/// The `_MM_CMPINT_ENUM` type used to specify comparison operations in AVX-512 intrinsics. +_MM_CMPINT_ENUM :: i32 + +/// The `MM_MANTISSA_NORM_ENUM` type used to specify mantissa normalized operations in AVX-512 intrinsics. +_MM_MANTISSA_NORM_ENUM :: i32 + +/// The `MM_MANTISSA_SIGN_ENUM` type used to specify mantissa signed operations in AVX-512 intrinsics. +_MM_MANTISSA_SIGN_ENUM :: i32 + +_MM_PERM_ENUM :: i32 + +@(private) u8x16 :: simd.u8x16 +@(private) i8x16 :: simd.i8x16 +@(private) u16x8 :: simd.u16x8 +@(private) i16x8 :: simd.i16x8 +@(private) u32x4 :: simd.u32x4 +@(private) i32x4 :: simd.i32x4 +@(private) u64x2 :: simd.u64x2 +@(private) i64x2 :: simd.i64x2 +@(private) f32x4 :: simd.f32x4 +@(private) f64x2 :: simd.f64x2 diff --git a/core/slice/heap/heap.odin b/core/slice/heap/heap.odin new file mode 100644 index 000000000..0a3f53efb --- /dev/null +++ b/core/slice/heap/heap.odin @@ -0,0 +1,231 @@ +/* + Copyright 2022 Dale Weiler . + Made available under Odin's BSD-3 license. + + List of contributors: + Dale Weiler: Initial implementation +*/ + +// Package implements a generic max heap in-place on a slice for any type. +package heap + +/* + Constructs a max heap in slice given by data with comparator. A max heap is + a range of elements which has the following properties: + + 1. With N = len(data), for all 0 < i < N, data[(i - 1) / 2] does not compare + less than data[i]. + + 2. A new element can be added using push in O(log n) time. + + 3. The first element can be removed using pop in O(log n) time. + + The comparator compares elements of type T and can be used to construct a + max heap (less than) or min heap (greater than) for T. +*/ +make :: proc(data: []$T, less: proc(a, b: T) -> bool) { + // amoritize length lookup + length := len(data) + if length <= 1 do return + + // start from data parent, no need to consider children + for start := (length - 2) / 2; start >= 0; start -= 1 { + sift_down(data, less, start) + } +} + +/* + Inserts the element at the position len(data)-1 into the max heap with + comparator. + + At most log(N) comparisons where N = len(data) will be performed. +*/ +push :: proc(data: []$T, less: proc(a, b: T) -> bool) { + sift_up(data, less) +} + +/* + Swaps the value in position data[0] and the value in data[len(data)-1] and + makes subrange [0, len(data)-1) into a heap. This has the effect of removing + the first element from the heap. + + At most 2 * log(N) comparisons where N = len(data) will be performed. +*/ +pop :: proc(data: []$T, less: proc(a, b: T) -> bool) { + length := len(data) + if length <= 1 do return + + last := length + + // create a hole at 0 + top := data[0] + hole := floyd_sift_down(data, less) + last -= 1 + + if hole == last { + data[hole] = top + } else { + data[hole] = data[last] + hole += 1 + data[last] = top + sift_up(data[:hole], less) + } +} + +/* + Converts the max heap into a sorted range in ascending order. The resulting + slice will no longer be a heap after this. + + At most 2 * N * log(N) comparisons where N = len(data) will be performed. +*/ +sort :: proc(data: []$T, less: proc(a, b: T) -> bool) { + for n := len(data); n >= 1; n -= 1 { + pop(data[:n], less) + } +} + +/* + Examines the slice and finds the largest range which is a max-heap. Elements + are compared with user-supplied comparison procedure. + + This returns the upper bound of the largest range in the slice which is a + max heap. That is, the last index for which data is a max heap. + + At most O(n) comparisons where N = len(data) will be performed. +*/ +is_heap_until :: proc(data: []$T, less: proc(a, b: T) -> bool) -> int { + length := len(data) + a := 0 + b := 1 + for b < length { + if less(data[a], data[b]) { + return b + } + b += 1 + if b == length || less(data[a], data[b]) { + return b + } + a += 1 + b = 2 * a + 1 + } + return length +} + +/* + Checks if a given slice is a max heap. + + At most O(n) comparisons where N = len(data) will be performed. +*/ +is_heap :: #force_inline proc(data: []$T, less: proc(a, b: T) -> bool) -> bool { + return is_heap_until(data, less) == len(data) +} + +@(private="file") +floyd_sift_down :: proc(data: []$T, less: proc(a, b: T) -> bool) -> int { + length := len(data) + assert(length >= 2) + + hole := 0 + child := 0 + index := 0 + for { + index += child + 1 + child = 2 * child + 1 + if child + 1 < length && less(data[index], data[index + 1]) { + child += 1 + index += 1 + } + + data[hole] = data[index] + hole = index + + if child > (length - 2) / 2 { + return hole + } + } + + unreachable() +} + +@(private="file") +sift_down :: proc(data: []$T, less: proc(a, b: T) -> bool, start: int) { + start := start + child := start + + // amoritize length lookup + length := len(data) + + // left child of start is at 2 * start + 1 + // right child of start is at 2 * start + 2 + if length < 2 || (length - 2) / 2 < child { + return + } + + child = 2 * child + 1 + + if child + 1 < length && less(data[child], data[child + 1]) { + // right child exists and is greater than left child + child += 1 + } + + // check if in heap order + if less(data[child], data[start]) { + // start is larger than its largest child + return + } + + top := data[start] + for { + // not in heap order, swap parent with its largest child + data[start] = data[child] + start = child + + if (length - 2) / 2 < child { + break + } + + // recompute child based off updated parent + child = 2 * child + 1 + + if child + 1 < length && less(data[child], data[child + 1]) { + // right child exists and is greater than left child + child += 1 + } + + // check if we are in heap order + if less(data[child], top) { + break + } + } + + data[start] = top +} + +@(private="file") +sift_up :: proc(data: []$T, less: proc(a, b: T) -> bool) { + // amoritize length lookup + length := len(data) + + if length <= 1 do return + + last := length + length = (length - 2) / 2 + index := length + last -= 1 + if less(data[index], data[last]) { + top := data[last] + for { + data[last] = data[index] + last = index + if length == 0 { + break + } + length = (length - 1) / 2 + index = length + if !less(data[index], top) { + break + } + } + data[last] = top + } +} \ No newline at end of file diff --git a/core/slice/map.odin b/core/slice/map.odin index 1c5512ceb..9de00b174 100644 --- a/core/slice/map.odin +++ b/core/slice/map.odin @@ -2,11 +2,9 @@ package slice import "core:intrinsics" import "core:runtime" -import "core:mem" _ :: intrinsics _ :: runtime -_ :: mem map_keys :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (keys: []K) { keys = make(type_of(keys), len(m), allocator) @@ -52,7 +50,7 @@ map_entries :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries map_entry_infos :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries: []Map_Entry_Info(K, V)) #no_bounds_check { m := m - rm := (^mem.Raw_Map)(&m) + rm := (^runtime.Raw_Map)(&m) info := runtime.type_info_base(type_info_of(M)).variant.(runtime.Type_Info_Map) gs := runtime.type_info_base(info.generated_struct).variant.(runtime.Type_Info_Struct) diff --git a/core/slice/slice.odin b/core/slice/slice.odin index 426829a22..440cf643f 100644 --- a/core/slice/slice.odin +++ b/core/slice/slice.odin @@ -10,6 +10,53 @@ _ :: builtin _ :: bits _ :: mem +/* + Turn a pointer and a length into a slice. +*/ +from_ptr :: proc "contextless" (ptr: ^$T, count: int) -> []T { + return ([^]T)(ptr)[:count] +} + +/* + Turn a pointer and a length into a byte slice. +*/ +bytes_from_ptr :: proc "contextless" (ptr: rawptr, byte_count: int) -> []byte { + return ([^]byte)(ptr)[:byte_count] +} + +/* + Turn a slice into a byte slice. + + See `slice.reinterpret` to go the other way. +*/ +to_bytes :: proc "contextless" (s: []$T) -> []byte { + return ([^]byte)(raw_data(s))[:len(s) * size_of(T)] +} + +/* + Turn a slice of one type, into a slice of another type. + + Only converts the type and length of the slice itself. + The length is rounded down to the nearest whole number of items. + + ``` + large_items := []i64{1, 2, 3, 4} + small_items := slice.reinterpret([]i32, large_items) + assert(len(small_items) == 8) + ``` + ``` + small_items := []byte{1, 0, 0, 0, 0, 0, 0, 0, + 2, 0, 0, 0} + large_items := slice.reinterpret([]i64, small_items) + assert(len(large_items) == 1) // only enough bytes to make 1 x i64; two would need at least 8 bytes. + ``` +*/ +reinterpret :: proc "contextless" ($T: typeid/[]$U, s: []$V) -> []U { + bytes := to_bytes(s) + n := len(bytes) / size_of(U) + return ([^]U)(raw_data(bytes))[:n] +} + swap :: proc(array: $T/[]$E, a, b: int) { when size_of(E) > 8 { @@ -123,6 +170,21 @@ simple_equal :: proc(a, b: $T/[]$E) -> bool where intrinsics.type_is_simple_comp return mem.compare_ptrs(raw_data(a), raw_data(b), len(a)*size_of(E)) == 0 } +/* + return the prefix length common between slices `a` and `b`. + + slice.prefix_length([]u8{1, 2, 3, 4}, []u8{1}) -> 1 + slice.prefix_length([]u8{1, 2, 3, 4}, []u8{1, 2, 3}) -> 3 + slice.prefix_length([]u8{1, 2, 3, 4}, []u8{2, 3, 4}) -> 0 +*/ +prefix_length :: proc(a, b: $T/[]$E) -> (n: int) where intrinsics.type_is_comparable(E) { + _len := builtin.min(len(a), len(b)) + + #no_bounds_check for n < _len && a[n] == b[n] { + n += 1 + } + return +} has_prefix :: proc(array: $T/[]$E, needle: E) -> bool where intrinsics.type_is_comparable(E) { n := len(needle) @@ -304,22 +366,22 @@ filter :: proc(s: $S/[]$U, f: proc(U) -> bool, allocator := context.allocator) - return r[:] } -scanner :: proc (s: $S/[]$U, initializer: $V, f: proc(V, U)->V, allocator := context.allocator) -> []V { - if len(s) == 0 { return {} } +scanner :: proc (s: $S/[]$U, initializer: $V, f: proc(V, U) -> V, allocator := context.allocator) -> []V { + if len(s) == 0 { return {} } - res := make([]V, len(s), allocator) - p := as_ptr(s) - q := as_ptr(res) - r := initializer + res := make([]V, len(s), allocator) + p := as_ptr(s) + q := as_ptr(res) + r := initializer - for l := len(s); l > 0; l -= 1 { - r = f(r, p[0]) - q[0] = r - p = p[1:] - q = q[1:] - } + for l := len(s); l > 0; l -= 1 { + r = f(r, p[0]) + q[0] = r + p = p[1:] + q = q[1:] + } - return res + return res } @@ -344,15 +406,106 @@ max :: proc(s: $S/[]$T) -> (res: T, ok: bool) where intrinsics.type_is_ordered(T return } +min_max :: proc(s: $S/[]$T) -> (min, max: T, ok: bool) where intrinsics.type_is_ordered(T) { + if len(s) != 0 { + min, max = s[0], s[0] + ok = true + for v in s[1:] { + min = builtin.min(min, v) + max = builtin.max(max, v) + } + } + return +} -dot_product :: proc(a, b: $S/[]$T) -> T +any_of :: proc(s: $S/[]$T, value: T) -> bool where intrinsics.type_is_comparable(T) { + for v in s { + if v == value { + return true + } + } + return false +} + +none_of :: proc(s: $S/[]$T, value: T) -> bool where intrinsics.type_is_comparable(T) { + for v in s { + if v == value { + return false + } + } + return true +} + +all_of :: proc(s: $S/[]$T, value: T) -> bool where intrinsics.type_is_comparable(T) { + if len(s) == 0 { + return false + } + for v in s { + if v != value { + return false + } + } + return true +} + + +any_of_proc :: proc(s: $S/[]$T, f: proc(T) -> bool) -> bool { + for v in s { + if f(v) { + return true + } + } + return false +} + +none_of_proc :: proc(s: $S/[]$T, f: proc(T) -> bool) -> bool { + for v in s { + if f(v) { + return false + } + } + return true +} + +all_of_proc :: proc(s: $S/[]$T, f: proc(T) -> bool) -> bool { + if len(s) == 0 { + return false + } + for v in s { + if !f(v) { + return false + } + } + return true +} + + +count :: proc(s: $S/[]$T, value: T) -> (n: int) where intrinsics.type_is_comparable(T) { + for v in s { + if v == value { + n += 1 + } + } + return +} + +count_proc :: proc(s: $S/[]$T, f: proc(T) -> bool) -> (n: int) { + for v in s { + if f(v) { + n += 1 + } + } + return +} + + +dot_product :: proc(a, b: $S/[]$T) -> (r: T, ok: bool) where intrinsics.type_is_numeric(T) { if len(a) != len(b) { - panic("slice.dot_product: slices of unequal length") + return } - r: T #no_bounds_check for _, i in a { r += a[i] * b[i] } - return r + return r, true } diff --git a/core/slice/sort.odin b/core/slice/sort.odin index d9755ad0e..b6e455056 100644 --- a/core/slice/sort.odin +++ b/core/slice/sort.odin @@ -1,10 +1,5 @@ package slice -import "core:intrinsics" -_ :: intrinsics - -ORD :: intrinsics.type_is_ordered - Ordering :: enum { Less = -1, Equal = 0, @@ -38,7 +33,7 @@ cmp_proc :: proc($E: typeid) -> (proc(E, E) -> Ordering) where ORD(E) { sort :: proc(data: $T/[]$E) where ORD(E) { when size_of(E) != 0 { if n := len(data); n > 1 { - _quick_sort(data, 0, n, _max_depth(n)) + _quick_sort_general(data, 0, n, _max_depth(n), struct{}{}, .Ordered) } } } @@ -48,7 +43,7 @@ sort :: proc(data: $T/[]$E) where ORD(E) { sort_by :: proc(data: $T/[]$E, less: proc(i, j: E) -> bool) { when size_of(E) != 0 { if n := len(data); n > 1 { - _quick_sort_less(data, 0, n, _max_depth(n), less) + _quick_sort_general(data, 0, n, _max_depth(n), less, .Less) } } } @@ -56,7 +51,33 @@ sort_by :: proc(data: $T/[]$E, less: proc(i, j: E) -> bool) { sort_by_cmp :: proc(data: $T/[]$E, cmp: proc(i, j: E) -> Ordering) { when size_of(E) != 0 { if n := len(data); n > 1 { - _quick_sort_cmp(data, 0, n, _max_depth(n), cmp) + _quick_sort_general(data, 0, n, _max_depth(n), cmp, .Cmp) + } + } +} + +// stable_sort sorts a slice +stable_sort :: proc(data: $T/[]$E) where ORD(E) { + when size_of(E) != 0 { + if n := len(data); n > 1 { + _stable_sort_general(data, struct{}{}, .Ordered) + } + } +} + +// stable_sort_by sorts a slice with a given procedure to test whether two values are ordered "i < j" +stable_sort_by :: proc(data: $T/[]$E, less: proc(i, j: E) -> bool) { + when size_of(E) != 0 { + if n := len(data); n > 1 { + _stable_sort_general(data, less, .Less) + } + } +} + +stable_sort_by_cmp :: proc(data: $T/[]$E, cmp: proc(i, j: E) -> Ordering) { + when size_of(E) != 0 { + if n := len(data); n > 1 { + _stable_sort_general(data, cmp, .Cmp) } } } @@ -79,9 +100,10 @@ is_sorted_by :: proc(array: $T/[]$E, less: proc(i, j: E) -> bool) -> bool { return true } +is_sorted_by_cmp :: is_sorted_cmp is_sorted_cmp :: proc(array: $T/[]$E, cmp: proc(i, j: E) -> Ordering) -> bool { for i := len(array)-1; i > 0; i -= 1 { - if cmp(array[i], array[i-1]) == .Equal { + if cmp(array[i], array[i-1]) == .Less { return false } } @@ -140,489 +162,10 @@ is_sorted_by_key :: proc(array: $T/[]$E, key: proc(E) -> $K) -> bool where ORD(K return true } - - @(private) -_max_depth :: proc(n: int) -> int { // 2*ceil(log2(n+1)) - depth: int +_max_depth :: proc(n: int) -> (depth: int) { // 2*ceil(log2(n+1)) for i := n; i > 0; i >>= 1 { depth += 1 } return depth * 2 } - -@(private) -_quick_sort :: proc(data: $T/[]$E, a, b, max_depth: int) where ORD(E) #no_bounds_check { - median3 :: proc(data: T, m1, m0, m2: int) #no_bounds_check { - if data[m1] < data[m0] { - swap(data, m1, m0) - } - if data[m2] < data[m1] { - swap(data, m2, m1) - if data[m1] < data[m0] { - swap(data, m1, m0) - } - } - } - - do_pivot :: proc(data: T, lo, hi: int) -> (midlo, midhi: int) #no_bounds_check { - m := int(uint(lo+hi)>>1) - if hi-lo > 40 { - s := (hi-lo)/8 - median3(data, lo, lo+s, lo+s*2) - median3(data, m, m-s, m+s) - median3(data, hi-1, hi-1-s, hi-1-s*2) - } - median3(data, lo, m, hi-1) - - - pivot := lo - a, c := lo+1, hi-1 - - for ; a < c && data[a] < data[pivot]; a += 1 { - } - b := a - - for { - for ; b < c && !(data[pivot] < data[b]); b += 1 { // data[b] <= pivot - } - for ; b < c && data[pivot] < data[c-1]; c -=1 { // data[c-1] > pivot - } - if b >= c { - break - } - - swap(data, b, c-1) - b += 1 - c -= 1 - } - - protect := hi-c < 5 - if !protect && hi-c < (hi-lo)/4 { - dups := 0 - if !(data[pivot] < data[hi-1]) { - swap(data, c, hi-1) - c += 1 - dups += 1 - } - if !(data[b-1] < data[pivot]) { - b -= 1 - dups += 1 - } - - if !(data[m] < data[pivot]) { - swap(data, m, b-1) - b -= 1 - dups += 1 - } - protect = dups > 1 - } - if protect { - for { - for ; a < b && !(data[b-1] < data[pivot]); b -= 1 { - } - for ; a < b && data[a] < data[pivot]; a += 1 { - } - if a >= b { - break - } - swap(data, a, b-1) - a += 1 - b -= 1 - } - } - swap(data, pivot, b-1) - return b-1, c - } - - - a, b, max_depth := a, b, max_depth - - if b-a > 12 { // only use shell sort for lengths <= 12 - if max_depth == 0 { - _heap_sort(data, a, b) - return - } - max_depth -= 1 - mlo, mhi := do_pivot(data, a, b) - if mlo-a < b-mhi { - _quick_sort(data, a, mlo, max_depth) - a = mhi - } else { - _quick_sort(data, mhi, b, max_depth) - b = mlo - } - } - if b-a > 1 { - // Shell short with gap 6 - for i in a+6.. a && data[j] < data[j-1]; j -= 1 { - swap(data, j, j-1) - } - } -} - -@(private) -_heap_sort :: proc(data: $T/[]$E, a, b: int) where ORD(E) #no_bounds_check { - sift_down :: proc(data: T, lo, hi, first: int) #no_bounds_check { - root := lo - for { - child := 2*root + 1 - if child >= hi { - break - } - if child+1 < hi && data[first+child] < data[first+child+1] { - child += 1 - } - if !(data[first+root] < data[first+child]) { - return - } - swap(data, first+root, first+child) - root = child - } - } - - - first, lo, hi := a, 0, b-a - - for i := (hi-1)/2; i >= 0; i -= 1 { - sift_down(data, i, hi, first) - } - - for i := hi-1; i >= 0; i -= 1 { - swap(data, first, first+i) - sift_down(data, lo, i, first) - } -} - - - - - - -@(private) -_quick_sort_less :: proc(data: $T/[]$E, a, b, max_depth: int, less: proc(i, j: E) -> bool) #no_bounds_check { - median3 :: proc(data: T, m1, m0, m2: int, less: proc(i, j: E) -> bool) #no_bounds_check { - if less(data[m1], data[m0]) { - swap(data, m1, m0) - } - if less(data[m2], data[m1]) { - swap(data, m2, m1) - if less(data[m1], data[m0]) { - swap(data, m1, m0) - } - } - } - - do_pivot :: proc(data: T, lo, hi: int, less: proc(i, j: E) -> bool) -> (midlo, midhi: int) #no_bounds_check { - m := int(uint(lo+hi)>>1) - if hi-lo > 40 { - s := (hi-lo)/8 - median3(data, lo, lo+s, lo+s*2, less) - median3(data, m, m-s, m+s, less) - median3(data, hi-1, hi-1-s, hi-1-s*2, less) - } - median3(data, lo, m, hi-1, less) - - pivot := lo - a, c := lo+1, hi-1 - - for ; a < c && less(data[a], data[pivot]); a += 1 { - } - b := a - - for { - for ; b < c && !less(data[pivot], data[b]); b += 1 { // data[b] <= pivot - } - for ; b < c && less(data[pivot], data[c-1]); c -=1 { // data[c-1] > pivot - } - if b >= c { - break - } - - swap(data, b, c-1) - b += 1 - c -= 1 - } - - protect := hi-c < 5 - if !protect && hi-c < (hi-lo)/4 { - dups := 0 - if !less(data[pivot], data[hi-1]) { - swap(data, c, hi-1) - c += 1 - dups += 1 - } - if !less(data[b-1], data[pivot]) { - b -= 1 - dups += 1 - } - - if !less(data[m], data[pivot]) { - swap(data, m, b-1) - b -= 1 - dups += 1 - } - protect = dups > 1 - } - if protect { - for { - for ; a < b && !less(data[b-1], data[pivot]); b -= 1 { - } - for ; a < b && less(data[a], data[pivot]); a += 1 { - } - if a >= b { - break - } - swap(data, a, b-1) - a += 1 - b -= 1 - } - } - swap(data, pivot, b-1) - return b-1, c - } - - - a, b, max_depth := a, b, max_depth - - if b-a > 12 { // only use shell sort for lengths <= 12 - if max_depth == 0 { - _heap_sort_less(data, a, b, less) - return - } - max_depth -= 1 - mlo, mhi := do_pivot(data, a, b, less) - if mlo-a < b-mhi { - _quick_sort_less(data, a, mlo, max_depth, less) - a = mhi - } else { - _quick_sort_less(data, mhi, b, max_depth, less) - b = mlo - } - } - if b-a > 1 { - // Shell short with gap 6 - for i in a+6.. bool) #no_bounds_check { - for i in a+1.. a && less(data[j], data[j-1]); j -= 1 { - swap(data, j, j-1) - } - } -} - -@(private) -_heap_sort_less :: proc(data: $T/[]$E, a, b: int, less: proc(i, j: E) -> bool) #no_bounds_check { - sift_down :: proc(data: T, lo, hi, first: int, less: proc(i, j: E) -> bool) #no_bounds_check { - root := lo - for { - child := 2*root + 1 - if child >= hi { - break - } - if child+1 < hi && less(data[first+child], data[first+child+1]) { - child += 1 - } - if !less(data[first+root], data[first+child]) { - return - } - swap(data, first+root, first+child) - root = child - } - } - - - first, lo, hi := a, 0, b-a - - for i := (hi-1)/2; i >= 0; i -= 1 { - sift_down(data, i, hi, first, less) - } - - for i := hi-1; i >= 0; i -= 1 { - swap(data, first, first+i) - sift_down(data, lo, i, first, less) - } -} - - - - - - -@(private) -_quick_sort_cmp :: proc(data: $T/[]$E, a, b, max_depth: int, cmp: proc(i, j: E) -> Ordering) #no_bounds_check { - median3 :: proc(data: T, m1, m0, m2: int, cmp: proc(i, j: E) -> Ordering) #no_bounds_check { - if cmp(data[m1], data[m0]) == .Less { - swap(data, m1, m0) - } - if cmp(data[m2], data[m1]) == .Less { - swap(data, m2, m1) - if cmp(data[m1], data[m0]) == .Less { - swap(data, m1, m0) - } - } - } - - do_pivot :: proc(data: T, lo, hi: int, cmp: proc(i, j: E) -> Ordering) -> (midlo, midhi: int) #no_bounds_check { - m := int(uint(lo+hi)>>1) - if hi-lo > 40 { - s := (hi-lo)/8 - median3(data, lo, lo+s, lo+s*2, cmp) - median3(data, m, m-s, m+s, cmp) - median3(data, hi-1, hi-1-s, hi-1-s*2, cmp) - } - median3(data, lo, m, hi-1, cmp) - - pivot := lo - a, c := lo+1, hi-1 - - for ; a < c && cmp(data[a], data[pivot]) == .Less; a += 1 { - } - b := a - - for { - for ; b < c && cmp(data[pivot], data[b]) >= .Equal; b += 1 { // data[b] <= pivot - } - for ; b < c && cmp(data[pivot], data[c-1]) == .Less; c -=1 { // data[c-1] > pivot - } - if b >= c { - break - } - - swap(data, b, c-1) - b += 1 - c -= 1 - } - - protect := hi-c < 5 - if !protect && hi-c < (hi-lo)/4 { - dups := 0 - if cmp(data[pivot], data[hi-1]) != .Less { - swap(data, c, hi-1) - c += 1 - dups += 1 - } - if cmp(data[b-1], data[pivot]) != .Less { - b -= 1 - dups += 1 - } - - if cmp(data[m], data[pivot]) != .Less { - swap(data, m, b-1) - b -= 1 - dups += 1 - } - protect = dups > 1 - } - if protect { - for { - for ; a < b && cmp(data[b-1], data[pivot]) >= .Equal; b -= 1 { - } - for ; a < b && cmp(data[a], data[pivot]) == .Less; a += 1 { - } - if a >= b { - break - } - swap(data, a, b-1) - a += 1 - b -= 1 - } - } - swap(data, pivot, b-1) - return b-1, c - } - - - a, b, max_depth := a, b, max_depth - - if b-a > 12 { // only use shell sort for lengths <= 12 - if max_depth == 0 { - _heap_sort_cmp(data, a, b, cmp) - return - } - max_depth -= 1 - mlo, mhi := do_pivot(data, a, b, cmp) - if mlo-a < b-mhi { - _quick_sort_cmp(data, a, mlo, max_depth, cmp) - a = mhi - } else { - _quick_sort_cmp(data, mhi, b, max_depth, cmp) - b = mlo - } - } - if b-a > 1 { - // Shell short with gap 6 - for i in a+6.. Ordering) #no_bounds_check { - for i in a+1.. a && cmp(data[j], data[j-1]) == .Less; j -= 1 { - swap(data, j, j-1) - } - } -} - -@(private) -_heap_sort_cmp :: proc(data: $T/[]$E, a, b: int, cmp: proc(i, j: E) -> Ordering) #no_bounds_check { - sift_down :: proc(data: T, lo, hi, first: int, cmp: proc(i, j: E) -> Ordering) #no_bounds_check { - root := lo - for { - child := 2*root + 1 - if child >= hi { - break - } - if child+1 < hi && cmp(data[first+child], data[first+child+1]) == .Less { - child += 1 - } - if cmp(data[first+root], data[first+child]) >= .Equal { - return - } - swap(data, first+root, first+child) - root = child - } - } - - - first, lo, hi := a, 0, b-a - - for i := (hi-1)/2; i >= 0; i -= 1 { - sift_down(data, i, hi, first, cmp) - } - - for i := hi-1; i >= 0; i -= 1 { - swap(data, first, first+i) - sift_down(data, lo, i, first, cmp) - } -} - - - diff --git a/core/slice/sort_private.odin b/core/slice/sort_private.odin new file mode 100644 index 000000000..d93d74bf9 --- /dev/null +++ b/core/slice/sort_private.odin @@ -0,0 +1,200 @@ +//+private +package slice + +import "core:intrinsics" +_ :: intrinsics + +ORD :: intrinsics.type_is_ordered + +Sort_Kind :: enum { + Ordered, + Less, + Cmp, +} + +_quick_sort_general :: proc(data: $T/[]$E, a, b, max_depth: int, call: $P, $KIND: Sort_Kind) where (ORD(E) && KIND == .Ordered) || (KIND != .Ordered) #no_bounds_check { + less :: #force_inline proc(a, b: E, call: P) -> bool { + when KIND == .Ordered { + return a < b + } else when KIND == .Less { + return call(a, b) + } else when KIND == .Cmp { + return call(a, b) == .Less + } else { + #panic("unhandled Sort_Kind") + } + } + + insertion_sort :: proc(data: $T/[]$E, a, b: int, call: P) #no_bounds_check { + for i in a+1.. a && less(data[j], data[j-1], call); j -= 1 { + swap(data, j, j-1) + } + } + } + + heap_sort :: proc(data: $T/[]$E, a, b: int, call: P) #no_bounds_check { + sift_down :: proc(data: T, lo, hi, first: int, call: P) #no_bounds_check { + root := lo + for { + child := 2*root + 1 + if child >= hi { + break + } + if child+1 < hi && less(data[first+child], data[first+child+1], call) { + child += 1 + } + if !less(data[first+root], data[first+child], call) { + return + } + swap(data, first+root, first+child) + root = child + } + } + + + first, lo, hi := a, 0, b-a + + for i := (hi-1)/2; i >= 0; i -= 1 { + sift_down(data, i, hi, first, call) + } + + for i := hi-1; i >= 0; i -= 1 { + swap(data, first, first+i) + sift_down(data, lo, i, first, call) + } + } + + median3 :: proc(data: T, m1, m0, m2: int, call: P) #no_bounds_check { + if less(data[m1], data[m0], call) { + swap(data, m1, m0) + } + if less(data[m2], data[m1], call) { + swap(data, m2, m1) + if less(data[m1], data[m0], call) { + swap(data, m1, m0) + } + } + } + + do_pivot :: proc(data: T, lo, hi: int, call: P) -> (midlo, midhi: int) #no_bounds_check { + m := int(uint(lo+hi)>>1) + if hi-lo > 40 { + s := (hi-lo)/8 + median3(data, lo, lo+s, lo+s*2, call) + median3(data, m, m-s, m+s, call) + median3(data, hi-1, hi-1-s, hi-1-s*2, call) + } + median3(data, lo, m, hi-1, call) + + pivot := lo + a, c := lo+1, hi-1 + + + for ; a < c && less(data[a], data[pivot], call); a += 1 { + } + b := a + + for { + for ; b < c && !less(data[pivot], data[b], call); b += 1 { // data[b] <= pivot + } + for ; b < c && less(data[pivot], data[c-1], call); c -=1 { // data[c-1] > pivot + } + if b >= c { + break + } + + swap(data, b, c-1) + b += 1 + c -= 1 + } + + protect := hi-c < 5 + if !protect && hi-c < (hi-lo)/4 { + dups := 0 + if !less(data[pivot], data[hi-1], call) { + swap(data, c, hi-1) + c += 1 + dups += 1 + } + if !less(data[b-1], data[pivot], call) { + b -= 1 + dups += 1 + } + + if !less(data[m], data[pivot], call) { + swap(data, m, b-1) + b -= 1 + dups += 1 + } + protect = dups > 1 + } + if protect { + for { + for ; a < b && !less(data[b-1], data[pivot], call); b -= 1 { + } + for ; a < b && less(data[a], data[pivot], call); a += 1 { + } + if a >= b { + break + } + swap(data, a, b-1) + a += 1 + b -= 1 + } + } + swap(data, pivot, b-1) + return b-1, c + } + + + a, b, max_depth := a, b, max_depth + + for b-a > 12 { // only use shell sort for lengths <= 12 + if max_depth == 0 { + heap_sort(data, a, b, call) + return + } + max_depth -= 1 + mlo, mhi := do_pivot(data, a, b, call) + if mlo-a < b-mhi { + _quick_sort_general(data, a, mlo, max_depth, call, KIND) + a = mhi + } else { + _quick_sort_general(data, mhi, b, max_depth, call, KIND) + b = mlo + } + } + if b-a > 1 { + // Shell short with gap 6 + for i in a+6.. bool { + when KIND == .Ordered { + return a < b + } else when KIND == .Less { + return call(a, b) + } else when KIND == .Cmp { + return call(a, b) == .Less + } else { + #panic("unhandled Sort_Kind") + } + } + + n := len(data) + for i in 1.. 0 && less(data[j], data[j-1], call); j -= 1 { + swap(data, j, j-1) + } + } +} diff --git a/core/strconv/strconv.odin b/core/strconv/strconv.odin index 6ea8b39e6..65161a820 100644 --- a/core/strconv/strconv.odin +++ b/core/strconv/strconv.odin @@ -895,6 +895,7 @@ unquote_string :: proc(lit: string, allocator := context.allocator) -> (res: str if s == `""` { return "", false, true } + s = s[1:len(s)-1] if contains_rune(s, '\n') >= 0 { return s, false, false diff --git a/core/strings/ascii_set.odin b/core/strings/ascii_set.odin index 582049eee..9b59666f3 100644 --- a/core/strings/ascii_set.odin +++ b/core/strings/ascii_set.odin @@ -5,6 +5,7 @@ import "core:unicode/utf8" Ascii_Set :: distinct [8]u32 +// create an ascii set of all unique characters in the string ascii_set_make :: proc(chars: string) -> (as: Ascii_Set, ok: bool) #no_bounds_check { for i in 0.. (as: Ascii_Set, ok: bool) #no_bounds_ch return } +// returns true when the `c` byte is contained in the `as` ascii set ascii_set_contains :: proc(as: Ascii_Set, c: byte) -> bool #no_bounds_check { return as[c>>5] & (1<<(c&31)) != 0 } \ No newline at end of file diff --git a/core/strings/builder.odin b/core/strings/builder.odin index 6952ba088..6de42804d 100644 --- a/core/strings/builder.odin +++ b/core/strings/builder.odin @@ -1,50 +1,69 @@ package strings -import "core:mem" +import "core:runtime" import "core:unicode/utf8" import "core:strconv" import "core:io" Builder_Flush_Proc :: #type proc(b: ^Builder) -> (do_reset: bool) +/* + dynamic byte buffer / string builder with helper procedures + the dynamic array is wrapped inside the struct to be more opaque + you can use `fmt.sbprint*` procedures with a `^strings.Builder` directly +*/ Builder :: struct { buf: [dynamic]byte, } -make_builder_none :: proc(allocator := context.allocator) -> Builder { +// return a builder, default length 0 / cap 16 are done through make +builder_make_none :: proc(allocator := context.allocator) -> Builder { return Builder{buf=make([dynamic]byte, allocator)} } -make_builder_len :: proc(len: int, allocator := context.allocator) -> Builder { +// return a builder, with a set length `len` and cap 16 byte buffer +builder_make_len :: proc(len: int, allocator := context.allocator) -> Builder { return Builder{buf=make([dynamic]byte, len, allocator)} } -make_builder_len_cap :: proc(len, cap: int, allocator := context.allocator) -> Builder { +// return a builder, with a set length `len` byte buffer and a custom `cap` +builder_make_len_cap :: proc(len, cap: int, allocator := context.allocator) -> Builder { return Builder{buf=make([dynamic]byte, len, cap, allocator)} } -make_builder :: proc{ - make_builder_none, - make_builder_len, - make_builder_len_cap, +// overload simple `builder_make_*` with or without len / cap parameters +builder_make :: proc{ + builder_make_none, + builder_make_len, + builder_make_len_cap, } -init_builder_none :: proc(b: ^Builder, allocator := context.allocator) { +// initialize a builder, default length 0 / cap 16 are done through make +// replaces the existing `buf` +builder_init_none :: proc(b: ^Builder, allocator := context.allocator) -> ^Builder { b.buf = make([dynamic]byte, allocator) + return b } -init_builder_len :: proc(b: ^Builder, len: int, allocator := context.allocator) { +// initialize a builder, with a set length `len` and cap 16 byte buffer +// replaces the existing `buf` +builder_init_len :: proc(b: ^Builder, len: int, allocator := context.allocator) -> ^Builder { b.buf = make([dynamic]byte, len, allocator) + return b } -init_builder_len_cap :: proc(b: ^Builder, len, cap: int, allocator := context.allocator) { +// initialize a builder, with a set length `len` byte buffer and a custom `cap` +// replaces the existing `buf` +builder_init_len_cap :: proc(b: ^Builder, len, cap: int, allocator := context.allocator) -> ^Builder { b.buf = make([dynamic]byte, len, cap, allocator) + return b } -init_builder :: proc{ - init_builder_none, - init_builder_len, - init_builder_len_cap, +// overload simple `builder_init_*` with or without len / ap parameters +builder_init :: proc{ + builder_init_none, + builder_init_len, + builder_init_len_cap, } @(private) @@ -76,56 +95,85 @@ _builder_stream_vtable := &io.Stream_VTable{ }, } +// return an `io.Stream` from a builder to_stream :: proc(b: ^Builder) -> io.Stream { return io.Stream{stream_vtable=_builder_stream_vtable, stream_data=b} } + +// return an `io.Writer` from a builder to_writer :: proc(b: ^Builder) -> io.Writer { return io.to_writer(to_stream(b)) } - - - -destroy_builder :: proc(b: ^Builder) { +// delete and clear the builder byte buffer content +builder_destroy :: proc(b: ^Builder) { delete(b.buf) clear(&b.buf) } -grow_builder :: proc(b: ^Builder, cap: int) { +// reserve the builfer byte buffer to a specific cap, when it's higher than before +builder_grow :: proc(b: ^Builder, cap: int) { reserve(&b.buf, cap) } -reset_builder :: proc(b: ^Builder) { +// clear the builder byte buffer content +builder_reset :: proc(b: ^Builder) { clear(&b.buf) } - -builder_from_slice :: proc(backing: []byte) -> Builder { - s := transmute(mem.Raw_Slice)backing - d := mem.Raw_Dynamic_Array{ +/* + create an empty builder with the same slice length as its cap + uses the `mem.nil_allocator` to avoid allocation and keep a fixed length + used in `fmt.bprint*` + + bytes: [8]byte // <-- gets filled + builder := strings.builder_from_bytes(bytes[:]) + strings.write_byte(&builder, 'a') -> "a" + strings.write_byte(&builder, 'b') -> "ab" +*/ +builder_from_bytes :: proc(backing: []byte) -> Builder { + s := transmute(runtime.Raw_Slice)backing + d := runtime.Raw_Dynamic_Array{ data = s.data, len = 0, cap = s.len, - allocator = mem.nil_allocator(), + allocator = runtime.nil_allocator(), } return Builder{ buf = transmute([dynamic]byte)d, } } +builder_from_slice :: builder_from_bytes + +// cast the builder byte buffer to a string and return it to_string :: proc(b: Builder) -> string { return string(b.buf[:]) } +// return the length of the builder byte buffer builder_len :: proc(b: Builder) -> int { return len(b.buf) } + +// return the cap of the builder byte buffer builder_cap :: proc(b: Builder) -> int { return cap(b.buf) } + +// returns the space left in the builder byte buffer to use up builder_space :: proc(b: Builder) -> int { - return max(cap(b.buf), len(b.buf), 0) + return cap(b.buf) - len(b.buf) } +/* + appends a byte to the builder, returns the append diff + + builder := strings.builder_make() + strings.write_byte(&builder, 'a') // 1 + strings.write_byte(&builder, 'b') // 1 + strings.write_byte(&builder, 'c') // 1 + fmt.println(strings.to_string(builder)) // -> abc +*/ write_byte :: proc(b: ^Builder, x: byte) -> (n: int) { n0 := len(b.buf) append(&b.buf, x) @@ -133,6 +181,14 @@ write_byte :: proc(b: ^Builder, x: byte) -> (n: int) { return n1-n0 } +/* + appends a slice of bytes to the builder, returns the append diff + + builder := strings.builder_make() + bytes := [?]byte { 'a', 'b', 'c' } + strings.write_bytes(&builder, bytes[:]) // 3 + fmt.println(strings.to_string(builder)) // -> abc +*/ write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) { n0 := len(b.buf) append(&b.buf, ..x) @@ -140,142 +196,129 @@ write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) { return n1-n0 } -write_rune_builder :: proc(b: ^Builder, r: rune) -> (int, io.Error) { +/* + appends a single rune into the builder, returns written rune size and an `io.Error` + + builder := strings.builder_make() + strings.write_rune(&builder, 'ä') // 2 None + strings.write_rune(&builder, 'b') // 1 None + strings.write_rune(&builder, 'c') // 1 None + fmt.println(strings.to_string(builder)) // -> äbc +*/ +write_rune :: proc(b: ^Builder, r: rune) -> (int, io.Error) { return io.write_rune(to_writer(b), r) } +/* + appends a quoted rune into the builder, returns written size -write_quoted_rune_builder :: proc(b: ^Builder, r: rune) -> (n: int) { - return write_quoted_rune(to_writer(b), r) -} - -@(private) -_write_byte :: proc(w: io.Writer, c: byte) -> int { - err := io.write_byte(w, c) - return 1 if err == nil else 0 + builder := strings.builder_make() + strings.write_string(&builder, "abc") // 3 + strings.write_quoted_rune(&builder, 'ä') // 4 + strings.write_string(&builder, "abc") // 3 + fmt.println(strings.to_string(builder)) // -> abc'ä'abc +*/ +write_quoted_rune :: proc(b: ^Builder, r: rune) -> (n: int) { + return io.write_quoted_rune(to_writer(b), r) } -write_quoted_rune :: proc(w: io.Writer, r: rune) -> (n: int) { - quote := byte('\'') - n += _write_byte(w, quote) - buf, width := utf8.encode_rune(r) - if width == 1 && r == utf8.RUNE_ERROR { - n += _write_byte(w, '\\') - n += _write_byte(w, 'x') - n += _write_byte(w, DIGITS_LOWER[buf[0]>>4]) - n += _write_byte(w, DIGITS_LOWER[buf[0]&0xf]) - } else { - i, _ := io.write_escaped_rune(w, r, quote) - n += i - } - n += _write_byte(w, quote) - return +/* + appends a string to the builder, return the written byte size + + builder := strings.builder_make() + strings.write_string(&builder, "a") // 1 + strings.write_string(&builder, "bc") // 2 + strings.write_string(&builder, "xyz") // 3 + fmt.println(strings.to_string(builder)) // -> abcxyz +*/ +write_string :: proc(b: ^Builder, s: string) -> (n: int) { + n0 := len(b.buf) + append(&b.buf, s) + n1 := len(b.buf) + return n1-n0 } -write_string :: proc{ - write_string_builder, - write_string_writer, -} - -write_string_builder :: proc(b: ^Builder, s: string) -> (n: int) { - return write_string_writer(to_writer(b), s) -} - -write_string_writer :: proc(w: io.Writer, s: string) -> (n: int) { - n, _ = io.write(w, transmute([]byte)s) - return -} - - - - +// pops and returns the last byte in the builder +// returns 0 when the builder is empty pop_byte :: proc(b: ^Builder) -> (r: byte) { if len(b.buf) == 0 { return 0 } + r = b.buf[len(b.buf)-1] - d := cast(^mem.Raw_Dynamic_Array)&b.buf + d := cast(^runtime.Raw_Dynamic_Array)&b.buf d.len = max(d.len-1, 0) return } +// pops the last rune in the builder and returns the popped rune and its rune width +// returns 0, 0 when the builder is empty pop_rune :: proc(b: ^Builder) -> (r: rune, width: int) { + if len(b.buf) == 0 { + return 0, 0 + } + r, width = utf8.decode_last_rune(b.buf[:]) - d := cast(^mem.Raw_Dynamic_Array)&b.buf + d := cast(^runtime.Raw_Dynamic_Array)&b.buf d.len = max(d.len-width, 0) return } - @(private) DIGITS_LOWER := "0123456789abcdefx" -write_quoted_string :: proc{ - write_quoted_string_builder, - write_quoted_string_writer, -} +/* + append a quoted string into the builder, return the written byte size -write_quoted_string_builder :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n: int) { + builder := strings.builder_make() + strings.write_quoted_string(&builder, "a") // 3 + strings.write_quoted_string(&builder, "bc", '\'') // 4 + strings.write_quoted_string(&builder, "xyz") // 5 + fmt.println(strings.to_string(builder)) // -> "a"'bc'xyz" +*/ +write_quoted_string :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n: int) { n, _ = io.write_quoted_string(to_writer(b), str, quote) return } -@(deprecated="prefer io.write_quoted_string") -write_quoted_string_writer :: proc(w: io.Writer, str: string, quote: byte = '"') -> (n: int) { - n, _ = io.write_quoted_string(w, str, quote) - return -} -write_encoded_rune :: proc{ - write_encoded_rune_builder, - write_encoded_rune_writer, -} - -write_encoded_rune_builder :: proc(b: ^Builder, r: rune, write_quote := true) -> (n: int) { +// appends a rune to the builder, optional `write_quote` boolean tag, returns the written rune size +write_encoded_rune :: proc(b: ^Builder, r: rune, write_quote := true) -> (n: int) { n, _ = io.write_encoded_rune(to_writer(b), r, write_quote) return } -@(deprecated="prefer io.write_encoded_rune") -write_encoded_rune_writer :: proc(w: io.Writer, r: rune, write_quote := true) -> (n: int) { - n, _ = io.write_encoded_rune(w, r, write_quote) - return -} - -write_escaped_rune :: proc{ - write_escaped_rune_builder, - write_escaped_rune_writer, -} - -write_escaped_rune_builder :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false) -> (n: int) { +// appends a rune to the builder, fully written out in case of escaped runes e.g. '\a' will be written as such +// when `r` and `quote` match and `quote` is `\\` - they will be written as two slashes +// `html_safe` flag in case the runes '<', '>', '&' should be encoded as digits e.g. `\u0026` +write_escaped_rune :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false) -> (n: int) { n, _ = io.write_escaped_rune(to_writer(b), r, quote, html_safe) return } -@(deprecated="prefer io.write_escaped_rune") -write_escaped_rune_writer :: proc(w: io.Writer, r: rune, quote: byte, html_safe := false) -> (n: int) { - n, _ = io.write_escaped_rune(w, r, quote, html_safe) - return -} - - +// writes a u64 value `i` in `base` = 10 into the builder, returns the written amount of characters write_u64 :: proc(b: ^Builder, i: u64, base: int = 10) -> (n: int) { buf: [32]byte s := strconv.append_bits(buf[:], i, base, false, 64, strconv.digits, nil) return write_string(b, s) } + +// writes a i64 value `i` in `base` = 10 into the builder, returns the written amount of characters write_i64 :: proc(b: ^Builder, i: i64, base: int = 10) -> (n: int) { buf: [32]byte s := strconv.append_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil) return write_string(b, s) } +// writes a uint value `i` in `base` = 10 into the builder, returns the written amount of characters write_uint :: proc(b: ^Builder, i: uint, base: int = 10) -> (n: int) { return write_u64(b, u64(i), base) } + +// writes a int value `i` in `base` = 10 into the builder, returns the written amount of characters write_int :: proc(b: ^Builder, i: int, base: int = 10) -> (n: int) { return write_i64(b, i64(i), base) } diff --git a/core/strings/conversion.odin b/core/strings/conversion.odin index b0d42b2eb..ab827490d 100644 --- a/core/strings/conversion.odin +++ b/core/strings/conversion.odin @@ -10,7 +10,7 @@ to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) -> } b: Builder - init_builder(&b, 0, 0, allocator) + builder_init(&b, 0, 0, allocator) s := s for c, i in s { @@ -20,7 +20,7 @@ to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) -> _, w := utf8.decode_rune_in_string(s[i:]) if w == 1 { - grow_builder(&b, len(s) + len(replacement)) + builder_grow(&b, len(s) + len(replacement)) write_string(&b, s[:i]) s = s[i:] break @@ -58,30 +58,45 @@ to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) -> return to_string(b) } +/* + returns the input string `s` with all runes set to lowered case + always allocates using the `allocator` + + strings.to_lower("test") -> test + strings.to_lower("Test") -> test +*/ to_lower :: proc(s: string, allocator := context.allocator) -> string { b: Builder - init_builder(&b, 0, len(s), allocator) + builder_init(&b, 0, len(s), allocator) for r in s { - write_rune_builder(&b, unicode.to_lower(r)) + write_rune(&b, unicode.to_lower(r)) } return to_string(b) } + +/* + returns the input string `s` with all runes set to upper case + always allocates using the `allocator` + + strings.to_lower("test") -> TEST + strings.to_lower("Test") -> TEST +*/ to_upper :: proc(s: string, allocator := context.allocator) -> string { b: Builder - init_builder(&b, 0, len(s), allocator) + builder_init(&b, 0, len(s), allocator) for r in s { - write_rune_builder(&b, unicode.to_upper(r)) + write_rune(&b, unicode.to_upper(r)) } return to_string(b) } - - - +// returns true when the `c` rune is a space, '-' or '_' +// useful when treating strings like words in a text editor or html paths is_delimiter :: proc(c: rune) -> bool { return c == '-' || c == '_' || is_space(c) } +// returns true when the `r` rune is a non alpha or `unicode.is_space` rune is_separator :: proc(r: rune) -> bool { if r <= 0x7f { switch r { @@ -101,7 +116,10 @@ is_separator :: proc(r: rune) -> bool { return unicode.is_space(r) } - +/* + iterator that loops through the string and calls the callback with the `prev`, `curr` and `next` rune + on empty string `s` the callback gets called once with empty runes +*/ string_case_iterator :: proc(w: io.Writer, s: string, callback: proc(w: io.Writer, prev, curr, next: rune)) { prev, curr: rune for next in s { @@ -122,13 +140,14 @@ string_case_iterator :: proc(w: io.Writer, s: string, callback: proc(w: io.Write } } - to_lower_camel_case :: to_camel_case + +// converts the `s` string to "lowerCamelCase" to_camel_case :: proc(s: string, allocator := context.allocator) -> string { s := s s = trim_space(s) b: Builder - init_builder(&b, 0, len(s), allocator) + builder_init(&b, 0, len(s), allocator) w := to_writer(&b) string_case_iterator(w, s, proc(w: io.Writer, prev, curr, next: rune) { @@ -147,11 +166,13 @@ to_camel_case :: proc(s: string, allocator := context.allocator) -> string { } to_upper_camel_case :: to_pascal_case + +// converts the `s` string to "PascalCase" to_pascal_case :: proc(s: string, allocator := context.allocator) -> string { s := s s = trim_space(s) b: Builder - init_builder(&b, 0, len(s), allocator) + builder_init(&b, 0, len(s), allocator) w := to_writer(&b) string_case_iterator(w, s, proc(w: io.Writer, prev, curr, next: rune) { @@ -169,11 +190,20 @@ to_pascal_case :: proc(s: string, allocator := context.allocator) -> string { return to_string(b) } +/* + returns the `s` string to words seperated by the given `delimiter` rune + all runes will be upper or lowercased based on the `all_uppercase` bool + + strings.to_delimiter_case("Hello World", '_', false) -> hello_world + strings.to_delimiter_case("Hello World", ' ', true) -> HELLO WORLD + strings.to_delimiter_case("Hello World", ' ', true) -> HELLO WORLD + strings.to_delimiter_case("aBC", '_', false) -> a_b_c +*/ to_delimiter_case :: proc(s: string, delimiter: rune, all_upper_case: bool, allocator := context.allocator) -> string { s := s s = trim_space(s) b: Builder - init_builder(&b, 0, len(s), allocator) + builder_init(&b, 0, len(s), allocator) w := to_writer(&b) adjust_case := unicode.to_upper if all_upper_case else unicode.to_lower @@ -208,31 +238,41 @@ to_delimiter_case :: proc(s: string, delimiter: rune, all_upper_case: bool, allo return to_string(b) } - +/* + converts the `s` string to "snake_case" with all runes lowercased + + strings.to_snake_case("HelloWorld") -> hello_world + strings.to_snake_case("Hello World") -> hello_world +*/ to_snake_case :: proc(s: string, allocator := context.allocator) -> string { return to_delimiter_case(s, '_', false, allocator) } to_screaming_snake_case :: to_upper_snake_case + +// converts the `s` string to "SNAKE_CASE" with all runes uppercased to_upper_snake_case :: proc(s: string, allocator := context.allocator) -> string { return to_delimiter_case(s, '_', true, allocator) } +// converts the `s` string to "kebab-case" with all runes lowercased to_kebab_case :: proc(s: string, allocator := context.allocator) -> string { return to_delimiter_case(s, '-', false, allocator) } -to_upper_case :: proc(s: string, allocator := context.allocator) -> string { +// converts the `s` string to "KEBAB-CASE" with all runes uppercased +to_upper_kebab_case :: proc(s: string, allocator := context.allocator) -> string { return to_delimiter_case(s, '-', true, allocator) } +// converts the `s` string to "Ada_case" to_ada_case :: proc(s: string, allocator := context.allocator) -> string { delimiter :: '_' s := s s = trim_space(s) b: Builder - init_builder(&b, 0, len(s), allocator) + builder_init(&b, 0, len(s), allocator) w := to_writer(&b) prev, curr: rune diff --git a/core/strings/intern.odin b/core/strings/intern.odin index ff26d7dbb..1e9577e61 100644 --- a/core/strings/intern.odin +++ b/core/strings/intern.odin @@ -1,22 +1,27 @@ package strings -import "core:mem" +import "core:runtime" +// custom string entry struct Intern_Entry :: struct { len: int, str: [1]byte, // string is allocated inline with the entry to keep allocations simple } +// "intern" is a more memory efficient string map +// `allocator` is used to allocate the actual `Intern_Entry` strings Intern :: struct { - allocator: mem.Allocator, + allocator: runtime.Allocator, entries: map[string]^Intern_Entry, } +// initialize the entries map and set the allocator for the string entries intern_init :: proc(m: ^Intern, allocator := context.allocator, map_allocator := context.allocator) { m.allocator = allocator m.entries = make(map[string]^Intern_Entry, 16, map_allocator) } +// free the map and all its content allocated using the `.allocator` intern_destroy :: proc(m: ^Intern) { for _, value in m.entries { free(value, m.allocator) @@ -24,15 +29,22 @@ intern_destroy :: proc(m: ^Intern) { delete(m.entries) } +// returns the `text` string from the intern map - gets set if it didnt exist yet +// the returned string lives as long as the map entry lives intern_get :: proc(m: ^Intern, text: string) -> string { entry := _intern_get_entry(m, text) #no_bounds_check return string(entry.str[:entry.len]) } + +// returns the `text` cstring from the intern map - gets set if it didnt exist yet +// the returned cstring lives as long as the map entry lives intern_get_cstring :: proc(m: ^Intern, text: string) -> cstring { entry := _intern_get_entry(m, text) return cstring(&entry.str[0]) } +// looks up wether the `text` string exists in the map, returns the entry +// sets & allocates the entry if it wasnt set yet _intern_get_entry :: proc(m: ^Intern, text: string) -> ^Intern_Entry #no_bounds_check { if prev, ok := m.entries[text]; ok { return prev @@ -42,7 +54,8 @@ _intern_get_entry :: proc(m: ^Intern, text: string) -> ^Intern_Entry #no_bounds_ } entry_size := int(offset_of(Intern_Entry, str)) + len(text) + 1 - new_entry := (^Intern_Entry)(mem.alloc(entry_size, align_of(Intern_Entry), m.allocator)) + ptr, _ := runtime.mem_alloc(entry_size, align_of(Intern_Entry), m.allocator) + new_entry := (^Intern_Entry)(ptr) new_entry.len = len(text) copy(new_entry.str[:new_entry.len], text) diff --git a/core/strings/reader.odin b/core/strings/reader.odin index ba266c0b5..9b2e10b68 100644 --- a/core/strings/reader.odin +++ b/core/strings/reader.odin @@ -3,46 +3,60 @@ package strings import "core:io" import "core:unicode/utf8" +/* + io stream data for a string reader that can read based on bytes or runes + implements the vtable when using the io.Reader variants + "read" calls advance the current reading offset `i` +*/ Reader :: struct { s: string, // read-only buffer i: i64, // current reading index prev_rune: int, // previous reading index of rune or < 0 } +// init the reader to the string `s` reader_init :: proc(r: ^Reader, s: string) { r.s = s r.i = 0 r.prev_rune = -1 } +// returns a stream from the reader data reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) { s.stream_data = r s.stream_vtable = _reader_vtable return } +// init a reader to the string `s` and return an io.Reader to_reader :: proc(r: ^Reader, s: string) -> io.Reader { reader_init(r, s) rr, _ := io.to_reader(reader_to_stream(r)) return rr } + +// init a reader to the string `s` and return an io.Reader_At to_reader_at :: proc(r: ^Reader, s: string) -> io.Reader_At { reader_init(r, s) rr, _ := io.to_reader_at(reader_to_stream(r)) return rr } + +// init a reader to the string `s` and return an io.Byte_Reader to_byte_reader :: proc(r: ^Reader, s: string) -> io.Byte_Reader { reader_init(r, s) rr, _ := io.to_byte_reader(reader_to_stream(r)) return rr } + +// init a reader to the string `s` and return an io.Rune_Reader to_rune_reader :: proc(r: ^Reader, s: string) -> io.Rune_Reader { reader_init(r, s) rr, _ := io.to_rune_reader(reader_to_stream(r)) return rr } - +// remaining length of the reader reader_length :: proc(r: ^Reader) -> int { if r.i >= i64(len(r.s)) { return 0 @@ -50,10 +64,13 @@ reader_length :: proc(r: ^Reader) -> int { return int(i64(len(r.s)) - r.i) } +// returns the string length stored by the reader reader_size :: proc(r: ^Reader) -> i64 { return i64(len(r.s)) } +// reads len(p) bytes into the slice from the string in the reader +// returns `n` amount of read bytes and an io.Error reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) { if r.i >= i64(len(r.s)) { return 0, .EOF @@ -63,6 +80,9 @@ reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) { r.i += i64(n) return } + +// reads len(p) bytes into the slice from the string in the reader at an offset +// returns `n` amount of read bytes and an io.Error reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Error) { if off < 0 { return 0, .Invalid_Offset @@ -76,6 +96,8 @@ reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Erro } return } + +// reads and returns a single byte - error when out of bounds reader_read_byte :: proc(r: ^Reader) -> (byte, io.Error) { r.prev_rune = -1 if r.i >= i64(len(r.s)) { @@ -85,6 +107,8 @@ reader_read_byte :: proc(r: ^Reader) -> (byte, io.Error) { r.i += 1 return b, nil } + +// decreases the reader offset - error when below 0 reader_unread_byte :: proc(r: ^Reader) -> io.Error { if r.i <= 0 { return .Invalid_Unread @@ -93,6 +117,8 @@ reader_unread_byte :: proc(r: ^Reader) -> io.Error { r.i -= 1 return nil } + +// reads and returns a single rune and the rune size - error when out bounds reader_read_rune :: proc(r: ^Reader) -> (ch: rune, size: int, err: io.Error) { if r.i >= i64(len(r.s)) { r.prev_rune = -1 @@ -107,6 +133,9 @@ reader_read_rune :: proc(r: ^Reader) -> (ch: rune, size: int, err: io.Error) { r.i += i64(size) return } + +// decreases the reader offset by the last rune +// can only be used once and after a valid read_rune call reader_unread_rune :: proc(r: ^Reader) -> io.Error { if r.i <= 0 { return .Invalid_Unread @@ -118,6 +147,8 @@ reader_unread_rune :: proc(r: ^Reader) -> io.Error { r.prev_rune = -1 return nil } + +// seeks the reader offset to a wanted offset reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.Error) { r.prev_rune = -1 abs: i64 @@ -138,6 +169,8 @@ reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.E r.i = abs return abs, nil } + +// writes the string content left to read into the io.Writer `w` reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) { r.prev_rune = -1 if r.i >= i64(len(r.s)) { @@ -157,7 +190,6 @@ reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) { return } - @(private) _reader_vtable := &io.Stream_VTable{ impl_size = proc(s: io.Stream) -> i64 { diff --git a/core/strings/strings.odin b/core/strings/strings.odin index 67046c669..603917698 100644 --- a/core/strings/strings.odin +++ b/core/strings/strings.odin @@ -1,16 +1,28 @@ +// simple procedures to manipulate UTF-8 encoded strings package strings import "core:io" import "core:mem" +import "core:slice" import "core:unicode" import "core:unicode/utf8" +// returns a clone of the string `s` allocated using the `allocator` clone :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> string { c := make([]byte, len(s), allocator, loc) copy(c, s) return string(c[:len(s)]) } +// returns a clone of the string `s` allocated using the `allocator` +clone_safe :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> (str: string, err: mem.Allocator_Error) { + c := make([]byte, len(s), allocator, loc) or_return + copy(c, s) + return string(c[:len(s)]), nil +} + +// returns a clone of the string `s` allocated using the `allocator` as a cstring +// a nul byte is appended to the clone, to make the cstring safe clone_to_cstring :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> cstring { c := make([]byte, len(s)+1, allocator, loc) copy(c, s) @@ -18,27 +30,35 @@ clone_to_cstring :: proc(s: string, allocator := context.allocator, loc := #call return cstring(&c[0]) } +// returns a string from a byte pointer `ptr` and byte length `len` +// the string is valid as long as the parameters stay alive string_from_ptr :: proc(ptr: ^byte, len: int) -> string { return transmute(string)mem.Raw_String{ptr, len} } +// returns a string from a byte pointer `ptr and byte length `len` +// searches for a nul byte from 0.. string { s := transmute(string)mem.Raw_String{ptr, len} s = truncate_to_byte(s, 0) return s } - +// returns the raw ^byte start of the string `str` ptr_from_string :: proc(str: string) -> ^byte { d := transmute(mem.Raw_String)str return d.data } +// returns the transmute of string `str` to a cstring +// not safe since the origin string may not contain a nul byte unsafe_string_to_cstring :: proc(str: string) -> cstring { d := transmute(mem.Raw_String)str return cstring(d.data) } +// returns a string truncated to the first time it finds the byte `b` +// uses the `len` of the string `str` when it couldn't find the input truncate_to_byte :: proc(str: string, b: byte) -> string { n := index_byte(str, b) if n < 0 { @@ -46,6 +66,9 @@ truncate_to_byte :: proc(str: string, b: byte) -> string { } return str[:n] } + +// returns a string truncated to the first time it finds the rune `r` +// uses the `len` of the string `str` when it couldn't find the input truncate_to_rune :: proc(str: string, r: rune) -> string { n := index_rune(str, r) if n < 0 { @@ -54,20 +77,28 @@ truncate_to_rune :: proc(str: string, r: rune) -> string { return str[:n] } +// returns a cloned string of the byte array `s` using the `allocator` +// appends a leading nul byte clone_from_bytes :: proc(s: []byte, allocator := context.allocator, loc := #caller_location) -> string { c := make([]byte, len(s)+1, allocator, loc) copy(c, s) c[len(s)] = 0 return string(c[:len(s)]) } + +// returns a clone of the cstring `s` using the `allocator` as a string clone_from_cstring :: proc(s: cstring, allocator := context.allocator, loc := #caller_location) -> string { return clone(string(s), allocator, loc) } + +// returns a cloned string from the pointer `ptr` and a byte length `len` using the `allocator` +// same to `string_from_ptr` but allocates clone_from_ptr :: proc(ptr: ^byte, len: int, allocator := context.allocator, loc := #caller_location) -> string { s := string_from_ptr(ptr, len) return clone(s, allocator, loc) } +// overload to clone from a `string`, `[]byte`, `cstring` or a `^byte + length` to a string clone_from :: proc{ clone, clone_from_bytes, @@ -75,6 +106,8 @@ clone_from :: proc{ clone_from_ptr, } +// returns a cloned string from the cstring `ptr` and a byte length `len` using the `allocator` +// truncates till the first nul byte it finds or the byte len clone_from_cstring_bounded :: proc(ptr: cstring, len: int, allocator := context.allocator, loc := #caller_location) -> string { s := string_from_ptr((^u8)(ptr), len) s = truncate_to_byte(s, 0) @@ -82,11 +115,12 @@ clone_from_cstring_bounded :: proc(ptr: cstring, len: int, allocator := context. } // Compares two strings, returning a value representing which one comes first lexiographically. -// -1 for `a`; 1 for `b`, or 0 if they are equal. +// -1 for `lhs`; 1 for `rhs`, or 0 if they are equal. compare :: proc(lhs, rhs: string) -> int { return mem.compare(transmute([]byte)lhs, transmute([]byte)rhs) } +// returns the byte offset of the rune `r` in the string `s`, -1 when not found contains_rune :: proc(s: string, r: rune) -> int { for c, offset in s { if c == r { @@ -96,20 +130,48 @@ contains_rune :: proc(s: string, r: rune) -> int { return -1 } +/* + returns true when the string `substr` is contained inside the string `s` + + strings.contains("testing", "test") -> true + strings.contains("testing", "ing") -> true + strings.contains("testing", "text") -> false +*/ contains :: proc(s, substr: string) -> bool { return index(s, substr) >= 0 } +/* + returns true when the string `s` contains any of the characters inside the string `chars` + + strings.contains_any("test", "test") -> true + strings.contains_any("test", "ts") -> true + strings.contains_any("test", "et") -> true + strings.contains_any("test", "a") -> false +*/ contains_any :: proc(s, chars: string) -> bool { return index_any(s, chars) >= 0 } +/* + returns the utf8 rune count of the string `s` + strings.rune_count("test") -> 4 + strings.rune_count("testö") -> 5, where len("testö") -> 6 +*/ rune_count :: proc(s: string) -> int { return utf8.rune_count_in_string(s) } +/* + returns wether the strings `u` and `v` are the same alpha characters + works with utf8 string content and ignores different casings + strings.equal_fold("test", "test") -> true + strings.equal_fold("Test", "test") -> true + strings.equal_fold("Test", "tEsT") -> true + strings.equal_fold("test", "tes") -> false +*/ equal_fold :: proc(u, v: string) -> bool { s, t := u, v loop: for s != "" && t != "" { @@ -153,15 +215,71 @@ equal_fold :: proc(u, v: string) -> bool { return s == t } +/* + return the prefix length common between strings `a` and `b`. + + strings.prefix_length("testing", "test") -> 4 + strings.prefix_length("testing", "te") -> 2 + strings.prefix_length("telephone", "te") -> 2 + strings.prefix_length("testing", "est") -> 0 +*/ +prefix_length :: proc(a, b: string) -> (n: int) { + _len := min(len(a), len(b)) + + // Scan for matches including partial codepoints. + #no_bounds_check for n < _len && a[n] == b[n] { + n += 1 + } + + // Now scan to ignore partial codepoints. + if n > 0 { + s := a[:n] + n = 0 + for { + r0, w := utf8.decode_rune(s[n:]) + if r0 != utf8.RUNE_ERROR { + n += w + } else { + break + } + } + } + return +} + +/* + return true when the string `prefix` is contained at the start of the string `s` + + strings.has_prefix("testing", "test") -> true + strings.has_prefix("testing", "te") -> true + strings.has_prefix("telephone", "te") -> true + strings.has_prefix("testing", "est") -> false +*/ has_prefix :: proc(s, prefix: string) -> bool { return len(s) >= len(prefix) && s[0:len(prefix)] == prefix } +/* + returns true when the string `suffix` is contained at the end of the string `s` + good example to use this is for file extensions + + strings.has_suffix("todo.txt", ".txt") -> true + strings.has_suffix("todo.doc", ".txt") -> false + strings.has_suffix("todo.doc.txt", ".txt") -> true +*/ has_suffix :: proc(s, suffix: string) -> bool { return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix } +/* + returns a combined string from the slice of strings `a` seperated with the `sep` string + allocates the string using the `allocator` + a := [?]string { "a", "b", "c" } + b := strings.join(a[:], " ") -> "a b c" + c := strings.join(a[:], "-") -> "a-b-c" + d := strings.join(a[:], "...") -> "a...b...c" +*/ join :: proc(a: []string, sep: string, allocator := context.allocator) -> string { if len(a) == 0 { return "" @@ -181,6 +299,33 @@ join :: proc(a: []string, sep: string, allocator := context.allocator) -> string return string(b) } +join_safe :: proc(a: []string, sep: string, allocator := context.allocator) -> (str: string, err: mem.Allocator_Error) { + if len(a) == 0 { + return "", nil + } + + n := len(sep) * (len(a) - 1) + for s in a { + n += len(s) + } + + b := make([]byte, n, allocator) or_return + i := copy(b, a[0]) + for s in a[1:] { + i += copy(b[i:], sep) + i += copy(b[i:], s) + } + return string(b), nil +} + +/* + returns a combined string from the slice of strings `a` without a seperator + allocates the string using the `allocator` + + + a := [?]string { "a", "b", "c" } + b := strings.concatenate(a[:]) -> "abc" +*/ concatenate :: proc(a: []string, allocator := context.allocator) -> string { if len(a) == 0 { return "" @@ -198,32 +343,75 @@ concatenate :: proc(a: []string, allocator := context.allocator) -> string { return string(b) } +concatenate_safe :: proc(a: []string, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) { + if len(a) == 0 { + return "", nil + } + + n := 0 + for s in a { + n += len(s) + } + b := make([]byte, n, allocator) or_return + i := 0 + for s in a { + i += copy(b[i:], s) + } + return string(b), nil +} + /* `rune_offset` and `rune_length` are in runes, not bytes. - If `rune_length` <= 0, then it'll return the remainder of the string starting with `rune_offset`. + If `rune_length` <= 0, then it'll return the remainder of the string starting at `rune_offset`. + + strings.cut("some example text", 0, 4) -> "some" + strings.cut("some example text", 2, 2) -> "me" + strings.cut("some example text", 5, 7) -> "example" */ cut :: proc(s: string, rune_offset := int(0), rune_length := int(0), allocator := context.allocator) -> (res: string) { s := s; rune_length := rune_length - l := utf8.rune_count_in_string(s) + context.allocator = allocator - if rune_offset >= l { return "" } + // If we signal that we want the entire remainder (length <= 0) *and* + // the offset is zero, then we can early out by cloning the input if rune_offset == 0 && rune_length <= 0 { - return clone(s, allocator) + return clone(s) } - if rune_length == 0 { rune_length = l } + // We need to know if we have enough runes to cover offset + length. + rune_count := utf8.rune_count_in_string(s) + + // We're asking for a substring starting after the end of the input string. + // That's just an empty string. + if rune_offset >= rune_count { + return "" + } + + // If we don't specify the length of the substring, use the remainder. + if rune_length <= 0 { + rune_length = rune_count - rune_offset + } + + // We don't yet know how many bytes we need exactly. + // But we do know it's bounded by the number of runes * 4 bytes, + // and can be no more than the size of the input string. bytes_needed := min(rune_length * 4, len(s)) - buf := make([]u8, bytes_needed, allocator) + buf := make([]u8, bytes_needed) byte_offset := 0 - for i := 0; i < l; i += 1 { + for i := 0; i < rune_count; i += 1 { _, w := utf8.decode_rune_in_string(s) + + // If the rune is part of the substring, copy it to the output buffer. if i >= rune_offset { for j := 0; j < w; j += 1 { buf[byte_offset+j] = s[j] } byte_offset += w } + + // We're done if we reach the end of the input string, *or* + // if we've reached a specified length in runes. if rune_length > 0 { if i == rune_offset + rune_length - 1 { break } } @@ -280,28 +468,61 @@ _split :: proc(s_, sep: string, sep_save, n_: int, allocator := context.allocato return res[:i+1] } +/* + Splits a string into parts, based on a separator. + Returned strings are substrings of 's'. + ``` + s := "aaa.bbb.ccc.ddd.eee" // 5 parts + ss := split(s, ".") + fmt.println(ss) // [aaa, bbb, ccc, ddd, eee] + ``` +*/ split :: proc(s, sep: string, allocator := context.allocator) -> []string { return _split(s, sep, 0, -1, allocator) } +/* + Splits a string into a total of 'n' parts, based on a separator. + Returns fewer parts if there wasn't enough occurrences of the separator. + Returned strings are substrings of 's'. + ``` + s := "aaa.bbb.ccc.ddd.eee" // 5 parts present + ss := split_n(s, ".", 3) // total of 3 wanted + fmt.println(ss) // [aaa, bbb, ccc.ddd.eee] + ``` +*/ split_n :: proc(s, sep: string, n: int, allocator := context.allocator) -> []string { return _split(s, sep, 0, n, allocator) } +/* + splits the string `s` after the seperator string `sep` appears + returns the slice of split strings allocated using `allocator` + + a := "aaa.bbb.ccc.ddd.eee" + aa := strings.split_after(a, ".") + fmt.eprintln(aa) // [aaa., bbb., ccc., ddd., eee] +*/ split_after :: proc(s, sep: string, allocator := context.allocator) -> []string { return _split(s, sep, len(sep), -1, allocator) } +/* + splits the string `s` after the seperator string `sep` appears into a total of `n` parts + returns the slice of split strings allocated using `allocator` + + a := "aaa.bbb.ccc.ddd.eee" + aa := strings.split_after(a, ".") + fmt.eprintln(aa) // [aaa., bbb., ccc., ddd., eee] +*/ split_after_n :: proc(s, sep: string, n: int, allocator := context.allocator) -> []string { return _split(s, sep, len(sep), n, allocator) } - @private -_split_iterator :: proc(s: ^string, sep: string, sep_save, n: int) -> (res: string, ok: bool) { - s, n := s, n - - if n == 0 { +_split_iterator :: proc(s: ^string, sep: string, sep_save: int) -> (res: string, ok: bool) { + // stop once the string is empty or nil + if s == nil || len(s^) == 0 { return } @@ -312,44 +533,68 @@ _split_iterator :: proc(s: ^string, sep: string, sep_save, n: int) -> (res: stri return } - if n < 0 { - n = count(s^, sep) + 1 - } - - n -= 1 - - i := 0 - for ; i < n; i += 1 { - m := index(s^, sep) - if m < 0 { - break - } + m := index(s^, sep) + if m < 0 { + // not found + res = s[:] + ok = res != "" + s^ = s[len(s):] + } else { res = s[:m+sep_save] ok = true s^ = s[m+len(sep):] - return } - res = s[:] - ok = res != "" - s^ = s[len(s):] return } +/* + split the ^string `s` by the byte seperator `sep` in an iterator fashion + consumes the original string till the end, leaving the string `s` with len == 0 + text := "a.b.c.d.e" + for str in strings.split_by_byte_iterator(&text, '.') { + fmt.eprintln(str) // every loop -> a b c d e + } +*/ +split_by_byte_iterator :: proc(s: ^string, sep: u8) -> (res: string, ok: bool) { + m := index_byte(s^, sep) + if m < 0 { + // not found + res = s[:] + ok = res != "" + s^ = {} + } else { + res = s[:m] + ok = true + s^ = s[m+1:] + } + return +} + +/* + split the ^string `s` by the seperator string `sep` in an iterator fashion + consumes the original string till the end + + text := "a.b.c.d.e" + for str in strings.split_iterator(&text, ".") { + fmt.eprintln(str) // every loop -> a b c d e + } +*/ split_iterator :: proc(s: ^string, sep: string) -> (string, bool) { - return _split_iterator(s, sep, 0, -1) + return _split_iterator(s, sep, 0) } -split_n_iterator :: proc(s: ^string, sep: string, n: int) -> (string, bool) { - return _split_iterator(s, sep, 0, n) -} +/* + split the ^string `s` after every seperator string `sep` in an iterator fashion + consumes the original string till the end + text := "a.b.c.d.e" + for str in strings.split_after_iterator(&text, ".") { + fmt.eprintln(str) // every loop -> a. b. c. d. e + } +*/ split_after_iterator :: proc(s: ^string, sep: string) -> (string, bool) { - return _split_iterator(s, sep, len(sep), -1) -} - -split_after_n_iterator :: proc(s: ^string, sep: string, n: int) -> (string, bool) { - return _split_iterator(s, sep, len(sep), n) + return _split_iterator(s, sep, len(sep)) } @@ -364,6 +609,14 @@ _trim_cr :: proc(s: string) -> string { return s } +/* + split the string `s` at every line break '\n' + return an allocated slice of strings + + a := "a\nb\nc\nd\ne" + b := strings.split_lines(a) + fmt.eprintln(b) // [a, b, c, d, e] +*/ split_lines :: proc(s: string, allocator := context.allocator) -> []string { sep :: "\n" lines := _split(s, sep, 0, -1, allocator) @@ -373,6 +626,14 @@ split_lines :: proc(s: string, allocator := context.allocator) -> []string { return lines } +/* + split the string `s` at every line break '\n' for `n` parts + return an allocated slice of strings + + a := "a\nb\nc\nd\ne" + b := strings.split_lines_n(a, 3) + fmt.eprintln(b) // [a, b, c, d\ne\n] +*/ split_lines_n :: proc(s: string, n: int, allocator := context.allocator) -> []string { sep :: "\n" lines := _split(s, sep, 0, n, allocator) @@ -382,6 +643,14 @@ split_lines_n :: proc(s: string, n: int, allocator := context.allocator) -> []st return lines } +/* + split the string `s` at every line break '\n' leaving the '\n' in the resulting strings + return an allocated slice of strings + + a := "a\nb\nc\nd\ne" + b := strings.split_lines_after(a) + fmt.eprintln(b) // [a\n, b\n, c\n, d\n, e\n] +*/ split_lines_after :: proc(s: string, allocator := context.allocator) -> []string { sep :: "\n" lines := _split(s, sep, len(sep), -1, allocator) @@ -391,6 +660,15 @@ split_lines_after :: proc(s: string, allocator := context.allocator) -> []string return lines } +/* + split the string `s` at every line break '\n' leaving the '\n' in the resulting strings + only runs for `n` parts + return an allocated slice of strings + + a := "a\nb\nc\nd\ne" + b := strings.split_lines_after_n(a, 3) + fmt.eprintln(b) // [a\n, b\n, c\n, d\ne\n] +*/ split_lines_after_n :: proc(s: string, n: int, allocator := context.allocator) -> []string { sep :: "\n" lines := _split(s, sep, len(sep), n, allocator) @@ -400,33 +678,45 @@ split_lines_after_n :: proc(s: string, n: int, allocator := context.allocator) - return lines } +/* + split the string `s` at every line break '\n' + returns the current split string every iteration till the string is consumed + + text := "a\nb\nc\nd\ne" + for str in strings.split_lines_iterator(&text) { + fmt.eprintln(text) // every loop -> a b c d e + } +*/ split_lines_iterator :: proc(s: ^string) -> (line: string, ok: bool) { sep :: "\n" - line = _split_iterator(s, sep, 0, -1) or_return + line = _split_iterator(s, sep, 0) or_return return _trim_cr(line), true } -split_lines_n_iterator :: proc(s: ^string, n: int) -> (line: string, ok: bool) { - sep :: "\n" - line = _split_iterator(s, sep, 0, n) or_return - return _trim_cr(line), true -} +/* + split the string `s` at every line break '\n' + returns the current split string every iteration till the string is consumed + text := "a\nb\nc\nd\ne" + for str in strings.split_lines_after_iterator(&text) { + fmt.eprintln(text) // every loop -> a\n b\n c\n d\n e\n + } +*/ split_lines_after_iterator :: proc(s: ^string) -> (line: string, ok: bool) { sep :: "\n" - line = _split_iterator(s, sep, len(sep), -1) or_return + line = _split_iterator(s, sep, len(sep)) or_return return _trim_cr(line), true } -split_lines_after_n_iterator :: proc(s: ^string, n: int) -> (line: string, ok: bool) { - sep :: "\n" - line = _split_iterator(s, sep, len(sep), n) or_return - return _trim_cr(line), true -} - - - +/* + returns the byte offset of the first byte `c` in the string `s` it finds, -1 when not found + can't find utf8 based runes + strings.index_byte("test", 't') -> 0 + strings.index_byte("test", 'e') -> 1 + strings.index_byte("test", 'x') -> -1 + strings.index_byte("teäst", 'ä') -> -1 +*/ index_byte :: proc(s: string, c: byte) -> int { for i := 0; i < len(s); i += 1 { if s[i] == c { @@ -436,7 +726,15 @@ index_byte :: proc(s: string, c: byte) -> int { return -1 } -// Returns -1 if c is not present +/* + returns the byte offset of the last byte `c` in the string `s` it finds, -1 when not found + can't find utf8 based runes + + strings.index_byte("test", 't') -> 3 + strings.index_byte("test", 'e') -> 1 + strings.index_byte("test", 'x') -> -1 + strings.index_byte("teäst", 'ä') -> -1 +*/ last_index_byte :: proc(s: string, c: byte) -> int { for i := len(s)-1; i >= 0; i -= 1 { if s[i] == c { @@ -447,9 +745,50 @@ last_index_byte :: proc(s: string, c: byte) -> int { } +/* + returns the byte offset of the first rune `r` in the string `s` it finds, -1 when not found + avoids invalid runes + + strings.index_rune("abcädef", 'x') -> -1 + strings.index_rune("abcädef", 'a') -> 0 + strings.index_rune("abcädef", 'b') -> 1 + strings.index_rune("abcädef", 'c') -> 2 + strings.index_rune("abcädef", 'ä') -> 3 + strings.index_rune("abcädef", 'd') -> 5 + strings.index_rune("abcädef", 'e') -> 6 + strings.index_rune("abcädef", 'f') -> 7 +*/ +index_rune :: proc(s: string, r: rune) -> int { + switch { + case 0 <= r && r < utf8.RUNE_SELF: + return index_byte(s, byte(r)) + + case r == utf8.RUNE_ERROR: + for c, i in s { + if c == utf8.RUNE_ERROR { + return i + } + } + return -1 + + case !utf8.valid_rune(r): + return -1 + } + + b, w := utf8.encode_rune(r) + return index(s, string(b[:w])) +} @private PRIME_RABIN_KARP :: 16777619 +/* + returns the byte offset of the string `substr` in the string `s`, -1 when not found + + strings.index("test", "t") -> 0 + strings.index("test", "te") -> 0 + strings.index("test", "st") -> 2 + strings.index("test", "tt") -> -1 +*/ index :: proc(s, substr: string) -> int { hash_str_rabin_karp :: proc(s: string) -> (hash: u32 = 0, pow: u32 = 1) { for i := 0; i < len(s); i += 1 { @@ -500,6 +839,14 @@ index :: proc(s, substr: string) -> int { return -1 } +/* + returns the last byte offset of the string `substr` in the string `s`, -1 when not found + + strings.index("test", "t") -> 3 + strings.index("test", "te") -> 0 + strings.index("test", "st") -> 2 + strings.index("test", "tt") -> -1 +*/ last_index :: proc(s, substr: string) -> int { hash_str_rabin_karp_reverse :: proc(s: string) -> (hash: u32 = 0, pow: u32 = 1) { for i := len(s) - 1; i >= 0; i -= 1 { @@ -548,7 +895,15 @@ last_index :: proc(s, substr: string) -> int { return -1 } -// index_any returns the index of the first char of `chars` found in `s`. -1 if not found. +/* + returns the index of any first char of `chars` found in `s`, -1 if not found + + strings.index_any("test", "s") -> 2 + strings.index_any("test", "se") -> 1 + strings.index_any("test", "et") -> 0 + strings.index_any("test", "set") -> 0 + strings.index_any("test", "x") -> -1 +*/ index_any :: proc(s, chars: string) -> int { if chars == "" { return -1 @@ -581,6 +936,16 @@ index_any :: proc(s, chars: string) -> int { return -1 } +/* + returns the index of any first char of `chars` found in `s`, -1 if not found + iterates the string in reverse + + strings.index_any("test", "s") -> 2 + strings.index_any("test", "se") -> 2 + strings.index_any("test", "et") -> 1 + strings.index_any("test", "set") -> 3 + strings.index_any("test", "x") -> -1 +*/ last_index_any :: proc(s, chars: string) -> int { if chars == "" { return -1 @@ -630,6 +995,16 @@ last_index_any :: proc(s, chars: string) -> int { return -1 } +/* + returns the count of the string `substr` found in the string `s` + returns the rune_count + 1 of the string `s` on empty `substr` + + strings.count("abbccc", "a") -> 1 + strings.count("abbccc", "b") -> 2 + strings.count("abbccc", "c") -> 3 + strings.count("abbccc", "ab") -> 1 + strings.count("abbccc", " ") -> 0 +*/ count :: proc(s, substr: string) -> int { if len(substr) == 0 { // special case return rune_count(s) + 1 @@ -665,7 +1040,12 @@ count :: proc(s, substr: string) -> int { return n } +/* + repeats the string `s` multiple `count` times and returns the allocated string + panics when `count` is below 0 + strings.repeat("abc", 2) -> "abcabc" +*/ repeat :: proc(s: string, count: int, allocator := context.allocator) -> string { if count < 0 { panic("strings: negative repeat count") @@ -682,11 +1062,28 @@ repeat :: proc(s: string, count: int, allocator := context.allocator) -> string return string(b) } +/* + replaces all instances of `old` in the string `s` with the `new` string + returns the `output` string and true when an a allocation through a replace happened + + strings.replace_all("xyzxyz", "xyz", "abc") -> "abcabc", true + strings.replace_all("xyzxyz", "abc", "xyz") -> "xyzxyz", false + strings.replace_all("xyzxyz", "xy", "z") -> "zzzz", true +*/ replace_all :: proc(s, old, new: string, allocator := context.allocator) -> (output: string, was_allocation: bool) { return replace(s, old, new, -1, allocator) } -// if n < 0, no limit on the number of replacements +/* + replaces `n` instances of `old` in the string `s` with the `new` string + if n < 0, no limit on the number of replacements + returns the `output` string and true when an a allocation through a replace happened + + strings.replace("xyzxyz", "xyz", "abc", 2) -> "abcabc", true + strings.replace("xyzxyz", "xyz", "abc", 1) -> "abcxyz", true + strings.replace("xyzxyz", "abc", "xyz", -1) -> "xyzxyz", false + strings.replace("xyzxyz", "xy", "z", -1) -> "zzzz", true +*/ replace :: proc(s, old, new: string, n: int, allocator := context.allocator) -> (output: string, was_allocation: bool) { if old == new || n == 0 { was_allocation = false @@ -727,17 +1124,35 @@ replace :: proc(s, old, new: string, n: int, allocator := context.allocator) -> return } +/* + removes the `key` string `n` times from the `s` string + if n < 0, no limit on the number of removes + returns the `output` string and true when an a allocation through a remove happened + + strings.remove("abcabc", "abc", 1) -> "abc", true + strings.remove("abcabc", "abc", -1) -> "", true + strings.remove("abcabc", "a", -1) -> "bcbc", true + strings.remove("abcabc", "x", -1) -> "abcabc", false +*/ remove :: proc(s, key: string, n: int, allocator := context.allocator) -> (output: string, was_allocation: bool) { return replace(s, key, "", n, allocator) } +/* + removes all the `key` string instanes from the `s` string + returns the `output` string and true when an a allocation through a remove happened + + strings.remove("abcabc", "abc") -> "", true + strings.remove("abcabc", "a") -> "bcbc", true + strings.remove("abcabc", "x") -> "abcabc", false +*/ remove_all :: proc(s, key: string, allocator := context.allocator) -> (output: string, was_allocation: bool) { return remove(s, key, -1, allocator) } @(private) _ascii_space := [256]bool{'\t' = true, '\n' = true, '\v' = true, '\f' = true, '\r' = true, ' ' = true} - +// return true when the `r` rune is '\t', '\n', '\v', '\f', '\r' or ' ' is_ascii_space :: proc(r: rune) -> bool { if r < utf8.RUNE_SELF { return _ascii_space[u8(r)] @@ -745,6 +1160,7 @@ is_ascii_space :: proc(r: rune) -> bool { return false } +// returns true when the `r` rune is any asci or utf8 based whitespace is_space :: proc(r: rune) -> bool { if r < 0x2000 { switch r { @@ -763,10 +1179,24 @@ is_space :: proc(r: rune) -> bool { return false } +// returns true when the `r` rune is a nul byte is_null :: proc(r: rune) -> bool { return r == 0x0000 } +/* + runs trough the `s` string linearly and watches wether the `p` procedure matches the `truth` bool + returns the rune offset or -1 when no match was found + + call :: proc(r: rune) -> bool { + return r == 'a' + } + strings.index_proc("abcabc", call) -> 0 + strings.index_proc("cbacba", call) -> 2 + strings.index_proc("cbacba", call, false) -> 0 + strings.index_proc("abcabc", call, false) -> 1 + strings.index_proc("xyz", call) -> -1 +*/ index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> int { for r, i in s { if p(r) == truth { @@ -776,6 +1206,7 @@ index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> int { return -1 } +// same as `index_proc` but with a `p` procedure taking a rawptr for state index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr, truth := true) -> int { for r, i in s { if p(state, r) == truth { @@ -785,6 +1216,7 @@ index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: r return -1 } +// same as `index_proc` but runs through the string in reverse last_index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> int { // TODO(bill): Probably use Rabin-Karp Search for i := len(s); i > 0; { @@ -797,6 +1229,7 @@ last_index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> int return -1 } +// same as `index_proc_with_state` but runs through the string in reverse last_index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr, truth := true) -> int { // TODO(bill): Probably use Rabin-Karp Search for i := len(s); i > 0; { @@ -808,7 +1241,17 @@ last_index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, sta } return -1 } + +/* + trims the input string `s` until the procedure `p` returns false + does not allocate - only returns a cut variant of the input string + returns an empty string when no match was found at all + find :: proc(r: rune) -> bool { + return r != 'i' + } + strings.trim_left_proc("testing", find) -> "ing" +*/ trim_left_proc :: proc(s: string, p: proc(rune) -> bool) -> string { i := index_proc(s, p, false) if i == -1 { @@ -817,29 +1260,10 @@ trim_left_proc :: proc(s: string, p: proc(rune) -> bool) -> string { return s[i:] } - -index_rune :: proc(s: string, r: rune) -> int { - switch { - case 0 <= r && r < utf8.RUNE_SELF: - return index_byte(s, byte(r)) - - case r == utf8.RUNE_ERROR: - for c, i in s { - if c == utf8.RUNE_ERROR { - return i - } - } - return -1 - - case !utf8.valid_rune(r): - return -1 - } - - b, w := utf8.encode_rune(r) - return index(s, string(b[:w])) -} - - +/* + trims the input string `s` until the procedure `p` with state returns false + returns an empty string when no match was found at all +*/ trim_left_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr) -> string { i := index_proc_with_state(s, p, state, false) if i == -1 { @@ -848,6 +1272,16 @@ trim_left_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, stat return s[i:] } +/* + trims the input string `s` from the right until the procedure `p` returns false + does not allocate - only returns a cut variant of the input string + returns an empty string when no match was found at all + + find :: proc(r: rune) -> bool { + return r != 't' + } + strings.trim_left_proc("testing", find) -> "test" +*/ trim_right_proc :: proc(s: string, p: proc(rune) -> bool) -> string { i := last_index_proc(s, p, false) if i >= 0 && s[i] >= utf8.RUNE_SELF { @@ -859,6 +1293,10 @@ trim_right_proc :: proc(s: string, p: proc(rune) -> bool) -> string { return s[0:i] } +/* + trims the input string `s` from the right until the procedure `p` with state returns false + returns an empty string when no match was found at all +*/ trim_right_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr) -> string { i := last_index_proc_with_state(s, p, state, false) if i >= 0 && s[i] >= utf8.RUNE_SELF { @@ -870,7 +1308,7 @@ trim_right_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, sta return s[0:i] } - +// procedure for `trim_*_proc` variants, which has a string rawptr cast + rune comparison is_in_cutset :: proc(state: rawptr, r: rune) -> bool { if state == nil { return false @@ -884,7 +1322,7 @@ is_in_cutset :: proc(state: rawptr, r: rune) -> bool { return false } - +// trims the `cutset` string from the `s` string trim_left :: proc(s: string, cutset: string) -> string { if s == "" || cutset == "" { return s @@ -893,6 +1331,7 @@ trim_left :: proc(s: string, cutset: string) -> string { return trim_left_proc_with_state(s, is_in_cutset, &state) } +// trims the `cutset` string from the `s` string from the right trim_right :: proc(s: string, cutset: string) -> string { if s == "" || cutset == "" { return s @@ -901,35 +1340,48 @@ trim_right :: proc(s: string, cutset: string) -> string { return trim_right_proc_with_state(s, is_in_cutset, &state) } +// trims the `cutset` string from the `s` string, both from left and right trim :: proc(s: string, cutset: string) -> string { return trim_right(trim_left(s, cutset), cutset) } +// trims until a valid non space rune: "\t\txyz\t\t" -> "xyz\t\t" trim_left_space :: proc(s: string) -> string { return trim_left_proc(s, is_space) } +// trims from the right until a valid non space rune: "\t\txyz\t\t" -> "\t\txyz" trim_right_space :: proc(s: string) -> string { return trim_right_proc(s, is_space) } +// trims from both sides until a valid non space rune: "\t\txyz\t\t" -> "xyz" trim_space :: proc(s: string) -> string { return trim_right_space(trim_left_space(s)) } - +// trims nul runes from the left: "\x00\x00testing\x00\x00" -> "testing\x00\x00" trim_left_null :: proc(s: string) -> string { return trim_left_proc(s, is_null) } +// trims nul runes from the right: "\x00\x00testing\x00\x00" -> "\x00\x00testing" trim_right_null :: proc(s: string) -> string { return trim_right_proc(s, is_null) } +// trims nul runes from both sides: "\x00\x00testing\x00\x00" -> "testing" trim_null :: proc(s: string) -> string { return trim_right_null(trim_left_null(s)) } +/* + trims a `prefix` string from the start of the `s` string and returns the trimmed string + returns the input string `s` when no prefix was found + + strings.trim_prefix("testing", "test") -> "ing" + strings.trim_prefix("testing", "abc") -> "testing" +*/ trim_prefix :: proc(s, prefix: string) -> string { if has_prefix(s, prefix) { return s[len(prefix):] @@ -937,6 +1389,13 @@ trim_prefix :: proc(s, prefix: string) -> string { return s } +/* + trims a `suffix` string from the end of the `s` string and returns the trimmed string + returns the input string `s` when no suffix was found + + strings.trim_suffix("todo.txt", ".txt") -> "todo" + strings.trim_suffix("todo.doc", ".txt") -> "todo.doc" +*/ trim_suffix :: proc(s, suffix: string) -> string { if has_suffix(s, suffix) { return s[:len(s)-len(suffix)] @@ -944,148 +1403,157 @@ trim_suffix :: proc(s, suffix: string) -> string { return s } -split_multi :: proc(s: string, substrs: []string, skip_empty := false, allocator := context.allocator) -> []string #no_bounds_check { +/* + splits the input string `s` by all possible `substrs` []string + returns the allocated []string, nil on any empty substring or no matches + + splits := [?]string { "---", "~~~", ".", "_", "," } + res := strings.split_multi("testing,this.out_nice---done~~~last", splits[:]) + fmt.eprintln(res) // -> [testing, this, out, nice, done, last] +*/ +split_multi :: proc(s: string, substrs: []string, allocator := context.allocator) -> (buf: []string) #no_bounds_check { if s == "" || len(substrs) <= 0 { - return nil + return } - sublen := len(substrs[0]) - - for substr in substrs[1:] { - sublen = min(sublen, len(substr)) + // disallow "" substr + for substr in substrs { + if len(substr) == 0 { + return + } } - shared := len(s) - sublen + // TODO maybe remove duplicate substrs + // sort substrings by string size, largest to smallest + temp_substrs := slice.clone(substrs, context.temp_allocator) + slice.sort_by(temp_substrs, proc(a, b: string) -> bool { + return len(a) > len(b) + }) - if shared <= 0 { - return nil - } + substrings_found: int + temp := s - // number, index, last - n, i, l := 0, 0, 0 - - // count results - first_pass: for i <= shared { - for substr in substrs { - if s[i:i+sublen] == substr { - if !skip_empty || i - l > 0 { - n += 1 - } - - i += sublen - l = i + // count substr results found in string + first_pass: for len(temp) > 0 { + for substr in temp_substrs { + size := len(substr) + // check range and compare string to substr + if size <= len(temp) && temp[:size] == substr { + substrings_found += 1 + temp = temp[size:] continue first_pass } } - _, skip := utf8.decode_rune_in_string(s[i:]) - i += skip + // step through string + _, skip := utf8.decode_rune_in_string(temp[:]) + temp = temp[skip:] } - if !skip_empty || len(s) - l > 0 { - n += 1 + // skip when no results + if substrings_found < 1 { + return } - if n < 1 { - // no results - return nil - } + buf = make([]string, substrings_found + 1, allocator) + buf_index: int + temp = s + temp_old := temp - buf := make([]string, n, allocator) - - n, i, l = 0, 0, 0 - - // slice results - second_pass: for i <= shared { - for substr in substrs { - if s[i:i+sublen] == substr { - if !skip_empty || i - l > 0 { - buf[n] = s[l:i] - n += 1 - } - - i += sublen - l = i + // gather results in the same fashion + second_pass: for len(temp) > 0 { + for substr in temp_substrs { + size := len(substr) + // check range and compare string to substr + if size <= len(temp) && temp[:size] == substr { + buf[buf_index] = temp_old[:len(temp_old) - len(temp)] + buf_index += 1 + temp = temp[size:] + temp_old = temp continue second_pass } } - _, skip := utf8.decode_rune_in_string(s[i:]) - i += skip + // step through string + _, skip := utf8.decode_rune_in_string(temp[:]) + temp = temp[skip:] } - if !skip_empty || len(s) - l > 0 { - buf[n] = s[l:] - } + buf[buf_index] = temp_old[:] return buf } +// state for the split multi iterator +Split_Multi :: struct { + temp: string, + temp_old: string, + substrs: []string, +} +// returns split multi state with sorted `substrs` +split_multi_init :: proc(s: string, substrs: []string) -> Split_Multi { + // sort substrings, largest to smallest + temp_substrs := slice.clone(substrs, context.temp_allocator) + slice.sort_by(temp_substrs, proc(a, b: string) -> bool { + return len(a) > len(b) + }) - -split_multi_iterator :: proc(s: ^string, substrs: []string, skip_empty := false) -> (string, bool) #no_bounds_check { - if s == nil || s^ == "" || len(substrs) <= 0 { - return "", false + return { + temp = s, + temp_old = s, + substrs = temp_substrs, } +} - sublen := len(substrs[0]) +/* + splits the input string `s` by all possible `substrs` []string in an iterator fashion + returns the split string every iteration, the full string on no match - for substr in substrs[1:] { - sublen = min(sublen, len(substr)) + splits := [?]string { "---", "~~~", ".", "_", "," } + state := strings.split_multi_init("testing,this.out_nice---done~~~last", splits[:]) + for str in strings.split_multi_iterate(&state) { + fmt.eprintln(str) // every iteration -> [testing, this, out, nice, done, last] } - - shared := len(s) - sublen - - if shared <= 0 { - return "", false - } - - // index, last - i, l := 0, 0 - - loop: for i <= shared { +*/ +split_multi_iterate :: proc(using sm: ^Split_Multi) -> (res: string, ok: bool) #no_bounds_check { + pass: for len(temp) > 0 { for substr in substrs { - if s[i:i+sublen] == substr { - if !skip_empty || i - l > 0 { - res := s[l:i] - s^ = s[i:] - return res, true - } + size := len(substr) - i += sublen - l = i - - continue loop + // check range and compare string to substr + if size <= len(temp) && temp[:size] == substr { + res = temp_old[:len(temp_old) - len(temp)] + temp = temp[size:] + temp_old = temp + ok = true + return } } - _, skip := utf8.decode_rune_in_string(s[i:]) - i += skip + // step through string + _, skip := utf8.decode_rune_in_string(temp[:]) + temp = temp[skip:] } - if !skip_empty || len(s) - l > 0 { - res := s[l:] - s^ = s[len(s):] - return res, true + // allow last iteration + if temp_old != "" { + res = temp_old[:] + ok = true + temp_old = "" } - return "", false + return } - - - - - // scrub scruvs invalid utf-8 characters and replaces them with the replacement string // Adjacent invalid bytes are only replaced once scrub :: proc(s: string, replacement: string, allocator := context.allocator) -> string { str := s b: Builder - init_builder(&b, 0, len(s), allocator) + builder_init(&b, 0, len(s), allocator) has_error := false cursor := 0 @@ -1114,7 +1582,13 @@ scrub :: proc(s: string, replacement: string, allocator := context.allocator) -> return to_string(b) } +/* + returns a reversed version of the `s` string + a := "abcxyz" + b := strings.reverse(a) + fmt.eprintln(a, b) // abcxyz zyxcba +*/ reverse :: proc(s: string, allocator := context.allocator) -> string { str := s n := len(str) @@ -1130,18 +1604,25 @@ reverse :: proc(s: string, allocator := context.allocator) -> string { return string(buf) } +/* + expands the string to a grid spaced by `tab_size` whenever a `\t` character appears + returns the tabbed string, panics on tab_size <= 0 + + strings.expand_tabs("abc1\tabc2\tabc3", 4) -> abc1 abc2 abc3 + strings.expand_tabs("abc1\tabc2\tabc3", 5) -> abc1 abc2 abc3 + strings.expand_tabs("abc1\tabc2\tabc3", 6) -> abc1 abc2 abc3 +*/ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) -> string { if tab_size <= 0 { panic("tab size must be positive") } - if s == "" { return "" } b: Builder - init_builder(&b, allocator) + builder_init(&b, allocator) writer := to_writer(&b) str := s column: int @@ -1173,7 +1654,16 @@ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) -> return to_string(b) } +/* + splits the `str` string by the seperator `sep` string and returns 3 parts + `head`: before the split, `match`: the seperator, `tail`: the end of the split + returns the input string when the `sep` was not found + text := "testing this out" + strings.partition(text, " this ") -> head: "testing", match: " this ", tail: "out" + strings.partition(text, "hi") -> head: "testing t", match: "hi", tail: "s out" + strings.partition(text, "xyz") -> head: "testing this out", match: "", tail: "" +*/ partition :: proc(str, sep: string) -> (head, match, tail: string) { i := index(str, sep) if i == -1 { @@ -1200,8 +1690,8 @@ centre_justify :: proc(str: string, length: int, pad: string, allocator := conte pad_len := rune_count(pad) b: Builder - init_builder(&b, allocator) - grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad)) + builder_init(&b, allocator) + builder_grow(&b, len(str) + (remains/pad_len + 1)*len(pad)) w := to_writer(&b) @@ -1223,8 +1713,8 @@ left_justify :: proc(str: string, length: int, pad: string, allocator := context pad_len := rune_count(pad) b: Builder - init_builder(&b, allocator) - grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad)) + builder_init(&b, allocator) + builder_grow(&b, len(str) + (remains/pad_len + 1)*len(pad)) w := to_writer(&b) @@ -1245,8 +1735,8 @@ right_justify :: proc(str: string, length: int, pad: string, allocator := contex pad_len := rune_count(pad) b: Builder - init_builder(&b, allocator) - grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad)) + builder_init(&b, allocator) + builder_grow(&b, len(str) + (remains/pad_len + 1)*len(pad)) w := to_writer(&b) @@ -1362,3 +1852,97 @@ fields_proc :: proc(s: string, f: proc(rune) -> bool, allocator := context.alloc return substrings[:] } + + +// `fields_iterator` returns the first run of characters in `s` that does not contain white space, defined by `unicode.is_space` +// `s` will then start from any space after the substring, or be an empty string if the substring was the remaining characters +fields_iterator :: proc(s: ^string) -> (field: string, ok: bool) { + start, end := -1, -1 + for r, offset in s { + end = offset + if unicode.is_space(r) { + if start >= 0 { + field = s[start : end] + ok = true + s^ = s[end:] + return + } + } else { + if start < 0 { + start = end + } + } + } + + // if either of these are true, the string did not contain any characters + if end < 0 || start < 0 { + return "", false + } + + field = s[start:] + ok = true + s^ = s[len(s):] + return +} + +// `levenshtein_distance` returns the Levenshtein edit distance between 2 strings. +// This is a single-row-version of the Wagner–Fischer algorithm, based on C code by Martin Ettl. +// Note: allocator isn't used if the length of string b in runes is smaller than 64. +levenshtein_distance :: proc(a, b: string, allocator := context.allocator) -> int { + LEVENSHTEIN_DEFAULT_COSTS: []int : { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, + } + + m, n := utf8.rune_count_in_string(a), utf8.rune_count_in_string(b) + + if m == 0 { + return n + } + if n == 0 { + return m + } + + costs: []int + + if n + 1 > len(LEVENSHTEIN_DEFAULT_COSTS) { + costs = make([]int, n + 1, allocator) + for k in 0..=n { + costs[k] = k + } + } else { + costs = LEVENSHTEIN_DEFAULT_COSTS + } + + defer if n + 1 > len(LEVENSHTEIN_DEFAULT_COSTS) { + delete(costs, allocator) + } + + i: int + for c1 in a { + costs[0] = i + 1 + corner := i + j: int + for c2 in b { + upper := costs[j + 1] + if c1 == c2 { + costs[j + 1] = corner + } else { + t := upper if upper < corner else corner + costs[j + 1] = (costs[j] if costs[j] < t else t) + 1 + } + + corner = upper + j += 1 + } + + i += 1 + } + + return costs[n] +} diff --git a/core/sync/atomic.odin b/core/sync/atomic.odin index 21dcea178..0900a6544 100644 --- a/core/sync/atomic.odin +++ b/core/sync/atomic.odin @@ -2,167 +2,44 @@ package sync import "core:intrinsics" -Ordering :: enum { - Relaxed, // Monotonic - Release, - Acquire, - Acquire_Release, - Sequentially_Consistent, -} - -strongest_failure_ordering_table := [Ordering]Ordering{ - .Relaxed = .Relaxed, - .Release = .Relaxed, - .Acquire = .Acquire, - .Acquire_Release = .Acquire, - .Sequentially_Consistent = .Sequentially_Consistent, -} - -strongest_failure_ordering :: #force_inline proc(order: Ordering) -> Ordering { - return strongest_failure_ordering_table[order] -} - -fence :: #force_inline proc($order: Ordering) { - when order == .Relaxed { #panic("there is no such thing as a relaxed fence") } - else when order == .Release { intrinsics.atomic_fence_rel() } - else when order == .Acquire { intrinsics.atomic_fence_acq() } - else when order == .Acquire_Release { intrinsics.atomic_fence_acqrel() } - else when order == .Sequentially_Consistent { intrinsics.atomic_fence() } - else { #panic("unknown order") } +cpu_relax :: intrinsics.cpu_relax + +/* +Atomic_Memory_Order :: enum { + Relaxed = 0, // Unordered + Consume = 1, // Monotonic + Acquire = 2, + Release = 3, + Acq_Rel = 4, + Seq_Cst = 5, } +*/ +Atomic_Memory_Order :: intrinsics.Atomic_Memory_Order -atomic_store :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) { - when order == .Relaxed { intrinsics.atomic_store_relaxed(dst, val) } - else when order == .Release { intrinsics.atomic_store_rel(dst, val) } - else when order == .Sequentially_Consistent { intrinsics.atomic_store(dst, val) } - else when order == .Acquire { #panic("there is not such thing as an acquire store") } - else when order == .Acquire_Release { #panic("there is not such thing as an acquire/release store") } - else { #panic("unknown order") } -} - -atomic_load :: #force_inline proc(dst: ^$T, $order: Ordering) -> T { - when order == .Relaxed { return intrinsics.atomic_load_relaxed(dst) } - else when order == .Acquire { return intrinsics.atomic_load_acq(dst) } - else when order == .Sequentially_Consistent { return intrinsics.atomic_load(dst) } - else when order == .Release { #panic("there is no such thing as a release load") } - else when order == .Acquire_Release { #panic("there is no such thing as an acquire/release load") } - else { #panic("unknown order") } -} - -atomic_swap :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T { - when order == .Relaxed { return intrinsics.atomic_xchg_relaxed(dst, val) } - else when order == .Release { return intrinsics.atomic_xchg_rel(dst, val) } - else when order == .Acquire { return intrinsics.atomic_xchg_acq(dst, val) } - else when order == .Acquire_Release { return intrinsics.atomic_xchg_acqrel(dst, val) } - else when order == .Sequentially_Consistent { return intrinsics.atomic_xchg(dst, val) } - else { #panic("unknown order") } -} - -atomic_compare_exchange :: #force_inline proc(dst: ^$T, old, new: T, $success, $failure: Ordering) -> (val: T, ok: bool) { - when failure == .Relaxed { - when success == .Relaxed { return intrinsics.atomic_cxchg_relaxed(dst, old, new) } - else when success == .Acquire { return intrinsics.atomic_cxchg_acq_failrelaxed(dst, old, new) } - else when success == .Acquire_Release { return intrinsics.atomic_cxchg_acqrel_failrelaxed(dst, old, new) } - else when success == .Sequentially_Consistent { return intrinsics.atomic_cxchg_failrelaxed(dst, old, new) } - else when success == .Release { return intrinsics.atomic_cxchg_rel(dst, old, new) } - else { #panic("an unknown ordering combination") } - } else when failure == .Acquire { - when success == .Release { return intrinsics.atomic_cxchg_acqrel(dst, old, new) } - else when success == .Acquire { return intrinsics.atomic_cxchg_acq(dst, old, new) } - else { #panic("an unknown ordering combination") } - } else when failure == .Sequentially_Consistent { - when success == .Sequentially_Consistent { return intrinsics.atomic_cxchg(dst, old, new) } - else { #panic("an unknown ordering combination") } - } else when failure == .Acquire_Release { - #panic("there is not such thing as an acquire/release failure ordering") - } else when failure == .Release { - when success == .Acquire { return instrinsics.atomic_cxchg_failacq(dst, old, new) } - else { #panic("an unknown ordering combination") } - } else { - return T{}, false - } - -} - -atomic_compare_exchange_weak :: #force_inline proc(dst: ^$T, old, new: T, $success, $failure: Ordering) -> (val: T, ok: bool) { - when failure == .Relaxed { - when success == .Relaxed { return intrinsics.atomic_cxchgweak_relaxed(dst, old, new) } - else when success == .Acquire { return intrinsics.atomic_cxchgweak_acq_failrelaxed(dst, old, new) } - else when success == .Acquire_Release { return intrinsics.atomic_cxchgweak_acqrel_failrelaxed(dst, old, new) } - else when success == .Sequentially_Consistent { return intrinsics.atomic_cxchgweak_failrelaxed(dst, old, new) } - else when success == .Release { return intrinsics.atomic_cxchgweak_rel(dst, old, new) } - else { #panic("an unknown ordering combination") } - } else when failure == .Acquire { - when success == .Release { return intrinsics.atomic_cxchgweak_acqrel(dst, old, new) } - else when success == .Acquire { return intrinsics.atomic_cxchgweak_acq(dst, old, new) } - else { #panic("an unknown ordering combination") } - } else when failure == .Sequentially_Consistent { - when success == .Sequentially_Consistent { return intrinsics.atomic_cxchgweak(dst, old, new) } - else { #panic("an unknown ordering combination") } - } else when failure == .Acquire_Release { - #panic("there is not such thing as an acquire/release failure ordering") - } else when failure == .Release { - when success == .Acquire { return intrinsics.atomic_cxchgweak_failacq(dst, old, new) } - else { #panic("an unknown ordering combination") } - } else { - return T{}, false - } - -} - - -atomic_add :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T { - when order == .Relaxed { return intrinsics.atomic_add_relaxed(dst, val) } - else when order == .Release { return intrinsics.atomic_add_rel(dst, val) } - else when order == .Acquire { return intrinsics.atomic_add_acq(dst, val) } - else when order == .Acquire_Release { return intrinsics.atomic_add_acqrel(dst, val) } - else when order == .Sequentially_Consistent { return intrinsics.atomic_add(dst, val) } - else { #panic("unknown order") } -} - -atomic_sub :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T { - when order == .Relaxed { return intrinsics.atomic_sub_relaxed(dst, val) } - else when order == .Release { return intrinsics.atomic_sub_rel(dst, val) } - else when order == .Acquire { return intrinsics.atomic_sub_acq(dst, val) } - else when order == .Acquire_Release { return intrinsics.atomic_sub_acqrel(dst, val) } - else when order == .Sequentially_Consistent { return intrinsics.atomic_sub(dst, val) } - else { #panic("unknown order") } -} - -atomic_and :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T { - when order == .Relaxed { return intrinsics.atomic_and_relaxed(dst, val) } - else when order == .Release { return intrinsics.atomic_and_rel(dst, val) } - else when order == .Acquire { return intrinsics.atomic_and_acq(dst, val) } - else when order == .Acquire_Release { return intrinsics.atomic_and_acqrel(dst, val) } - else when order == .Sequentially_Consistent { return intrinsics.atomic_and(dst, val) } - else { #panic("unknown order") } -} - -atomic_nand :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T { - when order == .Relaxed { return intrinsics.atomic_nand_relaxed(dst, val) } - else when order == .Release { return intrinsics.atomic_nand_rel(dst, val) } - else when order == .Acquire { return intrinsics.atomic_nand_acq(dst, val) } - else when order == .Acquire_Release { return intrinsics.atomic_nand_acqrel(dst, val) } - else when order == .Sequentially_Consistent { return intrinsics.atomic_nand(dst, val) } - else { #panic("unknown order") } -} - -atomic_or :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T { - when order == .Relaxed { return intrinsics.atomic_or_relaxed(dst, val) } - else when order == .Release { return intrinsics.atomic_or_rel(dst, val) } - else when order == .Acquire { return intrinsics.atomic_or_acq(dst, val) } - else when order == .Acquire_Release { return intrinsics.atomic_or_acqrel(dst, val) } - else when order == .Sequentially_Consistent { return intrinsics.atomic_or(dst, val) } - else { #panic("unknown order") } -} - -atomic_xor :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T { - when order == .Relaxed { return intrinsics.atomic_xor_relaxed(dst, val) } - else when order == .Release { return intrinsics.atomic_xor_rel(dst, val) } - else when order == .Acquire { return intrinsics.atomic_xor_acq(dst, val) } - else when order == .Acquire_Release { return intrinsics.atomic_xor_acqrel(dst, val) } - else when order == .Sequentially_Consistent { return intrinsics.atomic_xor(dst, val) } - else { #panic("unknown order") } -} +atomic_thread_fence :: intrinsics.atomic_thread_fence +atomic_signal_fence :: intrinsics.atomic_signal_fence +atomic_store :: intrinsics.atomic_store +atomic_store_explicit :: intrinsics.atomic_store_explicit +atomic_load :: intrinsics.atomic_load +atomic_load_explicit :: intrinsics.atomic_load_explicit +atomic_add :: intrinsics.atomic_add +atomic_add_explicit :: intrinsics.atomic_add_explicit +atomic_sub :: intrinsics.atomic_sub +atomic_sub_explicit :: intrinsics.atomic_sub_explicit +atomic_and :: intrinsics.atomic_and +atomic_and_explicit :: intrinsics.atomic_and_explicit +atomic_nand :: intrinsics.atomic_nand +atomic_nand_explicit :: intrinsics.atomic_nand_explicit +atomic_or :: intrinsics.atomic_or +atomic_or_explicit :: intrinsics.atomic_or_explicit +atomic_xor :: intrinsics.atomic_xor +atomic_xor_explicit :: intrinsics.atomic_xor_explicit +atomic_exchange :: intrinsics.atomic_exchange +atomic_exchange_explicit :: intrinsics.atomic_exchange_explicit +// Returns value and optional ok boolean +atomic_compare_exchange_strong :: intrinsics.atomic_compare_exchange_strong +atomic_compare_exchange_strong_explicit :: intrinsics.atomic_compare_exchange_strong_explicit +atomic_compare_exchange_weak :: intrinsics.atomic_compare_exchange_weak +atomic_compare_exchange_weak_explicit :: intrinsics.atomic_compare_exchange_weak_explicit \ No newline at end of file diff --git a/core/sync/barrier.odin b/core/sync/barrier.odin deleted file mode 100644 index 13c870a3b..000000000 --- a/core/sync/barrier.odin +++ /dev/null @@ -1,80 +0,0 @@ -package sync - - -/* -A barrier enabling multiple threads to synchronize the beginning of some computation -Example: - - package example - - import "core:fmt" - import "core:sync" - import "core:thread" - - barrier := &sync.Barrier{}; - - main :: proc() { - fmt.println("Start"); - - THREAD_COUNT :: 4; - threads: [THREAD_COUNT]^thread.Thread; - - sync.barrier_init(barrier, THREAD_COUNT); - defer sync.barrier_destroy(barrier); - - - for _, i in threads { - threads[i] = thread.create_and_start(proc(t: ^thread.Thread) { - // Same messages will be printed together but without any interleaving - fmt.println("Getting ready!"); - sync.barrier_wait(barrier); - fmt.println("Off their marks they go!"); - }); - } - - for t in threads { - thread.destroy(t); // join and free thread - } - fmt.println("Finished"); - } -*/ -Barrier :: struct { - mutex: Blocking_Mutex, - cond: Condition, - index: int, - generation_id: int, - thread_count: int, -} - -barrier_init :: proc(b: ^Barrier, thread_count: int) { - blocking_mutex_init(&b.mutex) - condition_init(&b.cond, &b.mutex) - b.index = 0 - b.generation_id = 0 - b.thread_count = thread_count -} - -barrier_destroy :: proc(b: ^Barrier) { - blocking_mutex_destroy(&b.mutex) - condition_destroy(&b.cond) -} - -// Block the current thread until all threads have rendezvoused -// Barrier can be reused after all threads rendezvoused once, and can be used continuously -barrier_wait :: proc(b: ^Barrier) -> (is_leader: bool) { - blocking_mutex_lock(&b.mutex) - defer blocking_mutex_unlock(&b.mutex) - local_gen := b.generation_id - b.index += 1 - if b.index < b.thread_count { - for local_gen == b.generation_id && b.index < b.thread_count { - condition_wait_for(&b.cond) - } - return false - } - - b.index = 0 - b.generation_id += 1 - condition_broadcast(&b.cond) - return true -} diff --git a/core/sync/channel.odin b/core/sync/channel.odin deleted file mode 100644 index 82b9504f4..000000000 --- a/core/sync/channel.odin +++ /dev/null @@ -1,889 +0,0 @@ -package sync - -import "core:mem" -import "core:time" -import "core:intrinsics" -import "core:math/rand" - -_, _ :: time, rand - -Channel_Direction :: enum i8 { - Both = 0, - Send = +1, - Recv = -1, -} - -Channel :: struct($T: typeid, $Direction := Channel_Direction.Both) { - using _internal: ^Raw_Channel, -} - -channel_init :: proc(ch: ^$C/Channel($T, $D), cap := 0, allocator := context.allocator) { - context.allocator = allocator - ch._internal = raw_channel_create(size_of(T), align_of(T), cap) - return -} - -channel_make :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T, .Both)) { - context.allocator = allocator - ch._internal = raw_channel_create(size_of(T), align_of(T), cap) - return -} - -channel_make_send :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T, .Send)) { - context.allocator = allocator - ch._internal = raw_channel_create(size_of(T), align_of(T), cap) - return -} -channel_make_recv :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T, .Recv)) { - context.allocator = allocator - ch._internal = raw_channel_create(size_of(T), align_of(T), cap) - return -} - -channel_destroy :: proc(ch: $C/Channel($T, $D)) { - raw_channel_destroy(ch._internal) -} - -channel_as_send :: proc(ch: $C/Channel($T, .Both)) -> (res: Channel(T, .Send)) { - res._internal = ch._internal - return -} - -channel_as_recv :: proc(ch: $C/Channel($T, .Both)) -> (res: Channel(T, .Recv)) { - res._internal = ch._internal - return -} - - -channel_len :: proc(ch: $C/Channel($T, $D)) -> int { - return ch._internal.len if ch._internal != nil else 0 -} -channel_cap :: proc(ch: $C/Channel($T, $D)) -> int { - return ch._internal.cap if ch._internal != nil else 0 -} - - -channel_send :: proc(ch: $C/Channel($T, $D), msg: T, loc := #caller_location) where D >= .Both { - msg := msg - _ = raw_channel_send_impl(ch._internal, &msg, /*block*/true, loc) -} -channel_try_send :: proc(ch: $C/Channel($T, $D), msg: T, loc := #caller_location) -> bool where D >= .Both { - msg := msg - return raw_channel_send_impl(ch._internal, &msg, /*block*/false, loc) -} - -channel_recv :: proc(ch: $C/Channel($T, $D), loc := #caller_location) -> (msg: T) where D <= .Both { - c := ch._internal - if c == nil { - panic(message="cannot recv message; channel is nil", loc=loc) - } - mutex_lock(&c.mutex) - raw_channel_recv_impl(c, &msg, loc) - mutex_unlock(&c.mutex) - return -} -channel_try_recv :: proc(ch: $C/Channel($T, $D), loc := #caller_location) -> (msg: T, ok: bool) where D <= .Both { - c := ch._internal - if c != nil && mutex_try_lock(&c.mutex) { - if c.len > 0 { - raw_channel_recv_impl(c, &msg, loc) - ok = true - } - mutex_unlock(&c.mutex) - } - return -} -channel_try_recv_ptr :: proc(ch: $C/Channel($T, $D), msg: ^T, loc := #caller_location) -> (ok: bool) where D <= .Both { - res: T - res, ok = channel_try_recv(ch, loc) - if ok && msg != nil { - msg^ = res - } - return -} - - -channel_is_nil :: proc(ch: $C/Channel($T, $D)) -> bool { - return ch._internal == nil -} -channel_is_open :: proc(ch: $C/Channel($T, $D)) -> bool { - c := ch._internal - return c != nil && !c.closed -} - - -channel_eq :: proc(a, b: $C/Channel($T, $D)) -> bool { - return a._internal == b._internal -} -channel_ne :: proc(a, b: $C/Channel($T, $D)) -> bool { - return a._internal != b._internal -} - - -channel_can_send :: proc(ch: $C/Channel($T, $D)) -> (ok: bool) where D >= .Both { - return raw_channel_can_send(ch._internal) -} -channel_can_recv :: proc(ch: $C/Channel($T, $D)) -> (ok: bool) where D <= .Both { - return raw_channel_can_recv(ch._internal) -} - - -channel_peek :: proc(ch: $C/Channel($T, $D)) -> int { - c := ch._internal - if c == nil { - return -1 - } - if intrinsics.atomic_load(&c.closed) { - return -1 - } - return intrinsics.atomic_load(&c.len) -} - - -channel_close :: proc(ch: $C/Channel($T, $D), loc := #caller_location) { - raw_channel_close(ch._internal, loc) -} - - -channel_iterator :: proc(ch: $C/Channel($T, $D)) -> (msg: T, ok: bool) where D <= .Both { - c := ch._internal - if c == nil { - return - } - - if !c.closed || c.len > 0 { - msg, ok = channel_recv(ch), true - } - return -} -channel_drain :: proc(ch: $C/Channel($T, $D)) where D >= .Both { - raw_channel_drain(ch._internal) -} - - -channel_move :: proc(dst: $C1/Channel($T, $D1) src: $C2/Channel(T, $D2)) where D1 <= .Both, D2 >= .Both { - for msg in channel_iterator(src) { - channel_send(dst, msg) - } -} - - -Raw_Channel_Wait_Queue :: struct { - next: ^Raw_Channel_Wait_Queue, - state: ^uintptr, -} - - -Raw_Channel :: struct { - closed: bool, - ready: bool, // ready to recv - data_offset: u16, // data is stored at the end of this data structure - elem_size: u32, - len, cap: int, - read, write: int, - mutex: Mutex, - cond: Condition, - allocator: mem.Allocator, - - sendq: ^Raw_Channel_Wait_Queue, - recvq: ^Raw_Channel_Wait_Queue, -} - -raw_channel_wait_queue_insert :: proc(head: ^^Raw_Channel_Wait_Queue, val: ^Raw_Channel_Wait_Queue) { - val.next = head^ - head^ = val -} -raw_channel_wait_queue_remove :: proc(head: ^^Raw_Channel_Wait_Queue, val: ^Raw_Channel_Wait_Queue) { - p := head - for p^ != nil && p^ != val { - p = &p^.next - } - if p != nil { - p^ = p^.next - } -} - - -raw_channel_create :: proc(elem_size, elem_align: int, cap := 0) -> ^Raw_Channel { - assert(int(u32(elem_size)) == elem_size) - - s := size_of(Raw_Channel) - s = mem.align_forward_int(s, elem_align) - data_offset := uintptr(s) - s += elem_size * max(cap, 1) - - a := max(elem_align, align_of(Raw_Channel)) - - c := (^Raw_Channel)(mem.alloc(s, a)) - if c == nil { - return nil - } - - c.data_offset = u16(data_offset) - c.elem_size = u32(elem_size) - c.len, c.cap = 0, max(cap, 0) - c.read, c.write = 0, 0 - mutex_init(&c.mutex) - condition_init(&c.cond, &c.mutex) - c.allocator = context.allocator - c.closed = false - - return c -} - - -raw_channel_destroy :: proc(c: ^Raw_Channel) { - if c == nil { - return - } - context.allocator = c.allocator - intrinsics.atomic_store(&c.closed, true) - - condition_destroy(&c.cond) - mutex_destroy(&c.mutex) - free(c) -} - -raw_channel_close :: proc(c: ^Raw_Channel, loc := #caller_location) { - if c == nil { - panic(message="cannot close nil channel", loc=loc) - } - mutex_lock(&c.mutex) - defer mutex_unlock(&c.mutex) - intrinsics.atomic_store(&c.closed, true) - - // Release readers and writers - raw_channel_wait_queue_broadcast(c.recvq) - raw_channel_wait_queue_broadcast(c.sendq) - condition_broadcast(&c.cond) -} - - - -raw_channel_send_impl :: proc(c: ^Raw_Channel, msg: rawptr, block: bool, loc := #caller_location) -> bool { - send :: proc(c: ^Raw_Channel, src: rawptr) { - data := uintptr(c) + uintptr(c.data_offset) - dst := data + uintptr(c.write * int(c.elem_size)) - mem.copy(rawptr(dst), src, int(c.elem_size)) - c.len += 1 - c.write = (c.write + 1) % max(c.cap, 1) - } - - switch { - case c == nil: - panic(message="cannot send message; channel is nil", loc=loc) - case c.closed: - panic(message="cannot send message; channel is closed", loc=loc) - } - - mutex_lock(&c.mutex) - defer mutex_unlock(&c.mutex) - - if c.cap > 0 { - if !block && c.len >= c.cap { - return false - } - - for c.len >= c.cap { - condition_wait_for(&c.cond) - } - } else if c.len > 0 { // TODO(bill): determine correct behaviour - if !block { - return false - } - condition_wait_for(&c.cond) - } else if c.len == 0 && !block { - return false - } - - send(c, msg) - condition_signal(&c.cond) - raw_channel_wait_queue_signal(c.recvq) - - return true -} - -raw_channel_recv_impl :: proc(c: ^Raw_Channel, res: rawptr, loc := #caller_location) { - recv :: proc(c: ^Raw_Channel, dst: rawptr, loc := #caller_location) { - if c.len < 1 { - panic(message="cannot recv message; channel is empty", loc=loc) - } - c.len -= 1 - - data := uintptr(c) + uintptr(c.data_offset) - src := data + uintptr(c.read * int(c.elem_size)) - mem.copy(dst, rawptr(src), int(c.elem_size)) - c.read = (c.read + 1) % max(c.cap, 1) - } - - if c == nil { - panic(message="cannot recv message; channel is nil", loc=loc) - } - intrinsics.atomic_store(&c.ready, true) - for c.len < 1 { - raw_channel_wait_queue_signal(c.sendq) - condition_wait_for(&c.cond) - } - intrinsics.atomic_store(&c.ready, false) - recv(c, res, loc) - if c.cap > 0 { - if c.len == c.cap - 1 { - // NOTE(bill): Only signal on the last one - condition_signal(&c.cond) - } - } else { - condition_signal(&c.cond) - } -} - - -raw_channel_can_send :: proc(c: ^Raw_Channel) -> (ok: bool) { - if c == nil { - return false - } - mutex_lock(&c.mutex) - switch { - case c.closed: - ok = false - case c.cap > 0: - ok = c.ready && c.len < c.cap - case: - ok = c.ready && c.len == 0 - } - mutex_unlock(&c.mutex) - return -} -raw_channel_can_recv :: proc(c: ^Raw_Channel) -> (ok: bool) { - if c == nil { - return false - } - mutex_lock(&c.mutex) - ok = c.len > 0 - mutex_unlock(&c.mutex) - return -} - - -raw_channel_drain :: proc(c: ^Raw_Channel) { - if c == nil { - return - } - mutex_lock(&c.mutex) - c.len = 0 - c.read = 0 - c.write = 0 - mutex_unlock(&c.mutex) -} - - - -MAX_SELECT_CHANNELS :: 64 -SELECT_MAX_TIMEOUT :: max(time.Duration) - -Select_Command :: enum { - Recv, - Send, -} - -Select_Channel :: struct { - channel: ^Raw_Channel, - command: Select_Command, -} - - - -select :: proc(channels: ..Select_Channel) -> (index: int) { - return select_timeout(SELECT_MAX_TIMEOUT, ..channels) -} -select_timeout :: proc(timeout: time.Duration, channels: ..Select_Channel) -> (index: int) { - switch len(channels) { - case 0: - panic("sync: select with no channels") - } - - assert(len(channels) <= MAX_SELECT_CHANNELS) - - backing: [MAX_SELECT_CHANNELS]int - queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue - candidates := backing[:] - cap := len(channels) - candidates = candidates[:cap] - - count := u32(0) - for c, i in channels { - if c.channel == nil { - continue - } - switch c.command { - case .Recv: - if raw_channel_can_recv(c.channel) { - candidates[count] = i - count += 1 - } - case .Send: - if raw_channel_can_send(c.channel) { - candidates[count] = i - count += 1 - } - } - } - - if count == 0 { - wait_state: uintptr = 0 - for _, i in channels { - q := &queues[i] - q.state = &wait_state - } - - for c, i in channels { - if c.channel == nil { - continue - } - q := &queues[i] - switch c.command { - case .Recv: raw_channel_wait_queue_insert(&c.channel.recvq, q) - case .Send: raw_channel_wait_queue_insert(&c.channel.sendq, q) - } - } - raw_channel_wait_queue_wait_on(&wait_state, timeout) - for c, i in channels { - if c.channel == nil { - continue - } - q := &queues[i] - switch c.command { - case .Recv: raw_channel_wait_queue_remove(&c.channel.recvq, q) - case .Send: raw_channel_wait_queue_remove(&c.channel.sendq, q) - } - } - - for c, i in channels { - switch c.command { - case .Recv: - if raw_channel_can_recv(c.channel) { - candidates[count] = i - count += 1 - } - case .Send: - if raw_channel_can_send(c.channel) { - candidates[count] = i - count += 1 - } - } - } - if count == 0 && timeout == SELECT_MAX_TIMEOUT { - index = -1 - return - } - - assert(count != 0) - } - - t := time.now() - r := rand.create(transmute(u64)t) - i := rand.uint32(&r) - - index = candidates[i % count] - return -} - -select_recv :: proc(channels: ..^Raw_Channel) -> (index: int) { - switch len(channels) { - case 0: - panic("sync: select with no channels") - } - - assert(len(channels) <= MAX_SELECT_CHANNELS) - - backing: [MAX_SELECT_CHANNELS]int - queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue - candidates := backing[:] - cap := len(channels) - candidates = candidates[:cap] - - count := u32(0) - for c, i in channels { - if raw_channel_can_recv(c) { - candidates[count] = i - count += 1 - } - } - - if count == 0 { - state: uintptr - for c, i in channels { - q := &queues[i] - q.state = &state - raw_channel_wait_queue_insert(&c.recvq, q) - } - raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT) - for c, i in channels { - q := &queues[i] - raw_channel_wait_queue_remove(&c.recvq, q) - } - - for c, i in channels { - if raw_channel_can_recv(c) { - candidates[count] = i - count += 1 - } - } - assert(count != 0) - } - - t := time.now() - r := rand.create(transmute(u64)t) - i := rand.uint32(&r) - - index = candidates[i % count] - return -} - -select_recv_msg :: proc(channels: ..$C/Channel($T, $D)) -> (msg: T, index: int) { - switch len(channels) { - case 0: - panic("sync: select with no channels") - } - - assert(len(channels) <= MAX_SELECT_CHANNELS) - - queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue - candidates: [MAX_SELECT_CHANNELS]int - - count := u32(0) - for c, i in channels { - if raw_channel_can_recv(c) { - candidates[count] = i - count += 1 - } - } - - if count == 0 { - state: uintptr - for c, i in channels { - q := &queues[i] - q.state = &state - raw_channel_wait_queue_insert(&c.recvq, q) - } - raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT) - for c, i in channels { - q := &queues[i] - raw_channel_wait_queue_remove(&c.recvq, q) - } - - for c, i in channels { - if raw_channel_can_recv(c) { - candidates[count] = i - count += 1 - } - } - assert(count != 0) - } - - t := time.now() - r := rand.create(transmute(u64)t) - i := rand.uint32(&r) - - index = candidates[i % count] - msg = channel_recv(channels[index]) - - return -} - -select_send_msg :: proc(msg: $T, channels: ..$C/Channel(T, $D)) -> (index: int) { - switch len(channels) { - case 0: - panic("sync: select with no channels") - } - - assert(len(channels) <= MAX_SELECT_CHANNELS) - - backing: [MAX_SELECT_CHANNELS]int - queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue - candidates := backing[:] - cap := len(channels) - candidates = candidates[:cap] - - count := u32(0) - for c, i in channels { - if raw_channel_can_recv(c) { - candidates[count] = i - count += 1 - } - } - - if count == 0 { - state: uintptr - for c, i in channels { - q := &queues[i] - q.state = &state - raw_channel_wait_queue_insert(&c.recvq, q) - } - raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT) - for c, i in channels { - q := &queues[i] - raw_channel_wait_queue_remove(&c.recvq, q) - } - - for c, i in channels { - if raw_channel_can_recv(c) { - candidates[count] = i - count += 1 - } - } - assert(count != 0) - } - - t := time.now() - r := rand.create(transmute(u64)t) - i := rand.uint32(&r) - - index = candidates[i % count] - - if msg != nil { - channel_send(channels[index], msg) - } - - return -} - -select_send :: proc(channels: ..^Raw_Channel) -> (index: int) { - switch len(channels) { - case 0: - panic("sync: select with no channels") - } - - assert(len(channels) <= MAX_SELECT_CHANNELS) - candidates: [MAX_SELECT_CHANNELS]int - queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue - - count := u32(0) - for c, i in channels { - if raw_channel_can_send(c) { - candidates[count] = i - count += 1 - } - } - - if count == 0 { - state: uintptr - for c, i in channels { - q := &queues[i] - q.state = &state - raw_channel_wait_queue_insert(&c.sendq, q) - } - raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT) - for c, i in channels { - q := &queues[i] - raw_channel_wait_queue_remove(&c.sendq, q) - } - - for c, i in channels { - if raw_channel_can_send(c) { - candidates[count] = i - count += 1 - } - } - assert(count != 0) - } - - t := time.now() - r := rand.create(transmute(u64)t) - i := rand.uint32(&r) - - index = candidates[i % count] - return -} - -select_try :: proc(channels: ..Select_Channel) -> (index: int) { - switch len(channels) { - case 0: - panic("sync: select with no channels") - } - - assert(len(channels) <= MAX_SELECT_CHANNELS) - - backing: [MAX_SELECT_CHANNELS]int - candidates := backing[:] - cap := len(channels) - candidates = candidates[:cap] - - count := u32(0) - for c, i in channels { - switch c.command { - case .Recv: - if raw_channel_can_recv(c.channel) { - candidates[count] = i - count += 1 - } - case .Send: - if raw_channel_can_send(c.channel) { - candidates[count] = i - count += 1 - } - } - } - - if count == 0 { - index = -1 - return - } - - t := time.now() - r := rand.create(transmute(u64)t) - i := rand.uint32(&r) - - index = candidates[i % count] - return -} - - -select_try_recv :: proc(channels: ..^Raw_Channel) -> (index: int) { - switch len(channels) { - case 0: - index = -1 - return - case 1: - index = -1 - if raw_channel_can_recv(channels[0]) { - index = 0 - } - return - } - - assert(len(channels) <= MAX_SELECT_CHANNELS) - candidates: [MAX_SELECT_CHANNELS]int - - count := u32(0) - for c, i in channels { - if raw_channel_can_recv(c) { - candidates[count] = i - count += 1 - } - } - - if count == 0 { - index = -1 - return - } - - t := time.now() - r := rand.create(transmute(u64)t) - i := rand.uint32(&r) - - index = candidates[i % count] - return -} - - -select_try_send :: proc(channels: ..^Raw_Channel) -> (index: int) #no_bounds_check { - switch len(channels) { - case 0: - return -1 - case 1: - if raw_channel_can_send(channels[0]) { - return 0 - } - return -1 - } - - assert(len(channels) <= MAX_SELECT_CHANNELS) - candidates: [MAX_SELECT_CHANNELS]int - - count := u32(0) - for c, i in channels { - if raw_channel_can_send(c) { - candidates[count] = i - count += 1 - } - } - - if count == 0 { - index = -1 - return - } - - t := time.now() - r := rand.create(transmute(u64)t) - i := rand.uint32(&r) - - index = candidates[i % count] - return -} - -select_try_recv_msg :: proc(channels: ..$C/Channel($T, $D)) -> (msg: T, index: int) { - switch len(channels) { - case 0: - index = -1 - return - case 1: - ok: bool - if msg, ok = channel_try_recv(channels[0]); ok { - index = 0 - } - return - } - - assert(len(channels) <= MAX_SELECT_CHANNELS) - candidates: [MAX_SELECT_CHANNELS]int - - count := u32(0) - for c, i in channels { - if channel_can_recv(c) { - candidates[count] = i - count += 1 - } - } - - if count == 0 { - index = -1 - return - } - - t := time.now() - r := rand.create(transmute(u64)t) - i := rand.uint32(&r) - - index = candidates[i % count] - msg = channel_recv(channels[index]) - return -} - -select_try_send_msg :: proc(msg: $T, channels: ..$C/Channel(T, $D)) -> (index: int) { - index = -1 - switch len(channels) { - case 0: - return - case 1: - if channel_try_send(channels[0], msg) { - index = 0 - } - return - } - - - assert(len(channels) <= MAX_SELECT_CHANNELS) - candidates: [MAX_SELECT_CHANNELS]int - - count := u32(0) - for c, i in channels { - if raw_channel_can_send(c) { - candidates[count] = i - count += 1 - } - } - - if count == 0 { - index = -1 - return - } - - t := time.now() - r := rand.create(transmute(u64)t) - i := rand.uint32(&r) - - index = candidates[i % count] - channel_send(channels[index], msg) - return -} - diff --git a/core/sync/channel_unix.odin b/core/sync/channel_unix.odin deleted file mode 100644 index d6bac2d71..000000000 --- a/core/sync/channel_unix.odin +++ /dev/null @@ -1,16 +0,0 @@ -// +build linux, darwin, freebsd -package sync - -import "core:time" - -raw_channel_wait_queue_wait_on :: proc(state: ^uintptr, timeout: time.Duration) { - // stub -} - -raw_channel_wait_queue_signal :: proc(q: ^Raw_Channel_Wait_Queue) { - // stub -} - -raw_channel_wait_queue_broadcast :: proc(q: ^Raw_Channel_Wait_Queue) { - // stub -} diff --git a/core/sync/channel_windows.odin b/core/sync/channel_windows.odin deleted file mode 100644 index 5d469ffff..000000000 --- a/core/sync/channel_windows.odin +++ /dev/null @@ -1,33 +0,0 @@ -package sync - -import "core:intrinsics" -import win32 "core:sys/windows" -import "core:time" - -raw_channel_wait_queue_wait_on :: proc(state: ^uintptr, timeout: time.Duration) { - ms: win32.DWORD = win32.INFINITE - if max(time.Duration) != SELECT_MAX_TIMEOUT { - ms = win32.DWORD((max(time.duration_nanoseconds(timeout), 0) + 999999)/1000000) - } - - v := intrinsics.atomic_load(state) - for v == 0 { - win32.WaitOnAddress(state, &v, size_of(state^), ms) - v = intrinsics.atomic_load(state) - } - intrinsics.atomic_store(state, 0) -} - -raw_channel_wait_queue_signal :: proc(q: ^Raw_Channel_Wait_Queue) { - for x := q; x != nil; x = x.next { - intrinsics.atomic_add(x.state, 1) - win32.WakeByAddressSingle(x.state) - } -} - -raw_channel_wait_queue_broadcast :: proc(q: ^Raw_Channel_Wait_Queue) { - for x := q; x != nil; x = x.next { - intrinsics.atomic_add(x.state, 1) - win32.WakeByAddressAll(x.state) - } -} diff --git a/core/sync/sync2/extended.odin b/core/sync/extended.odin similarity index 66% rename from core/sync/sync2/extended.odin rename to core/sync/extended.odin index deb48a22d..49d296c90 100644 --- a/core/sync/sync2/extended.odin +++ b/core/sync/extended.odin @@ -1,4 +1,4 @@ -package sync2 +package sync import "core:time" @@ -16,8 +16,7 @@ wait_group_add :: proc(wg: ^Wait_Group, delta: int) { return } - mutex_lock(&wg.mutex) - defer mutex_unlock(&wg.mutex) + guard(&wg.mutex) atomic_add(&wg.counter, delta) if wg.counter < 0 { @@ -36,8 +35,7 @@ wait_group_done :: proc(wg: ^Wait_Group) { } wait_group_wait :: proc(wg: ^Wait_Group) { - mutex_lock(&wg.mutex) - defer mutex_unlock(&wg.mutex) + guard(&wg.mutex) if wg.counter != 0 { cond_wait(&wg.cond, &wg.mutex) @@ -51,8 +49,7 @@ wait_group_wait_with_timeout :: proc(wg: ^Wait_Group, duration: time.Duration) - if duration <= 0 { return false } - mutex_lock(&wg.mutex) - defer mutex_unlock(&wg.mutex) + guard(&wg.mutex) if wg.counter != 0 { if !cond_wait_with_timeout(&wg.cond, &wg.mutex, duration) { @@ -119,8 +116,7 @@ barrier_init :: proc(b: ^Barrier, thread_count: int) { // Block the current thread until all threads have rendezvoused // Barrier can be reused after all threads rendezvoused once, and can be used continuously barrier_wait :: proc(b: ^Barrier) -> (is_leader: bool) { - mutex_lock(&b.mutex) - defer mutex_unlock(&b.mutex) + guard(&b.mutex) local_gen := b.generation_id b.index += 1 if b.index < b.thread_count { @@ -146,10 +142,10 @@ Auto_Reset_Event :: struct { } auto_reset_event_signal :: proc(e: ^Auto_Reset_Event) { - old_status := atomic_load_relaxed(&e.status) + old_status := atomic_load_explicit(&e.status, .Relaxed) for { new_status := old_status + 1 if old_status < 1 else 1 - if _, ok := atomic_compare_exchange_weak_release(&e.status, old_status, new_status); ok { + if _, ok := atomic_compare_exchange_weak_explicit(&e.status, old_status, new_status, .Release, .Relaxed); ok { break } @@ -160,7 +156,7 @@ auto_reset_event_signal :: proc(e: ^Auto_Reset_Event) { } auto_reset_event_wait :: proc(e: ^Auto_Reset_Event) { - old_status := atomic_sub_acquire(&e.status, 1) + old_status := atomic_sub_explicit(&e.status, 1, .Acquire) if old_status < 1 { sema_wait(&e.sema) } @@ -174,14 +170,14 @@ Ticket_Mutex :: struct { } ticket_mutex_lock :: #force_inline proc(m: ^Ticket_Mutex) { - ticket := atomic_add_relaxed(&m.ticket, 1) - for ticket != atomic_load_acquire(&m.serving) { + ticket := atomic_add_explicit(&m.ticket, 1, .Relaxed) + for ticket != atomic_load_explicit(&m.serving, .Acquire) { cpu_relax() } } ticket_mutex_unlock :: #force_inline proc(m: ^Ticket_Mutex) { - atomic_add_relaxed(&m.serving, 1) + atomic_add_explicit(&m.serving, 1, .Relaxed) } @(deferred_in=ticket_mutex_unlock) ticket_mutex_guard :: proc(m: ^Ticket_Mutex) -> bool { @@ -196,18 +192,18 @@ Benaphore :: struct { } benaphore_lock :: proc(b: ^Benaphore) { - if atomic_add_acquire(&b.counter, 1) > 1 { + if atomic_add_explicit(&b.counter, 1, .Acquire) > 1 { sema_wait(&b.sema) } } benaphore_try_lock :: proc(b: ^Benaphore) -> bool { - v, _ := atomic_compare_exchange_strong_acquire(&b.counter, 1, 0) + v, _ := atomic_compare_exchange_strong_explicit(&b.counter, 0, 1, .Acquire, .Acquire) return v == 0 } benaphore_unlock :: proc(b: ^Benaphore) { - if atomic_sub_release(&b.counter, 1) > 0 { + if atomic_sub_explicit(&b.counter, 1, .Release) > 0 { sema_post(&b.sema) } } @@ -227,7 +223,7 @@ Recursive_Benaphore :: struct { recursive_benaphore_lock :: proc(b: ^Recursive_Benaphore) { tid := current_thread_id() - if atomic_add_acquire(&b.counter, 1) > 1 { + if atomic_add_explicit(&b.counter, 1, .Acquire) > 1 { if tid != b.owner { sema_wait(&b.sema) } @@ -240,10 +236,10 @@ recursive_benaphore_lock :: proc(b: ^Recursive_Benaphore) { recursive_benaphore_try_lock :: proc(b: ^Recursive_Benaphore) -> bool { tid := current_thread_id() if b.owner == tid { - atomic_add_acquire(&b.counter, 1) + atomic_add_explicit(&b.counter, 1, .Acquire) } - if v, _ := atomic_compare_exchange_strong_acquire(&b.counter, 1, 0); v != 0 { + if v, _ := atomic_compare_exchange_strong_explicit(&b.counter, 0, 1, .Acquire, .Acquire); v != 0 { return false } // inside the lock @@ -260,7 +256,7 @@ recursive_benaphore_unlock :: proc(b: ^Recursive_Benaphore) { if recursion == 0 { b.owner = 0 } - if atomic_sub_release(&b.counter, 1) > 0 { + if atomic_sub_explicit(&b.counter, 1, .Release) > 0 { if recursion == 0 { sema_post(&b.sema) } @@ -289,16 +285,71 @@ Once :: struct { once_do :: proc(o: ^Once, fn: proc()) { @(cold) do_slow :: proc(o: ^Once, fn: proc()) { - mutex_lock(&o.m) - defer mutex_unlock(&o.m) + guard(&o.m) if !o.done { fn() - atomic_store_release(&o.done, true) + atomic_store_explicit(&o.done, true, .Release) } } - if atomic_load_acquire(&o.done) == false { + if atomic_load_explicit(&o.done, .Acquire) == false { do_slow(o, fn) } } + + + +// A Parker is an associated token which is initially not present: +// * The `park` procedure blocks the current thread unless or until the token +// is available, at which point the token is consumed. +// * The `park_with_timeout` procedures works the same as `park` but only +// blocks for the specified duration. +// * The `unpark` procedure automatically makes the token available if it +// was not already. +Parker :: struct { + state: Futex, +} + +// Blocks the current thread until the token is made available. +// +// Assumes this is only called by the thread that owns the Parker. +park :: proc(p: ^Parker) { + EMPTY :: 0 + NOTIFIED :: 1 + PARKED :: max(u32) + if atomic_sub_explicit(&p.state, 1, .Acquire) == NOTIFIED { + return + } + for { + futex_wait(&p.state, PARKED) + if _, ok := atomic_compare_exchange_strong_explicit(&p.state, NOTIFIED, EMPTY, .Acquire, .Acquire); ok { + return + } + } +} + +// Blocks the current thread until the token is made available, but only +// for a limited duration. +// +// Assumes this is only called by the thread that owns the Parker +park_with_timeout :: proc(p: ^Parker, duration: time.Duration) { + EMPTY :: 0 + NOTIFIED :: 1 + PARKED :: max(u32) + if atomic_sub_explicit(&p.state, 1, .Acquire) == NOTIFIED { + return + } + futex_wait_with_timeout(&p.state, PARKED, duration) + atomic_exchange_explicit(&p.state, EMPTY, .Acquire) +} + +// Automatically makes thee token available if it was not already. +unpark :: proc(p: ^Parker) { + EMPTY :: 0 + NOTIFIED :: 1 + PARKED :: max(Futex) + if atomic_exchange_explicit(&p.state, NOTIFIED, .Release) == PARKED { + futex_signal(&p.state) + } +} \ No newline at end of file diff --git a/core/sync/sync2/futex_darwin.odin b/core/sync/futex_darwin.odin similarity index 96% rename from core/sync/sync2/futex_darwin.odin rename to core/sync/futex_darwin.odin index 9dad8d375..a106faa9c 100644 --- a/core/sync/sync2/futex_darwin.odin +++ b/core/sync/futex_darwin.odin @@ -1,6 +1,6 @@ //+private //+build darwin -package sync2 +package sync import "core:c" import "core:time" @@ -8,7 +8,7 @@ import "core:time" foreign import System "System.framework" foreign System { - __ulock_wait :: proc "c" (operation: u32, addr: rawptr, value: u64, timeout_ms: u32) -> c.int --- + __ulock_wait :: proc "c" (operation: u32, addr: rawptr, value: u64, timeout_us: u32) -> c.int --- __ulock_wait2 :: proc "c" (operation: u32, addr: rawptr, value: u64, timeout_ns: u64, value2: u64) -> c.int --- __ulock_wake :: proc "c" (operation: u32, addr: rawptr, wake_value: u64) -> c.int --- } diff --git a/core/sync/futex_freebsd.odin b/core/sync/futex_freebsd.odin new file mode 100644 index 000000000..07fba82a8 --- /dev/null +++ b/core/sync/futex_freebsd.odin @@ -0,0 +1,71 @@ +//+private +//+build freebsd +package sync + +import "core:c" +import "core:time" + +UMTX_OP_WAIT :: 2 +UMTX_OP_WAKE :: 3 + +ETIMEDOUT :: 60 + +foreign import libc "system:c" + +foreign libc { + _umtx_op :: proc "c" (obj: rawptr, op: c.int, val: c.ulong, uaddr: rawptr, uaddr2: rawptr) -> c.int --- + __error :: proc "c" () -> ^c.int --- +} + +_futex_wait :: proc(f: ^Futex, expected: u32) -> bool { + timeout := [2]i64{14400, 0} // 4 hours + for { + res := _umtx_op(f, UMTX_OP_WAIT, c.ulong(expected), nil, &timeout) + + if res != -1 { + return true + } + + if __error()^ == ETIMEDOUT { + continue + } + + panic("_futex_wait failure") + } + unreachable() +} + +_futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duration) -> bool { + if duration <= 0 { + return false + } + + timeout := [2]i64{i64(duration/1e9), i64(duration%1e9)} + + res := _umtx_op(f, UMTX_OP_WAIT, c.ulong(expected), nil, &timeout) + if res != -1 { + return true + } + + if __error()^ == ETIMEDOUT { + return false + } + + panic("_futex_wait_with_timeout failure") +} + +_futex_signal :: proc(f: ^Futex) { + res := _umtx_op(f, UMTX_OP_WAKE, 1, nil, nil) + + if res == -1 { + panic("_futex_signal failure") + } +} + +_futex_broadcast :: proc(f: ^Futex) { + res := _umtx_op(f, UMTX_OP_WAKE, c.ulong(max(i32)), nil, nil) + + if res == -1 { + panic("_futex_broadcast failure") + } +} diff --git a/core/sync/sync2/futex_linux.odin b/core/sync/futex_linux.odin similarity index 94% rename from core/sync/sync2/futex_linux.odin rename to core/sync/futex_linux.odin index fca28cace..c429a9d64 100644 --- a/core/sync/sync2/futex_linux.odin +++ b/core/sync/futex_linux.odin @@ -1,6 +1,6 @@ //+private //+build linux -package sync2 +package sync import "core:c" import "core:time" @@ -14,12 +14,6 @@ FUTEX_PRIVATE_FLAG :: 128 FUTEX_WAIT_PRIVATE :: (FUTEX_WAIT | FUTEX_PRIVATE_FLAG) FUTEX_WAKE_PRIVATE :: (FUTEX_WAKE | FUTEX_PRIVATE_FLAG) -foreign import libc "system:c" - -foreign libc { - __errno_location :: proc "c" () -> ^c.int --- -} - ESUCCESS :: 0 EINTR :: -4 EAGAIN :: -11 diff --git a/core/sync/futex_openbsd.odin b/core/sync/futex_openbsd.odin new file mode 100644 index 000000000..6ac9d3efb --- /dev/null +++ b/core/sync/futex_openbsd.odin @@ -0,0 +1,78 @@ +//+private +//+build openbsd +package sync + +import "core:c" +import "core:os" +import "core:time" + +FUTEX_WAIT :: 1 +FUTEX_WAKE :: 2 + +FUTEX_PRIVATE_FLAG :: 128 + +FUTEX_WAIT_PRIVATE :: (FUTEX_WAIT | FUTEX_PRIVATE_FLAG) +FUTEX_WAKE_PRIVATE :: (FUTEX_WAKE | FUTEX_PRIVATE_FLAG) + +foreign import libc "system:c" + +foreign libc { + @(link_name="futex") + _unix_futex :: proc "c" (f: ^Futex, op: c.int, val: u32, timeout: rawptr) -> c.int --- +} + +_futex_wait :: proc(f: ^Futex, expected: u32) -> bool { + res := _unix_futex(f, FUTEX_WAIT_PRIVATE, expected, nil) + + if res != -1 { + return true + } + + if os.Errno(os.get_last_error()) == os.ETIMEDOUT { + return false + } + + panic("futex_wait failure") +} + +_futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duration) -> bool { + if duration <= 0 { + return false + } + + timespec_t :: struct { + tv_sec: c.long, + tv_nsec: c.long, + } + + res := _unix_futex(f, FUTEX_WAIT_PRIVATE, expected, ×pec_t{ + tv_sec = (c.long)(duration/1e9), + tv_nsec = (c.long)(duration%1e9), + }) + + if res != -1 { + return true + } + + if os.Errno(os.get_last_error()) == os.ETIMEDOUT { + return false + } + + panic("futex_wait_with_timeout failure") +} + +_futex_signal :: proc(f: ^Futex) { + res := _unix_futex(f, FUTEX_WAKE_PRIVATE, 1, nil) + + if res == -1 { + panic("futex_wake_single failure") + } +} + +_futex_broadcast :: proc(f: ^Futex) { + res := _unix_futex(f, FUTEX_WAKE_PRIVATE, u32(max(i32)), nil) + + if res == -1 { + panic("_futex_wake_all failure") + } +} diff --git a/core/sync/futex_wasm.odin b/core/sync/futex_wasm.odin new file mode 100644 index 000000000..a32935143 --- /dev/null +++ b/core/sync/futex_wasm.odin @@ -0,0 +1,36 @@ +//+private +//+build wasm32, wasm64 +package sync + +import "core:intrinsics" +import "core:time" + +_futex_wait :: proc(f: ^Futex, expected: u32) -> bool { + s := intrinsics.wasm_memory_atomic_wait32((^u32)(f), expected, -1) + return s != 0 +} + +_futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duration) -> bool { + s := intrinsics.wasm_memory_atomic_wait32((^u32)(f), expected, i64(duration)) + return s != 0 + +} + +_futex_signal :: proc(f: ^Futex) { + loop: for { + s := intrinsics.wasm_memory_atomic_notify32((^u32)(f), 1) + if s >= 1 { + return + } + } +} + +_futex_broadcast :: proc(f: ^Futex) { + loop: for { + s := intrinsics.wasm_memory_atomic_notify32((^u32)(f), ~u32(0)) + if s >= 0 { + return + } + } +} + diff --git a/core/sync/sync2/futex_windows.odin b/core/sync/futex_windows.odin similarity index 56% rename from core/sync/sync2/futex_windows.odin rename to core/sync/futex_windows.odin index 200a119ff..ce662ba9e 100644 --- a/core/sync/sync2/futex_windows.odin +++ b/core/sync/futex_windows.odin @@ -1,32 +1,32 @@ //+private //+build windows -package sync2 +package sync import "core:time" foreign import Synchronization "system:Synchronization.lib" - @(default_calling_convention="stdcall") foreign Synchronization { - WaitOnAddress :: proc(Address: rawptr, CompareAddress: rawptr, AddressSize: uint, Timeout: u32) -> b32 --- WakeByAddressSingle :: proc(Address: rawptr) --- WakeByAddressAll :: proc(Address: rawptr) --- } - +foreign import Ntdll "system:Ntdll.lib" +@(default_calling_convention="stdcall") +foreign Ntdll { + RtlWaitOnAddress :: proc(Address: rawptr, CompareAddress: rawptr, AddressSize: uint, Timeout: ^i64) -> i32 --- +} _futex_wait :: proc(f: ^Futex, expect: u32) -> bool { expect := expect - return bool(WaitOnAddress(f, &expect, size_of(expect), ~u32(0))) + return 0 == RtlWaitOnAddress(f, &expect, size_of(expect), nil) } _futex_wait_with_timeout :: proc(f: ^Futex, expect: u32, duration: time.Duration) -> bool { expect := expect - timeout := u32(0) - if duration > 0 { - timeout = u32(duration/1e6) - } - return bool(WaitOnAddress(f, &expect, size_of(expect), timeout)) + // NOTE(bill): for some bizarre reason, this has be a negative number + timeout := -i64(duration / 100) + return 0 == RtlWaitOnAddress(f, &expect, size_of(expect), &timeout) } _futex_signal :: proc(f: ^Futex) { diff --git a/core/sync/sync2/primitives.odin b/core/sync/primitives.odin similarity index 97% rename from core/sync/sync2/primitives.odin rename to core/sync/primitives.odin index 6d056d439..bfbdc6f9b 100644 --- a/core/sync/sync2/primitives.odin +++ b/core/sync/primitives.odin @@ -1,4 +1,4 @@ -package sync2 +package sync import "core:time" @@ -195,7 +195,7 @@ sema_wait_with_timeout :: proc(s: ^Sema, duration: time.Duration) -> bool { Futex :: distinct u32 futex_wait :: proc(f: ^Futex, expected: u32) { - if u32(atomic_load(f)) != expected { + if u32(atomic_load_explicit(f, .Acquire)) != expected { return } @@ -204,7 +204,7 @@ futex_wait :: proc(f: ^Futex, expected: u32) { // returns true if the wait happened within the duration, false if it exceeded the time duration futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duration) -> bool { - if u32(atomic_load(f)) != expected { + if u32(atomic_load_explicit(f, .Acquire)) != expected { return true } if duration <= 0 { diff --git a/core/sync/sync2/primitives_atomic.odin b/core/sync/primitives_atomic.odin similarity index 71% rename from core/sync/sync2/primitives_atomic.odin rename to core/sync/primitives_atomic.odin index 5fc6fba85..a0f08c412 100644 --- a/core/sync/sync2/primitives_atomic.odin +++ b/core/sync/primitives_atomic.odin @@ -1,4 +1,4 @@ -package sync2 +package sync import "core:time" @@ -24,7 +24,7 @@ atomic_mutex_lock :: proc(m: ^Atomic_Mutex) { new_state := curr_state // Make a copy of it spin_lock: for spin in 0.. bool { - _, ok := atomic_compare_exchange_strong_acquire(&m.state, .Unlocked, .Locked) + _, ok := atomic_compare_exchange_strong_explicit(&m.state, .Unlocked, .Locked, .Acquire, .Consume) return ok } @@ -282,118 +281,39 @@ atomic_recursive_mutex_guard :: proc(m: ^Atomic_Recursive_Mutex) -> bool { - -@(private="file") -Queue_Item :: struct { - next: ^Queue_Item, - futex: Futex, -} - -@(private="file") -queue_item_wait :: proc(item: ^Queue_Item) { - for atomic_load_acquire(&item.futex) == 0 { - futex_wait(&item.futex, 0) - cpu_relax() - } -} -@(private="file") -queue_item_wait_with_timeout :: proc(item: ^Queue_Item, duration: time.Duration) -> bool { - start := time.tick_now() - for atomic_load_acquire(&item.futex) == 0 { - remaining := duration - time.tick_since(start) - if remaining < 0 { - return false - } - if !futex_wait_with_timeout(&item.futex, 0, remaining) { - return false - } - cpu_relax() - } - return true -} -@(private="file") -queue_item_signal :: proc(item: ^Queue_Item) { - atomic_store_release(&item.futex, 1) - futex_signal(&item.futex) -} - - // Atomic_Cond implements a condition variable, a rendezvous point for threads // waiting for signalling the occurence of an event // // An Atomic_Cond must not be copied after first use Atomic_Cond :: struct { - queue_mutex: Atomic_Mutex, - queue_head: ^Queue_Item, - pending: bool, + state: Futex, } atomic_cond_wait :: proc(c: ^Atomic_Cond, m: ^Atomic_Mutex) { - waiter := &Queue_Item{} + state := u32(atomic_load_explicit(&c.state, .Relaxed)) + unlock(m) + futex_wait(&c.state, state) + lock(m) - atomic_mutex_lock(&c.queue_mutex) - waiter.next = c.queue_head - c.queue_head = waiter - - atomic_store(&c.pending, true) - atomic_mutex_unlock(&c.queue_mutex) - - atomic_mutex_unlock(m) - queue_item_wait(waiter) - atomic_mutex_lock(m) } atomic_cond_wait_with_timeout :: proc(c: ^Atomic_Cond, m: ^Atomic_Mutex, duration: time.Duration) -> (ok: bool) { - waiter := &Queue_Item{} - - atomic_mutex_lock(&c.queue_mutex) - waiter.next = c.queue_head - c.queue_head = waiter - - atomic_store(&c.pending, true) - atomic_mutex_unlock(&c.queue_mutex) - - atomic_mutex_unlock(m) - ok = queue_item_wait_with_timeout(waiter, duration) - atomic_mutex_lock(m) + state := u32(atomic_load_explicit(&c.state, .Relaxed)) + unlock(m) + ok = futex_wait_with_timeout(&c.state, state, duration) + lock(m) return } atomic_cond_signal :: proc(c: ^Atomic_Cond) { - if !atomic_load(&c.pending) { - return - } - - atomic_mutex_lock(&c.queue_mutex) - waiter := c.queue_head - if c.queue_head != nil { - c.queue_head = c.queue_head.next - } - atomic_store(&c.pending, c.queue_head != nil) - atomic_mutex_unlock(&c.queue_mutex) - - if waiter != nil { - queue_item_signal(waiter) - } + atomic_add_explicit(&c.state, 1, .Release) + futex_signal(&c.state) } atomic_cond_broadcast :: proc(c: ^Atomic_Cond) { - if !atomic_load(&c.pending) { - return - } - - atomic_store(&c.pending, false) - - atomic_mutex_lock(&c.queue_mutex) - waiters := c.queue_head - c.queue_head = nil - atomic_mutex_unlock(&c.queue_mutex) - - for waiters != nil { - queue_item_signal(waiters) - waiters = waiters.next - } + atomic_add_explicit(&c.state, 1, .Release) + futex_broadcast(&c.state) } // When waited upon, blocks until the internal count is greater than zero, then subtracts one. @@ -401,30 +321,28 @@ atomic_cond_broadcast :: proc(c: ^Atomic_Cond) { // // An Atomic_Sema must not be copied after first use Atomic_Sema :: struct { - mutex: Atomic_Mutex, - cond: Atomic_Cond, - count: int, + count: Futex, } atomic_sema_post :: proc(s: ^Atomic_Sema, count := 1) { - atomic_mutex_lock(&s.mutex) - defer atomic_mutex_unlock(&s.mutex) - - s.count += count - atomic_cond_signal(&s.cond) + atomic_add_explicit(&s.count, Futex(count), .Release) + if count == 1 { + futex_signal(&s.count) + } else { + futex_broadcast(&s.count) + } } atomic_sema_wait :: proc(s: ^Atomic_Sema) { - atomic_mutex_lock(&s.mutex) - defer atomic_mutex_unlock(&s.mutex) - - for s.count == 0 { - atomic_cond_wait(&s.cond, &s.mutex) - } - - s.count -= 1 - if s.count > 0 { - atomic_cond_signal(&s.cond) + for { + original_count := atomic_load_explicit(&s.count, .Relaxed) + for original_count == 0 { + futex_wait(&s.count, u32(original_count)) + original_count = s.count + } + if original_count == atomic_compare_exchange_strong_explicit(&s.count, original_count, original_count-1, .Acquire, .Acquire) { + return + } } } @@ -432,25 +350,21 @@ atomic_sema_wait_with_timeout :: proc(s: ^Atomic_Sema, duration: time.Duration) if duration <= 0 { return false } - atomic_mutex_lock(&s.mutex) - defer atomic_mutex_unlock(&s.mutex) - - start := time.tick_now() + for { + original_count := atomic_load_explicit(&s.count, .Relaxed) + for start := time.tick_now(); original_count == 0; /**/ { + remaining := duration - time.tick_since(start) + if remaining < 0 { + return false + } - for s.count == 0 { - remaining := duration - time.tick_since(start) - if remaining < 0 { - return false + if !futex_wait_with_timeout(&s.count, u32(original_count), remaining) { + return false + } + original_count = s.count } - - if !atomic_cond_wait_with_timeout(&s.cond, &s.mutex, remaining) { - return false + if original_count == atomic_compare_exchange_strong_explicit(&s.count, original_count, original_count-1, .Acquire, .Acquire) { + return true } } - - s.count -= 1 - if s.count > 0 { - atomic_cond_signal(&s.cond) - } - return true } diff --git a/core/sync/primitives_darwin.odin b/core/sync/primitives_darwin.odin new file mode 100644 index 000000000..726113ae7 --- /dev/null +++ b/core/sync/primitives_darwin.odin @@ -0,0 +1,18 @@ +//+build darwin +//+private +package sync + +import "core:c" +import "core:intrinsics" + +foreign import pthread "System.framework" + +_current_thread_id :: proc "contextless" () -> int { + tid: u64 + // NOTE(Oskar): available from OSX 10.6 and iOS 3.2. + // For older versions there is `syscall(SYS_thread_selfid)`, but not really + // the same thing apparently. + foreign pthread { pthread_threadid_np :: proc "c" (rawptr, ^u64) -> c.int --- } + pthread_threadid_np(nil, &tid) + return int(tid) +} diff --git a/core/sync/primitives_freebsd.odin b/core/sync/primitives_freebsd.odin new file mode 100644 index 000000000..2d7cbf18d --- /dev/null +++ b/core/sync/primitives_freebsd.odin @@ -0,0 +1,15 @@ +//+build freebsd +//+private +package sync + +import "core:c" + +foreign import dl "system:dl" + +foreign dl { + pthread_getthreadid_np :: proc "c" () -> c.int --- +} + +_current_thread_id :: proc "contextless" () -> int { + return int(pthread_getthreadid_np()) +} diff --git a/core/sync/primitives_internal.odin b/core/sync/primitives_internal.odin new file mode 100644 index 000000000..ba17c2eb5 --- /dev/null +++ b/core/sync/primitives_internal.odin @@ -0,0 +1,133 @@ +//+private +package sync + +import "core:time" + +_Sema :: struct { + atomic: Atomic_Sema, +} + +_sema_post :: proc(s: ^Sema, count := 1) { + atomic_sema_post(&s.impl.atomic, count) +} + +_sema_wait :: proc(s: ^Sema) { + atomic_sema_wait(&s.impl.atomic) +} + +_sema_wait_with_timeout :: proc(s: ^Sema, duration: time.Duration) -> bool { + return atomic_sema_wait_with_timeout(&s.impl.atomic, duration) +} + + +_Recursive_Mutex :: struct { + owner: Futex, + recursion: i32, +} + +_recursive_mutex_lock :: proc(m: ^Recursive_Mutex) { + tid := Futex(current_thread_id()) + for { + prev_owner := atomic_compare_exchange_strong_explicit(&m.impl.owner, 0, tid, .Acquire, .Acquire) + switch prev_owner { + case 0, tid: + m.impl.recursion += 1 + // inside the lock + return + } + + futex_wait(&m.impl.owner, u32(prev_owner)) + } +} + +_recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) { + m.impl.recursion -= 1 + if m.impl.recursion != 0 { + return + } + atomic_exchange_explicit(&m.impl.owner, 0, .Release) + + futex_signal(&m.impl.owner) + // outside the lock + +} + +_recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool { + tid := Futex(current_thread_id()) + prev_owner := atomic_compare_exchange_strong_explicit(&m.impl.owner, 0, tid, .Acquire, .Acquire) + switch prev_owner { + case 0, tid: + m.impl.recursion += 1 + // inside the lock + return true + } + return false +} + + +when ODIN_OS != .Windows { + _Mutex :: struct { + mutex: Atomic_Mutex, + } + + _mutex_lock :: proc(m: ^Mutex) { + atomic_mutex_lock(&m.impl.mutex) + } + + _mutex_unlock :: proc(m: ^Mutex) { + atomic_mutex_unlock(&m.impl.mutex) + } + + _mutex_try_lock :: proc(m: ^Mutex) -> bool { + return atomic_mutex_try_lock(&m.impl.mutex) + } + + _Cond :: struct { + cond: Atomic_Cond, + } + + _cond_wait :: proc(c: ^Cond, m: ^Mutex) { + atomic_cond_wait(&c.impl.cond, &m.impl.mutex) + } + + _cond_wait_with_timeout :: proc(c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool { + return atomic_cond_wait_with_timeout(&c.impl.cond, &m.impl.mutex, duration) + } + + _cond_signal :: proc(c: ^Cond) { + atomic_cond_signal(&c.impl.cond) + } + + _cond_broadcast :: proc(c: ^Cond) { + atomic_cond_broadcast(&c.impl.cond) + } + + + _RW_Mutex :: struct { + mutex: Atomic_RW_Mutex, + } + + _rw_mutex_lock :: proc(rw: ^RW_Mutex) { + atomic_rw_mutex_lock(&rw.impl.mutex) + } + + _rw_mutex_unlock :: proc(rw: ^RW_Mutex) { + atomic_rw_mutex_unlock(&rw.impl.mutex) + } + + _rw_mutex_try_lock :: proc(rw: ^RW_Mutex) -> bool { + return atomic_rw_mutex_try_lock(&rw.impl.mutex) + } + + _rw_mutex_shared_lock :: proc(rw: ^RW_Mutex) { + atomic_rw_mutex_shared_lock(&rw.impl.mutex) + } + + _rw_mutex_shared_unlock :: proc(rw: ^RW_Mutex) { + atomic_rw_mutex_shared_unlock(&rw.impl.mutex) + } + + _rw_mutex_try_shared_lock :: proc(rw: ^RW_Mutex) -> bool { + return atomic_rw_mutex_try_shared_lock(&rw.impl.mutex) + } +} \ No newline at end of file diff --git a/core/sync/sync2/primitives_linux.odin b/core/sync/primitives_linux.odin similarity index 90% rename from core/sync/sync2/primitives_linux.odin rename to core/sync/primitives_linux.odin index 89ed97985..1e75891df 100644 --- a/core/sync/sync2/primitives_linux.odin +++ b/core/sync/primitives_linux.odin @@ -1,6 +1,6 @@ //+build linux //+private -package sync2 +package sync import "core:sys/unix" diff --git a/core/sync/primitives_openbsd.odin b/core/sync/primitives_openbsd.odin new file mode 100644 index 000000000..4072a14e8 --- /dev/null +++ b/core/sync/primitives_openbsd.odin @@ -0,0 +1,9 @@ +//+build openbsd +//+private +package sync + +import "core:os" + +_current_thread_id :: proc "contextless" () -> int { + return os.current_thread_id() +} diff --git a/core/sync/primitives_wasm.odin b/core/sync/primitives_wasm.odin new file mode 100644 index 000000000..ac36404d9 --- /dev/null +++ b/core/sync/primitives_wasm.odin @@ -0,0 +1,8 @@ +//+private +//+build wasm32, wasm64 +package sync + +_current_thread_id :: proc "contextless" () -> int { + // TODO(bill): _current_thread_id for wasm32 + return 0 +} \ No newline at end of file diff --git a/core/sync/sync2/primitives_windows.odin b/core/sync/primitives_windows.odin similarity index 99% rename from core/sync/sync2/primitives_windows.odin rename to core/sync/primitives_windows.odin index ca7a5c9ee..055167892 100644 --- a/core/sync/sync2/primitives_windows.odin +++ b/core/sync/primitives_windows.odin @@ -1,6 +1,6 @@ //+build windows //+private -package sync2 +package sync import "core:time" import win32 "core:sys/windows" diff --git a/core/sync/sync.odin b/core/sync/sync.odin deleted file mode 100644 index 05c86a868..000000000 --- a/core/sync/sync.odin +++ /dev/null @@ -1,123 +0,0 @@ -package sync - -import "core:intrinsics" - -cpu_relax :: #force_inline proc "contextless" () { - intrinsics.cpu_relax() -} - -Condition_Mutex_Ptr :: union{^Mutex, ^Blocking_Mutex} - - -Ticket_Mutex :: struct { - ticket: u64, - serving: u64, -} - -ticket_mutex_init :: proc(m: ^Ticket_Mutex) { - atomic_store(&m.ticket, 0, .Relaxed) - atomic_store(&m.serving, 0, .Relaxed) -} - -ticket_mutex_lock :: #force_inline proc(m: ^Ticket_Mutex) { - ticket := atomic_add(&m.ticket, 1, .Relaxed) - for ticket != atomic_load(&m.serving, .Acquire) { - intrinsics.cpu_relax() - } -} - -ticket_mutex_unlock :: #force_inline proc(m: ^Ticket_Mutex) { - atomic_add(&m.serving, 1, .Relaxed) -} - - -Benaphore :: struct { - counter: int, - sema: Semaphore, -} - -benaphore_init :: proc(b: ^Benaphore) { - intrinsics.atomic_store(&b.counter, 0) - semaphore_init(&b.sema) -} - -benaphore_destroy :: proc(b: ^Benaphore) { - semaphore_destroy(&b.sema) -} - -benaphore_lock :: proc(b: ^Benaphore) { - if intrinsics.atomic_add_acq(&b.counter, 1) > 1 { - semaphore_wait_for(&b.sema) - } -} - -benaphore_try_lock :: proc(b: ^Benaphore) -> bool { - v, _ := intrinsics.atomic_cxchg_acq(&b.counter, 1, 0) - return v == 0 -} - -benaphore_unlock :: proc(b: ^Benaphore) { - if intrinsics.atomic_sub_rel(&b.counter, 1) > 0 { - semaphore_post(&b.sema) - } -} - -Recursive_Benaphore :: struct { - counter: int, - owner: int, - recursion: int, - sema: Semaphore, -} - -recursive_benaphore_init :: proc(b: ^Recursive_Benaphore) { - intrinsics.atomic_store(&b.counter, 0) - semaphore_init(&b.sema) -} - -recursive_benaphore_destroy :: proc(b: ^Recursive_Benaphore) { - semaphore_destroy(&b.sema) -} - -recursive_benaphore_lock :: proc(b: ^Recursive_Benaphore) { - tid := current_thread_id() - if intrinsics.atomic_add_acq(&b.counter, 1) > 1 { - if tid != b.owner { - semaphore_wait_for(&b.sema) - } - } - // inside the lock - b.owner = tid - b.recursion += 1 -} - -recursive_benaphore_try_lock :: proc(b: ^Recursive_Benaphore) -> bool { - tid := current_thread_id() - if b.owner == tid { - intrinsics.atomic_add_acq(&b.counter, 1) - } else { - v, _ := intrinsics.atomic_cxchg_acq(&b.counter, 1, 0) - if v != 0 { - return false - } - // inside the lock - b.owner = tid - } - b.recursion += 1 - return true -} - -recursive_benaphore_unlock :: proc(b: ^Recursive_Benaphore) { - tid := current_thread_id() - assert(tid == b.owner) - b.recursion -= 1 - recursion := b.recursion - if recursion == 0 { - b.owner = 0 - } - if intrinsics.atomic_sub_rel(&b.counter, 1) > 0 { - if recursion == 0 { - semaphore_post(&b.sema) - } - } - // outside the lock -} diff --git a/core/sync/sync2/atomic.odin b/core/sync/sync2/atomic.odin deleted file mode 100644 index fe19f17c8..000000000 --- a/core/sync/sync2/atomic.odin +++ /dev/null @@ -1,79 +0,0 @@ -package sync2 - -import "core:intrinsics" - -cpu_relax :: intrinsics.cpu_relax - -atomic_fence :: intrinsics.atomic_fence -atomic_fence_acquire :: intrinsics.atomic_fence_acq -atomic_fence_release :: intrinsics.atomic_fence_rel -atomic_fence_acqrel :: intrinsics.atomic_fence_acqrel - -atomic_store :: intrinsics.atomic_store -atomic_store_release :: intrinsics.atomic_store_rel -atomic_store_relaxed :: intrinsics.atomic_store_relaxed -atomic_store_unordered :: intrinsics.atomic_store_unordered - -atomic_load :: intrinsics.atomic_load -atomic_load_acquire :: intrinsics.atomic_load_acq -atomic_load_relaxed :: intrinsics.atomic_load_relaxed -atomic_load_unordered :: intrinsics.atomic_load_unordered - -atomic_add :: intrinsics.atomic_add -atomic_add_acquire :: intrinsics.atomic_add_acq -atomic_add_release :: intrinsics.atomic_add_rel -atomic_add_acqrel :: intrinsics.atomic_add_acqrel -atomic_add_relaxed :: intrinsics.atomic_add_relaxed -atomic_sub :: intrinsics.atomic_sub -atomic_sub_acquire :: intrinsics.atomic_sub_acq -atomic_sub_release :: intrinsics.atomic_sub_rel -atomic_sub_acqrel :: intrinsics.atomic_sub_acqrel -atomic_sub_relaxed :: intrinsics.atomic_sub_relaxed -atomic_and :: intrinsics.atomic_and -atomic_and_acquire :: intrinsics.atomic_and_acq -atomic_and_release :: intrinsics.atomic_and_rel -atomic_and_acqrel :: intrinsics.atomic_and_acqrel -atomic_and_relaxed :: intrinsics.atomic_and_relaxed -atomic_nand :: intrinsics.atomic_nand -atomic_nand_acquire :: intrinsics.atomic_nand_acq -atomic_nand_release :: intrinsics.atomic_nand_rel -atomic_nand_acqrel :: intrinsics.atomic_nand_acqrel -atomic_nand_relaxed :: intrinsics.atomic_nand_relaxed -atomic_or :: intrinsics.atomic_or -atomic_or_acquire :: intrinsics.atomic_or_acq -atomic_or_release :: intrinsics.atomic_or_rel -atomic_or_acqrel :: intrinsics.atomic_or_acqrel -atomic_or_relaxed :: intrinsics.atomic_or_relaxed -atomic_xor :: intrinsics.atomic_xor -atomic_xor_acquire :: intrinsics.atomic_xor_acq -atomic_xor_release :: intrinsics.atomic_xor_rel -atomic_xor_acqrel :: intrinsics.atomic_xor_acqrel -atomic_xor_relaxed :: intrinsics.atomic_xor_relaxed - -atomic_exchange :: intrinsics.atomic_xchg -atomic_exchange_acquire :: intrinsics.atomic_xchg_acq -atomic_exchange_release :: intrinsics.atomic_xchg_rel -atomic_exchange_acqrel :: intrinsics.atomic_xchg_acqrel -atomic_exchange_relaxed :: intrinsics.atomic_xchg_relaxed - -// Returns value and optional ok boolean -atomic_compare_exchange_strong :: intrinsics.atomic_cxchg -atomic_compare_exchange_strong_acquire :: intrinsics.atomic_cxchg_acq -atomic_compare_exchange_strong_release :: intrinsics.atomic_cxchg_rel -atomic_compare_exchange_strong_acqrel :: intrinsics.atomic_cxchg_acqrel -atomic_compare_exchange_strong_relaxed :: intrinsics.atomic_cxchg_relaxed -atomic_compare_exchange_strong_failrelaxed :: intrinsics.atomic_cxchg_failrelaxed -atomic_compare_exchange_strong_failacquire :: intrinsics.atomic_cxchg_failacq -atomic_compare_exchange_strong_acquire_failrelaxed :: intrinsics.atomic_cxchg_acq_failrelaxed -atomic_compare_exchange_strong_acqrel_failrelaxed :: intrinsics.atomic_cxchg_acqrel_failrelaxed - -// Returns value and optional ok boolean -atomic_compare_exchange_weak :: intrinsics.atomic_cxchgweak -atomic_compare_exchange_weak_acquire :: intrinsics.atomic_cxchgweak_acq -atomic_compare_exchange_weak_release :: intrinsics.atomic_cxchgweak_rel -atomic_compare_exchange_weak_acqrel :: intrinsics.atomic_cxchgweak_acqrel -atomic_compare_exchange_weak_relaxed :: intrinsics.atomic_cxchgweak_relaxed -atomic_compare_exchange_weak_failrelaxed :: intrinsics.atomic_cxchgweak_failrelaxed -atomic_compare_exchange_weak_failacquire :: intrinsics.atomic_cxchgweak_failacq -atomic_compare_exchange_weak_acquire_failrelaxed :: intrinsics.atomic_cxchgweak_acq_failrelaxed -atomic_compare_exchange_weak_acqrel_failrelaxed :: intrinsics.atomic_cxchgweak_acqrel_failrelaxed diff --git a/core/sync/sync2/primitives_darwin.odin b/core/sync/sync2/primitives_darwin.odin deleted file mode 100644 index 66995bd01..000000000 --- a/core/sync/sync2/primitives_darwin.odin +++ /dev/null @@ -1,57 +0,0 @@ -//+build darwin -//+private -package sync2 - -import "core:c" -import "core:time" -import "core:intrinsics" - -foreign import pthread "System.framework" - -_current_thread_id :: proc "contextless" () -> int { - tid: u64 - // NOTE(Oskar): available from OSX 10.6 and iOS 3.2. - // For older versions there is `syscall(SYS_thread_selfid)`, but not really - // the same thing apparently. - foreign pthread { pthread_threadid_np :: proc "c" (rawptr, ^u64) -> c.int --- } - pthread_threadid_np(nil, &tid) - return int(tid) -} - - - -_Mutex :: struct { - mutex: Atomic_Mutex, -} - -_mutex_lock :: proc(m: ^Mutex) { - atomic_mutex_lock(&m.impl.mutex) -} - -_mutex_unlock :: proc(m: ^Mutex) { - atomic_mutex_unlock(&m.impl.mutex) -} - -_mutex_try_lock :: proc(m: ^Mutex) -> bool { - return atomic_mutex_try_lock(&m.impl.mutex) -} - -_Cond :: struct { - cond: Atomic_Cond, -} - -_cond_wait :: proc(c: ^Cond, m: ^Mutex) { - atomic_cond_wait(&c.impl.cond, &m.impl.mutex) -} - -_cond_wait_with_timeout :: proc(c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool { - return atomic_cond_wait_with_timeout(&c.impl.cond, &m.impl.mutex, duration) -} - -_cond_signal :: proc(c: ^Cond) { - atomic_cond_signal(&c.impl.cond) -} - -_cond_broadcast :: proc(c: ^Cond) { - atomic_cond_broadcast(&c.impl.cond) -} diff --git a/core/sync/sync2/primitives_internal.odin b/core/sync/sync2/primitives_internal.odin deleted file mode 100644 index ae7e2599c..000000000 --- a/core/sync/sync2/primitives_internal.odin +++ /dev/null @@ -1,184 +0,0 @@ -//+private -package sync2 - -when #config(ODIN_SYNC_RECURSIVE_MUTEX_USE_FUTEX, true) { - _Recursive_Mutex :: struct { - owner: Futex, - recursion: i32, - } - - _recursive_mutex_lock :: proc(m: ^Recursive_Mutex) { - tid := Futex(current_thread_id()) - for { - prev_owner := atomic_compare_exchange_strong_acquire(&m.impl.owner, tid, 0) - switch prev_owner { - case 0, tid: - m.impl.recursion += 1 - // inside the lock - return - } - - futex_wait(&m.impl.owner, u32(prev_owner)) - } - } - - _recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) { - m.impl.recursion -= 1 - if m.impl.recursion != 0 { - return - } - atomic_exchange_release(&m.impl.owner, 0) - - futex_signal(&m.impl.owner) - // outside the lock - - } - - _recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool { - tid := Futex(current_thread_id()) - prev_owner := atomic_compare_exchange_strong_acquire(&m.impl.owner, tid, 0) - switch prev_owner { - case 0, tid: - m.impl.recursion += 1 - // inside the lock - return true - } - return false - } -} else { - _Recursive_Mutex :: struct { - owner: int, - recursion: int, - mutex: Mutex, - } - - _recursive_mutex_lock :: proc(m: ^Recursive_Mutex) { - tid := current_thread_id() - if tid != m.impl.owner { - mutex_lock(&m.impl.mutex) - } - // inside the lock - m.impl.owner = tid - m.impl.recursion += 1 - } - - _recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) { - tid := current_thread_id() - assert(tid == m.impl.owner) - m.impl.recursion -= 1 - recursion := m.impl.recursion - if recursion == 0 { - m.impl.owner = 0 - } - if recursion == 0 { - mutex_unlock(&m.impl.mutex) - } - // outside the lock - - } - - _recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool { - tid := current_thread_id() - if m.impl.owner == tid { - return mutex_try_lock(&m.impl.mutex) - } - if !mutex_try_lock(&m.impl.mutex) { - return false - } - // inside the lock - m.impl.owner = tid - m.impl.recursion += 1 - return true - } -} - - -when ODIN_OS != "windows" { - RW_Mutex_State :: distinct uint - RW_Mutex_State_Half_Width :: size_of(RW_Mutex_State)*8/2 - RW_Mutex_State_Is_Writing :: RW_Mutex_State(1) - RW_Mutex_State_Writer :: RW_Mutex_State(1)<<1 - RW_Mutex_State_Reader :: RW_Mutex_State(1)< bool { - if mutex_try_lock(&rw.impl.mutex) { - state := atomic_load(&rw.impl.state) - if state & RW_Mutex_State_Reader_Mask == 0 { - _ = atomic_or(&rw.impl.state, RW_Mutex_State_Is_Writing) - return true - } - - mutex_unlock(&rw.impl.mutex) - } - return false - } - - _rw_mutex_shared_lock :: proc(rw: ^RW_Mutex) { - state := atomic_load(&rw.impl.state) - for state & (RW_Mutex_State_Is_Writing|RW_Mutex_State_Writer_Mask) == 0 { - ok: bool - state, ok = atomic_compare_exchange_weak(&rw.impl.state, state, state + RW_Mutex_State_Reader) - if ok { - return - } - } - - mutex_lock(&rw.impl.mutex) - _ = atomic_add(&rw.impl.state, RW_Mutex_State_Reader) - mutex_unlock(&rw.impl.mutex) - } - - _rw_mutex_shared_unlock :: proc(rw: ^RW_Mutex) { - state := atomic_sub(&rw.impl.state, RW_Mutex_State_Reader) - - if (state & RW_Mutex_State_Reader_Mask == RW_Mutex_State_Reader) && - (state & RW_Mutex_State_Is_Writing != 0) { - sema_post(&rw.impl.sema) - } - } - - _rw_mutex_try_shared_lock :: proc(rw: ^RW_Mutex) -> bool { - state := atomic_load(&rw.impl.state) - if state & (RW_Mutex_State_Is_Writing|RW_Mutex_State_Writer_Mask) == 0 { - _, ok := atomic_compare_exchange_strong(&rw.impl.state, state, state + RW_Mutex_State_Reader) - if ok { - return true - } - } - if mutex_try_lock(&rw.impl.mutex) { - _ = atomic_add(&rw.impl.state, RW_Mutex_State_Reader) - mutex_unlock(&rw.impl.mutex) - return true - } - - return false - } - -} \ No newline at end of file diff --git a/core/sync/sync2/primitives_pthreads.odin b/core/sync/sync2/primitives_pthreads.odin deleted file mode 100644 index 8d2c3986d..000000000 --- a/core/sync/sync2/primitives_pthreads.odin +++ /dev/null @@ -1,58 +0,0 @@ -//+build linux, freebsd -//+private -package sync2 - -import "core:time" -import "core:sys/unix" - -_Mutex_State :: enum i32 { - Unlocked = 0, - Locked = 1, - Waiting = 2, -} -_Mutex :: struct { - pthread_mutex: unix.pthread_mutex_t, -} - -_mutex_lock :: proc(m: ^Mutex) { - err := unix.pthread_mutex_lock(&m.impl.pthread_mutex) - assert(err == 0) -} - -_mutex_unlock :: proc(m: ^Mutex) { - err := unix.pthread_mutex_unlock(&m.impl.pthread_mutex) - assert(err == 0) -} - -_mutex_try_lock :: proc(m: ^Mutex) -> bool { - err := unix.pthread_mutex_trylock(&m.impl.pthread_mutex) - return err == 0 -} - -_Cond :: struct { - pthread_cond: unix.pthread_cond_t, -} - -_cond_wait :: proc(c: ^Cond, m: ^Mutex) { - err := unix.pthread_cond_wait(&c.impl.pthread_cond, &m.impl.pthread_mutex) - assert(err == 0) -} - - -_cond_wait_with_timeout :: proc(c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool { - tv_sec := i64(duration/1e9) - tv_nsec := i64(duration%1e9) - err := unix.pthread_cond_timedwait(&c.impl.pthread_cond, &m.impl.pthread_mutex, &{tv_sec, tv_nsec}) - return err == 0 -} - - -_cond_signal :: proc(c: ^Cond) { - err := unix.pthread_cond_signal(&c.impl.pthread_cond) - assert(err == 0) -} - -_cond_broadcast :: proc(c: ^Cond) { - err := unix.pthread_cond_broadcast(&c.impl.pthread_cond) - assert(err == 0) -} diff --git a/core/sync/sync2/sema_internal.odin b/core/sync/sync2/sema_internal.odin deleted file mode 100644 index f4027e908..000000000 --- a/core/sync/sync2/sema_internal.odin +++ /dev/null @@ -1,73 +0,0 @@ -//+private -package sync2 - -import "core:time" - - -when #config(ODIN_SYNC_SEMA_USE_FUTEX, true) { - _Sema :: struct { - count: Futex, - } - - _sema_post :: proc(s: ^Sema, count := 1) { - atomic_add(&s.impl.count, Futex(count)) - if count == 1 { - futex_signal(&s.impl.count) - } else { - futex_broadcast(&s.impl.count) - } - } - - _sema_wait :: proc(s: ^Sema) { - for { - original_count := atomic_load(&s.impl.count) - for original_count == 0 { - futex_wait(&s.impl.count, u32(original_count)) - original_count = s.impl.count - } - if original_count == atomic_compare_exchange_strong(&s.impl.count, original_count-1, original_count) { - return - } - } - } - - _sema_wait_with_timeout :: proc(s: ^Sema, duration: time.Duration) -> bool { - if duration <= 0 { - return false - } - for { - - original_count := atomic_load(&s.impl.count) - for start := time.tick_now(); original_count == 0; /**/ { - remaining := duration - time.tick_since(start) - if remaining < 0 { - return false - } - - if !futex_wait_with_timeout(&s.impl.count, u32(original_count), remaining) { - return false - } - original_count = s.impl.count - } - if original_count == atomic_compare_exchange_strong(&s.impl.count, original_count-1, original_count) { - return true - } - } - } -} else { - _Sema :: struct { - wg: Wait_Group, - } - - _sema_post :: proc(s: ^Sema, count := 1) { - wait_group_add(&s.impl.wg, count) - } - - _sema_wait :: proc(s: ^Sema) { - wait_group_wait(&s.impl.wg) - } - - _sema_wait_with_timeout :: proc(s: ^Sema, duration: time.Duration) -> bool { - return wait_group_wait_with_timeout(&s.impl.wg, duration) - } -} \ No newline at end of file diff --git a/core/sync/sync_darwin.odin b/core/sync/sync_darwin.odin deleted file mode 100644 index f3bb4d5a3..000000000 --- a/core/sync/sync_darwin.odin +++ /dev/null @@ -1,54 +0,0 @@ -package sync - -import "core:sys/darwin" - -import "core:c" - -foreign import pthread "System.framework" - -current_thread_id :: proc "contextless" () -> int { - tid: u64 - // NOTE(Oskar): available from OSX 10.6 and iOS 3.2. - // For older versions there is `syscall(SYS_thread_selfid)`, but not really - // the same thing apparently. - foreign pthread { pthread_threadid_np :: proc "c" (rawptr, ^u64) -> c.int --- } - pthread_threadid_np(nil, &tid) - return int(tid) -} - - -// The Darwin docs say it best: -// A semaphore is much like a lock, except that a finite number of threads can hold it simultaneously. -// Semaphores can be thought of as being much like piles of tokens; multiple threads can take these tokens, -// but when there are none left, a thread must wait until another thread returns one. -Semaphore :: struct #align 16 { - handle: darwin.semaphore_t, -} -// TODO(tetra): Only marked with alignment because we cannot mark distinct integers with alignments. -// See core/sys/unix/pthread_linux.odin/pthread_t. - -semaphore_init :: proc(s: ^Semaphore, initial_count := 0) { - ct := darwin.mach_task_self() - res := darwin.semaphore_create(ct, &s.handle, 0, c.int(initial_count)) - assert(res == 0) -} - -semaphore_destroy :: proc(s: ^Semaphore) { - ct := darwin.mach_task_self() - res := darwin.semaphore_destroy(ct, s.handle) - assert(res == 0) - s.handle = {} -} - -semaphore_post :: proc(s: ^Semaphore, count := 1) { - // NOTE: SPEED: If there's one syscall to do this, we should use it instead of the loop. - for in 0.. int { - SYS_GETTID :: 186; - return int(intrinsics.syscall(SYS_GETTID)); -} - - -// The Darwin docs say it best: -// A semaphore is much like a lock, except that a finite number of threads can hold it simultaneously. -// Semaphores can be thought of as being much like piles of tokens; multiple threads can take these tokens, -// but when there are none left, a thread must wait until another thread returns one. -Semaphore :: struct #align 16 { - handle: unix.sem_t, -} - -semaphore_init :: proc(s: ^Semaphore, initial_count := 0) { - assert(unix.sem_init(&s.handle, 0, u32(initial_count)) == 0); -} - -semaphore_destroy :: proc(s: ^Semaphore) { - assert(unix.sem_destroy(&s.handle) == 0); - s.handle = {}; -} - -semaphore_post :: proc(s: ^Semaphore, count := 1) { - // NOTE: SPEED: If there's one syscall to do this, we should use it instead of the loop. - for in 0.. int { - return unix.sys_gettid() -} - - -// The Darwin docs say it best: -// A semaphore is much like a lock, except that a finite number of threads can hold it simultaneously. -// Semaphores can be thought of as being much like piles of tokens; multiple threads can take these tokens, -// but when there are none left, a thread must wait until another thread returns one. -Semaphore :: struct #align 16 { - handle: unix.sem_t, -} - -semaphore_init :: proc(s: ^Semaphore, initial_count := 0) { - assert(unix.sem_init(&s.handle, 0, u32(initial_count)) == 0) -} - -semaphore_destroy :: proc(s: ^Semaphore) { - assert(unix.sem_destroy(&s.handle) == 0) - s.handle = {} -} - -semaphore_post :: proc(s: ^Semaphore, count := 1) { - // NOTE: SPEED: If there's one syscall to do this, we should use it instead of the loop. - for in 0.. bool { - return unix.pthread_mutex_trylock(&m.handle) == 0 -} - -mutex_unlock :: proc(m: ^Mutex) { - assert(unix.pthread_mutex_unlock(&m.handle) == 0) -} - - -Blocking_Mutex :: struct { - handle: unix.pthread_mutex_t, -} - - -blocking_mutex_init :: proc(m: ^Blocking_Mutex) { - // NOTE(tetra, 2019-11-01): POSIX OOM if we cannot init the attrs or the mutex. - attrs: unix.pthread_mutexattr_t - assert(unix.pthread_mutexattr_init(&attrs) == 0) - defer unix.pthread_mutexattr_destroy(&attrs) // ignores destruction error - - assert(unix.pthread_mutex_init(&m.handle, &attrs) == 0) -} - -blocking_mutex_destroy :: proc(m: ^Blocking_Mutex) { - assert(unix.pthread_mutex_destroy(&m.handle) == 0) - m.handle = {} -} - -blocking_mutex_lock :: proc(m: ^Blocking_Mutex) { - assert(unix.pthread_mutex_lock(&m.handle) == 0) -} - -// Returns false if someone else holds the lock. -blocking_mutex_try_lock :: proc(m: ^Blocking_Mutex) -> bool { - return unix.pthread_mutex_trylock(&m.handle) == 0 -} - -blocking_mutex_unlock :: proc(m: ^Blocking_Mutex) { - assert(unix.pthread_mutex_unlock(&m.handle) == 0) -} - - - -// Blocks until signalled, and then lets past exactly -// one thread. -Condition :: struct { - handle: unix.pthread_cond_t, - mutex: Condition_Mutex_Ptr, - - // NOTE(tetra, 2019-11-11): Used to mimic the more sane behavior of Windows' AutoResetEvent. - // This means that you may signal the condition before anyone is waiting to cause the - // next thread that tries to wait to just pass by uninterrupted, without sleeping. - // Without this, signalling a condition will only wake up a thread which is already waiting, - // but not one that is about to wait, which can cause your program to become out of sync in - // ways that are hard to debug or fix. - flag: bool, // atomically mutated -} - -condition_init :: proc(c: ^Condition, mutex: Condition_Mutex_Ptr) -> bool { - // NOTE(tetra, 2019-11-01): POSIX OOM if we cannot init the attrs or the condition. - attrs: unix.pthread_condattr_t - if unix.pthread_condattr_init(&attrs) != 0 { - return false - } - defer unix.pthread_condattr_destroy(&attrs) // ignores destruction error - - c.flag = false - c.mutex = mutex - return unix.pthread_cond_init(&c.handle, &attrs) == 0 -} - -condition_destroy :: proc(c: ^Condition) { - assert(unix.pthread_cond_destroy(&c.handle) == 0) - c.handle = {} -} - -// Awaken exactly one thread who is waiting on the condition -condition_signal :: proc(c: ^Condition) -> bool { - switch m in c.mutex { - case ^Mutex: - mutex_lock(m) - defer mutex_unlock(m) - atomic_swap(&c.flag, true, .Sequentially_Consistent) - return unix.pthread_cond_signal(&c.handle) == 0 - case ^Blocking_Mutex: - blocking_mutex_lock(m) - defer blocking_mutex_unlock(m) - atomic_swap(&c.flag, true, .Sequentially_Consistent) - return unix.pthread_cond_signal(&c.handle) == 0 - } - return false -} - -// Awaken all threads who are waiting on the condition -condition_broadcast :: proc(c: ^Condition) -> bool { - return unix.pthread_cond_broadcast(&c.handle) == 0 -} - -// Wait for the condition to be signalled. -// Does not block if the condition has been signalled and no one -// has waited on it yet. -condition_wait_for :: proc(c: ^Condition) -> bool { - switch m in c.mutex { - case ^Mutex: - mutex_lock(m) - defer mutex_unlock(m) - // NOTE(tetra): If a thread comes by and steals the flag immediately after the signal occurs, - // the thread that gets signalled and wakes up, discovers that the flag was taken and goes - // back to sleep. - // Though this overall behavior is the most sane, there may be a better way to do this that means that - // the first thread to wait, gets the flag first. - if atomic_swap(&c.flag, false, .Sequentially_Consistent) { - return true - } - for { - if unix.pthread_cond_wait(&c.handle, &m.handle) != 0 { - return false - } - if atomic_swap(&c.flag, false, .Sequentially_Consistent) { - return true - } - } - return false - - case ^Blocking_Mutex: - blocking_mutex_lock(m) - defer blocking_mutex_unlock(m) - // NOTE(tetra): If a thread comes by and steals the flag immediately after the signal occurs, - // the thread that gets signalled and wakes up, discovers that the flag was taken and goes - // back to sleep. - // Though this overall behavior is the most sane, there may be a better way to do this that means that - // the first thread to wait, gets the flag first. - if atomic_swap(&c.flag, false, .Sequentially_Consistent) { - return true - } - for { - if unix.pthread_cond_wait(&c.handle, &m.handle) != 0 { - return false - } - if atomic_swap(&c.flag, false, .Sequentially_Consistent) { - return true - } - } - return false - } - return false -} - -// Wait for the condition to be signalled. -// Does not block if the condition has been signalled and no one -// has waited on it yet. -condition_wait_for_timeout :: proc(c: ^Condition, duration: time.Duration) -> bool { - switch m in c.mutex { - case ^Mutex: - mutex_lock(m) - defer mutex_unlock(m) - // NOTE(tetra): If a thread comes by and steals the flag immediately after the signal occurs, - // the thread that gets signalled and wakes up, discovers that the flag was taken and goes - // back to sleep. - // Though this overall behavior is the most sane, there may be a better way to do this that means that - // the first thread to wait, gets the flag first. - if atomic_swap(&c.flag, false, .Sequentially_Consistent) { - return true - } - - ns := time.duration_nanoseconds(duration) - timeout: time.TimeSpec - timeout.tv_sec = ns / 1e9 - timeout.tv_nsec = ns % 1e9 - - for { - if unix.pthread_cond_timedwait(&c.handle, &m.handle, &timeout) != 0 { - return false - } - if atomic_swap(&c.flag, false, .Sequentially_Consistent) { - return true - } - } - return false - - case ^Blocking_Mutex: - blocking_mutex_lock(m) - defer blocking_mutex_unlock(m) - // NOTE(tetra): If a thread comes by and steals the flag immediately after the signal occurs, - // the thread that gets signalled and wakes up, discovers that the flag was taken and goes - // back to sleep. - // Though this overall behavior is the most sane, there may be a better way to do this that means that - // the first thread to wait, gets the flag first. - if atomic_swap(&c.flag, false, .Sequentially_Consistent) { - return true - } - - ns := time.duration_nanoseconds(duration) - - timeout: time.TimeSpec - timeout.tv_sec = ns / 1e9 - timeout.tv_nsec = ns % 1e9 - - for { - if unix.pthread_cond_timedwait(&c.handle, &m.handle, &timeout) != 0 { - return false - } - if atomic_swap(&c.flag, false, .Sequentially_Consistent) { - return true - } - } - return false - } - return false -} - - - -thread_yield :: proc() { - unix.sched_yield() -} diff --git a/core/sync/sync2/sync_util.odin b/core/sync/sync_util.odin similarity index 99% rename from core/sync/sync2/sync_util.odin rename to core/sync/sync_util.odin index 013bf511a..4add9064d 100644 --- a/core/sync/sync2/sync_util.odin +++ b/core/sync/sync_util.odin @@ -1,4 +1,4 @@ -package sync2 +package sync /* Example: diff --git a/core/sync/sync_windows.odin b/core/sync/sync_windows.odin deleted file mode 100644 index 0a7cf71b2..000000000 --- a/core/sync/sync_windows.odin +++ /dev/null @@ -1,180 +0,0 @@ -// +build windows -package sync - -import win32 "core:sys/windows" -import "core:time" - -current_thread_id :: proc "contextless" () -> int { - return int(win32.GetCurrentThreadId()) -} - - -// When waited upon, blocks until the internal count is greater than zero, then subtracts one. -// Posting to the semaphore increases the count by one, or the provided amount. -Semaphore :: struct { - _handle: win32.HANDLE, -} - -semaphore_init :: proc(s: ^Semaphore, initial_count := 0) { - s._handle = win32.CreateSemaphoreW(nil, i32(initial_count), 1<<31-1, nil) -} - -semaphore_destroy :: proc(s: ^Semaphore) { - win32.CloseHandle(s._handle) -} - -semaphore_post :: proc(s: ^Semaphore, count := 1) { - win32.ReleaseSemaphore(s._handle, i32(count), nil) -} - -semaphore_wait_for :: proc(s: ^Semaphore) { - // NOTE(tetra, 2019-10-30): wait_for_single_object decrements the count before it returns. - result := win32.WaitForSingleObject(s._handle, win32.INFINITE) - assert(result != win32.WAIT_FAILED) -} - - -Mutex :: struct { - _critical_section: win32.CRITICAL_SECTION, -} - - -mutex_init :: proc(m: ^Mutex, spin_count := 0) { - win32.InitializeCriticalSectionAndSpinCount(&m._critical_section, u32(spin_count)) -} - -mutex_destroy :: proc(m: ^Mutex) { - win32.DeleteCriticalSection(&m._critical_section) -} - -mutex_lock :: proc(m: ^Mutex) { - win32.EnterCriticalSection(&m._critical_section) -} - -mutex_try_lock :: proc(m: ^Mutex) -> bool { - return bool(win32.TryEnterCriticalSection(&m._critical_section)) -} - -mutex_unlock :: proc(m: ^Mutex) { - win32.LeaveCriticalSection(&m._critical_section) -} - -Blocking_Mutex :: struct { - _handle: win32.SRWLOCK, -} - - -blocking_mutex_init :: proc(m: ^Blocking_Mutex) { - win32.InitializeSRWLock(&m._handle) -} - -blocking_mutex_destroy :: proc(m: ^Blocking_Mutex) { - // -} - -blocking_mutex_lock :: proc(m: ^Blocking_Mutex) { - win32.AcquireSRWLockExclusive(&m._handle) -} - -blocking_mutex_try_lock :: proc(m: ^Blocking_Mutex) -> bool { - return bool(win32.TryAcquireSRWLockExclusive(&m._handle)) -} - -blocking_mutex_unlock :: proc(m: ^Blocking_Mutex) { - win32.ReleaseSRWLockExclusive(&m._handle) -} - - -// Blocks until signalled. -// When signalled, awakens exactly one waiting thread. -Condition :: struct { - _handle: win32.CONDITION_VARIABLE, - - mutex: Condition_Mutex_Ptr, -} - - -condition_init :: proc(c: ^Condition, mutex: Condition_Mutex_Ptr) -> bool { - assert(mutex != nil) - win32.InitializeConditionVariable(&c._handle) - c.mutex = mutex - return true -} - -condition_destroy :: proc(c: ^Condition) { - // -} - -condition_signal :: proc(c: ^Condition) -> bool { - if c._handle.ptr == nil { - return false - } - win32.WakeConditionVariable(&c._handle) - return true -} - -condition_broadcast :: proc(c: ^Condition) -> bool { - if c._handle.ptr == nil { - return false - } - win32.WakeAllConditionVariable(&c._handle) - return true -} - -condition_wait_for :: proc(c: ^Condition) -> bool { - switch m in &c.mutex { - case ^Mutex: - return cast(bool)win32.SleepConditionVariableCS(&c._handle, &m._critical_section, win32.INFINITE) - case ^Blocking_Mutex: - return cast(bool)win32.SleepConditionVariableSRW(&c._handle, &m._handle, win32.INFINITE, 0) - } - return false -} -condition_wait_for_timeout :: proc(c: ^Condition, duration: time.Duration) -> bool { - ms := win32.DWORD((max(time.duration_nanoseconds(duration), 0) + 999999)/1000000) - switch m in &c.mutex { - case ^Mutex: - return cast(bool)win32.SleepConditionVariableCS(&c._handle, &m._critical_section, ms) - case ^Blocking_Mutex: - return cast(bool)win32.SleepConditionVariableSRW(&c._handle, &m._handle, ms, 0) - } - return false -} - - - - -RW_Lock :: struct { - _handle: win32.SRWLOCK, -} - -rw_lock_init :: proc(l: ^RW_Lock) { - l._handle = win32.SRWLOCK_INIT -} -rw_lock_destroy :: proc(l: ^RW_Lock) { - // -} -rw_lock_read :: proc(l: ^RW_Lock) { - win32.AcquireSRWLockShared(&l._handle) -} -rw_lock_try_read :: proc(l: ^RW_Lock) -> bool { - return bool(win32.TryAcquireSRWLockShared(&l._handle)) -} -rw_lock_write :: proc(l: ^RW_Lock) { - win32.AcquireSRWLockExclusive(&l._handle) -} -rw_lock_try_write :: proc(l: ^RW_Lock) -> bool { - return bool(win32.TryAcquireSRWLockExclusive(&l._handle)) -} -rw_lock_read_unlock :: proc(l: ^RW_Lock) { - win32.ReleaseSRWLockShared(&l._handle) -} -rw_lock_write_unlock :: proc(l: ^RW_Lock) { - win32.ReleaseSRWLockExclusive(&l._handle) -} - - -thread_yield :: proc() { - win32.SwitchToThread() -} - diff --git a/core/sync/wait_group.odin b/core/sync/wait_group.odin deleted file mode 100644 index 63d882ed1..000000000 --- a/core/sync/wait_group.odin +++ /dev/null @@ -1,58 +0,0 @@ -package sync - -import "core:intrinsics" - -Wait_Group :: struct { - counter: int, - mutex: Blocking_Mutex, - cond: Condition, -} - -wait_group_init :: proc(wg: ^Wait_Group) { - wg.counter = 0 - blocking_mutex_init(&wg.mutex) - condition_init(&wg.cond, &wg.mutex) -} - - -wait_group_destroy :: proc(wg: ^Wait_Group) { - condition_destroy(&wg.cond) - blocking_mutex_destroy(&wg.mutex) -} - -wait_group_add :: proc(wg: ^Wait_Group, delta: int) { - if delta == 0 { - return - } - - blocking_mutex_lock(&wg.mutex) - defer blocking_mutex_unlock(&wg.mutex) - - intrinsics.atomic_add(&wg.counter, delta) - if wg.counter < 0 { - panic("sync.Wait_Group negative counter") - } - if wg.counter == 0 { - condition_broadcast(&wg.cond) - if wg.counter != 0 { - panic("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait") - } - } -} - -wait_group_done :: proc(wg: ^Wait_Group) { - wait_group_add(wg, -1) -} - -wait_group_wait :: proc(wg: ^Wait_Group) { - blocking_mutex_lock(&wg.mutex) - defer blocking_mutex_unlock(&wg.mutex) - - if wg.counter != 0 { - condition_wait_for(&wg.cond) - if wg.counter != 0 { - panic("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait") - } - } -} - diff --git a/core/sys/cpu/cpu.odin b/core/sys/cpu/cpu.odin deleted file mode 100644 index b99fe01d8..000000000 --- a/core/sys/cpu/cpu.odin +++ /dev/null @@ -1,33 +0,0 @@ -package sys_cpu - -Cache_Line_Pad :: struct {_: [_cache_line_size]byte}; - -initialized: bool; - -x86: struct { - _: Cache_Line_Pad, - has_aes: bool, // AES hardware implementation (AES NI) - has_adx: bool, // Multi-precision add-carry instruction extensions - has_avx: bool, // Advanced vector extension - has_avx2: bool, // Advanced vector extension 2 - has_bmi1: bool, // Bit manipulation instruction set 1 - has_bmi2: bool, // Bit manipulation instruction set 2 - has_erms: bool, // Enhanced REP for MOVSB and STOSB - has_fma: bool, // Fused-multiply-add instructions - has_os_xsave: bool, // OS supports XSAVE/XRESTOR for saving/restoring XMM registers. - has_pclmulqdq: bool, // PCLMULQDQ instruction - most often used for AES-GCM - has_popcnt: bool, // Hamming weight instruction POPCNT. - has_rdrand: bool, // RDRAND instruction (on-chip random number generator) - has_rdseed: bool, // RDSEED instruction (on-chip random number generator) - has_sse2: bool, // Streaming SIMD extension 2 (always available on amd64) - has_sse3: bool, // Streaming SIMD extension 3 - has_ssse3: bool, // Supplemental streaming SIMD extension 3 - has_sse41: bool, // Streaming SIMD extension 4 and 4.1 - has_sse42: bool, // Streaming SIMD extension 4 and 4.2 - _: Cache_Line_Pad, -}; - - -init :: proc() { - _init(); -} diff --git a/core/sys/cpu/cpu_x86.odin b/core/sys/cpu/cpu_x86.odin deleted file mode 100644 index 146822e61..000000000 --- a/core/sys/cpu/cpu_x86.odin +++ /dev/null @@ -1,67 +0,0 @@ -//+build i386, amd64 -package sys_cpu - -_cache_line_size :: 64; - -cpuid :: proc(ax, cx: u32) -> (eax, ebc, ecx, edx: u32) { - return expand_to_tuple(asm(u32, u32) -> struct{eax, ebc, ecx, edx: u32} { - "cpuid", - "={ax},={bx},={cx},={dx},{ax},{cx}", - }(ax, cx)); -} - -xgetbv :: proc() -> (eax, edx: u32) { - return expand_to_tuple(asm(u32) -> struct{eax, edx: u32} { - "xgetbv", - "={ax},={dx},{cx}", - }(0)); -} - -_init :: proc() { - is_set :: proc(hwc: u32, value: u32) -> bool { - return hwc&value != 0; - } - - initialized = true; - - max_id, _, _, _ := cpuid(0, 0); - - if max_id < 1 { - return; - } - - _, _, ecx1, edx1 := cpuid(1, 0); - - x86.has_sse2 = is_set(26, edx1); - - x86.has_sse3 = is_set(0, ecx1); - x86.has_pclmulqdq = is_set(1, ecx1); - x86.has_ssse3 = is_set(9, ecx1); - x86.has_fma = is_set(12, ecx1); - x86.has_sse41 = is_set(19, ecx1); - x86.has_sse42 = is_set(20, ecx1); - x86.has_popcnt = is_set(23, ecx1); - x86.has_aes = is_set(25, ecx1); - x86.has_os_xsave = is_set(27, ecx1); - x86.has_rdrand = is_set(30, ecx1); - - os_supports_avx := false; - if x86.has_os_xsave { - eax, _ := xgetbv(); - os_supports_avx = is_set(1, eax) && is_set(2, eax); - } - - x86.has_avx = is_set(28, ecx1) && os_supports_avx; - - if max_id < 7 { - return; - } - - _, ebx7, _, _ := cpuid(7, 0); - x86.has_bmi1 = is_set(3, ebx7); - x86.has_avx2 = is_set(5, ebx7) && os_supports_avx; - x86.has_bmi2 = is_set(8, ebx7); - x86.has_erms = is_set(9, ebx7); - x86.has_rdseed = is_set(18, ebx7); - x86.has_adx = is_set(19, ebx7); -} diff --git a/core/sys/darwin/xnu_system_call_helpers.odin b/core/sys/darwin/xnu_system_call_helpers.odin new file mode 100644 index 000000000..94fe0bf47 --- /dev/null +++ b/core/sys/darwin/xnu_system_call_helpers.odin @@ -0,0 +1,168 @@ +package darwin + +import "core:strings" +import "core:c" + +// this package uses the sys prefix for the proc names to indicate that these aren't native syscalls but directly call such +sys_write_string :: proc (fd: c.int, message: string) -> bool { + return syscall_write(fd, strings.ptr_from_string(message), cast(u64)len(message)) +} + +Offset_From :: enum c.int { + SEEK_SET = 0, // the offset is set to offset bytes. + SEEK_CUR = 1, // the offset is set to its current location plus offset bytes. + SEEK_END = 2, // the offset is set to the size of the file plus offset bytes. + SEEK_HOLE = 3, // the offset is set to the start of the next hole greater than or equal to the supplied offset. + SEEK_DATA = 4, // the offset is set to the start of the next non-hole file region greater than or equal to the supplied offset. +} + +Open_Flags_Enum :: enum u8 { + RDONLY, /* open for reading only */ + WRONLY, /* open for writing only */ + RDWR, /* open for reading and writing */ + + NONBLOCK, /* no delay */ + APPEND, /* set append mode */ + CREAT, /* create if nonexistant */ + TRUNC, /* truncate to zero length */ + EXCL, /* error if already exists */ + SHLOCK, /* open with shared file lock */ + EXLOCK, /* open with exclusive file lock */ + DIRECTORY, /* restrict open to only directories */ + NOFOLLOW, /* don't follow symlinks */ + SYMLINK, /* allow open of a symlink */ + EVTONLY, /* descriptor requested for event notifications only */ + CLOEXEC, /* causes the descriptor to be closed if you use any of the exec like functions */ + NOFOLLOW_ANY, /* no symlinks allowed in path */ +} +Open_Flags :: bit_set[Open_Flags_Enum; u16] + +Permission_Enum :: enum u8 { + /* For owner */ + PERMISSION_OWNER_READ, /* R for owner */ + PERMISSION_OWNER_WRITE, /* W for owner */ + PERMISSION_OWNER_EXECUTE, /* X for owner */ + //IRWXU, /* RWX mask for owner */ + + /* For group */ + PERMISSION_GROUP_READ, /* R for group */ + PERMISSION_GROUP_WRITE, /* W for group */ + PERMISSION_GROUP_EXECUTE, /* X for group */ + //IRWXG, /* RWX mask for group */ + + /* For other */ + PERMISSION_OTHER_READ, /* R for other */ + PERMISSION_OTHER_WRITE, /* W for other */ + PERMISSION_OTHER_EXECUTE, /* X for other */ + //IRWXO, /* RWX mask for other */ + + /* Special */ + PERMISSION_SET_USER_ON_EXECUTION, /* set user id on execution */ + PERMISSION_SET_GROUP_ON_EXECUTION, /* set group id on execution */ + + /* ?? */ + PERMISSION_ISVTX, /* save swapped text even after use */ +} +Permission :: bit_set[Permission_Enum; u16] + +PERMISSION_NONE_NONE :: Permission{} +PERMISSION_OWNER_ALL :: Permission{.PERMISSION_OWNER_READ, .PERMISSION_OWNER_WRITE, .PERMISSION_OWNER_EXECUTE} +PERMISSION_GROUP_ALL :: Permission{.PERMISSION_GROUP_READ, .PERMISSION_GROUP_WRITE, .PERMISSION_GROUP_EXECUTE} +PERMISSION_OTHER_ALL :: Permission{.PERMISSION_OTHER_READ, .PERMISSION_OTHER_WRITE, .PERMISSION_OTHER_EXECUTE} +PERMISSION_ALL_ALL :: PERMISSION_OWNER_ALL | PERMISSION_GROUP_ALL | PERMISSION_OTHER_ALL + +_sys_permission_mode :: #force_inline proc (mode: Permission) -> u32 { + cflags: u32 = 0 + + cflags |= PERMISSION_MASK_IRUSR * u32(Permission.PERMISSION_OWNER_READ in mode) + cflags |= PERMISSION_MASK_IWUSR * u32(Permission.PERMISSION_OWNER_WRITE in mode) + cflags |= PERMISSION_MASK_IXUSR * u32(Permission.PERMISSION_OWNER_WRITE in mode) + cflags |= PERMISSION_MASK_IRGRP * u32(Permission.PERMISSION_GROUP_READ in mode) + cflags |= PERMISSION_MASK_IWGRP * u32(Permission.PERMISSION_GROUP_WRITE in mode) + cflags |= PERMISSION_MASK_IXGRP * u32(Permission.PERMISSION_GROUP_WRITE in mode) + cflags |= PERMISSION_MASK_IROTH * u32(Permission.PERMISSION_OTHER_READ in mode) + cflags |= PERMISSION_MASK_IWOTH * u32(Permission.PERMISSION_OTHER_WRITE in mode) + cflags |= PERMISSION_MASK_IXOTH * u32(Permission.PERMISSION_OTHER_WRITE in mode) + + return cflags +} + +sys_open :: proc(path: string, oflag: Open_Flags, mode: Permission) -> (c.int, bool) { + + cmode: u32 = 0 + cflags: u32 = 0 + cpath: cstring = strings.clone_to_cstring(path, context.temp_allocator) + + cflags = _sys_permission_mode(mode) + + cmode |= OPEN_FLAG_RDONLY * u32(Open_Flags.RDONLY in oflag) + cmode |= OPEN_FLAG_WRONLY * u32(Open_Flags.WRONLY in oflag) + cmode |= OPEN_FLAG_RDWR * u32(Open_Flags.RDWR in oflag) + cmode |= OPEN_FLAG_NONBLOCK * u32(Open_Flags.NONBLOCK in oflag) + cmode |= OPEN_FLAG_CREAT * u32(Open_Flags.CREAT in oflag) + cmode |= OPEN_FLAG_APPEND * u32(Open_Flags.APPEND in oflag) + cmode |= OPEN_FLAG_TRUNC * u32(Open_Flags.TRUNC in oflag) + cmode |= OPEN_FLAG_EXCL * u32(Open_Flags.EXCL in oflag) + cmode |= OPEN_FLAG_SHLOCK * u32(Open_Flags.SHLOCK in oflag) + cmode |= OPEN_FLAG_EXLOCK * u32(Open_Flags.EXLOCK in oflag) + cmode |= OPEN_FLAG_DIRECTORY * u32(Open_Flags.DIRECTORY in oflag) + cmode |= OPEN_FLAG_NOFOLLOW * u32(Open_Flags.NOFOLLOW in oflag) + cmode |= OPEN_FLAG_SYMLINK * u32(Open_Flags.SYMLINK in oflag) + cmode |= OPEN_FLAG_EVTONLY * u32(Open_Flags.EVTONLY in oflag) + cmode |= OPEN_FLAG_CLOEXEC * u32(Open_Flags.CLOEXEC in oflag) + cmode |= OPEN_FLAG_NOFOLLOW_ANY * u32(Open_Flags.NOFOLLOW_ANY in oflag) + + result := syscall_open(cpath, cmode, cflags) + state := result != -1 + + if state && cflags != 0 { + state = (syscall_fchmod(result, cflags) != -1) + } + + return result * cast(c.int)state, state +} + +sys_mkdir :: proc(path: string, mode: Permission) -> bool { + cpath: cstring = strings.clone_to_cstring(path, context.temp_allocator) + cflags := _sys_permission_mode(mode) + return syscall_mkdir(cpath, cflags) != -1 +} + +sys_mkdir_at :: proc(fd: c.int, path: string, mode: Permission) -> bool { + cpath: cstring = strings.clone_to_cstring(path, context.temp_allocator) + cflags := _sys_permission_mode(mode) + return syscall_mkdir_at(fd, cpath, cflags) != -1 +} + +sys_rmdir :: proc(path: string, mode: Permission) -> bool { + cpath: cstring = strings.clone_to_cstring(path, context.temp_allocator) + cflags := _sys_permission_mode(mode) + return syscall_rmdir(cpath, cflags) != -1 +} + +sys_rename :: proc(path: string, new_path: string) -> bool { + cpath: cstring = strings.clone_to_cstring(path, context.temp_allocator) + cnpath: cstring = strings.clone_to_cstring(new_path, context.temp_allocator) + return syscall_rename(cpath, cnpath) != -1 +} + +sys_rename_at :: proc(fd: c.int, path: string, to_fd: c.int, new_path: string) -> bool { + cpath: cstring = strings.clone_to_cstring(path, context.temp_allocator) + cnpath: cstring = strings.clone_to_cstring(new_path, context.temp_allocator) + return syscall_rename_at(fd, cpath, to_fd, cnpath) != -1 +} + +sys_lseek :: proc(fd: c.int, offset: i64, whence: Offset_From) -> i64 { + return syscall_lseek(fd, offset, cast(c.int)whence) +} + +sys_chmod :: proc(path: string, mode: Permission) -> bool { + cpath: cstring = strings.clone_to_cstring(path, context.temp_allocator) + cmode := _sys_permission_mode(mode) + return syscall_chmod(cpath, cmode) != -1 +} + +sys_lstat :: proc(path: string, status: ^stat) -> bool { + cpath: cstring = strings.clone_to_cstring(path, context.temp_allocator) + return syscall_lstat(cpath, status) != -1 +} diff --git a/core/sys/darwin/xnu_system_call_numbers.odin b/core/sys/darwin/xnu_system_call_numbers.odin new file mode 100644 index 000000000..b90373fdc --- /dev/null +++ b/core/sys/darwin/xnu_system_call_numbers.odin @@ -0,0 +1,558 @@ +package darwin + +unix_offset_syscall :: proc(number: System_Call_Number) -> uintptr { + return uintptr(number) + uintptr(0x2000000) +} + +System_Call_Number :: enum uintptr { + /* 0 syscall */ + exit = 1, + fork = 2, + read = 3, + write = 4, + open = 5, + close = 6, + wait4 = 7, + /* 8 old creat */ + link = 9, + unlink = 10, + /* 11 old execv */ + chdir = 12, + fchdir = 13, + mknod = 14, + chmod = 15, + chown = 16, + /* 17 old break */ + getfsstat = 18, + /* 19 old lseek */ + getpid = 20, + /* 21 old mount */ + /* 22 old umount */ + setuid = 23, + getuid = 24, + geteuid = 25, + ptrace = 26, + recvmsg = 27, + sendmsg = 28, + recvfrom = 29, + accept = 30, + getpeername = 31, + getsockname = 32, + access = 33, + chflags = 34, + fchflags = 35, + sync = 36, + kill = 37, + /* 38 old stat */ + getppid = 39, + /* 40 old lstat */ + dup = 41, + pipe = 42, + getegid = 43, + /* 44 old profil */ + /* 45 old ktrace */ + sigaction = 46, + getgid = 47, + sigprocmask = 48, + getlogin = 49, + setlogin = 50, + acct = 51, + sigpending = 52, + sigaltstack = 53, + ioctl = 54, + reboot = 55, + revoke = 56, + symlink = 57, + readlink = 58, + execve = 59, + umask = 60, + chroot = 61, + /* 62 old fstat */ + /* 63 used internally and reserved */ + /* getpagesize = 64, invalid */ + msync = 65, + vfork = 66, + /* 67 old vread */ + /* 68 old vwrite */ + /* 69 old sbrk */ + /* 70 old sstk */ + /* 71 old mmap */ + /* 72 old vadvise */ + munmap = 73, + mprotect = 74, + madvise = 75, + /* 76 old vhangup */ + /* 77 old vlimit */ + mincore = 78, + getgroups = 79, + setgroups = 80, + getpgrp = 81, + setpgid = 82, + setitimer = 83, + /* 84 old wait */ + swapon = 85, + getitimer = 86, + /* 87 old gethostname */ + /* 88 old sethostname */ + getdtablesize = 89, + dup2 = 90, + /* 91 old getdopt */ + fcntl = 92, + select = 93, + /* 94 old setdopt */ + fsync = 95, + setpriority = 96, + socket = 97, + connect = 98, + /* 99 old accept */ + getpriority = 100, + /* 101 old send */ + /* 102 old recv */ + /* 103 old sigreturn */ + bind = 104, + setsockopt = 105, + listen = 106, + /* 107 old vtimes */ + /* 108 old sigvec */ + /* 109 old sigblock */ + /* 110 old sigsetmask */ + sigsuspend = 111, + /* 112 old sigstack */ + /* 113 old recvmsg */ + /* 114 old sendmsg */ + /* 115 old vtrace */ + gettimeofday = 116, + getrusage = 117, + getsockopt = 118, + /* 119 old resuba */ + readv = 120, + writev = 121, + settimeofday = 122, + fchown = 123, + fchmod = 124, + /* 125 old recvfrom */ + setreuid = 126, + setregid = 127, + rename = 128, + /* 129 old truncate */ + /* 130 old ftruncate */ + flock = 131, + mkfifo = 132, + sendto = 133, + shutdown = 134, + socketpair = 135, + mkdir = 136, + rmdir = 137, + utimes = 138, + futimes = 139, + adjtime = 140, + /* 141 old getpeername */ + gethostuuid = 142, + /* 143 old sethostid */ + /* 144 old getrlimit */ + /* 145 old setrlimit */ + /* 146 old killpg */ + setsid = 147, + /* 148 old setquota */ + /* 149 old qquota */ + /* 150 old getsockname */ + getpgid = 151, + setprivexec = 152, + pread = 153, + pwrite = 154, + nfssvc = 155, + /* 156 old getdirentries */ + statfs = 157, + fstatfs = 158, + unmount = 159, + /* 160 old async_daemon */ + getfh = 161, + /* 162 old getdomainname */ + /* 163 old setdomainname */ + /* 164 */ + quotactl = 165, + /* 166 old exportfs */ + mount = 167, + /* 168 old ustat */ + csops = 169, + csops_audittoken = 170, + /* 171 old wait3 */ + /* 172 old rpause */ + waitid = 173, + /* 174 old getdents */ + /* 175 old gc_control */ + /* 176 old add_profil */ + kdebug_typefilter = 177, + kdebug_trace_string = 178, + kdebug_trace64 = 179, + kdebug_trace = 180, + setgid = 181, + setegid = 182, + seteuid = 183, + sigreturn = 184, + /* 185 old chud */ + thread_selfcounts = 186, + fdatasync = 187, + stat = 188, + fstat = 189, + lstat = 190, + pathconf = 191, + fpathconf = 192, + /* 193 old getfsstat */ + getrlimit = 194, + setrlimit = 195, + getdirentries = 196, + mmap = 197, + /* 198 old __syscall */ + lseek = 199, + truncate = 200, + ftruncate = 201, + sysctl = 202, + mlock = 203, + munlock = 204, + undelete = 205, + /* 206 old ATsocket */ + /* 207 old ATgetmsg */ + /* 208 old ATputmsg */ + /* 209 old ATsndreq */ + /* 210 old ATsndrsp */ + /* 211 old ATgetreq */ + /* 212 old ATgetrsp */ + /* 213 Reserved for AppleTalk */ + /* 214 */ + /* 215 */ + open_dprotected_np = 216, + fsgetpath_ext = 217, + /* 218 old lstatv */ + /* 219 old fstatv */ + getattrlist = 220, + setattrlist = 221, + getdirentriesattr = 222, + exchangedata = 223, + /* 224 old checkuseraccess or fsgetpath */ + searchfs = 225, + delete = 226, + copyfile = 227, + fgetattrlist = 228, + fsetattrlist = 229, + poll = 230, + /* 231 old watchevent */ + /* 232 old waitevent */ + /* 233 old modwatch */ + getxattr = 234, + fgetxattr = 235, + setxattr = 236, + fsetxattr = 237, + removexattr = 238, + fremovexattr = 239, + listxattr = 240, + flistxattr = 241, + fsctl = 242, + initgroups = 243, + posix_spawn = 244, + ffsctl = 245, + /* 246 */ + nfsclnt = 247, + fhopen = 248, + /* 249 */ + minherit = 250, + semsys = 251, + msgsys = 252, + shmsys = 253, + semctl = 254, + semget = 255, + semop = 256, + /* 257 old semconfig */ + msgctl = 258, + msgget = 259, + msgsnd = 260, + msgrcv = 261, + shmat = 262, + shmctl = 263, + shmdt = 264, + shmget = 265, + shm_open = 266, + shm_unlink = 267, + sem_open = 268, + sem_close = 269, + sem_unlink = 270, + sem_wait = 271, + sem_trywait = 272, + sem_post = 273, + sysctlbyname = 274, + /* 275 old sem_init */ + /* 276 old sem_destroy */ + open_extended = 277, + umask_extended = 278, + stat_extended = 279, + lstat_extended = 280, + fstat_extended = 281, + chmod_extended = 282, + fchmod_extended = 283, + access_extended = 284, + settid = 285, + gettid = 286, + setsgroups = 287, + getsgroups = 288, + setwgroups = 289, + getwgroups = 290, + mkfifo_extended = 291, + mkdir_extended = 292, + identitysvc = 293, + shared_region_check_np = 294, + /* 295 old shared_region_map_np */ + vm_pressure_monitor = 296, + psynch_rw_longrdlock = 297, + psynch_rw_yieldwrlock = 298, + psynch_rw_downgrade = 299, + psynch_rw_upgrade = 300, + psynch_mutexwait = 301, + psynch_mutexdrop = 302, + psynch_cvbroad = 303, + psynch_cvsignal = 304, + psynch_cvwait = 305, + psynch_rw_rdlock = 306, + psynch_rw_wrlock = 307, + psynch_rw_unlock = 308, + psynch_rw_unlock2 = 309, + getsid = 310, + settid_with_pid = 311, + psynch_cvclrprepost = 312, + aio_fsync = 313, + aio_return = 314, + aio_suspend = 315, + aio_cancel = 316, + aio_error = 317, + aio_read = 318, + aio_write = 319, + lio_listio = 320, + /* 321 old __pthread_cond_wait */ + iopolicysys = 322, + process_policy = 323, + mlockall = 324, + munlockall = 325, + /* 326 */ + issetugid = 327, + __pthread_kill = 328, + __pthread_sigmask = 329, + __sigwait = 330, + __disable_threadsignal = 331, + __pthread_markcancel = 332, + __pthread_canceled = 333, + __semwait_signal = 334, + /* 335 old utrace */ + proc_info = 336, + sendfile = 337, + stat64 = 338, + fstat64 = 339, + lstat64 = 340, + stat64_extended = 341, + lstat64_extended = 342, + fstat64_extended = 343, + getdirentries64 = 344, + statfs64 = 345, + fstatfs64 = 346, + getfsstat64 = 347, + __pthread_chdir = 348, + __pthread_fchdir = 349, + audit = 350, + auditon = 351, + /* 352 */ + getauid = 353, + setauid = 354, + /* 355 old getaudit */ + /* 356 old setaudit */ + getaudit_addr = 357, + setaudit_addr = 358, + auditctl = 359, + bsdthread_create = 360, + bsdthread_terminate = 361, + kqueue = 362, + kevent = 363, + lchown = 364, + /* 365 old stack_snapshot */ + bsdthread_register = 366, + workq_open = 367, + workq_kernreturn = 368, + kevent64 = 369, + __old_semwait_signal = 370, + __old_semwait_signal_nocancel = 371, + thread_selfid = 372, + ledger = 373, + kevent_qos = 374, + kevent_id = 375, + /* 376 */ + /* 377 */ + /* 378 */ + /* 379 */ + __mac_execve = 380, + __mac_syscall = 381, + __mac_get_file = 382, + __mac_set_file = 383, + __mac_get_link = 384, + __mac_set_link = 385, + __mac_get_proc = 386, + __mac_set_proc = 387, + __mac_get_fd = 388, + __mac_set_fd = 389, + __mac_get_pid = 390, + /* 391 */ + /* 392 */ + /* 393 */ + pselect = 394, + pselect_nocancel = 395, + read_nocancel = 396, + write_nocancel = 397, + open_nocancel = 398, + close_nocancel = 399, + wait4_nocancel = 400, + recvmsg_nocancel = 401, + sendmsg_nocancel = 402, + recvfrom_nocancel = 403, + accept_nocancel = 404, + msync_nocancel = 405, + fcntl_nocancel = 406, + select_nocancel = 407, + fsync_nocancel = 408, + connect_nocancel = 409, + sigsuspend_nocancel = 410, + readv_nocancel = 411, + writev_nocancel = 412, + sendto_nocancel = 413, + pread_nocancel = 414, + pwrite_nocancel = 415, + waitid_nocancel = 416, + poll_nocancel = 417, + msgsnd_nocancel = 418, + msgrcv_nocancel = 419, + sem_wait_nocancel = 420, + aio_suspend_nocancel = 421, + __sigwait_nocancel = 422, + __semwait_signal_nocancel = 423, + __mac_mount = 424, + __mac_get_mount = 425, + __mac_getfsstat = 426, + fsgetpath = 427, + audit_session_self = 428, + audit_session_join = 429, + fileport_makeport = 430, + fileport_makefd = 431, + audit_session_port = 432, + pid_suspend = 433, + pid_resume = 434, + pid_hibernate = 435, + pid_shutdown_sockets = 436, + /* 437 old shared_region_slide_np */ + shared_region_map_and_slide_np = 438, + kas_info = 439, + memorystatus_control = 440, + guarded_open_np = 441, + guarded_close_np = 442, + guarded_kqueue_np = 443, + change_fdguard_np = 444, + usrctl = 445, + proc_rlimit_control = 446, + connectx = 447, + disconnectx = 448, + peeloff = 449, + socket_delegate = 450, + telemetry = 451, + proc_uuid_policy = 452, + memorystatus_get_level = 453, + system_override = 454, + vfs_purge = 455, + sfi_ctl = 456, + sfi_pidctl = 457, + coalition = 458, + coalition_info = 459, + necp_match_policy = 460, + getattrlistbulk = 461, + clonefileat = 462, + openat = 463, + openat_nocancel = 464, + renameat = 465, + faccessat = 466, + fchmodat = 467, + fchownat = 468, + fstatat = 469, + fstatat64 = 470, + linkat = 471, + unlinkat = 472, + readlinkat = 473, + symlinkat = 474, + mkdirat = 475, + getattrlistat = 476, + proc_trace_log = 477, + bsdthread_ctl = 478, + openbyid_np = 479, + recvmsg_x = 480, + sendmsg_x = 481, + thread_selfusage = 482, + csrctl = 483, + guarded_open_dprotected_np = 484, + guarded_write_np = 485, + guarded_pwrite_np = 486, + guarded_writev_np = 487, + renameatx_np = 488, + mremap_encrypted = 489, + netagent_trigger = 490, + stack_snapshot_with_config = 491, + microstackshot = 492, + grab_pgo_data = 493, + persona = 494, + /* 495 */ + mach_eventlink_signal = 496, + mach_eventlink_wait_until = 497, + mach_eventlink_signal_wait_until = 498, + work_interval_ctl = 499, + getentropy = 500, + necp_open = 501, + necp_client_action = 502, + nexus_open = 503, // for those who are intressted http://newosxbook.com/bonus/vol1ch16.html + nexus_register = 504, + nexus_deregister = 505, + nexus_create = 506, + nexus_destroy = 507, + nexus_get_opt = 508, + nexus_set_opt = 509, + channel_open = 510, + channel_get_info = 511, + channel_sync = 512, + channel_get_opt = 513, + channel_set_opt = 514, + ulock_wait = 515, + ulock_wake = 516, + fclonefileat = 517, + fs_snapshot = 518, + register_uexc_handler = 519, + terminate_with_payload = 520, + abort_with_payload = 521, + necp_session_open = 522, + necp_session_action = 523, + setattrlistat = 524, + net_qos_guideline = 525, + fmount = 526, + ntp_adjtime = 527, + ntp_gettime = 528, + os_fault_with_payload = 529, + kqueue_workloop_ctl = 530, + mach_bridge_remote_time = 531, + coalition_ledger = 532, + log_data = 533, + memorystatus_available_memory = 534, + objc_bp_assist_cfg_np = 535, + shared_region_map_and_slide_2_np = 536, + pivot_root = 537, + task_inspect_for_pid = 538, + task_read_for_pid = 539, + preadv = 540, + pwritev = 541, + preadv_nocancel = 542, + pwritev_nocancel = 543, + ulock_wait2 = 544, + proc_info_extended_id = 545, + tracker_action = 546, + debug_syscall_reject = 547, + MAXSYSCALL = 548, + /* invalid = 63, */ +} diff --git a/core/sys/darwin/xnu_system_call_wrappers.odin b/core/sys/darwin/xnu_system_call_wrappers.odin new file mode 100644 index 000000000..685f75ffa --- /dev/null +++ b/core/sys/darwin/xnu_system_call_wrappers.odin @@ -0,0 +1,419 @@ +package darwin + +import "core:c" +import "core:intrinsics" + +/* flock */ +LOCK_SH :: 1 /* shared lock */ +LOCK_EX :: 2 /* exclusive lock */ +LOCK_NB :: 4 /* don't block when locking */ +LOCK_UN :: 8 /* unlock */ + +/* sys/unistd.h for access */ +F_OK :: c.int(0) /* test for existence of file */ +X_OK :: c.int((1 << 0)) /* test for execute or search permission */ +W_OK :: c.int((1 << 1)) /* test for write permission */ +R_OK :: c.int((1 << 2)) /* test for read permission */ + +/* copyfile flags */ +COPYFILE_ACL :: (1 << 0) +COPYFILE_STAT :: (1 << 1) +COPYFILE_XATTR :: (1 << 2) +COPYFILE_DATA :: (1 << 3) + +COPYFILE_SECURITY :: (COPYFILE_STAT | COPYFILE_ACL) +COPYFILE_METADATA :: (COPYFILE_SECURITY | COPYFILE_XATTR) +COPYFILE_ALL :: (COPYFILE_METADATA | COPYFILE_DATA) + +/* syslimits.h */ +PATH_MAX :: 1024 /* max bytes in pathname */ + +/* param.h */ +MAXPATHLEN :: PATH_MAX + +/* proc_info.h */ +DARWIN_PROC_PIDPATHINFO_SIZE :: MAXPATHLEN +DARWIN_PROC_PIDPATHINFO :: 11 + +DARWIN_PROC_ALL_PIDS :: c.int(1) +DARWIN_PROC_PGRP_ONLY :: c.int(2) +DARWIN_PROC_TTY_ONLY :: c.int(3) +DARWIN_PROC_UID_ONLY :: c.int(4) +DARWIN_PROC_RUID_ONLY :: c.int(5) +DARWIN_PROC_PPID_ONLY :: c.int(6) +DARWIN_PROC_KDBG_ONLY :: c.int(7) + +DARWIN_PROC_INFO_CALL_LISTPIDS :: c.int(0x1) +DARWIN_PROC_INFO_CALL_PIDINFO :: c.int(0x2) +DARWIN_PROC_INFO_CALL_PIDFDINFO :: c.int(0x3) +DARWIN_PROC_INFO_CALL_KERNMSGBUF :: c.int(0x4) +DARWIN_PROC_INFO_CALL_SETCONTROL :: c.int(0x5) +DARWIN_PROC_INFO_CALL_PIDFILEPORTINFO :: c.int(0x6) +DARWIN_PROC_INFO_CALL_TERMINATE :: c.int(0x7) +DARWIN_PROC_INFO_CALL_DIRTYCONTROL :: c.int(0x8) +DARWIN_PROC_INFO_CALL_PIDRUSAGE :: c.int(0x9) +DARWIN_PROC_INFO_CALL_PIDORIGINATORINFO :: c.int(0xa) +DARWIN_PROC_INFO_CALL_LISTCOALITIONS :: c.int(0xb) +DARWIN_PROC_INFO_CALL_CANUSEFGHW :: c.int(0xc) +DARWIN_PROC_INFO_CALL_PIDDYNKQUEUEINFO :: c.int(0xd) +DARWIN_PROC_INFO_CALL_UDATA_INFO :: c.int(0xe) + +/* mmap flags */ +MAP_ANONYMOUS :: 0x1000 /* allocated from memory, swap space */ +MAP_FILE :: 0x0000 /* map from file (default) */ +MAP_FIXED :: 0x0010 /* [MF|SHM] interpret addr exactly */ +MAP_HASSEMAPHORE :: 0x0200 /* region may contain semaphores */ +MAP_PRIVATE :: 0x0002 /* [MF|SHM] changes are private */ +MAP_SHARED :: 0x0001 /* [MF|SHM] share changes */ +MAP_NOCACHE :: 0x0400 /* don't cache pages for this mapping */ +MAP_JIT :: 0x0800 /* Allocate a region that will be used for JIT purposes */ +MAP_32BIT :: 0x8000 /* Return virtual addresses <4G only */ + +/* mmap prot flags */ +PROT_NONE :: 0x00 /* [MC2] no permissions */ +PROT_READ :: 0x01 /* [MC2] pages can be read */ +PROT_WRITE :: 0x02 /* [MC2] pages can be written */ +PROT_EXEC :: 0x04 /* [MC2] pages can be executed */ + +/* For owner Mode/Permission Flags for Open etc. */ +PERMISSION_MASK_IRWXU :: 0o000700 /* RWX mask for owner */ +PERMISSION_MASK_IRUSR :: 0o000400 /* R for owner */ +PERMISSION_MASK_IWUSR :: 0o000200 /* W for owner */ +PERMISSION_MASK_IXUSR :: 0o000100 /* X for owner */ + +/* For group Mode/Permission Flags for Open etc. */ +PERMISSION_MASK_IRWXG :: 0o000070 /* RWX mask for group */ +PERMISSION_MASK_IRGRP :: 0o000040 /* R for group */ +PERMISSION_MASK_IWGRP :: 0o000020 /* W for group */ +PERMISSION_MASK_IXGRP :: 0o000010 /* X for group */ + +/* For other Mode/Permission Flags for Open etc. */ +PERMISSION_MASK_IRWXO :: 0o000007 /* RWX mask for other */ +PERMISSION_MASK_IROTH :: 0o000004 /* R for other */ +PERMISSION_MASK_IWOTH :: 0o000002 /* W for other */ +PERMISSION_MASK_IXOTH :: 0o000001 /* X for other */ + +/* Special Mode/Permission Flags for Open etc. */ +PERMISSION_MASK_ISUID :: 0o004000 /* set user id on execution */ +PERMISSION_MASK_ISGID :: 0o002000 /* set group id on execution */ +PERMISSION_MASK_ISVTX :: 0o001000 /* save swapped text even after use */ + +OPEN_FLAG_RDONLY :: 0x0000 /* open for reading only */ +OPEN_FLAG_WRONLY :: 0x0001 /* open for writing only */ +OPEN_FLAG_RDWR :: 0x0002 /* open for reading and writing */ + +/* mask for above rd/wr/rdwr flags */ +MASK_ACCMODE :: 0x0003 + +OPEN_FLAG_NONBLOCK :: 0x00000004 /* no delay */ +OPEN_FLAG_APPEND :: 0x00000008 /* set append mode */ +OPEN_FLAG_CREAT :: 0x00000200 /* create if nonexistant */ +OPEN_FLAG_TRUNC :: 0x00000400 /* truncate to zero length */ +OPEN_FLAG_EXCL :: 0x00000800 /* error if already exists */ +OPEN_FLAG_SHLOCK :: 0x00000010 /* open with shared file lock */ +OPEN_FLAG_EXLOCK :: 0x00000020 /* open with exclusive file lock */ +OPEN_FLAG_DIRECTORY :: 0x00100000 /* restrict open to only directories */ +OPEN_FLAG_NOFOLLOW :: 0x00000100 /* don't follow symlinks */ +OPEN_FLAG_SYMLINK :: 0x00200000 /* allow open of a symlink */ +OPEN_FLAG_EVTONLY :: 0x00008000 /* descriptor requested for event notifications only */ +OPEN_FLAG_CLOEXEC :: 0x01000000 /* causes the descriptor to be closed if you use any of the exec like functions */ +OPEN_FLAG_NOFOLLOW_ANY :: 0x20000000 /* no symlinks allowed in path */ + +/* bsd/sys/param.h */ +DARWIN_MAXCOMLEN :: 16 + +/*--==========================================================================--*/ + +__darwin_ino64_t :: u64 +__darwin_time_t :: u32 +__darwin_dev_t :: i32 +__darwin_mode_t :: u16 +__darwin_off_t :: i64 +__darwin_blkcnt_t :: i64 +__darwin_blksize_t :: i32 +__darwin_pid_t :: i32 +__darwin_suseconds_t :: i32 + +time_t :: __darwin_time_t +dev_t :: __darwin_dev_t +mode_t :: u16 +nlink_t :: u16 +uid_t :: u16 +gid_t :: u16 +off_t :: __darwin_off_t +blkcnt_t :: __darwin_blkcnt_t +blksize_t :: __darwin_blksize_t +pid_t :: __darwin_pid_t + +stat :: __DARWIN_STRUCT_STAT64 +timeval :: _STRUCT_TIMEVAL + +/*--==========================================================================--*/ + +/* sys/stat.h */ +__DARWIN_STRUCT_STAT64 :: struct { + st_dev: dev_t, /* [XSI] ID of device containing file */ + st_mode: mode_t, /* [XSI] Mode of file (see below) */ + st_nlink: nlink_t, /* [XSI] Number of hard links */ + st_ino: __darwin_ino64_t, /* [XSI] File serial number */ + st_uid_t: uid_t, /* [XSI] User ID of the file */ + st_gid_t: gid_t, /* [XSI] Group ID of the file */ + st_rdev: dev_t, /* [XSI] Device ID */ + + // __DARWIN_STRUCT_STAT64_TIMES + st_atime: time_t, /* [XSI] Time of last access */ + st_atimensec: i32, /* nsec of last access */ + st_mtime: time_t, /* [XSI] Last data modification time */ + st_mtimensec: i32, /* last data modification nsec */ + st_ctime: time_t, /* [XSI] Time of last status change */ + st_ctimensec: u32, /* nsec of last status change */ + st_birthtime: time_t, /* File creation time(birth) */ + st_birthtimensec: i32, /* nsec of File creation time */ + // end __DARWIN_STRUCT_STAT64_TIMES + + st_size: off_t, /* [XSI] file size, in bytes */ + st_blocks: blkcnt_t, /* [XSI] blocks allocated for file */ + st_blksize: blksize_t, /* [XSI] optimal blocksize for I/O */ + st_flags: u32, /* user defined flags for file */ + st_gen: u32, /* file generation number */ + st_lspare: i32, /* RESERVED: DO NOT USE! */ + st_qspare: [2]i64, /* RESERVED: DO NOT USE! */ +} + +/* sys/_types/_timeval.h */ +_STRUCT_TIMEVAL :: struct { + tv_sec: __darwin_time_t, /* seconds */ + tv_usec: __darwin_suseconds_t, /* microseconds */ +} + +/* pwd.h */ +_Password_Entry :: struct { + pw_name: cstring, /* username */ + pw_passwd: cstring, /* user password */ + pw_uid: i32, /* user ID */ + pw_gid: i32, /* group ID */ + pw_change: u64, /* password change time */ + pw_class: cstring, /* user access class */ + pw_gecos: cstring, /* full user name */ + pw_dir: cstring, /* home directory */ + pw_shell: cstring, /* shell program */ + pw_expire: u64, /* account expiration */ + pw_fields: i32, /* filled fields */ +} + +/* processinfo.h */ +_Proc_Bsdinfo :: struct { + pbi_flags: u32, /* if is 64bit; emulated etc */ + pbi_status: u32, + pbi_xstatus: u32, + pbi_pid: u32, + pbi_ppid: u32, + pbi_uid: u32, + pbi_gid: u32, + pbi_ruid: u32, + pbi_rgid: u32, + pbi_svuid: u32, + pbi_svgid: u32, + res: u32, + pbi_comm: [DARWIN_MAXCOMLEN]u8, + pbi_name: [2 * DARWIN_MAXCOMLEN]u8, /* empty if no name is registered */ + pbi_nfiles: u32, + pbi_pgid: u32, + pbi_pjobc: u32, + e_tdev: u32, /* controlling tty dev */ + e_tpgid: u32, /* tty process group id */ + pbi_nice: i32, + pbi_start_tvsec: u64, + pbi_start_tvusec: u64, +} + +/*--==========================================================================--*/ + +syscall_fsync :: #force_inline proc(fildes: c.int) -> bool { + return !(cast(bool)intrinsics.syscall(unix_offset_syscall(.fsync), uintptr(fildes))) +} + +syscall_write :: #force_inline proc (fildes: c.int, buf: ^byte, nbyte: u64) -> bool { + return !(cast(bool)intrinsics.syscall(unix_offset_syscall(.write), uintptr(fildes), uintptr(buf), uintptr(nbyte))) +} + +syscall_read :: #force_inline proc(fildes: c.int, buf: ^byte, nbyte: u64) -> i64 { + return cast(i64)intrinsics.syscall(unix_offset_syscall(.read), uintptr(fildes), uintptr(buf), uintptr(nbyte)) +} + +syscall_open :: #force_inline proc(path: cstring, oflag: u32, mode: u32) -> c.int { + return cast(c.int)intrinsics.syscall(unix_offset_syscall(.open), transmute(uintptr)path, uintptr(oflag), uintptr(mode)) +} + +syscall_close :: #force_inline proc(fd: c.int) -> bool { + return !(cast(bool)intrinsics.syscall(unix_offset_syscall(.close), uintptr(fd))) +} + +syscall_fchmod :: #force_inline proc(fildes: c.int, mode: u32) -> c.int { + return (cast(c.int)intrinsics.syscall(unix_offset_syscall(.fchmod), uintptr(fildes), uintptr(mode))) +} + +syscall_chmod :: #force_inline proc(path: cstring, mode: u32) -> c.int { + return (cast(c.int)intrinsics.syscall(unix_offset_syscall(.chmod), transmute(uintptr)path, uintptr(mode))) +} + +syscall_mkdir :: #force_inline proc(path: cstring, mode: u32) -> c.int { + return (cast(c.int)intrinsics.syscall(unix_offset_syscall(.mkdir), transmute(uintptr)path, uintptr(mode))) +} + +syscall_mkdir_at :: #force_inline proc(fd: c.int, path: cstring, mode: u32) -> c.int { + return (cast(c.int)intrinsics.syscall(unix_offset_syscall(.mkdir), uintptr(fd), transmute(uintptr)path, uintptr(mode))) +} + +syscall_rmdir :: #force_inline proc(path: cstring, mode: u32) -> c.int { + return (cast(c.int)intrinsics.syscall(unix_offset_syscall(.rmdir), transmute(uintptr)path, uintptr(mode))) +} + +syscall_rename :: #force_inline proc(path_old: cstring, path_new: cstring) -> c.int { + return (cast(c.int)intrinsics.syscall(unix_offset_syscall(.rename), transmute(uintptr)path_old, transmute(uintptr)path_new)) +} + +syscall_rename_at :: #force_inline proc(from_fd: c.int, from: cstring, to_fd: c.int, to: cstring) -> c.int { + return (cast(c.int)intrinsics.syscall(unix_offset_syscall(.renameat), uintptr(from_fd), transmute(uintptr)from, uintptr(to_fd), transmute(uintptr)to)) +} + +syscall_lseek :: #force_inline proc(fd: c.int, offset: i64, whence: c.int) -> i64 { + return cast(i64)intrinsics.syscall(unix_offset_syscall(.lseek), uintptr(fd), uintptr(offset), uintptr(whence)) +} + +syscall_gettid :: #force_inline proc() -> u64 { + return cast(u64)intrinsics.syscall(unix_offset_syscall(.gettid)) +} + +syscall_fstat :: #force_inline proc(fd: c.int, status: ^stat) -> c.int { + return cast(c.int)intrinsics.syscall(unix_offset_syscall(.fstat), uintptr(fd), uintptr(status)) +} + +syscall_lstat :: #force_inline proc(path: cstring, status: ^stat) -> c.int { + return cast(c.int)intrinsics.syscall(unix_offset_syscall(.lstat), transmute(uintptr)path, uintptr(status)) +} + +syscall_stat :: #force_inline proc(path: cstring, status: ^stat) -> c.int { + return cast(c.int)intrinsics.syscall(unix_offset_syscall(.stat), transmute(uintptr)path, uintptr(status)) +} + +syscall_fstatat :: #force_inline proc(fd: c.int, path: cstring, status: ^stat) -> c.int { + return cast(c.int)intrinsics.syscall(unix_offset_syscall(.fstatat), uintptr(fd), transmute(uintptr)path, uintptr(status)) +} + +syscall_link :: #force_inline proc(path: cstring, to_link: cstring) -> c.int { + return cast(c.int)intrinsics.syscall(unix_offset_syscall(.link), transmute(uintptr)path, transmute(uintptr)to_link) +} + +syscall_linkat :: #force_inline proc(fd: c.int, path: cstring, fd2: c.int, to_link: cstring) -> c.int { + return cast(c.int)intrinsics.syscall(unix_offset_syscall(.linkat), uintptr(fd), transmute(uintptr)path, uintptr(fd2), transmute(uintptr)to_link) +} + +syscall_readlink :: #force_inline proc(path: cstring, buf: ^u8, buf_size: u64) -> i64 { + return cast(i64)intrinsics.syscall(unix_offset_syscall(.readlink), transmute(uintptr)path, uintptr(buf), uintptr(buf_size)) +} + +syscall_readlinkat :: #force_inline proc(fd: c.int, path: cstring, buf: ^u8, buf_size: u64) -> i64 { + return cast(i64)intrinsics.syscall(unix_offset_syscall(.readlinkat), uintptr(fd), transmute(uintptr)path, uintptr(buf), uintptr(buf_size)) +} + +syscall_access :: #force_inline proc(path: cstring, mode: c.int) -> c.int { + return cast(c.int)intrinsics.syscall(unix_offset_syscall(.access), transmute(uintptr)path, uintptr(mode)) +} + +syscall_faccessat :: #force_inline proc(fd: c.int, path: cstring, mode: c.int, flag: c.int) -> c.int { + return cast(c.int)intrinsics.syscall(unix_offset_syscall(.faccessat), uintptr(fd), transmute(uintptr)path, uintptr(mode), uintptr(flag)) +} + +syscall_getdirentries :: #force_inline proc(fd: c.int, buf: ^u8, nbytes: c.int, base_pointer: ^u32) -> c.int { + return cast(c.int)intrinsics.syscall(unix_offset_syscall(.getdirentries), uintptr(fd), uintptr(buf), uintptr(nbytes), uintptr(base_pointer)) +} + +syscall_truncate :: #force_inline proc (path: cstring, length: off_t) -> c.int { + return cast(c.int)intrinsics.syscall(unix_offset_syscall(.truncate), transmute(uintptr)path, uintptr(length)) +} + +syscall_ftruncate :: #force_inline proc (fd: c.int, length: off_t) -> c.int { + return cast(c.int)intrinsics.syscall(unix_offset_syscall(.ftruncate), uintptr(fd), uintptr(length)) +} + +syscall_sysctl :: #force_inline proc (name: ^c.int, namelen: c.uint, oldp: rawptr, oldlenp: ^i64, newp: ^i8, newlen: i64) -> c.int { + return cast(c.int)intrinsics.syscall(unix_offset_syscall(.sysctl), uintptr(name), uintptr(namelen), uintptr(oldp), uintptr(oldlenp), uintptr(newp), uintptr(newlen)) +} + +syscall_copyfile :: #force_inline proc(from: cstring, to: cstring, state: rawptr, flags: u32) -> c.int { + return cast(c.int)intrinsics.syscall(unix_offset_syscall(.copyfile), transmute(uintptr)from, transmute(uintptr)to, uintptr(state), uintptr(flags)) +} + +// think about this? last arg should be more than one +syscall_fcntl :: #force_inline proc(fd: c.int, cmd: c.int, other: rawptr) -> c.int { + return cast(c.int)intrinsics.syscall(unix_offset_syscall(.fsctl), uintptr(fd), uintptr(cmd), uintptr(other)) +} + +syscall_exit :: #force_inline proc(code: c.int) { + intrinsics.syscall(unix_offset_syscall(.exit), uintptr(code)) +} + +syscall_kill :: #force_inline proc(pid: pid_t, sig: c.int) -> c.int { + return cast(c.int)intrinsics.syscall(unix_offset_syscall(.kill), uintptr(pid), uintptr(sig)) +} + +syscall_dup :: #force_inline proc(fd: c.int) -> c.int { + return cast(c.int)intrinsics.syscall(unix_offset_syscall(.dup), uintptr(fd)) +} + +syscall_execve :: #force_inline proc(path: cstring, argv: [^]cstring, env: [^]cstring) -> c.int { + return cast(c.int)intrinsics.syscall(unix_offset_syscall(.execve), transmute(uintptr)path, transmute(uintptr)argv, transmute(uintptr)env) +} + +syscall_munmap :: #force_inline proc(addr: rawptr, len: u64) -> c.int { + return cast(c.int)intrinsics.syscall(unix_offset_syscall(.mmap), uintptr(addr), uintptr(len)) +} + +syscall_mmap :: #force_inline proc(addr: ^u8, len: u64, port: c.int, flags: c.int, fd: int, offset: off_t) -> ^u8 { + return cast(^u8)intrinsics.syscall(unix_offset_syscall(.mmap), uintptr(addr), uintptr(len), uintptr(port), uintptr(flags), uintptr(fd), uintptr(offset)) +} + +syscall_flock :: #force_inline proc(fd: c.int, operation: c.int) -> c.int { + return cast(c.int)intrinsics.syscall(unix_offset_syscall(.flock), uintptr(fd), uintptr(operation)) +} + +syscall_utimes :: #force_inline proc(path: cstring, times: ^timeval) -> c.int { + return cast(c.int)intrinsics.syscall(unix_offset_syscall(.utimes), transmute(uintptr)path, uintptr(times)) +} + +syscall_futimes :: #force_inline proc(fd: c.int, times: ^timeval) -> c.int { + return cast(c.int)intrinsics.syscall(unix_offset_syscall(.futimes), uintptr(fd), uintptr(times)) +} + +syscall_adjtime :: #force_inline proc(delta: ^timeval, old_delta: ^timeval) -> c.int { + return cast(c.int)intrinsics.syscall(unix_offset_syscall(.adjtime), uintptr(delta), uintptr(old_delta)) +} + +syscall_sysctlbyname :: #force_inline proc(name: cstring, oldp: rawptr, oldlenp: ^i64, newp: rawptr, newlen: i64) -> c.int { + return cast(c.int)intrinsics.syscall(unix_offset_syscall(.sysctlbyname), transmute(uintptr)name, uintptr(oldp), uintptr(oldlenp), uintptr(newp), uintptr(newlen)) +} + +syscall_proc_info :: #force_inline proc(num: c.int, pid: u32, flavor: c.int, arg: u64, buffer: rawptr, buffer_size: c.int) -> c.int { + return cast(c.int)intrinsics.syscall(unix_offset_syscall(.proc_info), uintptr(num), uintptr(pid), uintptr(flavor), uintptr(arg), uintptr(buffer), uintptr(buffer_size)) +} + +syscall_openat :: #force_inline proc(fd: int, path: cstring, oflag: u32, mode: u32) -> c.int { + return cast(c.int)intrinsics.syscall(unix_offset_syscall(.openat), uintptr(fd), transmute(uintptr)path, uintptr(oflag), uintptr(mode)) +} + +syscall_getentropy :: #force_inline proc(buf: [^]u8, buflen: u64) -> c.int { + return cast(c.int)intrinsics.syscall(unix_offset_syscall(.getentropy), uintptr(buf), uintptr(buflen)) +} + +syscall_pipe :: #force_inline proc(fds: [^]c.int) -> c.int { + return cast(c.int)intrinsics.syscall(unix_offset_syscall(.getentropy), uintptr(&fds[0]), uintptr(&fds[1])) +} + +syscall_chdir :: #force_inline proc(path: cstring) -> c.int { + return cast(c.int)intrinsics.syscall(unix_offset_syscall(.getentropy), transmute(uintptr)path) +} + +syscall_fchdir :: #force_inline proc(fd: c.int, path: cstring) -> c.int { + return cast(c.int)intrinsics.syscall(unix_offset_syscall(.getentropy), uintptr(fd), transmute(uintptr)path) +} \ No newline at end of file diff --git a/core/sys/unix/pthread_darwin.odin b/core/sys/unix/pthread_darwin.odin index 542a550cb..e138b8610 100644 --- a/core/sys/unix/pthread_darwin.odin +++ b/core/sys/unix/pthread_darwin.odin @@ -81,3 +81,16 @@ PTHREAD_MUTEX_NORMAL :: 0 PTHREAD_MUTEX_RECURSIVE :: 1 PTHREAD_MUTEX_ERRORCHECK :: 2 +PTHREAD_CANCEL_ENABLE :: 0 +PTHREAD_CANCEL_DISABLE :: 1 +PTHREAD_CANCEL_DEFERRED :: 0 +PTHREAD_CANCEL_ASYNCHRONOUS :: 1 + +foreign import pthread "System.framework" + +@(default_calling_convention="c") +foreign pthread { + pthread_setcancelstate :: proc (state: c.int, old_state: ^c.int) -> c.int --- + pthread_setcanceltype :: proc (type: c.int, old_type: ^c.int) -> c.int --- + pthread_cancel :: proc (thread: pthread_t) -> c.int --- +} \ No newline at end of file diff --git a/core/sys/unix/pthread_freebsd.odin b/core/sys/unix/pthread_freebsd.odin index e5b0087b1..e02345cad 100644 --- a/core/sys/unix/pthread_freebsd.odin +++ b/core/sys/unix/pthread_freebsd.odin @@ -1,76 +1,76 @@ //+build freebsd package unix -import "core:c"; +import "core:c" -pthread_t :: distinct u64; -// pthread_t :: struct #align 16 { x: u64 }; +pthread_t :: distinct u64 +// pthread_t :: struct #align 16 { x: u64 } -PTHREAD_COND_T_SIZE :: 8; +PTHREAD_COND_T_SIZE :: 8 -PTHREAD_MUTEXATTR_T_SIZE :: 8; -PTHREAD_CONDATTR_T_SIZE :: 8; -PTHREAD_RWLOCKATTR_T_SIZE :: 8; -PTHREAD_BARRIERATTR_T_SIZE :: 8; +PTHREAD_MUTEXATTR_T_SIZE :: 8 +PTHREAD_CONDATTR_T_SIZE :: 8 +PTHREAD_RWLOCKATTR_T_SIZE :: 8 +PTHREAD_BARRIERATTR_T_SIZE :: 8 // WARNING: The sizes of these things are different yet again // on non-X86! when size_of(int) == 8 { - PTHREAD_ATTR_T_SIZE :: 8; - PTHREAD_MUTEX_T_SIZE :: 8; - PTHREAD_RWLOCK_T_SIZE :: 8; - PTHREAD_BARRIER_T_SIZE :: 8; + PTHREAD_ATTR_T_SIZE :: 8 + PTHREAD_MUTEX_T_SIZE :: 8 + PTHREAD_RWLOCK_T_SIZE :: 8 + PTHREAD_BARRIER_T_SIZE :: 8 } else when size_of(int) == 4 { // TODO - PTHREAD_ATTR_T_SIZE :: 32; - PTHREAD_MUTEX_T_SIZE :: 32; - PTHREAD_RWLOCK_T_SIZE :: 44; - PTHREAD_BARRIER_T_SIZE :: 20; + PTHREAD_ATTR_T_SIZE :: 32 + PTHREAD_MUTEX_T_SIZE :: 32 + PTHREAD_RWLOCK_T_SIZE :: 44 + PTHREAD_BARRIER_T_SIZE :: 20 } pthread_cond_t :: struct #align 16 { _: [PTHREAD_COND_T_SIZE] c.char, -}; +} pthread_mutex_t :: struct #align 16 { _: [PTHREAD_MUTEX_T_SIZE] c.char, -}; +} pthread_rwlock_t :: struct #align 16 { _: [PTHREAD_RWLOCK_T_SIZE] c.char, -}; +} pthread_barrier_t :: struct #align 16 { _: [PTHREAD_BARRIER_T_SIZE] c.char, -}; +} pthread_attr_t :: struct #align 16 { _: [PTHREAD_ATTR_T_SIZE] c.char, -}; +} pthread_condattr_t :: struct #align 16 { _: [PTHREAD_CONDATTR_T_SIZE] c.char, -}; +} pthread_mutexattr_t :: struct #align 16 { _: [PTHREAD_MUTEXATTR_T_SIZE] c.char, -}; +} pthread_rwlockattr_t :: struct #align 16 { _: [PTHREAD_RWLOCKATTR_T_SIZE] c.char, -}; +} pthread_barrierattr_t :: struct #align 16 { _: [PTHREAD_BARRIERATTR_T_SIZE] c.char, -}; +} -PTHREAD_MUTEX_ERRORCHECK :: 1; -PTHREAD_MUTEX_RECURSIVE :: 2; -PTHREAD_MUTEX_NORMAL :: 3; +PTHREAD_MUTEX_ERRORCHECK :: 1 +PTHREAD_MUTEX_RECURSIVE :: 2 +PTHREAD_MUTEX_NORMAL :: 3 -PTHREAD_CREATE_JOINABLE :: 0; -PTHREAD_CREATE_DETACHED :: 1; -PTHREAD_INHERIT_SCHED :: 4; -PTHREAD_EXPLICIT_SCHED :: 0; -PTHREAD_PROCESS_PRIVATE :: 0; -PTHREAD_PROCESS_SHARED :: 1; +PTHREAD_CREATE_JOINABLE :: 0 +PTHREAD_CREATE_DETACHED :: 1 +PTHREAD_INHERIT_SCHED :: 4 +PTHREAD_EXPLICIT_SCHED :: 0 +PTHREAD_PROCESS_PRIVATE :: 0 +PTHREAD_PROCESS_SHARED :: 1 -SCHED_FIFO :: 1; -SCHED_OTHER :: 2; -SCHED_RR :: 3; // Round robin. +SCHED_FIFO :: 1 +SCHED_OTHER :: 2 +SCHED_RR :: 3 // Round robin. sched_param :: struct { @@ -92,23 +92,31 @@ sem_t :: struct { _padding: u32, } +PTHREAD_CANCEL_ENABLE :: 0 +PTHREAD_CANCEL_DISABLE :: 1 +PTHREAD_CANCEL_DEFERRED :: 0 +PTHREAD_CANCEL_ASYNCHRONOUS :: 1 + foreign import "system:pthread" @(default_calling_convention="c") foreign pthread { // create named semaphore. // used in process-shared semaphores. - sem_open :: proc(name: cstring, flags: c.int) -> ^sem_t ---; + sem_open :: proc(name: cstring, flags: c.int) -> ^sem_t --- - sem_init :: proc(sem: ^sem_t, pshared: c.int, initial_value: c.uint) -> c.int ---; - sem_destroy :: proc(sem: ^sem_t) -> c.int ---; - sem_post :: proc(sem: ^sem_t) -> c.int ---; - sem_wait :: proc(sem: ^sem_t) -> c.int ---; - sem_trywait :: proc(sem: ^sem_t) -> c.int ---; - // sem_timedwait :: proc(sem: ^sem_t, timeout: time.TimeSpec) -> c.int ---; + sem_init :: proc(sem: ^sem_t, pshared: c.int, initial_value: c.uint) -> c.int --- + sem_destroy :: proc(sem: ^sem_t) -> c.int --- + sem_post :: proc(sem: ^sem_t) -> c.int --- + sem_wait :: proc(sem: ^sem_t) -> c.int --- + sem_trywait :: proc(sem: ^sem_t) -> c.int --- + // sem_timedwait :: proc(sem: ^sem_t, timeout: time.TimeSpec) -> c.int --- // NOTE: unclear whether pthread_yield is well-supported on Linux systems, // see https://linux.die.net/man/3/pthread_yield - pthread_yield :: proc() ---; -} + pthread_yield :: proc() --- + pthread_setcancelstate :: proc (state: c.int, old_state: ^c.int) -> c.int --- + pthread_setcanceltype :: proc (type: c.int, old_type: ^c.int) -> c.int --- + pthread_cancel :: proc (thread: pthread_t) -> c.int --- +} \ No newline at end of file diff --git a/core/sys/unix/pthread_linux.odin b/core/sys/unix/pthread_linux.odin index 099e7c7e9..9c297ef22 100644 --- a/core/sys/unix/pthread_linux.odin +++ b/core/sys/unix/pthread_linux.odin @@ -94,6 +94,11 @@ when size_of(int) == 8 { SEM_T_SIZE :: 16 } +PTHREAD_CANCEL_ENABLE :: 0 +PTHREAD_CANCEL_DISABLE :: 1 +PTHREAD_CANCEL_DEFERRED :: 0 +PTHREAD_CANCEL_ASYNCHRONOUS :: 1 + foreign import "system:pthread" @(default_calling_convention="c") @@ -112,4 +117,8 @@ foreign pthread { // NOTE: unclear whether pthread_yield is well-supported on Linux systems, // see https://linux.die.net/man/3/pthread_yield pthread_yield :: proc() -> c.int --- + + pthread_setcancelstate :: proc (state: c.int, old_state: ^c.int) -> c.int --- + pthread_setcanceltype :: proc (type: c.int, old_type: ^c.int) -> c.int --- + pthread_cancel :: proc (thread: pthread_t) -> c.int --- } diff --git a/core/sys/unix/pthread_openbsd.odin b/core/sys/unix/pthread_openbsd.odin new file mode 100644 index 000000000..7ae82e662 --- /dev/null +++ b/core/sys/unix/pthread_openbsd.odin @@ -0,0 +1,74 @@ +//+build openbsd +package unix + +import "core:c" + +pthread_t :: distinct rawptr +pthread_attr_t :: distinct rawptr +pthread_mutex_t :: distinct rawptr +pthread_mutexattr_t :: distinct rawptr +pthread_cond_t :: distinct rawptr +pthread_condattr_t :: distinct rawptr +pthread_rwlock_t :: distinct rawptr +pthread_rwlockattr_t :: distinct rawptr +pthread_barrier_t :: distinct rawptr +pthread_barrierattr_t :: distinct rawptr +pthread_spinlock_t :: distinct rawptr + +pthread_key_t :: distinct c.int +pthread_once_t :: struct { + state: c.int, + mutex: pthread_mutex_t, +} + +PTHREAD_MUTEX_ERRORCHECK :: 1 +PTHREAD_MUTEX_RECURSIVE :: 2 +PTHREAD_MUTEX_NORMAL :: 3 +PTHREAD_MUTEX_STRICT_NP :: 4 + +PTHREAD_DETACHED :: 0x1 +PTHREAD_SCOPE_SYSTEM :: 0x2 +PTHREAD_INHERIT_SCHED :: 0x4 +PTHREAD_NOFLOAT :: 0x8 + +PTHREAD_CREATE_DETACHED :: PTHREAD_DETACHED +PTHREAD_CREATE_JOINABLE :: 0 +PTHREAD_SCOPE_PROCESS :: 0 +PTHREAD_EXPLICIT_SCHED :: 0 + +SCHED_FIFO :: 1 +SCHED_OTHER :: 2 +SCHED_RR :: 3 + +sched_param :: struct { + sched_priority: c.int, +} + +sem_t :: distinct rawptr + +PTHREAD_CANCEL_ENABLE :: 0 +PTHREAD_CANCEL_DISABLE :: 1 +PTHREAD_CANCEL_DEFERRED :: 0 +PTHREAD_CANCEL_ASYNCHRONOUS :: 1 + +foreign import libc "system:c" + +@(default_calling_convention="c") +foreign libc { + sem_open :: proc(name: cstring, flags: c.int) -> ^sem_t --- + + sem_init :: proc(sem: ^sem_t, pshared: c.int, initial_value: c.uint) -> c.int --- + sem_destroy :: proc(sem: ^sem_t) -> c.int --- + sem_post :: proc(sem: ^sem_t) -> c.int --- + sem_wait :: proc(sem: ^sem_t) -> c.int --- + sem_trywait :: proc(sem: ^sem_t) -> c.int --- + //sem_timedwait :: proc(sem: ^sem_t, timeout: time.TimeSpec) -> c.int --- + + // NOTE: unclear whether pthread_yield is well-supported on Linux systems, + // see https://linux.die.net/man/3/pthread_yield + pthread_yield :: proc() --- + + pthread_setcancelstate :: proc (state: c.int, old_state: ^c.int) -> c.int --- + pthread_setcanceltype :: proc (type: c.int, old_type: ^c.int) -> c.int --- + pthread_cancel :: proc (thread: pthread_t) -> c.int --- +} \ No newline at end of file diff --git a/core/sys/unix/pthread_unix.odin b/core/sys/unix/pthread_unix.odin index ccd8f7844..8bf397647 100644 --- a/core/sys/unix/pthread_unix.odin +++ b/core/sys/unix/pthread_unix.odin @@ -1,10 +1,9 @@ -//+build linux, darwin, freebsd +//+build linux, darwin, freebsd, openbsd package unix foreign import "system:pthread" import "core:c" -import "core:time" // // On success, these functions return 0. @@ -72,7 +71,7 @@ foreign pthread { // assumes the mutex is pre-locked pthread_cond_wait :: proc(cond: ^pthread_cond_t, mutex: ^pthread_mutex_t) -> c.int --- - pthread_cond_timedwait :: proc(cond: ^pthread_cond_t, mutex: ^pthread_mutex_t, timeout: ^time.TimeSpec) -> c.int --- + pthread_cond_timedwait :: proc(cond: ^pthread_cond_t, mutex: ^pthread_mutex_t, timeout: ^timespec) -> c.int --- pthread_condattr_init :: proc(attrs: ^pthread_condattr_t) -> c.int --- pthread_condattr_destroy :: proc(attrs: ^pthread_condattr_t) -> c.int --- @@ -95,7 +94,7 @@ foreign pthread { pthread_mutex_lock :: proc(mutex: ^pthread_mutex_t) -> c.int --- - pthread_mutex_timedlock :: proc(mutex: ^pthread_mutex_t, timeout: ^time.TimeSpec) -> c.int --- + pthread_mutex_timedlock :: proc(mutex: ^pthread_mutex_t, timeout: ^timespec) -> c.int --- pthread_mutex_unlock :: proc(mutex: ^pthread_mutex_t) -> c.int --- diff --git a/core/sys/unix/syscalls_linux.odin b/core/sys/unix/syscalls_linux.odin index 3dc3d2c74..d611e33f0 100644 --- a/core/sys/unix/syscalls_linux.odin +++ b/core/sys/unix/syscalls_linux.odin @@ -15,7 +15,7 @@ import "core:intrinsics" // 386: arch/x86/entry/syscalls/sycall_32.tbl // arm: arch/arm/tools/syscall.tbl -when ODIN_ARCH == "amd64" { +when ODIN_ARCH == .amd64 { SYS_read : uintptr : 0 SYS_write : uintptr : 1 SYS_open : uintptr : 2 @@ -33,8 +33,8 @@ when ODIN_ARCH == "amd64" { SYS_rt_sigprocmask : uintptr : 14 SYS_rt_sigreturn : uintptr : 15 SYS_ioctl : uintptr : 16 - SYS_pread : uintptr : 17 - SYS_pwrite : uintptr : 18 + SYS_pread64 : uintptr : 17 + SYS_pwrite64 : uintptr : 18 SYS_readv : uintptr : 19 SYS_writev : uintptr : 20 SYS_access : uintptr : 21 @@ -374,7 +374,7 @@ when ODIN_ARCH == "amd64" { SYS_landlock_add_rule : uintptr : 445 SYS_landlock_restrict_self : uintptr : 446 SYS_memfd_secret : uintptr : 447 -} else when ODIN_ARCH == "arm64" { +} else when ODIN_ARCH == .arm64 { SYS_io_setup : uintptr : 0 SYS_io_destroy : uintptr : 1 SYS_io_submit : uintptr : 2 @@ -675,7 +675,9 @@ when ODIN_ARCH == "amd64" { SYS_landlock_create_ruleset : uintptr : 444 SYS_landlock_add_rule : uintptr : 445 SYS_landlock_restrict_self : uintptr : 446 -} else when ODIN_ARCH == "i386" { + + SIGCHLD :: 17 +} else when ODIN_ARCH == .i386 { SYS_restart_syscall : uintptr : 0 SYS_exit : uintptr : 1 SYS_fork : uintptr : 2 @@ -1112,7 +1114,7 @@ when ODIN_ARCH == "amd64" { SYS_landlock_add_rule : uintptr : 445 SYS_landlock_restrict_self : uintptr : 446 SYS_memfd_secret : uintptr : 447 -} else when ODIN_ARCH == "arm" { +} else when ODIN_ARCH == .arm32 { // TODO SYS_restart_syscall : uintptr : 0 SYS_exit : uintptr : 1 SYS_fork : uintptr : 2 @@ -1516,10 +1518,337 @@ when ODIN_ARCH == "amd64" { #panic("Unsupported architecture") } +// syscall related constants +AT_FDCWD :: ~uintptr(99) +AT_REMOVEDIR :: uintptr(0x200) +AT_SYMLINK_FOLLOW :: uintptr(0x400) +AT_SYMLINK_NOFOLLOW :: uintptr(0x100) + +// mmap flags +PROT_NONE :: 0x0 +PROT_READ :: 0x1 +PROT_WRITE :: 0x2 +PROT_EXEC :: 0x4 +PROT_GROWSDOWN :: 0x01000000 +PROT_GROWSUP :: 0x02000000 + +MAP_FIXED :: 0x10 +MAP_SHARED :: 0x1 +MAP_PRIVATE :: 0x2 +MAP_SHARED_VALIDATE :: 0x3 +MAP_ANONYMOUS :: 0x20 + +// mremap flags +MREMAP_MAYMOVE :: 1 +MREMAP_FIXED :: 2 +MREMAP_DONTUNMAP :: 4 + +// madvise flags +MADV_NORMAL :: 0 +MADV_RANDOM :: 1 +MADV_SEQUENTIAL :: 2 +MADV_WILLNEED :: 3 +MADV_DONTNEED :: 4 +MADV_FREE :: 8 +MADV_REMOVE :: 9 +MADV_DONTFORK :: 10 +MADV_DOFORK :: 11 +MADV_MERGEABLE :: 12 +MADV_UNMERGEABLE :: 13 +MADV_HUGEPAGE :: 14 +MADV_NOHUGEPAGE :: 15 +MADV_DONTDUMP :: 16 +MADV_DODUMP :: 17 +MADV_WIPEONFORK :: 18 +MADV_KEEPONFORK :: 19 +MADV_HWPOISON :: 100 + sys_gettid :: proc "contextless" () -> int { return cast(int)intrinsics.syscall(SYS_gettid) } -sys_getrandom :: proc "contextless" (buf: ^byte, buflen: int, flags: uint) -> int { +sys_getrandom :: proc "contextless" (buf: [^]byte, buflen: int, flags: uint) -> int { return cast(int)intrinsics.syscall(SYS_getrandom, buf, cast(uintptr)(buflen), cast(uintptr)(flags)) } + +sys_open :: proc "contextless" (path: cstring, flags: int, mode: int = 0o000) -> int { + when ODIN_ARCH != .arm64 { + return int(intrinsics.syscall(SYS_open, uintptr(rawptr(path)), uintptr(flags), uintptr(mode))) + } else { // NOTE: arm64 does not have open + return int(intrinsics.syscall(SYS_openat, AT_FDCWD, uintptr(rawptr(path)), uintptr(flags), uintptr(mode))) + } +} + +sys_openat :: proc "contextless" (dfd: int, path: cstring, flags: int, mode: int = 0o000) -> int { + return int(intrinsics.syscall(SYS_openat, uintptr(dfd), uintptr(rawptr(path)), uintptr(flags), uintptr(mode))) +} + +sys_close :: proc "contextless" (fd: int) -> int { + return int(intrinsics.syscall(SYS_close, uintptr(fd))) +} + +sys_read :: proc "contextless" (fd: int, buf: rawptr, size: uint) -> int { + return int(intrinsics.syscall(SYS_read, uintptr(fd), uintptr(buf), uintptr(size))) +} + +sys_pread :: proc "contextless" (fd: int, buf: rawptr, size: uint, offset: i64) -> int { + when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 { + return int(intrinsics.syscall(SYS_pread64, uintptr(fd), uintptr(buf), uintptr(size), uintptr(offset))) + } else { + low := uintptr(offset & 0xFFFFFFFF) + high := uintptr(offset >> 32) + return int(intrinsics.syscall(SYS_pread64, uintptr(fd), uintptr(buf), uintptr(size), high, low)) + } +} + +sys_write :: proc "contextless" (fd: int, buf: rawptr, size: uint) -> int { + return int(intrinsics.syscall(SYS_write, uintptr(fd), uintptr(buf), uintptr(size))) +} + +sys_pwrite :: proc "contextless" (fd: int, buf: rawptr, size: uint, offset: i64) -> int { + when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 { + return int(intrinsics.syscall(SYS_pwrite64, uintptr(fd), uintptr(buf), uintptr(size), uintptr(offset))) + } else { + low := uintptr(offset & 0xFFFFFFFF) + high := uintptr(offset >> 32) + return int(intrinsics.syscall(SYS_pwrite64, uintptr(fd), uintptr(buf), uintptr(size), high, low)) + } +} + +sys_lseek :: proc "contextless" (fd: int, offset: i64, whence: int) -> i64 { + when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 { + return i64(intrinsics.syscall(SYS_lseek, uintptr(fd), uintptr(offset), uintptr(whence))) + } else { + low := uintptr(offset & 0xFFFFFFFF) + high := uintptr(offset >> 32) + result: i64 + res := i64(intrinsics.syscall(SYS__llseek, uintptr(fd), high, low, &result, uintptr(whence))) + return res if res < 0 else result + } +} + +sys_stat :: proc "contextless" (path: cstring, stat: rawptr) -> int { + when ODIN_ARCH == .amd64 { + return int(intrinsics.syscall(SYS_stat, uintptr(rawptr(path)), uintptr(stat))) + } else when ODIN_ARCH != .arm64 { + return int(intrinsics.syscall(SYS_stat64, uintptr(rawptr(path)), uintptr(stat))) + } else { // NOTE: arm64 does not have stat + return int(intrinsics.syscall(SYS_fstatat, AT_FDCWD, uintptr(rawptr(path)), uintptr(stat), 0)) + } +} + +sys_fstat :: proc "contextless" (fd: int, stat: rawptr) -> int { + when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 { + return int(intrinsics.syscall(SYS_fstat, uintptr(fd), uintptr(stat))) + } else { + return int(intrinsics.syscall(SYS_fstat64, uintptr(fd), uintptr(stat))) + } +} + +sys_lstat :: proc "contextless" (path: cstring, stat: rawptr) -> int { + when ODIN_ARCH == .amd64 { + return int(intrinsics.syscall(SYS_lstat, uintptr(rawptr(path)), uintptr(stat))) + } else when ODIN_ARCH != .arm64 { + return int(intrinsics.syscall(SYS_lstat64, uintptr(rawptr(path)), uintptr(stat))) + } else { // NOTE: arm64 does not have any lstat + return int(intrinsics.syscall(SYS_fstatat, AT_FDCWD, uintptr(rawptr(path)), uintptr(stat), AT_SYMLINK_NOFOLLOW)) + } +} + +sys_readlink :: proc "contextless" (path: cstring, buf: rawptr, bufsiz: uint) -> int { + when ODIN_ARCH != .arm64 { + return int(intrinsics.syscall(SYS_readlink, uintptr(rawptr(path)), uintptr(buf), uintptr(bufsiz))) + } else { // NOTE: arm64 does not have readlink + return int(intrinsics.syscall(SYS_readlinkat, AT_FDCWD, uintptr(rawptr(path)), uintptr(buf), uintptr(bufsiz))) + } +} + +sys_symlink :: proc "contextless" (old_name: cstring, new_name: cstring) -> int { + when ODIN_ARCH != .arm64 { + return int(intrinsics.syscall(SYS_symlink, uintptr(rawptr(old_name)), uintptr(rawptr(new_name)))) + } else { // NOTE: arm64 does not have symlink + return int(intrinsics.syscall(SYS_symlinkat, uintptr(rawptr(old_name)), AT_FDCWD, uintptr(rawptr(new_name)))) + } +} + +sys_access :: proc "contextless" (path: cstring, mask: int) -> int { + when ODIN_ARCH != .arm64 { + return int(intrinsics.syscall(SYS_access, uintptr(rawptr(path)), uintptr(mask))) + } else { // NOTE: arm64 does not have access + return int(intrinsics.syscall(SYS_faccessat, AT_FDCWD, uintptr(rawptr(path)), uintptr(mask))) + } +} + +sys_getcwd :: proc "contextless" (buf: rawptr, size: uint) -> int { + return int(intrinsics.syscall(SYS_getcwd, uintptr(buf), uintptr(size))) +} + +sys_chdir :: proc "contextless" (path: cstring) -> int { + return int(intrinsics.syscall(SYS_chdir, uintptr(rawptr(path)))) +} + +sys_fchdir :: proc "contextless" (fd: int) -> int { + return int(intrinsics.syscall(SYS_fchdir, uintptr(fd))) +} + +sys_chmod :: proc "contextless" (path: cstring, mode: int) -> int { + when ODIN_ARCH != .arm64 { + return int(intrinsics.syscall(SYS_chmod, uintptr(rawptr(path)), uintptr(mode))) + } else { // NOTE: arm64 does not have chmod + return int(intrinsics.syscall(SYS_fchmodat, AT_FDCWD, uintptr(rawptr(path)), uintptr(mode))) + } +} + +sys_fchmod :: proc "contextless" (fd: int, mode: int) -> int { + return int(intrinsics.syscall(SYS_fchmod, uintptr(fd), uintptr(mode))) +} + +sys_chown :: proc "contextless" (path: cstring, user: int, group: int) -> int { + when ODIN_ARCH != .arm64 { + return int(intrinsics.syscall(SYS_chown, uintptr(rawptr(path)), uintptr(user), uintptr(group))) + } else { // NOTE: arm64 does not have chown + return int(intrinsics.syscall(SYS_fchownat, AT_FDCWD, uintptr(rawptr(path)), uintptr(user), uintptr(group), 0)) + } +} + +sys_fchown :: proc "contextless" (fd: int, user: int, group: int) -> int { + return int(intrinsics.syscall(SYS_fchown, uintptr(fd), uintptr(user), uintptr(group))) +} + +sys_lchown :: proc "contextless" (path: cstring, user: int, group: int) -> int { + when ODIN_ARCH != .arm64 { + return int(intrinsics.syscall(SYS_lchown, uintptr(rawptr(path)), uintptr(user), uintptr(group))) + } else { // NOTE: arm64 does not have lchown + return int(intrinsics.syscall(SYS_fchownat, AT_FDCWD, uintptr(rawptr(path)), uintptr(user), uintptr(group), AT_SYMLINK_NOFOLLOW)) + } +} + +sys_rename :: proc "contextless" (old, new: cstring) -> int { + when ODIN_ARCH != .arm64 { + return int(intrinsics.syscall(SYS_rename, uintptr(rawptr(old)), uintptr(rawptr(new)))) + } else { // NOTE: arm64 does not have rename + return int(intrinsics.syscall(SYS_renameat, AT_FDCWD, uintptr(rawptr(old)), uintptr(rawptr(new)))) + } +} + +sys_link :: proc "contextless" (old_name: cstring, new_name: cstring) -> int { + when ODIN_ARCH != .arm64 { + return int(intrinsics.syscall(SYS_link, uintptr(rawptr(old_name)), uintptr(rawptr(new_name)))) + } else { // NOTE: arm64 does not have link + return int(intrinsics.syscall(SYS_linkat, AT_FDCWD, uintptr(rawptr(old_name)), AT_FDCWD, uintptr(rawptr(new_name)), AT_SYMLINK_FOLLOW)) + } +} + +sys_unlink :: proc "contextless" (path: cstring) -> int { + when ODIN_ARCH != .arm64 { + return int(intrinsics.syscall(SYS_unlink, uintptr(rawptr(path)))) + } else { // NOTE: arm64 does not have unlink + return int(intrinsics.syscall(SYS_unlinkat, AT_FDCWD, uintptr(rawptr(path)), 0)) + } +} + +sys_unlinkat :: proc "contextless" (dfd: int, path: cstring, flag: int = 0) -> int { + return int(intrinsics.syscall(SYS_unlinkat, uintptr(dfd), uintptr(rawptr(path)), flag)) +} + +sys_rmdir :: proc "contextless" (path: cstring) -> int { + when ODIN_ARCH != .arm64 { + return int(intrinsics.syscall(SYS_rmdir, uintptr(rawptr(path)))) + } else { // NOTE: arm64 does not have rmdir + return int(intrinsics.syscall(SYS_unlinkat, AT_FDCWD, uintptr(rawptr(path)), AT_REMOVEDIR)) + } +} + +sys_mkdir :: proc "contextless" (path: cstring, mode: int) -> int { + when ODIN_ARCH != .arm64 { + return int(intrinsics.syscall(SYS_mkdir, uintptr(rawptr(path)), uintptr(mode))) + } else { // NOTE: arm64 does not have mkdir + return int(intrinsics.syscall(SYS_mkdirat, AT_FDCWD, uintptr(rawptr(path)), uintptr(mode))) + } +} + +sys_mkdirat :: proc "contextless" (dfd: int, path: cstring, mode: int) -> int { + return int(intrinsics.syscall(SYS_mkdirat, uintptr(dfd), uintptr(rawptr(path)), uintptr(mode))) +} + +sys_mknod :: proc "contextless" (path: cstring, mode: int, dev: int) -> int { + when ODIN_ARCH != .arm64 { + return int(intrinsics.syscall(SYS_mknod, uintptr(rawptr(path)), uintptr(mode), uintptr(dev))) + } else { // NOTE: arm64 does not have mknod + return int(intrinsics.syscall(SYS_mknodat, AT_FDCWD, uintptr(rawptr(path)), uintptr(mode), uintptr(dev))) + } +} + +sys_mknodat :: proc "contextless" (dfd: int, path: cstring, mode: int, dev: int) -> int { + return int(intrinsics.syscall(SYS_mknodat, uintptr(dfd), uintptr(rawptr(path)), uintptr(mode), uintptr(dev))) +} + +sys_truncate :: proc "contextless" (path: cstring, length: i64) -> int { + when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 { + return int(intrinsics.syscall(SYS_truncate, uintptr(rawptr(path)), uintptr(length))) + } else { + low := uintptr(length & 0xFFFFFFFF) + high := uintptr(length >> 32) + return int(intrinsics.syscall(SYS_truncate64, uintptr(rawptr(path)), high, low)) + } +} + +sys_ftruncate :: proc "contextless" (fd: int, length: i64) -> int { + when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 { + return int(intrinsics.syscall(SYS_ftruncate, uintptr(fd), uintptr(length))) + } else { + low := uintptr(length & 0xFFFFFFFF) + high := uintptr(length >> 32) + return int(intrinsics.syscall(SYS_ftruncate64, uintptr(fd), high, low)) + } +} + +sys_fsync :: proc "contextless" (fd: int) -> int { + return int(intrinsics.syscall(SYS_fsync, uintptr(fd))) +} + +sys_getdents64 :: proc "contextless" (fd: int, dirent: rawptr, count: int) -> int { + return int(intrinsics.syscall(SYS_getdents64, uintptr(fd), uintptr(dirent), uintptr(count))) +} + +sys_fork :: proc "contextless" () -> int { + when ODIN_ARCH != .arm64 { + return int(intrinsics.syscall(SYS_fork)) + } else { + return int(intrinsics.syscall(SYS_clone, SIGCHLD)) + } +} + +sys_mmap :: proc "contextless" (addr: rawptr, length: uint, prot, flags, fd: int, offset: uintptr) -> int { + return int(intrinsics.syscall(SYS_mmap, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flags), uintptr(fd), offset)) +} + +sys_mremap :: proc "contextless" (addr: rawptr, old_length, new_length: uint, flags: int, new_addr: rawptr = nil) -> int { + return int(intrinsics.syscall(SYS_mremap, uintptr(addr), uintptr(old_length), uintptr(new_length), uintptr(flags), uintptr(new_addr))) +} + +sys_munmap :: proc "contextless" (addr: rawptr, length: uint) -> int { + return int(intrinsics.syscall(SYS_munmap, uintptr(addr), uintptr(length))) +} + +sys_mprotect :: proc "contextless" (addr: rawptr, length: uint, prot: int) -> int { + return int(intrinsics.syscall(SYS_mprotect, uintptr(addr), uintptr(length), uintptr(prot))) +} + +sys_madvise :: proc "contextless" (addr: rawptr, length: uint, advice: int) -> int { + return int(intrinsics.syscall(SYS_madvise, uintptr(addr), uintptr(length), uintptr(advice))) +} + + +// NOTE: Unsure about if this works directly on 32 bit archs. It may need 32 bit version of the time struct. +// As of Linux 5.1, there is a utimensat_time64 function. Maybe use this in the future? +sys_utimensat :: proc "contextless" (dfd: int, path: cstring, times: rawptr, flags: int) -> int { + return int(intrinsics.syscall(SYS_utimensat, uintptr(dfd), uintptr(rawptr(path)), uintptr(times), uintptr(flags))) +} + +get_errno :: proc "contextless" (res: int) -> i32 { + if res < 0 && res > -4096 { + return i32(-res) + } + return 0 +} diff --git a/core/sys/unix/time_unix.odin b/core/sys/unix/time_unix.odin new file mode 100644 index 000000000..fa3a7a29d --- /dev/null +++ b/core/sys/unix/time_unix.odin @@ -0,0 +1,75 @@ +//+build linux, darwin, freebsd, openbsd +package unix + +when ODIN_OS == .Darwin { + foreign import libc "System.framework" +} else { + foreign import libc "system:c" +} + +import "core:c" + +@(default_calling_convention="c") +foreign libc { + clock_gettime :: proc(clock_id: u64, timespec: ^timespec) -> c.int --- + sleep :: proc(seconds: c.uint) -> c.int --- + nanosleep :: proc(requested, remaining: ^timespec) -> c.int --- +} + +timespec :: struct { + tv_sec: i64, // seconds + tv_nsec: i64, // nanoseconds +} + +when ODIN_OS == .OpenBSD { + CLOCK_REALTIME :: 0 + CLOCK_PROCESS_CPUTIME_ID :: 2 + CLOCK_MONOTONIC :: 3 + CLOCK_THREAD_CPUTIME_ID :: 4 + CLOCK_UPTIME :: 5 + CLOCK_BOOTTIME :: 6 + + // CLOCK_MONOTONIC_RAW doesn't exist, use CLOCK_MONOTONIC + CLOCK_MONOTONIC_RAW :: CLOCK_MONOTONIC +} else { + CLOCK_REALTIME :: 0 // NOTE(tetra): May jump in time, when user changes the system time. + CLOCK_MONOTONIC :: 1 // NOTE(tetra): May stand still while system is asleep. + CLOCK_PROCESS_CPUTIME_ID :: 2 + CLOCK_THREAD_CPUTIME_ID :: 3 + CLOCK_MONOTONIC_RAW :: 4 // NOTE(tetra): "RAW" means: Not adjusted by NTP. + CLOCK_REALTIME_COARSE :: 5 // NOTE(tetra): "COARSE" clocks are apparently much faster, but not "fine-grained." + CLOCK_MONOTONIC_COARSE :: 6 + CLOCK_BOOTTIME :: 7 // NOTE(tetra): Same as MONOTONIC, except also including time system was asleep. + CLOCK_REALTIME_ALARM :: 8 + CLOCK_BOOTTIME_ALARM :: 9 +} + +// TODO(tetra, 2019-11-05): The original implementation of this package for Darwin used this constants. +// I do not know if Darwin programmers are used to the existance of these constants or not, so +// I'm leaving aliases to them for now. +CLOCK_SYSTEM :: CLOCK_REALTIME +CLOCK_CALENDAR :: CLOCK_MONOTONIC + +boot_time_in_nanoseconds :: proc "c" () -> i64 { + ts_now, ts_boottime: timespec + clock_gettime(CLOCK_REALTIME, &ts_now) + clock_gettime(CLOCK_BOOTTIME, &ts_boottime) + + ns := (ts_now.tv_sec - ts_boottime.tv_sec) * 1e9 + ts_now.tv_nsec - ts_boottime.tv_nsec + return i64(ns) +} + +seconds_since_boot :: proc "c" () -> f64 { + ts_boottime: timespec + clock_gettime(CLOCK_BOOTTIME, &ts_boottime) + return f64(ts_boottime.tv_sec) + f64(ts_boottime.tv_nsec) / 1e9 +} + + +inline_nanosleep :: proc "c" (nanoseconds: i64) -> (remaining: timespec, res: i32) { + s, ns := nanoseconds / 1e9, nanoseconds % 1e9 + requested := timespec{tv_sec=s, tv_nsec=ns} + res = nanosleep(&requested, &remaining) + return +} + diff --git a/core/sys/wasm/wasi/wasi_api.odin b/core/sys/wasm/wasi/wasi_api.odin index 2e2a99617..d4f0d19cf 100644 --- a/core/sys/wasm/wasi/wasi_api.odin +++ b/core/sys/wasm/wasi/wasi_api.odin @@ -1148,6 +1148,156 @@ foreign wasi { */ how: sdflags_t, ) -> errno_t --- + + + /** + * Return a description of the given preopened file descriptor. + */ + fd_prestat_dir_name :: proc( + fd: fd_t, + /** + * A buffer into which to write the preopened directory name. + */ + path: string, + ) -> errno_t --- + /** + * Create a directory. + * Note: This is similar to `mkdirat` in POSIX. + */ + path_create_directory :: proc( + fd: fd_t, + /** + * The path at which to create the directory. + */ + path: string, + ) -> errno_t --- + /** + * Adjust the timestamps of a file or directory. + * Note: This is similar to `utimensat` in POSIX. + */ + path_filestat_set_times :: proc( + fd: fd_t, + /** + * Flags determining the method of how the path is resolved. + */ + flags: lookupflags_t, + /** + * The path of the file or directory to operate on. + */ + path: string, + /** + * The desired values of the data access timestamp. + */ + atim: timestamp_t, + /** + * The desired values of the data modification timestamp. + */ + mtim: timestamp_t, + /** + * A bitmask indicating which timestamps to adjust. + */ + fst_flags: fstflags_t, + ) -> errno_t --- + /** + * Remove a directory. + * Return `errno::notempty` if the directory is not empty. + * Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. + */ + path_remove_directory :: proc( + fd: fd_t, + /** + * The path to a directory to remove. + */ + path: string, + ) -> errno_t --- + /** + * Create a hard link. + * Note: This is similar to `linkat` in POSIX. + */ + path_link :: proc( + old_fd: fd_t, + /** + * Flags determining the method of how the path is resolved. + */ + old_flags: lookupflags_t, + /** + * The source path from which to link. + */ + old_path: string, + /** + * The working directory at which the resolution of the new path starts. + */ + new_fd: fd_t, + /** + * The destination path at which to create the hard link. + */ + new_path: string, + ) -> errno_t --- + + /** + * Rename a file or directory. + * Note: This is similar to `renameat` in POSIX. + */ + path_rename :: proc( + fd: fd_t, + /** + * The source path of the file or directory to rename. + */ + old_path: string, + /** + * The working directory at which the resolution of the new path starts. + */ + new_fd: fd_t, + /** + * The destination path to which to rename the file or directory. + */ + new_path: string, + ) -> errno_t --- + + /** + * Create a symbolic link. + * Note: This is similar to `symlinkat` in POSIX. + */ + path_symlink :: proc( + /** + * The contents of the symbolic link. + */ + old_path: string, + fd: fd_t, + /** + * The destination path at which to create the symbolic link. + */ + new_path: string, + ) -> errno_t --- + + /** + * Unlink a file. + * Return `errno::isdir` if the path refers to a directory. + * Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. + */ + path_unlink_file :: proc( + fd: fd_t, + /** + * The path to a file to unlink. + */ + path: string, + ) -> errno_t --- + + /** + * Write high-quality random data into a buffer. + * This function blocks when the implementation is unable to immediately + * provide sufficient high-quality random data. + * This function may execute slowly, so when large mounts of random data are + * required, it's advisable to use this function to seed a pseudo-random + * number generator, rather than to provide the random data directly. + */ + random_get :: proc( + /** + * The buffer to fill with random data. + */ + buf: []u8, + ) -> errno_t --- + } /** @@ -1250,7 +1400,7 @@ fd_pread :: proc "c" ( */ offset: filesize_t, ) -> (n: size_t, err: errno_t) { - err = wasi_fd_pread(fd, raw_data(iovs), len(iovs), offset, &n) + err = wasi_fd_pread(fd, iovs, offset, &n) return } /** @@ -1281,7 +1431,7 @@ fd_pwrite :: proc "c" ( */ offset: filesize_t, ) -> (n: size_t, err: errno_t) { - err = wasi_fd_pwrite(fd, raw_data(iovs), len(iovs), offset, &n) + err = wasi_fd_pwrite(fd, iovs, offset, &n) return } /** @@ -1297,7 +1447,7 @@ fd_read :: proc "c" ( */ iovs: []iovec_t, ) -> (n: size_t, err: errno_t) { - err = wasi_fd_read(fd, raw_data(iovs), len(iovs), &n) + err = wasi_fd_read(fd, iovs, &n) return } /** @@ -1324,7 +1474,7 @@ fd_readdir :: proc "c" ( */ cookie: dircookie_t, ) -> (n: size_t, err: errno_t) { - err = wasi_fd_readdir(fd, raw_data(buf), len(buf), cookie, &n) + err = wasi_fd_readdir(fd, buf, cookie, &n) return } /** @@ -1370,7 +1520,7 @@ fd_write :: proc "c" ( */ iovs: []ciovec_t, ) -> (n: size_t, err: errno_t) { - err = wasi_fd_write(fd, raw_data(iovs), len(iovs), &n) + err = wasi_fd_write(fd, iovs, &n) return } /** @@ -1390,7 +1540,7 @@ path_filestat_get :: proc "c" ( */ path: string, ) -> (offset: filestat_t, err: errno_t) { - err = wasi_path_filestat_get(fd, flags, raw_data(path), len(path), &offset) + err = wasi_path_filestat_get(fd, flags, path, &offset) return } /** @@ -1432,7 +1582,7 @@ path_open :: proc "c" ( fs_rights_inheriting: rights_t, fdflags: fdflags_t, ) -> (file: fd_t, err: errno_t) { - err = wasi_path_open(fd, dirflags, raw_data(path), len(path), oflags, fs_rights_base, fs_rights_inheriting, fdflags, &file) + err = wasi_path_open(fd, dirflags, path, oflags, fs_rights_base, fs_rights_inheriting, fdflags, &file) return } /** @@ -1452,7 +1602,7 @@ path_readlink :: proc "c" ( */ buf: []u8, ) -> (n: size_t, err: errno_t) { - err = wasi_path_readlink(fd, raw_data(path), len(path), raw_data(buf), len(buf), &n) + err = wasi_path_readlink(fd, path, buf, &n) return } /** @@ -1495,7 +1645,7 @@ sock_recv :: proc "c" ( */ ri_flags: riflags_t, ) -> (n: size_t, flags: roflags_t, err: errno_t) { - err = wasi_sock_recv(fd, raw_data(ri_data), len(ri_data), ri_flags, &n, &flags) + err = wasi_sock_recv(fd, ri_data, ri_flags, &n, &flags) return } /** @@ -1516,172 +1666,11 @@ sock_send :: proc "c" ( */ si_flags: siflags_t, ) -> (n: size_t, err: errno_t) { - err = wasi_sock_send(fd, raw_data(si_data), len(si_data), si_flags, &n) + err = wasi_sock_send(fd, si_data, si_flags, &n) return } -/** - * Return a description of the given preopened file descriptor. - */ -fd_prestat_dir_name :: proc( - fd: fd_t, - /** - * A buffer into which to write the preopened directory name. - */ - path: string, -) -> errno_t { - return wasm_fd_prestat_dir_name(fd, raw_data(path), len(path)) -} -/** - * Create a directory. - * Note: This is similar to `mkdirat` in POSIX. - */ -path_create_directory :: proc( - fd: fd_t, - /** - * The path at which to create the directory. - */ - path: string, -) -> errno_t { - return wasm_path_create_directory(fd, raw_data(path), len(path)) -} -/** - * Adjust the timestamps of a file or directory. - * Note: This is similar to `utimensat` in POSIX. - */ -path_filestat_set_times :: proc( - fd: fd_t, - /** - * Flags determining the method of how the path is resolved. - */ - flags: lookupflags_t, - /** - * The path of the file or directory to operate on. - */ - path: string, - /** - * The desired values of the data access timestamp. - */ - atim: timestamp_t, - /** - * The desired values of the data modification timestamp. - */ - mtim: timestamp_t, - /** - * A bitmask indicating which timestamps to adjust. - */ - fst_flags: fstflags_t, -) -> errno_t { - return wasm_path_filestat_set_times(fd, flags, raw_data(path), len(path), atim, mtim, fst_flags) -} -/** - * Remove a directory. - * Return `errno::notempty` if the directory is not empty. - * Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. - */ -path_remove_directory :: proc( - fd: fd_t, - /** - * The path to a directory to remove. - */ - path: string, -) -> errno_t { - return wasm_path_remove_directory(fd, raw_data(path), len(path)) -} -/** - * Create a hard link. - * Note: This is similar to `linkat` in POSIX. - */ -path_link :: proc( - old_fd: fd_t, - /** - * Flags determining the method of how the path is resolved. - */ - old_flags: lookupflags_t, - /** - * The source path from which to link. - */ - old_path: string, - /** - * The working directory at which the resolution of the new path starts. - */ - new_fd: fd_t, - /** - * The destination path at which to create the hard link. - */ - new_path: string, -) -> errno_t { - return wasm_path_link(old_fd, old_flags, raw_data(old_path), len(old_path), new_fd, raw_data(new_path), len(new_path)) -} -/** - * Rename a file or directory. - * Note: This is similar to `renameat` in POSIX. - */ -path_rename :: proc( - fd: fd_t, - /** - * The source path of the file or directory to rename. - */ - old_path: string, - /** - * The working directory at which the resolution of the new path starts. - */ - new_fd: fd_t, - /** - * The destination path to which to rename the file or directory. - */ - new_path: string, -) -> errno_t { - return wasm_path_rename(fd, raw_data(old_path), len(old_path), new_fd, raw_data(new_path), len(new_path)) -} -/** - * Create a symbolic link. - * Note: This is similar to `symlinkat` in POSIX. - */ -path_symlink :: proc( - /** - * The contents of the symbolic link. - */ - old_path: string, - fd: fd_t, - /** - * The destination path at which to create the symbolic link. - */ - new_path: string, -) -> errno_t { - return wasm_path_symlink(raw_data(old_path), len(old_path), fd, raw_data(new_path), len(new_path)) -} -/** - * Unlink a file. - * Return `errno::isdir` if the path refers to a directory. - * Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. - */ -path_unlink_file :: proc( - fd: fd_t, - /** - * The path to a file to unlink. - */ - path: string, -) -> errno_t { - return wasm_path_unlink_file(fd, raw_data(path), len(path)) -} -/** - * Write high-quality random data into a buffer. - * This function blocks when the implementation is unable to immediately - * provide sufficient high-quality random data. - * This function may execute slowly, so when large mounts of random data are - * required, it's advisable to use this function to seed a pseudo-random - * number generator, rather than to provide the random data directly. - */ -random_get :: proc( - /** - * The buffer to fill with random data. - */ - buf: []u8, -) -> errno_t { - return wasm_random_get(raw_data(buf), len(buf)) -} @@ -1722,8 +1711,7 @@ foreign wasi { @(link_name="fd_pread") wasi_fd_pread :: proc( fd: fd_t, - iovs: [^]iovec_t, - iovs_len: size_t, + iovs: []iovec_t, offset: filesize_t, retptr0: ^size_t, ) -> errno_t --- @@ -1735,23 +1723,20 @@ foreign wasi { @(link_name="fd_pwrite") wasi_fd_pwrite :: proc( fd: fd_t, - iovs: [^]ciovec_t, - iovs_len: size_t, + iovs: []ciovec_t, offset: filesize_t, retptr0: ^size_t, ) -> errno_t --- @(link_name="fd_read") wasi_fd_read :: proc( fd: fd_t, - iovs: [^]iovec_t, - iovs_len: size_t, + iovs: []iovec_t, retptr0: ^size_t, ) -> errno_t --- @(link_name="fd_readdir") wasi_fd_readdir :: proc( fd: fd_t, - buf: [^]u8, - buf_len: size_t, + buf: []u8, cookie: dircookie_t, retptr0: ^size_t, ) -> errno_t --- @@ -1770,8 +1755,7 @@ foreign wasi { @(link_name="fd_write") wasi_fd_write :: proc( fd: fd_t, - iovs: [^]ciovec_t, - iovs_len: size_t, + iovs: []ciovec_t, retptr0: ^size_t, ) -> errno_t --- @(link_name="path_filestat_get") @@ -1781,16 +1765,14 @@ foreign wasi { /** * The path of the file or directory to inspect. */ - path: [^]u8, - path_len: size_t, + path: string, retptr0: ^filestat_t, ) -> errno_t --- @(link_name="path_open") wasi_path_open :: proc( fd: fd_t, dirflags: lookupflags_t, - path: [^]u8, - path_len: size_t, + path: string, oflags: oflags_t, fs_rights_base: rights_t, fs_rights_inheriting: rights_t, @@ -1800,10 +1782,8 @@ foreign wasi { @(link_name="path_readlink") wasi_path_readlink :: proc( fd: fd_t, - path: [^]u8, - path_len: size_t, - buf: [^]u8, - buf_len: size_t, + path: string, + buf: []u8, retptr0: ^size_t, ) -> errno_t --- @(link_name="poll_oneoff") @@ -1816,8 +1796,7 @@ foreign wasi { @(link_name="sock_recv") wasi_sock_recv :: proc( fd: fd_t, - ri_data: [^]iovec_t, - ri_data_len: size_t, + ri_data: []iovec_t, ri_flags: riflags_t, retptr0: ^size_t, retptr1: ^roflags_t, @@ -1825,75 +1804,8 @@ foreign wasi { @(link_name="sock_send") wasi_sock_send :: proc( fd: fd_t, - si_data: [^]ciovec_t, - si_data_len: size_t, + si_data: []ciovec_t, si_flags: siflags_t, retptr0: ^size_t, ) -> errno_t --- - @(link_name="fd_prestat_dir_name") - wasm_fd_prestat_dir_name :: proc( - fd: fd_t, - path: [^]u8, - path_len: size_t, - ) -> errno_t --- - @(link_name="path_create_directory") - wasm_path_create_directory :: proc( - fd: fd_t, - path: [^]u8, - path_len: size_t, - ) -> errno_t --- - @(link_name="path_filestat_set_times") - wasm_path_filestat_set_times :: proc( - fd: fd_t, - flags: lookupflags_t, - path: [^]u8, - path_len: size_t, - atim: timestamp_t, - mtim: timestamp_t, - fst_flags: fstflags_t, - ) -> errno_t --- - @(link_name="path_remove_directory") - wasm_path_remove_directory :: proc( - fd: fd_t, - path: [^]u8, - path_len: size_t, - ) -> errno_t --- - @(link_name="path_link") - wasm_path_link :: proc( - old_fd: fd_t, - old_flags: lookupflags_t, - old_path: [^]u8, - old_path_len: size_t, - new_fd: fd_t, - new_path: [^]u8, - new_path_len: size_t, - ) -> errno_t --- - @(link_name="path_rename") - wasm_path_rename :: proc( - fd: fd_t, - old_path: [^]u8, - old_path_len: size_t, - new_fd: fd_t, - new_path: [^]u8, - new_path_len: size_t, - ) -> errno_t --- - @(link_name="path_symlink") - wasm_path_symlink :: proc( - old_path: [^]u8, - old_path_len: size_t, - fd: fd_t, - new_path: [^]u8, - new_path_len: size_t, - ) -> errno_t --- - @(link_name="path_unlink_file") - wasm_path_unlink_file :: proc( - fd: fd_t, - path: [^]u8, - path_len: size_t, - ) -> errno_t --- - @(link_name="random_get") - wasm_random_get :: proc( - buf: [^]u8, - buf_len: size_t, - ) -> errno_t --- } diff --git a/core/sys/win32/crt.odin b/core/sys/win32/crt.odin deleted file mode 100644 index b39d35375..000000000 --- a/core/sys/win32/crt.odin +++ /dev/null @@ -1,15 +0,0 @@ -// +build windows -package win32 - -import "core:strings" - -foreign { - @(link_name="_wgetcwd") _get_cwd_wide :: proc(buffer: Wstring, buf_len: int) -> ^Wstring --- -} - -get_cwd :: proc(allocator := context.temp_allocator) -> string { - buffer := make([]u16, MAX_PATH_WIDE, allocator) - _get_cwd_wide(Wstring(&buffer[0]), MAX_PATH_WIDE) - file := utf16_to_utf8(buffer[:], allocator) - return strings.trim_right_null(file) -} diff --git a/core/sys/win32/gdi32.odin b/core/sys/win32/gdi32.odin deleted file mode 100644 index 13bc4796f..000000000 --- a/core/sys/win32/gdi32.odin +++ /dev/null @@ -1,26 +0,0 @@ -// +build windows -package win32 - -foreign import "system:gdi32.lib" - -WHITENESS :: 0x00FF0062 -BLACKNESS :: 0x00000042 - -@(default_calling_convention = "std") -foreign gdi32 { - @(link_name="GetStockObject") get_stock_object :: proc(fn_object: i32) -> Hgdiobj --- - - @(link_name="StretchDIBits") - stretch_dibits :: proc(hdc: Hdc, - x_dst, y_dst, width_dst, height_dst: i32, - x_src, y_src, width_src, header_src: i32, - bits: rawptr, bits_info: ^Bitmap_Info, - usage: u32, - rop: u32) -> i32 --- - - @(link_name="SetPixelFormat") set_pixel_format :: proc(hdc: Hdc, pixel_format: i32, pfd: ^Pixel_Format_Descriptor) -> Bool --- - @(link_name="ChoosePixelFormat") choose_pixel_format :: proc(hdc: Hdc, pfd: ^Pixel_Format_Descriptor) -> i32 --- - @(link_name="SwapBuffers") swap_buffers :: proc(hdc: Hdc) -> Bool --- - - @(link_name="PatBlt") pat_blt :: proc(hdc: Hdc, x, y, w, h: i32, rop: u32) -> Bool --- -} diff --git a/core/sys/win32/general.odin b/core/sys/win32/general.odin deleted file mode 100644 index d53bf8a4f..000000000 --- a/core/sys/win32/general.odin +++ /dev/null @@ -1,1176 +0,0 @@ -// +build windows -package win32 - -Uint_Ptr :: distinct uintptr -Int_Ptr :: distinct int -Long_Ptr :: distinct int - -Handle :: distinct rawptr -Hwnd :: distinct Handle -Hdc :: distinct Handle -Hinstance :: distinct Handle -Hicon :: distinct Handle -Hcursor :: distinct Handle -Hmenu :: distinct Handle -Hbitmap :: distinct Handle -Hbrush :: distinct Handle -Hgdiobj :: distinct Handle -Hmodule :: distinct Handle -Hmonitor :: distinct Handle -Hrawinput :: distinct Handle -Hresult :: distinct i32 -HKL :: distinct Handle -Wparam :: distinct Uint_Ptr -Lparam :: distinct Long_Ptr -Lresult :: distinct Long_Ptr -Wnd_Proc :: distinct #type proc "std" (Hwnd, u32, Wparam, Lparam) -> Lresult -Monitor_Enum_Proc :: distinct #type proc "std" (Hmonitor, Hdc, ^Rect, Lparam) -> bool - - - -Bool :: distinct b32 - -Wstring :: distinct ^u16 - -Point :: struct { - x, y: i32, -} - -Wnd_Class_A :: struct { - style: u32, - wnd_proc: Wnd_Proc, - cls_extra, wnd_extra: i32, - instance: Hinstance, - icon: Hicon, - cursor: Hcursor, - background: Hbrush, - menu_name, class_name: cstring, -} - -Wnd_Class_W :: struct { - style: u32, - wnd_proc: Wnd_Proc, - cls_extra, wnd_extra: i32, - instance: Hinstance, - icon: Hicon, - cursor: Hcursor, - background: Hbrush, - menu_name, class_name: Wstring, -} - -Wnd_Class_Ex_A :: struct { - size, style: u32, - wnd_proc: Wnd_Proc, - cls_extra, wnd_extra: i32, - instance: Hinstance, - icon: Hicon, - cursor: Hcursor, - background: Hbrush, - menu_name, class_name: cstring, - sm: Hicon, -} - -Wnd_Class_Ex_W :: struct { - size, style: u32, - wnd_proc: Wnd_Proc, - cls_extra, wnd_extra: i32, - instance: Hinstance, - icon: Hicon, - cursor: Hcursor, - background: Hbrush, - menu_name, class_name: Wstring, - sm: Hicon, -} - - -Msg :: struct { - hwnd: Hwnd, - message: u32, - wparam: Wparam, - lparam: Lparam, - time: u32, - pt: Point, -} - -Rect :: struct { - left: i32, - top: i32, - right: i32, - bottom: i32, -} - -Dev_Mode_A :: struct { - device_name: [32]u8, - spec_version: u16, - driver_version: u16, - size: u16, - driver_extra: u16, - fields: u32, - using _: struct #raw_union { - // Printer only fields. - using _: struct { - orientation: i16, - paper_size: i16, - paper_length: i16, - paper_width: i16, - scale: i16, - copies: i16, - default_source: i16, - print_quality: i16, - }, - // Display only fields. - using _: struct { - position: Point, - display_orientation: u32, - display_fixed_output: u32, - }, - }, - color: i16, - duplex: i16, - y_resolution: i16, - tt_option: i16, - collate: i16, - form_name: [32]u8, - log_pixels: u16, - bits_per_pel: u32, - pels_width: u32, - pels_height: u32, - using _: struct #raw_union { - display_flags: u32, - nup: u32, - }, - display_frequency: u32, - icm_method: u32, - icm_intent: u32, - media_type: u32, - dither_type: u32, - reserved_1: u32, - reserved_2: u32, - panning_width: u32, - panning_height: u32, -} - -Filetime :: struct { - lo, hi: u32, -} - -Systemtime :: struct { - year, month: u16, - day_of_week, day: u16, - hour, minute, second, millisecond: u16, -} - -By_Handle_File_Information :: struct { - file_attributes: u32, - creation_time, - last_access_time, - last_write_time: Filetime, - volume_serial_number, - file_size_high, - file_size_low, - number_of_links, - file_index_high, - file_index_low: u32, -} - -File_Attribute_Data :: struct { - file_attributes: u32, - creation_time, - last_access_time, - last_write_time: Filetime, - file_size_high, - file_size_low: u32, -} - -// NOTE(Jeroen): The widechar version might want at least the 32k MAX_PATH_WIDE -// https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-findfirstfilew#parameters -Find_Data_W :: struct{ - file_attributes: u32, - creation_time: Filetime, - last_access_time: Filetime, - last_write_time: Filetime, - file_size_high: u32, - file_size_low: u32, - reserved0: u32, - reserved1: u32, - file_name: [MAX_PATH]u16, - alternate_file_name: [14]u16, -} - -Find_Data_A :: struct{ - file_attributes: u32, - creation_time: Filetime, - last_access_time: Filetime, - last_write_time: Filetime, - file_size_high: u32, - file_size_low: u32, - reserved0: u32, - reserved1: u32, - file_name: [MAX_PATH]byte, - alternate_file_name: [14]byte, -} - -Security_Attributes :: struct { - length: u32, - security_descriptor: rawptr, - inherit_handle: Bool, -} - -Process_Information :: struct { - process: Handle, - thread: Handle, - process_id: u32, - thread_id: u32, -} - -Startup_Info :: struct { - cb: u32, - reserved: Wstring, - desktop: Wstring, - title: Wstring, - x: u32, - y: u32, - x_size: u32, - y_size: u32, - x_count_chars: u32, - y_count_chars: u32, - fill_attribute: u32, - flags: u32, - show_window: u16, - _: u16, - _: cstring, - stdin: Handle, - stdout: Handle, - stderr: Handle, -} - -Pixel_Format_Descriptor :: struct { - size, - version, - flags: u32, - - pixel_type, - color_bits, - red_bits, - red_shift, - green_bits, - green_shift, - blue_bits, - blue_shift, - alpha_bits, - alpha_shift, - accum_bits, - accum_red_bits, - accum_green_bits, - accum_blue_bits, - accum_alpha_bits, - depth_bits, - stencil_bits, - aux_buffers, - layer_type, - reserved: byte, - - layer_mask, - visible_mask, - damage_mask: u32, -} - -Critical_Section :: struct { - debug_info: ^Critical_Section_Debug, - - lock_count: i32, - recursion_count: i32, - owning_thread: Handle, - lock_semaphore: Handle, - spin_count: ^u32, -} - -Critical_Section_Debug :: struct { - typ: u16, - creator_back_trace_index: u16, - critical_section: ^Critical_Section, - process_locks_list: ^List_Entry, - entry_count: u32, - contention_count: u32, - flags: u32, - creator_back_trace_index_high: u16, - spare_word: u16, -} - -List_Entry :: struct {flink, blink: ^List_Entry} - - -Raw_Input_Device :: struct { - usage_page: u16, - usage: u16, - flags: u32, - wnd_target: Hwnd, -} - -Raw_Input_Header :: struct { - kind: u32, - size: u32, - device: Handle, - wparam: Wparam, -} - -Raw_HID :: struct { - size_hid: u32, - count: u32, - raw_data: [1]byte, -} - -Raw_Keyboard :: struct { - make_code: u16, - flags: u16, - reserved: u16, - vkey: u16, - message: u32, - extra_information: u32, -} - -Raw_Mouse :: struct { - flags: u16, - using data: struct #raw_union { - buttons: u32, - using _: struct { - button_flags: u16, - button_data: u16, - }, - }, - raw_buttons: u32, - last_x: i32, - last_y: i32, - extra_information: u32, -} - -Raw_Input :: struct { - using header: Raw_Input_Header, - data: struct #raw_union { - mouse: Raw_Mouse, - keyboard: Raw_Keyboard, - hid: Raw_HID, - }, -} - - -Overlapped :: struct { - internal: ^u64, - internal_high: ^u64, - using _: struct #raw_union { - using _: struct { - offset: u32, - offset_high: u32, - }, - pointer: rawptr, - }, - event: Handle, -} - -File_Notify_Information :: struct { - next_entry_offset: u32, - action: u32, - file_name_length: u32, - file_name: [1]u16, -} - -// https://docs.microsoft.com/en-gb/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info -System_Info :: struct { - using _: struct #raw_union { - oem_id: u32, - using _: struct #raw_union { - processor_architecture: u16, - _: u16, // reserved - }, - }, - page_size: u32, - minimum_application_address: rawptr, - maximum_application_address: rawptr, - active_processor_mask: u32, - number_of_processors: u32, - processor_type: u32, - allocation_granularity: u32, - processor_level: u16, - processor_revision: u16, -} - -// https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-_osversioninfoexa -OS_Version_Info_Ex_A :: struct { - os_version_info_size: u32, - major_version: u32, - minor_version: u32, - build_number: u32, - platform_id : u32, - service_pack_string: [128]u8, - service_pack_major: u16, - service_pack_minor: u16, - suite_mask: u16, - product_type: u8, - reserved: u8, -} - -MAPVK_VK_TO_VSC :: 0 -MAPVK_VSC_TO_VK :: 1 -MAPVK_VK_TO_CHAR :: 2 -MAPVK_VSC_TO_VK_EX :: 3 - -//WinUser.h -ENUM_CURRENT_SETTINGS :: u32(4294967295) // (DWORD)-1 -ENUM_REGISTRY_SETTINGS :: u32(4294967294) // (DWORD)-2 - -VK_LBUTTON :: 0x01 -VK_RBUTTON :: 0x02 -VK_CANCEL :: 0x03 -VK_MBUTTON :: 0x04 /* NOT contiguous with L & RBUTTON */ -VK_XBUTTON1 :: 0x05 /* NOT contiguous with L & RBUTTON */ -VK_XBUTTON2 :: 0x06 /* NOT contiguous with L & RBUTTON */ - -/* - * :: 0x07 : reserved - */ - -VK_BACK :: 0x08 -VK_TAB :: 0x09 - -/* - * :: 0x0A - :: 0x0B : reserved - */ - -VK_CLEAR :: 0x0C -VK_RETURN :: 0x0D - -/* - * :: 0x0E - :: 0x0F : unassigned - */ - -VK_SHIFT :: 0x10 -VK_CONTROL :: 0x11 -VK_MENU :: 0x12 -VK_PAUSE :: 0x13 -VK_CAPITAL :: 0x14 - -VK_KANA :: 0x15 -VK_HANGEUL :: 0x15 /* old name - should be here for compatibility */ -VK_HANGUL :: 0x15 - -/* - * :: 0x16 : unassigned - */ - -VK_JUNJA :: 0x17 -VK_FINAL :: 0x18 -VK_HANJA :: 0x19 -VK_KANJI :: 0x19 - -/* - * :: 0x1A : unassigned - */ - -VK_ESCAPE :: 0x1B - -VK_CONVERT :: 0x1C -VK_NONCONVERT :: 0x1D -VK_ACCEPT :: 0x1E -VK_MODECHANGE :: 0x1F - -VK_SPACE :: 0x20 -VK_PRIOR :: 0x21 -VK_NEXT :: 0x22 -VK_END :: 0x23 -VK_HOME :: 0x24 -VK_LEFT :: 0x25 -VK_UP :: 0x26 -VK_RIGHT :: 0x27 -VK_DOWN :: 0x28 -VK_SELECT :: 0x29 -VK_PRINT :: 0x2A -VK_EXECUTE :: 0x2B -VK_SNAPSHOT :: 0x2C -VK_INSERT :: 0x2D -VK_DELETE :: 0x2E -VK_HELP :: 0x2F - -/* - * VK_0 - VK_9 are the same as ASCII '0' - '9' (:: 0x30 - :: 0x39) - * :: 0x3A - :: 0x40 : unassigned - * VK_A - VK_Z are the same as ASCII 'A' - 'Z' (:: 0x41 - :: 0x5A) - */ - -VK_LWIN :: 0x5B -VK_RWIN :: 0x5C -VK_APPS :: 0x5D - -/* - * :: 0x5E : reserved - */ - -VK_SLEEP :: 0x5F - -VK_NUMPAD0 :: 0x60 -VK_NUMPAD1 :: 0x61 -VK_NUMPAD2 :: 0x62 -VK_NUMPAD3 :: 0x63 -VK_NUMPAD4 :: 0x64 -VK_NUMPAD5 :: 0x65 -VK_NUMPAD6 :: 0x66 -VK_NUMPAD7 :: 0x67 -VK_NUMPAD8 :: 0x68 -VK_NUMPAD9 :: 0x69 -VK_MULTIPLY :: 0x6A -VK_ADD :: 0x6B -VK_SEPARATOR :: 0x6C -VK_SUBTRACT :: 0x6D -VK_DECIMAL :: 0x6E -VK_DIVIDE :: 0x6F -VK_F1 :: 0x70 -VK_F2 :: 0x71 -VK_F3 :: 0x72 -VK_F4 :: 0x73 -VK_F5 :: 0x74 -VK_F6 :: 0x75 -VK_F7 :: 0x76 -VK_F8 :: 0x77 -VK_F9 :: 0x78 -VK_F10 :: 0x79 -VK_F11 :: 0x7A -VK_F12 :: 0x7B -VK_F13 :: 0x7C -VK_F14 :: 0x7D -VK_F15 :: 0x7E -VK_F16 :: 0x7F -VK_F17 :: 0x80 -VK_F18 :: 0x81 -VK_F19 :: 0x82 -VK_F20 :: 0x83 -VK_F21 :: 0x84 -VK_F22 :: 0x85 -VK_F23 :: 0x86 -VK_F24 :: 0x87 - -INVALID_HANDLE :: Handle(~uintptr(0)) - -CREATE_SUSPENDED :: 0x00000004 -STACK_SIZE_PARAM_IS_A_RESERVATION :: 0x00010000 -WAIT_ABANDONED :: 0x00000080 -WAIT_OBJECT_0 :: 0 -WAIT_TIMEOUT :: 0x00000102 -WAIT_FAILED :: 0xffffffff - -CS_VREDRAW :: 0x0001 -CS_HREDRAW :: 0x0002 -CS_OWNDC :: 0x0020 -CW_USEDEFAULT :: -0x80000000 - -WS_OVERLAPPED :: 0 -WS_MAXIMIZEBOX :: 0x00010000 -WS_MINIMIZEBOX :: 0x00020000 -WS_THICKFRAME :: 0x00040000 -WS_SYSMENU :: 0x00080000 -WS_BORDER :: 0x00800000 -WS_CAPTION :: 0x00C00000 -WS_VISIBLE :: 0x10000000 -WS_POPUP :: 0x80000000 -WS_MAXIMIZE :: 0x01000000 -WS_MINIMIZE :: 0x20000000 -WS_OVERLAPPEDWINDOW :: WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_THICKFRAME|WS_MINIMIZEBOX|WS_MAXIMIZEBOX -WS_POPUPWINDOW :: WS_POPUP | WS_BORDER | WS_SYSMENU - -WS_EX_DLGMODALFRAME :: 0x00000001 -WS_EX_NOPARENTNOTIFY :: 0x00000004 -WS_EX_TOPMOST :: 0x00000008 -WS_EX_ACCEPTFILES :: 0x00000010 -WS_EX_TRANSPARENT :: 0x00000020 -WS_EX_MDICHILD :: 0x00000040 -WS_EX_TOOLWINDOW :: 0x00000080 -WS_EX_WINDOWEDGE :: 0x00000100 -WS_EX_CLIENTEDGE :: 0x00000200 -WS_EX_CONTEXTHELP :: 0x00000400 -WS_EX_RIGHT :: 0x00001000 -WS_EX_LEFT :: 0x00000000 -WS_EX_RTLREADING :: 0x00002000 -WS_EX_LTRREADING :: 0x00000000 -WS_EX_LEFTSCROLLBAR :: 0x00004000 -WS_EX_RIGHTSCROLLBAR :: 0x00000000 -WS_EX_CONTROLPARENT :: 0x00010000 -WS_EX_STATICEDGE :: 0x00020000 -WS_EX_APPWINDOW :: 0x00040000 -WS_EX_OVERLAPPEDWINDOW :: WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE -WS_EX_PALETTEWINDOW :: WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST -WS_EX_LAYERED :: 0x00080000 -WS_EX_NOINHERITLAYOUT :: 0x00100000 // Disable inheritence of mirroring by children -WS_EX_NOREDIRECTIONBITMAP :: 0x00200000 -WS_EX_LAYOUTRTL :: 0x00400000 // Right to left mirroring -WS_EX_COMPOSITED :: 0x02000000 -WS_EX_NOACTIVATE :: 0x08000000 - -WM_ACTIVATE :: 0x0006 -WM_ACTIVATEAPP :: 0x001C -WM_CHAR :: 0x0102 -WM_CLOSE :: 0x0010 -WM_CREATE :: 0x0001 -WM_DESTROY :: 0x0002 -WM_INPUT :: 0x00FF -WM_KEYDOWN :: 0x0100 -WM_KEYUP :: 0x0101 -WM_KILLFOCUS :: 0x0008 -WM_QUIT :: 0x0012 -WM_SETCURSOR :: 0x0020 -WM_SETFOCUS :: 0x0007 -WM_SIZE :: 0x0005 -WM_SIZING :: 0x0214 -WM_SYSKEYDOWN :: 0x0104 -WM_SYSKEYUP :: 0x0105 -WM_USER :: 0x0400 -WM_WINDOWPOSCHANGED :: 0x0047 -WM_COMMAND :: 0x0111 -WM_PAINT :: 0x000F - -WM_MOUSEWHEEL :: 0x020A -WM_MOUSEMOVE :: 0x0200 -WM_LBUTTONDOWN :: 0x0201 -WM_LBUTTONUP :: 0x0202 -WM_LBUTTONDBLCLK :: 0x0203 -WM_RBUTTONDOWN :: 0x0204 -WM_RBUTTONUP :: 0x0205 -WM_RBUTTONDBLCLK :: 0x0206 -WM_MBUTTONDOWN :: 0x0207 -WM_MBUTTONUP :: 0x0208 -WM_MBUTTONDBLCLK :: 0x0209 - -PM_NOREMOVE :: 0x0000 -PM_REMOVE :: 0x0001 -PM_NOYIELD :: 0x0002 - -BLACK_BRUSH :: 4 - -SM_CXSCREEN :: 0 -SM_CYSCREEN :: 1 - -SW_SHOW :: 5 - -COLOR_BACKGROUND :: Hbrush(uintptr(1)) - -INVALID_SET_FILE_POINTER :: ~u32(0) -HEAP_ZERO_MEMORY :: 0x00000008 -INFINITE :: 0xffffffff -GWL_EXSTYLE :: -20 -GWLP_HINSTANCE :: -6 -GWLP_ID :: -12 -GWL_STYLE :: -16 -GWLP_USERDATA :: -21 -GWLP_WNDPROC :: -4 -Hwnd_TOP :: Hwnd(uintptr(0)) - -BI_RGB :: 0 -DIB_RGB_COLORS :: 0x00 -SRCCOPY: u32 : 0x00cc0020 - - -MONITOR_DEFAULTTONULL :: 0x00000000 -MONITOR_DEFAULTTOPRIMARY :: 0x00000001 -MONITOR_DEFAULTTONEAREST :: 0x00000002 - -SWP_FRAMECHANGED :: 0x0020 -SWP_NOOWNERZORDER :: 0x0200 -SWP_NOZORDER :: 0x0004 -SWP_NOSIZE :: 0x0001 -SWP_NOMOVE :: 0x0002 - - -// Raw Input - - -RID_HEADER :: 0x10000005 -RID_INPUT :: 0x10000003 - - -RIDEV_APPKEYS :: 0x00000400 -RIDEV_CAPTUREMOUSE :: 0x00000200 -RIDEV_DEVNOTIFY :: 0x00002000 -RIDEV_EXCLUDE :: 0x00000010 -RIDEV_EXINPUTSINK :: 0x00001000 -RIDEV_INPUTSINK :: 0x00000100 -RIDEV_NOHOTKEYS :: 0x00000200 -RIDEV_NOLEGACY :: 0x00000030 -RIDEV_PAGEONLY :: 0x00000020 -RIDEV_REMOVE :: 0x00000001 - - -RIM_TYPEMOUSE :: 0 -RIM_TYPEKEYBOARD :: 1 -RIM_TYPEHID :: 2 - - -MOUSE_ATTRIBUTES_CHANGED :: 0x04 -MOUSE_MOVE_RELATIVE :: 0 -MOUSE_MOVE_ABSOLUTE :: 1 -MOUSE_VIRTUAL_DESKTOP :: 0x02 - - - -RI_MOUSE_BUTTON_1_DOWN :: 0x0001 -RI_MOUSE_BUTTON_1_UP :: 0x0002 -RI_MOUSE_BUTTON_2_DOWN :: 0x0004 -RI_MOUSE_BUTTON_2_UP :: 0x0008 -RI_MOUSE_BUTTON_3_DOWN :: 0x0010 -RI_MOUSE_BUTTON_3_UP :: 0x0020 -RI_MOUSE_BUTTON_4_DOWN :: 0x0040 -RI_MOUSE_BUTTON_4_UP :: 0x0080 -RI_MOUSE_BUTTON_5_DOWN :: 0x0100 -RI_MOUSE_BUTTON_5_UP :: 0x0200 -RI_MOUSE_LEFT_BUTTON_DOWN :: 0x0001 -RI_MOUSE_LEFT_BUTTON_UP :: 0x0002 -RI_MOUSE_MIDDLE_BUTTON_DOWN :: 0x0010 -RI_MOUSE_MIDDLE_BUTTON_UP :: 0x0020 -RI_MOUSE_RIGHT_BUTTON_DOWN :: 0x0004 -RI_MOUSE_RIGHT_BUTTON_UP :: 0x0008 -RI_MOUSE_WHEEL :: 0x0400 - - -RI_KEY_MAKE :: 0x00 -RI_KEY_BREAK :: 0x01 -RI_KEY_E0 :: 0x02 -RI_KEY_E1 :: 0x04 -RI_KEY_TERMSRV_SET_LED :: 0x08 -RI_KEY_TERMSRV_SHADOW :: 0x10 - -// Windows OpenGL - -PFD_TYPE_RGBA :: 0 -PFD_TYPE_COLORINDEX :: 1 -PFD_MAIN_PLANE :: 0 -PFD_OVERLAY_PLANE :: 1 -PFD_UNDERLAY_PLANE :: -1 -PFD_DOUBLEBUFFER :: 1 -PFD_STEREO :: 2 -PFD_DRAW_TO_WINDOW :: 4 -PFD_DRAW_TO_BITMAP :: 8 -PFD_SUPPORT_GDI :: 16 -PFD_SUPPORT_OPENGL :: 32 -PFD_GENERIC_FORMAT :: 64 -PFD_NEED_PALETTE :: 128 -PFD_NEED_SYSTEM_PALETTE :: 0x00000100 -PFD_SWAP_EXCHANGE :: 0x00000200 -PFD_SWAP_COPY :: 0x00000400 -PFD_SWAP_LAYER_BUFFERS :: 0x00000800 -PFD_GENERIC_ACCELERATED :: 0x00001000 -PFD_DEPTH_DONTCARE :: 0x20000000 -PFD_DOUBLEBUFFER_DONTCARE :: 0x40000000 -PFD_STEREO_DONTCARE :: 0x80000000 - -GET_FILEEX_INFO_LEVELS :: distinct i32 -GetFileExInfoStandard: GET_FILEEX_INFO_LEVELS : 0 -GetFileExMaxInfoLevel: GET_FILEEX_INFO_LEVELS : 1 - -STARTF_USESHOWWINDOW :: 0x00000001 -STARTF_USESIZE :: 0x00000002 -STARTF_USEPOSITION :: 0x00000004 -STARTF_USECOUNTCHARS :: 0x00000008 -STARTF_USEFILLATTRIBUTE :: 0x00000010 -STARTF_RUNFULLSCREEN :: 0x00000020 // ignored for non-x86 platforms -STARTF_FORCEONFEEDBACK :: 0x00000040 -STARTF_FORCEOFFFEEDBACK :: 0x00000080 -STARTF_USESTDHANDLES :: 0x00000100 -STARTF_USEHOTKEY :: 0x00000200 -STARTF_TITLEISLINKNAME :: 0x00000800 -STARTF_TITLEISAPPID :: 0x00001000 -STARTF_PREVENTPINNING :: 0x00002000 -STARTF_UNTRUSTEDSOURCE :: 0x00008000 - - -MOVEFILE_REPLACE_EXISTING :: 0x00000001 -MOVEFILE_COPY_ALLOWED :: 0x00000002 -MOVEFILE_DELAY_UNTIL_REBOOT :: 0x00000004 -MOVEFILE_WRITE_THROUGH :: 0x00000008 -MOVEFILE_CREATE_HARDLINK :: 0x00000010 -MOVEFILE_FAIL_IF_NOT_TRACKABLE :: 0x00000020 - -FILE_NOTIFY_CHANGE_FILE_NAME :: 0x00000001 -FILE_NOTIFY_CHANGE_DIR_NAME :: 0x00000002 -FILE_NOTIFY_CHANGE_ATTRIBUTES :: 0x00000004 -FILE_NOTIFY_CHANGE_SIZE :: 0x00000008 -FILE_NOTIFY_CHANGE_LAST_WRITE :: 0x00000010 -FILE_NOTIFY_CHANGE_LAST_ACCESS :: 0x00000020 -FILE_NOTIFY_CHANGE_CREATION :: 0x00000040 -FILE_NOTIFY_CHANGE_SECURITY :: 0x00000100 - -FILE_FLAG_WRITE_THROUGH :: 0x80000000 -FILE_FLAG_OVERLAPPED :: 0x40000000 -FILE_FLAG_NO_BUFFERING :: 0x20000000 -FILE_FLAG_RANDOM_ACCESS :: 0x10000000 -FILE_FLAG_SEQUENTIAL_SCAN :: 0x08000000 -FILE_FLAG_DELETE_ON_CLOSE :: 0x04000000 -FILE_FLAG_BACKUP_SEMANTICS :: 0x02000000 -FILE_FLAG_POSIX_SEMANTICS :: 0x01000000 -FILE_FLAG_SESSION_AWARE :: 0x00800000 -FILE_FLAG_OPEN_REPARSE_POINT :: 0x00200000 -FILE_FLAG_OPEN_NO_RECALL :: 0x00100000 -FILE_FLAG_FIRST_PIPE_INSTANCE :: 0x00080000 - -FILE_ACTION_ADDED :: 0x00000001 -FILE_ACTION_REMOVED :: 0x00000002 -FILE_ACTION_MODIFIED :: 0x00000003 -FILE_ACTION_RENAMED_OLD_NAME :: 0x00000004 -FILE_ACTION_RENAMED_NEW_NAME :: 0x00000005 - -CP_ACP :: 0 // default to ANSI code page -CP_OEMCP :: 1 // default to OEM code page -CP_MACCP :: 2 // default to MAC code page -CP_THREAD_ACP :: 3 // current thread's ANSI code page -CP_SYMBOL :: 42 // SYMBOL translations -CP_UTF7 :: 65000 // UTF-7 translation -CP_UTF8 :: 65001 // UTF-8 translation - - -MB_ERR_INVALID_CHARS :: 8 -WC_ERR_INVALID_CHARS :: 128 - -utf8_to_utf16 :: proc(s: string, allocator := context.temp_allocator) -> []u16 { - if len(s) < 1 { - return nil - } - - b := transmute([]byte)s - cstr := cstring(&b[0]) - n := multi_byte_to_wide_char(CP_UTF8, MB_ERR_INVALID_CHARS, cstr, i32(len(s)), nil, 0) - if n == 0 { - return nil - } - - text := make([]u16, n+1, allocator) - - n1 := multi_byte_to_wide_char(CP_UTF8, MB_ERR_INVALID_CHARS, cstr, i32(len(s)), Wstring(&text[0]), i32(n)) - if n1 == 0 { - delete(text, allocator) - return nil - } - - text[n] = 0 - for n >= 1 && text[n-1] == 0 { - n -= 1 - } - return text[:n] -} -utf8_to_wstring :: proc(s: string, allocator := context.temp_allocator) -> Wstring { - if res := utf8_to_utf16(s, allocator); res != nil { - return Wstring(&res[0]) - } - return nil -} - -wstring_to_utf8 :: proc(s: Wstring, N: int, allocator := context.temp_allocator) -> string { - if N == 0 { - return "" - } - - n := wide_char_to_multi_byte(CP_UTF8, WC_ERR_INVALID_CHARS, s, i32(N), nil, 0, nil, nil) - if n == 0 { - return "" - } - - // If N == -1 the call to wide_char_to_multi_byte assume the wide string is null terminated - // and will scan it to find the first null terminated character. The resulting string will - // also null terminated. - // If N != -1 it assumes the wide string is not null terminated and the resulting string - // will not be null terminated, we therefore have to force it to be null terminated manually. - text := make([]byte, n+1 if N != -1 else n, allocator) - - if n1 := wide_char_to_multi_byte(CP_UTF8, WC_ERR_INVALID_CHARS, s, i32(N), cstring(&text[0]), n, nil, nil); n1 == 0 { - delete(text, allocator) - return "" - } - - for i in 0.. string { - if len(s) == 0 { - return "" - } - return wstring_to_utf8(cast(Wstring)&s[0], len(s), allocator) -} - -get_query_performance_frequency :: proc() -> i64 { - r: i64 - query_performance_frequency(&r) - return r -} - -HIWORD_W :: proc(wParam: Wparam) -> u16 { return u16((u32(wParam) >> 16) & 0xffff) } -HIWORD_L :: proc(lParam: Lparam) -> u16 { return u16((u32(lParam) >> 16) & 0xffff) } -LOWORD_W :: proc(wParam: Wparam) -> u16 { return u16(wParam) } -LOWORD_L :: proc(lParam: Lparam) -> u16 { return u16(lParam) } - -is_key_down :: #force_inline proc(key: Key_Code) -> bool { return get_async_key_state(i32(key)) < 0 } - - - - -MAX_PATH :: 0x00000104 -MAX_PATH_WIDE :: 0x8000 - -HANDLE_FLAG_INHERIT :: 1 -HANDLE_FLAG_PROTECT_FROM_CLOSE :: 2 - -FILE_BEGIN :: 0 -FILE_CURRENT :: 1 -FILE_END :: 2 - -FILE_SHARE_READ :: 0x00000001 -FILE_SHARE_WRITE :: 0x00000002 -FILE_SHARE_DELETE :: 0x00000004 -FILE_GENERIC_ALL :: 0x10000000 -FILE_GENERIC_EXECUTE :: 0x20000000 -FILE_GENERIC_WRITE :: 0x40000000 -FILE_GENERIC_READ :: 0x80000000 - -FILE_READ_DATA :: 0x0001 -FILE_LIST_DIRECTORY :: 0x0001 -FILE_WRITE_DATA :: 0x0002 -FILE_ADD_FILE :: 0x0002 -FILE_APPEND_DATA :: 0x0004 -FILE_ADD_SUBDIRECTORY :: 0x0004 -FILE_CREATE_PIPE_INSTANCE :: 0x0004 -FILE_READ_EA :: 0x0008 -FILE_WRITE_EA :: 0x0010 -FILE_EXECUTE :: 0x0020 -FILE_TRAVERSE :: 0x0020 -FILE_DELETE_CHILD :: 0x0040 -FILE_READ_ATTRIBUTES :: 0x0080 -FILE_WRITE_ATTRIBUTES :: 0x0100 - -STD_INPUT_HANDLE :: -10 -STD_OUTPUT_HANDLE :: -11 -STD_ERROR_HANDLE :: -12 - -CREATE_NEW :: 1 -CREATE_ALWAYS :: 2 -OPEN_EXISTING :: 3 -OPEN_ALWAYS :: 4 -TRUNCATE_EXISTING :: 5 - -INVALID_FILE_ATTRIBUTES :: -1 - -FILE_ATTRIBUTE_READONLY :: 0x00000001 -FILE_ATTRIBUTE_HIDDEN :: 0x00000002 -FILE_ATTRIBUTE_SYSTEM :: 0x00000004 -FILE_ATTRIBUTE_DIRECTORY :: 0x00000010 -FILE_ATTRIBUTE_ARCHIVE :: 0x00000020 -FILE_ATTRIBUTE_DEVICE :: 0x00000040 -FILE_ATTRIBUTE_NORMAL :: 0x00000080 -FILE_ATTRIBUTE_TEMPORARY :: 0x00000100 -FILE_ATTRIBUTE_SPARSE_FILE :: 0x00000200 -FILE_ATTRIBUTE_REPARSE_Point :: 0x00000400 -FILE_ATTRIBUTE_COMPRESSED :: 0x00000800 -FILE_ATTRIBUTE_OFFLINE :: 0x00001000 -FILE_ATTRIBUTE_NOT_CONTENT_INDEXED :: 0x00002000 -FILE_ATTRIBUTE_ENCRYPTED :: 0x00004000 - -FILE_TYPE_DISK :: 0x0001 -FILE_TYPE_CHAR :: 0x0002 -FILE_TYPE_PIPE :: 0x0003 - - -Monitor_Info :: struct { - size: u32, - monitor: Rect, - work: Rect, - flags: u32, -} - -Window_Placement :: struct { - length: u32, - flags: u32, - show_cmd: u32, - min_pos: Point, - max_pos: Point, - normal_pos: Rect, -} - -Bitmap_Info_Header :: struct { - size: u32, - width, height: i32, - planes, bit_count: i16, - compression: u32, - size_image: u32, - x_pels_per_meter: i32, - y_pels_per_meter: i32, - clr_used: u32, - clr_important: u32, -} -Bitmap_Info :: struct { - using header: Bitmap_Info_Header, - colors: [1]Rgb_Quad, -} - -Paint_Struct :: struct { - hdc: Hdc, - erase: Bool, - rc_paint: Rect, - restore: Bool, - inc_update: Bool, - rgb_reserved: [32]byte, -} - - -Rgb_Quad :: struct {blue, green, red, reserved: byte} - - -Key_Code :: enum i32 { - Unknown = 0x00, - - Lbutton = 0x01, - Rbutton = 0x02, - Cancel = 0x03, - Mbutton = 0x04, - Xbutton1 = 0x05, - Xbutton2 = 0x06, - Back = 0x08, - Tab = 0x09, - Clear = 0x0C, - Return = 0x0D, - - Shift = 0x10, - Control = 0x11, - Menu = 0x12, - Pause = 0x13, - Capital = 0x14, - Kana = 0x15, - Hangeul = 0x15, - Hangul = 0x15, - Junja = 0x17, - Final = 0x18, - Hanja = 0x19, - Kanji = 0x19, - Escape = 0x1B, - Convert = 0x1C, - NonConvert = 0x1D, - Accept = 0x1E, - ModeChange = 0x1F, - Space = 0x20, - Prior = 0x21, - Next = 0x22, - End = 0x23, - Home = 0x24, - Left = 0x25, - Up = 0x26, - Right = 0x27, - Down = 0x28, - Select = 0x29, - Print = 0x2A, - Execute = 0x2B, - Snapshot = 0x2C, - Insert = 0x2D, - Delete = 0x2E, - Help = 0x2F, - - Num0 = '0', - Num1 = '1', - Num2 = '2', - Num3 = '3', - Num4 = '4', - Num5 = '5', - Num6 = '6', - Num7 = '7', - Num8 = '8', - Num9 = '9', - A = 'A', - B = 'B', - C = 'C', - D = 'D', - E = 'E', - F = 'F', - G = 'G', - H = 'H', - I = 'I', - J = 'J', - K = 'K', - L = 'L', - M = 'M', - N = 'N', - O = 'O', - P = 'P', - Q = 'Q', - R = 'R', - S = 'S', - T = 'T', - U = 'U', - V = 'V', - W = 'W', - X = 'X', - Y = 'Y', - Z = 'Z', - - Lwin = 0x5B, - Rwin = 0x5C, - Apps = 0x5D, - - Numpad0 = 0x60, - Numpad1 = 0x61, - Numpad2 = 0x62, - Numpad3 = 0x63, - Numpad4 = 0x64, - Numpad5 = 0x65, - Numpad6 = 0x66, - Numpad7 = 0x67, - Numpad8 = 0x68, - Numpad9 = 0x69, - Multiply = 0x6A, - Add = 0x6B, - Separator = 0x6C, - Subtract = 0x6D, - Decimal = 0x6E, - Divide = 0x6F, - - F1 = 0x70, - F2 = 0x71, - F3 = 0x72, - F4 = 0x73, - F5 = 0x74, - F6 = 0x75, - F7 = 0x76, - F8 = 0x77, - F9 = 0x78, - F10 = 0x79, - F11 = 0x7A, - F12 = 0x7B, - F13 = 0x7C, - F14 = 0x7D, - F15 = 0x7E, - F16 = 0x7F, - F17 = 0x80, - F18 = 0x81, - F19 = 0x82, - F20 = 0x83, - F21 = 0x84, - F22 = 0x85, - F23 = 0x86, - F24 = 0x87, - - Numlock = 0x90, - Scroll = 0x91, - Lshift = 0xA0, - Rshift = 0xA1, - Lcontrol = 0xA2, - Rcontrol = 0xA3, - Lmenu = 0xA4, - Rmenu = 0xA5, - ProcessKey = 0xE5, - Attn = 0xF6, - Crsel = 0xF7, - Exsel = 0xF8, - Ereof = 0xF9, - Play = 0xFA, - Zoom = 0xFB, - Noname = 0xFC, - Pa1 = 0xFD, - OemClear = 0xFE, -} - diff --git a/core/sys/win32/helpers.odin b/core/sys/win32/helpers.odin deleted file mode 100644 index b04e5db95..000000000 --- a/core/sys/win32/helpers.odin +++ /dev/null @@ -1,29 +0,0 @@ -// +build windows -package win32 - -import "core:strings" - -call_external_process :: proc(program, command_line: string) -> bool { - si := Startup_Info{ cb=size_of(Startup_Info) } - pi := Process_Information{} - - return cast(bool)create_process_w( - utf8_to_wstring(program), - utf8_to_wstring(command_line), - nil, - nil, - Bool(false), - u32(0x10), - nil, - nil, - &si, - &pi, - ) -} - -open_website :: proc(url: string) -> bool { - p :: "C:\\Windows\\System32\\cmd.exe" - arg := []string{"/C", "start", url} - args := strings.join(arg, " ", context.temp_allocator) - return call_external_process(p, args) -} diff --git a/core/sys/win32/kernel32.odin b/core/sys/win32/kernel32.odin deleted file mode 100644 index 709964fb4..000000000 --- a/core/sys/win32/kernel32.odin +++ /dev/null @@ -1,237 +0,0 @@ -// +build windows -package win32 - -foreign import "system:kernel32.lib" - -@(default_calling_convention = "std") -foreign kernel32 { - @(link_name="CreateProcessA") create_process_a :: proc(application_name, command_line: cstring, - process_attributes, thread_attributes: ^Security_Attributes, - inherit_handle: Bool, creation_flags: u32, environment: rawptr, - current_directory: cstring, startup_info: ^Startup_Info, - process_information: ^Process_Information) -> Bool --- - @(link_name="CreateProcessW") create_process_w :: proc(application_name, command_line: Wstring, - process_attributes, thread_attributes: ^Security_Attributes, - inherit_handle: Bool, creation_flags: u32, environment: rawptr, - current_directory: Wstring, startup_info: ^Startup_Info, - process_information: ^Process_Information) -> Bool --- - @(link_name="GetExitCodeProcess") get_exit_code_process :: proc(process: Handle, exit: ^u32) -> Bool --- - @(link_name="ExitProcess") exit_process :: proc(exit_code: u32) --- - @(link_name="GetModuleHandleA") get_module_handle_a :: proc(module_name: cstring) -> Hmodule --- - @(link_name="GetModuleHandleW") get_module_handle_w :: proc(module_name: Wstring) -> Hmodule --- - - @(link_name="GetModuleFileNameA") get_module_file_name_a :: proc(module: Hmodule, filename: cstring, size: u32) -> u32 --- - @(link_name="GetModuleFileNameW") get_module_file_name_w :: proc(module: Hmodule, filename: Wstring, size: u32) -> u32 --- - - @(link_name="Sleep") sleep :: proc(ms: u32) --- - @(link_name="QueryPerformanceFrequency") query_performance_frequency :: proc(result: ^i64) -> i32 --- - @(link_name="QueryPerformanceCounter") query_performance_counter :: proc(result: ^i64) -> i32 --- - @(link_name="OutputDebugStringA") output_debug_string_a :: proc(c_str: cstring) --- - - @(link_name="GetCommandLineA") get_command_line_a :: proc() -> cstring --- - @(link_name="GetCommandLineW") get_command_line_w :: proc() -> Wstring --- - @(link_name="GetSystemMetrics") get_system_metrics :: proc(index: i32) -> i32 --- - @(link_name="GetSystemInfo") get_system_info :: proc(info: ^System_Info) --- - @(link_name="GetVersionExA") get_version :: proc(osvi: ^OS_Version_Info_Ex_A) --- - @(link_name="GetCurrentThreadId") get_current_thread_id :: proc() -> u32 --- - - // NOTE(tetra): Not thread safe with SetCurrentDirectory and GetFullPathName; - // The current directory is stored as a global variable in the process. - @(link_name="GetCurrentDirectoryW") get_current_directory_w :: proc(len: u32, buf: Wstring) -> u32 --- - @(link_name="SetCurrentDirectoryW") set_current_directory_w :: proc(buf: Wstring) -> u32 --- - - @(link_name="GetSystemTimeAsFileTime") get_system_time_as_file_time :: proc(system_time_as_file_time: ^Filetime) --- - @(link_name="FileTimeToLocalFileTime") file_time_to_local_file_time :: proc(file_time: ^Filetime, local_file_time: ^Filetime) -> Bool --- - @(link_name="FileTimeToSystemTime") file_time_to_system_time :: proc(file_time: ^Filetime, system_time: ^Systemtime) -> Bool --- - @(link_name="SystemTimeToFileTime") system_time_to_file_time :: proc(system_time: ^Systemtime, file_time: ^Filetime) -> Bool --- - - @(link_name="GetStdHandle") get_std_handle :: proc(h: i32) -> Handle --- - - @(link_name="CreateFileA") - create_file_a :: proc(filename: cstring, desired_access, share_module: u32, - security: rawptr, - creation, flags_and_attribs: u32, template_file: Handle) -> Handle --- - - @(link_name="CreateFileW") - create_file_w :: proc(filename: Wstring, desired_access, share_module: u32, - security: rawptr, - creation, flags_and_attribs: u32, template_file: Handle) -> Handle --- - - - @(link_name="ReadFile") read_file :: proc(h: Handle, buf: rawptr, to_read: u32, bytes_read: ^i32, overlapped: rawptr) -> Bool --- - @(link_name="WriteFile") write_file :: proc(h: Handle, buf: rawptr, len: i32, written_result: ^i32, overlapped: rawptr) -> Bool --- - - @(link_name="GetFileSizeEx") get_file_size_ex :: proc(file_handle: Handle, file_size: ^i64) -> Bool --- - @(link_name="GetFileInformationByHandle") get_file_information_by_handle :: proc(file_handle: Handle, file_info: ^By_Handle_File_Information) -> Bool --- - - @(link_name="CreateDirectoryA") create_directory_a :: proc(path: cstring, security_attributes: ^Security_Attributes) -> Bool --- - @(link_name="CreateDirectoryW") create_directory_w :: proc(path: Wstring, security_attributes: ^Security_Attributes) -> Bool --- - - @(link_name="GetFileType") get_file_type :: proc(file_handle: Handle) -> u32 --- - @(link_name="SetFilePointer") set_file_pointer :: proc(file_handle: Handle, distance_to_move: i32, distance_to_move_high: ^i32, move_method: u32) -> u32 --- - - @(link_name="SetHandleInformation") set_handle_information :: proc(obj: Handle, mask, flags: u32) -> Bool --- - - @(link_name="FindFirstFileA") find_first_file_a :: proc(file_name: cstring, data: ^Find_Data_A) -> Handle --- - @(link_name="FindNextFileA") find_next_file_a :: proc(file: Handle, data: ^Find_Data_A) -> Bool --- - - @(link_name="FindFirstFileW") find_first_file_w :: proc(file_name: Wstring, data: ^Find_Data_W) -> Handle --- - @(link_name="FindNextFileW") find_next_file_w :: proc(file: Handle, data: ^Find_Data_W) -> Bool --- - - @(link_name="FindClose") find_close :: proc(file: Handle) -> Bool --- - - @(link_name="MoveFileExA") move_file_ex_a :: proc(existing, new: cstring, flags: u32) -> Bool --- - @(link_name="DeleteFileA") delete_file_a :: proc(file_name: cstring) -> Bool --- - @(link_name="CopyFileA") copy_file_a :: proc(existing, new: cstring, fail_if_exists: Bool) -> Bool --- - - @(link_name="MoveFileExW") move_file_ex_w :: proc(existing, new: Wstring, flags: u32) -> Bool --- - @(link_name="DeleteFileW") delete_file_w :: proc(file_name: Wstring) -> Bool --- - @(link_name="CopyFileW") copy_file_w :: proc(existing, new: Wstring, fail_if_exists: Bool) -> Bool --- - - @(link_name="HeapAlloc") heap_alloc :: proc(h: Handle, flags: u32, bytes: int) -> rawptr --- - @(link_name="HeapReAlloc") heap_realloc :: proc(h: Handle, flags: u32, memory: rawptr, bytes: int) -> rawptr --- - @(link_name="HeapFree") heap_free :: proc(h: Handle, flags: u32, memory: rawptr) -> Bool --- - @(link_name="GetProcessHeap") get_process_heap :: proc() -> Handle --- - - @(link_name="LocalAlloc") local_alloc :: proc(flags: u32, bytes: int) -> rawptr --- - @(link_name="LocalReAlloc") local_realloc :: proc(mem: rawptr, bytes: int, flags: uint) -> rawptr --- - @(link_name="LocalFree") local_free :: proc(mem: rawptr) -> rawptr --- - - @(link_name="FindFirstChangeNotificationA") find_first_change_notification_a :: proc(path: cstring, watch_subtree: Bool, filter: u32) -> Handle --- - @(link_name="FindNextChangeNotification") find_next_change_notification :: proc(h: Handle) -> Bool --- - @(link_name="FindCloseChangeNotification") find_close_change_notification :: proc(h: Handle) -> Bool --- - - @(link_name="ReadDirectoryChangesW") read_directory_changes_w :: proc(dir: Handle, buf: rawptr, buf_length: u32, - watch_subtree: Bool, notify_filter: u32, - bytes_returned: ^u32, overlapped: ^Overlapped, - completion: rawptr) -> Bool --- - - @(link_name="GetOverlappedResult") get_overlapped_result :: proc(file: Handle, overlapped: ^Overlapped, number_of_bytes_transferred: ^u32, wait: Bool) -> Bool --- - - @(link_name="WideCharToMultiByte") wide_char_to_multi_byte :: proc(code_page: u32, flags: u32, - wchar_str: Wstring, wchar: i32, - multi_str: cstring, multi: i32, - default_char: cstring, used_default_char: ^Bool) -> i32 --- - - @(link_name="MultiByteToWideChar") multi_byte_to_wide_char :: proc(code_page: u32, flags: u32, - mb_str: cstring, mb: i32, - wc_str: Wstring, wc: i32) -> i32 --- - - @(link_name="CreateSemaphoreA") create_semaphore_a :: proc(attributes: ^Security_Attributes, initial_count, maximum_count: i32, name: cstring) -> Handle --- - @(link_name="CreateSemaphoreW") create_semaphore_w :: proc(attributes: ^Security_Attributes, initial_count, maximum_count: i32, name: cstring) -> Handle --- - @(link_name="ReleaseSemaphore") release_semaphore :: proc(semaphore: Handle, release_count: i32, previous_count: ^i32) -> Bool --- - @(link_name="WaitForSingleObject") wait_for_single_object :: proc(handle: Handle, milliseconds: u32) -> u32 --- -} - -// @(default_calling_convention = "c") -foreign kernel32 { - @(link_name="GetLastError") get_last_error :: proc() -> i32 --- - @(link_name="CloseHandle") close_handle :: proc(h: Handle) -> i32 --- - - @(link_name="GetFileAttributesA") get_file_attributes_a :: proc(filename: cstring) -> u32 --- - @(link_name="GetFileAttributesW") get_file_attributes_w :: proc(filename: Wstring) -> u32 --- - @(link_name="GetFileAttributesExA") get_file_attributes_ex_a :: proc(filename: cstring, info_level_id: GET_FILEEX_INFO_LEVELS, file_info: ^File_Attribute_Data) -> Bool --- - @(link_name="GetFileAttributesExW") get_file_attributes_ex_w :: proc(filename: Wstring, info_level_id: GET_FILEEX_INFO_LEVELS, file_info: ^File_Attribute_Data) -> Bool --- - @(link_name="CompareFileTime") compare_file_time :: proc(a, b: ^Filetime) -> i32 --- -} - -@(default_calling_convention = "c") -foreign kernel32 { - @(link_name="InterlockedCompareExchange") interlocked_compare_exchange :: proc(dst: ^i32, exchange, comparand: i32) -> i32 --- - @(link_name="InterlockedExchange") interlocked_exchange :: proc(dst: ^i32, desired: i32) -> i32 --- - @(link_name="InterlockedExchangeAdd") interlocked_exchange_add :: proc(dst: ^i32, desired: i32) -> i32 --- - @(link_name="InterlockedAnd") interlocked_and :: proc(dst: ^i32, desired: i32) -> i32 --- - @(link_name="InterlockedOr") interlocked_or :: proc(dst: ^i32, desired: i32) -> i32 --- - - @(link_name="InterlockedCompareExchange64") interlocked_compare_exchange64 :: proc(dst: ^i64, exchange, comparand: i64) -> i64 --- - @(link_name="InterlockedExchange64") interlocked_exchange64 :: proc(dst: ^i64, desired: i64) -> i64 --- - @(link_name="InterlockedExchangeAdd64") interlocked_exchange_add64 :: proc(dst: ^i64, desired: i64) -> i64 --- - @(link_name="InterlockedAnd64") interlocked_and64 :: proc(dst: ^i64, desired: i64) -> i64 --- - @(link_name="InterlockedOr64") interlocked_or64 :: proc(dst: ^i64, desired: i64) -> i64 --- -} - -@(default_calling_convention = "std") -foreign kernel32 { - @(link_name="_mm_pause") mm_pause :: proc() --- - @(link_name="ReadWriteBarrier") read_write_barrier :: proc() --- - @(link_name="WriteBarrier") write_barrier :: proc() --- - @(link_name="ReadBarrier") read_barrier :: proc() --- - - @(link_name="CreateThread") - create_thread :: proc(thread_attributes: ^Security_Attributes, stack_size: uint, start_routine: proc "stdcall" (rawptr) -> u32, - parameter: rawptr, creation_flags: u32, thread_id: ^u32) -> Handle --- - @(link_name="ResumeThread") resume_thread :: proc(thread: Handle) -> u32 --- - @(link_name="GetThreadPriority") get_thread_priority :: proc(thread: Handle) -> i32 --- - @(link_name="SetThreadPriority") set_thread_priority :: proc(thread: Handle, priority: i32) -> Bool --- - @(link_name="GetExitCodeThread") get_exit_code_thread :: proc(thread: Handle, exit_code: ^u32) -> Bool --- - @(link_name="TerminateThread") terminate_thread :: proc(thread: Handle, exit_code: u32) -> Bool --- - - @(link_name="InitializeCriticalSection") initialize_critical_section :: proc(critical_section: ^Critical_Section) --- - @(link_name="InitializeCriticalSectionAndSpinCount") initialize_critical_section_and_spin_count :: proc(critical_section: ^Critical_Section, spin_count: u32) -> b32 --- - @(link_name="DeleteCriticalSection") delete_critical_section :: proc(critical_section: ^Critical_Section) --- - @(link_name="SetCriticalSectionSpinCount") set_critical_section_spin_count :: proc(critical_section: ^Critical_Section, spin_count: u32) -> u32 --- - @(link_name="TryEnterCriticalSection") try_enter_critical_section :: proc(critical_section: ^Critical_Section) -> b8 --- - @(link_name="EnterCriticalSection") enter_critical_section :: proc(critical_section: ^Critical_Section) --- - @(link_name="LeaveCriticalSection") leave_critical_section :: proc(critical_section: ^Critical_Section) --- - - @(link_name="CreateEventA") create_event_a :: proc(event_attributes: ^Security_Attributes, manual_reset, initial_state: Bool, name: cstring) -> Handle --- - @(link_name="CreateEventW") create_event_w :: proc(event_attributes: ^Security_Attributes, manual_reset, initial_state: Bool, name: Wstring) -> Handle --- - @(link_name="PulseEvent") pulse_event :: proc(event: Handle) -> Bool --- - @(link_name="SetEvent") set_event :: proc(event: Handle) -> Bool --- - @(link_name="ResetEvent") reset_event :: proc(event: Handle) -> Bool --- - - @(link_name="LoadLibraryA") load_library_a :: proc(c_str: cstring) -> Hmodule --- - @(link_name="LoadLibraryW") load_library_w :: proc(c_str: Wstring) -> Hmodule --- - @(link_name="FreeLibrary") free_library :: proc(h: Hmodule) -> Bool --- - @(link_name="GetProcAddress") get_proc_address :: proc(h: Hmodule, c_str: cstring) -> rawptr --- - - @(link_name="GetFullPathNameA") get_full_path_name_a :: proc(filename: cstring, buffer_length: u32, buffer: cstring, file_part: ^Wstring) -> u32 --- - @(link_name="GetFullPathNameW") get_full_path_name_w :: proc(filename: Wstring, buffer_length: u32, buffer: Wstring, file_part: ^Wstring) -> u32 --- - @(link_name="GetLongPathNameA") get_long_path_name_a :: proc(short, long: cstring, len: u32) -> u32 --- - @(link_name="GetLongPathNameW") get_long_path_name_w :: proc(short, long: Wstring, len: u32) -> u32 --- - @(link_name="GetShortPathNameA") get_short_path_name_a :: proc(long, short: cstring, len: u32) -> u32 --- - @(link_name="GetShortPathNameW") get_short_path_name_w :: proc(long, short: Wstring, len: u32) -> u32 --- - - @(link_name="GetCurrentDirectoryA") get_current_directory_a :: proc(buffer_length: u32, buffer: cstring) -> u32 --- -} - -Memory_Basic_Information :: struct { - base_address: rawptr, - allocation_base: rawptr, - allocation_protect: u32, - region_size: uint, - state: u32, - protect: u32, - type: u32, -} - -@(default_calling_convention = "std") -foreign kernel32 { - @(link_name="VirtualAlloc") virtual_alloc :: proc(address: rawptr, size: uint, allocation_type: u32, protect: u32) -> rawptr --- - @(link_name="VirtualAllocEx") virtual_alloc_ex :: proc(process: Handle, address: rawptr, size: uint, allocation_type: u32, protect: u32) -> rawptr --- - @(link_name="VirtualFree") virtual_free :: proc(address: rawptr, size: uint, free_type: u32) -> Bool --- - @(link_name="VirtualLock") virtual_lock :: proc(address: rawptr, size: uint) -> Bool --- - @(link_name="VirtualProtect") virtual_protect :: proc(address: rawptr, size: uint, new_protect: u32, old_protect: ^u32) -> Bool --- - @(link_name="VirtualQuery") virtual_query :: proc(address: rawptr, buffer: ^Memory_Basic_Information, length: uint) -> uint --- -} - -MEM_COMMIT :: 0x00001000 -MEM_RESERVE :: 0x00002000 -MEM_DECOMMIT :: 0x00004000 -MEM_RELEASE :: 0x00008000 -MEM_RESET :: 0x00080000 -MEM_RESET_UNDO :: 0x01000000 - -MEM_LARGE_PAGES :: 0x20000000 -MEM_PHYSICAL :: 0x00400000 -MEM_TOP_DOWN :: 0x00100000 -MEM_WRITE_WATCH :: 0x00200000 - -PAGE_NOACCESS :: 0x01 -PAGE_READONLY :: 0x02 -PAGE_READWRITE :: 0x04 -PAGE_WRITECOPY :: 0x08 -PAGE_EXECUTE :: 0x10 -PAGE_EXECUTE_READ :: 0x20 -PAGE_EXECUTE_READWRITE :: 0x40 -PAGE_EXECUTE_WRITECOPY :: 0x80 diff --git a/core/sys/win32/ole32.odin b/core/sys/win32/ole32.odin deleted file mode 100644 index f4ee52399..000000000 --- a/core/sys/win32/ole32.odin +++ /dev/null @@ -1,18 +0,0 @@ -// +build windows -package win32 - -foreign import "system:ole32.lib" - -//objbase.h -Com_Init :: enum { - Multi_Threaded = 0x0, - Apartment_Threaded = 0x2, - Disable_OLE1_DDE = 0x4, - Speed_Over_Memory = 0x8, -} - -@(default_calling_convention = "std") -foreign ole32 { - @(link_name ="CoInitializeEx") com_init_ex :: proc(reserved: rawptr, co_init: Com_Init) ->Hresult --- - @(link_name = "CoUninitialize") com_shutdown :: proc() --- -} diff --git a/core/sys/win32/removal.odin b/core/sys/win32/removal.odin new file mode 100644 index 000000000..09c16aa05 --- /dev/null +++ b/core/sys/win32/removal.odin @@ -0,0 +1,3 @@ +package sys_win32 + +#panic(`"core:sys/win32" has been removed. Please use "core:sys/windows"`) \ No newline at end of file diff --git a/core/sys/win32/shell32.odin b/core/sys/win32/shell32.odin deleted file mode 100644 index 3cedf0527..000000000 --- a/core/sys/win32/shell32.odin +++ /dev/null @@ -1,9 +0,0 @@ -// +build windows -package win32 - -foreign import "system:shell32.lib" - -@(default_calling_convention = "std") -foreign shell32 { - @(link_name="CommandLineToArgvW") command_line_to_argv_w :: proc(cmd_list: Wstring, num_args: ^i32) -> ^Wstring --- -} diff --git a/core/sys/win32/tests/general.odin b/core/sys/win32/tests/general.odin deleted file mode 100644 index 1a5fc911a..000000000 --- a/core/sys/win32/tests/general.odin +++ /dev/null @@ -1,41 +0,0 @@ -package win32_tests - -import "core:sys/win32" -import "core:testing" - -utf16_to_utf8 :: proc(t: ^testing.T, str: []u16, comparison: string, expected_result: bool, loc := #caller_location) { - result := win32.utf16_to_utf8(str[:]); - testing.expect(t, (result == comparison) == expected_result, "Incorrect utf16_to_utf8 conversion", loc); -} - -wstring_to_utf8 :: proc(t: ^testing.T, str: []u16, comparison: string, expected_result: bool, loc := #caller_location) { - result := win32.wstring_to_utf8(nil if len(str) == 0 else cast(win32.Wstring)&str[0], -1); - testing.expect(t, (result == comparison) == expected_result, "Incorrect wstring_to_utf8 conversion", loc); -} - -@test -test_utf :: proc(t: ^testing.T) { - utf16_to_utf8(t, []u16{}, "", true); - utf16_to_utf8(t, []u16{0}, "", true); - utf16_to_utf8(t, []u16{0, 't', 'e', 's', 't'}, "", true); - utf16_to_utf8(t, []u16{0, 't', 'e', 's', 't', 0}, "", true); - utf16_to_utf8(t, []u16{'t', 'e', 's', 't'}, "test", true); - utf16_to_utf8(t, []u16{'t', 'e', 's', 't', 0}, "test", true); - utf16_to_utf8(t, []u16{'t', 'e', 0, 's', 't'}, "te", true); - utf16_to_utf8(t, []u16{'t', 'e', 0, 's', 't', 0}, "te", true); - - wstring_to_utf8(t, []u16{}, "", true); - wstring_to_utf8(t, []u16{0}, "", true); - wstring_to_utf8(t, []u16{0, 't', 'e', 's', 't'}, "", true); - wstring_to_utf8(t, []u16{0, 't', 'e', 's', 't', 0}, "", true); - wstring_to_utf8(t, []u16{'t', 'e', 's', 't', 0}, "test", true); - wstring_to_utf8(t, []u16{'t', 'e', 0, 's', 't'}, "te", true); - wstring_to_utf8(t, []u16{'t', 'e', 0, 's', 't', 0}, "te", true); - - // WARNING: Passing a non-zero-terminated string to wstring_to_utf8 is dangerous, - // as it will go out of bounds looking for a zero. - // It will "fail" or "succeed" by having a zero just after the end of the input string or not. - wstring_to_utf8(t, []u16{'t', 'e', 's', 't'}, "test", false); - wstring_to_utf8(t, []u16{'t', 'e', 's', 't', 0}[:4], "test", true); - wstring_to_utf8(t, []u16{'t', 'e', 's', 't', 'q'}[:4], "test", false); -} \ No newline at end of file diff --git a/core/sys/win32/user32.odin b/core/sys/win32/user32.odin deleted file mode 100644 index 593fdbb8e..000000000 --- a/core/sys/win32/user32.odin +++ /dev/null @@ -1,284 +0,0 @@ -// +build windows -package win32 - -foreign import "system:user32.lib" - -import "core:intrinsics" - - -Menu_Bar_Info :: struct { - size: u32, - bar: Rect, - menu: Hmenu, - wnd_menu: Hwnd, - fields: u8, - // field.bar_focused: 1, - // field.focuses: 1, -} - -Menu_Item_Info_A :: struct { - size: u32, - mask: u32, - type: u32, - state: u32, - id: u32, - submenu: Hmenu, - bmp_checked: Hbitmap, - bmp_unchecked: Hbitmap, - item_data: u32, - type_data: cstring, - cch: u32, -} -Menu_Item_Info_W :: struct { - size: u32, - mask: u32, - type: u32, - state: u32, - id: u32, - submenu: Hmenu, - bmp_checked: Hbitmap, - bmp_unchecked: Hbitmap, - item_data: u32, - type_data: Wstring, - cch: u32, -} - -MF_BYCOMMAND :: 0x00000000 -MF_BYPOSITION :: 0x00000400 -MF_BITMAP :: 0x00000004 -MF_CHECKED :: 0x00000008 -MF_DISABLED :: 0x00000002 -MF_ENABLED :: 0x00000000 -MF_GRAYED :: 0x00000001 -MF_MENUBARBREAK :: 0x00000020 -MF_MENUBREAK :: 0x00000040 -MF_OWNERDRAW :: 0x00000100 -MF_POPUP :: 0x00000010 -MF_SEPARATOR :: 0x00000800 -MF_STRING :: 0x00000000 -MF_UNCHECKED :: 0x00000000 - -MB_ABORTRETRYIGNORE :: 0x00000002 -MB_CANCELTRYCONTINUE :: 0x00000006 -MB_HELP :: 0x00004000 -MB_OK :: 0x00000000 -MB_OKCANCEL :: 0x00000001 -MB_RETRYCANCEL :: 0x00000005 -MB_YESNO :: 0x00000004 -MB_YESNOCANCEL :: 0x00000003 - -MB_ICONEXCLAMATION :: 0x00000030 -MB_ICONWARNING :: 0x00000030 -MB_ICONINFORMATION :: 0x00000040 -MB_ICONASTERISK :: 0x00000040 -MB_ICONQUESTION :: 0x00000020 -MB_ICONSTOP :: 0x00000010 -MB_ICONERROR :: 0x00000010 -MB_ICONHAND :: 0x00000010 - -MB_DEFBUTTON1 :: 0x00000000 -MB_DEFBUTTON2 :: 0x00000100 -MB_DEFBUTTON3 :: 0x00000200 -MB_DEFBUTTON4 :: 0x00000300 - -MB_APPLMODAL :: 0x00000000 -MB_SYSTEMMODAL :: 0x00001000 -MB_TASKMODAL :: 0x00002000 - -MB_DEFAULT_DESKTOP_ONLY :: 0x00020000 -MB_RIGHT :: 0x00080000 -MB_RTLREADING :: 0x00100000 -MB_SETFOREGROUND :: 0x00010000 -MB_TOPMOST :: 0x00040000 -MB_SERVICE_NOTIFICATION :: 0x00200000 - - -@(default_calling_convention = "std") -foreign user32 { - @(link_name="GetDesktopWindow") get_desktop_window :: proc() -> Hwnd --- - when !intrinsics.is_package_imported("raylib") { // NOTE(bill): this is a bit of hack but it's to get around the namespace collisions - @(link_name="ShowCursor")show_cursor :: proc(show: Bool) -> i32 --- - } - @(link_name="GetCursorPos") get_cursor_pos :: proc(p: ^Point) -> Bool --- - @(link_name="SetCursorPos") set_cursor_pos :: proc(x, y: i32) -> Bool --- - @(link_name="ScreenToClient") screen_to_client :: proc(h: Hwnd, p: ^Point) -> Bool --- - @(link_name="ClientToScreen") client_to_screen :: proc(h: Hwnd, p: ^Point) -> Bool --- - @(link_name="PostQuitMessage") post_quit_message :: proc(exit_code: i32) --- - @(link_name="SetWindowTextA") set_window_text_a :: proc(hwnd: Hwnd, c_string: cstring) -> Bool --- - @(link_name="SetWindowTextW") set_window_text_w :: proc(hwnd: Hwnd, c_string: Wstring) -> Bool --- - @(link_name="RegisterClassA") register_class_a :: proc(wc: ^Wnd_Class_A) -> i16 --- - @(link_name="RegisterClassW") register_class_w :: proc(wc: ^Wnd_Class_W) -> i16 --- - @(link_name="RegisterClassExA") register_class_ex_a :: proc(wc: ^Wnd_Class_Ex_A) -> i16 --- - @(link_name="RegisterClassExW") register_class_ex_w :: proc(wc: ^Wnd_Class_Ex_W) -> i16 --- - - @(link_name="CreateWindowExA") - create_window_ex_a :: proc(ex_style: u32, - class_name, title: cstring, - style: u32, - x, y, w, h: i32, - parent: Hwnd, menu: Hmenu, instance: Hinstance, - param: rawptr) -> Hwnd --- - - @(link_name="CreateWindowExW") - create_window_ex_w :: proc(ex_style: u32, - class_name, title: Wstring, - style: u32, - x, y, w, h: i32, - parent: Hwnd, menu: Hmenu, instance: Hinstance, - param: rawptr) -> Hwnd --- - - @(link_name="ShowWindow") show_window :: proc(hwnd: Hwnd, cmd_show: i32) -> Bool --- - @(link_name="TranslateMessage") translate_message :: proc(msg: ^Msg) -> Bool --- - @(link_name="DispatchMessageA") dispatch_message_a :: proc(msg: ^Msg) -> Lresult --- - @(link_name="DispatchMessageW") dispatch_message_w :: proc(msg: ^Msg) -> Lresult --- - @(link_name="UpdateWindow") update_window :: proc(hwnd: Hwnd) -> Bool --- - @(link_name="GetMessageA") get_message_a :: proc(msg: ^Msg, hwnd: Hwnd, msg_filter_min, msg_filter_max: u32) -> Bool --- - @(link_name="GetMessageW") get_message_w :: proc(msg: ^Msg, hwnd: Hwnd, msg_filter_min, msg_filter_max: u32) -> Bool --- - - @(link_name="PeekMessageA") peek_message_a :: proc(msg: ^Msg, hwnd: Hwnd, msg_filter_min, msg_filter_max, remove_msg: u32) -> Bool --- - @(link_name="PeekMessageW") peek_message_w :: proc(msg: ^Msg, hwnd: Hwnd, msg_filter_min, msg_filter_max, remove_msg: u32) -> Bool --- - - - @(link_name="PostMessageA") post_message_a :: proc(hwnd: Hwnd, msg: u32, wparam: Wparam, lparam: Lparam) -> Bool --- - @(link_name="PostMessageW") post_message_w :: proc(hwnd: Hwnd, msg: u32, wparam: Wparam, lparam: Lparam) -> Bool --- - @(link_name="SendMessageA") send_message_a :: proc(hwnd: Hwnd, msg: u32, wparam: Wparam, lparam: Lparam) -> Lresult --- - @(link_name="SendMessageW") send_message_w :: proc(hwnd: Hwnd, msg: u32, wparam: Wparam, lparam: Lparam) -> Lresult --- - - @(link_name="DefWindowProcA") def_window_proc_a :: proc(hwnd: Hwnd, msg: u32, wparam: Wparam, lparam: Lparam) -> Lresult --- - @(link_name="DefWindowProcW") def_window_proc_w :: proc(hwnd: Hwnd, msg: u32, wparam: Wparam, lparam: Lparam) -> Lresult --- - - @(link_name="AdjustWindowRect") adjust_window_rect :: proc(rect: ^Rect, style: u32, menu: Bool) -> Bool --- - @(link_name="GetActiveWindow") get_active_window :: proc() -> Hwnd --- - - @(link_name="DestroyWindow") destroy_window :: proc(wnd: Hwnd) -> Bool --- - @(link_name="DescribePixelFormat") describe_pixel_format :: proc(dc: Hdc, pixel_format: i32, bytes: u32, pfd: ^Pixel_Format_Descriptor) -> i32 --- - - @(link_name="GetMonitorInfoA") get_monitor_info_a :: proc(monitor: Hmonitor, mi: ^Monitor_Info) -> Bool --- - @(link_name="MonitorFromWindow") monitor_from_window :: proc(wnd: Hwnd, flags: u32) -> Hmonitor --- - - @(link_name="SetWindowPos") set_window_pos :: proc(wnd: Hwnd, wndInsertAfter: Hwnd, x, y, width, height: i32, flags: u32) -> Bool --- - - @(link_name="GetWindowPlacement") get_window_placement :: proc(wnd: Hwnd, wndpl: ^Window_Placement) -> Bool --- - @(link_name="SetWindowPlacement") set_window_placement :: proc(wnd: Hwnd, wndpl: ^Window_Placement) -> Bool --- - @(link_name="GetWindowRect") get_window_rect :: proc(wnd: Hwnd, rect: ^Rect) -> Bool --- - - @(link_name="GetWindowLongPtrA") get_window_long_ptr_a :: proc(wnd: Hwnd, index: i32) -> Long_Ptr --- - @(link_name="SetWindowLongPtrA") set_window_long_ptr_a :: proc(wnd: Hwnd, index: i32, new: Long_Ptr) -> Long_Ptr --- - @(link_name="GetWindowLongPtrW") get_window_long_ptr_w :: proc(wnd: Hwnd, index: i32) -> Long_Ptr --- - @(link_name="SetWindowLongPtrW") set_window_long_ptr_w :: proc(wnd: Hwnd, index: i32, new: Long_Ptr) -> Long_Ptr --- - - @(link_name="GetWindowText") get_window_text :: proc(wnd: Hwnd, str: cstring, maxCount: i32) -> i32 --- - - @(link_name="GetClientRect") get_client_rect :: proc(hwnd: Hwnd, rect: ^Rect) -> Bool --- - - @(link_name="GetDC") get_dc :: proc(h: Hwnd) -> Hdc --- - @(link_name="ReleaseDC") release_dc :: proc(wnd: Hwnd, hdc: Hdc) -> i32 --- - - @(link_name="MapVirtualKeyA") map_virtual_key_a :: proc(scancode: u32, map_type: u32) -> u32 --- - @(link_name="MapVirtualKeyW") map_virtual_key_w :: proc(scancode: u32, map_type: u32) -> u32 --- - - @(link_name="GetKeyState") get_key_state :: proc(v_key: i32) -> i16 --- - @(link_name="GetAsyncKeyState") get_async_key_state :: proc(v_key: i32) -> i16 --- - - @(link_name="SetForegroundWindow") set_foreground_window :: proc(h: Hwnd) -> Bool --- - @(link_name="SetFocus") set_focus :: proc(h: Hwnd) -> Hwnd --- - - - @(link_name="LoadImageA") load_image_a :: proc(instance: Hinstance, name: cstring, type_: u32, x_desired, y_desired : i32, load : u32) -> Handle --- - @(link_name="LoadIconA") load_icon_a :: proc(instance: Hinstance, icon_name: cstring) -> Hicon --- - @(link_name="DestroyIcon") destroy_icon :: proc(icon: Hicon) -> Bool --- - - @(link_name="LoadCursorA") load_cursor_a :: proc(instance: Hinstance, cursor_name: cstring) -> Hcursor --- - @(link_name="LoadCursorW") load_cursor_w :: proc(instance: Hinstance, cursor_name: Wstring) -> Hcursor --- - @(link_name="GetCursor") get_cursor :: proc() -> Hcursor --- - @(link_name="SetCursor") set_cursor :: proc(cursor: Hcursor) -> Hcursor --- - - @(link_name="RegisterRawInputDevices") register_raw_input_devices :: proc(raw_input_device: ^Raw_Input_Device, num_devices, size: u32) -> Bool --- - - @(link_name="GetRawInputData") get_raw_input_data :: proc(raw_input: Hrawinput, command: u32, data: rawptr, size: ^u32, size_header: u32) -> u32 --- - - @(link_name="MapVirtualKeyExW") map_virtual_key_ex_w :: proc(code, map_type: u32, hkl: HKL) -> u32 --- - @(link_name="MapVirtualKeyExA") map_virtual_key_ex_a :: proc(code, map_type: u32, hkl: HKL) -> u32 --- - - @(link_name="EnumDisplayMonitors") enum_display_monitors :: proc(hdc: Hdc, rect: ^Rect, enum_proc: Monitor_Enum_Proc, lparam: Lparam) -> bool --- - - @(link_name="EnumDisplaySettingsA") enum_display_settings_a :: proc(device_name: cstring, mode_number: u32, mode: ^Dev_Mode_A) -> Bool --- -} - -@(default_calling_convention = "std") -foreign user32 { - @(link_name="CreateMenu") create_menu :: proc() -> Hmenu --- - @(link_name="CreatePopupMenu") create_popup_menu :: proc() -> Hmenu --- - @(link_name="DestroyMenu") destroy_menu :: proc(menu: Hmenu) -> Bool --- - @(link_name="DeleteMenu") delete_menu :: proc(menu: Hmenu, position: u32, flags: u32) -> Bool --- - - @(link_name="EnableMenuItem") enable_menu_item :: proc(menu: Hmenu, id_enable_itme: i32, enable: u32) -> Bool --- - @(link_name="EndMenu") end_menu :: proc(menu: Hmenu, flags: u32, id_new_item: Uint_Ptr, new_item: cstring) -> Bool --- - @(link_name="GetMenu") get_menu :: proc(wnd: Hwnd) -> Hmenu --- - @(link_name="GetMenuBarInfo") get_menu_bar_info :: proc(wnd: Hwnd, id_object, id_item: u32, mbi: ^Menu_Bar_Info) -> Hmenu --- - @(link_name="GetMenuStringA") get_menu_string_a :: proc(menu: Hmenu, id_item: u32, s: cstring, cch_max: i32, flags: u32) -> i32 --- - @(link_name="GetMenuStringW") get_menu_string_w :: proc(menu: Hmenu, id_item: u32, s: Wstring, cch_max: i32, flags: u32) -> i32 --- - @(link_name="GetMenuState") get_menu_state :: proc(menu: Hmenu, id: u32, flags: u32) -> u32 --- - @(link_name="GetMenuItemRect") get_menu_item_rect :: proc(wnd: Hwnd, menu: Hmenu, id_item: u32, item: ^Rect) -> Bool --- - - @(link_name="SetMenu") set_menu :: proc(wnd: Hwnd, menu: Hmenu) -> Bool --- - - @(link_name="DrawMenuBar") draw_menu_bar :: proc(wnd: Hwnd) -> Bool --- - @(link_name="InsertMenuA") insert_menu_a :: proc(menu: Hmenu, position: u32, flags: u32, id_new_item: Uint_Ptr, new_item: cstring) -> Bool --- - @(link_name="InsertMenuW") insert_menu_w :: proc(menu: Hmenu, position: u32, flags: u32, id_new_item: Uint_Ptr, new_item: Wstring) -> Bool --- - - @(link_name="InsertMenuItemA") insert_menu_item_a :: proc(hmenu: Hmenu, item: u32, fByPosition: Bool, lpmi: ^Menu_Item_Info_A) -> Bool --- - @(link_name="InsertMenuItemW") insert_menu_item_w :: proc(hmenu: Hmenu, item: u32, fByPosition: Bool, lpmi: ^Menu_Item_Info_W) -> Bool --- - - @(link_name="AppendMenuA") append_menu_a :: proc(menu: Hmenu, flags: u32, id_new_item: Uint_Ptr, new_item: cstring) -> Bool --- - @(link_name="AppendMenuW") append_menu_w :: proc(menu: Hmenu, flags: u32, id_new_item: Uint_Ptr, new_item: Wstring) -> Bool --- - - @(link_name="CheckMenuItem") check_menu_item :: proc(menu: Hmenu, id_check_item: u32, check: u32) -> u32 --- - @(link_name="CheckMenuRadioItem") check_menu_radio_item :: proc(menu: Hmenu, first, last: u32, check: u32, flags: u32) -> Bool --- - - @(link_name="GetPropA") get_prop_a :: proc(wnd: Hwnd, s: cstring) -> Handle --- - @(link_name="GetPropW") get_prop_w :: proc(wnd: Hwnd, s: Wstring) -> Handle --- - - @(link_name="MessageBoxA") message_box_a :: proc(wnd: Hwnd, text, caption: cstring, type: u32) -> i32 --- - @(link_name="MessageBoxW") message_box_w :: proc(wnd: Hwnd, text, caption: Wstring, type: u32) -> i32 --- - - @(link_name="MessageBoxExA") message_box_ex_a :: proc(wnd: Hwnd, text, caption: cstring, type: u32, language_id: u16) -> i32 --- - @(link_name="MessageBoxExW") message_box_ex_w :: proc(wnd: Hwnd, text, caption: Wstring, type: u32, language_id: u16) -> i32 --- - - @(link_name="BeginPaint") begin_paint :: proc(wnd: Hwnd, paint: ^Paint_Struct) -> Hdc --- - @(link_name="EndPaint") end_paint :: proc(wnd: Hwnd, paint: ^Paint_Struct) -> Bool --- -} - - -_IDC_APPSTARTING := rawptr(uintptr(32650)) -_IDC_ARROW := rawptr(uintptr(32512)) -_IDC_CROSS := rawptr(uintptr(32515)) -_IDC_HAND := rawptr(uintptr(32649)) -_IDC_HELP := rawptr(uintptr(32651)) -_IDC_IBEAM := rawptr(uintptr(32513)) -_IDC_ICON := rawptr(uintptr(32641)) -_IDC_NO := rawptr(uintptr(32648)) -_IDC_SIZE := rawptr(uintptr(32640)) -_IDC_SIZEALL := rawptr(uintptr(32646)) -_IDC_SIZENESW := rawptr(uintptr(32643)) -_IDC_SIZENS := rawptr(uintptr(32645)) -_IDC_SIZENWSE := rawptr(uintptr(32642)) -_IDC_SIZEWE := rawptr(uintptr(32644)) -_IDC_UPARROW := rawptr(uintptr(32516)) -_IDC_WAIT := rawptr(uintptr(32514)) -IDC_APPSTARTING := cstring(_IDC_APPSTARTING) -IDC_ARROW := cstring(_IDC_ARROW) -IDC_CROSS := cstring(_IDC_CROSS) -IDC_HAND := cstring(_IDC_HAND) -IDC_HELP := cstring(_IDC_HELP) -IDC_IBEAM := cstring(_IDC_IBEAM) -IDC_ICON := cstring(_IDC_ICON) -IDC_NO := cstring(_IDC_NO) -IDC_SIZE := cstring(_IDC_SIZE) -IDC_SIZEALL := cstring(_IDC_SIZEALL) -IDC_SIZENESW := cstring(_IDC_SIZENESW) -IDC_SIZENS := cstring(_IDC_SIZENS) -IDC_SIZENWSE := cstring(_IDC_SIZENWSE) -IDC_SIZEWE := cstring(_IDC_SIZEWE) -IDC_UPARROW := cstring(_IDC_UPARROW) -IDC_WAIT := cstring(_IDC_WAIT) diff --git a/core/sys/win32/wgl.odin b/core/sys/win32/wgl.odin deleted file mode 100644 index e6c414b0e..000000000 --- a/core/sys/win32/wgl.odin +++ /dev/null @@ -1,114 +0,0 @@ -// +build windows -package win32 - -foreign import "system:opengl32.lib" - -CONTEXT_MAJOR_VERSION_ARB :: 0x2091 -CONTEXT_MINOR_VERSION_ARB :: 0x2092 -CONTEXT_FLAGS_ARB :: 0x2094 -CONTEXT_PROFILE_MASK_ARB :: 0x9126 -CONTEXT_FORWARD_COMPATIBLE_BIT_ARB :: 0x0002 -CONTEXT_CORE_PROFILE_BIT_ARB :: 0x00000001 -CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB :: 0x00000002 - -Hglrc :: distinct Handle -Color_Ref :: distinct u32 - -Layer_Plane_Descriptor :: struct { - size: u16, - version: u16, - flags: u32, - pixel_type: u8, - color_bits: u8, - red_bits: u8, - red_shift: u8, - green_bits: u8, - green_shift: u8, - blue_bits: u8, - blue_shift: u8, - alpha_bits: u8, - alpha_shift: u8, - accum_bits: u8, - accum_red_bits: u8, - accum_green_bits: u8, - accum_blue_bits: u8, - accum_alpha_bits: u8, - depth_bits: u8, - stencil_bits: u8, - aux_buffers: u8, - layer_type: u8, - reserved: u8, - transparent: Color_Ref, -} - -Point_Float :: struct {x, y: f32} - -Glyph_Metrics_Float :: struct { - black_box_x: f32, - black_box_y: f32, - glyph_origin: Point_Float, - cell_inc_x: f32, - cell_inc_y: f32, -} - -Create_Context_Attribs_ARB_Type :: #type proc "c" (hdc: Hdc, h_share_context: rawptr, attribList: ^i32) -> Hglrc -Choose_Pixel_Format_ARB_Type :: #type proc "c" (hdc: Hdc, attrib_i_list: ^i32, attrib_f_list: ^f32, max_formats: u32, formats: ^i32, num_formats : ^u32) -> Bool -Swap_Interval_EXT_Type :: #type proc "c" (interval: i32) -> bool -Get_Extensions_String_ARB_Type :: #type proc "c" (Hdc) -> cstring - -// Procedures - create_context_attribs_arb: Create_Context_Attribs_ARB_Type - choose_pixel_format_arb: Choose_Pixel_Format_ARB_Type - swap_interval_ext: Swap_Interval_EXT_Type - get_extensions_string_arb: Get_Extensions_String_ARB_Type - - -foreign opengl32 { - @(link_name="wglCreateContext") - create_context :: proc(hdc: Hdc) -> Hglrc --- - - @(link_name="wglMakeCurrent") - make_current :: proc(hdc: Hdc, hglrc: Hglrc) -> Bool --- - - @(link_name="wglGetProcAddress") - get_gl_proc_address :: proc(c_str: cstring) -> rawptr --- - - @(link_name="wglDeleteContext") - delete_context :: proc(hglrc: Hglrc) -> Bool --- - - @(link_name="wglCopyContext") - copy_context :: proc(src, dst: Hglrc, mask: u32) -> Bool --- - - @(link_name="wglCreateLayerContext") - create_layer_context :: proc(hdc: Hdc, layer_plane: i32) -> Hglrc --- - - @(link_name="wglDescribeLayerPlane") - describe_layer_plane :: proc(hdc: Hdc, pixel_format, layer_plane: i32, bytes: u32, pd: ^Layer_Plane_Descriptor) -> Bool --- - - @(link_name="wglGetCurrentContext") - get_current_context :: proc() -> Hglrc --- - - @(link_name="wglGetCurrentDC") - get_current_dc :: proc() -> Hdc --- - - @(link_name="wglGetLayerPaletteEntries") - get_layer_palette_entries :: proc(hdc: Hdc, layer_plane, start, entries: i32, cr: ^Color_Ref) -> i32 --- - - @(link_name="wglRealizeLayerPalette") - realize_layer_palette :: proc(hdc: Hdc, layer_plane: i32, realize: Bool) -> Bool --- - - @(link_name="wglSetLayerPaletteEntries") - set_layer_palette_entries :: proc(hdc: Hdc, layer_plane, start, entries: i32, cr: ^Color_Ref) -> i32 --- - - @(link_name="wglShareLists") - share_lists :: proc(hglrc1, hglrc2: Hglrc) -> Bool --- - - @(link_name="wglSwapLayerBuffers") - swap_layer_buffers :: proc(hdc: Hdc, planes: u32) -> Bool --- - - @(link_name="wglUseFontBitmaps") - use_font_bitmaps :: proc(hdc: Hdc, first, count, list_base: u32) -> Bool --- - - @(link_name="wglUseFontOutlines") - use_font_outlines :: proc(hdc: Hdc, first, count, list_base: u32, deviation, extrusion: f32, format: i32, gmf: ^Glyph_Metrics_Float) -> Bool --- -} diff --git a/core/sys/win32/winmm.odin b/core/sys/win32/winmm.odin deleted file mode 100644 index 0f567fbcc..000000000 --- a/core/sys/win32/winmm.odin +++ /dev/null @@ -1,12 +0,0 @@ -// +build windows -package win32 - -foreign import "system:winmm.lib" - - -@(default_calling_convention = "std") -foreign winmm { - @(link_name="timeBeginPeriod") time_begin_period :: proc(period: u32) -> u32 --- - - @(link_name="timeGetTime") time_get_time :: proc() -> u32 --- -} diff --git a/core/sys/windows/advapi32.odin b/core/sys/windows/advapi32.odin index a2a24242f..82031d4f7 100644 --- a/core/sys/windows/advapi32.odin +++ b/core/sys/windows/advapi32.odin @@ -3,6 +3,8 @@ package sys_windows foreign import advapi32 "system:Advapi32.lib" +HCRYPTPROV :: distinct HANDLE + @(default_calling_convention="stdcall") foreign advapi32 { @(link_name = "SystemFunction036") @@ -10,6 +12,10 @@ foreign advapi32 { OpenProcessToken :: proc(ProcessHandle: HANDLE, DesiredAccess: DWORD, TokenHandle: ^HANDLE) -> BOOL --- + + CryptAcquireContextW :: proc(hProv: ^HCRYPTPROV, szContainer, szProvider: wstring, dwProvType, dwFlags: DWORD) -> DWORD --- + CryptGenRandom :: proc(hProv: HCRYPTPROV, dwLen: DWORD, buf: LPVOID) -> DWORD --- + CryptReleaseContext :: proc(hProv: HCRYPTPROV, dwFlags: DWORD) -> DWORD --- } // Necessary to create a token to impersonate a user with for CreateProcessAsUser @@ -64,4 +70,62 @@ foreign advapi32 { lpStartupInfo: LPSTARTUPINFO, lpProcessInformation: LPPROCESS_INFORMATION, ) -> BOOL --- -} \ No newline at end of file + + RegCreateKeyExW :: proc( + hKey: HKEY, + lpSubKey: LPCWSTR, + Reserved: DWORD, + lpClass: LPWSTR, + dwOptions: DWORD, + samDesired: REGSAM, + lpSecurityAttributes: LPSECURITY_ATTRIBUTES, + phkResult: PHKEY, + lpdwDisposition: LPDWORD, + ) -> LSTATUS --- + + RegOpenKeyW :: proc( + hKey: HKEY, + lpSubKey: LPCWSTR, + phkResult: PHKEY, + ) -> LSTATUS --- + + RegOpenKeyExW :: proc( + hKey: HKEY, + lpSubKey: LPCWSTR, + ulOptions: DWORD, + samDesired: REGSAM, + phkResult: PHKEY, + ) -> LSTATUS --- + + RegCloseKey :: proc( + hKey: HKEY, + ) -> LSTATUS --- + + RegGetValueW :: proc( + hkey: HKEY, + lpSubKey: LPCWSTR, + lpValue: LPCWSTR, + dwFlags: DWORD, + pdwType: LPDWORD, + pvData: PVOID, + pcbData: LPDWORD, + ) -> LSTATUS --- + + RegSetValueExW :: proc( + hKey: HKEY, + lpValueName: LPCWSTR, + Reserved: DWORD, + dwType: DWORD, + lpData: ^BYTE, + cbData: DWORD, + ) -> LSTATUS --- + + RegSetKeyValueW :: proc( + hKey: HKEY, + lpSubKey: LPCWSTR, + lpValueName: LPCWSTR, + dwType: DWORD, + lpData: LPCVOID, + cbData: DWORD, + ) -> LSTATUS --- +} diff --git a/core/sys/windows/bcrypt.odin b/core/sys/windows/bcrypt.odin index ed28d5b7f..52eb4b1b6 100644 --- a/core/sys/windows/bcrypt.odin +++ b/core/sys/windows/bcrypt.odin @@ -7,5 +7,5 @@ BCRYPT_USE_SYSTEM_PREFERRED_RNG: DWORD : 0x00000002 @(default_calling_convention="stdcall") foreign bcrypt { - BCryptGenRandom :: proc(hAlgorithm: LPVOID, pBuffer: ^u8, cbBuffer: ULONG, dwFlags: ULONG) -> LONG --- + BCryptGenRandom :: proc(hAlgorithm: LPVOID, pBuffer: [^]u8, cbBuffer: ULONG, dwFlags: ULONG) -> LONG --- } diff --git a/core/sys/windows/bluetooth.odin b/core/sys/windows/bluetooth.odin index c9f6bcc93..c2534896b 100644 --- a/core/sys/windows/bluetooth.odin +++ b/core/sys/windows/bluetooth.odin @@ -51,54 +51,27 @@ BLUETOOTH_DEVICE_INFO :: struct { name: [BLUETOOTH_MAX_NAME_SIZE]u16, // Name of the device } -@(default_calling_convention = "std") +@(default_calling_convention="stdcall") foreign bthprops { /* Version */ - @(link_name="BluetoothIsVersionAvailable") bluetooth_is_version_available :: proc( - major: u8, minor: u8, - ) -> BOOL --- + BluetoothIsVersionAvailable :: proc(major: u8, minor: u8) -> BOOL --- /* Radio enumeration */ - @(link_name="BluetoothFindFirstRadio") bluetooth_find_first_radio :: proc( - find_radio_params: ^BLUETOOTH_FIND_RADIO_PARAMS, radio: ^HANDLE, - ) -> HBLUETOOTH_RADIO_FIND --- - - @(link_name="BluetoothFindNextRadio") bluetooth_find_next_radio :: proc( - handle: HBLUETOOTH_RADIO_FIND, radio: ^HANDLE, - ) -> BOOL --- - - @(link_name="BluetoothFindRadioClose") bluetooth_find_radio_close :: proc( - handle: HBLUETOOTH_RADIO_FIND, - ) -> BOOL --- - - @(link_name="BluetoothGetRadioInfo") bluetooth_get_radio_info :: proc( - radio: HANDLE, radio_info: ^BLUETOOTH_RADIO_INFO, - ) -> DWORD --- + BluetoothFindFirstRadio :: proc(find_radio_params: ^BLUETOOTH_FIND_RADIO_PARAMS, radio: ^HANDLE) -> HBLUETOOTH_RADIO_FIND --- + BluetoothFindNextRadio :: proc(handle: HBLUETOOTH_RADIO_FIND, radio: ^HANDLE) -> BOOL --- + BluetoothFindRadioClose :: proc(handle: HBLUETOOTH_RADIO_FIND) -> BOOL --- + BluetoothGetRadioInfo :: proc(radio: HANDLE, radio_info: ^BLUETOOTH_RADIO_INFO) -> DWORD --- /* Device enumeration */ - @(link_name="BluetoothFindFirstDevice") bluetooth_find_first_device :: proc( - search_params: ^BLUETOOTH_DEVICE_SEARCH_PARAMS, device_info: ^BLUETOOTH_DEVICE_INFO, - ) -> HBLUETOOTH_DEVICE_FIND --- - - @(link_name="BluetoothFindNextDevice") bluetooth_find_next_device :: proc( - handle: HBLUETOOTH_DEVICE_FIND, device_info: ^BLUETOOTH_DEVICE_INFO, - ) -> BOOL --- - - @(link_name="BluetoothFindDeviceClose") bluetooth_find_device_close :: proc( - handle: HBLUETOOTH_DEVICE_FIND, - ) -> BOOL --- - - @(link_name="BluetoothGetDeviceInfo") bluetooth_get_device_info :: proc( - radio: HANDLE, device_info: ^BLUETOOTH_DEVICE_INFO, - ) -> DWORD --- - - @(link_name="BluetoothDisplayDeviceProperties") bluetooth_display_device_properties :: proc( - hwnd_parent: HWND, device_info: ^BLUETOOTH_DEVICE_INFO, - ) -> BOOL --- + BluetoothFindFirstDevice :: proc(search_params: ^BLUETOOTH_DEVICE_SEARCH_PARAMS, device_info: ^BLUETOOTH_DEVICE_INFO) -> HBLUETOOTH_DEVICE_FIND --- + BluetoothFindNextDevice :: proc(handle: HBLUETOOTH_DEVICE_FIND, device_info: ^BLUETOOTH_DEVICE_INFO) -> BOOL --- + BluetoothFindDeviceClose :: proc(handle: HBLUETOOTH_DEVICE_FIND) -> BOOL --- + BluetoothGetDeviceInfo :: proc(radio: HANDLE, device_info: ^BLUETOOTH_DEVICE_INFO) -> DWORD --- + BluetoothDisplayDeviceProperties :: proc(hwnd_parent: HWND, device_info: ^BLUETOOTH_DEVICE_INFO) -> BOOL --- } \ No newline at end of file diff --git a/core/sys/win32/comdlg32.odin b/core/sys/windows/comdlg32.odin similarity index 57% rename from core/sys/win32/comdlg32.odin rename to core/sys/windows/comdlg32.odin index 1e0d5c3ca..8284050f1 100644 --- a/core/sys/win32/comdlg32.odin +++ b/core/sys/windows/comdlg32.odin @@ -1,70 +1,41 @@ // +build windows -package win32 +package sys_windows -foreign import "system:comdlg32.lib" -import "core:strings" +foreign import "system:Comdlg32.lib" -OFN_Hook_Proc :: #type proc "stdcall" (hdlg: Hwnd, msg: u32, wparam: Wparam, lparam: Lparam) -> Uint_Ptr +LPOFNHOOKPROC :: #type proc "stdcall" (hdlg: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> UINT_PTR -Open_File_Name_A :: struct { - struct_size: u32, - hwnd_owner: Hwnd, - instance: Hinstance, - filter: cstring, - custom_filter: cstring, - max_cust_filter: u32, - filter_index: u32, - file: cstring, - max_file: u32, - file_title: cstring, - max_file_title: u32, - initial_dir: cstring, - title: cstring, - flags: u32, - file_offset: u16, - file_extension: u16, - def_ext: cstring, - cust_data: Lparam, - hook: OFN_Hook_Proc, - template_name: cstring, - pv_reserved: rawptr, - dw_reserved: u32, - flags_ex: u32, +OPENFILENAMEW :: struct { + lStructSize: DWORD, + hwndOwner: HWND, + hInstance: HINSTANCE, + lpstrFilter: wstring, + lpstrCustomFilter: wstring, + nMaxCustFilter: DWORD, + nFilterIndex: DWORD, + lpstrFile: wstring, + nMaxFile: DWORD, + lpstrFileTitle: wstring, + nMaxFileTitle: DWORD, + lpstrInitialDir: wstring, + lpstrTitle: wstring, + Flags: DWORD, + nFileOffset: WORD, + nFileExtension: WORD, + lpstrDefExt: wstring, + lCustData: LPARAM, + lpfnHook: LPOFNHOOKPROC, + lpTemplateName: wstring, + pvReserved: rawptr, + dwReserved: DWORD, + FlagsEx: DWORD, } -Open_File_Name_W :: struct { - struct_size: u32, - hwnd_owner: Hwnd, - instance: Hinstance, - filter: Wstring, - custom_filter: Wstring, - max_cust_filter: u32, - filter_index: u32, - file: Wstring, - max_file: u32, - file_title: Wstring, - max_file_title: u32, - initial_dir: Wstring, - title: Wstring, - flags: u32, - file_offset: u16, - file_extension: u16, - def_ext: Wstring, - cust_data: Lparam, - hook: OFN_Hook_Proc, - template_name: Wstring, - pv_reserved: rawptr, - dw_reserved: u32, - flags_ex: u32, -} - -@(default_calling_convention = "c") -foreign comdlg32 { - @(link_name="GetOpenFileNameA") get_open_file_name_a :: proc(arg1: ^Open_File_Name_A) -> Bool --- - @(link_name="GetOpenFileNameW") get_open_file_name_w :: proc(arg1: ^Open_File_Name_W) -> Bool --- - @(link_name="GetSaveFileNameA") get_save_file_name_a :: proc(arg1: ^Open_File_Name_A) -> Bool --- - @(link_name="GetSaveFileNameW") get_save_file_name_w :: proc(arg1: ^Open_File_Name_W) -> Bool --- - @(link_name="CommDlgExtendedError") comm_dlg_extended_error :: proc() -> u32 --- +@(default_calling_convention="stdcall") +foreign Comdlg32 { + GetOpenFileNameW :: proc(arg1: ^OPENFILENAMEW) -> BOOL --- + GetSaveFileNameW :: proc(arg1: ^OPENFILENAMEW) -> BOOL --- + CommDlgExtendedError :: proc() -> u32 --- } OPEN_TITLE :: "Select file to open" @@ -75,6 +46,9 @@ SAVE_TITLE :: "Select file to save" SAVE_FLAGS :: u32(OFN_OVERWRITEPROMPT | OFN_EXPLORER) SAVE_EXT :: "txt" +/* +import "core:strings" + Open_Save_Mode :: enum { Open = 0, Save = 1, @@ -100,23 +74,23 @@ _open_file_dialog :: proc(title: string, dir: string, filter = strings.join(filters, "\u0000", context.temp_allocator) filter = strings.concatenate({filter, "\u0000"}, context.temp_allocator) - ofn := Open_File_Name_W{ - struct_size = size_of(Open_File_Name_W), - file = Wstring(&file_buf[0]), - max_file = MAX_PATH_WIDE, - title = utf8_to_wstring(title, context.temp_allocator), - filter = utf8_to_wstring(filter, context.temp_allocator), - initial_dir = utf8_to_wstring(dir, context.temp_allocator), - filter_index = u32(clamp(default_filter, 1, filter_len / 2)), - def_ext = utf8_to_wstring(default_ext, context.temp_allocator), - flags = u32(flags), + ofn := OPENFILENAMEW{ + lStructSize = size_of(OPENFILENAMEW), + lpstrFile = wstring(&file_buf[0]), + nMaxFile = MAX_PATH_WIDE, + lpstrTitle = utf8_to_wstring(title, context.temp_allocator), + lpstrFilter = utf8_to_wstring(filter, context.temp_allocator), + lpstrInitialDir = utf8_to_wstring(dir, context.temp_allocator), + nFilterIndex = u32(clamp(default_filter, 1, filter_len / 2)), + lpstrDefExt = utf8_to_wstring(default_ext, context.temp_allocator), + Flags = u32(flags), } switch mode { case .Open: - ok = bool(get_open_file_name_w(&ofn)) + ok = bool(GetOpenFileNameW(&ofn)) case .Save: - ok = bool(get_save_file_name_w(&ofn)) + ok = bool(GetSaveFileNameW(&ofn)) case: ok = false } @@ -124,9 +98,9 @@ _open_file_dialog :: proc(title: string, dir: string, if !ok { return } - - file_name := utf16_to_utf8(file_buf[:], allocator) + + file_name, _ := utf16_to_utf8(file_buf[:], allocator) path = strings.trim_right_null(file_name) return } @@ -140,13 +114,14 @@ select_file_to_open :: proc(title := OPEN_TITLE, dir := ".", } select_file_to_save :: proc(title := SAVE_TITLE, dir := ".", - filters := []string{"All Files", "*.*"}, default_filter := u32(1), - flags := SAVE_FLAGS, default_ext := SAVE_EXT, - allocator := context.temp_allocator) -> (path: string, ok: bool) { + filters := []string{"All Files", "*.*"}, default_filter := u32(1), + flags := SAVE_FLAGS, default_ext := SAVE_EXT, + allocator := context.temp_allocator) -> (path: string, ok: bool) { path, ok = _open_file_dialog(title, dir, filters, default_filter, flags, default_ext, Open_Save_Mode.Save, allocator) return } +*/ // TODO: Implement convenience function for select_file_to_open with ALLOW_MULTI_SELECT that takes // it output of the form "path\u0000\file1u\0000file2" and turns it into []string with the path + file pre-concatenated for you. diff --git a/core/sys/windows/dwmapi.odin b/core/sys/windows/dwmapi.odin new file mode 100644 index 000000000..468b5bb87 --- /dev/null +++ b/core/sys/windows/dwmapi.odin @@ -0,0 +1,9 @@ +// +build windows +package sys_windows + +foreign import dwmapi "system:Dwmapi.lib" + +@(default_calling_convention="stdcall") +foreign dwmapi { + DwmFlush :: proc() -> HRESULT --- +} diff --git a/core/sys/windows/gdi32.odin b/core/sys/windows/gdi32.odin new file mode 100644 index 000000000..4403a5dc3 --- /dev/null +++ b/core/sys/windows/gdi32.odin @@ -0,0 +1,84 @@ +// +build windows +package sys_windows + +foreign import gdi32 "system:Gdi32.lib" + +@(default_calling_convention="stdcall") +foreign gdi32 { + GetStockObject :: proc(i: c_int) -> HGDIOBJ --- + SelectObject :: proc(hdc: HDC, h: HGDIOBJ) -> HGDIOBJ --- + DeleteObject :: proc(ho: HGDIOBJ) -> BOOL --- + SetBkColor :: proc(hdc: HDC, color: COLORREF) -> COLORREF --- + + CreateDIBPatternBrush :: proc(h: HGLOBAL, iUsage: UINT) -> HBRUSH --- + + CreateDIBitmap :: proc( + hdc: HDC, + pbmih: ^BITMAPINFOHEADER, + flInit: DWORD, + pjBits: VOID, + pbmi: ^BITMAPINFO, + iUsage: UINT, + ) -> HBITMAP --- + + CreateDIBSection :: proc( + hdc: HDC, + pbmi: ^BITMAPINFO, + usage: UINT, + ppvBits: VOID, + hSection: HANDLE, + offset: DWORD, + ) -> HBITMAP --- + + StretchDIBits :: proc( + hdc: HDC, + xDest: c_int, + yDest: c_int, + DestWidth: c_int, + DestHeight: c_int, + xSrc: c_int, + ySrc: c_int, + SrcWidth: c_int, + SrcHeight: c_int, + lpBits: VOID, + lpbmi: ^BITMAPINFO, + iUsage: UINT, + rop: DWORD, + ) -> c_int --- + + StretchBlt :: proc( + hdcDest: HDC, + xDest: c_int, + yDest: c_int, + wDest: c_int, + hDest: c_int, + hdcSrc: HDC, + xSrc: c_int, + ySrc: c_int, + wSrc: c_int, + hSrc: c_int, + rop: DWORD, + ) -> BOOL --- + + SetPixelFormat :: proc(hdc: HDC, format: c_int, ppfd: ^PIXELFORMATDESCRIPTOR) -> BOOL --- + ChoosePixelFormat :: proc(hdc: HDC, ppfd: ^PIXELFORMATDESCRIPTOR) -> c_int --- + SwapBuffers :: proc(HDC) -> BOOL --- + + SetDCBrushColor :: proc(hdc: HDC, color: COLORREF) -> COLORREF --- + GetDCBrushColor :: proc(hdc: HDC) -> COLORREF --- + PatBlt :: proc(hdc: HDC, x, y, w, h: c_int, rop: DWORD) -> BOOL --- + Rectangle :: proc(hdc: HDC, left, top, right, bottom: c_int) -> BOOL --- + + CreateFontW :: proc( + cHeight, cWidth, cEscapement, cOrientation, cWeight: c_int, + bItalic, bUnderline, bStrikeOut, iCharSet, iOutPrecision: DWORD, + iClipPrecision, iQuality, iPitchAndFamily: DWORD, + pszFaceName: LPCWSTR, + ) -> HFONT --- + TextOutW :: proc(hdc: HDC, x, y: c_int, lpString: LPCWSTR, c: c_int) -> BOOL --- + GetTextExtentPoint32W :: proc(hdc: HDC, lpString: LPCWSTR, c: c_int, psizl: LPSIZE) -> BOOL --- +} + +RGB :: #force_inline proc "contextless" (r, g, b: u8) -> COLORREF { + return transmute(COLORREF)[4]u8{r, g, b, 0} +} diff --git a/core/sys/windows/kernel32.odin b/core/sys/windows/kernel32.odin index 8c58fbd52..284936852 100644 --- a/core/sys/windows/kernel32.odin +++ b/core/sys/windows/kernel32.odin @@ -3,11 +3,10 @@ package sys_windows foreign import kernel32 "system:Kernel32.lib" - - @(default_calling_convention="stdcall") foreign kernel32 { - OutputDebugStringA :: proc(lpOutputString: LPCSTR) --- + OutputDebugStringA :: proc(lpOutputString: LPCSTR) --- // The only A thing that is allowed + OutputDebugStringW :: proc(lpOutputString: LPCWSTR) --- ReadConsoleW :: proc(hConsoleInput: HANDLE, lpBuffer: LPVOID, @@ -23,12 +22,18 @@ foreign kernel32 { GetConsoleMode :: proc(hConsoleHandle: HANDLE, lpMode: LPDWORD) -> BOOL --- - + SetConsoleMode :: proc(hConsoleHandle: HANDLE, + dwMode: DWORD) -> BOOL --- GetFileInformationByHandle :: proc(hFile: HANDLE, lpFileInformation: LPBY_HANDLE_FILE_INFORMATION) -> BOOL --- SetHandleInformation :: proc(hObject: HANDLE, dwMask: DWORD, dwFlags: DWORD) -> BOOL --- + SetFileInformationByHandle :: proc(hFile: HANDLE, + FileInformationClass: FILE_INFO_BY_HANDLE_CLASS, + lpFileInformation: LPVOID, + dwBufferSize: DWORD) -> BOOL --- + AddVectoredExceptionHandler :: proc(FirstHandler: ULONG, VectoredHandler: PVECTORED_EXCEPTION_HANDLER) -> LPVOID --- AddVectoredContinueHandler :: proc(FirstHandler: ULONG, VectoredHandler: PVECTORED_EXCEPTION_HANDLER) -> LPVOID --- @@ -62,6 +67,13 @@ foreign kernel32 { GetCurrentProcessId :: proc() -> DWORD --- GetCurrentThread :: proc() -> HANDLE --- GetCurrentThreadId :: proc() -> DWORD --- + GetProcessTimes :: proc( + hProcess: HANDLE, + lpCreationTime: LPFILETIME, + lpExitTime: LPFILETIME, + lpKernelTime: LPFILETIME, + lpUserTime: LPFILETIME, + ) -> BOOL --- GetStdHandle :: proc(which: DWORD) -> HANDLE --- ExitProcess :: proc(uExitCode: c_uint) -> ! --- DeviceIoControl :: proc( @@ -89,9 +101,23 @@ foreign kernel32 { GetExitCodeThread :: proc(thread: HANDLE, exit_code: ^DWORD) -> BOOL --- TerminateThread :: proc(thread: HANDLE, exit_code: DWORD) -> BOOL --- - CreateSemaphoreW :: proc(attributes: LPSECURITY_ATTRIBUTES, initial_count, maximum_count: LONG, name: LPCSTR) -> HANDLE --- + CreateSemaphoreW :: proc(attributes: LPSECURITY_ATTRIBUTES, initial_count, maximum_count: LONG, name: LPCWSTR) -> HANDLE --- ReleaseSemaphore :: proc(semaphore: HANDLE, release_count: LONG, previous_count: ^LONG) -> BOOL --- + CreateWaitableTimerW :: proc( + lpTimerAttributes: LPSECURITY_ATTRIBUTES, + bManualReset: BOOL, + lpTimerName: LPCWSTR, + ) -> HANDLE --- + SetWaitableTimerEx :: proc( + hTimer: HANDLE, + lpDueTime: ^LARGE_INTEGER, + lPeriod: LONG, + pfnCompletionRoutine: PTIMERAPCROUTINE, + lpArgToCompletionRoutine: LPVOID, + WakeContext: PREASON_CONTEXT, + TolerableDelay: ULONG, + ) -> BOOL --- WaitForSingleObject :: proc(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD --- Sleep :: proc(dwMilliseconds: DWORD) --- GetProcessId :: proc(handle: HANDLE) -> DWORD --- @@ -217,6 +243,7 @@ foreign kernel32 { bInitialState: BOOL, lpName: LPCWSTR, ) -> HANDLE --- + ResetEvent :: proc(hEvent: HANDLE) -> BOOL --- WaitForMultipleObjects :: proc( nCount: DWORD, lpHandles: ^HANDLE, @@ -245,6 +272,22 @@ foreign kernel32 { HeapReAlloc :: proc(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID, dwBytes: SIZE_T) -> LPVOID --- HeapFree :: proc(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID) -> BOOL --- + LocalAlloc :: proc(flags: UINT, bytes: SIZE_T) -> LPVOID --- + LocalReAlloc :: proc(mem: LPVOID, bytes: SIZE_T, flags: UINT) -> LPVOID --- + LocalFree :: proc(mem: LPVOID) -> LPVOID --- + + + ReadDirectoryChangesW :: proc( + hDirectory: HANDLE, + lpBuffer: LPVOID, + nBufferLength: DWORD, + bWatchSubtree: BOOL, + dwNotifyFilter: DWORD, + lpBytesReturned: LPDWORD, + lpOverlapped: LPOVERLAPPED, + lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE, + ) -> BOOL --- + InitializeSRWLock :: proc(SRWLock: ^SRWLOCK) --- AcquireSRWLockExclusive :: proc(SRWLock: ^SRWLOCK) --- TryAcquireSRWLockExclusive :: proc(SRWLock: ^SRWLOCK) -> BOOL --- @@ -286,7 +329,6 @@ foreign kernel32 { } -STANDARD_RIGHTS_REQUIRED :: DWORD(0x000F0000) SECTION_QUERY :: DWORD(0x0001) SECTION_MAP_WRITE :: DWORD(0x0002) SECTION_MAP_READ :: DWORD(0x0004) @@ -341,6 +383,7 @@ MEM_TOP_DOWN :: 0x100000 MEM_LARGE_PAGES :: 0x20000000 MEM_4MB_PAGES :: 0x80000000 +@(default_calling_convention="stdcall") foreign kernel32 { VirtualAlloc :: proc( lpAddress: LPVOID, @@ -483,6 +526,7 @@ LowMemoryResourceNotification :: MEMORY_RESOURCE_NOTIFICATION_TYPE.LowMemoryRes HighMemoryResourceNotification :: MEMORY_RESOURCE_NOTIFICATION_TYPE.HighMemoryResourceNotification +@(default_calling_convention="stdcall") foreign kernel32 { CreateMemoryResourceNotification :: proc( NotificationType: MEMORY_RESOURCE_NOTIFICATION_TYPE, @@ -498,6 +542,7 @@ FILE_CACHE_MAX_HARD_DISABLE :: DWORD(0x00000002) FILE_CACHE_MIN_HARD_ENABLE :: DWORD(0x00000004) FILE_CACHE_MIN_HARD_DISABLE :: DWORD(0x00000008) +@(default_calling_convention="stdcall") foreign kernel32 { GetSystemFileCacheSize :: proc( lpMinimumFileCacheSize: PSIZE_T, @@ -527,6 +572,7 @@ WIN32_MEMORY_RANGE_ENTRY :: struct { PWIN32_MEMORY_RANGE_ENTRY :: ^WIN32_MEMORY_RANGE_ENTRY +@(default_calling_convention="stdcall") foreign kernel32 { PrefetchVirtualMemory :: proc( hProcess: HANDLE, @@ -584,6 +630,7 @@ foreign kernel32 { MEHC_PATROL_SCRUBBER_PRESENT :: ULONG(0x1) +@(default_calling_convention="stdcall") foreign kernel32 { GetMemoryErrorHandlingCapabilities :: proc( Capabilities: PULONG, @@ -592,6 +639,7 @@ foreign kernel32 { PBAD_MEMORY_CALLBACK_ROUTINE :: #type proc "stdcall" () +@(default_calling_convention="stdcall") foreign kernel32 { RegisterBadMemoryNotification :: proc( Callback: PBAD_MEMORY_CALLBACK_ROUTINE, @@ -612,6 +660,7 @@ VmOfferPriorityLow :: OFFER_PRIORITY.VmOfferPriorityLow VmOfferPriorityBelowNormal :: OFFER_PRIORITY.VmOfferPriorityBelowNormal VmOfferPriorityNormal :: OFFER_PRIORITY.VmOfferPriorityNormal +@(default_calling_convention="stdcall") foreign kernel32 { OfferVirtualMemory :: proc( VirtualAddress: PVOID, @@ -676,6 +725,7 @@ WIN32_MEMORY_REGION_INFORMATION_u_s_Bitfield :: distinct ULONG Reserved : 32-6, }*/ +@(default_calling_convention="stdcall") foreign kernel32 { QueryVirtualMemoryInformation :: proc( Process: HANDLE, @@ -700,7 +750,7 @@ foreign kernel32 { NUMA_NO_PREFERRED_NODE :: 0xffffffff -MapViewOfFile2 :: #force_inline proc( +MapViewOfFile2 :: #force_inline proc "stdcall" ( FileMappingHandle: HANDLE, ProcessHandle: HANDLE, Offset: ULONG64, @@ -721,6 +771,7 @@ MapViewOfFile2 :: #force_inline proc( ) } +@(default_calling_convention="stdcall") foreign kernel32 { UnmapViewOfFile2 :: proc( ProcessHandle: HANDLE, @@ -728,3 +779,18 @@ foreign kernel32 { UnmapFlags: ULONG, ) -> BOOL --- } + +@(default_calling_convention="stdcall") +foreign kernel32 { + @(link_name="SetConsoleCtrlHandler") set_console_ctrl_handler :: proc(handler: Handler_Routine, add: BOOL) -> BOOL --- +} + +Handler_Routine :: proc(dwCtrlType: Control_Event) -> BOOL + +Control_Event :: enum DWORD { + control_c = 0, + _break = 1, + close = 2, + logoff = 5, + shutdown = 6, +} diff --git a/core/sys/windows/key_codes.odin b/core/sys/windows/key_codes.odin new file mode 100644 index 000000000..284b0e437 --- /dev/null +++ b/core/sys/windows/key_codes.odin @@ -0,0 +1,260 @@ +// +build windows +package sys_windows + +// https://docs.microsoft.com/en-us/windows/win32/inputdev/about-keyboard-input +KF_EXTENDED :: 0x0100 +KF_DLGMODE :: 0x0800 +KF_MENUMODE :: 0x1000 +KF_ALTDOWN :: 0x2000 +KF_REPEAT :: 0x4000 +KF_UP :: 0x8000 + +// https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes +// Virtual Keys, Standard Set +VK_LBUTTON :: 0x01 +VK_RBUTTON :: 0x02 +VK_CANCEL :: 0x03 +VK_MBUTTON :: 0x04 // NOT contiguous with L & RBUTTON +VK_XBUTTON1 :: 0x05 // NOT contiguous with L & RBUTTON +VK_XBUTTON2 :: 0x06 // NOT contiguous with L & RBUTTON + +// 0x07 : reserved + +VK_BACK :: 0x08 +VK_TAB :: 0x09 + +// 0x0A - 0x0B : reserved + +VK_CLEAR :: 0x0C +VK_RETURN :: 0x0D + +// 0x0E - 0x0F : unassigned + +VK_SHIFT :: 0x10 +VK_CONTROL :: 0x11 +VK_MENU :: 0x12 +VK_PAUSE :: 0x13 +VK_CAPITAL :: 0x14 +VK_KANA :: 0x15 +VK_HANGEUL :: 0x15 // old name - should be here for compatibility +VK_HANGUL :: 0x15 +VK_IME_ON :: 0x16 +VK_JUNJA :: 0x17 +VK_FINAL :: 0x18 +VK_HANJA :: 0x19 +VK_KANJI :: 0x19 +VK_IME_OFF :: 0x1A +VK_ESCAPE :: 0x1B +VK_CONVERT :: 0x1C +VK_NONCONVERT :: 0x1D +VK_ACCEPT :: 0x1E +VK_MODECHANGE :: 0x1F +VK_SPACE :: 0x20 +VK_PRIOR :: 0x21 +VK_NEXT :: 0x22 +VK_END :: 0x23 +VK_HOME :: 0x24 +VK_LEFT :: 0x25 +VK_UP :: 0x26 +VK_RIGHT :: 0x27 +VK_DOWN :: 0x28 +VK_SELECT :: 0x29 +VK_PRINT :: 0x2A +VK_EXECUTE :: 0x2B +VK_SNAPSHOT :: 0x2C +VK_INSERT :: 0x2D +VK_DELETE :: 0x2E +VK_HELP :: 0x2F + +VK_0 :: '0' +VK_1 :: '1' +VK_2 :: '2' +VK_3 :: '3' +VK_4 :: '4' +VK_5 :: '5' +VK_6 :: '6' +VK_7 :: '7' +VK_8 :: '8' +VK_9 :: '9' + +// 0x3A - 0x40 : unassigned + +VK_A :: 'A' +VK_B :: 'B' +VK_C :: 'C' +VK_D :: 'D' +VK_E :: 'E' +VK_F :: 'F' +VK_G :: 'G' +VK_H :: 'H' +VK_I :: 'I' +VK_J :: 'J' +VK_K :: 'K' +VK_L :: 'L' +VK_M :: 'M' +VK_N :: 'N' +VK_O :: 'O' +VK_P :: 'P' +VK_Q :: 'Q' +VK_R :: 'R' +VK_S :: 'S' +VK_T :: 'T' +VK_U :: 'U' +VK_V :: 'V' +VK_W :: 'W' +VK_X :: 'X' +VK_Y :: 'Y' +VK_Z :: 'Z' + +VK_LWIN :: 0x5B +VK_RWIN :: 0x5C +VK_APPS :: 0x5D + +// 0x5E : reserved + +VK_SLEEP :: 0x5F +VK_NUMPAD0 :: 0x60 +VK_NUMPAD1 :: 0x61 +VK_NUMPAD2 :: 0x62 +VK_NUMPAD3 :: 0x63 +VK_NUMPAD4 :: 0x64 +VK_NUMPAD5 :: 0x65 +VK_NUMPAD6 :: 0x66 +VK_NUMPAD7 :: 0x67 +VK_NUMPAD8 :: 0x68 +VK_NUMPAD9 :: 0x69 +VK_MULTIPLY :: 0x6A +VK_ADD :: 0x6B +VK_SEPARATOR :: 0x6C +VK_SUBTRACT :: 0x6D +VK_DECIMAL :: 0x6E +VK_DIVIDE :: 0x6F +VK_F1 :: 0x70 +VK_F2 :: 0x71 +VK_F3 :: 0x72 +VK_F4 :: 0x73 +VK_F5 :: 0x74 +VK_F6 :: 0x75 +VK_F7 :: 0x76 +VK_F8 :: 0x77 +VK_F9 :: 0x78 +VK_F10 :: 0x79 +VK_F11 :: 0x7A +VK_F12 :: 0x7B +VK_F13 :: 0x7C +VK_F14 :: 0x7D +VK_F15 :: 0x7E +VK_F16 :: 0x7F +VK_F17 :: 0x80 +VK_F18 :: 0x81 +VK_F19 :: 0x82 +VK_F20 :: 0x83 +VK_F21 :: 0x84 +VK_F22 :: 0x85 +VK_F23 :: 0x86 +VK_F24 :: 0x87 + +// 0x88 - 0x8F : reserved + +VK_NUMLOCK :: 0x90 +VK_SCROLL :: 0x91 + +// NEC PC-9800 kbd definitions +VK_OEM_NEC_EQUAL :: 0x92 // '=' key on numpad + +// Fujitsu/OASYS kbd definitions +VK_OEM_FJ_JISHO :: 0x92 // 'Dictionary' key +VK_OEM_FJ_MASSHOU :: 0x93 // 'Unregister word' key +VK_OEM_FJ_TOUROKU :: 0x94 // 'Register word' key +VK_OEM_FJ_LOYA :: 0x95 // 'Left OYAYUBI' key +VK_OEM_FJ_ROYA :: 0x96 // 'Right OYAYUBI' key + +// 0x97 - 0x9F : unassigned + +// VK_L* & VK_R* - left and right Alt, Ctrl and Shift virtual keys. +// Used only as parameters to GetAsyncKeyState() and GetKeyState(). +// No other API or message will distinguish left and right keys in this way. +VK_LSHIFT :: 0xA0 +VK_RSHIFT :: 0xA1 +VK_LCONTROL :: 0xA2 +VK_RCONTROL :: 0xA3 +VK_LMENU :: 0xA4 +VK_RMENU :: 0xA5 + +VK_BROWSER_BACK :: 0xA6 +VK_BROWSER_FORWARD :: 0xA7 +VK_BROWSER_REFRESH :: 0xA8 +VK_BROWSER_STOP :: 0xA9 +VK_BROWSER_SEARCH :: 0xAA +VK_BROWSER_FAVORITES :: 0xAB +VK_BROWSER_HOME :: 0xAC +VK_VOLUME_MUTE :: 0xAD +VK_VOLUME_DOWN :: 0xAE +VK_VOLUME_UP :: 0xAF +VK_MEDIA_NEXT_TRACK :: 0xB0 +VK_MEDIA_PREV_TRACK :: 0xB1 +VK_MEDIA_STOP :: 0xB2 +VK_MEDIA_PLAY_PAUSE :: 0xB3 +VK_LAUNCH_MAIL :: 0xB4 +VK_LAUNCH_MEDIA_SELECT :: 0xB5 +VK_LAUNCH_APP1 :: 0xB6 +VK_LAUNCH_APP2 :: 0xB7 + +// 0xB8 - 0xB9 : reserved + +VK_OEM_1 :: 0xBA // ';:' for US +VK_OEM_PLUS :: 0xBB // '+' any country +VK_OEM_COMMA :: 0xBC // ',' any country +VK_OEM_MINUS :: 0xBD // '-' any country +VK_OEM_PERIOD :: 0xBE // '.' any country +VK_OEM_2 :: 0xBF // '/?' for US +VK_OEM_3 :: 0xC0 // '`~' for US + +// 0xC1 - 0xDA : reserved + +VK_OEM_4 :: 0xDB // '[{' for US +VK_OEM_5 :: 0xDC // '\|' for US +VK_OEM_6 :: 0xDD // ']}' for US +VK_OEM_7 :: 0xDE // ''"' for US +VK_OEM_8 :: 0xDF + +// 0xE0 : reserved + +// Various extended or enhanced keyboards +VK_OEM_AX :: 0xE1 // 'AX' key on Japanese AX kbd +VK_OEM_102 :: 0xE2 // "<>" or "\|" on RT 102-key kbd. +VK_ICO_HELP :: 0xE3 // Help key on ICO +VK_ICO_00 :: 0xE4 // 00 key on ICO + +VK_PROCESSKEY :: 0xE5 +VK_ICO_CLEAR :: 0xE6 +VK_PACKET :: 0xE7 + +// 0xE8 : unassigned + +// Nokia/Ericsson definitions +VK_OEM_RESET :: 0xE9 +VK_OEM_JUMP :: 0xEA +VK_OEM_PA1 :: 0xEB +VK_OEM_PA2 :: 0xEC +VK_OEM_PA3 :: 0xED +VK_OEM_WSCTRL :: 0xEE +VK_OEM_CUSEL :: 0xEF +VK_OEM_ATTN :: 0xF0 +VK_OEM_FINISH :: 0xF1 +VK_OEM_COPY :: 0xF2 +VK_OEM_AUTO :: 0xF3 +VK_OEM_ENLW :: 0xF4 +VK_OEM_BACKTAB :: 0xF5 + +VK_ATTN :: 0xF6 +VK_CRSEL :: 0xF7 +VK_EXSEL :: 0xF8 +VK_EREOF :: 0xF9 +VK_PLAY :: 0xFA +VK_ZOOM :: 0xFB +VK_NONAME :: 0xFC +VK_PA1 :: 0xFD +VK_OEM_CLEAR :: 0xFE + +// 0xFF : reserved diff --git a/core/sys/windows/ntdll.odin b/core/sys/windows/ntdll.odin index 5deffd9f9..dda5b9711 100644 --- a/core/sys/windows/ntdll.odin +++ b/core/sys/windows/ntdll.odin @@ -3,7 +3,7 @@ package sys_windows foreign import ntdll_lib "system:ntdll.lib" -@(default_calling_convention="std") +@(default_calling_convention="stdcall") foreign ntdll_lib { - RtlGetVersion :: proc(lpVersionInformation: ^OSVERSIONINFOEXW) -> NTSTATUS --- + RtlGetVersion :: proc(lpVersionInformation: ^OSVERSIONINFOEXW) -> NTSTATUS --- } \ No newline at end of file diff --git a/core/sys/windows/ole32.odin b/core/sys/windows/ole32.odin new file mode 100644 index 000000000..4a4b470ea --- /dev/null +++ b/core/sys/windows/ole32.odin @@ -0,0 +1,39 @@ +// +build windows +package sys_windows + +foreign import "system:Ole32.lib" + +//objbase.h +COINIT :: enum DWORD { + APARTMENTTHREADED = 0x2, + MULTITHREADED, + DISABLE_OLE1DDE = 0x4, + SPEED_OVER_MEMORY = 0x8, +} + +IUnknown :: struct { + using Vtbl: ^IUnknownVtbl, +} +IUnknownVtbl :: struct { + QueryInterface: proc "stdcall" (This: ^IUnknown, riid: REFIID, ppvObject: ^rawptr) -> HRESULT, + AddRef: proc "stdcall" (This: ^IUnknown) -> ULONG, + Release: proc "stdcall" (This: ^IUnknown) -> ULONG, +} + +LPUNKNOWN :: ^IUnknown + +@(default_calling_convention="stdcall") +foreign Ole32 { + CoInitializeEx :: proc(reserved: rawptr, co_init: COINIT) -> HRESULT --- + CoUninitialize :: proc() --- + + CoCreateInstance :: proc( + rclsid: REFCLSID, + pUnkOuter: LPUNKNOWN, + dwClsContext: DWORD, + riid: REFIID, + ppv: ^LPVOID, + ) -> HRESULT --- + + CoTaskMemFree :: proc(pv: rawptr) --- +} diff --git a/core/sys/windows/shell32.odin b/core/sys/windows/shell32.odin index 70d8943bd..a6ecefc32 100644 --- a/core/sys/windows/shell32.odin +++ b/core/sys/windows/shell32.odin @@ -3,7 +3,7 @@ package sys_windows foreign import shell32 "system:Shell32.lib" -@(default_calling_convention = "std") +@(default_calling_convention="stdcall") foreign shell32 { CommandLineToArgvW :: proc(cmd_list: wstring, num_args: ^c_int) -> ^wstring --- } diff --git a/core/sys/windows/shlwapi.odin b/core/sys/windows/shlwapi.odin new file mode 100644 index 000000000..1852d536f --- /dev/null +++ b/core/sys/windows/shlwapi.odin @@ -0,0 +1,11 @@ +// +build windows +package sys_windows + +foreign import shlwapi "system:shlwapi.lib" + +@(default_calling_convention="stdcall") +foreign shlwapi { + PathFileExistsW :: proc(pszPath: wstring) -> BOOL --- + PathFindExtensionW :: proc(pszPath: wstring) -> wstring --- + PathFindFileNameW :: proc(pszPath: wstring) -> wstring --- +} diff --git a/core/sys/windows/synchronization.odin b/core/sys/windows/synchronization.odin index c4e1d2188..c98730aa0 100644 --- a/core/sys/windows/synchronization.odin +++ b/core/sys/windows/synchronization.odin @@ -5,7 +5,7 @@ foreign import Synchronization "system:Synchronization.lib" @(default_calling_convention="stdcall") foreign Synchronization { - WaitOnAddress :: proc(Address: PVOID, CompareAddress: PVOID, AddressSize: SIZE_T, dwMilliseconds: DWORD) -> BOOL --- + WaitOnAddress :: proc(Address: PVOID, CompareAddress: PVOID, AddressSize: SIZE_T, dwMilliseconds: DWORD) -> BOOL --- WakeByAddressSingle :: proc(Address: PVOID) --- - WakeByAddressAll :: proc(Address: PVOID) --- + WakeByAddressAll :: proc(Address: PVOID) --- } diff --git a/core/sys/windows/system_params.odin b/core/sys/windows/system_params.odin new file mode 100644 index 000000000..e94d777bf --- /dev/null +++ b/core/sys/windows/system_params.odin @@ -0,0 +1,271 @@ +// +build windows +package sys_windows + +// Parameter for SystemParametersInfo. +SPI_GETBEEP :: 0x0001 +SPI_SETBEEP :: 0x0002 +SPI_GETMOUSE :: 0x0003 +SPI_SETMOUSE :: 0x0004 +SPI_GETBORDER :: 0x0005 +SPI_SETBORDER :: 0x0006 +SPI_GETKEYBOARDSPEED :: 0x000A +SPI_SETKEYBOARDSPEED :: 0x000B +SPI_LANGDRIVER :: 0x000C +SPI_ICONHORIZONTALSPACING :: 0x000D +SPI_GETSCREENSAVETIMEOUT :: 0x000E +SPI_SETSCREENSAVETIMEOUT :: 0x000F +SPI_GETSCREENSAVEACTIVE :: 0x0010 +SPI_SETSCREENSAVEACTIVE :: 0x0011 +SPI_GETGRIDGRANULARITY :: 0x0012 +SPI_SETGRIDGRANULARITY :: 0x0013 +SPI_SETDESKWALLPAPER :: 0x0014 +SPI_SETDESKPATTERN :: 0x0015 +SPI_GETKEYBOARDDELAY :: 0x0016 +SPI_SETKEYBOARDDELAY :: 0x0017 +SPI_ICONVERTICALSPACING :: 0x0018 +SPI_GETICONTITLEWRAP :: 0x0019 +SPI_SETICONTITLEWRAP :: 0x001A +SPI_GETMENUDROPALIGNMENT :: 0x001B +SPI_SETMENUDROPALIGNMENT :: 0x001C +SPI_SETDOUBLECLKWIDTH :: 0x001D +SPI_SETDOUBLECLKHEIGHT :: 0x001E +SPI_GETICONTITLELOGFONT :: 0x001F +SPI_SETDOUBLECLICKTIME :: 0x0020 +SPI_SETMOUSEBUTTONSWAP :: 0x0021 +SPI_SETICONTITLELOGFONT :: 0x0022 +SPI_GETFASTTASKSWITCH :: 0x0023 +SPI_SETFASTTASKSWITCH :: 0x0024 + +SPI_SETDRAGFULLWINDOWS :: 0x0025 +SPI_GETDRAGFULLWINDOWS :: 0x0026 +SPI_GETNONCLIENTMETRICS :: 0x0029 +SPI_SETNONCLIENTMETRICS :: 0x002A +SPI_GETMINIMIZEDMETRICS :: 0x002B +SPI_SETMINIMIZEDMETRICS :: 0x002C +SPI_GETICONMETRICS :: 0x002D +SPI_SETICONMETRICS :: 0x002E +SPI_SETWORKAREA :: 0x002F +SPI_GETWORKAREA :: 0x0030 +SPI_SETPENWINDOWS :: 0x0031 +SPI_GETHIGHCONTRAST :: 0x0042 +SPI_SETHIGHCONTRAST :: 0x0043 +SPI_GETKEYBOARDPREF :: 0x0044 +SPI_SETKEYBOARDPREF :: 0x0045 +SPI_GETSCREENREADER :: 0x0046 +SPI_SETSCREENREADER :: 0x0047 +SPI_GETANIMATION :: 0x0048 +SPI_SETANIMATION :: 0x0049 +SPI_GETFONTSMOOTHING :: 0x004A +SPI_SETFONTSMOOTHING :: 0x004B +SPI_SETDRAGWIDTH :: 0x004C +SPI_SETDRAGHEIGHT :: 0x004D +SPI_SETHANDHELD :: 0x004E +SPI_GETLOWPOWERTIMEOUT :: 0x004F +SPI_GETPOWEROFFTIMEOUT :: 0x0050 +SPI_SETLOWPOWERTIMEOUT :: 0x0051 +SPI_SETPOWEROFFTIMEOUT :: 0x0052 +SPI_GETLOWPOWERACTIVE :: 0x0053 +SPI_GETPOWEROFFACTIVE :: 0x0054 +SPI_SETLOWPOWERACTIVE :: 0x0055 +SPI_SETPOWEROFFACTIVE :: 0x0056 +SPI_SETCURSORS :: 0x0057 +SPI_SETICONS :: 0x0058 +SPI_GETDEFAULTINPUTLANG :: 0x0059 +SPI_SETDEFAULTINPUTLANG :: 0x005A +SPI_SETLANGTOGGLE :: 0x005B +SPI_GETWINDOWSEXTENSION :: 0x005C +SPI_SETMOUSETRAILS :: 0x005D +SPI_GETMOUSETRAILS :: 0x005E +SPI_SETSCREENSAVERRUNNING :: 0x0061 + +SPI_SCREENSAVERRUNNING :: SPI_SETSCREENSAVERRUNNING +SPI_GETFILTERKEYS :: 0x0032 +SPI_SETFILTERKEYS :: 0x0033 +SPI_GETTOGGLEKEYS :: 0x0034 +SPI_SETTOGGLEKEYS :: 0x0035 +SPI_GETMOUSEKEYS :: 0x0036 +SPI_SETMOUSEKEYS :: 0x0037 +SPI_GETSHOWSOUNDS :: 0x0038 +SPI_SETSHOWSOUNDS :: 0x0039 +SPI_GETSTICKYKEYS :: 0x003A +SPI_SETSTICKYKEYS :: 0x003B +SPI_GETACCESSTIMEOUT :: 0x003C +SPI_SETACCESSTIMEOUT :: 0x003D +SPI_GETSERIALKEYS :: 0x003E +SPI_SETSERIALKEYS :: 0x003F +SPI_GETSOUNDSENTRY :: 0x0040 +SPI_SETSOUNDSENTRY :: 0x0041 +SPI_GETSNAPTODEFBUTTON :: 0x005F +SPI_SETSNAPTODEFBUTTON :: 0x0060 +SPI_GETMOUSEHOVERWIDTH :: 0x0062 +SPI_SETMOUSEHOVERWIDTH :: 0x0063 +SPI_GETMOUSEHOVERHEIGHT :: 0x0064 +SPI_SETMOUSEHOVERHEIGHT :: 0x0065 +SPI_GETMOUSEHOVERTIME :: 0x0066 +SPI_SETMOUSEHOVERTIME :: 0x0067 +SPI_GETWHEELSCROLLLINES :: 0x0068 +SPI_SETWHEELSCROLLLINES :: 0x0069 +SPI_GETMENUSHOWDELAY :: 0x006A +SPI_SETMENUSHOWDELAY :: 0x006B + +SPI_GETWHEELSCROLLCHARS :: 0x006C +SPI_SETWHEELSCROLLCHARS :: 0x006D +SPI_GETSHOWIMEUI :: 0x006E +SPI_SETSHOWIMEUI :: 0x006F +SPI_GETMOUSESPEED :: 0x0070 +SPI_SETMOUSESPEED :: 0x0071 +SPI_GETSCREENSAVERRUNNING :: 0x0072 +SPI_GETDESKWALLPAPER :: 0x0073 +SPI_GETAUDIODESCRIPTION :: 0x0074 +SPI_SETAUDIODESCRIPTION :: 0x0075 +SPI_GETSCREENSAVESECURE :: 0x0076 +SPI_SETSCREENSAVESECURE :: 0x0077 + +SPI_GETHUNGAPPTIMEOUT :: 0x0078 +SPI_SETHUNGAPPTIMEOUT :: 0x0079 +SPI_GETWAITTOKILLTIMEOUT :: 0x007A +SPI_SETWAITTOKILLTIMEOUT :: 0x007B +SPI_GETWAITTOKILLSERVICETIMEOUT :: 0x007C +SPI_SETWAITTOKILLSERVICETIMEOUT :: 0x007D +SPI_GETMOUSEDOCKTHRESHOLD :: 0x007E +SPI_SETMOUSEDOCKTHRESHOLD :: 0x007F +SPI_GETPENDOCKTHRESHOLD :: 0x0080 +SPI_SETPENDOCKTHRESHOLD :: 0x0081 +SPI_GETWINARRANGING :: 0x0082 +SPI_SETWINARRANGING :: 0x0083 +SPI_GETMOUSEDRAGOUTTHRESHOLD :: 0x0084 +SPI_SETMOUSEDRAGOUTTHRESHOLD :: 0x0085 +SPI_GETPENDRAGOUTTHRESHOLD :: 0x0086 +SPI_SETPENDRAGOUTTHRESHOLD :: 0x0087 +SPI_GETMOUSESIDEMOVETHRESHOLD :: 0x0088 +SPI_SETMOUSESIDEMOVETHRESHOLD :: 0x0089 +SPI_GETPENSIDEMOVETHRESHOLD :: 0x008A +SPI_SETPENSIDEMOVETHRESHOLD :: 0x008B +SPI_GETDRAGFROMMAXIMIZE :: 0x008C +SPI_SETDRAGFROMMAXIMIZE :: 0x008D +SPI_GETSNAPSIZING :: 0x008E +SPI_SETSNAPSIZING :: 0x008F +SPI_GETDOCKMOVING :: 0x0090 +SPI_SETDOCKMOVING :: 0x0091 + +SPI_GETACTIVEWINDOWTRACKING :: 0x1000 +SPI_SETACTIVEWINDOWTRACKING :: 0x1001 +SPI_GETMENUANIMATION :: 0x1002 +SPI_SETMENUANIMATION :: 0x1003 +SPI_GETCOMBOBOXANIMATION :: 0x1004 +SPI_SETCOMBOBOXANIMATION :: 0x1005 +SPI_GETLISTBOXSMOOTHSCROLLING :: 0x1006 +SPI_SETLISTBOXSMOOTHSCROLLING :: 0x1007 +SPI_GETGRADIENTCAPTIONS :: 0x1008 +SPI_SETGRADIENTCAPTIONS :: 0x1009 +SPI_GETKEYBOARDCUES :: 0x100A +SPI_SETKEYBOARDCUES :: 0x100B +SPI_GETMENUUNDERLINES :: SPI_GETKEYBOARDCUES +SPI_SETMENUUNDERLINES :: SPI_SETKEYBOARDCUES +SPI_GETACTIVEWNDTRKZORDER :: 0x100C +SPI_SETACTIVEWNDTRKZORDER :: 0x100D +SPI_GETHOTTRACKING :: 0x100E +SPI_SETHOTTRACKING :: 0x100F +SPI_GETMENUFADE :: 0x1012 +SPI_SETMENUFADE :: 0x1013 +SPI_GETSELECTIONFADE :: 0x1014 +SPI_SETSELECTIONFADE :: 0x1015 +SPI_GETTOOLTIPANIMATION :: 0x1016 +SPI_SETTOOLTIPANIMATION :: 0x1017 +SPI_GETTOOLTIPFADE :: 0x1018 +SPI_SETTOOLTIPFADE :: 0x1019 +SPI_GETCURSORSHADOW :: 0x101A +SPI_SETCURSORSHADOW :: 0x101B +SPI_GETMOUSESONAR :: 0x101C +SPI_SETMOUSESONAR :: 0x101D +SPI_GETMOUSECLICKLOCK :: 0x101E +SPI_SETMOUSECLICKLOCK :: 0x101F +SPI_GETMOUSEVANISH :: 0x1020 +SPI_SETMOUSEVANISH :: 0x1021 +SPI_GETFLATMENU :: 0x1022 +SPI_SETFLATMENU :: 0x1023 +SPI_GETDROPSHADOW :: 0x1024 +SPI_SETDROPSHADOW :: 0x1025 +SPI_GETBLOCKSENDINPUTRESETS :: 0x1026 +SPI_SETBLOCKSENDINPUTRESETS :: 0x1027 +SPI_GETUIEFFECTS :: 0x103E +SPI_SETUIEFFECTS :: 0x103F +SPI_GETDISABLEOVERLAPPEDCONTENT :: 0x1040 +SPI_SETDISABLEOVERLAPPEDCONTENT :: 0x1041 +SPI_GETCLIENTAREAANIMATION :: 0x1042 +SPI_SETCLIENTAREAANIMATION :: 0x1043 +SPI_GETCLEARTYPE :: 0x1048 +SPI_SETCLEARTYPE :: 0x1049 +SPI_GETSPEECHRECOGNITION :: 0x104A +SPI_SETSPEECHRECOGNITION :: 0x104B +SPI_GETCARETBROWSING :: 0x104C +SPI_SETCARETBROWSING :: 0x104D +SPI_GETTHREADLOCALINPUTSETTINGS :: 0x104E +SPI_SETTHREADLOCALINPUTSETTINGS :: 0x104F +SPI_GETSYSTEMLANGUAGEBAR :: 0x1050 +SPI_SETSYSTEMLANGUAGEBAR :: 0x1051 +SPI_GETFOREGROUNDLOCKTIMEOUT :: 0x2000 +SPI_SETFOREGROUNDLOCKTIMEOUT :: 0x2001 +SPI_GETACTIVEWNDTRKTIMEOUT :: 0x2002 +SPI_SETACTIVEWNDTRKTIMEOUT :: 0x2003 +SPI_GETFOREGROUNDFLASHCOUNT :: 0x2004 +SPI_SETFOREGROUNDFLASHCOUNT :: 0x2005 +SPI_GETCARETWIDTH :: 0x2006 +SPI_SETCARETWIDTH :: 0x2007 +SPI_GETMOUSECLICKLOCKTIME :: 0x2008 +SPI_SETMOUSECLICKLOCKTIME :: 0x2009 +SPI_GETFONTSMOOTHINGTYPE :: 0x200A +SPI_SETFONTSMOOTHINGTYPE :: 0x200B +// constants for SPI_GETFONTSMOOTHINGTYPE and SPI_SETFONTSMOOTHINGTYPE: +FE_FONTSMOOTHINGSTANDARD :: 0x0001 +FE_FONTSMOOTHINGCLEARTYPE :: 0x0002 + +SPI_GETFONTSMOOTHINGCONTRAST :: 0x200C +SPI_SETFONTSMOOTHINGCONTRAST :: 0x200D + +SPI_GETFOCUSBORDERWIDTH :: 0x200E +SPI_SETFOCUSBORDERWIDTH :: 0x200F +SPI_GETFOCUSBORDERHEIGHT :: 0x2010 +SPI_SETFOCUSBORDERHEIGHT :: 0x2011 + +SPI_GETFONTSMOOTHINGORIENTATION :: 0x2012 +SPI_SETFONTSMOOTHINGORIENTATION :: 0x2013 + +// constants for SPI_GETFONTSMOOTHINGORIENTATION and SPI_SETFONTSMOOTHINGORIENTATION: +FE_FONTSMOOTHINGORIENTATIONBGR :: 0x0000 +FE_FONTSMOOTHINGORIENTATIONRGB :: 0x0001 + +SPI_GETMINIMUMHITRADIUS :: 0x2014 +SPI_SETMINIMUMHITRADIUS :: 0x2015 +SPI_GETMESSAGEDURATION :: 0x2016 +SPI_SETMESSAGEDURATION :: 0x2017 + +SPI_GETCONTACTVISUALIZATION :: 0x2018 +SPI_SETCONTACTVISUALIZATION :: 0x2019 +// constants for SPI_GETCONTACTVISUALIZATION and SPI_SETCONTACTVISUALIZATION +CONTACTVISUALIZATION_OFF :: 0x0000 +CONTACTVISUALIZATION_ON :: 0x0001 +CONTACTVISUALIZATION_PRESENTATIONMODE :: 0x0002 + +SPI_GETGESTUREVISUALIZATION :: 0x201A +SPI_SETGESTUREVISUALIZATION :: 0x201B +// constants for SPI_GETGESTUREVISUALIZATION and SPI_SETGESTUREVISUALIZATION +GESTUREVISUALIZATION_OFF :: 0x0000 +GESTUREVISUALIZATION_ON :: 0x001F +GESTUREVISUALIZATION_TAP :: 0x0001 +GESTUREVISUALIZATION_DOUBLETAP :: 0x0002 +GESTUREVISUALIZATION_PRESSANDTAP :: 0x0004 +GESTUREVISUALIZATION_PRESSANDHOLD :: 0x0008 +GESTUREVISUALIZATION_RIGHTTAP :: 0x0010 + +SPI_GETMOUSEWHEELROUTING :: 0x201C +SPI_SETMOUSEWHEELROUTING :: 0x201D + +MOUSEWHEEL_ROUTING_FOCUS :: 0 +MOUSEWHEEL_ROUTING_HYBRID :: 1 +MOUSEWHEEL_ROUTING_MOUSE_POS :: 2 + +// Flags +SPIF_UPDATEINIFILE :: 0x0001 +SPIF_SENDWININICHANGE :: 0x0002 +SPIF_SENDCHANGE :: SPIF_SENDWININICHANGE diff --git a/core/sys/windows/types.odin b/core/sys/windows/types.odin index 3e25a4c18..edf6e593e 100644 --- a/core/sys/windows/types.odin +++ b/core/sys/windows/types.odin @@ -3,30 +3,45 @@ package sys_windows import "core:c" -c_char :: c.char -c_uchar :: c.uchar -c_int :: c.int -c_uint :: c.uint -c_long :: c.long -c_longlong :: c.longlong -c_ulong :: c.ulong -c_short :: c.short -c_ushort :: c.ushort -size_t :: c.size_t -wchar_t :: c.wchar_t +c_char :: c.char +c_uchar :: c.uchar +c_int :: c.int +c_uint :: c.uint +c_long :: c.long +c_longlong :: c.longlong +c_ulong :: c.ulong +c_ulonglong :: c.ulonglong +c_short :: c.short +c_ushort :: c.ushort +size_t :: c.size_t +wchar_t :: c.wchar_t DWORD :: c_ulong +QWORD :: c.ulonglong HANDLE :: distinct LPVOID HINSTANCE :: HANDLE HMODULE :: distinct HINSTANCE HRESULT :: distinct LONG HWND :: distinct HANDLE +HDC :: distinct HANDLE HMONITOR :: distinct HANDLE +HICON :: distinct HANDLE +HCURSOR :: distinct HANDLE +HMENU :: distinct HANDLE +HBRUSH :: distinct HANDLE +HGDIOBJ :: distinct HANDLE +HBITMAP :: distinct HANDLE +HGLOBAL :: distinct HANDLE +HHOOK :: distinct HANDLE +HKEY :: distinct HANDLE +HDESK :: distinct HANDLE +HFONT :: distinct HANDLE BOOL :: distinct b32 BYTE :: distinct u8 BOOLEAN :: distinct b8 GROUP :: distinct c_uint LARGE_INTEGER :: distinct c_longlong +ULARGE_INTEGER :: distinct c_ulonglong LONG :: c_long UINT :: c_uint INT :: c_int @@ -42,9 +57,20 @@ PULONG_PTR :: ^ULONG_PTR LPULONG_PTR :: ^ULONG_PTR DWORD_PTR :: ULONG_PTR LONG_PTR :: int +UINT_PTR :: uintptr ULONG :: c_ulong +ULONGLONG :: c_ulonglong UCHAR :: BYTE NTSTATUS :: c.long +COLORREF :: DWORD +LPCOLORREF :: ^COLORREF +LPARAM :: LONG_PTR +WPARAM :: UINT_PTR +LRESULT :: LONG_PTR +LPRECT :: ^RECT +LPPOINT :: ^POINT +LSTATUS :: LONG +PHKEY :: ^HKEY UINT8 :: u8 UINT16 :: u16 @@ -71,6 +97,7 @@ PBOOL :: ^BOOL LPBOOL :: ^BOOL LPCSTR :: cstring LPCWSTR :: wstring +LPCTSTR :: wstring LPDWORD :: ^DWORD PCSTR :: cstring PCWSTR :: wstring @@ -81,7 +108,9 @@ LPPROCESS_INFORMATION :: ^PROCESS_INFORMATION PSECURITY_ATTRIBUTES :: ^SECURITY_ATTRIBUTES LPSECURITY_ATTRIBUTES :: ^SECURITY_ATTRIBUTES LPSTARTUPINFO :: ^STARTUPINFO -PVOID :: rawptr +LPTRACKMOUSEEVENT :: ^TRACKMOUSEEVENT +VOID :: rawptr +PVOID :: rawptr LPVOID :: rawptr PINT :: ^INT LPINT :: ^INT @@ -95,6 +124,8 @@ LPWSADATA :: ^WSADATA LPWSAPROTOCOL_INFO :: ^WSAPROTOCOL_INFO LPSTR :: ^CHAR LPWSTR :: ^WCHAR +OLECHAR :: WCHAR +LPOLESTR :: ^OLECHAR LPFILETIME :: ^FILETIME LPWSABUF :: ^WSABUF LPWSAOVERLAPPED :: distinct rawptr @@ -105,6 +136,8 @@ PCONDITION_VARIABLE :: ^CONDITION_VARIABLE PLARGE_INTEGER :: ^LARGE_INTEGER PSRWLOCK :: ^SRWLOCK +MMRESULT :: UINT + SOCKET :: distinct uintptr // TODO socklen_t :: c_int ADDRESS_FAMILY :: USHORT @@ -142,23 +175,47 @@ FILE_GENERIC_ALL: DWORD : 0x10000000 FILE_GENERIC_EXECUTE: DWORD : 0x20000000 FILE_GENERIC_READ: DWORD : 0x80000000 +FILE_ACTION_ADDED :: 0x00000001 +FILE_ACTION_REMOVED :: 0x00000002 +FILE_ACTION_MODIFIED :: 0x00000003 +FILE_ACTION_RENAMED_OLD_NAME :: 0x00000004 +FILE_ACTION_RENAMED_NEW_NAME :: 0x00000005 + +FILE_NOTIFY_CHANGE_FILE_NAME :: 0x00000001 +FILE_NOTIFY_CHANGE_DIR_NAME :: 0x00000002 +FILE_NOTIFY_CHANGE_ATTRIBUTES :: 0x00000004 +FILE_NOTIFY_CHANGE_SIZE :: 0x00000008 +FILE_NOTIFY_CHANGE_LAST_WRITE :: 0x00000010 +FILE_NOTIFY_CHANGE_LAST_ACCESS :: 0x00000020 +FILE_NOTIFY_CHANGE_CREATION :: 0x00000040 +FILE_NOTIFY_CHANGE_SECURITY :: 0x00000100 + CREATE_NEW: DWORD : 1 CREATE_ALWAYS: DWORD : 2 OPEN_ALWAYS: DWORD : 4 OPEN_EXISTING: DWORD : 3 TRUNCATE_EXISTING: DWORD : 5 +FILE_READ_DATA : DWORD : 0x00000001 +FILE_LIST_DIRECTORY : DWORD : 0x00000001 +FILE_WRITE_DATA : DWORD : 0x00000002 +FILE_ADD_FILE : DWORD : 0x00000002 +FILE_APPEND_DATA : DWORD : 0x00000004 +FILE_ADD_SUBDIRECTORY : DWORD : 0x00000004 +FILE_CREATE_PIPE_INSTANCE : DWORD : 0x00000004 +FILE_READ_EA : DWORD : 0x00000008 +FILE_WRITE_EA : DWORD : 0x00000010 +FILE_EXECUTE : DWORD : 0x00000020 +FILE_TRAVERSE : DWORD : 0x00000020 +FILE_DELETE_CHILD : DWORD : 0x00000040 +FILE_READ_ATTRIBUTES : DWORD : 0x00000080 +FILE_WRITE_ATTRIBUTES : DWORD : 0x00000100 +GENERIC_READ : DWORD : 0x80000000 +GENERIC_WRITE : DWORD : 0x40000000 +GENERIC_EXECUTE : DWORD : 0x20000000 +GENERIC_ALL : DWORD : 0x10000000 -FILE_WRITE_DATA: DWORD : 0x00000002 -FILE_APPEND_DATA: DWORD : 0x00000004 -FILE_WRITE_EA: DWORD : 0x00000010 -FILE_WRITE_ATTRIBUTES: DWORD : 0x00000100 -READ_CONTROL: DWORD : 0x00020000 -SYNCHRONIZE: DWORD : 0x00100000 -GENERIC_READ: DWORD : 0x80000000 -GENERIC_WRITE: DWORD : 0x40000000 -STANDARD_RIGHTS_WRITE: DWORD : READ_CONTROL FILE_GENERIC_WRITE: DWORD : STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | @@ -177,6 +234,555 @@ GET_FILEEX_INFO_LEVELS :: distinct i32 GetFileExInfoStandard: GET_FILEEX_INFO_LEVELS : 0 GetFileExMaxInfoLevel: GET_FILEEX_INFO_LEVELS : 1 +// String resource number bases (internal use) + +MMSYSERR_BASE :: 0 +WAVERR_BASE :: 32 +MIDIERR_BASE :: 64 +TIMERR_BASE :: 96 +JOYERR_BASE :: 160 +MCIERR_BASE :: 256 +MIXERR_BASE :: 1024 + +MCI_STRING_OFFSET :: 512 +MCI_VD_OFFSET :: 1024 +MCI_CD_OFFSET :: 1088 +MCI_WAVE_OFFSET :: 1152 +MCI_SEQ_OFFSET :: 1216 + +// timer error return values +TIMERR_NOERROR :: 0 // no error +TIMERR_NOCANDO :: TIMERR_BASE + 1 // request not completed +TIMERR_STRUCT :: TIMERR_BASE + 33 // time struct size + +DIAGNOSTIC_REASON_VERSION :: 0 + +DIAGNOSTIC_REASON_SIMPLE_STRING :: 0x00000001 +DIAGNOSTIC_REASON_DETAILED_STRING :: 0x00000002 +DIAGNOSTIC_REASON_NOT_SPECIFIED :: 0x80000000 + +ENUM_CURRENT_SETTINGS : DWORD : 4294967295 // (DWORD)-1 +ENUM_REGISTRY_SETTINGS : DWORD : 4294967294 // (DWORD)-2 + +// Defines for power request APIs + +POWER_REQUEST_CONTEXT_VERSION :: DIAGNOSTIC_REASON_VERSION + +POWER_REQUEST_CONTEXT_SIMPLE_STRING :: DIAGNOSTIC_REASON_SIMPLE_STRING +POWER_REQUEST_CONTEXT_DETAILED_STRING :: DIAGNOSTIC_REASON_DETAILED_STRING + +REASON_CONTEXT :: struct { + Version: ULONG, + Flags: DWORD, + Reason: struct #raw_union { + Detailed: struct { + LocalizedReasonModule: HMODULE, + LocalizedReasonId: ULONG, + ReasonStringCount: ULONG, + ReasonStrings: ^LPWSTR, + }, + SimpleReasonString: LPWSTR, + }, +} +PREASON_CONTEXT :: ^REASON_CONTEXT + +// RRF - Registry Routine Flags (for RegGetValue) +RRF_RT_REG_NONE :: 0x00000001 +RRF_RT_REG_SZ :: 0x00000002 +RRF_RT_REG_EXPAND_SZ :: 0x00000004 +RRF_RT_REG_BINARY :: 0x00000008 +RRF_RT_REG_DWORD :: 0x00000010 +RRF_RT_REG_MULTI_SZ :: 0x00000020 +RRF_RT_REG_QWORD :: 0x00000040 +RRF_RT_DWORD :: (RRF_RT_REG_BINARY | RRF_RT_REG_DWORD) +RRF_RT_QWORD :: (RRF_RT_REG_BINARY | RRF_RT_REG_QWORD) +RRF_RT_ANY :: 0x0000ffff +RRF_NOEXPAND :: 0x10000000 +RRF_ZEROONFAILURE :: 0x20000000 + +ACCESS_MASK :: DWORD +PACCESS_MASK :: ^ACCESS_MASK +REGSAM :: ACCESS_MASK + +// Reserved Key Handles. +HKEY_CLASSES_ROOT :: HKEY(uintptr(0x80000000)) +HKEY_CURRENT_USER :: HKEY(uintptr(0x80000001)) +HKEY_LOCAL_MACHINE :: HKEY(uintptr(0x80000002)) +HKEY_USERS :: HKEY(uintptr(0x80000003)) +HKEY_PERFORMANCE_DATA :: HKEY(uintptr(0x80000004)) +HKEY_PERFORMANCE_TEXT :: HKEY(uintptr(0x80000050)) +HKEY_PERFORMANCE_NLSTEXT :: HKEY(uintptr(0x80000060)) +HKEY_CURRENT_CONFIG :: HKEY(uintptr(0x80000005)) +HKEY_DYN_DATA :: HKEY(uintptr(0x80000006)) +HKEY_CURRENT_USER_LOCAL_SETTINGS :: HKEY(uintptr(0x80000007)) + +// The following are masks for the predefined standard access types +DELETE : DWORD : 0x00010000 +READ_CONTROL : DWORD : 0x00020000 +WRITE_DAC : DWORD : 0x00040000 +WRITE_OWNER : DWORD : 0x00080000 +SYNCHRONIZE : DWORD : 0x00100000 + +STANDARD_RIGHTS_REQUIRED : DWORD : 0x000F0000 +STANDARD_RIGHTS_READ : DWORD : READ_CONTROL +STANDARD_RIGHTS_WRITE : DWORD : READ_CONTROL +STANDARD_RIGHTS_EXECUTE : DWORD : READ_CONTROL +STANDARD_RIGHTS_ALL : DWORD : 0x001F0000 +SPECIFIC_RIGHTS_ALL : DWORD : 0x0000FFFF + +// Registry Specific Access Rights. +KEY_QUERY_VALUE :: 0x0001 +KEY_SET_VALUE :: 0x0002 +KEY_CREATE_SUB_KEY :: 0x0004 +KEY_ENUMERATE_SUB_KEYS :: 0x0008 +KEY_NOTIFY :: 0x0010 +KEY_CREATE_LINK :: 0x0020 +KEY_WOW64_32KEY :: 0x0200 +KEY_WOW64_64KEY :: 0x0100 +KEY_WOW64_RES :: 0x0300 + +KEY_READ :: (STANDARD_RIGHTS_READ | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY) & (~SYNCHRONIZE) +KEY_WRITE :: (STANDARD_RIGHTS_WRITE | KEY_SET_VALUE | KEY_CREATE_SUB_KEY) & (~SYNCHRONIZE) +KEY_EXECUTE :: (KEY_READ) & (~SYNCHRONIZE) +KEY_ALL_ACCESS :: (STANDARD_RIGHTS_ALL | + KEY_QUERY_VALUE | + KEY_SET_VALUE | + KEY_CREATE_SUB_KEY | + KEY_ENUMERATE_SUB_KEYS | + KEY_NOTIFY | + KEY_CREATE_LINK) & (~SYNCHRONIZE) + +// Open/Create Options +REG_OPTION_RESERVED :: 0x00000000 +REG_OPTION_NON_VOLATILE :: 0x00000000 +REG_OPTION_VOLATILE :: 0x00000001 +REG_OPTION_CREATE_LINK :: 0x00000002 +REG_OPTION_BACKUP_RESTORE :: 0x00000004 +REG_OPTION_OPEN_LINK :: 0x00000008 +REG_OPTION_DONT_VIRTUALIZE :: 0x00000010 + +REG_LEGAL_OPTION :: REG_OPTION_RESERVED | + REG_OPTION_NON_VOLATILE | + REG_OPTION_VOLATILE | + REG_OPTION_CREATE_LINK | + REG_OPTION_BACKUP_RESTORE | + REG_OPTION_OPEN_LINK | + REG_OPTION_DONT_VIRTUALIZE + +REG_OPEN_LEGAL_OPTION :: REG_OPTION_RESERVED | + REG_OPTION_BACKUP_RESTORE | + REG_OPTION_OPEN_LINK | + REG_OPTION_DONT_VIRTUALIZE + +// Key creation/open disposition +REG_CREATED_NEW_KEY :: 0x00000001 +REG_OPENED_EXISTING_KEY :: 0x00000002 + +// hive format to be used by Reg(Nt)SaveKeyEx +REG_STANDARD_FORMAT :: 1 +REG_LATEST_FORMAT :: 2 +REG_NO_COMPRESSION :: 4 + +// Key restore & hive load flags +REG_WHOLE_HIVE_VOLATILE :: 0x00000001 +REG_REFRESH_HIVE :: 0x00000002 +REG_NO_LAZY_FLUSH :: 0x00000004 +REG_FORCE_RESTORE :: 0x00000008 +REG_APP_HIVE :: 0x00000010 +REG_PROCESS_PRIVATE :: 0x00000020 +REG_START_JOURNAL :: 0x00000040 +REG_HIVE_EXACT_FILE_GROWTH :: 0x00000080 +REG_HIVE_NO_RM :: 0x00000100 +REG_HIVE_SINGLE_LOG :: 0x00000200 +REG_BOOT_HIVE :: 0x00000400 +REG_LOAD_HIVE_OPEN_HANDLE :: 0x00000800 +REG_FLUSH_HIVE_FILE_GROWTH :: 0x00001000 +REG_OPEN_READ_ONLY :: 0x00002000 +REG_IMMUTABLE :: 0x00004000 +REG_NO_IMPERSONATION_FALLBACK :: 0x00008000 +REG_APP_HIVE_OPEN_READ_ONLY :: REG_OPEN_READ_ONLY + +// Unload Flags +REG_FORCE_UNLOAD :: 1 +REG_UNLOAD_LEGAL_FLAGS :: REG_FORCE_UNLOAD + +// Notify filter values +REG_NOTIFY_CHANGE_NAME :: 0x00000001 +REG_NOTIFY_CHANGE_ATTRIBUTES :: 0x00000002 +REG_NOTIFY_CHANGE_LAST_SET :: 0x00000004 +REG_NOTIFY_CHANGE_SECURITY :: 0x00000008 +REG_NOTIFY_THREAD_AGNOSTIC :: 0x10000000 + +REG_LEGAL_CHANGE_FILTER :: REG_NOTIFY_CHANGE_NAME | + REG_NOTIFY_CHANGE_ATTRIBUTES | + REG_NOTIFY_CHANGE_LAST_SET | + REG_NOTIFY_CHANGE_SECURITY | + REG_NOTIFY_THREAD_AGNOSTIC + +// Predefined Value Types. +REG_NONE :: 0 +REG_SZ :: 1 +REG_EXPAND_SZ :: 2 +REG_BINARY :: 3 +REG_DWORD :: 4 +REG_DWORD_LITTLE_ENDIAN :: 4 +REG_DWORD_BIG_ENDIAN :: 5 +REG_LINK :: 6 +REG_MULTI_SZ :: 7 +REG_RESOURCE_LIST :: 8 +REG_FULL_RESOURCE_DESCRIPTOR :: 9 +REG_RESOURCE_REQUIREMENTS_LIST :: 10 +REG_QWORD :: 11 +REG_QWORD_LITTLE_ENDIAN :: 11 + +BSMINFO :: struct { + cbSize: UINT, + hdesk: HDESK, + hwnd: HWND, + luid: LUID, +} +PBSMINFO :: ^BSMINFO + +// Broadcast Special Message Recipient list +BSM_ALLCOMPONENTS :: 0x00000000 +BSM_VXDS :: 0x00000001 +BSM_NETDRIVER :: 0x00000002 +BSM_INSTALLABLEDRIVERS :: 0x00000004 +BSM_APPLICATIONS :: 0x00000008 +BSM_ALLDESKTOPS :: 0x00000010 + +// Broadcast Special Message Flags +BSF_QUERY :: 0x00000001 +BSF_IGNORECURRENTTASK :: 0x00000002 +BSF_FLUSHDISK :: 0x00000004 +BSF_NOHANG :: 0x00000008 +BSF_POSTMESSAGE :: 0x00000010 +BSF_FORCEIFHUNG :: 0x00000020 +BSF_NOTIMEOUTIFNOTHUNG :: 0x00000040 +BSF_ALLOWSFW :: 0x00000080 +BSF_SENDNOTIFYMESSAGE :: 0x00000100 +BSF_RETURNHDESK :: 0x00000200 +BSF_LUID :: 0x00000400 + +BROADCAST_QUERY_DENY :: 0x424D5144 + +// Special HWND value for use with PostMessage() and SendMessage() +HWND_BROADCAST :: HWND(uintptr(0xffff)) +HWND_MESSAGE :: HWND(~uintptr(0) - 2) // -3 + +// Color Types +CTLCOLOR_MSGBOX :: 0 +CTLCOLOR_EDIT :: 1 +CTLCOLOR_LISTBOX :: 2 +CTLCOLOR_BTN :: 3 +CTLCOLOR_DLG :: 4 +CTLCOLOR_SCROLLBAR :: 5 +CTLCOLOR_STATIC :: 6 +CTLCOLOR_MAX :: 7 + +COLOR_SCROLLBAR :: 0 +COLOR_BACKGROUND :: 1 +COLOR_ACTIVECAPTION :: 2 +COLOR_INACTIVECAPTION :: 3 +COLOR_MENU :: 4 +COLOR_WINDOW :: 5 +COLOR_WINDOWFRAME :: 6 +COLOR_MENUTEXT :: 7 +COLOR_WINDOWTEXT :: 8 +COLOR_CAPTIONTEXT :: 9 +COLOR_ACTIVEBORDER :: 10 +COLOR_INACTIVEBORDER :: 11 +COLOR_APPWORKSPACE :: 12 +COLOR_HIGHLIGHT :: 13 +COLOR_HIGHLIGHTTEXT :: 14 +COLOR_BTNFACE :: 15 +COLOR_BTNSHADOW :: 16 +COLOR_GRAYTEXT :: 17 +COLOR_BTNTEXT :: 18 +COLOR_INACTIVECAPTIONTEXT :: 19 +COLOR_BTNHIGHLIGHT :: 20 + +COLOR_3DDKSHADOW :: 21 +COLOR_3DLIGHT :: 22 +COLOR_INFOTEXT :: 23 +COLOR_INFOBK :: 24 +COLOR_HOTLIGHT :: 26 +COLOR_GRADIENTACTIVECAPTION :: 27 +COLOR_GRADIENTINACTIVECAPTION :: 28 +COLOR_MENUHILIGHT :: 29 +COLOR_MENUBAR :: 30 + +COLOR_DESKTOP :: COLOR_BACKGROUND +COLOR_3DFACE :: COLOR_BTNFACE +COLOR_3DSHADOW :: COLOR_BTNSHADOW +COLOR_3DHIGHLIGHT :: COLOR_BTNHIGHLIGHT +COLOR_3DHILIGHT :: COLOR_BTNHIGHLIGHT +COLOR_BTNHILIGHT :: COLOR_BTNHIGHLIGHT + +// Combo Box Notification Codes +CBN_ERRSPACE :: -1 +CBN_SELCHANGE :: 1 +CBN_DBLCLK :: 2 +CBN_SETFOCUS :: 3 +CBN_KILLFOCUS :: 4 +CBN_EDITCHANGE :: 5 +CBN_EDITUPDATE :: 6 +CBN_DROPDOWN :: 7 +CBN_CLOSEUP :: 8 +CBN_SELENDOK :: 9 +CBN_SELENDCANCEL :: 10 + +// Combo Box styles +CBS_SIMPLE :: 0x0001 +CBS_DROPDOWN :: 0x0002 +CBS_DROPDOWNLIST :: 0x0003 +CBS_OWNERDRAWFIXED :: 0x0010 +CBS_OWNERDRAWVARIABLE :: 0x0020 +CBS_AUTOHSCROLL :: 0x0040 +CBS_OEMCONVERT :: 0x0080 +CBS_SORT :: 0x0100 +CBS_HASSTRINGS :: 0x0200 +CBS_NOINTEGRALHEIGHT :: 0x0400 +CBS_DISABLENOSCROLL :: 0x0800 +CBS_UPPERCASE :: 0x2000 +CBS_LOWERCASE :: 0x4000 + +// User Button Notification Codes +BN_CLICKED :: 0 +BN_PAINT :: 1 +BN_HILITE :: 2 +BN_UNHILITE :: 3 +BN_DISABLE :: 4 +BN_DOUBLECLICKED :: 5 +BN_PUSHED :: BN_HILITE +BN_UNPUSHED :: BN_UNHILITE +BN_DBLCLK :: BN_DOUBLECLICKED +BN_SETFOCUS :: 6 +BN_KILLFOCUS :: 7 + +// Button Control Styles +BS_PUSHBUTTON :: 0x00000000 +BS_DEFPUSHBUTTON :: 0x00000001 +BS_CHECKBOX :: 0x00000002 +BS_AUTOCHECKBOX :: 0x00000003 +BS_RADIOBUTTON :: 0x00000004 +BS_3STATE :: 0x00000005 +BS_AUTO3STATE :: 0x00000006 +BS_GROUPBOX :: 0x00000007 +BS_USERBUTTON :: 0x00000008 +BS_AUTORADIOBUTTON :: 0x00000009 +BS_PUSHBOX :: 0x0000000A +BS_OWNERDRAW :: 0x0000000B +BS_TYPEMASK :: 0x0000000F +BS_LEFTTEXT :: 0x00000020 +BS_TEXT :: 0x00000000 +BS_ICON :: 0x00000040 +BS_BITMAP :: 0x00000080 +BS_LEFT :: 0x00000100 +BS_RIGHT :: 0x00000200 +BS_CENTER :: 0x00000300 +BS_TOP :: 0x00000400 +BS_BOTTOM :: 0x00000800 +BS_VCENTER :: 0x00000C00 +BS_PUSHLIKE :: 0x00001000 +BS_MULTILINE :: 0x00002000 +BS_NOTIFY :: 0x00004000 +BS_FLAT :: 0x00008000 +BS_RIGHTBUTTON :: BS_LEFTTEXT + +// Button Control Messages +BST_UNCHECKED :: 0x0000 +BST_CHECKED :: 0x0001 +BST_INDETERMINATE :: 0x0002 +BST_PUSHED :: 0x0004 +BST_FOCUS :: 0x0008 + +// Static Control Constants +SS_LEFT :: 0x00000000 +SS_CENTER :: 0x00000001 +SS_RIGHT :: 0x00000002 +SS_ICON :: 0x00000003 +SS_BLACKRECT :: 0x00000004 +SS_GRAYRECT :: 0x00000005 +SS_WHITERECT :: 0x00000006 +SS_BLACKFRAME :: 0x00000007 +SS_GRAYFRAME :: 0x00000008 +SS_WHITEFRAME :: 0x00000009 +SS_USERITEM :: 0x0000000A +SS_SIMPLE :: 0x0000000B +SS_LEFTNOWORDWRAP :: 0x0000000C +SS_OWNERDRAW :: 0x0000000D +SS_BITMAP :: 0x0000000E +SS_ENHMETAFILE :: 0x0000000F +SS_ETCHEDHORZ :: 0x00000010 +SS_ETCHEDVERT :: 0x00000011 +SS_ETCHEDFRAME :: 0x00000012 +SS_TYPEMASK :: 0x0000001F +SS_REALSIZECONTROL :: 0x00000040 +SS_NOPREFIX :: 0x00000080 +SS_NOTIFY :: 0x00000100 +SS_CENTERIMAGE :: 0x00000200 +SS_RIGHTJUST :: 0x00000400 +SS_REALSIZEIMAGE :: 0x00000800 +SS_SUNKEN :: 0x00001000 +SS_EDITCONTROL :: 0x00002000 +SS_ENDELLIPSIS :: 0x00004000 +SS_PATHELLIPSIS :: 0x00008000 +SS_WORDELLIPSIS :: 0x0000C000 +SS_ELLIPSISMASK :: 0x0000C000 + +// Edit Control Styles +ES_LEFT :: 0x0000 +ES_CENTER :: 0x0001 +ES_RIGHT :: 0x0002 +ES_MULTILINE :: 0x0004 +ES_UPPERCASE :: 0x0008 +ES_LOWERCASE :: 0x0010 +ES_PASSWORD :: 0x0020 +ES_AUTOVSCROLL :: 0x0040 +ES_AUTOHSCROLL :: 0x0080 +ES_NOHIDESEL :: 0x0100 +ES_OEMCONVERT :: 0x0400 +ES_READONLY :: 0x0800 +ES_WANTRETURN :: 0x1000 +ES_NUMBER :: 0x2000 + +// Edit Control Notification Codes +EN_SETFOCUS :: 0x0100 +EN_KILLFOCUS :: 0x0200 +EN_CHANGE :: 0x0300 +EN_UPDATE :: 0x0400 +EN_ERRSPACE :: 0x0500 +EN_MAXTEXT :: 0x0501 +EN_HSCROLL :: 0x0601 +EN_VSCROLL :: 0x0602 +EN_ALIGN_LTR_EC :: 0x0700 +EN_ALIGN_RTL_EC :: 0x0701 + +// Font Weights +FW_DONTCARE :: 0 +FW_THIN :: 100 +FW_EXTRALIGHT :: 200 +FW_LIGHT :: 300 +FW_NORMAL :: 400 +FW_MEDIUM :: 500 +FW_SEMIBOLD :: 600 +FW_BOLD :: 700 +FW_EXTRABOLD :: 800 +FW_HEAVY :: 900 + +FW_ULTRALIGHT :: FW_EXTRALIGHT +FW_REGULAR :: FW_NORMAL +FW_DEMIBOLD :: FW_SEMIBOLD +FW_ULTRABOLD :: FW_EXTRABOLD +FW_BLACK :: FW_HEAVY + +PTIMERAPCROUTINE :: #type proc "stdcall" (lpArgToCompletionRoutine: LPVOID, dwTimerLowValue, dwTimerHighValue: DWORD) + +TIMERPROC :: #type proc "stdcall" (HWND, UINT, UINT_PTR, DWORD) + +WNDPROC :: #type proc "stdcall" (HWND, UINT, WPARAM, LPARAM) -> LRESULT + +HOOKPROC :: #type proc "stdcall" (code: c_int, wParam: WPARAM, lParam: LPARAM) -> LRESULT + +CWPRETSTRUCT :: struct { + lResult: LRESULT, + lParam: LPARAM, + wParam: WPARAM, + message: UINT, + hwnd: HWND, +} + +KBDLLHOOKSTRUCT :: struct { + vkCode: DWORD, + scanCode: DWORD, + flags: DWORD, + time: DWORD, + dwExtraInfo: ULONG_PTR, +} + +WNDCLASSA :: struct { + style: UINT, + lpfnWndProc: WNDPROC, + cbClsExtra: c_int, + cbWndExtra: c_int, + hInstance: HINSTANCE, + hIcon: HICON, + hCursor: HCURSOR, + hbrBackground: HBRUSH, + lpszMenuName: LPCSTR, + lpszClassName: LPCSTR, +} + +WNDCLASSW :: struct { + style: UINT, + lpfnWndProc: WNDPROC, + cbClsExtra: c_int, + cbWndExtra: c_int, + hInstance: HINSTANCE, + hIcon: HICON, + hCursor: HCURSOR, + hbrBackground: HBRUSH, + lpszMenuName: LPCWSTR, + lpszClassName: LPCWSTR, +} + +WNDCLASSEXA :: struct { + cbSize: UINT, + style: UINT, + lpfnWndProc: WNDPROC, + cbClsExtra: c_int, + cbWndExtra: c_int, + hInstance: HINSTANCE, + hIcon: HICON, + hCursor: HCURSOR, + hbrBackground: HBRUSH, + lpszMenuName: LPCSTR, + lpszClassName: LPCSTR, + hIconSm: HICON, +} + +WNDCLASSEXW :: struct { + cbSize: UINT, + style: UINT, + lpfnWndProc: WNDPROC, + cbClsExtra: c_int, + cbWndExtra: c_int, + hInstance: HINSTANCE, + hIcon: HICON, + hCursor: HCURSOR, + hbrBackground: HBRUSH, + lpszMenuName: LPCWSTR, + lpszClassName: LPCWSTR, + hIconSm: HICON, +} + +MSG :: struct { + hwnd: HWND, + message: UINT, + wParam: WPARAM, + lParam: LPARAM, + time: DWORD, + pt: POINT, +} + +LPMSG :: ^MSG + +PAINTSTRUCT :: struct { + hdc: HDC, + fErase: BOOL, + rcPaint: RECT, + fRestore: BOOL, + fIncUpdate: BOOL, + rgbReserved: [32]BYTE, +} + +TRACKMOUSEEVENT :: struct { + cbSize: DWORD, + dwFlags: DWORD, + hwndTrack: HWND, + dwHoverTime: DWORD, +} WIN32_FIND_DATAW :: struct { dwFileAttributes: DWORD, @@ -191,6 +797,763 @@ WIN32_FIND_DATAW :: struct { cAlternateFileName: [14]wchar_t, } +CREATESTRUCTA :: struct { + lpCreateParams: LPVOID, + hInstance: HINSTANCE, + hMenu: HMENU, + hwndParent: HWND, + cy: c_int, + cx: c_int, + y: c_int, + x: c_int, + style: LONG, + lpszName: LPCSTR, + lpszClass: LPCSTR, + dwExStyle: DWORD, +} + +CREATESTRUCTW:: struct { + lpCreateParams: LPVOID, + hInstance: HINSTANCE, + hMenu: HMENU, + hwndParent: HWND, + cy: c_int, + cx: c_int, + y: c_int, + x: c_int, + style: LONG, + lpszName: LPCWSTR, + lpszClass: LPCWSTR, + dwExStyle: DWORD, +} + +DEVMODEW :: struct { + dmDeviceName: [32]wchar_t, + dmSpecVersion: WORD, + dmDriverVersion: WORD, + dmSize: WORD, + dmDriverExtra: WORD, + dmFields: DWORD, + using _: struct #raw_union { + // Printer only fields. + using _: struct { + dmOrientation: c_short, + dmPaperSize: c_short, + dmPaperLength: c_short, + dmPaperWidth: c_short, + dmScale: c_short, + dmCopies: c_short, + dmDefaultSource: c_short, + dmPrintQuality: c_short, + }, + // Display only fields. + using _: struct { + dmPosition: POINT, + dmDisplayOrientation: DWORD, + dmDisplayFixedOutput: DWORD, + }, + }, + dmColor: c_short, + dmDuplex: c_short, + dmYResolution: c_short, + dmTTOption: c_short, + dmCollate: c_short, + dmFormName: [32]wchar_t, + dmLogPixels: WORD, + dmBitsPerPel: DWORD, + dmPelsWidth: DWORD, + dmPelsHeight: DWORD, + using _: struct #raw_union { + dmDisplayFlags: DWORD, + dmNup: DWORD, + }, + dmDisplayFrequency: DWORD, + dmICMMethod: DWORD, + dmICMIntent: DWORD, + dmMediaType: DWORD, + dmDitherType: DWORD, + dmReserved1: DWORD, + dmReserved2: DWORD, + dmPanningWidth: DWORD, + dmPanningHeight: DWORD, +} + +// MessageBox() Flags +MB_OK :: 0x00000000 +MB_OKCANCEL :: 0x00000001 +MB_ABORTRETRYIGNORE :: 0x00000002 +MB_YESNOCANCEL :: 0x00000003 +MB_YESNO :: 0x00000004 +MB_RETRYCANCEL :: 0x00000005 +MB_CANCELTRYCONTINUE :: 0x00000006 + +MB_ICONHAND :: 0x00000010 +MB_ICONQUESTION :: 0x00000020 +MB_ICONEXCLAMATION :: 0x00000030 +MB_ICONASTERISK :: 0x00000040 +MB_USERICON :: 0x00000080 +MB_ICONWARNING :: MB_ICONEXCLAMATION +MB_ICONERROR :: MB_ICONHAND +MB_ICONINFORMATION :: MB_ICONASTERISK +MB_ICONSTOP :: MB_ICONHAND + +MB_DEFBUTTON1 :: 0x00000000 +MB_DEFBUTTON2 :: 0x00000100 +MB_DEFBUTTON3 :: 0x00000200 +MB_DEFBUTTON4 :: 0x00000300 + +MB_APPLMODAL :: 0x00000000 +MB_SYSTEMMODAL :: 0x00001000 +MB_TASKMODAL :: 0x00002000 +MB_HELP :: 0x00004000 // Help Button + +MB_NOFOCUS :: 0x00008000 +MB_SETFOREGROUND :: 0x00010000 +MB_DEFAULT_DESKTOP_ONLY :: 0x00020000 +MB_TOPMOST :: 0x00040000 +MB_RIGHT :: 0x00080000 +MB_RTLREADING :: 0x00100000 + +MB_SERVICE_NOTIFICATION :: 0x00200000 +MB_SERVICE_NOTIFICATION_NT3X :: 0x00040000 + +MB_TYPEMASK :: 0x0000000F +MB_ICONMASK :: 0x000000F0 +MB_DEFMASK :: 0x00000F00 +MB_MODEMASK :: 0x00003000 +MB_MISCMASK :: 0x0000C000 + +// Dialog Box Command IDs +IDOK :: 1 +IDCANCEL :: 2 +IDABORT :: 3 +IDRETRY :: 4 +IDIGNORE :: 5 +IDYES :: 6 +IDNO :: 7 +IDCLOSE :: 8 +IDHELP :: 9 +IDTRYAGAIN :: 10 +IDCONTINUE :: 11 +IDTIMEOUT :: 32000 + +CS_VREDRAW : UINT : 0x0001 +CS_HREDRAW : UINT : 0x0002 +CS_DBLCLKS : UINT : 0x0008 +CS_OWNDC : UINT : 0x0020 +CS_CLASSDC : UINT : 0x0040 +CS_PARENTDC : UINT : 0x0080 +CS_NOCLOSE : UINT : 0x0200 +CS_SAVEBITS : UINT : 0x0800 +CS_BYTEALIGNCLIENT : UINT : 0x1000 +CS_BYTEALIGNWINDOW : UINT : 0x2000 +CS_GLOBALCLASS : UINT : 0x4000 +CS_DROPSHADOW : UINT : 0x0002_0000 + +WS_BORDER : UINT : 0x0080_0000 +WS_CAPTION : UINT : 0x00C0_0000 +WS_CHILD : UINT : 0x4000_0000 +WS_CHILDWINDOW : UINT : WS_CHILD +WS_CLIPCHILDREN : UINT : 0x0200_0000 +WS_CLIPSIBLINGS : UINT : 0x0400_0000 +WS_DISABLED : UINT : 0x0800_0000 +WS_DLGFRAME : UINT : 0x0040_0000 +WS_GROUP : UINT : 0x0002_0000 +WS_HSCROLL : UINT : 0x0010_0000 +WS_ICONIC : UINT : 0x2000_0000 +WS_MAXIMIZE : UINT : 0x0100_0000 +WS_MAXIMIZEBOX : UINT : 0x0001_0000 +WS_MINIMIZE : UINT : 0x2000_0000 +WS_MINIMIZEBOX : UINT : 0x0002_0000 +WS_OVERLAPPED : UINT : 0x0000_0000 +WS_OVERLAPPEDWINDOW : UINT : WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX +WS_POPUP : UINT : 0x8000_0000 +WS_POPUPWINDOW : UINT : WS_POPUP | WS_BORDER | WS_SYSMENU +WS_SIZEBOX : UINT : 0x0004_0000 +WS_SYSMENU : UINT : 0x0008_0000 +WS_TABSTOP : UINT : 0x0001_0000 +WS_THICKFRAME : UINT : 0x0004_0000 +WS_TILED : UINT : 0x0000_0000 +WS_TILEDWINDOW : UINT : WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZE | WS_MAXIMIZE +WS_VISIBLE : UINT : 0x1000_0000 +WS_VSCROLL : UINT : 0x0020_0000 + +PBS_SMOOTH :: 0x01 +PBS_VERTICAL :: 0x04 + +QS_ALLEVENTS : UINT : QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_HOTKEY +QS_ALLINPUT : UINT : QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_HOTKEY | QS_SENDMESSAGE +QS_ALLPOSTMESSAGE : UINT : 0x0100 +QS_HOTKEY : UINT : 0x0080 +QS_INPUT : UINT : QS_MOUSE | QS_KEY | QS_RAWINPUT +QS_KEY : UINT : 0x0001 +QS_MOUSE : UINT : QS_MOUSEMOVE | QS_MOUSEBUTTON +QS_MOUSEBUTTON : UINT : 0x0004 +QS_MOUSEMOVE : UINT : 0x0002 +QS_PAINT : UINT : 0x0020 +QS_POSTMESSAGE : UINT : 0x0008 +QS_RAWINPUT : UINT : 0x0400 +QS_SENDMESSAGE : UINT : 0x0040 +QS_TIMER : UINT : 0x0010 + +PM_NOREMOVE : UINT : 0x0000 +PM_REMOVE : UINT : 0x0001 +PM_NOYIELD : UINT : 0x0002 + +PM_QS_INPUT : UINT : QS_INPUT << 16 +PM_QS_PAINT : UINT : QS_PAINT << 16 +PM_QS_POSTMESSAGE : UINT : (QS_POSTMESSAGE | QS_HOTKEY | QS_TIMER) << 16 +PM_QS_SENDMESSAGE : UINT : QS_SENDMESSAGE << 16 + +SW_HIDE : c_int : 0 +SW_SHOWNORMAL : c_int : SW_NORMAL +SW_NORMAL : c_int : 1 +SW_SHOWMINIMIZED : c_int : 2 +SW_SHOWMAXIMIZED : c_int : SW_MAXIMIZE +SW_MAXIMIZE : c_int : 3 +SW_SHOWNOACTIVATE : c_int : 4 +SW_SHOW : c_int : 5 +SW_MINIMIZE : c_int : 6 +SW_SHOWMINNOACTIVE : c_int : 7 +SW_SHOWNA : c_int : 8 +SW_RESTORE : c_int : 9 +SW_SHOWDEFAULT : c_int : 10 +SW_FORCEMINIMIZE : c_int : 11 + +// SetWindowPos Flags +SWP_NOSIZE :: 0x0001 +SWP_NOMOVE :: 0x0002 +SWP_NOZORDER :: 0x0004 +SWP_NOREDRAW :: 0x0008 +SWP_NOACTIVATE :: 0x0010 +SWP_FRAMECHANGED :: 0x0020 // The frame changed: send WM_NCCALCSIZE +SWP_SHOWWINDOW :: 0x0040 +SWP_HIDEWINDOW :: 0x0080 +SWP_NOCOPYBITS :: 0x0100 +SWP_NOOWNERZORDER :: 0x0200 // Don't do owner Z ordering +SWP_NOSENDCHANGING :: 0x0400 // Don't send WM_WINDOWPOSCHANGING + +SWP_DRAWFRAME :: SWP_FRAMECHANGED +SWP_NOREPOSITION :: SWP_NOOWNERZORDER + +SWP_DEFERERASE :: 0x2000 // same as SWP_DEFERDRAWING +SWP_ASYNCWINDOWPOS :: 0x4000 // same as SWP_CREATESPB + +HWND_TOP :: HWND( uintptr(0)) // 0 +HWND_BOTTOM :: HWND( uintptr(1)) // 1 +HWND_TOPMOST :: HWND(~uintptr(0)) // -1 +HWND_NOTOPMOST :: HWND(~uintptr(0) - 1) // -2 + +// Window field offsets for GetWindowLong() +GWL_STYLE :: -16 +GWL_EXSTYLE :: -20 +GWL_ID :: -12 + +when ODIN_ARCH == .i386 { + GWL_WNDPROC :: -4 + GWL_HINSTANCE :: -6 + GWL_HWNDPARENT :: -8 + GWL_USERDATA :: -21 +} + +GWLP_WNDPROC :: -4 +GWLP_HINSTANCE :: -6 +GWLP_HWNDPARENT :: -8 +GWLP_USERDATA :: -21 +GWLP_ID :: -12 + +// Class field offsets for GetClassLong() +GCL_CBWNDEXTRA :: -18 +GCL_CBCLSEXTRA :: -20 +GCL_STYLE :: -26 +GCW_ATOM :: -32 + +when ODIN_ARCH == .i386 { + GCL_MENUNAME :: -8 + GCL_HBRBACKGROUND :: -10 + GCL_HCURSOR :: -12 + GCL_HICON :: -14 + GCL_HMODULE :: -16 + GCL_WNDPROC :: -24 + GCL_HICONSM :: -34 +} + +GCLP_MENUNAME :: -8 +GCLP_HBRBACKGROUND :: -10 +GCLP_HCURSOR :: -12 +GCLP_HICON :: -14 +GCLP_HMODULE :: -16 +GCLP_WNDPROC :: -24 +GCLP_HICONSM :: -34 + +// GetSystemMetrics() codes +SM_CXSCREEN :: 0 +SM_CYSCREEN :: 1 +SM_CXVSCROLL :: 2 +SM_CYHSCROLL :: 3 +SM_CYCAPTION :: 4 +SM_CXBORDER :: 5 +SM_CYBORDER :: 6 +SM_CXDLGFRAME :: 7 +SM_CYDLGFRAME :: 8 +SM_CYVTHUMB :: 9 +SM_CXHTHUMB :: 10 +SM_CXICON :: 11 +SM_CYICON :: 12 +SM_CXCURSOR :: 13 +SM_CYCURSOR :: 14 +SM_CYMENU :: 15 +SM_CXFULLSCREEN :: 16 +SM_CYFULLSCREEN :: 17 +SM_CYKANJIWINDOW :: 18 +SM_MOUSEPRESENT :: 19 +SM_CYVSCROLL :: 20 +SM_CXHSCROLL :: 21 +SM_DEBUG :: 22 +SM_SWAPBUTTON :: 23 +SM_RESERVED1 :: 24 +SM_RESERVED2 :: 25 +SM_RESERVED3 :: 26 +SM_RESERVED4 :: 27 +SM_CXMIN :: 28 +SM_CYMIN :: 29 +SM_CXSIZE :: 30 +SM_CYSIZE :: 31 +SM_CXFRAME :: 32 +SM_CYFRAME :: 33 +SM_CXMINTRACK :: 34 +SM_CYMINTRACK :: 35 +SM_CXDOUBLECLK :: 36 +SM_CYDOUBLECLK :: 37 +SM_CXICONSPACING :: 38 +SM_CYICONSPACING :: 39 +SM_MENUDROPALIGNMENT :: 40 +SM_PENWINDOWS :: 41 +SM_DBCSENABLED :: 42 +SM_CMOUSEBUTTONS :: 43 + +SM_CXFIXEDFRAME :: SM_CXDLGFRAME // ;win40 name change +SM_CYFIXEDFRAME :: SM_CYDLGFRAME // ;win40 name change +SM_CXSIZEFRAME :: SM_CXFRAME // ;win40 name change +SM_CYSIZEFRAME :: SM_CYFRAME // ;win40 name change + +SM_SECURE :: 44 +SM_CXEDGE :: 45 +SM_CYEDGE :: 46 +SM_CXMINSPACING :: 47 +SM_CYMINSPACING :: 48 +SM_CXSMICON :: 49 +SM_CYSMICON :: 50 +SM_CYSMCAPTION :: 51 +SM_CXSMSIZE :: 52 +SM_CYSMSIZE :: 53 +SM_CXMENUSIZE :: 54 +SM_CYMENUSIZE :: 55 +SM_ARRANGE :: 56 +SM_CXMINIMIZED :: 57 +SM_CYMINIMIZED :: 58 +SM_CXMAXTRACK :: 59 +SM_CYMAXTRACK :: 60 +SM_CXMAXIMIZED :: 61 +SM_CYMAXIMIZED :: 62 +SM_NETWORK :: 63 +SM_CLEANBOOT :: 67 +SM_CXDRAG :: 68 +SM_CYDRAG :: 69 + +SM_SHOWSOUNDS :: 70 +SM_CXMENUCHECK :: 71 // Use instead of GetMenuCheckMarkDimensions()! +SM_CYMENUCHECK :: 72 +SM_SLOWMACHINE :: 73 +SM_MIDEASTENABLED :: 74 +SM_MOUSEWHEELPRESENT :: 75 +SM_XVIRTUALSCREEN :: 76 +SM_YVIRTUALSCREEN :: 77 +SM_CXVIRTUALSCREEN :: 78 +SM_CYVIRTUALSCREEN :: 79 +SM_CMONITORS :: 80 +SM_SAMEDISPLAYFORMAT :: 81 +SM_IMMENABLED :: 82 +SM_CXFOCUSBORDER :: 83 +SM_CYFOCUSBORDER :: 84 +SM_TABLETPC :: 86 +SM_MEDIACENTER :: 87 +SM_STARTER :: 88 +SM_SERVERR2 :: 89 + +SM_MOUSEHORIZONTALWHEELPRESENT :: 91 + +SM_CXPADDEDBORDER :: 92 +SM_DIGITIZER :: 94 +SM_MAXIMUMTOUCHES :: 95 +SM_CMETRICS :: 97 + +SM_REMOTESESSION :: 0x1000 +SM_SHUTTINGDOWN :: 0x2000 +SM_REMOTECONTROL :: 0x2001 +SM_CARETBLINKINGENABLED :: 0x2002 +SM_CONVERTIBLESLATEMODE :: 0x2003 +SM_SYSTEMDOCKED :: 0x2004 + +// System Menu Command Values +SC_SIZE :: 0xF000 +SC_MOVE :: 0xF010 +SC_MINIMIZE :: 0xF020 +SC_MAXIMIZE :: 0xF030 +SC_NEXTWINDOW :: 0xF040 +SC_PREVWINDOW :: 0xF050 +SC_CLOSE :: 0xF060 +SC_VSCROLL :: 0xF070 +SC_HSCROLL :: 0xF080 +SC_MOUSEMENU :: 0xF090 +SC_KEYMENU :: 0xF100 +SC_ARRANGE :: 0xF110 +SC_RESTORE :: 0xF120 +SC_TASKLIST :: 0xF130 +SC_SCREENSAVE :: 0xF140 +SC_HOTKEY :: 0xF150 +SC_DEFAULT :: 0xF160 +SC_MONITORPOWER :: 0xF170 +SC_CONTEXTHELP :: 0xF180 +SC_SEPARATOR :: 0xF00F +SCF_ISSECURE :: 0x00000001 +SC_ICON :: SC_MINIMIZE +SC_ZOOM :: SC_MAXIMIZE + +CW_USEDEFAULT : c_int : -2147483648 + +SIZE_RESTORED :: 0 +SIZE_MINIMIZED :: 1 +SIZE_MAXIMIZED :: 2 +SIZE_MAXSHOW :: 3 +SIZE_MAXHIDE :: 4 + +WMSZ_LEFT :: 1 +WMSZ_RIGHT :: 2 +WMSZ_TOP :: 3 +WMSZ_TOPLEFT :: 4 +WMSZ_TOPRIGHT :: 5 +WMSZ_BOTTOM :: 6 +WMSZ_BOTTOMLEFT :: 7 +WMSZ_BOTTOMRIGHT :: 8 + +// Key State Masks for Mouse Messages +MK_LBUTTON :: 0x0001 +MK_RBUTTON :: 0x0002 +MK_SHIFT :: 0x0004 +MK_CONTROL :: 0x0008 +MK_MBUTTON :: 0x0010 +MK_XBUTTON1 :: 0x0020 +MK_XBUTTON2 :: 0x0040 + +// Value for rolling one detent +WHEEL_DELTA :: 120 + +// Setting to scroll one page for SPI_GET/SETWHEELSCROLLLINES +WHEEL_PAGESCROLL :: max(UINT) + +// XButton values are WORD flags +XBUTTON1 :: 0x0001 +XBUTTON2 :: 0x0002 +// Were there to be an XBUTTON3, its value would be 0x0004 + +MAPVK_VK_TO_VSC :: 0 +MAPVK_VSC_TO_VK :: 1 +MAPVK_VK_TO_CHAR :: 2 +MAPVK_VSC_TO_VK_EX :: 3 +MAPVK_VK_TO_VSC_EX :: 4 + +TME_HOVER :: 0x00000001 +TME_LEAVE :: 0x00000002 +TME_NONCLIENT :: 0x00000010 +TME_QUERY :: 0x40000000 +TME_CANCEL :: 0x80000000 +HOVER_DEFAULT :: 0xFFFFFFFF + +USER_TIMER_MAXIMUM :: 0x7FFFFFFF +USER_TIMER_MINIMUM :: 0x0000000A + +// WM_ACTIVATE state values +WA_INACTIVE :: 0 +WA_ACTIVE :: 1 +WA_CLICKACTIVE :: 2 + +// SetWindowsHook() codes +WH_MIN :: -1 +WH_MSGFILTER :: -1 +WH_JOURNALRECORD :: 0 +WH_JOURNALPLAYBACK :: 1 +WH_KEYBOARD :: 2 +WH_GETMESSAGE :: 3 +WH_CALLWNDPROC :: 4 +WH_CBT :: 5 +WH_SYSMSGFILTER :: 6 +WH_MOUSE :: 7 +WH_HARDWARE :: 8 +WH_DEBUG :: 9 +WH_SHELL :: 10 +WH_FOREGROUNDIDLE :: 11 +WH_CALLWNDPROCRET :: 12 +WH_KEYBOARD_LL :: 13 +WH_MOUSE_LL :: 14 +WH_MAX :: 14 +WH_MINHOOK :: WH_MIN +WH_MAXHOOK :: WH_MAX + +// Hook Codes +HC_ACTION :: 0 +HC_GETNEXT :: 1 +HC_SKIP :: 2 +HC_NOREMOVE :: 3 +HC_NOREM :: HC_NOREMOVE +HC_SYSMODALON :: 4 +HC_SYSMODALOFF :: 5 + +// CBT Hook Codes +HCBT_MOVESIZE :: 0 +HCBT_MINMAX :: 1 +HCBT_QS :: 2 +HCBT_CREATEWND :: 3 +HCBT_DESTROYWND :: 4 +HCBT_ACTIVATE :: 5 +HCBT_CLICKSKIPPED :: 6 +HCBT_KEYSKIPPED :: 7 +HCBT_SYSCOMMAND :: 8 +HCBT_SETFOCUS :: 9 + +_IDC_APPSTARTING := rawptr(uintptr(32650)) +_IDC_ARROW := rawptr(uintptr(32512)) +_IDC_CROSS := rawptr(uintptr(32515)) +_IDC_HAND := rawptr(uintptr(32649)) +_IDC_HELP := rawptr(uintptr(32651)) +_IDC_IBEAM := rawptr(uintptr(32513)) +_IDC_ICON := rawptr(uintptr(32641)) +_IDC_NO := rawptr(uintptr(32648)) +_IDC_SIZE := rawptr(uintptr(32640)) +_IDC_SIZEALL := rawptr(uintptr(32646)) +_IDC_SIZENESW := rawptr(uintptr(32643)) +_IDC_SIZENS := rawptr(uintptr(32645)) +_IDC_SIZENWSE := rawptr(uintptr(32642)) +_IDC_SIZEWE := rawptr(uintptr(32644)) +_IDC_UPARROW := rawptr(uintptr(32516)) +_IDC_WAIT := rawptr(uintptr(32514)) + +IDC_APPSTARTING := cstring(_IDC_APPSTARTING) +IDC_ARROW := cstring(_IDC_ARROW) +IDC_CROSS := cstring(_IDC_CROSS) +IDC_HAND := cstring(_IDC_HAND) +IDC_HELP := cstring(_IDC_HELP) +IDC_IBEAM := cstring(_IDC_IBEAM) +IDC_ICON := cstring(_IDC_ICON) +IDC_NO := cstring(_IDC_NO) +IDC_SIZE := cstring(_IDC_SIZE) +IDC_SIZEALL := cstring(_IDC_SIZEALL) +IDC_SIZENESW := cstring(_IDC_SIZENESW) +IDC_SIZENS := cstring(_IDC_SIZENS) +IDC_SIZENWSE := cstring(_IDC_SIZENWSE) +IDC_SIZEWE := cstring(_IDC_SIZEWE) +IDC_UPARROW := cstring(_IDC_UPARROW) +IDC_WAIT := cstring(_IDC_WAIT) + + +_IDI_APPLICATION := rawptr(uintptr(32512)) +_IDI_HAND := rawptr(uintptr(32513)) +_IDI_QUESTION := rawptr(uintptr(32514)) +_IDI_EXCLAMATION := rawptr(uintptr(32515)) +_IDI_ASTERISK := rawptr(uintptr(32516)) +_IDI_WINLOGO := rawptr(uintptr(32517)) +_IDI_SHIELD := rawptr(uintptr(32518)) +IDI_APPLICATION := cstring(_IDI_APPLICATION) +IDI_HAND := cstring(_IDI_HAND) +IDI_QUESTION := cstring(_IDI_QUESTION) +IDI_EXCLAMATION := cstring(_IDI_EXCLAMATION) +IDI_ASTERISK := cstring(_IDI_ASTERISK) +IDI_WINLOGO := cstring(_IDI_WINLOGO) +IDI_SHIELD := cstring(_IDI_SHIELD) +IDI_WARNING := IDI_EXCLAMATION +IDI_ERROR := IDI_HAND +IDI_INFORMATION := IDI_ASTERISK + + +// DIB color table identifiers +DIB_RGB_COLORS :: 0 +DIB_PAL_COLORS :: 1 + +// constants for CreateDIBitmap +CBM_INIT :: 0x04 // initialize bitmap + +// Region Flags +ERROR :: 0 +NULLREGION :: 1 +SIMPLEREGION :: 2 +COMPLEXREGION :: 3 +RGN_ERROR :: ERROR + +// StretchBlt() Modes +BLACKONWHITE :: 1 +WHITEONBLACK :: 2 +COLORONCOLOR :: 3 +HALFTONE :: 4 +MAXSTRETCHBLTMODE :: 4 + +// Binary raster ops +R2_BLACK :: 1 // 0 +R2_NOTMERGEPEN :: 2 // DPon +R2_MASKNOTPEN :: 3 // DPna +R2_NOTCOPYPEN :: 4 // PN +R2_MASKPENNOT :: 5 // PDna +R2_NOT :: 6 // Dn +R2_XORPEN :: 7 // DPx +R2_NOTMASKPEN :: 8 // DPan +R2_MASKPEN :: 9 // DPa +R2_NOTXORPEN :: 10 // DPxn +R2_NOP :: 11 // D +R2_MERGENOTPEN :: 12 // DPno +R2_COPYPEN :: 13 // P +R2_MERGEPENNOT :: 14 // PDno +R2_MERGEPEN :: 15 // DPo +R2_WHITE :: 16 // 1 +R2_LAST :: 16 + +// Ternary raster operations +SRCCOPY : DWORD : 0x00CC0020 // dest = source +SRCPAINT : DWORD : 0x00EE0086 // dest = source OR dest +SRCAND : DWORD : 0x008800C6 // dest = source AND dest +SRCINVERT : DWORD : 0x00660046 // dest = source XOR dest +SRCERASE : DWORD : 0x00440328 // dest = source AND (NOT dest) +NOTSRCCOPY : DWORD : 0x00330008 // dest = (NOT source) +NOTSRCERASE : DWORD : 0x001100A6 // dest = (NOT src) AND (NOT dest) +MERGECOPY : DWORD : 0x00C000CA // dest = (source AND pattern +MERGEPAINT : DWORD : 0x00BB0226 // dest = (NOT source) OR dest +PATCOPY : DWORD : 0x00F00021 // dest = pattern +PATPAINT : DWORD : 0x00FB0A09 // dest = DPSnoo +PATINVERT : DWORD : 0x005A0049 // dest = pattern XOR dest +DSTINVERT : DWORD : 0x00550009 // dest = (NOT dest) +BLACKNESS : DWORD : 0x00000042 // dest = BLACK +WHITENESS : DWORD : 0x00FF0062 // dest = WHITE +NOMIRRORBITMAP : DWORD : 0x80000000 // Do not Mirror the bitmap in this call +CAPTUREBLT : DWORD : 0x40000000 // Include layered windows + +// Stock Logical Objects +WHITE_BRUSH :: 0 +LTGRAY_BRUSH :: 1 +GRAY_BRUSH :: 2 +DKGRAY_BRUSH :: 3 +BLACK_BRUSH :: 4 +NULL_BRUSH :: 5 +HOLLOW_BRUSH :: NULL_BRUSH +WHITE_PEN :: 6 +BLACK_PEN :: 7 +NULL_PEN :: 8 +OEM_FIXED_FONT :: 10 +ANSI_FIXED_FONT :: 11 +ANSI_VAR_FONT :: 12 +SYSTEM_FONT :: 13 +DEVICE_DEFAULT_FONT :: 14 +DEFAULT_PALETTE :: 15 +SYSTEM_FIXED_FONT :: 16 +DEFAULT_GUI_FONT :: 17 +DC_BRUSH :: 18 +DC_PEN :: 19 +STOCK_LAST :: 19 + +CLR_INVALID :: 0xFFFFFFFF + +RGBQUAD :: struct { + rgbBlue: BYTE, + rgbGreen: BYTE, + rgbRed: BYTE, + rgbReserved: BYTE, +} + +PIXELFORMATDESCRIPTOR :: struct { + nSize: WORD, + nVersion: WORD, + dwFlags: DWORD, + iPixelType: BYTE, + cColorBits: BYTE, + cRedBits: BYTE, + cRedShift: BYTE, + cGreenBits: BYTE, + cGreenShift: BYTE, + cBlueBits: BYTE, + cBlueShift: BYTE, + cAlphaBits: BYTE, + cAlphaShift: BYTE, + cAccumBits: BYTE, + cAccumRedBits: BYTE, + cAccumGreenBits: BYTE, + cAccumBlueBits: BYTE, + cAccumAlphaBits: BYTE, + cDepthBits: BYTE, + cStencilBits: BYTE, + cAuxBuffers: BYTE, + iLayerType: BYTE, + bReserved: BYTE, + dwLayerMask: DWORD, + dwVisibleMask: DWORD, + dwDamageMask: DWORD, +} + +BITMAPINFOHEADER :: struct { + biSize: DWORD, + biWidth: LONG, + biHeight: LONG, + biPlanes: WORD, + biBitCount: WORD, + biCompression: DWORD, + biSizeImage: DWORD, + biXPelsPerMeter: LONG, + biYPelsPerMeter: LONG, + biClrUsed: DWORD, + biClrImportant: DWORD, +} + +BITMAPINFO :: struct { + bmiHeader: BITMAPINFOHEADER, + bmiColors: [1]RGBQUAD, +} + +// pixel types +PFD_TYPE_RGBA :: 0 +PFD_TYPE_COLORINDEX :: 1 + +// layer types +PFD_MAIN_PLANE :: 0 +PFD_OVERLAY_PLANE :: 1 +PFD_UNDERLAY_PLANE :: -1 + +// PIXELFORMATDESCRIPTOR flags +PFD_DOUBLEBUFFER :: 0x00000001 +PFD_STEREO :: 0x00000002 +PFD_DRAW_TO_WINDOW :: 0x00000004 +PFD_DRAW_TO_BITMAP :: 0x00000008 +PFD_SUPPORT_GDI :: 0x00000010 +PFD_SUPPORT_OPENGL :: 0x00000020 +PFD_GENERIC_FORMAT :: 0x00000040 +PFD_NEED_PALETTE :: 0x00000080 +PFD_NEED_SYSTEM_PALETTE :: 0x00000100 +PFD_SWAP_EXCHANGE :: 0x00000200 +PFD_SWAP_COPY :: 0x00000400 +PFD_SWAP_LAYER_BUFFERS :: 0x00000800 +PFD_GENERIC_ACCELERATED :: 0x00001000 +PFD_SUPPORT_DIRECTDRAW :: 0x00002000 +PFD_DIRECT3D_ACCELERATED :: 0x00004000 +PFD_SUPPORT_COMPOSITION :: 0x00008000 + +// PIXELFORMATDESCRIPTOR flags for use in ChoosePixelFormat only +PFD_DEPTH_DONTCARE :: 0x20000000 +PFD_DOUBLEBUFFER_DONTCARE :: 0x40000000 +PFD_STEREO_DONTCARE :: 0x80000000 + +// constants for the biCompression field +BI_RGB :: 0 +BI_RLE8 :: 1 +BI_RLE4 :: 2 +BI_BITFIELDS :: 3 +BI_JPEG :: 4 +BI_PNG :: 5 + WSA_FLAG_OVERLAPPED: DWORD : 0x01 WSA_FLAG_NO_HANDLE_INHERIT: DWORD : 0x80 @@ -230,41 +1593,26 @@ STD_ERROR_HANDLE: DWORD : ~DWORD(0) -12 + 1 PROGRESS_CONTINUE: DWORD : 0 -ERROR_FILE_NOT_FOUND: DWORD : 2 -ERROR_PATH_NOT_FOUND: DWORD : 3 -ERROR_ACCESS_DENIED: DWORD : 5 -ERROR_NOT_ENOUGH_MEMORY: DWORD : 8 -ERROR_INVALID_HANDLE: DWORD : 6 -ERROR_NO_MORE_FILES: DWORD : 18 -ERROR_SHARING_VIOLATION: DWORD : 32 -ERROR_LOCK_VIOLATION: DWORD : 33 -ERROR_HANDLE_EOF: DWORD : 38 -ERROR_NOT_SUPPORTED: DWORD : 50 -ERROR_FILE_EXISTS: DWORD : 80 -ERROR_INVALID_PARAMETER: DWORD : 87 -ERROR_BROKEN_PIPE: DWORD : 109 -ERROR_CALL_NOT_IMPLEMENTED: DWORD : 120 -ERROR_INSUFFICIENT_BUFFER: DWORD : 122 -ERROR_INVALID_NAME: DWORD : 123 -ERROR_LOCK_FAILED: DWORD : 167 -ERROR_ALREADY_EXISTS: DWORD : 183 -ERROR_NO_DATA: DWORD : 232 -ERROR_ENVVAR_NOT_FOUND: DWORD : 203 -ERROR_OPERATION_ABORTED: DWORD : 995 -ERROR_IO_PENDING: DWORD : 997 -ERROR_TIMEOUT: DWORD : 0x5B4 -ERROR_NO_UNICODE_TRANSLATION: DWORD : 1113 - -E_NOTIMPL :: HRESULT(-0x7fff_bfff) // 0x8000_4001 - INVALID_HANDLE :: HANDLE(~uintptr(0)) INVALID_HANDLE_VALUE :: INVALID_HANDLE FACILITY_NT_BIT: DWORD : 0x1000_0000 -FORMAT_MESSAGE_FROM_SYSTEM: DWORD : 0x00001000 -FORMAT_MESSAGE_FROM_HMODULE: DWORD : 0x00000800 -FORMAT_MESSAGE_IGNORE_INSERTS: DWORD : 0x00000200 +FORMAT_MESSAGE_ALLOCATE_BUFFER :: 0x00000100 +FORMAT_MESSAGE_IGNORE_INSERTS :: 0x00000200 +FORMAT_MESSAGE_FROM_STRING :: 0x00000400 +FORMAT_MESSAGE_FROM_HMODULE :: 0x00000800 +FORMAT_MESSAGE_FROM_SYSTEM :: 0x00001000 +FORMAT_MESSAGE_ARGUMENT_ARRAY :: 0x00002000 +FORMAT_MESSAGE_MAX_WIDTH_MASK :: 0x000000FF + +LMEM_FIXED :: 0x0000 +LMEM_MOVEABLE :: 0x0002 +LMEM_ZEROINIT :: 0x0040 +LHND :: 0x0042 +LPTR :: 0x0040 +NONZEROLHND :: LMEM_MOVEABLE +NONZEROLPTR :: LMEM_FIXED TLS_OUT_OF_INDEXES: DWORD : 0xFFFFFFFF @@ -376,6 +1724,18 @@ FILE_TYPE_DISK :: 0x0001 FILE_TYPE_CHAR :: 0x0002 FILE_TYPE_PIPE :: 0x0003 +RECT :: struct {left, top, right, bottom: LONG} +POINT :: struct {x, y: LONG} + +WINDOWPOS :: struct { + hwnd: HWND, + hwndInsertAfter: HWND, + x: c_int, + y: c_int, + cx: c_int, + cy: c_int, + flags: UINT, +} when size_of(uintptr) == 4 { WSADATA :: struct { @@ -475,6 +1835,13 @@ FILE_END_OF_FILE_INFO :: struct { EndOfFile: LARGE_INTEGER, } +FILE_NOTIFY_INFORMATION :: struct { + next_entry_offset: DWORD, + action: DWORD, + file_name_length: DWORD, + file_name: [1]WCHAR, +} + REPARSE_DATA_BUFFER :: struct { ReparseTag: c_uint, ReparseDataLength: c_ushort, @@ -554,7 +1921,41 @@ PGUID :: ^GUID PCGUID :: ^GUID LPGUID :: ^GUID LPCGUID :: ^GUID +REFIID :: ^GUID +REFGUID :: GUID +IID :: GUID +CLSID :: GUID +REFCLSID :: ^CLSID + +CLSCTX_INPROC_SERVER :: 0x1 +CLSCTX_INPROC_HANDLER :: 0x2 +CLSCTX_LOCAL_SERVER :: 0x4 +CLSCTX_INPROC_SERVER16 :: 0x8 +CLSCTX_REMOTE_SERVER :: 0x10 +CLSCTX_INPROC_HANDLER16 :: 0x20 +CLSCTX_RESERVED1 :: 0x40 +CLSCTX_RESERVED2 :: 0x80 +CLSCTX_RESERVED3 :: 0x100 +CLSCTX_RESERVED4 :: 0x200 +CLSCTX_NO_CODE_DOWNLOAD :: 0x400 +CLSCTX_RESERVED5 :: 0x800 +CLSCTX_NO_CUSTOM_MARSHAL :: 0x1000 +CLSCTX_ENABLE_CODE_DOWNLOAD :: 0x2000 +CLSCTX_NO_FAILURE_LOG :: 0x4000 +CLSCTX_DISABLE_AAA :: 0x8000 +CLSCTX_ENABLE_AAA :: 0x10000 +CLSCTX_FROM_DEFAULT_CONTEXT :: 0x20000 +CLSCTX_ACTIVATE_X86_SERVER :: 0x40000 +CLSCTX_ACTIVATE_32_BIT_SERVER :: CLSCTX_ACTIVATE_X86_SERVER +CLSCTX_ACTIVATE_64_BIT_SERVER :: 0x80000 +CLSCTX_ENABLE_CLOAKING :: 0x100000 +CLSCTX_APPCONTAINER :: 0x400000 +CLSCTX_ACTIVATE_AAA_AS_IU :: 0x800000 +CLSCTX_RESERVED6 :: 0x1000000 +CLSCTX_ACTIVATE_ARM32_SERVER :: 0x2000000 +CLSCTX_ALLOW_LOWER_TRUST_REGISTRATION :: 0x4000000 +CLSCTX_PS_DLL :: 0x80000000 WSAPROTOCOLCHAIN :: struct { ChainLen: c_int, @@ -575,7 +1976,7 @@ PROCESS_INFORMATION :: struct { } // FYI: This is STARTUPINFOW, not STARTUPINFOA -STARTUPINFO :: struct #packed { +STARTUPINFO :: struct { cb: DWORD, lpReserved: LPWSTR, lpDesktop: LPWSTR, @@ -619,6 +2020,12 @@ OVERLAPPED :: struct { hEvent: HANDLE, } +LPOVERLAPPED_COMPLETION_ROUTINE :: #type proc "stdcall" ( + dwErrorCode: DWORD, + dwNumberOfBytesTransfered: DWORD, + lpOverlapped: LPOVERLAPPED, +) + ADDRESS_MODE :: enum c_int { AddrMode1616, AddrMode1632, @@ -644,6 +2051,33 @@ ADDRINFOA :: struct { ai_next: ^ADDRINFOA, } +PADDRINFOEXW :: ^ADDRINFOEXW +LPADDRINFOEXW :: ^ADDRINFOEXW +ADDRINFOEXW :: struct { + ai_flags: c_int, + ai_family: c_int, + ai_socktype: c_int, + ai_protocol: c_int, + ai_addrlen: size_t, + ai_canonname: wstring, + ai_addr: ^sockaddr, + ai_blob: rawptr, + ai_bloblen: size_t, + ai_provider: LPGUID, + ai_next: ^ADDRINFOEXW, +} + +LPLOOKUPSERVICE_COMPLETION_ROUTINE :: #type proc "stdcall" ( + dwErrorCode: DWORD, + dwNumberOfBytesTransfered: DWORD, + lpOverlapped: LPOVERLAPPED, +) + +sockaddr :: struct { + sa_family: USHORT, + sa_data: [14]byte, +} + sockaddr_in :: struct { sin_family: ADDRESS_FAMILY, sin_port: USHORT, @@ -781,17 +2215,17 @@ SYSTEM_INFO :: struct { // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_osversioninfoexw OSVERSIONINFOEXW :: struct { - dwOSVersionInfoSize: ULONG, - dwMajorVersion: ULONG, - dwMinorVersion: ULONG, - dwBuildNumber: ULONG, - dwPlatformId: ULONG, - szCSDVersion: [128]WCHAR, - wServicePackMajor: USHORT, - wServicePackMinor: USHORT, - wSuiteMask: USHORT, - wProductType: UCHAR, - wReserved: UCHAR, + dwOSVersionInfoSize: ULONG, + dwMajorVersion: ULONG, + dwMinorVersion: ULONG, + dwBuildNumber: ULONG, + dwPlatformId: ULONG, + szCSDVersion: [128]WCHAR, + wServicePackMajor: USHORT, + wServicePackMinor: USHORT, + wSuiteMask: USHORT, + wProductType: UCHAR, + wReserved: UCHAR, } // https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-quota_limits @@ -834,24 +2268,24 @@ PROFILEINFOW :: struct { lpDefaultPath: LPWSTR, lpServerName: LPWSTR, lpPolicyPath: LPWSTR, - hProfile: HANDLE, + hProfile: HANDLE, } // Used in LookupAccountNameW SID_NAME_USE :: distinct DWORD SID_TYPE :: enum SID_NAME_USE { - User = 1, - Group, - Domain, - Alias, - WellKnownGroup, - DeletedAccount, - Invalid, - Unknown, - Computer, - Label, - LogonSession, + User = 1, + Group, + Domain, + Alias, + WellKnownGroup, + DeletedAccount, + Invalid, + Unknown, + Computer, + Label, + LogonSession, } SECURITY_MAX_SID_SIZE :: 68 @@ -866,7 +2300,7 @@ SID :: struct #packed { #assert(size_of(SID) == SECURITY_MAX_SID_SIZE) SID_IDENTIFIER_AUTHORITY :: struct #packed { - Value: [6]u8, + Value: [6]u8, } // For NetAPI32 @@ -898,11 +2332,11 @@ USER_INFO_FLAG :: enum DWORD { Passwd_Cant_Change = 6, // 1 << 6: 0x0040, Encrypted_Text_Password_Allowed = 7, // 1 << 7: 0x0080, - Temp_Duplicate_Account = 8, // 1 << 8: 0x0100, - Normal_Account = 9, // 1 << 9: 0x0200, - InterDomain_Trust_Account = 11, // 1 << 11: 0x0800, - Workstation_Trust_Account = 12, // 1 << 12: 0x1000, - Server_Trust_Account = 13, // 1 << 13: 0x2000, + Temp_Duplicate_Account = 8, // 1 << 8: 0x0100, + Normal_Account = 9, // 1 << 9: 0x0200, + InterDomain_Trust_Account = 11, // 1 << 11: 0x0800, + Workstation_Trust_Account = 12, // 1 << 12: 0x1000, + Server_Trust_Account = 13, // 1 << 13: 0x2000, } USER_INFO_FLAGS :: distinct bit_set[USER_INFO_FLAG] @@ -1249,4 +2683,585 @@ SYSTEMTIME :: struct { minute: WORD, second: WORD, milliseconds: WORD, -} \ No newline at end of file +} + + +@(private="file") +IMAGE_DOS_HEADER :: struct { + e_magic: WORD, + e_cblp: WORD, + e_cp: WORD, + e_crlc: WORD, + e_cparhdr: WORD, + e_minalloc: WORD, + e_maxalloc: WORD, + e_ss: WORD, + e_sp: WORD, + e_csum: WORD, + e_ip: WORD, + e_cs: WORD, + e_lfarlc: WORD, + e_ovno: WORD, + e_res_0: WORD, + e_res_1: WORD, + e_res_2: WORD, + e_res_3: WORD, + e_oemid: WORD, + e_oeminfo: WORD, + e_res2_0: WORD, + e_res2_1: WORD, + e_res2_2: WORD, + e_res2_3: WORD, + e_res2_4: WORD, + e_res2_5: WORD, + e_res2_6: WORD, + e_res2_7: WORD, + e_res2_8: WORD, + e_res2_9: WORD, + e_lfanew: DWORD, +} + +IMAGE_DATA_DIRECTORY :: struct { + VirtualAddress: DWORD, + Size: DWORD, +} + +IMAGE_FILE_HEADER :: struct { + Machine: WORD, + NumberOfSections: WORD, + TimeDateStamp: DWORD, + PointerToSymbolTable: DWORD, + NumberOfSymbols: DWORD, + SizeOfOptionalHeader: WORD, + Characteristics: WORD, +} + +IMAGE_OPTIONAL_HEADER64 :: struct { + Magic: WORD, + MajorLinkerVersion: BYTE, + MinorLinkerVersion: BYTE, + SizeOfCode: DWORD, + SizeOfInitializedData: DWORD, + SizeOfUninitializedData: DWORD, + AddressOfEntryPoint: DWORD, + BaseOfCode: DWORD, + ImageBase: QWORD, + SectionAlignment: DWORD, + FileAlignment: DWORD, + MajorOperatingSystemVersion: WORD, + MinorOperatingSystemVersion: WORD, + MajorImageVersion: WORD, + MinorImageVersion: WORD, + MajorSubsystemVersion: WORD, + MinorSubsystemVersion: WORD, + Win32VersionValue: DWORD, + SizeOfImage: DWORD, + SizeOfHeaders: DWORD, + CheckSum: DWORD, + Subsystem: WORD, + DllCharacteristics: WORD, + SizeOfStackReserve: QWORD, + SizeOfStackCommit: QWORD, + SizeOfHeapReserve: QWORD, + SizeOfHeapCommit: QWORD, + LoaderFlags: DWORD, + NumberOfRvaAndSizes: DWORD, + ExportTable: IMAGE_DATA_DIRECTORY, + ImportTable: IMAGE_DATA_DIRECTORY, + ResourceTable: IMAGE_DATA_DIRECTORY, + ExceptionTable: IMAGE_DATA_DIRECTORY, + CertificateTable: IMAGE_DATA_DIRECTORY, + BaseRelocationTable: IMAGE_DATA_DIRECTORY, + Debug: IMAGE_DATA_DIRECTORY, + Architecture: IMAGE_DATA_DIRECTORY, + GlobalPtr: IMAGE_DATA_DIRECTORY, + TLSTable: IMAGE_DATA_DIRECTORY, + LoadConfigTable: IMAGE_DATA_DIRECTORY, + BoundImport: IMAGE_DATA_DIRECTORY, + IAT: IMAGE_DATA_DIRECTORY, + DelayImportDescriptor: IMAGE_DATA_DIRECTORY, + CLRRuntimeHeader: IMAGE_DATA_DIRECTORY, + Reserved: IMAGE_DATA_DIRECTORY, +} + +IMAGE_NT_HEADERS64 :: struct { + Signature: DWORD, + FileHeader: IMAGE_FILE_HEADER, + OptionalHeader: IMAGE_OPTIONAL_HEADER64, +} + +IMAGE_EXPORT_DIRECTORY :: struct { + Characteristics: DWORD, + TimeDateStamp: DWORD, + MajorVersion: WORD, + MinorVersion: WORD, + Name: DWORD, + Base: DWORD, + NumberOfFunctions: DWORD, + NumberOfNames: DWORD, + AddressOfFunctions: DWORD, // RVA from base of image + AddressOfNames: DWORD, // RVA from base of image + AddressOfNameOrdinals: DWORD, // RVA from base of image +} + +SICHINTF :: DWORD +SHCONTF :: DWORD +SFGAOF :: ULONG +FILEOPENDIALOGOPTIONS :: DWORD +REFPROPERTYKEY :: ^PROPERTYKEY +REFPROPVARIANT :: ^PROPVARIANT + +SIGDN :: enum c_int { + NORMALDISPLAY = 0, + PARENTRELATIVEPARSING = -2147385343, // 0x80018001 + DESKTOPABSOLUTEPARSING = -2147319808, // 0x80028000 + PARENTRELATIVEEDITING = -2147282943, // 0x80031001 + DESKTOPABSOLUTEEDITING = -2147172352, // 0x8004c000 + FILESYSPATH = -2147123200, // 0x80058000 + URL = -2147057664, // 0x80068000 + PARENTRELATIVEFORADDRESSBAR = -2146975743, // 0x8007c001 + PARENTRELATIVE = -2146959359, // 0x80080001 + PARENTRELATIVEFORUI = -2146877439, // 0x80094001 +} + +SIATTRIBFLAGS :: enum c_int { + AND = 0x1, + OR = 0x2, + APPCOMPAT = 0x3, + MASK = 0x3, + ALLITEMS = 0x4000, +} + +FDAP :: enum c_int { + BOTTOM = 0, + TOP = 1, +} + +FDE_SHAREVIOLATION_RESPONSE :: enum c_int { + DEFAULT = 0, + ACCEPT = 1, + REFUSE = 2, +} + +GETPROPERTYSTOREFLAGS :: enum c_int { + DEFAULT = 0, + HANDLERPROPERTIESONLY = 0x1, + READWRITE = 0x2, + TEMPORARY = 0x4, + FASTPROPERTIESONLY = 0x8, + OPENSLOWITEM = 0x10, + DELAYCREATION = 0x20, + BESTEFFORT = 0x40, + NO_OPLOCK = 0x80, + PREFERQUERYPROPERTIES = 0x100, + EXTRINSICPROPERTIES = 0x200, + EXTRINSICPROPERTIESONLY = 0x400, + VOLATILEPROPERTIES = 0x800, + VOLATILEPROPERTIESONLY = 0x1000, + MASK_VALID = 0x1fff, +} + +PROPERTYKEY :: struct { + fmtid: GUID, + pid: DWORD, +} + +BIND_OPTS :: struct { + cbStruct: DWORD, + grfFlags: DWORD, + grfMode: DWORD, + dwTickCountDeadline: DWORD, +} + +STATSTG :: struct { + pwcsName: LPOLESTR, + type: DWORD, + cbSize: ULARGE_INTEGER, + mtime: FILETIME, + ctime: FILETIME, + atime: FILETIME, + grfMode: DWORD, + grfLocksSupported: DWORD, + clsid: CLSID, + grfStateBits: DWORD, + reserved: DWORD, +} + +COMDLG_FILTERSPEC :: struct { + pszName, pszSpec: LPCWSTR, +} + +DECIMAL :: struct { + wReserved: USHORT, + _: struct #raw_union { + _: struct { + scale, sign: BYTE, + }, + signscale: USHORT, + }, + Hi32: ULONG, + _: struct #raw_union { + _: struct { + Lo32, Mid32: ULONG, + }, + Lo64: ULONGLONG, + }, +} + +// NOTE(ftphikari): bigger definition of this struct is ignored +PROPVARIANT :: struct { + decVal: DECIMAL, +} + +SICHINT_DISPLAY :: 0 +SICHINT_ALLFIELDS :: -2147483648 // 0x80000000 +SICHINT_CANONICAL :: 0x10000000 +SICHINT_TEST_FILESYSPATH_IF_NOT_EQUAL :: 0x20000000 + +FOS_OVERWRITEPROMPT :: 0x2 +FOS_STRICTFILETYPES :: 0x4 +FOS_NOCHANGEDIR :: 0x8 +FOS_PICKFOLDERS :: 0x20 +FOS_FORCEFILESYSTEM :: 0x40 +FOS_ALLNONSTORAGEITEMS :: 0x80 +FOS_NOVALIDATE :: 0x100 +FOS_ALLOWMULTISELECT :: 0x200 +FOS_PATHMUSTEXIST :: 0x800 +FOS_FILEMUSTEXIST :: 0x1000 +FOS_CREATEPROMPT :: 0x2000 +FOS_SHAREAWARE :: 0x4000 +FOS_NOREADONLYRETURN :: 0x8000 +FOS_NOTESTFILECREATE :: 0x10000 +FOS_HIDEMRUPLACES :: 0x20000 +FOS_HIDEPINNEDPLACES :: 0x40000 +FOS_NODEREFERENCELINKS :: 0x100000 +FOS_OKBUTTONNEEDSINTERACTION :: 0x200000 +FOS_DONTADDTORECENT :: 0x2000000 +FOS_FORCESHOWHIDDEN :: 0x10000000 +FOS_DEFAULTNOMINIMODE :: 0x20000000 +FOS_FORCEPREVIEWPANEON :: 0x40000000 +FOS_SUPPORTSTREAMABLEITEMS :: 0x80000000 + +SHCONTF_CHECKING_FOR_CHILDREN :: 0x10 +SHCONTF_FOLDERS :: 0x20 +SHCONTF_NONFOLDERS :: 0x40 +SHCONTF_INCLUDEHIDDEN :: 0x80 +SHCONTF_INIT_ON_FIRST_NEXT :: 0x100 +SHCONTF_NETPRINTERSRCH :: 0x200 +SHCONTF_SHAREABLE :: 0x400 +SHCONTF_STORAGE :: 0x800 +SHCONTF_NAVIGATION_ENUM :: 0x1000 +SHCONTF_FASTITEMS :: 0x2000 +SHCONTF_FLATLIST :: 0x4000 +SHCONTF_ENABLE_ASYNC :: 0x8000 +SHCONTF_INCLUDESUPERHIDDEN :: 0x10000 + +CLSID_FileOpenDialog := &GUID{0xDC1C5A9C, 0xE88A, 0x4DDE, {0xA5, 0xA1, 0x60, 0xF8, 0x2A, 0x20, 0xAE, 0xF7}} +CLSID_FileSaveDialog := &GUID{0xC0B4E2F3, 0xBA21, 0x4773, {0x8D, 0xBA, 0x33, 0x5E, 0xC9, 0x46, 0xEB, 0x8B}} + +IID_IFileDialog := &GUID{0x42F85136, 0xDB7E, 0x439C, {0x85, 0xF1, 0xE4, 0x07, 0x5D, 0x13, 0x5F, 0xC8}} +IID_IFileSaveDialog := &GUID{0x84BCCD23, 0x5FDE, 0x4CDB, {0xAE, 0xA4, 0xAF, 0x64, 0xB8, 0x3D, 0x78, 0xAB}} +IID_IFileOpenDialog := &GUID{0xD57C7288, 0xD4AD, 0x4768, {0xBE, 0x02, 0x9D, 0x96, 0x95, 0x32, 0xD9, 0x60}} + +IModalWindow :: struct #raw_union { + #subtype IUnknown: IUnknown, + using Vtbl: ^IModalWindowVtbl, +} +IModalWindowVtbl :: struct { + using IUnknownVtbl: IUnknownVtbl, + Show: proc "stdcall" (this: ^IModalWindow, hwndOwner: HWND) -> HRESULT, +} + +ISequentialStream :: struct #raw_union { + #subtype IUnknown: IUnknown, + using Vtbl: ^ISequentialStreamVtbl, +} +ISequentialStreamVtbl :: struct { + using IUnknownVtbl: IUnknownVtbl, + Read: proc "stdcall" (this: ^ISequentialStream, pv: rawptr, cb: ULONG, pcbRead: ^ULONG) -> HRESULT, + Write: proc "stdcall" (this: ^ISequentialStream, pv: rawptr, cb: ULONG, pcbWritten: ^ULONG) -> HRESULT, +} + +IStream :: struct #raw_union { + #subtype ISequentialStream: ISequentialStream, + using Vtbl: ^IStreamVtbl, +} +IStreamVtbl :: struct { + using ISequentialStreamVtbl: ISequentialStreamVtbl, + Seek: proc "stdcall" (this: ^IStream, dlibMove: LARGE_INTEGER, dwOrigin: DWORD, plibNewPosition: ^ULARGE_INTEGER) -> HRESULT, + SetSize: proc "stdcall" (this: ^IStream, libNewSize: ULARGE_INTEGER) -> HRESULT, + CopyTo: proc "stdcall" (this: ^IStream, pstm: ^IStream, cb: ULARGE_INTEGER, pcbRead: ^ULARGE_INTEGER, pcbWritten: ^ULARGE_INTEGER) -> HRESULT, + Commit: proc "stdcall" (this: ^IStream, grfCommitFlags: DWORD) -> HRESULT, + Revert: proc "stdcall" (this: ^IStream) -> HRESULT, + LockRegion: proc "stdcall" (this: ^IStream, libOffset: ULARGE_INTEGER, cb: ULARGE_INTEGER, dwLockType: DWORD) -> HRESULT, + UnlockRegion: proc "stdcall" (this: ^IStream, libOffset: ULARGE_INTEGER, cb: ULARGE_INTEGER, dwLockType: DWORD) -> HRESULT, + Stat: proc "stdcall" (this: ^IStream, pstatstg: ^STATSTG, grfStatFlag: DWORD) -> HRESULT, + Clone: proc "stdcall" (this: ^IStream, ppstm: ^^IStream) -> HRESULT, +} + +IPersist :: struct #raw_union { + #subtype IUnknown: IUnknown, + using Vtbl: ^IPersistVtbl, +} +IPersistVtbl :: struct { + using IUnknownVtbl: IUnknownVtbl, + GetClassID: proc "stdcall" (this: ^IPersist, pClassID: ^CLSID) -> HRESULT, +} + +IPersistStream :: struct #raw_union { + #subtype IPersist: IPersist, + using Vtbl: ^IPersistStreamVtbl, +} +IPersistStreamVtbl :: struct { + using IPersistVtbl: IPersistVtbl, + IsDirty: proc "stdcall" (this: ^IPersistStream) -> HRESULT, + Load: proc "stdcall" (this: ^IPersistStream, pStm: ^IStream) -> HRESULT, + Save: proc "stdcall" (this: ^IPersistStream, pStm: ^IStream, fClearDirty: BOOL) -> HRESULT, + GetSizeMax: proc "stdcall" (this: ^IPersistStream, pcbSize: ^ULARGE_INTEGER) -> HRESULT, +} + +IMoniker :: struct #raw_union { + #subtype IPersistStream: IPersistStream, + using Vtbl: ^IMonikerVtbl, +} +IMonikerVtbl :: struct { + using IPersistStreamVtbl: IPersistStreamVtbl, + BindToObject: proc "stdcall" (this: ^IMoniker, pbc: ^IBindCtx, pmkToLeft: ^IMoniker, riidResult: REFIID, ppvResult: ^rawptr) -> HRESULT, + BindToStorage: proc "stdcall" (this: ^IMoniker, pbc: ^IBindCtx, pmkToLeft: ^IMoniker, riid: REFIID, ppvObj: ^rawptr) -> HRESULT, + Reduce: proc "stdcall" (this: ^IMoniker, pbc: ^IBindCtx, dwReduceHowFar: DWORD, ppmkToLeft: ^^IMoniker, ppmkReduced: ^^IMoniker) -> HRESULT, + ComposeWith: proc "stdcall" (this: ^IMoniker, pmkRight: ^IMoniker, fOnlyIfNotGeneric: BOOL, ppmkComposite: ^^IMoniker) -> HRESULT, + Enum: proc "stdcall" (this: ^IMoniker, fForward: BOOL, ppenumMoniker: ^^IEnumMoniker) -> HRESULT, + IsEqual: proc "stdcall" (this: ^IMoniker, pmkOtherMoniker: ^IMoniker) -> HRESULT, + Hash: proc "stdcall" (this: ^IMoniker, pdwHash: ^DWORD) -> HRESULT, + IsRunning: proc "stdcall" (this: ^IMoniker, pbc: ^IBindCtx, pmkToLeft: ^IMoniker, pmkNewlyRunning: ^IMoniker) -> HRESULT, + GetTimeOfLastChange: proc "stdcall" (this: ^IMoniker, pbc: ^IBindCtx, pmkToLeft: ^IMoniker, pFileTime: ^FILETIME) -> HRESULT, + Inverse: proc "stdcall" (this: ^IMoniker, ppmk: ^^IMoniker) -> HRESULT, + CommonPrefixWith: proc "stdcall" (this: ^IMoniker, pmkOther: ^IMoniker, ppmkPrefix: ^^IMoniker) -> HRESULT, + RelativePathTo: proc "stdcall" (this: ^IMoniker, pmkOther: ^IMoniker, ppmkRelPath: ^^IMoniker) -> HRESULT, + GetDisplayName: proc "stdcall" (this: ^IMoniker, pbc: ^IBindCtx, pmkToLeft: ^IMoniker, ppszDisplayName: ^LPOLESTR) -> HRESULT, + ParseDisplayName: proc "stdcall" (this: ^IMoniker, pbc: ^IBindCtx, pmkToLeft: ^IMoniker, pszDisplayName: LPOLESTR, pchEaten: ^ULONG, ppmkOut: ^^IMoniker) -> HRESULT, + IsSystemMoniker: proc "stdcall" (this: ^IMoniker, pdwMksys: ^DWORD) -> HRESULT, +} + +IEnumMoniker :: struct #raw_union { + #subtype IUnknown: IUnknown, + using Vtbl: ^IEnumMonikerVtbl, +} +IEnumMonikerVtbl :: struct { + using IUnknownVtbl: IUnknownVtbl, + Next: proc "stdcall" (this: ^IEnumMoniker, celt: ULONG, rgelt: ^^IMoniker, pceltFetched: ^ULONG) -> HRESULT, + Skip: proc "stdcall" (this: ^IEnumMoniker, celt: ULONG) -> HRESULT, + Reset: proc "stdcall" (this: ^IEnumMoniker) -> HRESULT, + Clone: proc "stdcall" (this: ^IEnumMoniker, ppenum: ^^IEnumMoniker) -> HRESULT, +} + +IRunningObjectTable :: struct #raw_union { + #subtype IUnknown: IUnknown, + using Vtbl: ^IRunningObjectTableVtbl, +} +IRunningObjectTableVtbl :: struct { + using IUnknownVtbl: IUnknownVtbl, + Register: proc "stdcall" (this: ^IRunningObjectTable, grfFlags: DWORD, punkObject: ^IUnknown, pmkObjectName: ^IMoniker, pdwRegister: ^DWORD) -> HRESULT, + Revoke: proc "stdcall" (this: ^IRunningObjectTable, dwRegister: DWORD) -> HRESULT, + IsRunning: proc "stdcall" (this: ^IRunningObjectTable, pmkObjectName: ^IMoniker) -> HRESULT, + GetObject: proc "stdcall" (this: ^IRunningObjectTable, pmkObjectName: ^IMoniker, ppunkObject: ^^IUnknown) -> HRESULT, + NoteChangeTime: proc "stdcall" (this: ^IRunningObjectTable, dwRegister: DWORD, pfiletime: ^FILETIME) -> HRESULT, + GetTimeOfLastChange: proc "stdcall" (this: ^IRunningObjectTable, pmkObjectName: ^IMoniker, pfiletime: ^FILETIME) -> HRESULT, + EnumRunning: proc "stdcall" (this: ^IRunningObjectTable, ppenumMoniker: ^^IEnumMoniker) -> HRESULT, +} + +IEnumString :: struct #raw_union { + #subtype IUnknown: IUnknown, + using Vtbl: ^IEnumStringVtbl, +} +IEnumStringVtbl :: struct { + using IUnknownVtbl: IUnknownVtbl, + Next: proc "stdcall" (this: ^IEnumString, celt: ULONG, rgelt: ^LPOLESTR, pceltFetched: ^ULONG) -> HRESULT, + Skip: proc "stdcall" (this: ^IEnumString, celt: ULONG) -> HRESULT, + Reset: proc "stdcall" (this: ^IEnumString) -> HRESULT, + Clone: proc "stdcall" (this: ^IEnumString, ppenum: ^^IEnumString) -> HRESULT, +} + +IBindCtx :: struct #raw_union { + #subtype IUnknown: IUnknown, + using Vtbl: ^IBindCtxVtbl, +} +IBindCtxVtbl :: struct { + using IUnknownVtbl: IUnknownVtbl, + RegisterObjectBound: proc "stdcall" (this: ^IBindCtx, punk: ^IUnknown) -> HRESULT, + RevokeObjectBound: proc "stdcall" (this: ^IBindCtx, punk: ^IUnknown) -> HRESULT, + ReleaseBoundObjects: proc "stdcall" (this: ^IBindCtx) -> HRESULT, + SetBindOptions: proc "stdcall" (this: ^IBindCtx, pbindopts: ^BIND_OPTS) -> HRESULT, + GetBindOptions: proc "stdcall" (this: ^IBindCtx, pbindopts: ^BIND_OPTS) -> HRESULT, + GetRunningObjectTable: proc "stdcall" (this: ^IBindCtx, pprot: ^^IRunningObjectTable) -> HRESULT, + RegisterObjectParam: proc "stdcall" (this: ^IBindCtx, pszKey: LPOLESTR, punk: ^IUnknown) -> HRESULT, + GetObjectParam: proc "stdcall" (this: ^IBindCtx, pszKey: LPOLESTR, ppunk: ^^IUnknown) -> HRESULT, + EnumObjectParam: proc "stdcall" (this: ^IBindCtx, ppenum: ^^IEnumString) -> HRESULT, + RevokeObjectParam: proc "stdcall" (this: ^IBindCtx, pszKey: LPOLESTR) -> HRESULT, +} + +IEnumShellItems :: struct #raw_union { + #subtype IUnknown: IUnknown, + using Vtbl: ^IEnumShellItemsVtbl, +} +IEnumShellItemsVtbl :: struct { + using IUnknownVtbl: IUnknownVtbl, + Next: proc "stdcall" (this: ^IEnumShellItems, celt: ULONG, rgelt: ^^IShellItem, pceltFetched: ^ULONG) -> HRESULT, + Skip: proc "stdcall" (this: ^IEnumShellItems, celt: ULONG) -> HRESULT, + Reset: proc "stdcall" (this: ^IEnumShellItems) -> HRESULT, + Clone: proc "stdcall" (this: ^IEnumShellItems, ppenum: ^^IEnumShellItems) -> HRESULT, +} + +IShellItem :: struct #raw_union { + #subtype IUnknown: IUnknown, + using Vtbl: ^IShellItemVtbl, +} +IShellItemVtbl :: struct { + using IUnknownVtbl: IUnknownVtbl, + BindToHandler: proc "stdcall" (this: ^IShellItem, pbc: ^IBindCtx, bhid: REFGUID, riid: REFIID, ppv: ^rawptr) -> HRESULT, + GetParent: proc "stdcall" (this: ^IShellItem, ppsiFolder: ^^IShellItem) -> HRESULT, + GetDisplayName: proc "stdcall" (this: ^IShellItem, sigdnName: SIGDN, ppszName: ^LPWSTR) -> HRESULT, + GetAttributes: proc "stdcall" (this: ^IShellItem, sfgaoMask: SFGAOF, psfgaoAttribs: ^SFGAOF) -> HRESULT, + Compare: proc "stdcall" (this: ^IShellItem, psi: ^IShellItem, hint: SICHINTF, piOrder: ^c_int) -> HRESULT, +} + +IShellItemArray :: struct #raw_union { + #subtype IUnknown: IUnknown, + using Vtbl: ^IShellItemArrayVtbl, +} +IShellItemArrayVtbl :: struct { + using IUnknownVtbl: IUnknownVtbl, + BindToHandler: proc "stdcall" (this: ^IShellItemArray, pbc: ^IBindCtx, bhid: REFGUID, riid: REFIID, ppvOut: ^rawptr) -> HRESULT, + GetPropertyStore: proc "stdcall" (this: ^IShellItemArray, flags: GETPROPERTYSTOREFLAGS, riid: REFIID, ppv: ^rawptr) -> HRESULT, + GetPropertyDescriptionList: proc "stdcall" (this: ^IShellItemArray, keyType: REFPROPERTYKEY, riid: REFIID, ppv: ^rawptr) -> HRESULT, + GetAttributes: proc "stdcall" (this: ^IShellItemArray, AttribFlags: SIATTRIBFLAGS, sfgaoMask: SFGAOF, psfgaoAttribs: ^SFGAOF) -> HRESULT, + GetCount: proc "stdcall" (this: ^IShellItemArray, pdwNumItems: ^DWORD) -> HRESULT, + GetItemAt: proc "stdcall" (this: ^IShellItemArray, dwIndex: DWORD, ppsi: ^^IShellItem) -> HRESULT, + EnumItems: proc "stdcall" (this: ^IShellItemArray, ppenumShellItems: ^^IEnumShellItems) -> HRESULT, +} + +IFileDialogEvents :: struct #raw_union { + #subtype IUnknown: IUnknown, + using Vtbl: ^IFileDialogEventsVtbl, +} +IFileDialogEventsVtbl :: struct { + using IUnknownVtbl: IUnknownVtbl, + OnFileOk: proc "stdcall" (this: ^IFileDialogEvents, pfd: ^IFileDialog) -> HRESULT, + OnFolderChanging: proc "stdcall" (this: ^IFileDialogEvents, pfd: ^IFileDialog, psiFolder: ^IShellItem) -> HRESULT, + OnFolderChange: proc "stdcall" (this: ^IFileDialogEvents, pfd: ^IFileDialog) -> HRESULT, + OnSelectionChange: proc "stdcall" (this: ^IFileDialogEvents, pfd: ^IFileDialog) -> HRESULT, + OnShareViolation: proc "stdcall" (this: ^IFileDialogEvents, pfd: ^IFileDialog, psi: ^IShellItem, pResponse: ^FDE_SHAREVIOLATION_RESPONSE) -> HRESULT, + OnTypeChange: proc "stdcall" (this: ^IFileDialogEvents, pfd: ^IFileDialog) -> HRESULT, + OnOverwrite: proc "stdcall" (this: ^IFileDialogEvents, pfd: ^IFileDialog, psi: ^IShellItem, pResponse: ^FDE_SHAREVIOLATION_RESPONSE) -> HRESULT, +} + +IShellItemFilter :: struct #raw_union { + #subtype IUnknown: IUnknown, + using Vtbl: ^IShellItemFilterVtbl, +} +IShellItemFilterVtbl :: struct { + using IUnknownVtbl: IUnknownVtbl, + IncludeItem: proc "stdcall" (this: ^IShellItemFilter, psi: ^IShellItem) -> HRESULT, + GetEnumFlagsForItem: proc "stdcall" (this: ^IShellItemFilter, psi: ^IShellItem, pgrfFlags: ^SHCONTF) -> HRESULT, +} + +IFileDialog :: struct #raw_union { + #subtype IModalWindow: IModalWindow, + using Vtbl: ^IFileDialogVtbl, +} +IFileDialogVtbl :: struct { + using IModalWindowVtbl: IModalWindowVtbl, + SetFileTypes: proc "stdcall" (this: ^IFileDialog, cFileTypes: UINT, rgFilterSpec: ^COMDLG_FILTERSPEC) -> HRESULT, + SetFileTypeIndex: proc "stdcall" (this: ^IFileDialog, iFileType: UINT) -> HRESULT, + GetFileTypeIndex: proc "stdcall" (this: ^IFileDialog, piFileType: ^UINT) -> HRESULT, + Advise: proc "stdcall" (this: ^IFileDialog, pfde: ^IFileDialogEvents, pdwCookie: ^DWORD) -> HRESULT, + Unadvise: proc "stdcall" (this: ^IFileDialog, dwCookie: DWORD) -> HRESULT, + SetOptions: proc "stdcall" (this: ^IFileDialog, fos: FILEOPENDIALOGOPTIONS) -> HRESULT, + GetOptions: proc "stdcall" (this: ^IFileDialog, pfos: ^FILEOPENDIALOGOPTIONS) -> HRESULT, + SetDefaultFolder: proc "stdcall" (this: ^IFileDialog, psi: ^IShellItem) -> HRESULT, + SetFolder: proc "stdcall" (this: ^IFileDialog, psi: ^IShellItem) -> HRESULT, + GetFolder: proc "stdcall" (this: ^IFileDialog, ppsi: ^^IShellItem) -> HRESULT, + GetCurrentSelection: proc "stdcall" (this: ^IFileDialog, ppsi: ^^IShellItem) -> HRESULT, + SetFileName: proc "stdcall" (this: ^IFileDialog, pszName: LPCWSTR) -> HRESULT, + GetFileName: proc "stdcall" (this: ^IFileDialog, pszName: ^LPCWSTR) -> HRESULT, + SetTitle: proc "stdcall" (this: ^IFileDialog, pszTitle: LPCWSTR) -> HRESULT, + SetOkButtonLabel: proc "stdcall" (this: ^IFileDialog, pszText: LPCWSTR) -> HRESULT, + SetFileNameLabel: proc "stdcall" (this: ^IFileDialog, pszLabel: LPCWSTR) -> HRESULT, + GetResult: proc "stdcall" (this: ^IFileDialog, ppsi: ^^IShellItem) -> HRESULT, + AddPlace: proc "stdcall" (this: ^IFileDialog, psi: ^IShellItem, fdap: FDAP) -> HRESULT, + SetDefaultExtension: proc "stdcall" (this: ^IFileDialog, pszDefaultExtension: LPCWSTR) -> HRESULT, + Close: proc "stdcall" (this: ^IFileDialog, hr: HRESULT) -> HRESULT, + SetClientGuid: proc "stdcall" (this: ^IFileDialog, guid: REFGUID) -> HRESULT, + ClearClientData: proc "stdcall" (this: ^IFileDialog) -> HRESULT, + SetFilter: proc "stdcall" (this: ^IFileDialog, pFilter: ^IShellItemFilter) -> HRESULT, +} + +IFileOpenDialog :: struct #raw_union { + #subtype IFileDialog: IFileDialog, + using Vtbl: ^IFileOpenDialogVtbl, +} +IFileOpenDialogVtbl :: struct { + using IFileDialogVtbl: IFileDialogVtbl, + GetResults: proc "stdcall" (this: ^IFileOpenDialog, ppenum: ^^IShellItemArray) -> HRESULT, + GetSelectedItems: proc "stdcall" (this: ^IFileOpenDialog, ppsai: ^^IShellItemArray) -> HRESULT, +} + +IPropertyStore :: struct #raw_union { + #subtype IUnknown: IUnknown, + using Vtbl: ^IPropertyStoreVtbl, +} +IPropertyStoreVtbl :: struct { + using IUnknownVtbl: IUnknownVtbl, + GetCount: proc "stdcall" (this: ^IPropertyStore, cProps: ^DWORD) -> HRESULT, + GetAt: proc "stdcall" (this: ^IPropertyStore, iProp: DWORD, pkey: ^PROPERTYKEY) -> HRESULT, + GetValue: proc "stdcall" (this: ^IPropertyStore, key: REFPROPERTYKEY, pv: ^PROPVARIANT) -> HRESULT, + SetValue: proc "stdcall" (this: ^IPropertyStore, key: REFPROPERTYKEY, propvar: REFPROPVARIANT) -> HRESULT, + Commit: proc "stdcall" (this: ^IPropertyStore) -> HRESULT, +} + +IPropertyDescriptionList :: struct #raw_union { + #subtype IUnknown: IUnknown, + using Vtbl: ^IPropertyDescriptionListVtbl, +} +IPropertyDescriptionListVtbl :: struct { + using IUnknownVtbl: IUnknownVtbl, + GetCount: proc "stdcall" (this: ^IPropertyDescriptionList, pcElem: ^UINT) -> HRESULT, + GetAt: proc "stdcall" (this: ^IPropertyDescriptionList, iElem: UINT, riid: REFIID, ppv: ^rawptr) -> HRESULT, +} + +IFileOperationProgressSink :: struct #raw_union { + #subtype IUnknown: IUnknown, + using Vtbl: ^IFileOperationProgressSinkVtbl, +} +IFileOperationProgressSinkVtbl :: struct { + using IUnknownVtbl: IUnknownVtbl, + StartOperations: proc "stdcall" (this: ^IFileOperationProgressSink) -> HRESULT, + FinishOperations: proc "stdcall" (this: ^IFileOperationProgressSink, hrResult: HRESULT) -> HRESULT, + PreRenameItem: proc "stdcall" (this: ^IFileOperationProgressSink, dwFlags: DWORD, psiItem: ^IShellItem, pszNewName: LPCWSTR) -> HRESULT, + PostRenameItem: proc "stdcall" (this: ^IFileOperationProgressSink, dwFlags: DWORD, psiItem: ^IShellItem, pszNewName: LPCWSTR, hrRename: HRESULT, psiNewlyCreated: ^IShellItem) -> HRESULT, + PreMoveItem: proc "stdcall" (this: ^IFileOperationProgressSink, dwFlags: DWORD, psiItem: ^IShellItem, psiDestinationFolder: ^IShellItem, pszNewName: LPCWSTR) -> HRESULT, + PostMoveItem: proc "stdcall" (this: ^IFileOperationProgressSink, dwFlags: DWORD, psiItem: ^IShellItem, psiDestinationFolder: ^IShellItem, pszNewName: LPCWSTR, hrMove: HRESULT, psiNewlyCreated: ^IShellItem) -> HRESULT, + PreCopyItem: proc "stdcall" (this: ^IFileOperationProgressSink, dwFlags: DWORD, psiItem: ^IShellItem, psiDestinationFolder: ^IShellItem, pszNewName: LPCWSTR) -> HRESULT, + PostCopyItem: proc "stdcall" (this: ^IFileOperationProgressSink, dwFlags: DWORD, psiItem: ^IShellItem, psiDestinationFolder: ^IShellItem, pszNewName: LPCWSTR, hrMove: HRESULT, psiNewlyCreated: ^IShellItem) -> HRESULT, + PreDeleteItem: proc "stdcall" (this: ^IFileOperationProgressSink, dwFlags: DWORD, psiItem: ^IShellItem) -> HRESULT, + PostDeleteItem: proc "stdcall" (this: ^IFileOperationProgressSink, dwFlags: DWORD, psiItem: ^IShellItem, hrDelete: HRESULT, psiNewlyCreated: ^IShellItem) -> HRESULT, + PreNewItem: proc "stdcall" (this: ^IFileOperationProgressSink, dwFlags: DWORD, psiDestinationFolder: ^IShellItem, pszNewName: LPCWSTR) -> HRESULT, + PostNewItem: proc "stdcall" (this: ^IFileOperationProgressSink, dwFlags: DWORD, psiDestinationFolder: ^IShellItem, pszNewName: LPCWSTR, pszTemplateName: LPCWSTR, dwFileAttributes: DWORD, hrNew: HRESULT, psiNewItem: ^IShellItem) -> HRESULT, + UpdateProgress: proc "stdcall" (this: ^IFileOperationProgressSink, iWorkTotal: UINT, iWorkSoFar: UINT) -> HRESULT, + ResetTimer: proc "stdcall" (this: ^IFileOperationProgressSink) -> HRESULT, + PauseTimer: proc "stdcall" (this: ^IFileOperationProgressSink) -> HRESULT, + ResumeTimer: proc "stdcall" (this: ^IFileOperationProgressSink) -> HRESULT, +} + +IFileSaveDialog :: struct #raw_union { + #subtype IFileDialog: IFileDialog, + using Vtbl: ^IFileSaveDialogVtbl, +} +IFileSaveDialogVtbl :: struct { + using IFileDialogVtbl: IFileDialogVtbl, + SetSaveAsItem: proc "stdcall" (this: ^IFileSaveDialog, psi: ^IShellItem) -> HRESULT, + SetProperties: proc "stdcall" (this: ^IFileSaveDialog, pStore: ^IPropertyStore) -> HRESULT, + SetCollectedProperties: proc "stdcall" (this: ^IFileSaveDialog, pList: ^IPropertyDescriptionList, fAppendDefault: BOOL) -> HRESULT, + GetProperties: proc "stdcall" (this: ^IFileSaveDialog, ppStore: ^^IPropertyStore) -> HRESULT, + ApplyProperties: proc "stdcall" (this: ^IFileSaveDialog, psi: ^IShellItem, pStore: ^IPropertyStore, hwnd: HWND, pSink: ^IFileOperationProgressSink) -> HRESULT, +} diff --git a/core/sys/windows/user32.odin b/core/sys/windows/user32.odin new file mode 100644 index 000000000..47de354b6 --- /dev/null +++ b/core/sys/windows/user32.odin @@ -0,0 +1,248 @@ +// +build windows +package sys_windows + +foreign import user32 "system:User32.lib" + +@(default_calling_convention="stdcall") +foreign user32 { + GetClassInfoW :: proc(hInstance: HINSTANCE, lpClassNAme: LPCWSTR, lpWndClass: ^WNDCLASSW) -> BOOL --- + GetClassInfoExW :: proc(hInsatnce: HINSTANCE, lpszClass: LPCWSTR, lpwcx: ^WNDCLASSEXW) -> BOOL --- + + GetClassLongW :: proc(hWnd: HWND, nIndex: c_int) -> DWORD --- + SetClassLongW :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG) -> DWORD --- + + GetWindowLongW :: proc(hWnd: HWND, nIndex: c_int) -> LONG --- + SetWindowLongW :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG) -> LONG --- + + GetClassNameW :: proc(hWnd: HWND, lpClassName: LPWSTR, nMaxCount: c_int) -> c_int --- + + RegisterClassW :: proc(lpWndClass: ^WNDCLASSW) -> ATOM --- + RegisterClassExW :: proc(^WNDCLASSEXW) -> ATOM --- + + CreateWindowExW :: proc( + dwExStyle: DWORD, + lpClassName: LPCWSTR, + lpWindowName: LPCWSTR, + dwStyle: DWORD, + X: c_int, + Y: c_int, + nWidth: c_int, + nHeight: c_int, + hWndParent: HWND, + hMenu: HMENU, + hInstance: HINSTANCE, + lpParam: LPVOID, + ) -> HWND --- + + DestroyWindow :: proc(hWnd: HWND) -> BOOL --- + + ShowWindow :: proc(hWnd: HWND, nCmdShow: c_int) -> BOOL --- + BringWindowToTop :: proc(hWnd: HWND) -> BOOL --- + GetTopWindow :: proc(hWnd: HWND) -> HWND --- + SetForegroundWindow :: proc(hWnd: HWND) -> BOOL --- + GetForegroundWindow :: proc() -> HWND --- + SetActiveWindow :: proc(hWnd: HWND) -> HWND --- + GetActiveWindow :: proc() -> HWND --- + + GetMessageW :: proc(lpMsg: ^MSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT) -> BOOL --- + + TranslateMessage :: proc(lpMsg: ^MSG) -> BOOL --- + DispatchMessageW :: proc(lpMsg: ^MSG) -> LRESULT --- + + PeekMessageA :: proc(lpMsg: ^MSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT, wRemoveMsg: UINT) -> BOOL --- + PeekMessageW :: proc(lpMsg: ^MSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT, wRemoveMsg: UINT) -> BOOL --- + + PostMessageA :: proc(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> BOOL --- + PostMessageW :: proc(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> BOOL --- + SendMessageA :: proc(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT --- + SendMessageW :: proc(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT --- + + PostThreadMessageA :: proc(idThread: DWORD, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> BOOL --- + PostThreadMessageW :: proc(idThread: DWORD, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> BOOL --- + + PostQuitMessage :: proc(nExitCode: c_int) --- + + GetQueueStatus :: proc(flags: UINT) -> DWORD --- + + DefWindowProcA :: proc(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT --- + DefWindowProcW :: proc(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT --- + + FindWindowA :: proc(lpClassName: LPCSTR, lpWindowName: LPCSTR) -> HWND --- + FindWindowW :: proc(lpClassName: LPCWSTR, lpWindowName: LPCWSTR) -> HWND --- + FindWindowExA :: proc(hWndParent: HWND, hWndChildAfter: HWND, lpszClass: LPCSTR, lpszWindow: LPCSTR) -> HWND --- + FindWindowExW :: proc(hWndParent: HWND, hWndChildAfter: HWND, lpszClass: LPCWSTR, lpszWindow: LPCWSTR) -> HWND --- + + LoadIconA :: proc(hInstance: HINSTANCE, lpIconName: LPCSTR) -> HICON --- + LoadIconW :: proc(hInstance: HINSTANCE, lpIconName: LPCWSTR) -> HICON --- + LoadCursorA :: proc(hInstance: HINSTANCE, lpCursorName: LPCSTR) -> HCURSOR --- + LoadCursorW :: proc(hInstance: HINSTANCE, lpCursorName: LPCWSTR) -> HCURSOR --- + + GetWindowRect :: proc(hWnd: HWND, lpRect: LPRECT) -> BOOL --- + GetClientRect :: proc(hWnd: HWND, lpRect: LPRECT) -> BOOL --- + ClientToScreen :: proc(hWnd: HWND, lpPoint: LPPOINT) -> BOOL --- + ScreenToClient :: proc(hWnd: HWND, lpPoint: LPPOINT) -> BOOL --- + SetWindowPos :: proc( + hWnd: HWND, + hWndInsertAfter: HWND, + X: c_int, + Y: c_int, + cx: c_int, + cy: c_int, + uFlags: UINT, + ) -> BOOL --- + MoveWindow :: proc(hWnd: HWND, X, Y, hWidth, hHeight: c_int, bRepaint: BOOL) -> BOOL --- + GetSystemMetrics :: proc(nIndex: c_int) -> c_int --- + AdjustWindowRect :: proc(lpRect: LPRECT, dwStyle: DWORD, bMenu: BOOL) -> BOOL --- + AdjustWindowRectEx :: proc(lpRect: LPRECT, dwStyle: DWORD, bMenu: BOOL, dwExStyle: DWORD) -> BOOL --- + + SystemParametersInfoW :: proc(uiAction, uiParam: UINT, pvParam: PVOID, fWinIni: UINT) -> BOOL --- + + GetWindowDC :: proc(hWnd: HWND) -> HDC --- + GetDC :: proc(hWnd: HWND) -> HDC --- + ReleaseDC :: proc(hWnd: HWND, hDC: HDC) -> c_int --- + + GetUpdateRect :: proc(hWnd: HWND, lpRect: LPRECT, bErase: BOOL) -> BOOL --- + ValidateRect :: proc(hWnd: HWND, lpRect: ^RECT) -> BOOL --- + InvalidateRect :: proc(hWnd: HWND, lpRect: ^RECT, bErase: BOOL) -> BOOL --- + + BeginPaint :: proc(hWnd: HWND, lpPaint: ^PAINTSTRUCT) -> HDC --- + EndPaint :: proc(hWnd: HWND, lpPaint: ^PAINTSTRUCT) -> BOOL --- + + GetCapture :: proc() -> HWND --- + SetCapture :: proc(hWnd: HWND) -> HWND --- + ReleaseCapture :: proc() -> BOOL --- + TrackMouseEvent :: proc(lpEventTrack: LPTRACKMOUSEEVENT) -> BOOL --- + + GetKeyState :: proc(nVirtKey: c_int) -> SHORT --- + GetAsyncKeyState :: proc(vKey: c_int) -> SHORT --- + + MapVirtualKeyW :: proc(uCode: UINT, uMapType: UINT) -> UINT --- + + SetWindowsHookExW :: proc(idHook: c_int, lpfn: HOOKPROC, hmod: HINSTANCE, dwThreadId: DWORD) -> HHOOK --- + UnhookWindowsHookEx :: proc(hhk: HHOOK) -> BOOL --- + CallNextHookEx :: proc(hhk: HHOOK, nCode: c_int, wParam: WPARAM, lParam: LPARAM) -> LRESULT --- + + SetTimer :: proc(hWnd: HWND, nIDEvent: UINT_PTR, uElapse: UINT, lpTimerFunc: TIMERPROC) -> UINT_PTR --- + KillTimer :: proc(hWnd: HWND, uIDEvent: UINT_PTR) -> BOOL --- + + // MessageBoxA :: proc(hWnd: HWND, lpText: LPCSTR, lpCaption: LPCSTR, uType: UINT) -> c_int --- + MessageBoxW :: proc(hWnd: HWND, lpText: LPCWSTR, lpCaption: LPCWSTR, uType: UINT) -> c_int --- + // MessageBoxExA :: proc(hWnd: HWND, lpText: LPCSTR, lpCaption: LPCSTR, uType: UINT, wLanguageId: WORD) -> c_int --- + MessageBoxExW :: proc(hWnd: HWND, lpText: LPCWSTR, lpCaption: LPCWSTR, uType: UINT, wLanguageId: WORD) -> c_int --- + + ClipCursor :: proc(lpRect: LPRECT) -> BOOL --- + GetCursorPos :: proc(lpPoint: LPPOINT) -> BOOL --- + SetCursorPos :: proc(X: c_int, Y: c_int) -> BOOL --- + SetCursor :: proc(hCursor: HCURSOR) -> HCURSOR --- + + EnumDisplaySettingsW :: proc(lpszDeviceName: LPCWSTR, iModeNum: DWORD, lpDevMode: ^DEVMODEW) -> BOOL --- + + BroadcastSystemMessageW :: proc( + flags: DWORD, + lpInfo: LPDWORD, + Msg: UINT, + wParam: WPARAM, + lParam: LPARAM, + ) -> c_long --- + + BroadcastSystemMessageExW :: proc( + flags: DWORD, + lpInfo: LPDWORD, + Msg: UINT, + wParam: WPARAM, + lParam: LPARAM, + pbsmInfo: PBSMINFO, + ) -> c_long --- + + SendMessageTimeoutW :: proc( + hWnd: HWND, + Msg: UINT, + wParam: WPARAM, + lParam: LPARAM, + fuFlags: UINT, + uTimeout: UINT, + lpdwResult: PDWORD_PTR, + ) -> LRESULT --- + + GetSysColor :: proc(nIndex: c_int) -> DWORD --- + GetSysColorBrush :: proc(nIndex: c_int) -> HBRUSH --- + SetSysColors :: proc(cElements: c_int, lpaElements: ^INT, lpaRgbValues: ^COLORREF) -> BOOL --- + MessageBeep :: proc(uType: UINT) -> BOOL --- + + IsDialogMessageW :: proc(hDlg: HWND, lpMsg: LPMSG) -> BOOL --- + GetWindowTextLengthW :: proc(hWnd: HWND) -> c_int --- + GetWindowTextW :: proc(hWnd: HWND, lpString: LPWSTR, nMaxCount: c_int) -> c_int --- + SetWindowTextW :: proc(hWnd: HWND, lpString: LPCWSTR) -> BOOL --- + CallWindowProcW :: proc(lpPrevWndFunc: WNDPROC, hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT --- + EnableWindow :: proc(hWnd: HWND, bEnable: BOOL) -> BOOL --- +} + +CreateWindowW :: #force_inline proc "stdcall" ( + lpClassName: LPCTSTR, + lpWindowName: LPCTSTR, + dwStyle: DWORD, + X: c_int, + Y: c_int, + nWidth: c_int, + nHeight: c_int, + hWndParent: HWND, + hMenu: HMENU, + hInstance: HINSTANCE, + lpParam: LPVOID, +) -> HWND { + return CreateWindowExW( + 0, + lpClassName, + lpWindowName, + dwStyle, + X, + Y, + nWidth, + nHeight, + hWndParent, + hMenu, + hInstance, + lpParam, + ) +} + +when ODIN_ARCH == .amd64 { + @(default_calling_convention="stdcall") + foreign user32 { + GetClassLongPtrW :: proc(hWnd: HWND, nIndex: c_int) -> ULONG_PTR --- + SetClassLongPtrW :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG_PTR) -> ULONG_PTR --- + + GetWindowLongPtrW :: proc(hWnd: HWND, nIndex: c_int) -> LONG_PTR --- + SetWindowLongPtrW :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG_PTR) -> LONG_PTR --- + } +} else when ODIN_ARCH == .i386 { + GetClassLongPtrW :: GetClassLongW + SetClassLongPtrW :: SetClassLongW + + GetWindowLongPtrW :: GetWindowLongW + SetWindowLongPtrW :: GetWindowLongW +} + +GET_SC_WPARAM :: #force_inline proc "contextless" (wParam: WPARAM) -> c_int { + return c_int(wParam) & 0xFFF0 +} + +GET_WHEEL_DELTA_WPARAM :: #force_inline proc "contextless" (wParam: WPARAM) -> c_short { + return cast(c_short)HIWORD(cast(DWORD)wParam) +} + +GET_KEYSTATE_WPARAM :: #force_inline proc "contextless" (wParam: WPARAM) -> WORD { + return LOWORD(cast(DWORD)wParam) +} + +GET_NCHITTEST_WPARAM :: #force_inline proc "contextless" (wParam: WPARAM) -> c_short { + return cast(c_short)LOWORD(cast(DWORD)wParam) +} + +GET_XBUTTON_WPARAM :: #force_inline proc "contextless" (wParam: WPARAM) -> WORD { + return HIWORD(cast(DWORD)wParam) +} + +MAKEINTRESOURCEW :: #force_inline proc "contextless" (#any_int i: int) -> LPWSTR { + return cast(LPWSTR)uintptr(WORD(i)) +} diff --git a/core/sys/windows/util.odin b/core/sys/windows/util.odin index efb37dbc0..2bdd72ed2 100644 --- a/core/sys/windows/util.odin +++ b/core/sys/windows/util.odin @@ -1,8 +1,10 @@ // +build windows package sys_windows -import "core:strings" -import "core:sys/win32" +import "core:runtime" +import "core:intrinsics" + +L :: intrinsics.constant_utf16_cstring LOWORD :: #force_inline proc "contextless" (x: DWORD) -> WORD { return WORD(x & 0xffff) @@ -12,6 +14,18 @@ HIWORD :: #force_inline proc "contextless" (x: DWORD) -> WORD { return WORD(x >> 16) } +GET_X_LPARAM :: #force_inline proc "contextless" (lp: LPARAM) -> c_int { + return cast(c_int)cast(c_short)LOWORD(cast(DWORD)lp) +} + +GET_Y_LPARAM :: #force_inline proc "contextless" (lp: LPARAM) -> c_int { + return cast(c_int)cast(c_short)HIWORD(cast(DWORD)lp) +} + +MAKE_WORD :: #force_inline proc "contextless" (x, y: WORD) -> WORD { + return x << 8 | y +} + utf8_to_utf16 :: proc(s: string, allocator := context.temp_allocator) -> []u16 { if len(s) < 1 { return nil @@ -45,14 +59,16 @@ utf8_to_wstring :: proc(s: string, allocator := context.temp_allocator) -> wstri return nil } -wstring_to_utf8 :: proc(s: wstring, N: int, allocator := context.temp_allocator) -> string { +wstring_to_utf8 :: proc(s: wstring, N: int, allocator := context.temp_allocator) -> (res: string, err: runtime.Allocator_Error) { + context.allocator = allocator + if N <= 0 { - return "" + return } n := WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, s, i32(N), nil, 0, nil, nil) if n == 0 { - return "" + return } // If N == -1 the call to WideCharToMultiByte assume the wide string is null terminated @@ -60,12 +76,12 @@ wstring_to_utf8 :: proc(s: wstring, N: int, allocator := context.temp_allocator) // also null terminated. // If N != -1 it assumes the wide string is not null terminated and the resulting string // will not be null terminated, we therefore have to force it to be null terminated manually. - text := make([]byte, n+1 if N != -1 else n, allocator) + text := make([]byte, n+1 if N != -1 else n) or_return n1 := WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, s, i32(N), raw_data(text), n, nil, nil) if n1 == 0 { delete(text, allocator) - return "" + return } for i in 0.. string { +utf16_to_utf8 :: proc(s: []u16, allocator := context.temp_allocator) -> (res: string, err: runtime.Allocator_Error) { if len(s) == 0 { - return "" + return "", nil } return wstring_to_utf8(raw_data(s), len(s), allocator) } @@ -88,6 +103,20 @@ utf16_to_utf8 :: proc(s: []u16, allocator := context.temp_allocator) -> string { // AdvAPI32, NetAPI32 and UserENV helpers. allowed_username :: proc(username: string) -> bool { + contains_any :: proc(s, chars: string) -> bool { + if chars == "" { + return false + } + for c in transmute([]byte)s { + for b in transmute([]byte)chars { + if c == b { + return true + } + } + } + return false + } + /* User account names are limited to 20 characters and group names are limited to 256 characters. In addition, account names cannot be terminated by a period and they cannot include commas or any of the following printable characters: @@ -108,7 +137,7 @@ allowed_username :: proc(username: string) -> bool { return false } } - if strings.contains_any(username, _DISALLOWED) { + if contains_any(username, _DISALLOWED) { return false } @@ -204,7 +233,7 @@ get_computer_name_and_account_sid :: proc(username: string) -> (computer_name: s if !res { return "", {}, false } - computer_name = utf16_to_utf8(cname_w, context.temp_allocator) + computer_name = utf16_to_utf8(cname_w, context.temp_allocator) or_else "" ok = true return @@ -294,7 +323,7 @@ add_user_profile :: proc(username: string) -> (ok: bool, profile_path: string) { if res == false { return false, "" } - defer win32.local_free(sb) + defer LocalFree(sb) pszProfilePath := make([]u16, 257, context.temp_allocator) res2 := CreateProfile( @@ -306,7 +335,7 @@ add_user_profile :: proc(username: string) -> (ok: bool, profile_path: string) { if res2 != 0 { return false, "" } - profile_path = wstring_to_utf8(&pszProfilePath[0], 257) + profile_path = wstring_to_utf8(&pszProfilePath[0], 257) or_else "" return true, profile_path } @@ -324,7 +353,7 @@ delete_user_profile :: proc(username: string) -> (ok: bool) { if res == false { return false } - defer win32.local_free(sb) + defer LocalFree(sb) res2 := DeleteProfileW( sb, @@ -439,20 +468,20 @@ run_as_user :: proc(username, password, application, commandline: string, pi: ^P nil, // lpProcessAttributes, nil, // lpThreadAttributes, false, // bInheritHandles, - 0, // creation flags - nil, // environment, - nil, // current directory: inherit from parent if nil - &si, - pi, - )) - if ok { - if wait { - WaitForSingleObject(pi.hProcess, INFINITE) - CloseHandle(pi.hProcess) - CloseHandle(pi.hThread) - } - return true - } else { - return false - } -} \ No newline at end of file + 0, // creation flags + nil, // environment, + nil, // current directory: inherit from parent if nil + &si, + pi, + )) + if ok { + if wait { + WaitForSingleObject(pi.hProcess, INFINITE) + CloseHandle(pi.hProcess) + CloseHandle(pi.hThread) + } + return true + } else { + return false + } +} diff --git a/core/sys/windows/wgl.odin b/core/sys/windows/wgl.odin new file mode 100644 index 000000000..689a41dea --- /dev/null +++ b/core/sys/windows/wgl.odin @@ -0,0 +1,87 @@ +// +build windows +package sys_windows + +import "core:c" + +foreign import "system:Opengl32.lib" + +CONTEXT_MAJOR_VERSION_ARB :: 0x2091 +CONTEXT_MINOR_VERSION_ARB :: 0x2092 +CONTEXT_FLAGS_ARB :: 0x2094 +CONTEXT_PROFILE_MASK_ARB :: 0x9126 +CONTEXT_FORWARD_COMPATIBLE_BIT_ARB :: 0x0002 +CONTEXT_CORE_PROFILE_BIT_ARB :: 0x00000001 +CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB :: 0x00000002 + +HGLRC :: distinct HANDLE + +LPLAYERPLANEDESCRIPTOR :: ^LAYERPLANEDESCRIPTOR +LAYERPLANEDESCRIPTOR :: struct { + nSize: WORD, + nVersion: WORD, + dwFlags: DWORD, + iPixelType: BYTE, + cColorBits: BYTE, + cRedBits: BYTE, + cRedShift: BYTE, + cGreenBits: BYTE, + cGreenShift: BYTE, + cBlueBits: BYTE, + cBlueShift: BYTE, + cAlphaBits: BYTE, + cAlphaShift: BYTE, + cAccumBits: BYTE, + cAccumRedBits: BYTE, + cAccumGreenBits: BYTE, + cAccumBlueBits: BYTE, + cAccumAlphaBits: BYTE, + cDepthBits: BYTE, + cStencilBits: BYTE, + cAuxBuffers: BYTE, + iLayerPlane: BYTE, + bReserved: BYTE, + crTransparent: COLORREF, +} + +POINTFLOAT :: struct {x, y: f32} + +LPGLYPHMETRICSFLOAT :: ^GLYPHMETRICSFLOAT +GLYPHMETRICSFLOAT :: struct { + gmfBlackBoxX: f32, + gmfBlackBoxY: f32, + gmfptGlyphOrigin: POINTFLOAT, + gmfCellIncX: f32, + gmfCellIncY: f32, +} + +CreateContextAttribsARBType :: #type proc "c" (hdc: HDC, hShareContext: rawptr, attribList: [^]c.int) -> HGLRC +ChoosePixelFormatARBType :: #type proc "c" (hdc: HDC, attribIList: [^]c.int, attribFList: [^]f32, maxFormats: DWORD, formats: [^]c.int, numFormats: [^]DWORD) -> BOOL +SwapIntervalEXTType :: #type proc "c" (interval: c.int) -> bool +GetExtensionsStringARBType :: #type proc "c" (HDC) -> cstring + +// Procedures + wglCreateContextAttribsARB: CreateContextAttribsARBType + wglChoosePixelFormatARB: ChoosePixelFormatARBType + wglSwapIntervalExt: SwapIntervalEXTType + wglGetExtensionsStringARB: GetExtensionsStringARBType + + +@(default_calling_convention="stdcall") +foreign Opengl32 { + wglCreateContext :: proc(hdc: HDC) -> HGLRC --- + wglMakeCurrent :: proc(hdc: HDC, HGLRC: HGLRC) -> BOOL --- + wglGetProcAddress :: proc(c_str: cstring) -> rawptr --- + wglDeleteContext :: proc(HGLRC: HGLRC) -> BOOL --- + wglCopyContext :: proc(src, dst: HGLRC, mask: UINT) -> BOOL --- + wglCreateLayerContext :: proc(hdc: HDC, layer_plane: c.int) -> HGLRC --- + wglDescribeLayerPlane :: proc(hdc: HDC, pixel_format, layer_plane: c.int, bytes: UINT, pd: LPLAYERPLANEDESCRIPTOR) -> BOOL --- + wglGetCurrentContext :: proc() -> HGLRC --- + wglGetCurrentDC :: proc() -> HDC --- + wglGetLayerPaletteEntries :: proc(hdc: HDC, layer_plane, start, entries: c.int, cr: ^COLORREF) -> c.int --- + wglRealizeLayerPalette :: proc(hdc: HDC, layer_plane: c.int, realize: BOOL) -> BOOL --- + wglSetLayerPaletteEntries :: proc(hdc: HDC, layer_plane, start, entries: c.int, cr: ^COLORREF) -> c.int --- + wglShareLists :: proc(HGLRC1, HGLRC2: HGLRC) -> BOOL --- + wglSwapLayerBuffers :: proc(hdc: HDC, planes: DWORD) -> BOOL --- + wglUseFontBitmaps :: proc(hdc: HDC, first, count, list_base: DWORD) -> BOOL --- + wglUseFontOutlines :: proc(hdc: HDC, first, count, list_base: DWORD, deviation, extrusion: f32, format: c.int, gmf: LPGLYPHMETRICSFLOAT) -> BOOL --- +} diff --git a/core/sys/windows/window_messages.odin b/core/sys/windows/window_messages.odin new file mode 100644 index 000000000..d6b883916 --- /dev/null +++ b/core/sys/windows/window_messages.odin @@ -0,0 +1,1049 @@ +// +build windows +package sys_windows + +WM_NULL :: 0x0000 +WM_CREATE :: 0x0001 +WM_DESTROY :: 0x0002 +WM_MOVE :: 0x0003 +WM_SIZE :: 0x0005 +WM_ACTIVATE :: 0x0006 +WM_SETFOCUS :: 0x0007 +WM_KILLFOCUS :: 0x0008 +WM_ENABLE :: 0x000a +WM_SETREDRAW :: 0x000b +WM_SETTEXT :: 0x000c +WM_GETTEXT :: 0x000d +WM_GETTEXTLENGTH :: 0x000e +WM_PAINT :: 0x000f +WM_CLOSE :: 0x0010 +WM_QUERYENDSESSION :: 0x0011 +WM_QUIT :: 0x0012 +WM_QUERYOPEN :: 0x0013 +WM_ERASEBKGND :: 0x0014 +WM_SYSCOLORCHANGE :: 0x0015 +WM_ENDSESSION :: 0x0016 +WM_SHOWWINDOW :: 0x0018 +WM_CTLCOLOR :: 0x0019 +WM_WININICHANGE :: 0x001a +WM_SETTINGCHANGE :: WM_WININICHANGE +WM_DEVMODECHANGE :: 0x001b +WM_ACTIVATEAPP :: 0x001c +WM_FONTCHANGE :: 0x001d +WM_TIMECHANGE :: 0x001e +WM_CANCELMODE :: 0x001f +WM_SETCURSOR :: 0x0020 +WM_MOUSEACTIVATE :: 0x0021 +WM_CHILDACTIVATE :: 0x0022 +WM_QUEUESYNC :: 0x0023 +WM_GETMINMAXINFO :: 0x0024 +WM_PAINTICON :: 0x0026 +WM_ICONERASEBKGND :: 0x0027 +WM_NEXTDLGCTL :: 0x0028 +WM_SPOOLERSTATUS :: 0x002a +WM_DRAWITEM :: 0x002b +WM_MEASUREITEM :: 0x002c +WM_DELETEITEM :: 0x002d +WM_VKEYTOITEM :: 0x002e +WM_CHARTOITEM :: 0x002f +WM_SETFONT :: 0x0030 +WM_GETFONT :: 0x0031 +WM_SETHOTKEY :: 0x0032 +WM_GETHOTKEY :: 0x0033 +WM_QUERYDRAGICON :: 0x0037 +WM_COMPAREITEM :: 0x0039 +WM_GETOBJECT :: 0x003d +WM_COMPACTING :: 0x0041 +WM_COMMNOTIFY :: 0x0044 +WM_WINDOWPOSCHANGING :: 0x0046 +WM_WINDOWPOSCHANGED :: 0x0047 +WM_POWER :: 0x0048 +WM_COPYGLOBALDATA :: 0x0049 +WM_COPYDATA :: 0x004a +WM_CANCELJOURNAL :: 0x004b +WM_NOTIFY :: 0x004e +WM_INPUTLANGCHANGEREQUEST :: 0x0050 +WM_INPUTLANGCHANGE :: 0x0051 +WM_TCARD :: 0x0052 +WM_HELP :: 0x0053 +WM_USERCHANGED :: 0x0054 +WM_NOTIFYFORMAT :: 0x0055 +WM_CONTEXTMENU :: 0x007b +WM_STYLECHANGING :: 0x007c +WM_STYLECHANGED :: 0x007d +WM_DISPLAYCHANGE :: 0x007e +WM_GETICON :: 0x007f +WM_SETICON :: 0x0080 +WM_NCCREATE :: 0x0081 +WM_NCDESTROY :: 0x0082 +WM_NCCALCSIZE :: 0x0083 +WM_NCHITTEST :: 0x0084 +WM_NCPAINT :: 0x0085 +WM_NCACTIVATE :: 0x0086 +WM_GETDLGCODE :: 0x0087 +WM_SYNCPAINT :: 0x0088 +WM_NCMOUSEMOVE :: 0x00a0 +WM_NCLBUTTONDOWN :: 0x00a1 +WM_NCLBUTTONUP :: 0x00a2 +WM_NCLBUTTONDBLCLK :: 0x00a3 +WM_NCRBUTTONDOWN :: 0x00a4 +WM_NCRBUTTONUP :: 0x00a5 +WM_NCRBUTTONDBLCLK :: 0x00a6 +WM_NCMBUTTONDOWN :: 0x00a7 +WM_NCMBUTTONUP :: 0x00a8 +WM_NCMBUTTONDBLCLK :: 0x00a9 +WM_NCXBUTTONDOWN :: 0x00ab +WM_NCXBUTTONUP :: 0x00ac +WM_NCXBUTTONDBLCLK :: 0x00ad +EM_GETSEL :: 0x00b0 +EM_SETSEL :: 0x00b1 +EM_GETRECT :: 0x00b2 +EM_SETRECT :: 0x00b3 +EM_SETRECTNP :: 0x00b4 +EM_SCROLL :: 0x00b5 +EM_LINESCROLL :: 0x00b6 +EM_SCROLLCARET :: 0x00b7 +EM_GETMODIFY :: 0x00b8 +EM_SETMODIFY :: 0x00b9 +EM_GETLINECOUNT :: 0x00ba +EM_LINEINDEX :: 0x00bb +EM_SETHANDLE :: 0x00bc +EM_GETHANDLE :: 0x00bd +EM_GETTHUMB :: 0x00be +EM_LINELENGTH :: 0x00c1 +EM_REPLACESEL :: 0x00c2 +EM_SETFONT :: 0x00c3 +EM_GETLINE :: 0x00c4 +EM_LIMITTEXT :: 0x00c5 +EM_SETLIMITTEXT :: 0x00c5 +EM_CANUNDO :: 0x00c6 +EM_UNDO :: 0x00c7 +EM_FMTLINES :: 0x00c8 +EM_LINEFROMCHAR :: 0x00c9 +EM_SETWORDBREAK :: 0x00ca +EM_SETTABSTOPS :: 0x00cb +EM_SETPASSWORDCHAR :: 0x00cc +EM_EMPTYUNDOBUFFER :: 0x00cd +EM_GETFIRSTVISIBLELINE :: 0x00ce +EM_SETREADONLY :: 0x00cf +EM_SETWORDBREAKPROC :: 0x00d0 +EM_GETWORDBREAKPROC :: 0x00d1 +EM_GETPASSWORDCHAR :: 0x00d2 +EM_SETMARGINS :: 0x00d3 +EM_GETMARGINS :: 0x00d4 +EM_GETLIMITTEXT :: 0x00d5 +EM_POSFROMCHAR :: 0x00d6 +EM_CHARFROMPOS :: 0x00d7 +EM_SETIMESTATUS :: 0x00d8 +EM_GETIMESTATUS :: 0x00d9 +SBM_SETPOS :: 0x00e0 +SBM_GETPOS :: 0x00e1 +SBM_SETRANGE :: 0x00e2 +SBM_GETRANGE :: 0x00e3 +SBM_ENABLE_ARROWS :: 0x00e4 +SBM_SETRANGEREDRAW :: 0x00e6 +SBM_SETSCROLLINFO :: 0x00e9 +SBM_GETSCROLLINFO :: 0x00ea +SBM_GETSCROLLBARINFO :: 0x00eb +BM_GETCHECK :: 0x00f0 +BM_SETCHECK :: 0x00f1 +BM_GETSTATE :: 0x00f2 +BM_SETSTATE :: 0x00f3 +BM_SETSTYLE :: 0x00f4 +BM_CLICK :: 0x00f5 +BM_GETIMAGE :: 0x00f6 +BM_SETIMAGE :: 0x00f7 +BM_SETDONTCLICK :: 0x00f8 +WM_INPUT :: 0x00ff +WM_KEYDOWN :: 0x0100 +WM_KEYFIRST :: 0x0100 +WM_KEYUP :: 0x0101 +WM_CHAR :: 0x0102 +WM_DEADCHAR :: 0x0103 +WM_SYSKEYDOWN :: 0x0104 +WM_SYSKEYUP :: 0x0105 +WM_SYSCHAR :: 0x0106 +WM_SYSDEADCHAR :: 0x0107 +WM_UNICHAR :: 0x0109 +WM_KEYLAST :: 0x0109 +WM_WNT_CONVERTREQUESTEX :: 0x0109 +WM_CONVERTREQUEST :: 0x010a +WM_CONVERTRESULT :: 0x010b +WM_INTERIM :: 0x010c +WM_IME_STARTCOMPOSITION :: 0x010d +WM_IME_ENDCOMPOSITION :: 0x010e +WM_IME_COMPOSITION :: 0x010f +WM_IME_KEYLAST :: 0x010f +WM_INITDIALOG :: 0x0110 +WM_COMMAND :: 0x0111 +WM_SYSCOMMAND :: 0x0112 +WM_TIMER :: 0x0113 +WM_HSCROLL :: 0x0114 +WM_VSCROLL :: 0x0115 +WM_INITMENU :: 0x0116 +WM_INITMENUPOPUP :: 0x0117 +WM_SYSTIMER :: 0x0118 +WM_MENUSELECT :: 0x011f +WM_MENUCHAR :: 0x0120 +WM_ENTERIDLE :: 0x0121 +WM_MENURBUTTONUP :: 0x0122 +WM_MENUDRAG :: 0x0123 +WM_MENUGETOBJECT :: 0x0124 +WM_UNINITMENUPOPUP :: 0x0125 +WM_MENUCOMMAND :: 0x0126 +WM_CHANGEUISTATE :: 0x0127 +WM_UPDATEUISTATE :: 0x0128 +WM_QUERYUISTATE :: 0x0129 +WM_LBTRACKPOINT :: 0x0131 +WM_CTLCOLORMSGBOX :: 0x0132 +WM_CTLCOLOREDIT :: 0x0133 +WM_CTLCOLORLISTBOX :: 0x0134 +WM_CTLCOLORBTN :: 0x0135 +WM_CTLCOLORDLG :: 0x0136 +WM_CTLCOLORSCROLLBAR :: 0x0137 +WM_CTLCOLORSTATIC :: 0x0138 +CB_GETEDITSEL :: 0x0140 +CB_LIMITTEXT :: 0x0141 +CB_SETEDITSEL :: 0x0142 +CB_ADDSTRING :: 0x0143 +CB_DELETESTRING :: 0x0144 +CB_DIR :: 0x0145 +CB_GETCOUNT :: 0x0146 +CB_GETCURSEL :: 0x0147 +CB_GETLBTEXT :: 0x0148 +CB_GETLBTEXTLEN :: 0x0149 +CB_INSERTSTRING :: 0x014a +CB_RESETCONTENT :: 0x014b +CB_FINDSTRING :: 0x014c +CB_SELECTSTRING :: 0x014d +CB_SETCURSEL :: 0x014e +CB_SHOWDROPDOWN :: 0x014f +CB_GETITEMDATA :: 0x0150 +CB_SETITEMDATA :: 0x0151 +CB_GETDROPPEDCONTROLRECT :: 0x0152 +CB_SETITEMHEIGHT :: 0x0153 +CB_GETITEMHEIGHT :: 0x0154 +CB_SETEXTENDEDUI :: 0x0155 +CB_GETEXTENDEDUI :: 0x0156 +CB_GETDROPPEDSTATE :: 0x0157 +CB_FINDSTRINGEXACT :: 0x0158 +CB_SETLOCALE :: 0x0159 +CB_GETLOCALE :: 0x015a +CB_GETTOPINDEX :: 0x015b +CB_SETTOPINDEX :: 0x015c +CB_GETHORIZONTALEXTENT :: 0x015d +CB_SETHORIZONTALEXTENT :: 0x015e +CB_GETDROPPEDWIDTH :: 0x015f +CB_SETDROPPEDWIDTH :: 0x0160 +CB_INITSTORAGE :: 0x0161 +CB_MULTIPLEADDSTRING :: 0x0163 +CB_GETCOMBOBOXINFO :: 0x0164 +CB_MSGMAX :: 0x0165 +WM_MOUSEFIRST :: 0x0200 +WM_MOUSEMOVE :: 0x0200 +WM_LBUTTONDOWN :: 0x0201 +WM_LBUTTONUP :: 0x0202 +WM_LBUTTONDBLCLK :: 0x0203 +WM_RBUTTONDOWN :: 0x0204 +WM_RBUTTONUP :: 0x0205 +WM_RBUTTONDBLCLK :: 0x0206 +WM_MBUTTONDOWN :: 0x0207 +WM_MBUTTONUP :: 0x0208 +WM_MBUTTONDBLCLK :: 0x0209 +WM_MOUSELAST :: 0x0209 +WM_MOUSEWHEEL :: 0x020a +WM_XBUTTONDOWN :: 0x020b +WM_XBUTTONUP :: 0x020c +WM_XBUTTONDBLCLK :: 0x020d +WM_MOUSEHWHEEL :: 0x020e +WM_PARENTNOTIFY :: 0x0210 +WM_ENTERMENULOOP :: 0x0211 +WM_EXITMENULOOP :: 0x0212 +WM_NEXTMENU :: 0x0213 +WM_SIZING :: 0x0214 +WM_CAPTURECHANGED :: 0x0215 +WM_MOVING :: 0x0216 +WM_POWERBROADCAST :: 0x0218 +WM_DEVICECHANGE :: 0x0219 +WM_MDICREATE :: 0x0220 +WM_MDIDESTROY :: 0x0221 +WM_MDIACTIVATE :: 0x0222 +WM_MDIRESTORE :: 0x0223 +WM_MDINEXT :: 0x0224 +WM_MDIMAXIMIZE :: 0x0225 +WM_MDITILE :: 0x0226 +WM_MDICASCADE :: 0x0227 +WM_MDIICONARRANGE :: 0x0228 +WM_MDIGETACTIVE :: 0x0229 +WM_MDISETMENU :: 0x0230 +WM_ENTERSIZEMOVE :: 0x0231 +WM_EXITSIZEMOVE :: 0x0232 +WM_DROPFILES :: 0x0233 +WM_MDIREFRESHMENU :: 0x0234 +WM_IME_REPORT :: 0x0280 +WM_IME_SETCONTEXT :: 0x0281 +WM_IME_NOTIFY :: 0x0282 +WM_IME_CONTROL :: 0x0283 +WM_IME_COMPOSITIONFULL :: 0x0284 +WM_IME_SELECT :: 0x0285 +WM_IME_CHAR :: 0x0286 +WM_IME_REQUEST :: 0x0288 +WM_IMEKEYDOWN :: 0x0290 +WM_IME_KEYDOWN :: 0x0290 +WM_IMEKEYUP :: 0x0291 +WM_IME_KEYUP :: 0x0291 +WM_NCMOUSEHOVER :: 0x02a0 +WM_MOUSEHOVER :: 0x02a1 +WM_NCMOUSELEAVE :: 0x02a2 +WM_MOUSELEAVE :: 0x02a3 +WM_CUT :: 0x0300 +WM_COPY :: 0x0301 +WM_PASTE :: 0x0302 +WM_CLEAR :: 0x0303 +WM_UNDO :: 0x0304 +WM_RENDERFORMAT :: 0x0305 +WM_RENDERALLFORMATS :: 0x0306 +WM_DESTROYCLIPBOARD :: 0x0307 +WM_DRAWCLIPBOARD :: 0x0308 +WM_PAINTCLIPBOARD :: 0x0309 +WM_VSCROLLCLIPBOARD :: 0x030a +WM_SIZECLIPBOARD :: 0x030b +WM_ASKCBFORMATNAME :: 0x030c +WM_CHANGECBCHAIN :: 0x030d +WM_HSCROLLCLIPBOARD :: 0x030e +WM_QUERYNEWPALETTE :: 0x030f +WM_PALETTEISCHANGING :: 0x0310 +WM_PALETTECHANGED :: 0x0311 +WM_HOTKEY :: 0x0312 +WM_PRINT :: 0x0317 +WM_PRINTCLIENT :: 0x0318 +WM_APPCOMMAND :: 0x0319 +WM_HANDHELDFIRST :: 0x0358 +WM_HANDHELDLAST :: 0x035f +WM_AFXFIRST :: 0x0360 +WM_AFXLAST :: 0x037f +WM_PENWINFIRST :: 0x0380 +WM_RCRESULT :: 0x0381 +WM_HOOKRCRESULT :: 0x0382 +WM_GLOBALRCCHANGE :: 0x0383 +WM_PENMISCINFO :: 0x0383 +WM_SKB :: 0x0384 +WM_HEDITCTL :: 0x0385 +WM_PENCTL :: 0x0385 +WM_PENMISC :: 0x0386 +WM_CTLINIT :: 0x0387 +WM_PENEVENT :: 0x0388 +WM_PENWINLAST :: 0x038f +DDM_SETFMT :: 0x0400 +DM_GETDEFID :: 0x0400 +NIN_SELECT :: 0x0400 +TBM_GETPOS :: 0x0400 +WM_PSD_PAGESETUPDLG :: 0x0400 +WM_USER :: 0x0400 +CBEM_INSERTITEMA :: 0x0401 +DDM_DRAW :: 0x0401 +DM_SETDEFID :: 0x0401 +HKM_SETHOTKEY :: 0x0401 +PBM_SETRANGE :: 0x0401 +RB_INSERTBANDA :: 0x0401 +SB_SETTEXTA :: 0x0401 +TB_ENABLEBUTTON :: 0x0401 +TBM_GETRANGEMIN :: 0x0401 +TTM_ACTIVATE :: 0x0401 +WM_CHOOSEFONT_GETLOGFONT :: 0x0401 +WM_PSD_FULLPAGERECT :: 0x0401 +CBEM_SETIMAGELIST :: 0x0402 +DDM_CLOSE :: 0x0402 +DM_REPOSITION :: 0x0402 +HKM_GETHOTKEY :: 0x0402 +PBM_SETPOS :: 0x0402 +RB_DELETEBAND :: 0x0402 +SB_GETTEXTA :: 0x0402 +TB_CHECKBUTTON :: 0x0402 +TBM_GETRANGEMAX :: 0x0402 +WM_PSD_MINMARGINRECT :: 0x0402 +CBEM_GETIMAGELIST :: 0x0403 +DDM_BEGIN :: 0x0403 +HKM_SETRULES :: 0x0403 +PBM_DELTAPOS :: 0x0403 +RB_GETBARINFO :: 0x0403 +SB_GETTEXTLENGTHA :: 0x0403 +TBM_GETTIC :: 0x0403 +TB_PRESSBUTTON :: 0x0403 +TTM_SETDELAYTIME :: 0x0403 +WM_PSD_MARGINRECT :: 0x0403 +CBEM_GETITEMA :: 0x0404 +DDM_END :: 0x0404 +PBM_SETSTEP :: 0x0404 +RB_SETBARINFO :: 0x0404 +SB_SETPARTS :: 0x0404 +TB_HIDEBUTTON :: 0x0404 +TBM_SETTIC :: 0x0404 +TTM_ADDTOOLA :: 0x0404 +WM_PSD_GREEKTEXTRECT :: 0x0404 +CBEM_SETITEMA :: 0x0405 +PBM_STEPIT :: 0x0405 +TB_INDETERMINATE :: 0x0405 +TBM_SETPOS :: 0x0405 +TTM_DELTOOLA :: 0x0405 +WM_PSD_ENVSTAMPRECT :: 0x0405 +CBEM_GETCOMBOCONTROL :: 0x0406 +PBM_SETRANGE32 :: 0x0406 +RB_SETBANDINFOA :: 0x0406 +SB_GETPARTS :: 0x0406 +TB_MARKBUTTON :: 0x0406 +TBM_SETRANGE :: 0x0406 +TTM_NEWTOOLRECTA :: 0x0406 +WM_PSD_YAFULLPAGERECT :: 0x0406 +CBEM_GETEDITCONTROL :: 0x0407 +PBM_GETRANGE :: 0x0407 +RB_SETPARENT :: 0x0407 +SB_GETBORDERS :: 0x0407 +TBM_SETRANGEMIN :: 0x0407 +TTM_RELAYEVENT :: 0x0407 +CBEM_SETEXSTYLE :: 0x0408 +PBM_GETPOS :: 0x0408 +RB_HITTEST :: 0x0408 +SB_SETMINHEIGHT :: 0x0408 +TBM_SETRANGEMAX :: 0x0408 +TTM_GETTOOLINFOA :: 0x0408 +CBEM_GETEXSTYLE :: 0x0409 +CBEM_GETEXTENDEDSTYLE :: 0x0409 +PBM_SETBARCOLOR :: 0x0409 +RB_GETRECT :: 0x0409 +SB_SIMPLE :: 0x0409 +TB_ISBUTTONENABLED :: 0x0409 +TBM_CLEARTICS :: 0x0409 +TTM_SETTOOLINFOA :: 0x0409 +CBEM_HASEDITCHANGED :: 0x040a +RB_INSERTBANDW :: 0x040a +SB_GETRECT :: 0x040a +TB_ISBUTTONCHECKED :: 0x040a +TBM_SETSEL :: 0x040a +TTM_HITTESTA :: 0x040a +WIZ_QUERYNUMPAGES :: 0x040a +CBEM_INSERTITEMW :: 0x040b +RB_SETBANDINFOW :: 0x040b +SB_SETTEXTW :: 0x040b +TB_ISBUTTONPRESSED :: 0x040b +TBM_SETSELSTART :: 0x040b +TTM_GETTEXTA :: 0x040b +WIZ_NEXT :: 0x040b +CBEM_SETITEMW :: 0x040c +RB_GETBANDCOUNT :: 0x040c +SB_GETTEXTLENGTHW :: 0x040c +TB_ISBUTTONHIDDEN :: 0x040c +TBM_SETSELEND :: 0x040c +TTM_UPDATETIPTEXTA :: 0x040c +WIZ_PREV :: 0x040c +CBEM_GETITEMW :: 0x040d +RB_GETROWCOUNT :: 0x040d +SB_GETTEXTW :: 0x040d +TB_ISBUTTONINDETERMINATE :: 0x040d +TTM_GETTOOLCOUNT :: 0x040d +CBEM_SETEXTENDEDSTYLE :: 0x040e +RB_GETROWHEIGHT :: 0x040e +SB_ISSIMPLE :: 0x040e +TB_ISBUTTONHIGHLIGHTED :: 0x040e +TBM_GETPTICS :: 0x040e +TTM_ENUMTOOLSA :: 0x040e +SB_SETICON :: 0x040f +TBM_GETTICPOS :: 0x040f +TTM_GETCURRENTTOOLA :: 0x040f +RB_IDTOINDEX :: 0x0410 +SB_SETTIPTEXTA :: 0x0410 +TBM_GETNUMTICS :: 0x0410 +TTM_WINDOWFROMPOINT :: 0x0410 +RB_GETTOOLTIPS :: 0x0411 +SB_SETTIPTEXTW :: 0x0411 +TBM_GETSELSTART :: 0x0411 +TB_SETSTATE :: 0x0411 +TTM_TRACKACTIVATE :: 0x0411 +RB_SETTOOLTIPS :: 0x0412 +SB_GETTIPTEXTA :: 0x0412 +TB_GETSTATE :: 0x0412 +TBM_GETSELEND :: 0x0412 +TTM_TRACKPOSITION :: 0x0412 +RB_SETBKCOLOR :: 0x0413 +SB_GETTIPTEXTW :: 0x0413 +TB_ADDBITMAP :: 0x0413 +TBM_CLEARSEL :: 0x0413 +TTM_SETTIPBKCOLOR :: 0x0413 +RB_GETBKCOLOR :: 0x0414 +SB_GETICON :: 0x0414 +TB_ADDBUTTONSA :: 0x0414 +TBM_SETTICFREQ :: 0x0414 +TTM_SETTIPTEXTCOLOR :: 0x0414 +RB_SETTEXTCOLOR :: 0x0415 +TB_INSERTBUTTONA :: 0x0415 +TBM_SETPAGESIZE :: 0x0415 +TTM_GETDELAYTIME :: 0x0415 +RB_GETTEXTCOLOR :: 0x0416 +TB_DELETEBUTTON :: 0x0416 +TBM_GETPAGESIZE :: 0x0416 +TTM_GETTIPBKCOLOR :: 0x0416 +RB_SIZETORECT :: 0x0417 +TB_GETBUTTON :: 0x0417 +TBM_SETLINESIZE :: 0x0417 +TTM_GETTIPTEXTCOLOR :: 0x0417 +RB_BEGINDRAG :: 0x0418 +TB_BUTTONCOUNT :: 0x0418 +TBM_GETLINESIZE :: 0x0418 +TTM_SETMAXTIPWIDTH :: 0x0418 +RB_ENDDRAG :: 0x0419 +TB_COMMANDTOINDEX :: 0x0419 +TBM_GETTHUMBRECT :: 0x0419 +TTM_GETMAXTIPWIDTH :: 0x0419 +RB_DRAGMOVE :: 0x041a +TBM_GETCHANNELRECT :: 0x041a +TB_SAVERESTOREA :: 0x041a +TTM_SETMARGIN :: 0x041a +RB_GETBARHEIGHT :: 0x041b +TB_CUSTOMIZE :: 0x041b +TBM_SETTHUMBLENGTH :: 0x041b +TTM_GETMARGIN :: 0x041b +RB_GETBANDINFOW :: 0x041c +TB_ADDSTRINGA :: 0x041c +TBM_GETTHUMBLENGTH :: 0x041c +TTM_POP :: 0x041c +RB_GETBANDINFOA :: 0x041d +TB_GETITEMRECT :: 0x041d +TBM_SETTOOLTIPS :: 0x041d +TTM_UPDATE :: 0x041d +RB_MINIMIZEBAND :: 0x041e +TB_BUTTONSTRUCTSIZE :: 0x041e +TBM_GETTOOLTIPS :: 0x041e +TTM_GETBUBBLESIZE :: 0x041e +RB_MAXIMIZEBAND :: 0x041f +TBM_SETTIPSIDE :: 0x041f +TB_SETBUTTONSIZE :: 0x041f +TTM_ADJUSTRECT :: 0x041f +TBM_SETBUDDY :: 0x0420 +TB_SETBITMAPSIZE :: 0x0420 +TTM_SETTITLEA :: 0x0420 +MSG_FTS_JUMP_VA :: 0x0421 +TB_AUTOSIZE :: 0x0421 +TBM_GETBUDDY :: 0x0421 +TTM_SETTITLEW :: 0x0421 +RB_GETBANDBORDERS :: 0x0422 +MSG_FTS_JUMP_QWORD :: 0x0423 +RB_SHOWBAND :: 0x0423 +TB_GETTOOLTIPS :: 0x0423 +MSG_REINDEX_REQUEST :: 0x0424 +TB_SETTOOLTIPS :: 0x0424 +MSG_FTS_WHERE_IS_IT :: 0x0425 +RB_SETPALETTE :: 0x0425 +TB_SETPARENT :: 0x0425 +RB_GETPALETTE :: 0x0426 +RB_MOVEBAND :: 0x0427 +TB_SETROWS :: 0x0427 +TB_GETROWS :: 0x0428 +TB_GETBITMAPFLAGS :: 0x0429 +TB_SETCMDID :: 0x042a +RB_PUSHCHEVRON :: 0x042b +TB_CHANGEBITMAP :: 0x042b +TB_GETBITMAP :: 0x042c +MSG_GET_DEFFONT :: 0x042d +TB_GETBUTTONTEXTA :: 0x042d +TB_REPLACEBITMAP :: 0x042e +TB_SETINDENT :: 0x042f +TB_SETIMAGELIST :: 0x0430 +TB_GETIMAGELIST :: 0x0431 +TB_LOADIMAGES :: 0x0432 +EM_CANPASTE :: 0x0432 +TTM_ADDTOOLW :: 0x0432 +EM_DISPLAYBAND :: 0x0433 +TB_GETRECT :: 0x0433 +TTM_DELTOOLW :: 0x0433 +EM_EXGETSEL :: 0x0434 +TB_SETHOTIMAGELIST :: 0x0434 +TTM_NEWTOOLRECTW :: 0x0434 +EM_EXLIMITTEXT :: 0x0435 +TB_GETHOTIMAGELIST :: 0x0435 +TTM_GETTOOLINFOW :: 0x0435 +EM_EXLINEFROMCHAR :: 0x0436 +TB_SETDISABLEDIMAGELIST :: 0x0436 +TTM_SETTOOLINFOW :: 0x0436 +EM_EXSETSEL :: 0x0437 +TB_GETDISABLEDIMAGELIST :: 0x0437 +TTM_HITTESTW :: 0x0437 +EM_FINDTEXT :: 0x0438 +TB_SETSTYLE :: 0x0438 +TTM_GETTEXTW :: 0x0438 +EM_FORMATRANGE :: 0x0439 +TB_GETSTYLE :: 0x0439 +TTM_UPDATETIPTEXTW :: 0x0439 +EM_GETCHARFORMAT :: 0x043a +TB_GETBUTTONSIZE :: 0x043a +TTM_ENUMTOOLSW :: 0x043a +EM_GETEVENTMASK :: 0x043b +TB_SETBUTTONWIDTH :: 0x043b +TTM_GETCURRENTTOOLW :: 0x043b +EM_GETOLEINTERFACE :: 0x043c +TB_SETMAXTEXTROWS :: 0x043c +EM_GETPARAFORMAT :: 0x043d +TB_GETTEXTROWS :: 0x043d +EM_GETSELTEXT :: 0x043e +TB_GETOBJECT :: 0x043e +EM_HIDESELECTION :: 0x043f +TB_GETBUTTONINFOW :: 0x043f +EM_PASTESPECIAL :: 0x0440 +TB_SETBUTTONINFOW :: 0x0440 +EM_REQUESTRESIZE :: 0x0441 +TB_GETBUTTONINFOA :: 0x0441 +EM_SELECTIONTYPE :: 0x0442 +TB_SETBUTTONINFOA :: 0x0442 +EM_SETBKGNDCOLOR :: 0x0443 +TB_INSERTBUTTONW :: 0x0443 +EM_SETCHARFORMAT :: 0x0444 +TB_ADDBUTTONSW :: 0x0444 +EM_SETEVENTMASK :: 0x0445 +TB_HITTEST :: 0x0445 +EM_SETOLECALLBACK :: 0x0446 +TB_SETDRAWTEXTFLAGS :: 0x0446 +EM_SETPARAFORMAT :: 0x0447 +TB_GETHOTITEM :: 0x0447 +EM_SETTARGETDEVICE :: 0x0448 +TB_SETHOTITEM :: 0x0448 +EM_STREAMIN :: 0x0449 +TB_SETANCHORHIGHLIGHT :: 0x0449 +EM_STREAMOUT :: 0x044a +TB_GETANCHORHIGHLIGHT :: 0x044a +EM_GETTEXTRANGE :: 0x044b +TB_GETBUTTONTEXTW :: 0x044b +EM_FINDWORDBREAK :: 0x044c +TB_SAVERESTOREW :: 0x044c +EM_SETOPTIONS :: 0x044d +TB_ADDSTRINGW :: 0x044d +EM_GETOPTIONS :: 0x044e +TB_MAPACCELERATORA :: 0x044e +EM_FINDTEXTEX :: 0x044f +TB_GETINSERTMARK :: 0x044f +EM_GETWORDBREAKPROCEX :: 0x0450 +TB_SETINSERTMARK :: 0x0450 +EM_SETWORDBREAKPROCEX :: 0x0451 +TB_INSERTMARKHITTEST :: 0x0451 +EM_SETUNDOLIMIT :: 0x0452 +TB_MOVEBUTTON :: 0x0452 +TB_GETMAXSIZE :: 0x0453 +EM_REDO :: 0x0454 +TB_SETEXTENDEDSTYLE :: 0x0454 +EM_CANREDO :: 0x0455 +TB_GETEXTENDEDSTYLE :: 0x0455 +EM_GETUNDONAME :: 0x0456 +TB_GETPADDING :: 0x0456 +EM_GETREDONAME :: 0x0457 +TB_SETPADDING :: 0x0457 +EM_STOPGROUPTYPING :: 0x0458 +TB_SETINSERTMARKCOLOR :: 0x0458 +EM_SETTEXTMODE :: 0x0459 +TB_GETINSERTMARKCOLOR :: 0x0459 +EM_GETTEXTMODE :: 0x045a +TB_MAPACCELERATORW :: 0x045a +EM_AUTOURLDETECT :: 0x045b +TB_GETSTRINGW :: 0x045b +EM_GETAUTOURLDETECT :: 0x045c +TB_GETSTRINGA :: 0x045c +EM_SETPALETTE :: 0x045d +EM_GETTEXTEX :: 0x045e +EM_GETTEXTLENGTHEX :: 0x045f +EM_SHOWSCROLLBAR :: 0x0460 +EM_SETTEXTEX :: 0x0461 +TAPI_REPLY :: 0x0463 +ACM_OPENA :: 0x0464 +BFFM_SETSTATUSTEXTA :: 0x0464 +CDM_FIRST :: 0x0464 +CDM_GETSPEC :: 0x0464 +EM_SETPUNCTUATION :: 0x0464 +IPM_CLEARADDRESS :: 0x0464 +WM_CAP_UNICODE_START :: 0x0464 +ACM_PLAY :: 0x0465 +BFFM_ENABLEOK :: 0x0465 +CDM_GETFILEPATH :: 0x0465 +EM_GETPUNCTUATION :: 0x0465 +IPM_SETADDRESS :: 0x0465 +PSM_SETCURSEL :: 0x0465 +UDM_SETRANGE :: 0x0465 +WM_CHOOSEFONT_SETLOGFONT :: 0x0465 +ACM_STOP :: 0x0466 +BFFM_SETSELECTIONA :: 0x0466 +CDM_GETFOLDERPATH :: 0x0466 +EM_SETWORDWRAPMODE :: 0x0466 +IPM_GETADDRESS :: 0x0466 +PSM_REMOVEPAGE :: 0x0466 +UDM_GETRANGE :: 0x0466 +WM_CAP_SET_CALLBACK_ERRORW :: 0x0466 +WM_CHOOSEFONT_SETFLAGS :: 0x0466 +ACM_OPENW :: 0x0467 +BFFM_SETSELECTIONW :: 0x0467 +CDM_GETFOLDERIDLIST :: 0x0467 +EM_GETWORDWRAPMODE :: 0x0467 +IPM_SETRANGE :: 0x0467 +PSM_ADDPAGE :: 0x0467 +UDM_SETPOS :: 0x0467 +WM_CAP_SET_CALLBACK_STATUSW :: 0x0467 +BFFM_SETSTATUSTEXTW :: 0x0468 +CDM_SETCONTROLTEXT :: 0x0468 +EM_SETIMECOLOR :: 0x0468 +IPM_SETFOCUS :: 0x0468 +PSM_CHANGED :: 0x0468 +UDM_GETPOS :: 0x0468 +CDM_HIDECONTROL :: 0x0469 +EM_GETIMECOLOR :: 0x0469 +IPM_ISBLANK :: 0x0469 +PSM_RESTARTWINDOWS :: 0x0469 +UDM_SETBUDDY :: 0x0469 +CDM_SETDEFEXT :: 0x046a +EM_SETIMEOPTIONS :: 0x046a +PSM_REBOOTSYSTEM :: 0x046a +UDM_GETBUDDY :: 0x046a +EM_GETIMEOPTIONS :: 0x046b +PSM_CANCELTOCLOSE :: 0x046b +UDM_SETACCEL :: 0x046b +EM_CONVPOSITION :: 0x046c +PSM_QUERYSIBLINGS :: 0x046c +UDM_GETACCEL :: 0x046c +MCIWNDM_GETZOOM :: 0x046d +PSM_UNCHANGED :: 0x046d +UDM_SETBASE :: 0x046d +PSM_APPLY :: 0x046e +UDM_GETBASE :: 0x046e +PSM_SETTITLEA :: 0x046f +UDM_SETRANGE32 :: 0x046f +PSM_SETWIZBUTTONS :: 0x0470 +UDM_GETRANGE32 :: 0x0470 +WM_CAP_DRIVER_GET_NAMEW :: 0x0470 +PSM_PRESSBUTTON :: 0x0471 +UDM_SETPOS32 :: 0x0471 +WM_CAP_DRIVER_GET_VERSIONW :: 0x0471 +PSM_SETCURSELID :: 0x0472 +UDM_GETPOS32 :: 0x0472 +PSM_SETFINISHTEXTA :: 0x0473 +PSM_GETTABCONTROL :: 0x0474 +PSM_ISDIALOGMESSAGE :: 0x0475 +MCIWNDM_REALIZE :: 0x0476 +PSM_GETCURRENTPAGEHWND :: 0x0476 +MCIWNDM_SETTIMEFORMATA :: 0x0477 +PSM_INSERTPAGE :: 0x0477 +EM_SETLANGOPTIONS :: 0x0478 +MCIWNDM_GETTIMEFORMATA :: 0x0478 +PSM_SETTITLEW :: 0x0478 +WM_CAP_FILE_SET_CAPTURE_FILEW :: 0x0478 +EM_GETLANGOPTIONS :: 0x0479 +MCIWNDM_VALIDATEMEDIA :: 0x0479 +PSM_SETFINISHTEXTW :: 0x0479 +WM_CAP_FILE_GET_CAPTURE_FILEW :: 0x0479 +EM_GETIMECOMPMODE :: 0x047a +EM_FINDTEXTW :: 0x047b +MCIWNDM_PLAYTO :: 0x047b +WM_CAP_FILE_SAVEASW :: 0x047b +EM_FINDTEXTEXW :: 0x047c +MCIWNDM_GETFILENAMEA :: 0x047c +EM_RECONVERSION :: 0x047d +MCIWNDM_GETDEVICEA :: 0x047d +PSM_SETHEADERTITLEA :: 0x047d +WM_CAP_FILE_SAVEDIBW :: 0x047d +EM_SETIMEMODEBIAS :: 0x047e +MCIWNDM_GETPALETTE :: 0x047e +PSM_SETHEADERTITLEW :: 0x047e +EM_GETIMEMODEBIAS :: 0x047f +MCIWNDM_SETPALETTE :: 0x047f +PSM_SETHEADERSUBTITLEA :: 0x047f +MCIWNDM_GETERRORA :: 0x0480 +PSM_SETHEADERSUBTITLEW :: 0x0480 +PSM_HWNDTOINDEX :: 0x0481 +PSM_INDEXTOHWND :: 0x0482 +MCIWNDM_SETINACTIVETIMER :: 0x0483 +PSM_PAGETOINDEX :: 0x0483 +PSM_INDEXTOPAGE :: 0x0484 +DL_BEGINDRAG :: 0x0485 +MCIWNDM_GETINACTIVETIMER :: 0x0485 +PSM_IDTOINDEX :: 0x0485 +DL_DRAGGING :: 0x0486 +PSM_INDEXTOID :: 0x0486 +DL_DROPPED :: 0x0487 +PSM_GETRESULT :: 0x0487 +DL_CANCELDRAG :: 0x0488 +PSM_RECALCPAGESIZES :: 0x0488 +MCIWNDM_GET_SOURCE :: 0x048c +MCIWNDM_PUT_SOURCE :: 0x048d +MCIWNDM_GET_DEST :: 0x048e +MCIWNDM_PUT_DEST :: 0x048f +MCIWNDM_CAN_PLAY :: 0x0490 +MCIWNDM_CAN_WINDOW :: 0x0491 +MCIWNDM_CAN_RECORD :: 0x0492 +MCIWNDM_CAN_SAVE :: 0x0493 +MCIWNDM_CAN_EJECT :: 0x0494 +MCIWNDM_CAN_CONFIG :: 0x0495 +IE_GETINK :: 0x0496 +IE_MSGFIRST :: 0x0496 +MCIWNDM_PALETTEKICK :: 0x0496 +IE_SETINK :: 0x0497 +IE_GETPENTIP :: 0x0498 +IE_SETPENTIP :: 0x0499 +IE_GETERASERTIP :: 0x049a +IE_SETERASERTIP :: 0x049b +IE_GETBKGND :: 0x049c +IE_SETBKGND :: 0x049d +IE_GETGRIDORIGIN :: 0x049e +IE_SETGRIDORIGIN :: 0x049f +IE_GETGRIDPEN :: 0x04a0 +IE_SETGRIDPEN :: 0x04a1 +IE_GETGRIDSIZE :: 0x04a2 +IE_SETGRIDSIZE :: 0x04a3 +IE_GETMODE :: 0x04a4 +IE_SETMODE :: 0x04a5 +IE_GETINKRECT :: 0x04a6 +WM_CAP_SET_MCI_DEVICEW :: 0x04a6 +WM_CAP_GET_MCI_DEVICEW :: 0x04a7 +WM_CAP_PAL_OPENW :: 0x04b4 +WM_CAP_PAL_SAVEW :: 0x04b5 +IE_GETAPPDATA :: 0x04b8 +IE_SETAPPDATA :: 0x04b9 +IE_GETDRAWOPTS :: 0x04ba +IE_SETDRAWOPTS :: 0x04bb +IE_GETFORMAT :: 0x04bc +IE_SETFORMAT :: 0x04bd +IE_GETINKINPUT :: 0x04be +IE_SETINKINPUT :: 0x04bf +IE_GETNOTIFY :: 0x04c0 +IE_SETNOTIFY :: 0x04c1 +IE_GETRECOG :: 0x04c2 +IE_SETRECOG :: 0x04c3 +IE_GETSECURITY :: 0x04c4 +IE_SETSECURITY :: 0x04c5 +IE_GETSEL :: 0x04c6 +IE_SETSEL :: 0x04c7 +CDM_LAST :: 0x04c8 +EM_SETBIDIOPTIONS :: 0x04c8 +IE_DOCOMMAND :: 0x04c8 +MCIWNDM_NOTIFYMODE :: 0x04c8 +EM_GETBIDIOPTIONS :: 0x04c9 +IE_GETCOMMAND :: 0x04c9 +EM_SETTYPOGRAPHYOPTIONS :: 0x04ca +IE_GETCOUNT :: 0x04ca +EM_GETTYPOGRAPHYOPTIONS :: 0x04cb +IE_GETGESTURE :: 0x04cb +MCIWNDM_NOTIFYMEDIA :: 0x04cb +EM_SETEDITSTYLE :: 0x04cc +IE_GETMENU :: 0x04cc +EM_GETEDITSTYLE :: 0x04cd +IE_GETPAINTDC :: 0x04cd +MCIWNDM_NOTIFYERROR :: 0x04cd +IE_GETPDEVENT :: 0x04ce +IE_GETSELCOUNT :: 0x04cf +IE_GETSELITEMS :: 0x04d0 +IE_GETSTYLE :: 0x04d1 +MCIWNDM_SETTIMEFORMATW :: 0x04db +EM_OUTLINE :: 0x04dc +MCIWNDM_GETTIMEFORMATW :: 0x04dc +EM_GETSCROLLPOS :: 0x04dd +EM_SETSCROLLPOS :: 0x04de +EM_SETFONTSIZE :: 0x04df +EM_GETZOOM :: 0x04e0 +MCIWNDM_GETFILENAMEW :: 0x04e0 +EM_SETZOOM :: 0x04e1 +MCIWNDM_GETDEVICEW :: 0x04e1 +EM_GETVIEWKIND :: 0x04e2 +EM_SETVIEWKIND :: 0x04e3 +EM_GETPAGE :: 0x04e4 +MCIWNDM_GETERRORW :: 0x04e4 +EM_SETPAGE :: 0x04e5 +EM_GETHYPHENATEINFO :: 0x04e6 +EM_SETHYPHENATEINFO :: 0x04e7 +EM_GETPAGEROTATE :: 0x04eb +EM_SETPAGEROTATE :: 0x04ec +EM_GETCTFMODEBIAS :: 0x04ed +EM_SETCTFMODEBIAS :: 0x04ee +EM_GETCTFOPENSTATUS :: 0x04f0 +EM_SETCTFOPENSTATUS :: 0x04f1 +EM_GETIMECOMPTEXT :: 0x04f2 +EM_ISIME :: 0x04f3 +EM_GETIMEPROPERTY :: 0x04f4 +EM_GETQUERYRTFOBJ :: 0x050d +EM_SETQUERYRTFOBJ :: 0x050e +FM_GETFOCUS :: 0x0600 +FM_GETDRIVEINFOA :: 0x0601 +FM_GETSELCOUNT :: 0x0602 +FM_GETSELCOUNTLFN :: 0x0603 +FM_GETFILESELA :: 0x0604 +FM_GETFILESELLFNA :: 0x0605 +FM_REFRESH_WINDOWS :: 0x0606 +FM_RELOAD_EXTENSIONS :: 0x0607 +FM_GETDRIVEINFOW :: 0x0611 +FM_GETFILESELW :: 0x0614 +FM_GETFILESELLFNW :: 0x0615 +WLX_WM_SAS :: 0x0659 +SM_GETSELCOUNT :: 0x07e8 +UM_GETSELCOUNT :: 0x07e8 +WM_CPL_LAUNCH :: 0x07e8 +SM_GETSERVERSELA :: 0x07e9 +UM_GETUSERSELA :: 0x07e9 +WM_CPL_LAUNCHED :: 0x07e9 +SM_GETSERVERSELW :: 0x07ea +UM_GETUSERSELW :: 0x07ea +SM_GETCURFOCUSA :: 0x07eb +UM_GETGROUPSELA :: 0x07eb +SM_GETCURFOCUSW :: 0x07ec +UM_GETGROUPSELW :: 0x07ec +SM_GETOPTIONS :: 0x07ed +UM_GETCURFOCUSA :: 0x07ed +UM_GETCURFOCUSW :: 0x07ee +UM_GETOPTIONS :: 0x07ef +UM_GETOPTIONS2 :: 0x07f0 +LVM_FIRST :: 0x1000 +LVM_GETBKCOLOR :: 0x1000 +LVM_SETBKCOLOR :: 0x1001 +LVM_GETIMAGELIST :: 0x1002 +LVM_SETIMAGELIST :: 0x1003 +LVM_GETITEMCOUNT :: 0x1004 +LVM_GETITEMA :: 0x1005 +LVM_SETITEMA :: 0x1006 +LVM_INSERTITEMA :: 0x1007 +LVM_DELETEITEM :: 0x1008 +LVM_DELETEALLITEMS :: 0x1009 +LVM_GETCALLBACKMASK :: 0x100a +LVM_SETCALLBACKMASK :: 0x100b +LVM_GETNEXTITEM :: 0x100c +LVM_FINDITEMA :: 0x100d +LVM_GETITEMRECT :: 0x100e +LVM_SETITEMPOSITION :: 0x100f +LVM_GETITEMPOSITION :: 0x1010 +LVM_GETSTRINGWIDTHA :: 0x1011 +LVM_HITTEST :: 0x1012 +LVM_ENSUREVISIBLE :: 0x1013 +LVM_SCROLL :: 0x1014 +LVM_REDRAWITEMS :: 0x1015 +LVM_ARRANGE :: 0x1016 +LVM_EDITLABELA :: 0x1017 +LVM_GETEDITCONTROL :: 0x1018 +LVM_GETCOLUMNA :: 0x1019 +LVM_SETCOLUMNA :: 0x101a +LVM_INSERTCOLUMNA :: 0x101b +LVM_DELETECOLUMN :: 0x101c +LVM_GETCOLUMNWIDTH :: 0x101d +LVM_SETCOLUMNWIDTH :: 0x101e +LVM_GETHEADER :: 0x101f +LVM_CREATEDRAGIMAGE :: 0x1021 +LVM_GETVIEWRECT :: 0x1022 +LVM_GETTEXTCOLOR :: 0x1023 +LVM_SETTEXTCOLOR :: 0x1024 +LVM_GETTEXTBKCOLOR :: 0x1025 +LVM_SETTEXTBKCOLOR :: 0x1026 +LVM_GETTOPINDEX :: 0x1027 +LVM_GETCOUNTPERPAGE :: 0x1028 +LVM_GETORIGIN :: 0x1029 +LVM_UPDATE :: 0x102a +LVM_SETITEMSTATE :: 0x102b +LVM_GETITEMSTATE :: 0x102c +LVM_GETITEMTEXTA :: 0x102d +LVM_SETITEMTEXTA :: 0x102e +LVM_SETITEMCOUNT :: 0x102f +LVM_SORTITEMS :: 0x1030 +LVM_SETITEMPOSITION32 :: 0x1031 +LVM_GETSELECTEDCOUNT :: 0x1032 +LVM_GETITEMSPACING :: 0x1033 +LVM_GETISEARCHSTRINGA :: 0x1034 +LVM_SETICONSPACING :: 0x1035 +LVM_SETEXTENDEDLISTVIEWSTYLE :: 0x1036 +LVM_GETEXTENDEDLISTVIEWSTYLE :: 0x1037 +LVM_GETSUBITEMRECT :: 0x1038 +LVM_SUBITEMHITTEST :: 0x1039 +LVM_SETCOLUMNORDERARRAY :: 0x103a +LVM_GETCOLUMNORDERARRAY :: 0x103b +LVM_SETHOTITEM :: 0x103c +LVM_GETHOTITEM :: 0x103d +LVM_SETHOTCURSOR :: 0x103e +LVM_GETHOTCURSOR :: 0x103f +LVM_APPROXIMATEVIEWRECT :: 0x1040 +LVM_SETWORKAREAS :: 0x1041 +LVM_GETSELECTIONMARK :: 0x1042 +LVM_SETSELECTIONMARK :: 0x1043 +LVM_SETBKIMAGEA :: 0x1044 +LVM_GETBKIMAGEA :: 0x1045 +LVM_GETWORKAREAS :: 0x1046 +LVM_SETHOVERTIME :: 0x1047 +LVM_GETHOVERTIME :: 0x1048 +LVM_GETNUMBEROFWORKAREAS :: 0x1049 +LVM_SETTOOLTIPS :: 0x104a +LVM_GETITEMW :: 0x104b +LVM_SETITEMW :: 0x104c +LVM_INSERTITEMW :: 0x104d +LVM_GETTOOLTIPS :: 0x104e +LVM_FINDITEMW :: 0x1053 +LVM_GETSTRINGWIDTHW :: 0x1057 +LVM_GETCOLUMNW :: 0x105f +LVM_SETCOLUMNW :: 0x1060 +LVM_INSERTCOLUMNW :: 0x1061 +LVM_GETITEMTEXTW :: 0x1073 +LVM_SETITEMTEXTW :: 0x1074 +LVM_GETISEARCHSTRINGW :: 0x1075 +LVM_EDITLABELW :: 0x1076 +LVM_GETBKIMAGEW :: 0x108b +LVM_SETSELECTEDCOLUMN :: 0x108c +LVM_SETTILEWIDTH :: 0x108d +LVM_SETVIEW :: 0x108e +LVM_GETVIEW :: 0x108f +LVM_INSERTGROUP :: 0x1091 +LVM_SETGROUPINFO :: 0x1093 +LVM_GETGROUPINFO :: 0x1095 +LVM_REMOVEGROUP :: 0x1096 +LVM_MOVEGROUP :: 0x1097 +LVM_MOVEITEMTOGROUP :: 0x109a +LVM_SETGROUPMETRICS :: 0x109b +LVM_GETGROUPMETRICS :: 0x109c +LVM_ENABLEGROUPVIEW :: 0x109d +LVM_SORTGROUPS :: 0x109e +LVM_INSERTGROUPSORTED :: 0x109f +LVM_REMOVEALLGROUPS :: 0x10a0 +LVM_HASGROUP :: 0x10a1 +LVM_SETTILEVIEWINFO :: 0x10a2 +LVM_GETTILEVIEWINFO :: 0x10a3 +LVM_SETTILEINFO :: 0x10a4 +LVM_GETTILEINFO :: 0x10a5 +LVM_SETINSERTMARK :: 0x10a6 +LVM_GETINSERTMARK :: 0x10a7 +LVM_INSERTMARKHITTEST :: 0x10a8 +LVM_GETINSERTMARKRECT :: 0x10a9 +LVM_SETINSERTMARKCOLOR :: 0x10aa +LVM_GETINSERTMARKCOLOR :: 0x10ab +LVM_SETINFOTIP :: 0x10ad +LVM_GETSELECTEDCOLUMN :: 0x10ae +LVM_ISGROUPVIEWENABLED :: 0x10af +LVM_GETOUTLINECOLOR :: 0x10b0 +LVM_SETOUTLINECOLOR :: 0x10b1 +LVM_CANCELEDITLABEL :: 0x10b3 +LVM_MAPINDEXTOID :: 0x10b4 +LVM_MAPIDTOINDEX :: 0x10b5 +LVM_ISITEMVISIBLE :: 0x10b6 +LVM_GETEMPTYTEXT :: 0x10cc +LVM_GETFOOTERRECT :: 0x10cd +LVM_GETFOOTERINFO :: 0x10ce +LVM_GETFOOTERITEMRECT :: 0x10cf +LVM_GETFOOTERITEM :: 0x10d0 +LVM_GETITEMINDEXRECT :: 0x10d1 +LVM_SETITEMINDEXSTATE :: 0x10d2 +LVM_GETNEXTITEMINDEX :: 0x10d3 +OCM__BASE :: 0x2000 +LVM_SETUNICODEFORMAT :: 0x2005 +LVM_GETUNICODEFORMAT :: 0x2006 +OCM_CTLCOLOR :: 0x2019 +OCM_DRAWITEM :: 0x202b +OCM_MEASUREITEM :: 0x202c +OCM_DELETEITEM :: 0x202d +OCM_VKEYTOITEM :: 0x202e +OCM_CHARTOITEM :: 0x202f +OCM_COMPAREITEM :: 0x2039 +OCM_NOTIFY :: 0x204e +OCM_COMMAND :: 0x2111 +OCM_HSCROLL :: 0x2114 +OCM_VSCROLL :: 0x2115 +OCM_CTLCOLORMSGBOX :: 0x2132 +OCM_CTLCOLOREDIT :: 0x2133 +OCM_CTLCOLORLISTBOX :: 0x2134 +OCM_CTLCOLORBTN :: 0x2135 +OCM_CTLCOLORDLG :: 0x2136 +OCM_CTLCOLORSCROLLBAR :: 0x2137 +OCM_CTLCOLORSTATIC :: 0x2138 +OCM_PARENTNOTIFY :: 0x2210 +WM_APP :: 0x8000 +WM_RASDIALEVENT :: 0xcccd diff --git a/core/sys/windows/winerror.odin b/core/sys/windows/winerror.odin new file mode 100644 index 000000000..7bd0bfe9f --- /dev/null +++ b/core/sys/windows/winerror.odin @@ -0,0 +1,48 @@ +// +build windows +package sys_windows + +ERROR_SUCCESS : DWORD : 0 +NO_ERROR :: 0 +SEC_E_OK : HRESULT : 0x00000000 + +ERROR_INVALID_FUNCTION : DWORD : 1 +ERROR_FILE_NOT_FOUND : DWORD : 2 +ERROR_PATH_NOT_FOUND : DWORD : 3 +ERROR_ACCESS_DENIED : DWORD : 5 +ERROR_INVALID_HANDLE : DWORD : 6 +ERROR_NOT_ENOUGH_MEMORY : DWORD : 8 +ERROR_INVALID_BLOCK : DWORD : 9 +ERROR_BAD_ENVIRONMENT : DWORD : 10 +ERROR_BAD_FORMAT : DWORD : 11 +ERROR_INVALID_ACCESS : DWORD : 12 +ERROR_INVALID_DATA : DWORD : 13 +ERROR_OUTOFMEMORY : DWORD : 14 +ERROR_INVALID_DRIVE : DWORD : 15 +ERROR_CURRENT_DIRECTORY : DWORD : 16 +ERROR_NO_MORE_FILES : DWORD : 18 +ERROR_SHARING_VIOLATION : DWORD : 32 +ERROR_LOCK_VIOLATION : DWORD : 33 +ERROR_HANDLE_EOF : DWORD : 38 +ERROR_NOT_SUPPORTED : DWORD : 50 +ERROR_FILE_EXISTS : DWORD : 80 +ERROR_INVALID_PARAMETER : DWORD : 87 +ERROR_BROKEN_PIPE : DWORD : 109 +ERROR_CALL_NOT_IMPLEMENTED : DWORD : 120 +ERROR_INSUFFICIENT_BUFFER : DWORD : 122 +ERROR_INVALID_NAME : DWORD : 123 +ERROR_BAD_ARGUMENTS : DWORD : 160 +ERROR_LOCK_FAILED : DWORD : 167 +ERROR_ALREADY_EXISTS : DWORD : 183 +ERROR_NO_DATA : DWORD : 232 +ERROR_ENVVAR_NOT_FOUND : DWORD : 203 +ERROR_OPERATION_ABORTED : DWORD : 995 +ERROR_IO_PENDING : DWORD : 997 +ERROR_NO_UNICODE_TRANSLATION : DWORD : 1113 +ERROR_TIMEOUT : DWORD : 1460 +ERROR_DATATYPE_MISMATCH : DWORD : 1629 +ERROR_UNSUPPORTED_TYPE : DWORD : 1630 +ERROR_NOT_SAME_OBJECT : DWORD : 1656 + +E_NOTIMPL :: HRESULT(-0x7fff_bfff) // 0x8000_4001 + +SUCCEEDED :: #force_inline proc(#any_int result: int) -> bool { return result >= 0 } diff --git a/core/sys/windows/winmm.odin b/core/sys/windows/winmm.odin new file mode 100644 index 000000000..17f4d8e86 --- /dev/null +++ b/core/sys/windows/winmm.odin @@ -0,0 +1,10 @@ +// +build windows +package sys_windows + +foreign import winmm "system:Winmm.lib" + +@(default_calling_convention="stdcall") +foreign winmm { + timeBeginPeriod :: proc(uPeriod: UINT) -> MMRESULT --- + timeEndPeriod :: proc(uPeriod: UINT) -> MMRESULT --- +} diff --git a/core/sys/windows/ws2_32.odin b/core/sys/windows/ws2_32.odin index 0cff5c2da..09af86bce 100644 --- a/core/sys/windows/ws2_32.odin +++ b/core/sys/windows/ws2_32.odin @@ -87,6 +87,19 @@ foreign ws2_32 { res: ^^ADDRINFOA, ) -> c_int --- freeaddrinfo :: proc(res: ^ADDRINFOA) --- + FreeAddrInfoExW :: proc(pAddrInfoEx: PADDRINFOEXW) --- + GetAddrInfoExW :: proc( + pName: PCWSTR, + pServiceName: PCWSTR, + dwNameSpace: DWORD, + lpNspId: LPGUID, + hints: ^ADDRINFOEXW, + ppResult: ^PADDRINFOEXW, + timeout: ^timeval, + lpOverlapped: LPOVERLAPPED, + lpCompletionRoutine: LPLOOKUPSERVICE_COMPLETION_ROUTINE, + lpHandle: LPHANDLE) -> INT --- + select :: proc( nfds: c_int, readfds: ^fd_set, diff --git a/core/testing/runner_other.odin b/core/testing/runner_other.odin index 3978a3c83..f3271d209 100644 --- a/core/testing/runner_other.odin +++ b/core/testing/runner_other.odin @@ -6,7 +6,7 @@ import "core:time" run_internal_test :: proc(t: ^T, it: Internal_Test) { // TODO(bill): Catch panics on other platforms - it.p(t); + it.p(t) } _fail_timeout :: proc(t: ^T, duration: time.Duration, loc := #caller_location) { diff --git a/core/testing/runner_windows.odin b/core/testing/runner_windows.odin index 560f23956..525eae685 100644 --- a/core/testing/runner_windows.odin +++ b/core/testing/runner_windows.odin @@ -21,7 +21,7 @@ sema_wait :: proc "contextless" (s: ^Sema) { win32.WaitOnAddress(&s.count, &original_count, size_of(original_count), win32.INFINITE) original_count = s.count } - if original_count == intrinsics.atomic_cxchg(&s.count, original_count-1, original_count) { + if original_count == intrinsics.atomic_compare_exchange_strong(&s.count, original_count-1, original_count) { return } } @@ -46,7 +46,7 @@ sema_wait_with_timeout :: proc "contextless" (s: ^Sema, duration: time.Duration) } original_count = s.count } - if original_count == intrinsics.atomic_cxchg(&s.count, original_count-1, original_count) { + if original_count == intrinsics.atomic_compare_exchange_strong(&s.count, original_count-1, original_count) { return true } } @@ -203,7 +203,7 @@ run_internal_test :: proc(t: ^T, it: Internal_Test) { thread.it.p(t) sema_post(&global_fail_timeout_semaphore) - thread_join_and_destroy(global_fail_timeout_thread) + if global_fail_timeout_thread != nil do thread_join_and_destroy(global_fail_timeout_thread) thread.success = true sema_post(&global_threaded_runner_semaphore) diff --git a/core/text/i18n/doc.odin b/core/text/i18n/doc.odin new file mode 100644 index 000000000..cff1ce11f --- /dev/null +++ b/core/text/i18n/doc.odin @@ -0,0 +1,111 @@ +//+ignore +package i18n + +/* + The i18n package is flexible and easy to use. + + It has one call to get a translation: `get`, which the user can alias into something like `T`. + + `get`, referred to as `T` here, has a few different signatures. + All of them will return the key if the entry can't be found in the active translation catalog. + + - `T(key)` returns the translation of `key`. + - `T(key, n)` returns a pluralized translation of `key` according to value `n`. + + - `T(section, key)` returns the translation of `key` in `section`. + - `T(section, key, n)` returns a pluralized translation of `key` in `section` according to value `n`. + + By default lookup take place in the global `i18n.ACTIVE` catalog for ease of use. + If you want to override which translation to use, for example in a language preview dialog, you can use the following: + + - `T(key, n, catalog)` returns the pluralized version of `key` from explictly supplied catalog. + - `T(section, key, n, catalog)` returns the pluralized version of `key` in `section` from explictly supplied catalog. + + If a catalog has translation contexts or sections, then ommitting it in the above calls looks up in section "". + + The default pluralization rule is n != 1, which is to say that passing n == 1 (or not passing n) returns the singular form. + Passing n != 1 returns plural form 1. + + Should a language not conform to this rule, you can pass a pluralizer procedure to the catalog parser. + This is a procedure that maps an integer to an integer, taking a value and returning which plural slot should be used. + + You can also assign it to a loaded catalog after parsing, of course. + + Some code examples follow. +*/ + +/* +```cpp +import "core:fmt" +import "core:text/i18n" + +T :: i18n.get + +mo :: proc() { + using fmt + + err: i18n.Error + + /* + Parse MO file and set it as the active translation so we can omit `get`'s "catalog" parameter. + */ + i18n.ACTIVE, err = i18n.parse_mo(#load("translations/nl_NL.mo")) + defer i18n.destroy() + + if err != .None { return } + + /* + These are in the .MO catalog. + */ + println("-----") + println(T("")) + println("-----") + println(T("There are 69,105 leaves here.")) + println("-----") + println(T("Hellope, World!")) + println("-----") + // We pass 1 into `T` to get the singular format string, then 1 again into printf. + printf(T("There is %d leaf.\n", 1), 1) + // We pass 42 into `T` to get the plural format string, then 42 again into printf. + printf(T("There is %d leaf.\n", 42), 42) + + /* + This isn't in the translation catalog, so the key is passed back untranslated. + */ + println("-----") + println(T("Come visit us on Discord!")) +} + +qt :: proc() { + using fmt + + err: i18n.Error + + /* + Parse QT file and set it as the active translation so we can omit `get`'s "catalog" parameter. + */ + i18n.ACTIVE, err = i18n.parse_qt(#load("translations/nl_NL-qt-ts.ts")) + defer i18n.destroy() + + if err != .None { + return + } + + /* + These are in the .TS catalog. As you can see they have sections. + */ + println("--- Page section ---") + println("Page:Text for translation =", T("Page", "Text for translation")) + println("-----") + println("Page:Also text to translate =", T("Page", "Also text to translate")) + println("-----") + println("--- installscript section ---") + println("installscript:99 bottles of beer on the wall =", T("installscript", "99 bottles of beer on the wall")) + println("-----") + println("--- apple_count section ---") + println("apple_count:%d apple(s) =") + println("\t 1 =", T("apple_count", "%d apple(s)", 1)) + println("\t 42 =", T("apple_count", "%d apple(s)", 42)) +} +``` +*/ \ No newline at end of file diff --git a/core/text/i18n/gettext.odin b/core/text/i18n/gettext.odin new file mode 100644 index 000000000..d99ec1c9b --- /dev/null +++ b/core/text/i18n/gettext.odin @@ -0,0 +1,167 @@ +package i18n +/* + A parser for GNU GetText .MO files. + + Copyright 2021-2022 Jeroen van Rijn . + Made available under Odin's BSD-3 license. + + A from-scratch implementation based after the specification found here: + https://www.gnu.org/software/gettext/manual/html_node/MO-Files.html + + Options are ignored as they're not applicable to this format. + They're part of the signature for consistency with other catalog formats. + + List of contributors: + Jeroen van Rijn: Initial implementation. +*/ +import "core:os" +import "core:strings" +import "core:bytes" + +parse_mo_from_bytes :: proc(data: []byte, options := DEFAULT_PARSE_OPTIONS, pluralizer: proc(int) -> int = nil, allocator := context.allocator) -> (translation: ^Translation, err: Error) { + context.allocator = allocator + /* + An MO file should have at least a 4-byte magic, 2 x 2 byte version info, + a 4-byte number of strings value, and 2 x 4-byte offsets. + */ + if len(data) < 20 { + return {}, .MO_File_Invalid + } + + /* + Check magic. Should be 0x950412de in native Endianness. + */ + native := true + magic := read_u32(data, native) or_return + + if magic != 0x950412de { + native = false + magic = read_u32(data, native) or_return + + if magic != 0x950412de { return {}, .MO_File_Invalid_Signature } + } + + /* + We can ignore version_minor at offset 6. + */ + version_major := read_u16(data[4:]) or_return + if version_major > 1 { return {}, .MO_File_Unsupported_Version } + + count := read_u32(data[ 8:]) or_return + original_offset := read_u32(data[12:]) or_return + translated_offset := read_u32(data[16:]) or_return + + if count == 0 { return {}, .Empty_Translation_Catalog } + + /* + Initalize Translation, interner and optional pluralizer. + */ + translation = new(Translation) + translation.pluralize = pluralizer + strings.intern_init(&translation.intern, allocator, allocator) + + // Gettext MO files only have one section. + translation.k_v[""] = {} + section := &translation.k_v[""] + + for n := u32(0); n < count; n += 1 { + /* + Grab string's original length and offset. + */ + offset := original_offset + 8 * n + if len(data) < int(offset + 8) { return translation, .MO_File_Invalid } + + o_length := read_u32(data[offset :], native) or_return + o_offset := read_u32(data[offset + 4:], native) or_return + + offset = translated_offset + 8 * n + if len(data) < int(offset + 8) { return translation, .MO_File_Invalid } + + t_length := read_u32(data[offset :], native) or_return + t_offset := read_u32(data[offset + 4:], native) or_return + + max_offset := int(max(o_offset + o_length + 1, t_offset + t_length + 1)) + if len(data) < max_offset { return translation, .Premature_EOF } + + key := data[o_offset:][:o_length] + val := data[t_offset:][:t_length] + + /* + Could be a pluralized string. + */ + zero := []byte{0} + + keys := bytes.split(key, zero) + vals := bytes.split(val, zero) + + if len(keys) != len(vals) || max(len(keys), len(vals)) > MAX_PLURALS { + return translation, .MO_File_Incorrect_Plural_Count + } + + for k in keys { + interned_key := strings.intern_get(&translation.intern, string(k)) + + interned_vals := make([]string, len(keys)) + last_val: string + + i := 0 + for v in vals { + interned_vals[i] = strings.intern_get(&translation.intern, string(v)) + last_val = interned_vals[i] + i += 1 + } + section[interned_key] = interned_vals + } + delete(vals) + delete(keys) + } + return +} + +parse_mo_file :: proc(filename: string, options := DEFAULT_PARSE_OPTIONS, pluralizer: proc(int) -> int = nil, allocator := context.allocator) -> (translation: ^Translation, err: Error) { + context.allocator = allocator + + data, data_ok := os.read_entire_file(filename) + defer delete(data) + + if !data_ok { return {}, .File_Error } + + return parse_mo_from_bytes(data, options, pluralizer, allocator) +} + +parse_mo :: proc { parse_mo_file, parse_mo_from_bytes } + +/* + Helpers. +*/ +read_u32 :: proc(data: []u8, native_endian := true) -> (res: u32, err: Error) { + if len(data) < size_of(u32) { return 0, .Premature_EOF } + + val := (^u32)(raw_data(data))^ + + if native_endian { + return val, .None + } else { + when ODIN_ENDIAN == .Little { + return u32(transmute(u32be)val), .None + } else { + return u32(transmute(u32le)val), .None + } + } +} + +read_u16 :: proc(data: []u8, native_endian := true) -> (res: u16, err: Error) { + if len(data) < size_of(u16) { return 0, .Premature_EOF } + + val := (^u16)(raw_data(data))^ + + if native_endian { + return val, .None + } else { + when ODIN_ENDIAN == .Little { + return u16(transmute(u16be)val), .None + } else { + return u16(transmute(u16le)val), .None + } + } +} \ No newline at end of file diff --git a/core/text/i18n/i18n.odin b/core/text/i18n/i18n.odin new file mode 100644 index 000000000..9d030db16 --- /dev/null +++ b/core/text/i18n/i18n.odin @@ -0,0 +1,182 @@ +package i18n +/* + Internationalization helpers. + + Copyright 2021-2022 Jeroen van Rijn . + Made available under Odin's BSD-3 license. + + List of contributors: + Jeroen van Rijn: Initial implementation. +*/ +import "core:strings" + +/* + TODO: + - Support for more translation catalog file formats. +*/ + +/* + Currently active catalog. +*/ +ACTIVE: ^Translation + +// Allow between 1 and 255 plural forms. Default: 10. +MAX_PLURALS :: min(max(#config(ODIN_i18N_MAX_PLURAL_FORMS, 10), 1), 255) + +/* + The main data structure. This can be generated from various different file formats, as long as we have a parser for them. +*/ + +Section :: map[string][]string + +Translation :: struct { + k_v: map[string]Section, // k_v[section][key][plural_form] = ... + intern: strings.Intern, + + pluralize: proc(number: int) -> int, +} + +Error :: enum { + /* + General return values. + */ + None = 0, + Empty_Translation_Catalog, + Duplicate_Key, + + /* + Couldn't find, open or read file. + */ + File_Error, + + /* + File too short. + */ + Premature_EOF, + + /* + GNU Gettext *.MO file errors. + */ + MO_File_Invalid_Signature, + MO_File_Unsupported_Version, + MO_File_Invalid, + MO_File_Incorrect_Plural_Count, + + /* + Qt Linguist *.TS file errors. + */ + TS_File_Parse_Error, + TS_File_Expected_Context, + TS_File_Expected_Context_Name, + TS_File_Expected_Source, + TS_File_Expected_Translation, + TS_File_Expected_NumerusForm, + +} + +Parse_Options :: struct { + merge_sections: bool, +} + +DEFAULT_PARSE_OPTIONS :: Parse_Options{ + merge_sections = false, +} + +/* + Several ways to use: + - get(key), which defaults to the singular form and i18n.ACTIVE catalog, or + - get(key, number), which returns the appropriate plural from the active catalog, or + - get(key, number, catalog) to grab text from a specific one. +*/ +get_single_section :: proc(key: string, number := 0, catalog: ^Translation = ACTIVE) -> (value: string) { + /* + A lot of languages use singular for 1 item and plural for 0 or more than 1 items. This is our default pluralize rule. + */ + plural := 1 if number != 1 else 0 + + if catalog.pluralize != nil { + plural = catalog.pluralize(number) + } + return get_by_slot(key, plural, catalog) +} + +/* + Several ways to use: + - get(section, key), which defaults to the singular form and i18n.ACTIVE catalog, or + - get(section, key, number), which returns the appropriate plural from the active catalog, or + - get(section, key, number, catalog) to grab text from a specific one. +*/ +get_by_section :: proc(section, key: string, number := 0, catalog: ^Translation = ACTIVE) -> (value: string) { + /* + A lot of languages use singular for 1 item and plural for 0 or more than 1 items. This is our default pluralize rule. + */ + plural := 1 if number != 1 else 0 + + if catalog.pluralize != nil { + plural = catalog.pluralize(number) + } + return get_by_slot(section, key, plural, catalog) +} +get :: proc{get_single_section, get_by_section} + +/* + Several ways to use: + - get_by_slot(key), which defaults to the singular form and i18n.ACTIVE catalog, or + - get_by_slot(key, slot), which returns the requested plural from the active catalog, or + - get_by_slot(key, slot, catalog) to grab text from a specific one. + + If a file format parser doesn't (yet) support plural slots, each of the slots will point at the same string. +*/ +get_by_slot_single_section :: proc(key: string, slot := 0, catalog: ^Translation = ACTIVE) -> (value: string) { + return get_by_slot_by_section("", key, slot, catalog) +} + +/* + Several ways to use: + - get_by_slot(key), which defaults to the singular form and i18n.ACTIVE catalog, or + - get_by_slot(key, slot), which returns the requested plural from the active catalog, or + - get_by_slot(key, slot, catalog) to grab text from a specific one. + + If a file format parser doesn't (yet) support plural slots, each of the slots will point at the same string. +*/ +get_by_slot_by_section :: proc(section, key: string, slot := 0, catalog: ^Translation = ACTIVE) -> (value: string) { + if catalog == nil || section not_in catalog.k_v { + /* + Return the key if the catalog catalog hasn't been initialized yet, or the section is not present. + */ + return key + } + + /* + Return the translation from the requested slot if this key is known, else return the key. + */ + if translations, ok := catalog.k_v[section][key]; ok { + plural := min(max(0, slot), len(catalog.k_v[section][key]) - 1) + return translations[plural] + } + return key +} +get_by_slot :: proc{get_by_slot_single_section, get_by_slot_by_section} + +/* + Same for destroy: + - destroy(), to clean up the currently active catalog catalog i18n.ACTIVE + - destroy(catalog), to clean up a specific catalog. +*/ +destroy :: proc(catalog: ^Translation = ACTIVE, allocator := context.allocator) { + context.allocator = allocator + + if catalog == nil { + return + } + + for section in &catalog.k_v { + for key in &catalog.k_v[section] { + delete(catalog.k_v[section][key]) + } + delete(catalog.k_v[section]) + } + delete(catalog.k_v) + strings.intern_destroy(&catalog.intern) + free(catalog) +} \ No newline at end of file diff --git a/core/text/i18n/qt_linguist.odin b/core/text/i18n/qt_linguist.odin new file mode 100644 index 000000000..036a89eeb --- /dev/null +++ b/core/text/i18n/qt_linguist.odin @@ -0,0 +1,156 @@ +package i18n +/* + A parser for Qt Linguist TS files. + + Copyright 2022 Jeroen van Rijn . + Made available under Odin's BSD-3 license. + + A from-scratch implementation based after the specification found here: + https://doc.qt.io/qt-5/linguist-ts-file-format.html + + List of contributors: + Jeroen van Rijn: Initial implementation. +*/ +import "core:os" +import "core:encoding/xml" +import "core:strings" + +TS_XML_Options := xml.Options{ + flags = { + .Input_May_Be_Modified, + .Must_Have_Prolog, + .Must_Have_DocType, + .Ignore_Unsupported, + .Unbox_CDATA, + .Decode_SGML_Entities, + }, + expected_doctype = "TS", +} + +parse_qt_linguist_from_bytes :: proc(data: []byte, options := DEFAULT_PARSE_OPTIONS, pluralizer: proc(int) -> int = nil, allocator := context.allocator) -> (translation: ^Translation, err: Error) { + context.allocator = allocator + + ts, xml_err := xml.parse(data, TS_XML_Options) + defer xml.destroy(ts) + + if xml_err != .None || ts.element_count < 1 || ts.elements[0].ident != "TS" || len(ts.elements[0].children) == 0 { + return nil, .TS_File_Parse_Error + } + + /* + Initalize Translation, interner and optional pluralizer. + */ + translation = new(Translation) + translation.pluralize = pluralizer + strings.intern_init(&translation.intern, allocator, allocator) + + section: ^Section + + for child_id in ts.elements[0].children { + // These should be s. + child := ts.elements[child_id] + if child.ident != "context" { + return translation, .TS_File_Expected_Context + } + + // Find section name. + section_name_id, section_name_found := xml.find_child_by_ident(ts, child_id, "name") + if !section_name_found { + return translation, .TS_File_Expected_Context_Name, + } + + section_name := strings.intern_get(&translation.intern, "") + if !options.merge_sections { + section_name = strings.intern_get(&translation.intern, ts.elements[section_name_id].value) + } + + if section_name not_in translation.k_v { + translation.k_v[section_name] = {} + } + section = &translation.k_v[section_name] + + // Find messages in section. + nth: int + for { + message_id, message_found := xml.find_child_by_ident(ts, child_id, "message", nth) + if !message_found { + break + } + + numerus_tag, _ := xml.find_attribute_val_by_key(ts, message_id, "numerus") + has_plurals := numerus_tag == "yes" + + // We must have a = key + source_id, source_found := xml.find_child_by_ident(ts, message_id, "source") + if !source_found { + return translation, .TS_File_Expected_Source + } + + // We must have a + translation_id, translation_found := xml.find_child_by_ident(ts, message_id, "translation") + if !translation_found { + return translation, .TS_File_Expected_Translation + } + + source := strings.intern_get(&translation.intern, ts.elements[source_id].value) + xlat := strings.intern_get(&translation.intern, ts.elements[translation_id].value) + + if source in section { + return translation, .Duplicate_Key + } + + if has_plurals { + if xlat != "" { + return translation, .TS_File_Expected_NumerusForm + } + + num_plurals: int + for { + numerus_id, numerus_found := xml.find_child_by_ident(ts, translation_id, "numerusform", num_plurals) + if !numerus_found { + break + } + num_plurals += 1 + } + + if num_plurals < 2 { + return translation, .TS_File_Expected_NumerusForm + } + section[source] = make([]string, num_plurals) + + num_plurals = 0 + for { + numerus_id, numerus_found := xml.find_child_by_ident(ts, translation_id, "numerusform", num_plurals) + if !numerus_found { + break + } + numerus := strings.intern_get(&translation.intern, ts.elements[numerus_id].value) + section[source][num_plurals] = numerus + + num_plurals += 1 + } + } else { + // Single translation + section[source] = make([]string, 1) + section[source][0] = xlat + } + + nth += 1 + } + } + + return +} + +parse_qt_linguist_file :: proc(filename: string, options := DEFAULT_PARSE_OPTIONS, pluralizer: proc(int) -> int = nil, allocator := context.allocator) -> (translation: ^Translation, err: Error) { + context.allocator = allocator + + data, data_ok := os.read_entire_file(filename) + defer delete(data) + + if !data_ok { return {}, .File_Error } + + return parse_qt_linguist_from_bytes(data, options, pluralizer, allocator) +} + +parse_qt :: proc { parse_qt_linguist_file, parse_qt_linguist_from_bytes } \ No newline at end of file diff --git a/core/thread/thread.odin b/core/thread/thread.odin index d1b95a2fd..90230ae75 100644 --- a/core/thread/thread.odin +++ b/core/thread/thread.odin @@ -53,7 +53,7 @@ join :: proc(thread: ^Thread) { } -join_mulitple :: proc(threads: ..^Thread) { +join_multiple :: proc(threads: ..^Thread) { _join_multiple(..threads) } diff --git a/core/thread/thread_pool.odin b/core/thread/thread_pool.odin index 37ee4fa98..820de8ad4 100644 --- a/core/thread/thread_pool.odin +++ b/core/thread/thread_pool.odin @@ -1,67 +1,76 @@ package thread +/* + thread.Pool + Copyright 2022 eisbehr + Made available under Odin's BSD-3 license. +*/ + import "core:intrinsics" import "core:sync" import "core:mem" -Task_Status :: enum i32 { - Ready, - Busy, - Waiting, - Term, -} - -Task_Proc :: #type proc(task: ^Task) +Task_Proc :: #type proc(task: Task) Task :: struct { - procedure: Task_Proc, - data: rawptr, + procedure: Task_Proc, + data: rawptr, user_index: int, + allocator: mem.Allocator, } -Task_Id :: distinct i32 -INVALID_TASK_ID :: Task_Id(-1) - - +// Do not access the pool's members directly while the pool threads are running, +// since they use different kinds of locking and mutual exclusion devices. +// Careless access can and will lead to nasty bugs. Once initialized, the +// pool's memory address is not allowed to change until it is destroyed. Pool :: struct { - allocator: mem.Allocator, - mutex: sync.Mutex, - sem_available: sync.Semaphore, - processing_task_count: int, // atomic - is_running: bool, + allocator: mem.Allocator, + mutex: sync.Mutex, + sem_available: sync.Sema, + + // the following values are atomic + num_waiting: int, + num_in_processing: int, + num_outstanding: int, // num_waiting + num_in_processing + num_done: int, + // end of atomics + + is_running: bool, threads: []^Thread, - tasks: [dynamic]Task, + + tasks: [dynamic]Task, + tasks_done: [dynamic]Task, } -pool_init :: proc(pool: ^Pool, thread_count: int, allocator := context.allocator) { - worker_thread_internal :: proc(t: ^Thread) { - pool := (^Pool)(t.data) - - for pool.is_running { - sync.semaphore_wait_for(&pool.sem_available) - - if task, ok := pool_try_and_pop_task(pool); ok { - pool_do_work(pool, &task) - } - } - - sync.semaphore_post(&pool.sem_available, 1) - } - - +// Once initialized, the pool's memory address is not allowed to change until +// it is destroyed. +// +// The thread pool requires an allocator which it either owns, or which is thread safe. +pool_init :: proc(pool: ^Pool, allocator: mem.Allocator, thread_count: int) { context.allocator = allocator pool.allocator = allocator - pool.tasks = make([dynamic]Task) - pool.threads = make([]^Thread, thread_count) + pool.tasks = make([dynamic]Task) + pool.tasks_done = make([dynamic]Task) + pool.threads = make([]^Thread, max(thread_count, 1)) - sync.mutex_init(&pool.mutex) - sync.semaphore_init(&pool.sem_available) pool.is_running = true for _, i in pool.threads { - t := create(worker_thread_internal) + t := create(proc(t: ^Thread) { + pool := (^Pool)(t.data) + + for intrinsics.atomic_load(&pool.is_running) { + sync.wait(&pool.sem_available) + + if task, ok := pool_pop_waiting(pool); ok { + pool_do_work(pool, task) + } + } + + sync.post(&pool.sem_available, 1) + }) t.user_index = i t.data = pool pool.threads[i] = t @@ -70,15 +79,13 @@ pool_init :: proc(pool: ^Pool, thread_count: int, allocator := context.allocator pool_destroy :: proc(pool: ^Pool) { delete(pool.tasks) + delete(pool.tasks_done) - for thread in &pool.threads { - destroy(thread) + for t in &pool.threads { + destroy(t) } delete(pool.threads, pool.allocator) - - sync.mutex_destroy(&pool.mutex) - sync.semaphore_destroy(&pool.sem_available) } pool_start :: proc(pool: ^Pool) { @@ -87,65 +94,134 @@ pool_start :: proc(pool: ^Pool) { } } +// Finish tasks that have already started processing, then shut down all pool +// threads. Might leave over waiting tasks, any memory allocated for the +// user data of those tasks will not be freed. pool_join :: proc(pool: ^Pool) { - pool.is_running = false - - sync.semaphore_post(&pool.sem_available, len(pool.threads)) + intrinsics.atomic_store(&pool.is_running, false) + sync.post(&pool.sem_available, len(pool.threads)) yield() - for t in pool.threads { - join(t) - } -} - -pool_add_task :: proc(pool: ^Pool, procedure: Task_Proc, data: rawptr, user_index: int = 0) { - sync.mutex_lock(&pool.mutex) - defer sync.mutex_unlock(&pool.mutex) - - task: Task - task.procedure = procedure - task.data = data - task.user_index = user_index - - append(&pool.tasks, task) - sync.semaphore_post(&pool.sem_available, 1) -} - -pool_try_and_pop_task :: proc(pool: ^Pool) -> (task: Task, got_task: bool = false) { - if sync.mutex_try_lock(&pool.mutex) { - if len(pool.tasks) != 0 { - intrinsics.atomic_add(&pool.processing_task_count, 1) - task = pop_front(&pool.tasks) - got_task = true +started_count: int + for started_count < len(pool.threads) { + started_count = 0 + for t in pool.threads { + if .Started in t.flags { + started_count += 1 + if .Joined not_in t.flags { + join(t) + } + } } - sync.mutex_unlock(&pool.mutex) } +} + +// Add a task to the thread pool. +// +// Tasks can be added from any thread, not just the thread that created +// the thread pool. You can even add tasks from inside other tasks. +// +// Each task also needs an allocator which it either owns, or which is thread +// safe. +pool_add_task :: proc(pool: ^Pool, allocator: mem.Allocator, procedure: Task_Proc, data: rawptr, user_index: int = 0) { + sync.guard(&pool.mutex) + + append(&pool.tasks, Task{ + procedure = procedure, + data = data, + user_index = user_index, + allocator = allocator, + }) + intrinsics.atomic_add(&pool.num_waiting, 1) + intrinsics.atomic_add(&pool.num_outstanding, 1) + sync.post(&pool.sem_available, 1) +} + +// Number of tasks waiting to be processed. Only informational, mostly for +// debugging. Don't rely on this value being consistent with other num_* +// values. +pool_num_waiting :: #force_inline proc(pool: ^Pool) -> int { + return intrinsics.atomic_load(&pool.num_waiting) +} + +// Number of tasks currently being processed. Only informational, mostly for +// debugging. Don't rely on this value being consistent with other num_* +// values. +pool_num_in_processing :: #force_inline proc(pool: ^Pool) -> int { + return intrinsics.atomic_load(&pool.num_in_processing) +} + +// Outstanding tasks are all tasks that are not done, that is, tasks that are +// waiting, as well as tasks that are currently being processed. Only +// informational, mostly for debugging. Don't rely on this value being +// consistent with other num_* values. +pool_num_outstanding :: #force_inline proc(pool: ^Pool) -> int { + return intrinsics.atomic_load(&pool.num_outstanding) +} + +// Number of tasks which are done processing. Only informational, mostly for +// debugging. Don't rely on this value being consistent with other num_* +// values. +pool_num_done :: #force_inline proc(pool: ^Pool) -> int { + return intrinsics.atomic_load(&pool.num_done) +} + +// If tasks are only being added from one thread, and this procedure is being +// called from that same thread, it will reliably tell if the thread pool is +// empty or not. Empty in this case means there are no tasks waiting, being +// processed, or _done_. +pool_is_empty :: #force_inline proc(pool: ^Pool) -> bool { + return pool_num_outstanding(pool) == 0 && pool_num_done(pool) == 0 +} + +// Mostly for internal use. +pool_pop_waiting :: proc(pool: ^Pool) -> (task: Task, got_task: bool) { + sync.guard(&pool.mutex) + + if len(pool.tasks) != 0 { + intrinsics.atomic_sub(&pool.num_waiting, 1) + intrinsics.atomic_add(&pool.num_in_processing, 1) + task = pop_front(&pool.tasks) + got_task = true + } + return } +// Use this to take out finished tasks. +pool_pop_done :: proc(pool: ^Pool) -> (task: Task, got_task: bool) { + sync.guard(&pool.mutex) -pool_do_work :: proc(pool: ^Pool, task: ^Task) { - task.procedure(task) - intrinsics.atomic_sub(&pool.processing_task_count, 1) -} - - -pool_wait_and_process :: proc(pool: ^Pool) { - for len(pool.tasks) != 0 || intrinsics.atomic_load(&pool.processing_task_count) != 0 { - if task, ok := pool_try_and_pop_task(pool); ok { - pool_do_work(pool, &task) - } - - // Safety kick - if len(pool.tasks) != 0 && intrinsics.atomic_load(&pool.processing_task_count) == 0 { - sync.mutex_lock(&pool.mutex) - sync.semaphore_post(&pool.sem_available, len(pool.tasks)) - sync.mutex_unlock(&pool.mutex) - } - - yield() + if len(pool.tasks_done) != 0 { + task = pop_front(&pool.tasks_done) + got_task = true + intrinsics.atomic_sub(&pool.num_done, 1) } + return +} + +// Mostly for internal use. +pool_do_work :: proc(pool: ^Pool, task: Task) { + { + context.allocator = task.allocator + task.procedure(task) + } + + sync.guard(&pool.mutex) + + append(&pool.tasks_done, task) + intrinsics.atomic_add(&pool.num_done, 1) + intrinsics.atomic_sub(&pool.num_outstanding, 1) + intrinsics.atomic_sub(&pool.num_in_processing, 1) +} + +// Process the rest of the tasks, also use this thread for processing, then join +// all the pool threads. +pool_finish :: proc(pool: ^Pool) { + for task in pool_pop_waiting(pool) { + pool_do_work(pool, task) + } pool_join(pool) } diff --git a/core/thread/thread_unix.odin b/core/thread/thread_unix.odin index cee278c7a..8c7058f17 100644 --- a/core/thread/thread_unix.odin +++ b/core/thread/thread_unix.odin @@ -1,4 +1,4 @@ -// +build linux, darwin, freebsd +// +build linux, darwin, freebsd, openbsd // +private package thread @@ -7,30 +7,21 @@ import "core:intrinsics" import "core:sync" import "core:sys/unix" +CAS :: intrinsics.atomic_compare_exchange_strong + +Thread_State :: enum u8 { + Started, + Joined, + Done, +} + // NOTE(tetra): Aligned here because of core/unix/pthread_linux.odin/pthread_t. // Also see core/sys/darwin/mach_darwin.odin/semaphore_t. Thread_Os_Specific :: struct #align 16 { unix_thread: unix.pthread_t, // NOTE: very large on Darwin, small on Linux. - - // NOTE: pthread has a proc to query this, but it is marked - // as non-portable ("np") so we do this instead. - done: bool, - - // since libpthread doesn't seem to have a way to create a thread - // in a suspended state, we have it wait on this gate, which we - // signal to start it. - // destroyed after thread is started. - start_gate: sync.Condition, - start_mutex: sync.Mutex, - - // if true, the thread has been started and the start_gate has been destroyed. - started: bool, - - // NOTE: with pthreads, it is undefined behavior for multiple threads - // to call join on the same thread at the same time. - // this value is atomically updated to detect this. - // See the comment in `join`. - already_joined: bool, + cond: sync.Cond, + mutex: sync.Mutex, + flags: bit_set[Thread_State; u8], } // // Creates a thread which will run the given procedure. @@ -38,26 +29,44 @@ Thread_Os_Specific :: struct #align 16 { // _create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^Thread { __linux_thread_entry_proc :: proc "c" (t: rawptr) -> rawptr { - context = runtime.default_context() t := (^Thread)(t) - sync.condition_wait_for(&t.start_gate) - sync.condition_destroy(&t.start_gate) - sync.mutex_destroy(&t.start_mutex) - t.start_gate = {} - t.start_mutex = {} - context = t.init_context.? or_else runtime.default_context() - + when ODIN_OS != .Darwin { + // We need to give the thread a moment to start up before we enable cancellation. + can_set_thread_cancel_state := unix.pthread_setcancelstate(unix.PTHREAD_CANCEL_DISABLE, nil) == 0 + } + + context = runtime.default_context() + + sync.lock(&t.mutex) + t.id = sync.current_thread_id() - t.procedure(t) - if t.init_context == nil { - if context.temp_allocator.data == &runtime.global_default_temp_allocator_data { - runtime.default_temp_allocator_destroy(auto_cast context.temp_allocator.data) + for (.Started not_in t.flags) { + sync.wait(&t.cond, &t.mutex) + } + + init_context := t.init_context + context = init_context.? or_else runtime.default_context() + + when ODIN_OS != .Darwin { + // Enable thread's cancelability. + if can_set_thread_cancel_state { + unix.pthread_setcanceltype (unix.PTHREAD_CANCEL_ASYNCHRONOUS, nil) + unix.pthread_setcancelstate(unix.PTHREAD_CANCEL_DISABLE, nil) } } - intrinsics.atomic_store(&t.done, true) + t.procedure(t) + + intrinsics.atomic_store(&t.flags, t.flags + { .Done }) + + sync.unlock(&t.mutex) + + if init_context == nil && context.temp_allocator.data == &runtime.global_default_temp_allocator_data { + runtime.default_temp_allocator_destroy(auto_cast context.temp_allocator.data) + } + return nil } @@ -76,9 +85,6 @@ _create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^ return nil } thread.creation_allocator = context.allocator - - sync.mutex_init(&thread.start_mutex) - sync.condition_init(&thread.start_gate, &thread.start_mutex) // Set thread priority. policy: i32 @@ -97,65 +103,42 @@ _create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^ res = unix.pthread_attr_setschedparam(&attrs, ¶ms) assert(res == 0) + thread.procedure = procedure if unix.pthread_create(&thread.unix_thread, &attrs, __linux_thread_entry_proc, thread) != 0 { free(thread, thread.creation_allocator) - - sync.condition_destroy(&thread.start_gate) - sync.mutex_destroy(&thread.start_mutex) return nil } - thread.procedure = procedure - return thread } _start :: proc(t: ^Thread) { - if intrinsics.atomic_xchg(&t.started, true) { - return - } - sync.condition_signal(&t.start_gate) + // sync.guard(&t.mutex) + t.flags += { .Started } + sync.signal(&t.cond) } _is_done :: proc(t: ^Thread) -> bool { - return intrinsics.atomic_load(&t.done) + return .Done in intrinsics.atomic_load(&t.flags) } _join :: proc(t: ^Thread) { + // sync.guard(&t.mutex) + if unix.pthread_equal(unix.pthread_self(), t.unix_thread) { return } - // if unix.pthread_self().x == t.unix_thread.x do return; - // NOTE(tetra): It's apparently UB for multiple threads to join the same thread - // at the same time. - // If someone else already did, spin until the thread dies. - // See note on `already_joined` field. - // TODO(tetra): I'm not sure if we should do this, or panic, since I'm not - // sure it makes sense to need to join from multiple threads? - if intrinsics.atomic_xchg(&t.already_joined, true) { - for { - if intrinsics.atomic_load(&t.done) { - return - } - intrinsics.cpu_relax() - } - } + // Preserve other flags besides `.Joined`, like `.Started`. + unjoined := intrinsics.atomic_load(&t.flags) - {.Joined} + joined := unjoined + {.Joined} - // NOTE(tetra): If we're already dead, don't bother calling to pthread_join as that - // will just return 3 (ESRCH). - // We do this instead because I don't know if there is a danger - // that you may join a different thread from the one you called join on, - // if the thread handle is reused. - if intrinsics.atomic_load(&t.done) { + // Try to set `t.flags` from unjoined to joined. If it returns joined, + // it means the previous value had that flag set and we can return. + if res, ok := CAS(&t.flags, unjoined, joined); res == joined && !ok { return } - - ret_val: rawptr - _ = unix.pthread_join(t.unix_thread, &ret_val) - if !intrinsics.atomic_load(&t.done) { - panic("thread not done after join") - } + unix.pthread_join(t.unix_thread, nil) } _join_multiple :: proc(threads: ..^Thread) { @@ -164,18 +147,17 @@ _join_multiple :: proc(threads: ..^Thread) { } } - _destroy :: proc(t: ^Thread) { _join(t) - sync.condition_destroy(&t.start_gate) - sync.mutex_destroy(&t.start_mutex) t.unix_thread = {} free(t, t.creation_allocator) } - _terminate :: proc(t: ^Thread, exit_code: int) { - // TODO(bill) + // `pthread_cancel` is unreliable on Darwin for unknown reasons. + when ODIN_OS != .Darwin { + unix.pthread_cancel(t.unix_thread) + } } _yield :: proc() { diff --git a/core/thread/thread_windows.odin b/core/thread/thread_windows.odin index 428e241d0..68382444c 100644 --- a/core/thread/thread_windows.odin +++ b/core/thread/thread_windows.odin @@ -3,13 +3,21 @@ package thread import "core:runtime" -import sync "core:sync/sync2" +import "core:intrinsics" +import "core:sync" import win32 "core:sys/windows" +Thread_State :: enum u8 { + Started, + Joined, + Done, +} + Thread_Os_Specific :: struct { win32_thread: win32.HANDLE, win32_thread_id: win32.DWORD, - done: bool, // see note in `is_done` + mutex: sync.Mutex, + flags: bit_set[Thread_State; u8], } _thread_priority_map := [Thread_Priority]i32{ @@ -26,15 +34,16 @@ _create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^ context = t.init_context.? or_else runtime.default_context() t.id = sync.current_thread_id() + t.procedure(t) + intrinsics.atomic_store(&t.flags, t.flags + {.Done}) + if t.init_context == nil { if context.temp_allocator.data == &runtime.global_default_temp_allocator_data { runtime.default_temp_allocator_destroy(auto_cast context.temp_allocator.data) } } - - sync.atomic_store(&t.done, true) return 0 } @@ -61,23 +70,31 @@ _create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^ return thread } -_start :: proc(thread: ^Thread) { - win32.ResumeThread(thread.win32_thread) +_start :: proc(t: ^Thread) { + sync.guard(&t.mutex) + t.flags += {.Started} + win32.ResumeThread(t.win32_thread) } -_is_done :: proc(using thread: ^Thread) -> bool { +_is_done :: proc(t: ^Thread) -> bool { // NOTE(tetra, 2019-10-31): Apparently using wait_for_single_object and // checking if it didn't time out immediately, is not good enough, // so we do it this way instead. - return sync.atomic_load(&done) + return .Done in sync.atomic_load(&t.flags) } -_join :: proc(using thread: ^Thread) { - if win32_thread != win32.INVALID_HANDLE { - win32.WaitForSingleObject(win32_thread, win32.INFINITE) - win32.CloseHandle(win32_thread) - win32_thread = win32.INVALID_HANDLE +_join :: proc(t: ^Thread) { + sync.guard(&t.mutex) + + if .Joined in t.flags || t.win32_thread == win32.INVALID_HANDLE { + return } + + win32.WaitForSingleObject(t.win32_thread, win32.INFINITE) + win32.CloseHandle(t.win32_thread) + t.win32_thread = win32.INVALID_HANDLE + + t.flags += {.Joined} } _join_multiple :: proc(threads: ..^Thread) { diff --git a/core/time/perf.odin b/core/time/perf.odin index f49b57f5b..53406646f 100644 --- a/core/time/perf.odin +++ b/core/time/perf.odin @@ -1,6 +1,6 @@ package time -import "core:mem" +import "core:runtime" Tick :: struct { _nsec: i64, // relative amount @@ -50,9 +50,9 @@ Benchmark_Error :: enum { } Benchmark_Options :: struct { - setup: #type proc(options: ^Benchmark_Options, allocator: mem.Allocator) -> (err: Benchmark_Error), - bench: #type proc(options: ^Benchmark_Options, allocator: mem.Allocator) -> (err: Benchmark_Error), - teardown: #type proc(options: ^Benchmark_Options, allocator: mem.Allocator) -> (err: Benchmark_Error), + setup: #type proc(options: ^Benchmark_Options, allocator: runtime.Allocator) -> (err: Benchmark_Error), + bench: #type proc(options: ^Benchmark_Options, allocator: runtime.Allocator) -> (err: Benchmark_Error), + teardown: #type proc(options: ^Benchmark_Options, allocator: runtime.Allocator) -> (err: Benchmark_Error), rounds: int, bytes: int, diff --git a/core/time/time.odin b/core/time/time.odin index fddb09d85..6c6e47dc0 100644 --- a/core/time/time.odin +++ b/core/time/time.odin @@ -14,6 +14,8 @@ Hour :: 60 * Minute MIN_DURATION :: Duration(-1 << 63) MAX_DURATION :: Duration(1<<63 - 1) +IS_SUPPORTED :: _IS_SUPPORTED + Time :: struct { _nsec: i64, // zero is 1970-01-01 00:00:00 } @@ -49,6 +51,14 @@ Stopwatch :: struct { _accumulation: Duration, } +now :: proc "contextless" () -> Time { + return _now() +} + +sleep :: proc "contextless" (d: Duration) { + _sleep(d) +} + stopwatch_start :: proc(using stopwatch: ^Stopwatch) { if !running { _start_time = tick_now() @@ -82,36 +92,36 @@ since :: proc(start: Time) -> Duration { return diff(start, now()) } -duration_nanoseconds :: proc(d: Duration) -> i64 { +duration_nanoseconds :: proc "contextless" (d: Duration) -> i64 { return i64(d) } -duration_microseconds :: proc(d: Duration) -> f64 { +duration_microseconds :: proc "contextless" (d: Duration) -> f64 { return duration_seconds(d) * 1e6 } -duration_milliseconds :: proc(d: Duration) -> f64 { +duration_milliseconds :: proc "contextless" (d: Duration) -> f64 { return duration_seconds(d) * 1e3 } -duration_seconds :: proc(d: Duration) -> f64 { +duration_seconds :: proc "contextless" (d: Duration) -> f64 { sec := d / Second nsec := d % Second return f64(sec) + f64(nsec)/1e9 } -duration_minutes :: proc(d: Duration) -> f64 { +duration_minutes :: proc "contextless" (d: Duration) -> f64 { min := d / Minute nsec := d % Minute return f64(min) + f64(nsec)/(60*1e9) } -duration_hours :: proc(d: Duration) -> f64 { +duration_hours :: proc "contextless" (d: Duration) -> f64 { hour := d / Hour nsec := d % Hour return f64(hour) + f64(nsec)/(60*60*1e9) } -_less_than_half :: #force_inline proc(x, y: Duration) -> bool { - return u64(x)+u64(x) < u64(y) -} - duration_round :: proc(d, m: Duration) -> Duration { + _less_than_half :: #force_inline proc(x, y: Duration) -> bool { + return u64(x)+u64(x) < u64(y) + } + if m <= 0 { return d } @@ -201,10 +211,12 @@ unix :: proc(sec: i64, nsec: i64) -> Time { return Time{(sec*1e9 + nsec) + UNIX_TO_INTERNAL} } +to_unix_seconds :: time_to_unix time_to_unix :: proc(t: Time) -> i64 { return t._nsec/1e9 } +to_unix_nanoseconds :: time_to_unix_nano time_to_unix_nano :: proc(t: Time) -> i64 { return t._nsec } @@ -213,6 +225,44 @@ time_add :: proc(t: Time, d: Duration) -> Time { return Time{t._nsec + i64(d)} } +// Accurate sleep borrowed from: https://blat-blatnik.github.io/computerBear/making-accurate-sleep-function/ +// +// Accuracy seems to be pretty good out of the box on Linux, to within around 4µs worst case. +// On Windows it depends but is comparable with regular sleep in the worst case. +// To get the same kind of accuracy as on Linux, have your program call `win32.time_begin_period(1)` to +// tell Windows to use a more accurate timer for your process. +accurate_sleep :: proc(d: Duration) { + to_sleep, estimate, mean, m2, count: Duration + + to_sleep = d + estimate = 5 * Millisecond + mean = 5 * Millisecond + count = 1 + + for to_sleep > estimate { + start := tick_now() + sleep(1 * Millisecond) + + observed := tick_since(start) + to_sleep -= observed + + count += 1 + + delta := observed - mean + mean += delta / count + m2 += delta * (observed - mean) + stddev := intrinsics.sqrt(f64(m2) / f64(count - 1)) + estimate = mean + Duration(stddev) + } + + start := tick_now() + for to_sleep > tick_since(start) { + // prevent the spinlock from taking the thread hostage, still accurate enough + _yield() + // NOTE: it might be possible that it yields for too long, in that case it should spinlock freely for a while + // TODO: needs actual testing done to check if that's the case + } +} ABSOLUTE_ZERO_YEAR :: i64(-292277022399) // Day is chosen so that 2001-01-01 is Monday in the calculations ABSOLUTE_TO_INTERNAL :: i64(-9223371966579724800) // i64((ABSOLUTE_ZERO_YEAR - 1) * 365.2425 * SECONDS_PER_DAY); @@ -227,20 +277,24 @@ INTERNAL_TO_WALL :: -WALL_TO_INTERNAL UNIX_TO_ABSOLUTE :: UNIX_TO_INTERNAL + INTERNAL_TO_ABSOLUTE ABSOLUTE_TO_UNIX :: -UNIX_TO_ABSOLUTE -_is_leap_year :: proc(year: int) -> bool { - return year%4 == 0 && (year%100 != 0 || year%400 == 0) -} +@(private) _date :: proc(t: Time, full: bool) -> (year: int, month: Month, day: int, yday: int) { year, month, day, yday = _abs_date(_time_abs(t), full) return } +@(private) _time_abs :: proc(t: Time) -> u64 { return u64(t._nsec/1e9 + UNIX_TO_ABSOLUTE) } +@(private) _abs_date :: proc(abs: u64, full: bool) -> (year: int, month: Month, day: int, yday: int) { + _is_leap_year :: proc(year: int) -> bool { + return year%4 == 0 && (year%100 != 0 || year%400 == 0) + } + d := abs / SECONDS_PER_DAY // 400 year cycles diff --git a/core/time/time_essence.odin b/core/time/time_essence.odin index 72efe9f8f..b7bc616d8 100644 --- a/core/time/time_essence.odin +++ b/core/time/time_essence.odin @@ -1,18 +1,19 @@ +//+private package time import "core:sys/es" -IS_SUPPORTED :: true; +_IS_SUPPORTED :: true -now :: proc "contextless" () -> Time { +_now :: proc "contextless" () -> Time { // TODO Replace once there's a proper time API. - return Time{_nsec = i64(es.TimeStampMs() * 1e6)}; + return Time{_nsec = i64(es.TimeStampMs() * 1e6)} } -sleep :: proc "contextless" (d: Duration) { - es.Sleep(u64(d/Millisecond)); +_sleep :: proc "contextless" (d: Duration) { + es.Sleep(u64(d/Millisecond)) } _tick_now :: proc "contextless" () -> Tick { - return Tick{_nsec = i64(es.TimeStampMs() * 1e6)}; + return Tick{_nsec = i64(es.TimeStampMs() * 1e6)} } diff --git a/core/time/time_freestanding.odin b/core/time/time_freestanding.odin index 17a21d79c..7c67cc5e8 100644 --- a/core/time/time_freestanding.odin +++ b/core/time/time_freestanding.odin @@ -1,16 +1,19 @@ +//+private //+build freestanding package time -IS_SUPPORTED :: false +_IS_SUPPORTED :: false -now :: proc() -> Time { +_now :: proc "contextless" () -> Time { return {} } -sleep :: proc(d: Duration) { +_sleep :: proc "contextless" (d: Duration) { } _tick_now :: proc "contextless" () -> Tick { return {} } +_yield :: proc "contextless" () { +} diff --git a/core/time/time_js.odin b/core/time/time_js.odin index 1b801cdad..226f921f9 100644 --- a/core/time/time_js.odin +++ b/core/time/time_js.odin @@ -1,21 +1,33 @@ +//+private //+build js package time -IS_SUPPORTED :: false +foreign import "odin_env" -now :: proc() -> Time { - return {} +_IS_SUPPORTED :: true + +_now :: proc "contextless" () -> Time { + foreign odin_env { + time_now :: proc "contextless" () -> i64 --- + } + return Time{time_now()} } -sleep :: proc(d: Duration) { +_sleep :: proc "contextless" (d: Duration) { + foreign odin_env { + time_sleep :: proc "contextless" (ms: u32) --- + } + if d > 0 { + time_sleep(u32(d/1e6)) + } } _tick_now :: proc "contextless" () -> Tick { - // mul_div_u64 :: proc "contextless" (val, num, den: i64) -> i64 { - // q := val / den - // r := val % den - // return q * num + r * num / den - // } - return {} + foreign odin_env { + tick_now :: proc "contextless" () -> i64 --- + } + return Tick{tick_now()} } +_yield :: proc "contextless" () { +} diff --git a/core/time/time_unix.odin b/core/time/time_unix.odin index 0d765b72d..ba0d91527 100644 --- a/core/time/time_unix.odin +++ b/core/time/time_unix.odin @@ -1,92 +1,34 @@ -//+build linux, darwin, freebsd +//+private +//+build linux, darwin, freebsd, openbsd package time -IS_SUPPORTED :: true // NOTE: Times on Darwin are UTC. +import "core:sys/unix" -when ODIN_OS == "darwin" { - foreign import libc "System.framework" -} else { - foreign import libc "system:c" -} +_IS_SUPPORTED :: true // NOTE: Times on Darwin are UTC. - -@(default_calling_convention="c") -foreign libc { - @(link_name="clock_gettime") _unix_clock_gettime :: proc(clock_id: u64, timespec: ^TimeSpec) -> i32 --- - @(link_name="sleep") _unix_sleep :: proc(seconds: u32) -> i32 --- - @(link_name="nanosleep") _unix_nanosleep :: proc(requested: ^TimeSpec, remaining: ^TimeSpec) -> i32 --- -} - -TimeSpec :: struct { - tv_sec : i64, /* seconds */ - tv_nsec : i64, /* nanoseconds */ -} - -CLOCK_REALTIME :: 0 // NOTE(tetra): May jump in time, when user changes the system time. -CLOCK_MONOTONIC :: 1 // NOTE(tetra): May stand still while system is asleep. -CLOCK_PROCESS_CPUTIME_ID :: 2 -CLOCK_THREAD_CPUTIME_ID :: 3 -CLOCK_MONOTONIC_RAW :: 4 // NOTE(tetra): "RAW" means: Not adjusted by NTP. -CLOCK_REALTIME_COARSE :: 5 // NOTE(tetra): "COARSE" clocks are apparently much faster, but not "fine-grained." -CLOCK_MONOTONIC_COARSE :: 6 -CLOCK_BOOTTIME :: 7 // NOTE(tetra): Same as MONOTONIC, except also including time system was asleep. -CLOCK_REALTIME_ALARM :: 8 -CLOCK_BOOTTIME_ALARM :: 9 - -// TODO(tetra, 2019-11-05): The original implementation of this package for Darwin used this constants. -// I do not know if Darwin programmers are used to the existance of these constants or not, so -// I'm leaving aliases to them for now. -CLOCK_SYSTEM :: CLOCK_REALTIME -CLOCK_CALENDAR :: CLOCK_MONOTONIC - - -clock_gettime :: proc "contextless" (clock_id: u64) -> TimeSpec { - ts : TimeSpec // NOTE(tetra): Do we need to initialize this? - _unix_clock_gettime(clock_id, &ts) - return ts -} - -now :: proc() -> Time { - time_spec_now := clock_gettime(CLOCK_REALTIME) +_now :: proc "contextless" () -> Time { + time_spec_now: unix.timespec + unix.clock_gettime(unix.CLOCK_REALTIME, &time_spec_now) ns := time_spec_now.tv_sec * 1e9 + time_spec_now.tv_nsec return Time{_nsec=ns} } -boot_time :: proc() -> Time { - ts_now := clock_gettime(CLOCK_REALTIME) - ts_boottime := clock_gettime(CLOCK_BOOTTIME) - - ns := (ts_now.tv_sec - ts_boottime.tv_sec) * 1e9 + ts_now.tv_nsec - ts_boottime.tv_nsec - return Time{_nsec=ns} -} - -seconds_since_boot :: proc() -> f64 { - ts_boottime := clock_gettime(CLOCK_BOOTTIME) - return f64(ts_boottime.tv_sec) + f64(ts_boottime.tv_nsec) / 1e9 -} - - -sleep :: proc(d: Duration) { +_sleep :: proc "contextless" (d: Duration) { ds := duration_seconds(d) seconds := u32(ds) nanoseconds := i64((ds - f64(seconds)) * 1e9) - if seconds > 0 { _unix_sleep(seconds) } - if nanoseconds > 0 { nanosleep(nanoseconds) } + if seconds > 0 { unix.sleep(seconds) } + if nanoseconds > 0 { unix.inline_nanosleep(nanoseconds) } } -nanosleep :: proc(nanoseconds: i64) -> int { - // NOTE(tetra): Should we remove this assert? We are measuring nanoseconds after all... - assert(nanoseconds <= 999999999) - - requested := TimeSpec{tv_nsec = nanoseconds} - remaining: TimeSpec // NOTE(tetra): Do we need to initialize this? - return int(_unix_nanosleep(&requested, &remaining)) -} - - _tick_now :: proc "contextless" () -> Tick { - t := clock_gettime(CLOCK_MONOTONIC_RAW) - _nsec := t.tv_sec*1e9 + t.tv_nsec - return Tick{_nsec = _nsec} + t: unix.timespec + unix.clock_gettime(unix.CLOCK_MONOTONIC_RAW, &t) + return Tick{_nsec = t.tv_sec*1e9 + t.tv_nsec} } + +_yield :: proc "contextless" () { + unix.sched_yield() +} + diff --git a/core/time/time_wasi.odin b/core/time/time_wasi.odin index 4a6c8afc0..9360e3591 100644 --- a/core/time/time_wasi.odin +++ b/core/time/time_wasi.odin @@ -1,15 +1,16 @@ +//+private //+build wasi package time import wasi "core:sys/wasm/wasi" -IS_SUPPORTED :: false +_IS_SUPPORTED :: false -now :: proc() -> Time { +_now :: proc "contextless" () -> Time { return {} } -sleep :: proc(d: Duration) { +_sleep :: proc "contextless" (d: Duration) { } _tick_now :: proc "contextless" () -> Tick { diff --git a/core/time/time_windows.odin b/core/time/time_windows.odin index 0fb9eaa0f..20863c323 100644 --- a/core/time/time_windows.odin +++ b/core/time/time_windows.odin @@ -1,22 +1,21 @@ +//+private package time import win32 "core:sys/windows" -IS_SUPPORTED :: true +_IS_SUPPORTED :: true -now :: proc() -> Time { +_now :: proc "contextless" () -> Time { file_time: win32.FILETIME win32.GetSystemTimeAsFileTime(&file_time) ns := win32.FILETIME_as_unix_nanoseconds(file_time) return Time{_nsec=ns} } -sleep :: proc(d: Duration) { +_sleep :: proc "contextless" (d: Duration) { win32.Sleep(win32.DWORD(d/Millisecond)) } - - _tick_now :: proc "contextless" () -> Tick { mul_div_u64 :: proc "contextless" (val, num, den: i64) -> i64 { q := val / den @@ -35,3 +34,7 @@ _tick_now :: proc "contextless" () -> Tick { _nsec := mul_div_u64(i64(now), 1e9, i64(qpc_frequency)) return Tick{_nsec = _nsec} } + +_yield :: proc "contextless" () { + win32.SwitchToThread() +} diff --git a/core/unicode/tools/generate_entity_table.odin b/core/unicode/tools/generate_entity_table.odin new file mode 100644 index 000000000..328ba9091 --- /dev/null +++ b/core/unicode/tools/generate_entity_table.odin @@ -0,0 +1,287 @@ +package xml_example + +import "core:encoding/xml" +import "core:os" +import "core:path" +import "core:mem" +import "core:strings" +import "core:strconv" +import "core:slice" +import "core:fmt" + +/* + Silent error handler for the parser. +*/ +Error_Handler :: proc(pos: xml.Pos, fmt: string, args: ..any) {} + +OPTIONS :: xml.Options{ flags = { .Ignore_Unsupported, }, expected_doctype = "unicode", } + +Entity :: struct { + name: string, + codepoint: rune, + description: string, +} + +generate_encoding_entity_table :: proc() { + using fmt + + filename := path.join(ODIN_ROOT, "tests", "core", "assets", "XML", "unicode.xml") + defer delete(filename) + + generated_filename := path.join(ODIN_ROOT, "core", "encoding", "entity", "generated.odin") + defer delete(generated_filename) + + doc, err := xml.parse(filename, OPTIONS, Error_Handler) + defer xml.destroy(doc) + + if err != .None { + printf("Load/Parse error: %v\n", err) + if err == .File_Error { + printf("\"%v\" not found. Did you run \"tests\\download_assets.py\"?", filename) + } + os.exit(1) + } + + printf("\"%v\" loaded and parsed.\n", filename) + + generated_buf: strings.Builder + defer strings.builder_destroy(&generated_buf) + w := strings.to_writer(&generated_buf) + + charlist, charlist_ok := xml.find_child_by_ident(doc.root, "charlist") + if !charlist_ok { + eprintln("Could not locate top-level `` tag.") + os.exit(1) + } + + printf("Found `` with %v children.\n", len(charlist.children)) + + entity_map: map[string]Entity + names: [dynamic]string + + min_name_length := max(int) + max_name_length := min(int) + shortest_name: string + longest_name: string + + count := 0 + for char in charlist.children { + if char.ident != "character" { + eprintf("Expected ``, got `<%v>`\n", char.ident) + os.exit(1) + } + + if codepoint_string, ok := xml.find_attribute_val_by_key(char, "dec"); !ok { + eprintln("`` attribute not found.") + os.exit(1) + } else { + codepoint := strconv.atoi(codepoint_string) + + desc, desc_ok := xml.find_child_by_ident(char, "description") + description := desc.value if desc_ok else "" + + /* + For us to be interested in this codepoint, it has to have at least one entity. + */ + + nth := 0 + for { + character_entity, entity_ok := xml.find_child_by_ident(char, "entity", nth) + if !entity_ok { break } + + nth += 1 + if name, name_ok := xml.find_attribute_val_by_key(character_entity, "id"); name_ok { + + if len(name) == 0 { + /* + Invalid name. Skip. + */ + continue + } + + if name == "\"\"" { + printf("%#v\n", char) + printf("%#v\n", character_entity) + } + + if len(name) > max_name_length { longest_name = name } + if len(name) < min_name_length { shortest_name = name } + + min_name_length = min(min_name_length, len(name)) + max_name_length = max(max_name_length, len(name)) + + e := Entity{ + name = name, + codepoint = rune(codepoint), + description = description, + } + + if _, seen := entity_map[name]; seen { + continue + } + + entity_map[name] = e + append(&names, name) + count += 1 + } + } + } + } + + /* + Sort by name. + */ + slice.sort(names[:]) + + printf("Found %v unique `&name;` -> rune mappings.\n", count) + printf("Shortest name: %v (%v)\n", shortest_name, min_name_length) + printf("Longest name: %v (%v)\n", longest_name, max_name_length) + + // println(rune_to_string(1234)) + + /* + Generate table. + */ + wprintln(w, "package unicode_entity") + wprintln(w, "") + wprintln(w, GENERATED) + wprintln(w, "") + wprintf (w, TABLE_FILE_PROLOG) + wprintln(w, "") + + wprintf (w, "// `&%v;`\n", shortest_name) + wprintf (w, "XML_NAME_TO_RUNE_MIN_LENGTH :: %v\n", min_name_length) + wprintf (w, "// `&%v;`\n", longest_name) + wprintf (w, "XML_NAME_TO_RUNE_MAX_LENGTH :: %v\n", max_name_length) + wprintln(w, "") + + wprintln(w, +` +/* + Input: + entity_name - a string, like "copy" that describes a user-encoded Unicode entity as used in XML. + + Output: + "decoded" - The decoded rune if found by name, or -1 otherwise. + "ok" - true if found, false if not. + + IMPORTANT: XML processors (including browsers) treat these names as case-sensitive. So do we. +*/ +named_xml_entity_to_rune :: proc(name: string) -> (decoded: rune, ok: bool) { + /* + Early out if the name is too short or too long. + min as a precaution in case the generated table has a bogus value. + */ + if len(name) < min(1, XML_NAME_TO_RUNE_MIN_LENGTH) || len(name) > XML_NAME_TO_RUNE_MAX_LENGTH { + return -1, false + } + + switch rune(name[0]) { +`) + + prefix := '?' + should_close := false + + for v in names { + if rune(v[0]) != prefix { + if should_close { + wprintln(w, "\t\t}\n") + } + + prefix = rune(v[0]) + wprintf (w, "\tcase '%v':\n", prefix) + wprintln(w, "\t\tswitch name {") + } + + e := entity_map[v] + + wprintf(w, "\t\t\tcase \"%v\": \n", e.name) + wprintf(w, "\t\t\t\t// %v\n", e.description) + wprintf(w, "\t\t\t\treturn %v, true\n", rune_to_string(e.codepoint)) + + should_close = true + } + wprintln(w, "\t\t}") + wprintln(w, "\t}") + wprintln(w, "\treturn -1, false") + wprintln(w, "}\n") + wprintln(w, GENERATED) + + println() + println(strings.to_string(generated_buf)) + println() + + written := os.write_entire_file(generated_filename, transmute([]byte)strings.to_string(generated_buf)) + + if written { + fmt.printf("Successfully written generated \"%v\".", generated_filename) + } else { + fmt.printf("Failed to write generated \"%v\".", generated_filename) + } + + delete(entity_map) + delete(names) + for name in &names { + free(&name) + } +} + +GENERATED :: `/* + ------ GENERATED ------ DO NOT EDIT ------ GENERATED ------ DO NOT EDIT ------ GENERATED ------ +*/` + +TABLE_FILE_PROLOG :: `/* + This file is generated from "https://www.w3.org/2003/entities/2007xml/unicode.xml". + + UPDATE: + - Ensure the XML file was downloaded using "tests\core\download_assets.py". + - Run "core/unicode/tools/generate_entity_table.odin" + + Odin unicode generated tables: https://github.com/odin-lang/Odin/tree/master/core/encoding/entity + + Copyright © 2021 World Wide Web Consortium, (Massachusetts Institute of Technology, + European Research Consortium for Informatics and Mathematics, Keio University, Beihang). + + All Rights Reserved. + + This work is distributed under the W3C® Software License [1] in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + [1] http://www.w3.org/Consortium/Legal/copyright-software + + See also: LICENSE_table.md +*/ +` + +rune_to_string :: proc(r: rune) -> (res: string) { + res = fmt.tprintf("%08x", int(r)) + for len(res) > 2 && res[:2] == "00" { + res = res[2:] + } + return fmt.tprintf("rune(0x%v)", res) +} + +is_dotted_name :: proc(name: string) -> (dotted: bool) { + for r in name { + if r == '.' { return true} + } + return false +} + +main :: proc() { + using fmt + + track: mem.Tracking_Allocator + mem.tracking_allocator_init(&track, context.allocator) + context.allocator = mem.tracking_allocator(&track) + + generate_encoding_entity_table() + + if len(track.allocation_map) > 0 { + println() + for _, v in track.allocation_map { + printf("%v Leaked %v bytes.\n", v.location, v.size) + } + } + println("Done and cleaned up!") +} \ No newline at end of file diff --git a/core/unicode/utf8/utf8.odin b/core/unicode/utf8/utf8.odin index 6a04b0fe9..a0da5c5d1 100644 --- a/core/unicode/utf8/utf8.odin +++ b/core/unicode/utf8/utf8.odin @@ -90,10 +90,15 @@ encode_rune :: proc(c: rune) -> ([4]u8, int) { return buf, 4 } -decode_rune_in_string :: #force_inline proc(s: string) -> (rune, int) { - return decode_rune(transmute([]u8)s) + +decode_rune :: proc{ + decode_rune_in_string, + decode_rune_in_bytes, } -decode_rune :: proc(s: []u8) -> (rune, int) { +decode_rune_in_string :: #force_inline proc(s: string) -> (rune, int) { + return decode_rune_in_bytes(transmute([]u8)s) +} +decode_rune_in_bytes :: proc(s: []u8) -> (rune, int) { n := len(s) if n < 1 { return RUNE_ERROR, 0 @@ -161,10 +166,15 @@ runes_to_string :: proc(runes: []rune, allocator := context.allocator) -> string } -decode_last_rune_in_string :: #force_inline proc(s: string) -> (rune, int) { - return decode_last_rune(transmute([]u8)s) +decode_last_rune :: proc{ + decode_last_rune_in_string, + decode_last_rune_in_bytes, } -decode_last_rune :: proc(s: []u8) -> (rune, int) { + +decode_last_rune_in_string :: #force_inline proc(s: string) -> (rune, int) { + return decode_last_rune_in_bytes(transmute([]u8)s) +} +decode_last_rune_in_bytes :: proc(s: []u8) -> (rune, int) { r: rune size: int start, end, limit: int @@ -297,10 +307,15 @@ rune_start :: #force_inline proc(b: u8) -> bool { return b&0xc0 != 0x80 } -rune_count_in_string :: #force_inline proc(s: string) -> int { - return rune_count(transmute([]u8)s) +rune_count :: proc{ + rune_count_in_string, + rune_count_in_bytes, } -rune_count :: proc(s: []u8) -> int { + +rune_count_in_string :: #force_inline proc(s: string) -> int { + return rune_count_in_bytes(transmute([]u8)s) +} +rune_count_in_bytes :: proc(s: []u8) -> int { count := 0 n := len(s) @@ -353,7 +368,14 @@ rune_size :: proc(r: rune) -> int { // full_rune reports if the bytes in b begin with a full utf-8 encoding of a rune or not // An invalid encoding is considered a full rune since it will convert as an error rune of width 1 (RUNE_ERROR) -full_rune :: proc(b: []byte) -> bool { +full_rune :: proc{ + full_rune_in_bytes, + full_rune_in_string, +} + +// full_rune_in_bytes reports if the bytes in b begin with a full utf-8 encoding of a rune or not +// An invalid encoding is considered a full rune since it will convert as an error rune of width 1 (RUNE_ERROR) +full_rune_in_bytes :: proc(b: []byte) -> bool { n := len(b) if n == 0 { return false @@ -374,7 +396,7 @@ full_rune :: proc(b: []byte) -> bool { // full_rune_in_string reports if the bytes in s begin with a full utf-8 encoding of a rune or not // An invalid encoding is considered a full rune since it will convert as an error rune of width 1 (RUNE_ERROR) full_rune_in_string :: proc(s: string) -> bool { - return full_rune(transmute([]byte)s) + return full_rune_in_bytes(transmute([]byte)s) } diff --git a/core/unicode/utf8/utf8string/string.odin b/core/unicode/utf8/utf8string/string.odin index 23e2eefc6..86267defb 100644 --- a/core/unicode/utf8/utf8string/string.odin +++ b/core/unicode/utf8/utf8string/string.odin @@ -16,140 +16,140 @@ String :: struct { } @(private) -_len :: builtin.len; // helper procedure +_len :: builtin.len // helper procedure init :: proc(s: ^String, contents: string) -> ^String { - s.contents = contents; - s.byte_pos = 0; - s.rune_pos = 0; + s.contents = contents + s.byte_pos = 0 + s.rune_pos = 0 for i in 0..<_len(contents) { if contents[i] >= utf8.RUNE_SELF { - s.rune_count = utf8.rune_count_in_string(contents); - _, s.width = utf8.decode_rune_in_string(contents); - s.non_ascii = i; - return s; + s.rune_count = utf8.rune_count_in_string(contents) + _, s.width = utf8.decode_rune_in_string(contents) + s.non_ascii = i + return s } } - s.rune_count = _len(contents); - s.width = 0; - s.non_ascii = _len(contents); - return s; + s.rune_count = _len(contents) + s.width = 0 + s.non_ascii = _len(contents) + return s } to_string :: proc(s: ^String) -> string { - return s.contents; + return s.contents } len :: proc(s: ^String) -> int { - return s.rune_count; + return s.rune_count } is_ascii :: proc(s: ^String) -> bool { - return s.width == 0; + return s.width == 0 } at :: proc(s: ^String, i: int, loc := #caller_location) -> (r: rune) { - runtime.bounds_check_error_loc(loc, i, s.rune_count); + runtime.bounds_check_error_loc(loc, i, s.rune_count) if i < s.non_ascii { - return rune(s.contents[i]); + return rune(s.contents[i]) } switch i { case 0: - r, s.width = utf8.decode_rune_in_string(s.contents); - s.rune_pos = 0; - s.byte_pos = 0; - return; + r, s.width = utf8.decode_rune_in_string(s.contents) + s.rune_pos = 0 + s.byte_pos = 0 + return case s.rune_count-1: - r, s.width = utf8.decode_rune_in_string(s.contents); - s.rune_pos = i; - s.byte_pos = _len(s.contents) - s.width; - return; + r, s.width = utf8.decode_rune_in_string(s.contents) + s.rune_pos = i + s.byte_pos = _len(s.contents) - s.width + return case s.rune_pos-1: - r, s.width = utf8.decode_rune_in_string(s.contents[0:s.byte_pos]); - s.rune_pos = i; - s.byte_pos -= s.width; - return; + r, s.width = utf8.decode_rune_in_string(s.contents[0:s.byte_pos]) + s.rune_pos = i + s.byte_pos -= s.width + return case s.rune_pos+1: - s.rune_pos = i; - s.byte_pos += s.width; - fallthrough; + s.rune_pos = i + s.byte_pos += s.width + fallthrough case s.rune_pos: - r, s.width = utf8.decode_rune_in_string(s.contents[s.byte_pos:]); - return; + r, s.width = utf8.decode_rune_in_string(s.contents[s.byte_pos:]) + return } // Linear scan - scan_forward := true; + scan_forward := true if i < s.rune_pos { if i < (s.rune_pos-s.non_ascii)/2 { - s.byte_pos, s.rune_pos = s.non_ascii, s.non_ascii; + s.byte_pos, s.rune_pos = s.non_ascii, s.non_ascii } else { - scan_forward = false; + scan_forward = false } } else if i-s.rune_pos < (s.rune_count-s.rune_pos)/2 { - // scan_forward = true; + // scan_forward = true } else { - s.byte_pos, s.rune_pos = _len(s.contents), s.rune_count; - scan_forward = false; + s.byte_pos, s.rune_pos = _len(s.contents), s.rune_count + scan_forward = false } if scan_forward { for { - r, s.width = utf8.decode_rune_in_string(s.contents[s.byte_pos:]); + r, s.width = utf8.decode_rune_in_string(s.contents[s.byte_pos:]) if s.rune_pos == i { - return; + return } - s.rune_pos += 1; - s.byte_pos += s.width; + s.rune_pos += 1 + s.byte_pos += s.width } } else { for { - r, s.width = utf8.decode_last_rune_in_string(s.contents[:s.byte_pos]); - s.rune_pos -= 1; - s.byte_pos -= s.width; + r, s.width = utf8.decode_last_rune_in_string(s.contents[:s.byte_pos]) + s.rune_pos -= 1 + s.byte_pos -= s.width if s.rune_pos == i { - return; + return } } } } slice :: proc(s: ^String, i, j: int, loc := #caller_location) -> string { - runtime.slice_expr_error_lo_hi_loc(loc, i, j, s.rune_count); + runtime.slice_expr_error_lo_hi_loc(loc, i, j, s.rune_count) if j < s.non_ascii { - return s.contents[i:j]; + return s.contents[i:j] } if i == j { - return ""; + return "" } - lo, hi: int; + lo, hi: int if i < s.non_ascii { - lo = i; + lo = i } else if i == s.rune_count { - lo = _len(s.contents); + lo = _len(s.contents) } else { - at(s, i, loc); - lo = s.byte_pos; + at(s, i, loc) + lo = s.byte_pos } if j == s.rune_count { - hi = _len(s.contents); + hi = _len(s.contents) } else { - at(s, j, loc); - hi = s.byte_pos; + at(s, j, loc) + hi = s.byte_pos } - return s.contents[lo:hi]; + return s.contents[lo:hi] } diff --git a/examples/all/all_main.odin b/examples/all/all_main.odin index c24238602..9f347fad3 100644 --- a/examples/all/all_main.odin +++ b/examples/all/all_main.odin @@ -5,25 +5,71 @@ package all import bufio "core:bufio" import bytes "core:bytes" + import c "core:c" import libc "core:c/libc" + import compress "core:compress" +import shoco "core:compress/shoco" import gzip "core:compress/gzip" import zlib "core:compress/zlib" -import container "core:container" + +import bit_array "core:container/bit_array" +import priority_queue "core:container/priority_queue" +import queue "core:container/queue" +import small_array "core:container/small_array" +import lru "core:container/lru" + +import crypto "core:crypto" +import blake "core:crypto/blake" +import blake2b "core:crypto/blake2b" +import blake2s "core:crypto/blake2s" +import chacha20 "core:crypto/chacha20" +import chacha20poly1305 "core:crypto/chacha20poly1305" +import gost "core:crypto/gost" +import groestl "core:crypto/groestl" +import haval "core:crypto/haval" +import jh "core:crypto/jh" +import keccak "core:crypto/keccak" +import md2 "core:crypto/md2" +import md4 "core:crypto/md4" +import md5 "core:crypto/md5" +import poly1305 "core:crypto/poly1305" +import ripemd "core:crypto/ripemd" +import sha1 "core:crypto/sha1" +import sha2 "core:crypto/sha2" +import sha3 "core:crypto/sha3" +import shake "core:crypto/shake" +import sm3 "core:crypto/sm3" +import streebog "core:crypto/streebog" +import tiger "core:crypto/tiger" +import tiger2 "core:crypto/tiger2" +import crypto_util "core:crypto/util" +import whirlpool "core:crypto/whirlpool" +import x25519 "core:crypto/x25519" + import dynlib "core:dynlib" -import encoding "core:encoding" + import base32 "core:encoding/base32" import base64 "core:encoding/base64" import csv "core:encoding/csv" import hxa "core:encoding/hxa" import json "core:encoding/json" +import varint "core:encoding/varint" +import xml "core:encoding/xml" + import fmt "core:fmt" import hash "core:hash" + import image "core:image" +import netpbm "core:image/netpbm" import png "core:image/png" +import qoi "core:image/qoi" +import tga "core:image/tga" + import io "core:io" import log "core:log" + import math "core:math" import big "core:math/big" import bits "core:math/bits" @@ -32,29 +78,40 @@ import linalg "core:math/linalg" import glm "core:math/linalg/glsl" import hlm "core:math/linalg/hlsl" import rand "core:math/rand" + import mem "core:mem" +// import virtual "core:mem/virtual" + import ast "core:odin/ast" import doc_format "core:odin/doc-format" import odin_format "core:odin/format" import odin_parser "core:odin/parser" import odin_printer "core:odin/printer" import odin_tokenizer "core:odin/tokenizer" + import os "core:os" + import slashpath "core:path/slashpath" import filepath "core:path/filepath" + import reflect "core:reflect" import runtime "core:runtime" +import simd "core:simd" import slice "core:slice" +import slice_heap "core:slice/heap" import sort "core:sort" import strconv "core:strconv" import strings "core:strings" import sync "core:sync" -import sync2 "core:sync/sync2" +import testing "core:testing" import scanner "core:text/scanner" +import i18n "core:text/i18n" import thread "core:thread" import time "core:time" + import unicode "core:unicode" import utf8 "core:unicode/utf8" +import utf8string "core:unicode/utf8/utf8string" import utf16 "core:unicode/utf16" main :: proc(){} @@ -65,20 +122,56 @@ _ :: bytes _ :: c _ :: libc _ :: compress +_ :: shoco _ :: gzip _ :: zlib -_ :: container +_ :: bit_array +_ :: priority_queue +_ :: queue +_ :: small_array +_ :: lru +_ :: crypto +_ :: blake +_ :: blake2b +_ :: blake2s +_ :: chacha20 +_ :: chacha20poly1305 +_ :: gost +_ :: groestl +_ :: haval +_ :: jh +_ :: keccak +_ :: md2 +_ :: md4 +_ :: md5 +_ :: poly1305 +_ :: ripemd +_ :: sha1 +_ :: sha2 +_ :: sha3 +_ :: shake +_ :: sm3 +_ :: streebog +_ :: tiger +_ :: tiger2 +_ :: crypto_util +_ :: whirlpool +_ :: x25519 _ :: dynlib -_ :: encoding _ :: base32 _ :: base64 _ :: csv _ :: hxa _ :: json +_ :: varint +_ :: xml _ :: fmt _ :: hash _ :: image +_ :: netpbm _ :: png +_ :: qoi +_ :: tga _ :: io _ :: log _ :: math @@ -101,15 +194,19 @@ _ :: slashpath _ :: filepath _ :: reflect _ :: runtime +_ :: simd _ :: slice +_ :: slice_heap _ :: sort _ :: strconv _ :: strings _ :: sync -_ :: sync2 +_ :: testing _ :: scanner +_ :: i18n _ :: thread _ :: time _ :: unicode _ :: utf8 +_ :: utf8string _ :: utf16 \ No newline at end of file diff --git a/examples/all/all_vendor.odin b/examples/all/all_vendor.odin index f94e092af..7da2e501b 100644 --- a/examples/all/all_vendor.odin +++ b/examples/all/all_vendor.odin @@ -1,15 +1,15 @@ -//+build windows package all - import botan "vendor:botan" import ENet "vendor:ENet" +import ggpo "vendor:ggpo" import gl "vendor:OpenGL" import glfw "vendor:glfw" import microui "vendor:microui" import miniaudio "vendor:miniaudio" import PM "vendor:portmidi" import rl "vendor:raylib" +import exr "vendor:OpenEXRCore" import SDL "vendor:sdl2" import SDLNet "vendor:sdl2/net" @@ -17,31 +17,28 @@ import IMG "vendor:sdl2/image" import MIX "vendor:sdl2/mixer" import TTF "vendor:sdl2/ttf" -import stb_easy_font "vendor:stb/easy_font" -import stbi "vendor:stb/image" -import stbrp "vendor:stb/rect_pack" -import stbtt "vendor:stb/truetype" -import stb_vorbis "vendor:stb/vorbis" - import vk "vendor:vulkan" +import NS "vendor:darwin/Foundation" +import MTL "vendor:darwin/Metal" +import CA "vendor:darwin/QuartzCore" _ :: botan _ :: ENet +_ :: ggpo _ :: gl _ :: glfw _ :: microui _ :: miniaudio _ :: PM _ :: rl +_ :: exr _ :: SDL _ :: SDLNet _ :: IMG _ :: MIX _ :: TTF -_ :: stb_easy_font -_ :: stbi -_ :: stbrp -_ :: stbtt -_ :: stb_vorbis -_ :: vk \ No newline at end of file +_ :: vk +_ :: NS +_ :: MTL +_ :: CA diff --git a/examples/all/all_vendor_directx.odin b/examples/all/all_vendor_directx.odin new file mode 100644 index 000000000..2f10d92f8 --- /dev/null +++ b/examples/all/all_vendor_directx.odin @@ -0,0 +1,10 @@ +//+build windows +package all + +import D3D11 "vendor:directx/d3d11" +import D3D12 "vendor:directx/d3d12" +import DXGI "vendor:directx/dxgi" + +_ :: D3D11 +_ :: D3D12 +_ :: DXGI diff --git a/examples/all/all_vendor_stl.odin b/examples/all/all_vendor_stl.odin new file mode 100644 index 000000000..9faf53c63 --- /dev/null +++ b/examples/all/all_vendor_stl.odin @@ -0,0 +1,15 @@ +//+build windows, linux +package all + +import stb_easy_font "vendor:stb/easy_font" +import stbi "vendor:stb/image" +import stbrp "vendor:stb/rect_pack" +import stbtt "vendor:stb/truetype" +import stb_vorbis "vendor:stb/vorbis" + +_ :: stb_easy_font +_ :: stbi +_ :: stbrp +_ :: stbtt +_ :: stb_vorbis + diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index 3e34e3d49..457aa786a 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -11,22 +11,32 @@ import "core:intrinsics" import "core:math/big" /* - The Odin programming language is fast, concise, readable, pragmatic and open sourced. - It is designed with the intent of replacing C with the following goals: - * simplicity - * high performance - * built for modern systems - * joy of programming + Odin is a general-purpose programming language with distinct typing built + for high performance, modern systems and data-oriented programming. + + Odin is the C alternative for the Joy of Programming. # Installing Odin Getting Started - https://odin-lang.org/docs/install/ Instructions for downloading and install the Odin compiler and libraries. # Learning Odin + Getting Started - https://odin-lang.org/docs/install/ + Getting Started with Odin. Downloading, installing, and getting your + first program to compile and run. Overview of Odin - https://odin-lang.org/docs/overview/ - An overview of the Odin programming language. + An overview of the Odin programming language and its features. Frequently Asked Questions (FAQ) - https://odin-lang.org/docs/faq/ Answers to common questions about Odin. + Packages - https://pkg.odin-lang.org/ + Documentation for all the official packages part of the + core and vendor library collections. + Nightly Builds - https://odin-lang.org/docs/nightly/ + Get the latest nightly builds of Odin. + More Odin Examples - https://github.com/odin-lang/examples + This repository contains examples of how certain things can be accomplished + in idiomatic Odin, allowing you learn its semantics, as well as how to use + parts of the core and vendor package collections. */ the_basics :: proc() { @@ -88,6 +98,7 @@ the_basics :: proc() { z: f64 // `z` is typed of type `f64` (64-bit floating point number) z = 1 // `1` is an untyped integer literal which can be implicitly converted to `f64` // No need for any suffixes or decimal places like in other languages + // (with the exception of negative zero, which must be given as `-0.0`) // CONSTANTS JUST WORK!!! @@ -244,10 +255,10 @@ control_flow :: proc() { // A switch statement is another way to write a sequence of if-else statements. // In Odin, the default case is denoted as a case without any expression. - switch arch := ODIN_ARCH; arch { - case "386": + #partial switch arch := ODIN_ARCH; arch { + case .i386: fmt.println("32-bit") - case "amd64": + case .amd64: fmt.println("64-bit") case: // default fmt.println("Unsupported architecture") @@ -363,12 +374,12 @@ control_flow :: proc() { */ // Example - when ODIN_ARCH == "386" { + when ODIN_ARCH == .i386 { fmt.println("32 bit") - } else when ODIN_ARCH == "amd64" { + } else when ODIN_ARCH == .amd64 { fmt.println("64 bit") } else { - fmt.println("Unsupported architecture") + fmt.println("Unknown architecture") } // The when statement is very useful for writing platform specific code. // This is akin to the #if construct in C’s preprocessor however, in Odin, @@ -1099,14 +1110,16 @@ prefix_table := [?]string{ "Black", } -threading_example :: proc() { - if ODIN_OS == "darwin" { - // TODO: Fix threads on darwin/macOS - return - } +print_mutex := b64(false) +threading_example :: proc() { fmt.println("\n# threading_example") + did_acquire :: proc(m: ^b64) -> (acquired: bool) { + res, ok := intrinsics.atomic_compare_exchange_strong(m, false, true) + return ok && res == false + } + { // Basic Threads fmt.println("\n## Basic Threads") worker_proc :: proc(t: ^thread.Thread) { @@ -1145,26 +1158,47 @@ threading_example :: proc() { { // Thread Pool fmt.println("\n## Thread Pool") - task_proc :: proc(t: ^thread.Task) { + task_proc :: proc(t: thread.Task) { index := t.user_index % len(prefix_table) for iteration in 1..=5 { + for !did_acquire(&print_mutex) { thread.yield() } // Allow one thread to print at a time. + fmt.printf("Worker Task %d is on iteration %d\n", t.user_index, iteration) fmt.printf("`%s`: iteration %d\n", prefix_table[index], iteration) + + print_mutex = false + time.sleep(1 * time.Millisecond) } } + N :: 3 + pool: thread.Pool - thread.pool_init(pool=&pool, thread_count=3) + thread.pool_init(pool=&pool, thread_count=N, allocator=context.allocator) defer thread.pool_destroy(&pool) for i in 0..<30 { - thread.pool_add_task(pool=&pool, procedure=task_proc, data=nil, user_index=i) + // be mindful of the allocator used for tasks. The allocator needs to be thread safe, or be owned by the task for exclusive use + thread.pool_add_task(pool=&pool, procedure=task_proc, data=nil, user_index=i, allocator=context.allocator) } thread.pool_start(&pool) - thread.pool_wait_and_process(&pool) + + { + // Wait a moment before we cancel a thread + time.sleep(5 * time.Millisecond) + + // Allow one thread to print at a time. + for !did_acquire(&print_mutex) { thread.yield() } + + thread.terminate(pool.threads[N - 1], 0) + fmt.println("Canceled last thread") + print_mutex = false + } + + thread.pool_finish(&pool) } } @@ -1606,13 +1640,13 @@ where_clauses :: proc() { } -when ODIN_OS == "windows" { +when ODIN_OS == .Windows { foreign import kernel32 "system:kernel32.lib" } foreign_system :: proc() { fmt.println("\n#foreign system") - when ODIN_OS == "windows" { + when ODIN_OS == .Windows { // It is sometimes necessarily to interface with foreign code, // such as a C library. In Odin, this is achieved through the // foreign system. You can “import” a library into the code @@ -1708,7 +1742,6 @@ deprecated_attribute :: proc() { } range_statements_with_multiple_return_values :: proc() { - // IMPORTANT NOTE(bill, 2019-11-02): This feature is subject to be changed/removed fmt.println("\n#range statements with multiple return values") My_Iterator :: struct { index: int, @@ -1921,14 +1954,14 @@ constant_literal_expressions :: proc() { fmt.println("-------") - Partial_Baz :: enum{A=5, B, C, D=16} - #assert(len(Partial_Baz) < len(#partial [Partial_Baz]int)) - PARTIAL_ENUM_ARRAY_CONST :: #partial [Partial_Baz]int{.A ..= .C = 1, .D = 16} + Sparse_Baz :: enum{A=5, B, C, D=16} + #assert(len(Sparse_Baz) < len(#sparse[Sparse_Baz]int)) + SPARSE_ENUM_ARRAY_CONST :: #sparse[Sparse_Baz]int{.A ..= .C = 1, .D = 16} - fmt.println(PARTIAL_ENUM_ARRAY_CONST[.A]) - fmt.println(PARTIAL_ENUM_ARRAY_CONST[.B]) - fmt.println(PARTIAL_ENUM_ARRAY_CONST[.C]) - fmt.println(PARTIAL_ENUM_ARRAY_CONST[.D]) + fmt.println(SPARSE_ENUM_ARRAY_CONST[.A]) + fmt.println(SPARSE_ENUM_ARRAY_CONST[.B]) + fmt.println(SPARSE_ENUM_ARRAY_CONST[.C]) + fmt.println(SPARSE_ENUM_ARRAY_CONST[.D]) fmt.println("-------") @@ -1944,15 +1977,17 @@ constant_literal_expressions :: proc() { } union_maybe :: proc() { - fmt.println("\n#union #maybe") + fmt.println("\n#union based maybe") // NOTE: This is already built-in, and this is just a reimplementation to explain the behaviour - Maybe :: union($T: typeid) #maybe {T} + Maybe :: union($T: typeid) {T} i: Maybe(u8) p: Maybe(^u8) // No tag is stored for pointers, nil is the sentinel value + // Tag size will be as small as needed for the number of variants #assert(size_of(i) == size_of(u8) + size_of(u8)) + // No need to store a tag here, the `nil` state is shared with the variant's `nil` #assert(size_of(p) == size_of(^u8)) i = 123 @@ -1998,7 +2033,6 @@ relative_data_types :: proc() { or_else_operator :: proc() { fmt.println("\n#'or_else'") - // IMPORTANT NOTE: 'or_else' is an experimental feature and subject to change/removal { m: map[string]int i: int @@ -2029,8 +2063,6 @@ or_else_operator :: proc() { or_return_operator :: proc() { fmt.println("\n#'or_return'") - // IMPORTANT NOTE: 'or_return' is an experimental feature and subject to change/removal - // // The concept of 'or_return' will work by popping off the end value in a multiple // valued expression and checking whether it was not 'nil' or 'false', and if so, // set the end return value to value if possible. If the procedure only has one @@ -2421,6 +2453,13 @@ matrix_type :: proc() { } main :: proc() { + /* + For More Odin Examples - https://github.com/odin-lang/examples + This repository contains examples of how certain things can be accomplished + in idiomatic Odin, allowing you learn its semantics, as well as how to use + parts of the core and vendor package collections. + */ + when true { the_basics() control_flow() diff --git a/examples/hms2019/basic.odin b/examples/hms2019/basic.odin deleted file mode 100644 index 4c4a4eff0..000000000 --- a/examples/hms2019/basic.odin +++ /dev/null @@ -1,7 +0,0 @@ -package basic - -import "core:fmt" - -main :: proc() { - fmt.println("Hellope!"); -} \ No newline at end of file diff --git a/examples/hms2019/eca.odin b/examples/hms2019/eca.odin deleted file mode 100644 index eaefeeb9b..000000000 --- a/examples/hms2019/eca.odin +++ /dev/null @@ -1,67 +0,0 @@ -package eca - -import "core:fmt" -import "core:math/rand" -import "core:time" -import "intrinsics" - -elementary_cellular_automata :: proc(state: $T, rule: u8, generations: int, pause: time.Duration = 0) - where intrinsics.type_is_integer(T), - intrinsics.type_is_unsigned(T) { - N :: 8*size_of(state); - - output :: proc(state: T) { - buf: [N]byte; - for i in 0.. T { - return (x >> i) & 0x1; - } - set :: proc(x: ^T, cell, k: T, rule: u8) { - x^ &~= 1<>k&1 != 0 { - x^ |= 1< 0 do time.sleep(pause); - - - k := bit(a, last) | bit(a, 0)<<1 | bit(a, 1)<<2; - set(&a1, 0, k, rule); - a1 |= (1<<0) * T(rule>>k&1); - for c in 1..>1 | bit(a, c+1)<<2; - set(&a1, c, k, rule); - } - set(&a1, last, k>>1|bit(a, 0)<<2, rule); - a, a1 = a1, a; - output(a); - if a == a1 { - return; - } - } -} - -main :: proc() { - elementary_cellular_automata( - state=rand.uint128(), - rule=30, - generations=5000, - pause=100*time.Millisecond, - ); -} \ No newline at end of file diff --git a/examples/hms2019/hms2019.odin b/examples/hms2019/hms2019.odin deleted file mode 100644 index 5c2e836f8..000000000 --- a/examples/hms2019/hms2019.odin +++ /dev/null @@ -1,1754 +0,0 @@ -package hms2019 - -import "core:fmt" -import "core:mem" -import "core:os" -import "core:reflect" -import "intrinsics" - -/* - Welcome to Handmade Seattle 2019! - - The Odin programming language is fast, concise, readable, pragmatic and open sourced. - It is designed with the intent of replacing C with the following goals: - * simplicity - * high performance - * built for modern systems - * joy of programming - - # Installing Odin - Getting Started - https://odin-lang.org/docs/install/ - Instructions for downloading and install the Odin compiler and libraries. - - # Learning Odin - Overview of Odin - https://odin-lang.org/docs/overview/ - An overview of the Odin programming language. - Frequently Asked Questions (FAQ) - https://odin-lang.org/docs/faq/ - Answers to common questions about Odin. -*/ - -the_basics :: proc() { - fmt.println("\n# the basics"); - - { // The Basics - fmt.println("Hellope"); - - // Lexical elements and literals - // A comment - - my_integer_variable: int; // A comment for documentaton - - // Multi-line comments begin with /* and end with */. Multi-line comments can - // also be nested (unlike in C): - /* - You can have any text or code here and - have it be commented. - /* - NOTE: comments can be nested! - */ - */ - - // String literals are enclosed in double quotes and character literals in single quotes. - // Special characters are escaped with a backslash \ - - some_string := "This is a string"; - _ = 'A'; // unicode codepoint literal - _ = '\n'; - _ = "C:\\Windows\\notepad.exe"; - // Raw string literals are enclosed with single back ticks - _ = `C:\Windows\notepad.exe`; - - // The length of a string in bytes can be found using the built-in `len` procedure: - _ = len("Foo"); - _ = len(some_string); - - - // Numbers - - // Numerical literals are written similar to most other programming languages. - // A useful feature in Odin is that underscores are allowed for better - // readability: 1_000_000_000 (one billion). A number that contains a dot is a - // floating point literal: 1.0e9 (one billion). If a number literal is suffixed - // with i, is an imaginary number literal: 2i (2 multiply the square root of -1). - - // Binary literals are prefixed with 0b, octal literals with 0o, and hexadecimal - // literals 0x. A leading zero does not produce an octal constant (unlike C). - - // In Odin, if a number constant is possible to be represented by a type without - // precision loss, it will automatically convert to that type. - - x: int = 1.0; // A float literal but it can be represented by an integer without precision loss - // Constant literals are “untyped” which means that they can implicitly convert to a type. - - y: int; // `y` is typed of type `int` - y = 1; // `1` is an untyped integer literal which can implicitly convert to `int` - - z: f64; // `z` is typed of type `f64` (64-bit floating point number) - z = 1; // `1` is an untyped integer literals which can be implicity conver to `f64` - // No need for any suffixes or decimal places like in other languages - // CONSTANTS JUST WORK!!! - - - // Assignment statements - h: int = 123; // declares a new variable `h` with type `int` and assigns a value to it - h = 637; // assigns a new value to `h` - - // `=` is the assignment operator - - // You can assign multiple variables with it: - a, b := 1, "hello"; // declares `a` and `b` and infers the types from the assignments - b, a = "byte", 0; - - // Note: `:=` is two tokens, `:` and `=`. The follow are equivalent - /* - i: int = 123; - i: = 123; - i := 123 - */ - - // Constant declarations - // Constants are entities (symbols) which have an assigned value. - // The constant’s value cannot be changed. - // The constant’s value must be able to be evaluated at compile time: - X :: "what"; // constant `X` has the untyped string value "what" - - // Constants can be explicitly typed like a variable declaration: - Y : int : 123; - Z :: Y + 7; // constant computations are possible - } -} - -control_flow :: proc() { - fmt.println("\n# control flow"); - { // Control flow - // For loop - // Odin has only one loop statement, the `for` loop - - // Basic for loop - for i := 0; i < 10; i += 1 { - fmt.println(i); - } - - // NOTE: Unlike other languages like C, there are no parentheses `( )` surrounding the three components. - // Braces `{ }` or a `do` are always required> - for i := 0; i < 10; i += 1 { } - for i := 0; i < 10; i += 1 do fmt.print(); - - // The initial and post statements are optional - i := 0; - for ; i < 10; { - i += 1; - } - - // These semicolons can be dropped. This `for` loop is equivalent to C's `while` loop - i = 0; - for i < 10 { - i += 1; - } - - // If the condition is omitted, this produces an infinite loop: - for { - break; - } - - // Range-based for loop - // The basic for loop - for i := 0; i < 10; i += 1 { - fmt.println(i); - } - // can also be written - for i in 0..<10 { - fmt.println(i); - } - for i in 0..9 { - fmt.println(i); - } - - // Certain built-in types can be iterated over - some_string := "Hello, 世界"; - for character in some_string { // Strings are assumed to be UTF-8 - fmt.println(character); - } - - some_array := [3]int{1, 4, 9}; - for value in some_array { - fmt.println(value); - } - - some_slice := []int{1, 4, 9}; - for value in some_slice { - fmt.println(value); - } - - some_dynamic_array := [dynamic]int{1, 4, 9}; - defer delete(some_dynamic_array); - for value in some_dynamic_array { - fmt.println(value); - } - - - some_map := map[string]int{"A" = 1, "C" = 9, "B" = 4}; - defer delete(some_map); - for key in some_map { - fmt.println(key); - } - - // Alternatively a second index value can be added - for character, index in some_string { - fmt.println(index, character); - } - for value, index in some_array { - fmt.println(index, value); - } - for value, index in some_slice { - fmt.println(index, value); - } - for value, index in some_dynamic_array { - fmt.println(index, value); - } - for key, value in some_map { - fmt.println(key, value); - } - - // The iterated values are copies and cannot be written to. - // The following idiom is useful for iterating over a container in a by-reference manner: - for _, i in some_slice { - some_slice[i] = (i+1)*(i+1); - } - - - // If statements - x := 123; - if x >= 0 { - fmt.println("x is positive"); - } - - if y := -34; y < 0 { - fmt.println("y is negative"); - } - - if y := 123; y < 0 { - fmt.println("y is negative"); - } else if y == 0 { - fmt.println("y is zero"); - } else { - fmt.println("y is positive"); - } - - // Switch statement - // A switch statement is another way to write a sequence of if-else statements. - // In Odin, the default case is denoted as a case without any expression. - - switch arch := ODIN_ARCH; arch { - case "386": - fmt.println("32-bit"); - case "amd64": - fmt.println("64-bit"); - case: // default - fmt.println("Unsupported architecture"); - } - - // Odin’s `switch` is like one in C or C++, except that Odin only runs the selected case. - // This means that a `break` statement is not needed at the end of each case. - // Another important difference is that the case values need not be integers nor constants. - - // To achieve a C-like fall through into the next case block, the keyword `fallthrough` can be used. - one_angry_dwarf :: proc() -> int { - fmt.println("one_angry_dwarf was called"); - return 1; - } - - switch i := 0; i { - case 0: - case one_angry_dwarf(): - } - - // A switch statement without a condition is the same as `switch true`. - // This can be used to write a clean and long if-else chain and have the - // ability to break if needed - - switch { - case x < 0: - fmt.println("x is negative"); - case x == 0: - fmt.println("x is zero"); - case: - fmt.println("x is positive"); - } - - // A `switch` statement can also use ranges like a range-based loop: - switch c := 'j'; c { - case 'A'..'Z', 'a'..'z', '0'..'9': - fmt.println("c is alphanumeric"); - } - - switch x { - case 0..<10: - fmt.println("units"); - case 10..<13: - fmt.println("pre-teens"); - case 13..<20: - fmt.println("teens"); - case 20..<30: - fmt.println("twenties"); - } - } - - { // Defer statement - // A defer statement defers the execution of a statement until the end of - // the scope it is in. - - // The following will print 4 then 234: - { - x := 123; - defer fmt.println(x); - { - defer x = 4; - x = 2; - } - fmt.println(x); - - x = 234; - } - - // You can defer an entire block too: - { - bar :: proc() {} - - defer { - fmt.println("1"); - fmt.println("2"); - } - - cond := false; - defer if cond { - bar(); - } - } - - // Defer statements are executed in the reverse order that they were declared: - { - defer fmt.println("1"); - defer fmt.println("2"); - defer fmt.println("3"); - } - // Will print 3, 2, and then 1. - - if false { - f, err := os.open("my_file.txt"); - if err != 0 { - // handle error - } - defer os.close(f); - // rest of code - } - } - - { // When statement - /* - The when statement is almost identical to the if statement but with some differences: - - * Each condition must be a constant expression as a when - statement is evaluated at compile time. - * The statements within a branch do not create a new scope - * The compiler checks the semantics and code only for statements - that belong to the first condition that is true - * An initial statement is not allowed in a when statement - * when statements are allowed at file scope - */ - - // Example - when ODIN_ARCH == "386" { - fmt.println("32 bit"); - } else when ODIN_ARCH == "amd64" { - fmt.println("64 bit"); - } else { - fmt.println("Unsupported architecture"); - } - // The when statement is very useful for writing platform specific code. - // This is akin to the #if construct in C’s preprocessor however, in Odin, - // it is type checked. - } - - { // Branch statements - cond, cond1, cond2 := false, false, false; - one_step :: proc() { fmt.println("one_step"); } - beyond :: proc() { fmt.println("beyond"); } - - // Break statement - for cond { - switch { - case: - if cond { - break; // break out of the `switch` statement - } - } - - break; // break out of the `for` statement - } - - loop: for cond1 { - for cond2 { - break loop; // leaves both loops - } - } - - // Continue statement - for cond { - if cond2 { - continue; - } - fmt.println("Hellope"); - } - - // Fallthrough statement - - // Odin’s switch is like one in C or C++, except that Odin only runs the selected - // case. This means that a break statement is not needed at the end of each case. - // Another important difference is that the case values need not be integers nor - // constants. - - // fallthrough can be used to explicitly fall through into the next case block: - - switch i := 0; i { - case 0: - one_step(); - fallthrough; - case 1: - beyond(); - } - } -} - - -named_proc_return_parameters :: proc() { - fmt.println("\n# named proc return parameters"); - - foo0 :: proc() -> int { - return 123; - } - foo1 :: proc() -> (a: int) { - a = 123; - return; - } - foo2 :: proc() -> (a, b: int) { - // Named return values act like variables within the scope - a = 321; - b = 567; - return b, a; - } - fmt.println("foo0 =", foo0()); // 123 - fmt.println("foo1 =", foo1()); // 123 - fmt.println("foo2 =", foo2()); // 567 321 -} - - -explicit_procedure_overloading :: proc() { - fmt.println("\n# explicit procedure overloading"); - - add_ints :: proc(a, b: int) -> int { - x := a + b; - fmt.println("add_ints", x); - return x; - } - add_floats :: proc(a, b: f32) -> f32 { - x := a + b; - fmt.println("add_floats", x); - return x; - } - add_numbers :: proc(a: int, b: f32, c: u8) -> int { - x := int(a) + int(b) + int(c); - fmt.println("add_numbers", x); - return x; - } - - add :: proc{add_ints, add_floats, add_numbers}; - - add(int(1), int(2)); - add(f32(1), f32(2)); - add(int(1), f32(2), u8(3)); - - add(1, 2); // untyped ints coerce to int tighter than f32 - add(1.0, 2.0); // untyped floats coerce to f32 tighter than int - add(1, 2, 3); // three parameters - - // Ambiguous answers - // add(1.0, 2); - // add(1, 2.0); -} - -struct_type :: proc() { - fmt.println("\n# struct type"); - // A struct is a record type in Odin. It is a collection of fields. - // Struct fields are accessed by using a dot: - { - Vector2 :: struct { - x: f32, - y: f32, - }; - v := Vector2{1, 2}; - v.x = 4; - fmt.println(v.x); - - // Struct fields can be accessed through a struct pointer: - - v = Vector2{1, 2}; - p := &v; - p.x = 1335; - fmt.println(v); - - // We could write p^.x, however, it is to nice abstract the ability - // to not explicitly dereference the pointer. This is very useful when - // refactoring code to use a pointer rather than a value, and vice versa. - } - { - // A struct literal can be denoted by providing the struct’s type - // followed by {}. A struct literal must either provide all the - // arguments or none: - Vector3 :: struct { - x, y, z: f32, - }; - v: Vector3; - v = Vector3{}; // Zero value - v = Vector3{1, 4, 9}; - - // You can list just a subset of the fields if you specify the - // field by name (the order of the named fields does not matter): - v = Vector3{z=1, y=2}; - assert(v.x == 0); - assert(v.y == 2); - assert(v.z == 1); - } - { - // Structs can tagged with different memory layout and alignment requirements: - - a :: struct #align 4 {}; // align to 4 bytes - b :: struct #packed {}; // remove padding between fields - c :: struct #raw_union {}; // all fields share the same offset (0). This is the same as C's union - } - -} - - -union_type :: proc() { - fmt.println("\n# union type"); - { - val: union{int, bool}; - val = 137; - if i, ok := val.(int); ok { - fmt.println(i); - } - val = true; - fmt.println(val); - - val = nil; - - switch v in val { - case int: fmt.println("int", v); - case bool: fmt.println("bool", v); - case: fmt.println("nil"); - } - } - { - // There is a duality between `any` and `union` - // An `any` has a pointer to the data and allows for any type (open) - // A `union` has as binary blob to store the data and allows only certain types (closed) - // The following code is with `any` but has the same syntax - val: any; - val = 137; - if i, ok := val.(int); ok { - fmt.println(i); - } - val = true; - fmt.println(val); - - val = nil; - - switch v in val { - case int: fmt.println("int", v); - case bool: fmt.println("bool", v); - case: fmt.println("nil"); - } - } - - Vector3 :: distinct [3]f32; - Quaternion :: distinct quaternion128; - - // More realistic examples - { - // NOTE(bill): For the above basic examples, you may not have any - // particular use for it. However, my main use for them is not for these - // simple cases. My main use is for hierarchical types. Many prefer - // subtyping, embedding the base data into the derived types. Below is - // an example of this for a basic game Entity. - - Entity :: struct { - id: u64, - name: string, - position: Vector3, - orientation: Quaternion, - - derived: any, - }; - - Frog :: struct { - using entity: Entity, - jump_height: f32, - }; - - Monster :: struct { - using entity: Entity, - is_robot: bool, - is_zombie: bool, - }; - - // See `parametric_polymorphism` procedure for details - new_entity :: proc($T: typeid) -> ^Entity { - t := new(T); - t.derived = t^; - return t; - } - - entity := new_entity(Monster); - - switch e in entity.derived { - case Frog: - fmt.println("Ribbit"); - case Monster: - if e.is_robot do fmt.println("Robotic"); - if e.is_zombie do fmt.println("Grrrr!"); - fmt.println("I'm a monster"); - } - } - - { - // NOTE(bill): A union can be used to achieve something similar. Instead - // of embedding the base data into the derived types, the derived data - // in embedded into the base type. Below is the same example of the - // basic game Entity but using an union. - - Entity :: struct { - id: u64, - name: string, - position: Vector3, - orientation: Quaternion, - - derived: union {Frog, Monster}, - }; - - Frog :: struct { - using entity: ^Entity, - jump_height: f32, - }; - - Monster :: struct { - using entity: ^Entity, - is_robot: bool, - is_zombie: bool, - }; - - // See `parametric_polymorphism` procedure for details - new_entity :: proc($T: typeid) -> ^Entity { - t := new(Entity); - t.derived = T{entity = t}; - return t; - } - - entity := new_entity(Monster); - - switch e in entity.derived { - case Frog: - fmt.println("Ribbit"); - case Monster: - if e.is_robot do fmt.println("Robotic"); - if e.is_zombie do fmt.println("Grrrr!"); - } - - // NOTE(bill): As you can see, the usage code has not changed, only its - // memory layout. Both approaches have their own advantages but they can - // be used together to achieve different results. The subtyping approach - // can allow for a greater control of the memory layout and memory - // allocation, e.g. storing the derivatives together. However, this is - // also its disadvantage. You must either preallocate arrays for each - // derivative separation (which can be easily missed) or preallocate a - // bunch of "raw" memory; determining the maximum size of the derived - // types would require the aid of metaprogramming. Unions solve this - // particular problem as the data is stored with the base data. - // Therefore, it is possible to preallocate, e.g. [100]Entity. - - // It should be noted that the union approach can have the same memory - // layout as the any and with the same type restrictions by using a - // pointer type for the derivatives. - - /* - Entity :: struct { - ... - derived: union{^Frog, ^Monster}, - } - - Frog :: struct { - using entity: Entity, - ... - } - Monster :: struct { - using entity: Entity, - ... - - } - new_entity :: proc(T: type) -> ^Entity { - t := new(T); - t.derived = t; - return t; - } - */ - } -} - -using_statement :: proc() { - fmt.println("\n# using statement"); - // using can used to bring entities declared in a scope/namespace - // into the current scope. This can be applied to import declarations, - // import names, struct fields, procedure fields, and struct values. - - Vector3 :: struct{x, y, z: f32}; - { - Entity :: struct { - position: Vector3, - orientation: quaternion128, - }; - - // It can used like this: - foo0 :: proc(entity: ^Entity) { - fmt.println(entity.position.x, entity.position.y, entity.position.z); - } - - // The entity members can be brought into the procedure scope by using it: - foo1 :: proc(entity: ^Entity) { - using entity; - fmt.println(position.x, position.y, position.z); - } - - // The using can be applied to the parameter directly: - foo2 :: proc(using entity: ^Entity) { - fmt.println(position.x, position.y, position.z); - } - - // It can also be applied to sub-fields: - foo3 :: proc(entity: ^Entity) { - using entity.position; - fmt.println(x, y, z); - } - } - { - // We can also apply the using statement to the struct fields directly, - // making all the fields of position appear as if they on Entity itself: - Entity :: struct { - using position: Vector3, - orientation: quaternion128, - }; - foo :: proc(entity: ^Entity) { - fmt.println(entity.x, entity.y, entity.z); - } - - - // Subtype polymorphism - // It is possible to get subtype polymorphism, similar to inheritance-like - // functionality in C++, but without the requirement of vtables or unknown - // struct layout: - - Colour :: struct {r, g, b, a: u8}; - Frog :: struct { - ribbit_volume: f32, - using entity: Entity, - colour: Colour, - }; - - frog: Frog; - // Both work - foo(&frog.entity); - foo(&frog); - frog.x = 123; - - // Note: using can be applied to arbitrarily many things, which allows - // the ability to have multiple subtype polymorphism (but also its issues). - - // Note: using’d fields can still be referred by name. - } - { // using on an enum declaration - - using Foo :: enum {A, B, C}; - - f0 := A; - f1 := B; - f2 := C; - fmt.println(f0, f1, f2); - fmt.println(len(Foo)); - } -} - - -implicit_context_system :: proc() { - fmt.println("\n# implicit context system"); - // In each scope, there is an implicit value named context. This - // context variable is local to each scope and is implicitly passed - // by pointer to any procedure call in that scope (if the procedure - // has the Odin calling convention). - - // The main purpose of the implicit context system is for the ability - // to intercept third-party code and libraries and modify their - // functionality. One such case is modifying how a library allocates - // something or logs something. In C, this was usually achieved with - // the library defining macros which could be overridden so that the - // user could define what he wanted. However, not many libraries - // supported this in many languages by default which meant intercepting - // third-party code to see what it does and to change how it does it is - // not possible. - - c := context; // copy the current scope's context - - context.user_index = 456; - { - context.allocator = my_custom_allocator(); - context.user_index = 123; - what_a_fool_believes(); // the `context` for this scope is implicitly passed to `what_a_fool_believes` - } - - // `context` value is local to the scope it is in - assert(context.user_index == 456); - - what_a_fool_believes :: proc() { - c := context; // this `context` is the same as the parent procedure that it was called from - // From this example, context.user_index == 123 - // An context.allocator is assigned to the return value of `my_custom_allocator()` - assert(context.user_index == 123); - - // The memory management procedure use the `context.allocator` by - // default unless explicitly specified otherwise - china_grove := new(int); - free(china_grove); - } - - my_custom_allocator :: mem.nil_allocator; - - // By default, the context value has default values for its parameters which is - // decided in the package runtime. What the defaults are are compiler specific. - - // To see what the implicit context value contains, please see the following - // definition in package runtime. -} - -parametric_polymorphism :: proc() { - fmt.println("\n# parametric polymorphism"); - - print_value :: proc(value: $T) { - fmt.printf("print_value: %T %v\n", value, value); - } - - v1: int = 1; - v2: f32 = 2.1; - v3: f64 = 3.14; - v4: string = "message"; - - print_value(v1); - print_value(v2); - print_value(v3); - print_value(v4); - - fmt.println(); - - add :: proc(p, q: $T) -> T { - x: T = p + q; - return x; - } - - a := add(3, 4); - fmt.printf("a: %T = %v\n", a, a); - - b := add(3.2, 4.3); - fmt.printf("b: %T = %v\n", b, b); - - // This is how `new` is implemented - alloc_type :: proc($T: typeid) -> ^T { - t := cast(^T)alloc(size_of(T), align_of(T)); - t^ = T{}; // Use default initialization value - return t; - } - - copy_slice :: proc(dst, src: []$T) -> int { - n := min(len(dst), len(src)); - if n > 0 { - mem.copy(&dst[0], &src[0], n*size_of(T)); - } - return n; - } - - double_params :: proc(a: $A, b: $B) -> A { - return a + A(b); - } - - fmt.println(double_params(12, 1.345)); - - - - { // Polymorphic Types and Type Specialization - Table_Slot :: struct(Key, Value: typeid) { - occupied: bool, - hash: u32, - key: Key, - value: Value, - }; - TABLE_SIZE_MIN :: 32; - Table :: struct(Key, Value: typeid) { - count: int, - allocator: mem.Allocator, - slots: []Table_Slot(Key, Value), - }; - - // Only allow types that are specializations of a (polymorphic) slice - make_slice :: proc($T: typeid/[]$E, len: int) -> T { - return make(T, len); - } - - // Only allow types that are specializations of `Table` - allocate :: proc(table: ^$T/Table, capacity: int) { - c := context; - if table.allocator.procedure != nil do c.allocator = table.allocator; - context = c; - - table.slots = make_slice(type_of(table.slots), max(capacity, TABLE_SIZE_MIN)); - } - - expand :: proc(table: ^$T/Table) { - c := context; - if table.allocator.procedure != nil do c.allocator = table.allocator; - context = c; - - old_slots := table.slots; - defer delete(old_slots); - - cap := max(2*len(table.slots), TABLE_SIZE_MIN); - allocate(table, cap); - - for s in old_slots do if s.occupied { - put(table, s.key, s.value); - } - } - - // Polymorphic determination of a polymorphic struct - // put :: proc(table: ^$T/Table, key: T.Key, value: T.Value) { - put :: proc(table: ^Table($Key, $Value), key: Key, value: Value) { - hash := get_hash(key); // Ad-hoc method which would fail in a different scope - index := find_index(table, key, hash); - if index < 0 { - if f64(table.count) >= 0.75*f64(len(table.slots)) { - expand(table); - } - assert(table.count <= len(table.slots)); - - index = int(hash % u32(len(table.slots))); - - for table.slots[index].occupied { - if index += 1; index >= len(table.slots) { - index = 0; - } - } - - table.count += 1; - } - - slot := &table.slots[index]; - slot.occupied = true; - slot.hash = hash; - slot.key = key; - slot.value = value; - } - - - // find :: proc(table: ^$T/Table, key: T.Key) -> (T.Value, bool) { - find :: proc(table: ^Table($Key, $Value), key: Key) -> (Value, bool) { - hash := get_hash(key); - index := find_index(table, key, hash); - if index < 0 { - return Value{}, false; - } - return table.slots[index].value, true; - } - - find_index :: proc(table: ^Table($Key, $Value), key: Key, hash: u32) -> int { - if len(table.slots) <= 0 do return -1; - - index := int(hash % u32(len(table.slots))); - for table.slots[index].occupied { - if table.slots[index].hash == hash { - if table.slots[index].key == key { - return index; - } - } - - if index += 1; index >= len(table.slots) { - index = 0; - } - } - - return -1; - } - - get_hash :: proc(s: string) -> u32 { // fnv32a - h: u32 = 0x811c9dc5; - for i in 0.. (res: [N]T) { - // `N` is the constant value passed - // `I` is the type of N - // `T` is the type passed - fmt.printf("Generating an array of type %v from the value %v of type %v\n", - typeid_of(type_of(res)), N, typeid_of(I)); - for i in 0.. (c: [M][P]T) { - for i in 0.. Vector3 { - i := swizzle(a, 1, 2, 0) * swizzle(b, 2, 0, 1); - j := swizzle(a, 2, 0, 1) * swizzle(b, 1, 2, 0); - return i - j; - } - - blah :: proc(a: Vector3) -> f32 { - return a.x + a.y + a.z; - } - - x := cross(a, b); - fmt.println(x); - fmt.println(blah(x)); - } -} - -map_type :: proc() { - fmt.println("\n# map type"); - - m := make(map[string]int); - defer delete(m); - - m["Bob"] = 2; - m["Ted"] = 5; - fmt.println(m["Bob"]); - - delete_key(&m, "Ted"); - - // If an element of a key does not exist, the zero value of the - // element will be returned. To check to see if an element exists - // can be done in two ways: - elem, ok := m["Bob"]; - exists := "Bob" in m; - -} - -implicit_selector_expression :: proc() { - fmt.println("\n# implicit selector expression"); - - Foo :: enum {A, B, C}; - - f: Foo; - f = Foo.A; - f = .A; - - BAR :: bit_set[Foo]{.B, .C}; - - switch f { - case .A: - fmt.println("HERE"); - case .B: - fmt.println("NEVER"); - case .C: - fmt.println("FOREVER"); - } - - my_map := make(map[Foo]int); - defer delete(my_map); - - my_map[.A] = 123; - my_map[Foo.B] = 345; - - fmt.println(my_map[.A] + my_map[Foo.B] + my_map[.C]); -} - - -complete_switch :: proc() { - fmt.println("\n# complete_switch"); - { // enum - Foo :: enum { - A, - B, - C, - D, - }; - - f := Foo.A; - #complete switch f { - case .A: fmt.println("A"); - case .B: fmt.println("B"); - case .C: fmt.println("C"); - case .D: fmt.println("D"); - case: fmt.println("?"); - } - } - { // union - Foo :: union {int, bool}; - f: Foo = 123; - #complete switch in f { - case int: fmt.println("int"); - case bool: fmt.println("bool"); - case: - } - } -} - -cstring_example :: proc() { - fmt.println("\n# cstring_example"); - - W :: "Hellope"; - X :: cstring(W); - Y :: string(X); - - w := W; - _ = w; - x: cstring = X; - y: string = Y; - z := string(x); - fmt.println(x, y, z); - fmt.println(len(x), len(y), len(z)); - fmt.println(len(W), len(X), len(Y)); - // IMPORTANT NOTE for cstring variables - // len(cstring) is O(N) - // cast(string)cstring is O(N) -} - -bit_set_type :: proc() { - fmt.println("\n# bit_set type"); - - { - using Day :: enum { - Sunday, - Monday, - Tuesday, - Wednesday, - Thursday, - Friday, - Saturday, - }; - - Days :: distinct bit_set[Day]; - WEEKEND :: Days{Sunday, Saturday}; - - d: Days; - d = {Sunday, Monday}; - e := d | WEEKEND; - e |= {Monday}; - fmt.println(d, e); - - ok := Saturday in e; // `in` is only allowed for `map` and `bit_set` types - fmt.println(ok); - if Saturday in e { - fmt.println("Saturday in", e); - } - X :: Saturday in WEEKEND; // Constant evaluation - fmt.println(X); - fmt.println("Cardinality:", card(e)); - } - { - x: bit_set['A'..'Z']; - #assert(size_of(x) == size_of(u32)); - y: bit_set[0..8; u16]; - fmt.println(typeid_of(type_of(x))); // bit_set[A..Z] - fmt.println(typeid_of(type_of(y))); // bit_set[0..8; u16] - - incl(&x, 'F'); - assert('F' in x); - excl(&x, 'F'); - assert('F' not_in x); - - y |= {1, 4, 2}; - assert(2 in y); - } - { - Letters :: bit_set['A'..'Z']; - a := Letters{'A', 'B'}; - b := Letters{'A', 'B', 'C', 'D', 'F'}; - c := Letters{'A', 'B'}; - - assert(a <= b); // 'a' is a subset of 'b' - assert(b >= a); // 'b' is a superset of 'a' - assert(a < b); // 'a' is a strict subset of 'b' - assert(b > a); // 'b' is a strict superset of 'a' - - assert(!(a < c)); // 'a' is a not strict subset of 'c' - assert(!(c > a)); // 'c' is a not strict superset of 'a' - } -} - -deferred_procedure_associations :: proc() { - fmt.println("\n# deferred procedure associations"); - - @(deferred_out=closure) - open :: proc(s: string) -> bool { - fmt.println(s); - return true; - } - - closure :: proc(ok: bool) { - fmt.println("Goodbye?", ok); - } - - if open("Welcome") { - fmt.println("Something in the middle, mate."); - } -} - -reflection :: proc() { - fmt.println("\n# reflection"); - - Foo :: struct { - x: int `tag1`, - y: string `json:"y_field"`, - z: bool, // no tag - }; - - id := typeid_of(Foo); - names := reflect.struct_field_names(id); - types := reflect.struct_field_types(id); - tags := reflect.struct_field_tags(id); - - assert(len(names) == len(types) && len(names) == len(tags)); - - fmt.println("Foo :: struct {"); - for tag, i in tags { - name, type := names[i], types[i]; - if tag != "" { - fmt.printf("\t%s: %T `%s`,\n", name, type, tag); - } else { - fmt.printf("\t%s: %T,\n", name, type); - } - } - fmt.println("}"); - - - for tag, i in tags { - if val, ok := reflect.struct_tag_lookup(tag, "json"); ok { - fmt.printf("json: %s -> %s\n", names[i], val); - } - } -} - -quaternions :: proc() { - // Not just an April Fool's Joke any more, but a fully working thing! - fmt.println("\n# quaternions"); - - { // Quaternion operations - q := 1 + 2i + 3j + 4k; - r := quaternion(5, 6, 7, 8); - t := q * r; - fmt.printf("(%v) * (%v) = %v\n", q, r, t); - v := q / r; - fmt.printf("(%v) / (%v) = %v\n", q, r, v); - u := q + r; - fmt.printf("(%v) + (%v) = %v\n", q, r, u); - s := q - r; - fmt.printf("(%v) - (%v) = %v\n", q, r, s); - } - { // The quaternion types - q128: quaternion128; // 4xf32 - q256: quaternion256; // 4xf64 - q128 = quaternion(1, 0, 0, 0); - q256 = 1; // quaternion(1, 0, 0, 0); - } - { // Built-in procedures - q := 1 + 2i + 3j + 4k; - fmt.println("q =", q); - fmt.println("real(q) =", real(q)); - fmt.println("imag(q) =", imag(q)); - fmt.println("jmag(q) =", jmag(q)); - fmt.println("kmag(q) =", kmag(q)); - fmt.println("conj(q) =", conj(q)); - fmt.println("abs(q) =", abs(q)); - } - { // Conversion of a complex type to a quaternion type - c := 1 + 2i; - q := quaternion256(c); - fmt.println(c); - fmt.println(q); - } - { // Memory layout of Quaternions - q := 1 + 2i + 3j + 4k; - a := transmute([4]f64)q; - fmt.println("Quaternion memory layout: xyzw/(ijkr)"); - fmt.println(q); // 1.000+2.000i+3.000j+4.000k - fmt.println(a); // [2.000, 3.000, 4.000, 1.000] - } -} - -inline_for_statement :: proc() { - fmt.println("\n#inline for statements"); - - // 'inline for' works the same as if the 'inline' prefix did not - // exist but these ranged loops are explicitly unrolled which can - // be very very useful for certain optimizations - - fmt.println("Ranges"); - inline for x, i in 1..<4 { - fmt.println(x, i); - } - - fmt.println("Strings"); - inline for r, i in "Hello, 世界" { - fmt.println(r, i); - } - - fmt.println("Arrays"); - inline for elem, idx in ([4]int{1, 4, 9, 16}) { - fmt.println(elem, idx); - } - - - Foo_Enum :: enum { - A = 1, - B, - C = 6, - D, - }; - fmt.println("Enum types"); - inline for elem, idx in Foo_Enum { - fmt.println(elem, idx); - } -} - -where_clauses :: proc() { - fmt.println("\n#procedure 'where' clauses"); - - { // Sanity checks - simple_sanity_check :: proc(x: [2]int) - where len(x) > 1, - type_of(x) == [2]int { - fmt.println(x); - } - } - { // Parametric polymorphism checks - cross_2d :: proc(a, b: $T/[2]$E) -> E - where intrinsics.type_is_numeric(E) { - return a.x*b.y - a.y*b.x; - } - cross_3d :: proc(a, b: $T/[3]$E) -> T - where intrinsics.type_is_numeric(E) { - x := a.y*b.z - a.z*b.y; - y := a.z*b.x - a.x*b.z; - z := a.x*b.y - a.y*b.z; - return T{x, y, z}; - } - - a := [2]int{1, 2}; - b := [2]int{5, -3}; - fmt.println(cross_2d(a, b)); - - x := [3]f32{1, 4, 9}; - y := [3]f32{-5, 0, 3}; - fmt.println(cross_3d(x, y)); - - // Failure case - // i := [2]bool{true, false}; - // j := [2]bool{false, true}; - // fmt.println(cross_2d(i, j)); - - } - - { // Procedure groups usage - foo :: proc(x: [$N]int) -> bool - where N > 2 { - fmt.println(#procedure, "was called with the parameter", x); - return true; - } - - bar :: proc(x: [$N]int) -> bool - where 0 < N, - N <= 2 { - fmt.println(#procedure, "was called with the parameter", x); - return false; - } - - baz :: proc{foo, bar}; - - x := [3]int{1, 2, 3}; - y := [2]int{4, 9}; - ok_x := baz(x); - ok_y := baz(y); - assert(ok_x == true); - assert(ok_y == false); - } - - { // Record types - Foo :: struct(T: typeid, N: int) - where intrinsics.type_is_integer(T), - N > 2 { - x: [N]T, - y: [N-2]T, - }; - - T :: i32; - N :: 5; - f: Foo(T, N); - #assert(size_of(f) == (N+N-2)*size_of(T)); - } -} - - -when ODIN_OS == "windows" do foreign import kernel32 "system:kernel32.lib" - -foreign_system :: proc() { - fmt.println("\n#foreign system"); - when ODIN_OS == "windows" { - // It is sometimes necessarily to interface with foreign code, - // such as a C library. In Odin, this is achieved through the - // foreign system. You can “import” a library into the code - // using the same semantics as a normal import declaration. - - // This foreign import declaration will create a - // “foreign import name” which can then be used to associate - // entities within a foreign block. - - foreign kernel32 { - ExitProcess :: proc "stdcall" (exit_code: u32) --- - } - - // Foreign procedure declarations have the cdecl/c calling - // convention by default unless specified otherwise. Due to - // foreign procedures do not have a body declared within this - // code, you need append the --- symbol to the end to distinguish - // it as a procedure literal without a body and not a procedure type. - - // The attributes system can be used to change specific properties - // of entities declared within a block: - - @(default_calling_convention = "std") - foreign kernel32 { - @(link_name="GetLastError") get_last_error :: proc() -> i32 --- - } - - // Example using the link_prefix attribute - @(default_calling_convention = "std") - @(link_prefix = "Get") - foreign kernel32 { - LastError :: proc() -> i32 --- - } - } -} - -ranged_fields_for_array_compound_literals :: proc() { - fmt.println("\n#ranged fields for array compound literals"); - { // Normal Array Literal - foo := [?]int{1, 4, 9, 16}; - fmt.println(foo); - } - { // Indexed - foo := [?]int{ - 3 = 16, - 1 = 4, - 2 = 9, - 0 = 1, - }; - fmt.println(foo); - } - { // Ranges - i := 2; - foo := [?]int { - 0 = 123, - 5..9 = 54, - 10..<16 = i*3 + (i-1)*2, - }; - #assert(len(foo) == 16); - fmt.println(foo); // [123, 0, 0, 0, 0, 54, 54, 54, 54, 54, 8, 8, 8, 8, 8] - } - { // Slice and Dynamic Array support - i := 2; - foo_slice := []int { - 0 = 123, - 5..9 = 54, - 10..<16 = i*3 + (i-1)*2, - }; - assert(len(foo_slice) == 16); - fmt.println(foo_slice); // [123, 0, 0, 0, 0, 54, 54, 54, 54, 54, 8, 8, 8, 8, 8] - - foo_dynamic_array := [dynamic]int { - 0 = 123, - 5..9 = 54, - 10..<16 = i*3 + (i-1)*2, - }; - assert(len(foo_dynamic_array) == 16); - fmt.println(foo_dynamic_array); // [123, 0, 0, 0, 0, 54, 54, 54, 54, 54, 8, 8, 8, 8, 8] - } -} - -deprecated_attribute :: proc() { - @(deprecated="Use foo_v2 instead") - foo_v1 :: proc(x: int) { - fmt.println("foo_v1"); - } - foo_v2 :: proc(x: int) { - fmt.println("foo_v2"); - } - - // NOTE: Uncomment to see the warning messages - // foo_v1(1); -} - -range_statements_with_multiple_return_values :: proc() { - // IMPORTANT NOTE(bill, 2019-11-02): This feature is subject to be changed/removed - fmt.println("\n#range statements with multiple return values"); - My_Iterator :: struct { - index: int, - data: []i32, - }; - make_my_iterator :: proc(data: []i32) -> My_Iterator { - return My_Iterator{data = data}; - } - my_iterator :: proc(it: ^My_Iterator) -> (val: i32, idx: int, cond: bool) { - if cond = it.index < len(it.data); cond { - val = it.data[it.index]; - idx = it.index; - it.index += 1; - } - return; - } - - data := make([]i32, 6); - for _, i in data { - data[i] = i32(i*i); - } - - { - it := make_my_iterator(data); - for val in my_iterator(&it) { - fmt.println(val); - } - } - { - it := make_my_iterator(data); - for val, idx in my_iterator(&it) { - fmt.println(val, idx); - } - } - { - it := make_my_iterator(data); - for { - val, _, cond := my_iterator(&it); - if !cond do break; - fmt.println(val); - } - } -} - -soa_struct_layout :: proc() { - // IMPORTANT NOTE(bill, 2019-11-03): This feature is subject to be changed/removed - // NOTE(bill): Most likely #soa [N]T - fmt.println("\n#SOA Struct Layout"); - - { - Vector3 :: struct {x, y, z: f32}; - - N :: 2; - v_aos: [N]Vector3; - v_aos[0].x = 1; - v_aos[0].y = 4; - v_aos[0].z = 9; - - fmt.println(len(v_aos)); - fmt.println(v_aos[0]); - fmt.println(v_aos[0].x); - fmt.println(&v_aos[0].x); - - v_aos[1] = {0, 3, 4}; - v_aos[1].x = 2; - fmt.println(v_aos[1]); - fmt.println(v_aos); - - v_soa: #soa[N]Vector3; - - v_soa[0].x = 1; - v_soa[0].y = 4; - v_soa[0].z = 9; - - - // Same syntax as AOS and treat as if it was an array - fmt.println(len(v_soa)); - fmt.println(v_soa[0]); - fmt.println(v_soa[0].x); - v_soa[1] = {0, 3, 4}; - v_soa[1].x = 2; - fmt.println(v_soa[1]); - - // Can use SOA syntax if necessary - v_soa.x[0] = 1; - v_soa.y[0] = 4; - v_soa.z[0] = 9; - fmt.println(v_soa.x[0]); - - // Same pointer addresses with both syntaxes - assert(&v_soa[0].x == &v_soa.x[0]); - - - // Same fmt printing - fmt.println(v_aos); - fmt.println(v_soa); - } - { - // Works with arrays of length <= 4 which have the implicit fields xyzw/rgba - Vector3 :: distinct [3]f32; - - N :: 2; - v_aos: [N]Vector3; - v_aos[0].x = 1; - v_aos[0].y = 4; - v_aos[0].z = 9; - - v_soa: #soa[N]Vector3; - - v_soa[0].x = 1; - v_soa[0].y = 4; - v_soa[0].z = 9; - } -} - - -main :: proc() { - when true { - the_basics(); - control_flow(); - named_proc_return_parameters(); - explicit_procedure_overloading(); - struct_type(); - union_type(); - using_statement(); - implicit_context_system(); - parametric_polymorphism(); - array_programming(); - map_type(); - implicit_selector_expression(); - complete_switch(); - cstring_example(); - bit_set_type(); - deferred_procedure_associations(); - reflection(); - quaternions(); - inline_for_statement(); - where_clauses(); - foreign_system(); - ranged_fields_for_array_compound_literals(); - deprecated_attribute(); - range_statements_with_multiple_return_values(); - soa_struct_layout(); - } -} - diff --git a/src/array.cpp b/src/array.cpp index ac3727978..d08bd647f 100644 --- a/src/array.cpp +++ b/src/array.cpp @@ -89,7 +89,9 @@ template void slice_init(Slice *s, gbAllocator const &allocator, isize count) { GB_ASSERT(count >= 0); s->data = gb_alloc_array(allocator, T, count); - GB_ASSERT(s->data != nullptr); + if (count > 0) { + GB_ASSERT(s->data != nullptr); + } s->count = count; } diff --git a/src/big_int.cpp b/src/big_int.cpp index 20f940e8e..5509545ca 100644 --- a/src/big_int.cpp +++ b/src/big_int.cpp @@ -40,7 +40,7 @@ typedef mp_int BigInt; void big_int_from_u64(BigInt *dst, u64 x); void big_int_from_i64(BigInt *dst, i64 x); void big_int_init (BigInt *dst, BigInt const *src); -void big_int_from_string(BigInt *dst, String const &s); +void big_int_from_string(BigInt *dst, String const &s, bool *success); void big_int_dealloc(BigInt *dst) { mp_clear(dst); @@ -84,7 +84,7 @@ void big_int_quo_eq(BigInt *dst, BigInt const *x); void big_int_rem_eq(BigInt *dst, BigInt const *x); bool big_int_is_neg(BigInt const *x); - +void big_int_neg(BigInt *dst, BigInt const *x); void big_int_add_eq(BigInt *dst, BigInt const *x) { BigInt res = {}; @@ -169,7 +169,11 @@ BigInt big_int_make_i64(i64 x) { } -void big_int_from_string(BigInt *dst, String const &s) { +void big_int_from_string(BigInt *dst, String const &s, bool *success) { + *success = true; + + bool is_negative = false; + u64 base = 10; bool has_prefix = false; if (s.len > 2 && s[0] == '0') { @@ -197,11 +201,26 @@ void big_int_from_string(BigInt *dst, String const &s) { isize i = 0; for (; i < len; i++) { Rune r = cast(Rune)text[i]; + + if (r == '-') { + if (is_negative) { + // NOTE(Jeroen): Can't have a doubly negative number. + *success = false; + return; + } + is_negative = true; + continue; + } + if (r == '_') { continue; } u64 v = u64_digit_value(r); if (v >= base) { + // NOTE(Jeroen): Can still be a valid integer if the next character is an `e` or `E`. + if (r != 'e' && r != 'E') { + *success = false; + } break; } BigInt val = big_int_make_u64(v); @@ -225,6 +244,7 @@ void big_int_from_string(BigInt *dst, String const &s) { if (gb_char_is_digit(r)) { v = u64_digit_value(r); } else { + *success = false; break; } exp *= 10; @@ -234,6 +254,10 @@ void big_int_from_string(BigInt *dst, String const &s) { big_int_mul_eq(dst, &b); } } + + if (is_negative) { + big_int_neg(dst, dst); + } } diff --git a/src/bug_report.cpp b/src/bug_report.cpp index 9a1cb2254..02a2b1ba2 100644 --- a/src/bug_report.cpp +++ b/src/bug_report.cpp @@ -17,6 +17,11 @@ #include #endif +#if defined(GB_SYSTEM_OPENBSD) + #include + #include +#endif + /* NOTE(Jeroen): This prints the Windows product edition only, to be called from `print_platform_details`. */ @@ -242,6 +247,14 @@ void report_ram_info() { if (sysctl(sysctls, 2, &ram_amount, &val_size, NULL, 0) != -1) { gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1)); } + #elif defined(GB_SYSTEM_OPENBSD) + uint64_t ram_amount; + size_t val_size = sizeof(ram_amount); + + int sysctls[] = { CTL_HW, HW_PHYSMEM64 }; + if (sysctl(sysctls, 2, &ram_amount, &val_size, NULL, 0) != -1) { + gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1)); + } #else gb_printf("Unknown.\n"); #endif @@ -473,11 +486,11 @@ void print_bug_report_help() { #elif defined(GB_SYSTEM_LINUX) /* - Try to parse `/usr/lib/os-release` for `PRETTY_NAME="Ubuntu 20.04.3 LTS` + Try to parse `/etc/os-release` for `PRETTY_NAME="Ubuntu 20.04.3 LTS` */ gbAllocator a = heap_allocator(); - gbFileContents release = gb_file_read_contents(a, 1, "/usr/lib/os-release"); + gbFileContents release = gb_file_read_contents(a, 1, "/etc/os-release"); defer (gb_file_free_contents(&release)); b32 found = 0; @@ -643,6 +656,14 @@ void print_bug_report_help() { } else { gb_printf("macOS: Unknown\n"); } + #elif defined(GB_SYSTEM_OPENBSD) + struct utsname un; + + if (uname(&un) != -1) { + gb_printf("%s %s %s %s\n", un.sysname, un.release, un.version, un.machine); + } else { + gb_printf("OpenBSD: Unknown\n"); + } #else gb_printf("Unknown\n"); @@ -657,4 +678,4 @@ void print_bug_report_help() { And RAM info. */ report_ram_info(); -} \ No newline at end of file +} diff --git a/src/build_settings.cpp b/src/build_settings.cpp index b4a934ec8..65da09df0 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -1,14 +1,13 @@ -#if defined(GB_SYSTEM_FREEBSD) +#if defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD) #include #include #endif - // #if defined(GB_SYSTEM_WINDOWS) // #define DEFAULT_TO_THREADED_CHECKER // #endif -enum TargetOsKind { +enum TargetOsKind : u16 { TargetOs_Invalid, TargetOs_windows, @@ -16,6 +15,7 @@ enum TargetOsKind { TargetOs_linux, TargetOs_essence, TargetOs_freebsd, + TargetOs_openbsd, TargetOs_wasi, TargetOs_js, @@ -25,11 +25,12 @@ enum TargetOsKind { TargetOs_COUNT, }; -enum TargetArchKind { +enum TargetArchKind : u16 { TargetArch_Invalid, TargetArch_amd64, TargetArch_i386, + TargetArch_arm32, TargetArch_arm64, TargetArch_wasm32, TargetArch_wasm64, @@ -37,7 +38,7 @@ enum TargetArchKind { TargetArch_COUNT, }; -enum TargetEndianKind { +enum TargetEndianKind : u8 { TargetEndian_Invalid, TargetEndian_Little, @@ -46,6 +47,16 @@ enum TargetEndianKind { TargetEndian_COUNT, }; +enum TargetABIKind : u16 { + TargetABI_Default, + + TargetABI_Win64, + TargetABI_SysV, + + TargetABI_COUNT, +}; + + String target_os_names[TargetOs_COUNT] = { str_lit(""), str_lit("windows"), @@ -53,6 +64,7 @@ String target_os_names[TargetOs_COUNT] = { str_lit("linux"), str_lit("essence"), str_lit("freebsd"), + str_lit("openbsd"), str_lit("wasi"), str_lit("js"), @@ -64,6 +76,7 @@ String target_arch_names[TargetArch_COUNT] = { str_lit(""), str_lit("amd64"), str_lit("i386"), + str_lit("arm32"), str_lit("arm64"), str_lit("wasm32"), str_lit("wasm64"), @@ -75,12 +88,19 @@ String target_endian_names[TargetEndian_COUNT] = { str_lit("big"), }; +String target_abi_names[TargetABI_COUNT] = { + str_lit(""), + str_lit("win64"), + str_lit("sysv"), +}; + TargetEndianKind target_endians[TargetArch_COUNT] = { TargetEndian_Invalid, TargetEndian_Little, TargetEndian_Little, TargetEndian_Little, TargetEndian_Little, + TargetEndian_Little, }; #ifndef ODIN_VERSION_RAW @@ -98,6 +118,7 @@ struct TargetMetrics { isize max_align; String target_triplet; String target_data_layout; + TargetABIKind abi; }; @@ -165,6 +186,36 @@ enum TimingsExportFormat : i32 { TimingsExportCSV = 2, }; +enum ErrorPosStyle { + ErrorPosStyle_Default, // path(line:column) msg + ErrorPosStyle_Unix, // path:line:column: msg + + ErrorPosStyle_COUNT +}; + +enum RelocMode : u8 { + RelocMode_Default, + RelocMode_Static, + RelocMode_PIC, + RelocMode_DynamicNoPIC, +}; + +enum BuildPath : u8 { + BuildPath_Main_Package, // Input Path to the package directory (or file) we're building. + BuildPath_RC, // Input Path for .rc file, can be set with `-resource:`. + BuildPath_RES, // Output Path for .res file, generated from previous. + BuildPath_Win_SDK_Root, // windows_sdk_root + BuildPath_Win_SDK_UM_Lib, // windows_sdk_um_library_path + BuildPath_Win_SDK_UCRT_Lib, // windows_sdk_ucrt_library_path + BuildPath_VS_EXE, // vs_exe_path + BuildPath_VS_LIB, // vs_library_path + + BuildPath_Output, // Output Path for .exe, .dll, .so, etc. Can be overridden with `-out:`. + BuildPath_PDB, // Output Path for .pdb file, can be overridden with `-pdb-name:`. + + BuildPathCOUNT, +}; + // This stores the information for the specify architecture of this build struct BuildContext { // Constants @@ -175,7 +226,10 @@ struct BuildContext { String ODIN_ROOT; // Odin ROOT bool ODIN_DEBUG; // Odin in debug mode bool ODIN_DISABLE_ASSERT; // Whether the default 'assert' et al is disabled in code or not -bool ODIN_DEFAULT_TO_NIL_ALLOCATOR; // Whether the default allocator is a "nil" allocator or not (i.e. it does nothing) + bool ODIN_DEFAULT_TO_NIL_ALLOCATOR; // Whether the default allocator is a "nil" allocator or not (i.e. it does nothing) + bool ODIN_FOREIGN_ERROR_PROCEDURES; + + ErrorPosStyle ODIN_ERROR_POS_STYLE; TargetEndianKind endian_kind; @@ -190,9 +244,13 @@ bool ODIN_DEFAULT_TO_NIL_ALLOCATOR; // Whether the default allocator is a "nil bool show_help; + Array build_paths; // Contains `Path` objects to output filename, pdb, resource and intermediate files. + // BuildPath enum contains the indices of paths we know *before* the work starts. + String out_filepath; String resource_filepath; String pdb_filepath; + bool has_resource; String link_flags; String extra_linker_flags; @@ -243,6 +301,12 @@ bool ODIN_DEFAULT_TO_NIL_ALLOCATOR; // Whether the default allocator is a "nil bool copy_file_contents; + bool disallow_rtti; + + RelocMode reloc_mode; + bool disable_red_zone; + + u32 cmd_doc_flags; Array extra_packages; @@ -254,10 +318,13 @@ bool ODIN_DEFAULT_TO_NIL_ALLOCATOR; // Whether the default allocator is a "nil isize thread_count; PtrMap defined_values; + + BlockingMutex target_features_mutex; + StringSet target_features_set; + String target_features_string; + }; - - gb_global BuildContext build_context = {0}; bool global_warnings_as_errors(void) { @@ -268,7 +335,7 @@ bool global_ignore_warnings(void) { } -gb_global TargetMetrics target_windows_386 = { +gb_global TargetMetrics target_windows_i386 = { TargetOs_windows, TargetArch_i386, 4, @@ -284,7 +351,7 @@ gb_global TargetMetrics target_windows_amd64 = { str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"), }; -gb_global TargetMetrics target_linux_386 = { +gb_global TargetMetrics target_linux_i386 = { TargetOs_linux, TargetArch_i386, 4, @@ -306,7 +373,16 @@ gb_global TargetMetrics target_linux_arm64 = { 8, 16, str_lit("aarch64-linux-elf"), - str_lit("e-m:e-i8:8:32-i16:32-i64:64-i128:128-n32:64-S128"), + str_lit("e-m:o-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"), +}; + +gb_global TargetMetrics target_linux_arm32 = { + TargetOs_linux, + TargetArch_arm32, + 4, + 8, + str_lit("arm-linux-gnu"), + str_lit("e-m:o-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"), }; gb_global TargetMetrics target_darwin_amd64 = { @@ -327,7 +403,7 @@ gb_global TargetMetrics target_darwin_arm64 = { str_lit("e-m:o-i64:64-i128:128-n32:64-S128"), // TODO(bill): Is this correct? }; -gb_global TargetMetrics target_freebsd_386 = { +gb_global TargetMetrics target_freebsd_i386 = { TargetOs_freebsd, TargetArch_i386, 4, @@ -344,6 +420,15 @@ gb_global TargetMetrics target_freebsd_amd64 = { str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"), }; +gb_global TargetMetrics target_openbsd_amd64 = { + TargetOs_openbsd, + TargetArch_amd64, + 8, + 16, + str_lit("x86_64-unknown-openbsd-elf"), + str_lit("e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"), +}; + gb_global TargetMetrics target_essence_amd64 = { TargetOs_essence, TargetArch_amd64, @@ -370,6 +455,15 @@ gb_global TargetMetrics target_js_wasm32 = { str_lit(""), }; +gb_global TargetMetrics target_js_wasm64 = { + TargetOs_js, + TargetArch_wasm64, + 8, + 16, + str_lit("wasm64-js-js"), + str_lit(""), +}; + gb_global TargetMetrics target_wasi_wasm32 = { TargetOs_wasi, TargetArch_wasm32, @@ -389,6 +483,16 @@ gb_global TargetMetrics target_wasi_wasm32 = { // str_lit(""), // }; +gb_global TargetMetrics target_freestanding_amd64_sysv = { + TargetOs_freestanding, + TargetArch_amd64, + 8, + 16, + str_lit("x86_64-pc-none-gnu"), + str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"), + TargetABI_SysV, +}; + struct NamedTargetMetrics { @@ -400,17 +504,21 @@ gb_global NamedTargetMetrics named_targets[] = { { str_lit("darwin_amd64"), &target_darwin_amd64 }, { str_lit("darwin_arm64"), &target_darwin_arm64 }, { str_lit("essence_amd64"), &target_essence_amd64 }, - { str_lit("linux_386"), &target_linux_386 }, + { str_lit("linux_i386"), &target_linux_i386 }, { str_lit("linux_amd64"), &target_linux_amd64 }, { str_lit("linux_arm64"), &target_linux_arm64 }, - { str_lit("windows_386"), &target_windows_386 }, + { str_lit("linux_arm32"), &target_linux_arm32 }, + { str_lit("windows_i386"), &target_windows_i386 }, { str_lit("windows_amd64"), &target_windows_amd64 }, - { str_lit("freebsd_386"), &target_freebsd_386 }, + { str_lit("freebsd_i386"), &target_freebsd_i386 }, { str_lit("freebsd_amd64"), &target_freebsd_amd64 }, + { str_lit("openbsd_amd64"), &target_openbsd_amd64 }, { str_lit("freestanding_wasm32"), &target_freestanding_wasm32 }, { str_lit("wasi_wasm32"), &target_wasi_wasm32 }, { str_lit("js_wasm32"), &target_js_wasm32 }, - // { str_lit("freestanding_wasm64"), &target_freestanding_wasm64 }, + { str_lit("js_wasm64"), &target_js_wasm64 }, + + { str_lit("freestanding_amd64_sysv"), &target_freestanding_amd64_sysv }, }; NamedTargetMetrics *selected_target_metrics; @@ -524,6 +632,15 @@ bool is_arch_wasm(void) { return false; } +bool is_arch_x86(void) { + switch (build_context.metrics.arch) { + case TargetArch_i386: + case TargetArch_amd64: + return true; + } + return false; +} + bool allow_check_foreign_filepath(void) { switch (build_context.metrics.arch) { case TargetArch_wasm32: @@ -533,7 +650,6 @@ bool allow_check_foreign_filepath(void) { return true; } - // TODO(bill): OS dependent versions for the BuildContext // join_path // is_dir @@ -712,10 +828,38 @@ String internal_odin_root_dir(void) { len = readlink("/proc/curproc/exe", &path_buf[0], path_buf.count); #elif defined(GB_SYSTEM_DRAGONFLYBSD) len = readlink("/proc/curproc/file", &path_buf[0], path_buf.count); -#else +#elif defined(GB_SYSTEM_LINUX) len = readlink("/proc/self/exe", &path_buf[0], path_buf.count); +#elif defined(GB_SYSTEM_OPENBSD) + int error; + int mib[] = { + CTL_KERN, + KERN_PROC_ARGS, + getpid(), + KERN_PROC_ARGV, + }; + // get argv size + error = sysctl(mib, 4, NULL, (size_t *) &len, NULL, 0); + if (error == -1) { + // sysctl error + return make_string(nullptr, 0); + } + // get argv + char **argv = (char **)gb_malloc(len); + error = sysctl(mib, 4, argv, (size_t *) &len, NULL, 0); + if (error == -1) { + // sysctl error + gb_mfree(argv); + return make_string(nullptr, 0); + } + // copy argv[0] to path_buf + len = gb_strlen(argv[0]); + if(len < path_buf.count) { + gb_memmove(&path_buf[0], argv[0], len); + } + gb_mfree(argv); #endif - if(len == 0) { + if(len == 0 || len == -1) { return make_string(nullptr, 0); } if (len < path_buf.count) { @@ -843,6 +987,21 @@ bool has_asm_extension(String const &path) { return false; } +// temporary +char *token_pos_to_string(TokenPos const &pos) { + gbString s = gb_string_make_reserve(temporary_allocator(), 128); + String file = get_file_path_string(pos.file_id); + switch (build_context.ODIN_ERROR_POS_STYLE) { + default: /*fallthrough*/ + case ErrorPosStyle_Default: + s = gb_string_append_fmt(s, "%.*s(%d:%d)", LIT(file), pos.line, pos.column); + break; + case ErrorPosStyle_Unix: + s = gb_string_append_fmt(s, "%.*s:%d:%d:", LIT(file), pos.line, pos.column); + break; + } + return s; +} void init_build_context(TargetMetrics *cross_target) { BuildContext *bc = &build_context; @@ -855,7 +1014,31 @@ void init_build_context(TargetMetrics *cross_target) { bc->ODIN_VENDOR = str_lit("odin"); bc->ODIN_VERSION = ODIN_VERSION; bc->ODIN_ROOT = odin_root_dir(); - + + { + char const *found = gb_get_env("ODIN_ERROR_POS_STYLE", permanent_allocator()); + if (found) { + ErrorPosStyle kind = ErrorPosStyle_Default; + String style = make_string_c(found); + style = string_trim_whitespace(style); + if (style == "" || style == "default" || style == "odin") { + kind = ErrorPosStyle_Default; + } else if (style == "unix" || style == "gcc" || style == "clang" || style == "llvm") { + kind = ErrorPosStyle_Unix; + } else { + gb_printf_err("Invalid ODIN_ERROR_POS_STYLE: got %.*s\n", LIT(style)); + gb_printf_err("Valid formats:\n"); + gb_printf_err("\t\"default\" or \"odin\"\n"); + gb_printf_err("\t\tpath(line:column) message\n"); + gb_printf_err("\t\"unix\"\n"); + gb_printf_err("\t\tpath:line:column: message\n"); + gb_exit(1); + } + + build_context.ODIN_ERROR_POS_STYLE = kind; + } + } + bc->copy_file_contents = true; TargetMetrics *metrics = nullptr; @@ -871,6 +1054,8 @@ void init_build_context(TargetMetrics *cross_target) { #endif #elif defined(GB_SYSTEM_FREEBSD) metrics = &target_freebsd_amd64; + #elif defined(GB_SYSTEM_OPENBSD) + metrics = &target_openbsd_amd64; #elif defined(GB_CPU_ARM) metrics = &target_linux_arm64; #else @@ -878,13 +1063,13 @@ void init_build_context(TargetMetrics *cross_target) { #endif #else #if defined(GB_SYSTEM_WINDOWS) - metrics = &target_windows_386; + metrics = &target_windows_i386; #elif defined(GB_SYSTEM_OSX) #error "Build Error: Unsupported architecture" #elif defined(GB_SYSTEM_FREEBSD) - metrics = &target_freebsd_386; + metrics = &target_freebsd_i386; #else - metrics = &target_linux_386; + metrics = &target_linux_i386; #endif #endif @@ -912,6 +1097,21 @@ void init_build_context(TargetMetrics *cross_target) { bc->threaded_checker = true; #endif + if (bc->disable_red_zone) { + if (is_arch_wasm() && bc->metrics.os == TargetOs_freestanding) { + gb_printf_err("-disable-red-zone is not support for this target"); + gb_exit(1); + } + } + + if (bc->metrics.os == TargetOs_freestanding) { + bc->no_entry_point = true; + } else { + if (bc->disallow_rtti) { + gb_printf_err("-disallow-rtti is only allowed on freestanding targets\n"); + gb_exit(1); + } + } // NOTE(zangent): The linker flags to set the build architecture are different // across OSs. It doesn't make sense to allocate extra data on the heap @@ -929,6 +1129,9 @@ void init_build_context(TargetMetrics *cross_target) { case TargetOs_freebsd: bc->link_flags = str_lit("-arch x86-64 "); break; + case TargetOs_openbsd: + bc->link_flags = str_lit("-arch x86-64 "); + break; } } else if (bc->metrics.arch == TargetArch_i386) { switch (bc->metrics.os) { @@ -946,6 +1149,15 @@ void init_build_context(TargetMetrics *cross_target) { bc->link_flags = str_lit("-arch x86 "); break; } + } else if (bc->metrics.arch == TargetArch_arm32) { + switch (bc->metrics.os) { + case TargetOs_linux: + bc->link_flags = str_lit("-arch arm "); + break; + default: + gb_printf_err("Compiler Error: Unsupported architecture\n"); + gb_exit(1); + } } else if (bc->metrics.arch == TargetArch_arm64) { switch (bc->metrics.os) { case TargetOs_darwin: @@ -961,16 +1173,16 @@ void init_build_context(TargetMetrics *cross_target) { // link_flags = gb_string_appendc(link_flags, "--export-table "); link_flags = gb_string_appendc(link_flags, "--allow-undefined "); if (bc->metrics.arch == TargetArch_wasm64) { - link_flags = gb_string_appendc(link_flags, "-mwas64 "); + link_flags = gb_string_appendc(link_flags, "-mwasm64 "); } - if (bc->metrics.os == TargetOs_freestanding) { + if (bc->no_entry_point) { link_flags = gb_string_appendc(link_flags, "--no-entry "); } bc->link_flags = make_string_c(link_flags); // Disallow on wasm - build_context.use_separate_modules = false; + bc->use_separate_modules = false; } else { gb_printf_err("Compiler Error: Unsupported architecture\n"); gb_exit(1); @@ -981,3 +1193,305 @@ void init_build_context(TargetMetrics *cross_target) { #undef LINK_FLAG_X64 #undef LINK_FLAG_386 } + +#if defined(GB_SYSTEM_WINDOWS) +// NOTE(IC): In order to find Visual C++ paths without relying on environment variables. +// NOTE(Jeroen): No longer needed in `main.cpp -> linker_stage`. We now resolve those paths in `init_build_paths`. +#include "microsoft_craziness.h" +#endif + + +Array split_by_comma(String const &list) { + isize n = 1; + for (isize i = 0; i < list.len; i++) { + if (list.text[i] == ',') { + n++; + } + } + auto res = array_make(heap_allocator(), n); + + String s = list; + for (isize i = 0; i < n; i++) { + isize m = string_index_byte(s, ','); + if (m < 0) { + res[i] = s; + break; + } + res[i] = substring(s, 0, m); + s = substring(s, m+1, s.len); + } + return res; +} + +bool check_target_feature_is_valid(TokenPos pos, String const &feature) { + // TODO(bill): check_target_feature_is_valid + return true; +} + +bool check_target_feature_is_enabled(TokenPos pos, String const &target_feature_list) { + BuildContext *bc = &build_context; + mutex_lock(&bc->target_features_mutex); + defer (mutex_unlock(&bc->target_features_mutex)); + + auto items = split_by_comma(target_feature_list); + array_free(&items); + for_array(i, items) { + String const &item = items.data[i]; + if (!check_target_feature_is_valid(pos, item)) { + error(pos, "Target feature '%.*s' is not valid", LIT(item)); + return false; + } + if (!string_set_exists(&bc->target_features_set, item)) { + error(pos, "Target feature '%.*s' is not enabled", LIT(item)); + return false; + } + } + + return true; +} + +void enable_target_feature(TokenPos pos, String const &target_feature_list) { + BuildContext *bc = &build_context; + mutex_lock(&bc->target_features_mutex); + defer (mutex_unlock(&bc->target_features_mutex)); + + auto items = split_by_comma(target_feature_list); + array_free(&items); + for_array(i, items) { + String const &item = items.data[i]; + if (!check_target_feature_is_valid(pos, item)) { + error(pos, "Target feature '%.*s' is not valid", LIT(item)); + } + } +} + + +char const *target_features_set_to_cstring(gbAllocator allocator, bool with_quotes) { + isize len = 0; + for_array(i, build_context.target_features_set.entries) { + if (i != 0) { + len += 1; + } + String feature = build_context.target_features_set.entries[i].value; + len += feature.len; + if (with_quotes) len += 2; + } + char *features = gb_alloc_array(allocator, char, len+1); + len = 0; + for_array(i, build_context.target_features_set.entries) { + if (i != 0) { + features[len++] = ','; + } + + if (with_quotes) features[len++] = '"'; + String feature = build_context.target_features_set.entries[i].value; + gb_memmove(features, feature.text, feature.len); + len += feature.len; + if (with_quotes) features[len++] = '"'; + } + features[len++] = 0; + + return features; +} + +// NOTE(Jeroen): Set/create the output and other paths and report an error as appropriate. +// We've previously called `parse_build_flags`, so `out_filepath` should be set. +bool init_build_paths(String init_filename) { + gbAllocator ha = heap_allocator(); + BuildContext *bc = &build_context; + + // NOTE(Jeroen): We're pre-allocating BuildPathCOUNT slots so that certain paths are always at the same enumerated index. + array_init(&bc->build_paths, permanent_allocator(), BuildPathCOUNT); + + string_set_init(&bc->target_features_set, heap_allocator(), 1024); + mutex_init(&bc->target_features_mutex); + + // [BuildPathMainPackage] Turn given init path into a `Path`, which includes normalizing it into a full path. + bc->build_paths[BuildPath_Main_Package] = path_from_string(ha, init_filename); + + bool produces_output_file = false; + if (bc->command_kind == Command_doc && bc->cmd_doc_flags & CmdDocFlag_DocFormat) { + produces_output_file = true; + } else if (bc->command_kind & Command__does_build) { + produces_output_file = true; + } + + if (!produces_output_file) { + // Command doesn't produce output files. We're done. + return true; + } + + #if defined(GB_SYSTEM_WINDOWS) + if (bc->metrics.os == TargetOs_windows) { + if (bc->resource_filepath.len > 0) { + bc->build_paths[BuildPath_RC] = path_from_string(ha, bc->resource_filepath); + bc->build_paths[BuildPath_RES] = path_from_string(ha, bc->resource_filepath); + bc->build_paths[BuildPath_RC].ext = copy_string(ha, STR_LIT("rc")); + bc->build_paths[BuildPath_RES].ext = copy_string(ha, STR_LIT("res")); + } + + if (bc->pdb_filepath.len > 0) { + bc->build_paths[BuildPath_PDB] = path_from_string(ha, bc->pdb_filepath); + } + + if ((bc->command_kind & Command__does_build) && (!bc->ignore_microsoft_magic)) { + // NOTE(ic): It would be nice to extend this so that we could specify the Visual Studio version that we want instead of defaulting to the latest. + Find_Result_Utf8 find_result = find_visual_studio_and_windows_sdk_utf8(); + defer (mc_free_all()); + + if (find_result.windows_sdk_version == 0) { + gb_printf_err("Windows SDK not found.\n"); + return false; + } + + if (!build_context.use_lld && find_result.vs_exe_path.len == 0) { + gb_printf_err("link.exe not found.\n"); + return false; + } + + if (find_result.vs_library_path.len == 0) { + gb_printf_err("VS library path not found.\n"); + return false; + } + + if (find_result.windows_sdk_um_library_path.len > 0) { + GB_ASSERT(find_result.windows_sdk_ucrt_library_path.len > 0); + + if (find_result.windows_sdk_root.len > 0) { + bc->build_paths[BuildPath_Win_SDK_Root] = path_from_string(ha, find_result.windows_sdk_root); + } + + if (find_result.windows_sdk_um_library_path.len > 0) { + bc->build_paths[BuildPath_Win_SDK_UM_Lib] = path_from_string(ha, find_result.windows_sdk_um_library_path); + } + + if (find_result.windows_sdk_ucrt_library_path.len > 0) { + bc->build_paths[BuildPath_Win_SDK_UCRT_Lib] = path_from_string(ha, find_result.windows_sdk_ucrt_library_path); + } + + if (find_result.vs_exe_path.len > 0) { + bc->build_paths[BuildPath_VS_EXE] = path_from_string(ha, find_result.vs_exe_path); + } + + if (find_result.vs_library_path.len > 0) { + bc->build_paths[BuildPath_VS_LIB] = path_from_string(ha, find_result.vs_library_path); + } + } + } + } + #endif + + // All the build targets and OSes. + String output_extension; + + if (bc->command_kind == Command_doc && bc->cmd_doc_flags & CmdDocFlag_DocFormat) { + output_extension = STR_LIT("odin-doc"); + } else if (is_arch_wasm()) { + output_extension = STR_LIT("wasm"); + } else if (build_context.build_mode == BuildMode_Executable) { + // By default use a .bin executable extension. + output_extension = STR_LIT("bin"); + + if (build_context.metrics.os == TargetOs_windows) { + output_extension = STR_LIT("exe"); + } else if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) { + output_extension = make_string(nullptr, 0); + } + } else if (build_context.build_mode == BuildMode_DynamicLibrary) { + // By default use a .so shared library extension. + output_extension = STR_LIT("so"); + + if (build_context.metrics.os == TargetOs_windows) { + output_extension = STR_LIT("dll"); + } else if (build_context.metrics.os == TargetOs_darwin) { + output_extension = STR_LIT("dylib"); + } + } else if (build_context.build_mode == BuildMode_Object) { + // By default use a .o object extension. + output_extension = STR_LIT("o"); + + if (build_context.metrics.os == TargetOs_windows) { + output_extension = STR_LIT("obj"); + } + } else if (build_context.build_mode == BuildMode_Assembly) { + // By default use a .S asm extension. + output_extension = STR_LIT("S"); + } else if (build_context.build_mode == BuildMode_LLVM_IR) { + output_extension = STR_LIT("ll"); + } else { + GB_PANIC("Unhandled build mode/target combination.\n"); + } + + if (bc->out_filepath.len > 0) { + bc->build_paths[BuildPath_Output] = path_from_string(ha, bc->out_filepath); + if (build_context.metrics.os == TargetOs_windows) { + String output_file = path_to_string(ha, bc->build_paths[BuildPath_Output]); + defer (gb_free(ha, output_file.text)); + if (path_is_directory(bc->build_paths[BuildPath_Output])) { + gb_printf_err("Output path %.*s is a directory.\n", LIT(output_file)); + return false; + } else if (bc->build_paths[BuildPath_Output].ext.len == 0) { + gb_printf_err("Output path %.*s must have an appropriate extension.\n", LIT(output_file)); + return false; + } + } + } else { + Path output_path; + + if (str_eq(init_filename, str_lit("."))) { + // We must name the output file after the current directory. + debugf("Output name will be created from current base name %.*s.\n", LIT(bc->build_paths[BuildPath_Main_Package].basename)); + String last_element = last_path_element(bc->build_paths[BuildPath_Main_Package].basename); + + if (last_element.len == 0) { + gb_printf_err("The output name is created from the last path element. `%.*s` has none. Use `-out:output_name.ext` to set it.\n", LIT(bc->build_paths[BuildPath_Main_Package].basename)); + return false; + } + output_path.basename = copy_string(ha, bc->build_paths[BuildPath_Main_Package].basename); + output_path.name = copy_string(ha, last_element); + + } else { + // Init filename was not 'current path'. + // Contruct the output name from the path elements as usual. + String output_name = init_filename; + // If it ends with a trailing (back)slash, strip it before continuing. + while (output_name.len > 0 && (output_name[output_name.len-1] == '/' || output_name[output_name.len-1] == '\\')) { + output_name.len -= 1; + } + output_name = remove_directory_from_path(output_name); + output_name = remove_extension_from_path(output_name); + output_name = copy_string(ha, string_trim_whitespace(output_name)); + output_path = path_from_string(ha, output_name); + + // Replace extension. + if (output_path.ext.len > 0) { + gb_free(ha, output_path.ext.text); + } + } + output_path.ext = copy_string(ha, output_extension); + + bc->build_paths[BuildPath_Output] = output_path; + } + + // Do we have an extension? We might not if the output filename was supplied. + if (bc->build_paths[BuildPath_Output].ext.len == 0) { + if (build_context.metrics.os == TargetOs_windows || build_context.build_mode != BuildMode_Executable) { + bc->build_paths[BuildPath_Output].ext = copy_string(ha, output_extension); + } + } + + // Check if output path is a directory. + if (path_is_directory(bc->build_paths[BuildPath_Output])) { + String output_file = path_to_string(ha, bc->build_paths[BuildPath_Output]); + defer (gb_free(ha, output_file.text)); + gb_printf_err("Output path %.*s is a directory.\n", LIT(output_file)); + return false; + } + + if (bc->target_features_string.len != 0) { + enable_target_feature({}, bc->target_features_string); + } + + return true; +} + diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index a42741976..8108604ba 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -29,6 +29,7 @@ BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_simple_boolean_end - is_type_named, is_type_pointer, + is_type_multi_pointer, is_type_array, is_type_enumerated_array, is_type_slice, @@ -143,6 +144,936 @@ void check_or_return_split_types(CheckerContext *c, Operand *x, String const &na } +bool does_require_msgSend_stret(Type *return_type) { + if (return_type == nullptr) { + return false; + } + if (build_context.metrics.arch == TargetArch_i386 || build_context.metrics.arch == TargetArch_amd64) { + i64 struct_limit = type_size_of(t_uintptr) << 1; + return type_size_of(return_type) > struct_limit; + } + if (build_context.metrics.arch == TargetArch_arm64) { + return false; + } + + // if (build_context.metrics.arch == TargetArch_arm32) { + // i64 struct_limit = type_size_of(t_uintptr); + // // NOTE(bill): This is technically wrong + // return is_type_struct(return_type) && !is_type_raw_union(return_type) && type_size_of(return_type) > struct_limit; + // } + GB_PANIC("unsupported architecture"); + return false; +} + +ObjcMsgKind get_objc_proc_kind(Type *return_type) { + if (return_type == nullptr) { + return ObjcMsg_normal; + } + + if (build_context.metrics.arch == TargetArch_i386 || build_context.metrics.arch == TargetArch_amd64) { + if (is_type_float(return_type)) { + return ObjcMsg_fpret; + } + if (build_context.metrics.arch == TargetArch_amd64) { + if (is_type_complex(return_type)) { + // URL: https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/message.h#L143-L159 + return ObjcMsg_fpret; + } + } + } + if (build_context.metrics.arch != TargetArch_arm64) { + if (does_require_msgSend_stret(return_type)) { + return ObjcMsg_stret; + } + } + return ObjcMsg_normal; +} + +void add_objc_proc_type(CheckerContext *c, Ast *call, Type *return_type, Slice param_types) { + ObjcMsgKind kind = get_objc_proc_kind(return_type); + + Scope *scope = create_scope(c->info, nullptr); + + // NOTE(bill, 2022-02-08): the backend's ABI handling should handle this correctly, I hope + Type *params = alloc_type_tuple(); + { + auto variables = array_make(permanent_allocator(), 0, param_types.count); + + for_array(i, param_types) { + Type *type = param_types[i]; + Entity *param = alloc_entity_param(scope, blank_token, type, false, true); + array_add(&variables, param); + } + params->Tuple.variables = slice_from_array(variables); + } + + Type *results = alloc_type_tuple(); + if (return_type) { + auto variables = array_make(permanent_allocator(), 1); + results->Tuple.variables = slice_from_array(variables); + Entity *param = alloc_entity_param(scope, blank_token, return_type, false, true); + results->Tuple.variables[0] = param; + } + + + ObjcMsgData data = {}; + data.kind = kind; + data.proc_type = alloc_type_proc(scope, params, param_types.count, results, results->Tuple.variables.count, false, ProcCC_CDecl); + + mutex_lock(&c->info->objc_types_mutex); + map_set(&c->info->objc_msgSend_types, call, data); + mutex_unlock(&c->info->objc_types_mutex); + + try_to_add_package_dependency(c, "runtime", "objc_msgSend"); + try_to_add_package_dependency(c, "runtime", "objc_msgSend_fpret"); + try_to_add_package_dependency(c, "runtime", "objc_msgSend_fp2ret"); + try_to_add_package_dependency(c, "runtime", "objc_msgSend_stret"); +} + +bool is_constant_string(CheckerContext *c, String const &builtin_name, Ast *expr, String *name_) { + Operand op = {}; + check_expr(c, &op, expr); + if (op.mode == Addressing_Constant && op.value.kind == ExactValue_String) { + if (name_) *name_ = op.value.value_string; + return true; + } + gbString e = expr_to_string(op.expr); + gbString t = type_to_string(op.type); + error(op.expr, "'%.*s' expected a constant string value, got %s of type %s", LIT(builtin_name), e, t); + gb_string_free(t); + gb_string_free(e); + return false; +} + +bool check_builtin_objc_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) { + String const &builtin_name = builtin_procs[id].name; + + if (build_context.metrics.os != TargetOs_darwin) { + // allow on doc generation (e.g. Metal stuff) + if (build_context.command_kind != Command_doc && build_context.command_kind != Command_check) { + error(call, "'%.*s' only works on darwin", LIT(builtin_name)); + } + } + + + ast_node(ce, CallExpr, call); + switch (id) { + default: + GB_PANIC("Implement objective built-in procedure: %.*s", LIT(builtin_name)); + return false; + + case BuiltinProc_objc_send: { + Type *return_type = nullptr; + + Operand rt = {}; + check_expr_or_type(c, &rt, ce->args[0]); + if (rt.mode == Addressing_Type) { + return_type = rt.type; + } else if (is_operand_nil(rt)) { + return_type = nullptr; + } else { + gbString e = expr_to_string(rt.expr); + error(rt.expr, "'%.*s' expected a type or nil to define the return type of the Objective-C call, got %s", LIT(builtin_name), e); + gb_string_free(e); + return false; + } + + operand->type = return_type; + operand->mode = return_type ? Addressing_Value : Addressing_NoValue; + + String class_name = {}; + String sel_name = {}; + + Type *sel_type = t_objc_SEL; + Operand self = {}; + check_expr_or_type(c, &self, ce->args[1]); + if (self.mode == Addressing_Type) { + if (!is_type_objc_object(self.type)) { + gbString t = type_to_string(self.type); + error(self.expr, "'%.*s' expected a type or value derived from intrinsics.objc_object, got type %s", LIT(builtin_name), t); + gb_string_free(t); + return false; + } + if (!has_type_got_objc_class_attribute(self.type)) { + gbString t = type_to_string(self.type); + error(self.expr, "'%.*s' expected a named type with the attribute @(obj_class=) , got type %s", LIT(builtin_name), t); + gb_string_free(t); + return false; + } + + sel_type = t_objc_Class; + } else if (!is_operand_value(self) || !check_is_assignable_to(c, &self, t_objc_id)) { + gbString e = expr_to_string(self.expr); + gbString t = type_to_string(self.type); + error(self.expr, "'%.*s' expected a type or value derived from intrinsics.objc_object, got '%s' of type %s", LIT(builtin_name), e, t); + gb_string_free(t); + gb_string_free(e); + return false; + } else if (!is_type_pointer(self.type)) { + gbString e = expr_to_string(self.expr); + gbString t = type_to_string(self.type); + error(self.expr, "'%.*s' expected a pointer of a value derived from intrinsics.objc_object, got '%s' of type %s", LIT(builtin_name), e, t); + gb_string_free(t); + gb_string_free(e); + return false; + } else { + Type *type = type_deref(self.type); + if (!(type->kind == Type_Named && + type->Named.type_name != nullptr && + type->Named.type_name->TypeName.objc_class_name != "")) { + gbString t = type_to_string(type); + error(self.expr, "'%.*s' expected a named type with the attribute @(obj_class=) , got type %s", LIT(builtin_name), t); + gb_string_free(t); + return false; + } + } + + + if (!is_constant_string(c, builtin_name, ce->args[2], &sel_name)) { + return false; + } + + isize const arg_offset = 1; + auto param_types = slice_make(permanent_allocator(), ce->args.count-arg_offset); + param_types[0] = t_objc_id; + param_types[1] = sel_type; + + for (isize i = 2+arg_offset; i < ce->args.count; i++) { + Operand x = {}; + check_expr(c, &x, ce->args[i]); + param_types[i-arg_offset] = x.type; + } + + add_objc_proc_type(c, call, return_type, param_types); + + return true; + } break; + + case BuiltinProc_objc_find_selector: + case BuiltinProc_objc_find_class: + case BuiltinProc_objc_register_selector: + case BuiltinProc_objc_register_class: + { + String sel_name = {}; + if (!is_constant_string(c, builtin_name, ce->args[0], &sel_name)) { + return false; + } + + switch (id) { + case BuiltinProc_objc_find_selector: + case BuiltinProc_objc_register_selector: + operand->type = t_objc_SEL; + break; + case BuiltinProc_objc_find_class: + case BuiltinProc_objc_register_class: + operand->type = t_objc_Class; + break; + + } + operand->mode = Addressing_Value; + + try_to_add_package_dependency(c, "runtime", "objc_lookUpClass"); + try_to_add_package_dependency(c, "runtime", "sel_registerName"); + try_to_add_package_dependency(c, "runtime", "objc_allocateClassPair"); + return true; + } break; + } +} + +bool check_atomic_memory_order_argument(CheckerContext *c, Ast *expr, String const &builtin_name, OdinAtomicMemoryOrder *memory_order_, char const *extra_message = nullptr) { + Operand x = {}; + check_expr_with_type_hint(c, &x, expr, t_atomic_memory_order); + if (x.mode == Addressing_Invalid) { + return false; + } + if (!are_types_identical(x.type, t_atomic_memory_order) || x.mode != Addressing_Constant) { + gbString str = type_to_string(x.type); + if (extra_message) { + error(x.expr, "Expected a constant Atomic_Memory_Order value for the %s of '%.*s', got %s", extra_message, LIT(builtin_name), str); + } else { + error(x.expr, "Expected a constant Atomic_Memory_Order value for '%.*s', got %s", LIT(builtin_name), str); + } + gb_string_free(str); + return false; + } + i64 value = exact_value_to_i64(x.value); + if (value < 0 || value >= OdinAtomicMemoryOrder_COUNT) { + error(x.expr, "Illegal Atomic_Memory_Order value, got %lld", cast(long long)value); + return false; + } + if (memory_order_) { + *memory_order_ = cast(OdinAtomicMemoryOrder)value; + } + + return true; + +} + + +bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) { + ast_node(ce, CallExpr, call); + + String const &builtin_name = builtin_procs[id].name; + switch (id) { + // Any numeric + case BuiltinProc_simd_add: + case BuiltinProc_simd_sub: + case BuiltinProc_simd_mul: + case BuiltinProc_simd_div: + case BuiltinProc_simd_min: + case BuiltinProc_simd_max: + { + Operand x = {}; + Operand y = {}; + check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false; + check_expr_with_type_hint(c, &y, ce->args[1], x.type); if (y.mode == Addressing_Invalid) return false; + convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false; + if (!is_type_simd_vector(x.type)) { + error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); + return false; + } + if (!is_type_simd_vector(y.type)) { + error(y.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); + return false; + } + if (!are_types_identical(x.type, y.type)) { + gbString xs = type_to_string(x.type); + gbString ys = type_to_string(y.type); + error(x.expr, "'%.*s' expected 2 arguments of the same type, got '%s' vs '%s'", LIT(builtin_name), xs, ys); + gb_string_free(ys); + gb_string_free(xs); + return false; + } + Type *elem = base_array_type(x.type); + if (!is_type_integer(elem) && !is_type_float(elem)) { + gbString xs = type_to_string(x.type); + error(x.expr, "'%.*s' expected a #simd type with an integer or floating point element, got '%s'", LIT(builtin_name), xs); + gb_string_free(xs); + return false; + } + + if (id == BuiltinProc_simd_div && is_type_integer(elem)) { + gbString xs = type_to_string(x.type); + error(x.expr, "'%.*s' is not supported for integer elements, got '%s'", LIT(builtin_name), xs); + gb_string_free(xs); + // don't return + } + + operand->mode = Addressing_Value; + operand->type = x.type; + return true; + } + + // Integer only + case BuiltinProc_simd_add_sat: + case BuiltinProc_simd_sub_sat: + case BuiltinProc_simd_and: + case BuiltinProc_simd_or: + case BuiltinProc_simd_xor: + case BuiltinProc_simd_and_not: + { + Operand x = {}; + Operand y = {}; + check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false; + check_expr_with_type_hint(c, &y, ce->args[1], x.type); if (y.mode == Addressing_Invalid) return false; + convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false; + if (!is_type_simd_vector(x.type)) { + error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); + return false; + } + if (!is_type_simd_vector(y.type)) { + error(y.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); + return false; + } + if (!are_types_identical(x.type, y.type)) { + gbString xs = type_to_string(x.type); + gbString ys = type_to_string(y.type); + error(x.expr, "'%.*s' expected 2 arguments of the same type, got '%s' vs '%s'", LIT(builtin_name), xs, ys); + gb_string_free(ys); + gb_string_free(xs); + return false; + } + Type *elem = base_array_type(x.type); + + switch (id) { + case BuiltinProc_simd_add_sat: + case BuiltinProc_simd_sub_sat: + if (!is_type_integer(elem)) { + gbString xs = type_to_string(x.type); + error(x.expr, "'%.*s' expected a #simd type with an integer element, got '%s'", LIT(builtin_name), xs); + gb_string_free(xs); + return false; + } + break; + default: + if (!is_type_integer(elem) && !is_type_boolean(elem)) { + gbString xs = type_to_string(x.type); + error(x.expr, "'%.*s' expected a #simd type with an integer or boolean element, got '%s'", LIT(builtin_name), xs); + gb_string_free(xs); + return false; + } + break; + } + + operand->mode = Addressing_Value; + operand->type = x.type; + return true; + } + + case BuiltinProc_simd_shl: // Odin-like + case BuiltinProc_simd_shr: // Odin-like + case BuiltinProc_simd_shl_masked: // C-like + case BuiltinProc_simd_shr_masked: // C-like + { + Operand x = {}; + Operand y = {}; + check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false; + check_expr_with_type_hint(c, &y, ce->args[1], x.type); if (y.mode == Addressing_Invalid) return false; + convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false; + if (!is_type_simd_vector(x.type)) { + error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); + return false; + } + if (!is_type_simd_vector(y.type)) { + error(y.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); + return false; + } + GB_ASSERT(x.type->kind == Type_SimdVector); + GB_ASSERT(y.type->kind == Type_SimdVector); + Type *xt = x.type; + Type *yt = y.type; + + if (xt->SimdVector.count != yt->SimdVector.count) { + error(x.expr, "'%.*s' mismatched simd vector lengths, got '%lld' vs '%lld'", + LIT(builtin_name), + cast(long long)xt->SimdVector.count, + cast(long long)yt->SimdVector.count); + return false; + } + if (!is_type_integer(base_array_type(x.type))) { + gbString xs = type_to_string(x.type); + error(x.expr, "'%.*s' expected a #simd type with an integer element, got '%s'", LIT(builtin_name), xs); + gb_string_free(xs); + return false; + } + if (!is_type_unsigned(base_array_type(y.type))) { + gbString ys = type_to_string(y.type); + error(y.expr, "'%.*s' expected a #simd type with an unsigned integer element as the shifting operand, got '%s'", LIT(builtin_name), ys); + gb_string_free(ys); + return false; + } + + operand->mode = Addressing_Value; + operand->type = x.type; + return true; + } + + // Unary + case BuiltinProc_simd_neg: + case BuiltinProc_simd_abs: + { + Operand x = {}; + check_expr(c, &x, ce->args[0]); + if (x.mode == Addressing_Invalid) { + return false; + } + if (!is_type_simd_vector(x.type)) { + error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); + return false; + } + Type *elem = base_array_type(x.type); + if (!is_type_integer(elem) && !is_type_float(elem)) { + gbString xs = type_to_string(x.type); + error(x.expr, "'%.*s' expected a #simd type with an integer or floating point element, got '%s'", LIT(builtin_name), xs); + gb_string_free(xs); + return false; + } + operand->mode = Addressing_Value; + operand->type = x.type; + return true; + } + + // Return integer masks + case BuiltinProc_simd_lanes_eq: + case BuiltinProc_simd_lanes_ne: + case BuiltinProc_simd_lanes_lt: + case BuiltinProc_simd_lanes_le: + case BuiltinProc_simd_lanes_gt: + case BuiltinProc_simd_lanes_ge: + { + // op(#simd[N]T, #simd[N]T) -> #simd[N]V + // where `V` is an integer, `size_of(T) == size_of(V)` + // `V` will all 0s if false and all 1s if true (e.g. 0x00 and 0xff for false and true, respectively) + + Operand x = {}; + Operand y = {}; + check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false; + check_expr_with_type_hint(c, &y, ce->args[1], x.type); if (y.mode == Addressing_Invalid) return false; + convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false; + if (!is_type_simd_vector(x.type)) { + error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); + return false; + } + Type *elem = base_array_type(x.type); + switch (id) { + case BuiltinProc_simd_lanes_eq: + case BuiltinProc_simd_lanes_ne: + if (!is_type_integer(elem) && !is_type_float(elem) && !is_type_boolean(elem)) { + gbString xs = type_to_string(x.type); + error(x.expr, "'%.*s' expected a #simd type with an integer, floating point, or boolean element, got '%s'", LIT(builtin_name), xs); + gb_string_free(xs); + return false; + } + break; + default: + if (!is_type_integer(elem) && !is_type_float(elem)) { + gbString xs = type_to_string(x.type); + error(x.expr, "'%.*s' expected a #simd type with an integer or floating point element, got '%s'", LIT(builtin_name), xs); + gb_string_free(xs); + return false; + } + break; + } + + + Type *vt = base_type(x.type); + GB_ASSERT(vt->kind == Type_SimdVector); + i64 count = vt->SimdVector.count; + + i64 sz = type_size_of(elem); + Type *new_elem = nullptr; + + switch (sz) { + case 1: new_elem = t_u8; break; + case 2: new_elem = t_u16; break; + case 4: new_elem = t_u32; break; + case 8: new_elem = t_u64; break; + case 16: + error(x.expr, "'%.*s' not supported 128-bit integer backed simd vector types", LIT(builtin_name)); + return false; + } + + operand->mode = Addressing_Value; + operand->type = alloc_type_simd_vector(count, new_elem); + return true; + } + + case BuiltinProc_simd_extract: + { + Operand x = {}; + check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false; + + if (!is_type_simd_vector(x.type)) { + error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); + return false; + } + Type *elem = base_array_type(x.type); + i64 max_count = x.type->SimdVector.count; + i64 value = -1; + if (!check_index_value(c, x.type, false, ce->args[1], max_count, &value)) { + return false; + } + if (max_count < 0) { + error(ce->args[1], "'%.*s' expected a constant integer index, got '%lld'", LIT(builtin_name), cast(long long)value); + return false; + } + + operand->mode = Addressing_Value; + operand->type = elem; + return true; + } + break; + case BuiltinProc_simd_replace: + { + Operand x = {}; + check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false; + + if (!is_type_simd_vector(x.type)) { + error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); + return false; + } + Type *elem = base_array_type(x.type); + i64 max_count = x.type->SimdVector.count; + i64 value = -1; + if (!check_index_value(c, x.type, false, ce->args[1], max_count, &value)) { + return false; + } + if (max_count < 0) { + error(ce->args[1], "'%.*s' expected a constant integer index, got '%lld'", LIT(builtin_name), cast(long long)value); + return false; + } + + Operand y = {}; + check_expr_with_type_hint(c, &y, ce->args[2], elem); if (y.mode == Addressing_Invalid) return false; + convert_to_typed(c, &y, elem); if (y.mode == Addressing_Invalid) return false; + if (!are_types_identical(y.type, elem)) { + gbString et = type_to_string(elem); + gbString yt = type_to_string(y.type); + error(y.expr, "'%.*s' expected a type of '%s' to insert, got '%s'", LIT(builtin_name), et, yt); + gb_string_free(yt); + gb_string_free(et); + return false; + } + + operand->mode = Addressing_Value; + operand->type = x.type; + return true; + } + break; + + case BuiltinProc_simd_reduce_add_ordered: + case BuiltinProc_simd_reduce_mul_ordered: + case BuiltinProc_simd_reduce_min: + case BuiltinProc_simd_reduce_max: + { + Operand x = {}; + check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false; + + if (!is_type_simd_vector(x.type)) { + error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); + return false; + } + Type *elem = base_array_type(x.type); + if (!is_type_integer(elem) && !is_type_float(elem)) { + gbString xs = type_to_string(x.type); + error(x.expr, "'%.*s' expected a #simd type with an integer or floating point element, got '%s'", LIT(builtin_name), xs); + gb_string_free(xs); + return false; + } + + operand->mode = Addressing_Value; + operand->type = base_array_type(x.type); + return true; + } + + case BuiltinProc_simd_reduce_and: + case BuiltinProc_simd_reduce_or: + case BuiltinProc_simd_reduce_xor: + { + Operand x = {}; + check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false; + + if (!is_type_simd_vector(x.type)) { + error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); + return false; + } + Type *elem = base_array_type(x.type); + if (!is_type_integer(elem) && !is_type_boolean(elem)) { + gbString xs = type_to_string(x.type); + error(x.expr, "'%.*s' expected a #simd type with an integer or boolean element, got '%s'", LIT(builtin_name), xs); + gb_string_free(xs); + return false; + } + + operand->mode = Addressing_Value; + operand->type = base_array_type(x.type); + return true; + } + + + case BuiltinProc_simd_shuffle: + { + Operand x = {}; + Operand y = {}; + check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false; + check_expr_with_type_hint(c, &y, ce->args[1], x.type); if (y.mode == Addressing_Invalid) return false; + convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false; + if (!is_type_simd_vector(x.type)) { + error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); + return false; + } + if (!is_type_simd_vector(y.type)) { + error(y.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); + return false; + } + if (!are_types_identical(x.type, y.type)) { + gbString xs = type_to_string(x.type); + gbString ys = type_to_string(y.type); + error(x.expr, "'%.*s' expected 2 arguments of the same type, got '%s' vs '%s'", LIT(builtin_name), xs, ys); + gb_string_free(ys); + gb_string_free(xs); + return false; + } + Type *elem = base_array_type(x.type); + + i64 max_count = x.type->SimdVector.count + y.type->SimdVector.count; + + i64 arg_count = 0; + for_array(i, ce->args) { + if (i < 2) { + continue; + } + Ast *arg = ce->args[i]; + Operand op = {}; + check_expr(c, &op, arg); + if (op.mode == Addressing_Invalid) { + return false; + } + Type *arg_type = base_type(op.type); + if (!is_type_integer(arg_type) || op.mode != Addressing_Constant) { + error(op.expr, "Indices to '%.*s' must be constant integers", LIT(builtin_name)); + return false; + } + + if (big_int_is_neg(&op.value.value_integer)) { + error(op.expr, "Negative '%.*s' index", LIT(builtin_name)); + return false; + } + + BigInt mc = {}; + big_int_from_i64(&mc, max_count); + if (big_int_cmp(&mc, &op.value.value_integer) <= 0) { + error(op.expr, "'%.*s' index exceeds length", LIT(builtin_name)); + return false; + } + + arg_count++; + } + + if (arg_count > max_count) { + error(call, "Too many '%.*s' indices, %td > %td", LIT(builtin_name), arg_count, max_count); + return false; + } + + + if (!is_power_of_two(arg_count)) { + error(call, "'%.*s' must have a power of two index arguments, got %lld", LIT(builtin_name), cast(long long)arg_count); + return false; + } + + operand->mode = Addressing_Value; + operand->type = alloc_type_simd_vector(arg_count, elem); + return true; + } + + case BuiltinProc_simd_select: + { + Operand cond = {}; + check_expr(c, &cond, ce->args[0]); if (cond.mode == Addressing_Invalid) return false; + + if (!is_type_simd_vector(cond.type)) { + error(cond.expr, "'%.*s' expected a simd vector boolean type", LIT(builtin_name)); + return false; + } + Type *cond_elem = base_array_type(cond.type); + if (!is_type_boolean(cond_elem) && !is_type_integer(cond_elem)) { + gbString cond_str = type_to_string(cond.type); + error(cond.expr, "'%.*s' expected a simd vector boolean or integer type, got '%s'", LIT(builtin_name), cond_str); + gb_string_free(cond_str); + return false; + } + + Operand x = {}; + Operand y = {}; + check_expr(c, &x, ce->args[1]); if (x.mode == Addressing_Invalid) return false; + check_expr_with_type_hint(c, &y, ce->args[2], x.type); if (y.mode == Addressing_Invalid) return false; + convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false; + if (!is_type_simd_vector(x.type)) { + error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); + return false; + } + if (!is_type_simd_vector(y.type)) { + error(y.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); + return false; + } + if (!are_types_identical(x.type, y.type)) { + gbString xs = type_to_string(x.type); + gbString ys = type_to_string(y.type); + error(x.expr, "'%.*s' expected 2 results of the same type, got '%s' vs '%s'", LIT(builtin_name), xs, ys); + gb_string_free(ys); + gb_string_free(xs); + return false; + } + + if (cond.type->SimdVector.count != x.type->SimdVector.count) { + error(x.expr, "'%.*s' expected condition vector to match the length of the result lengths, got '%lld' vs '%lld'", + LIT(builtin_name), + cast(long long)cond.type->SimdVector.count, + cast(long long)x.type->SimdVector.count); + return false; + } + + + operand->mode = Addressing_Value; + operand->type = x.type; + return true; + } + + case BuiltinProc_simd_ceil: + case BuiltinProc_simd_floor: + case BuiltinProc_simd_trunc: + case BuiltinProc_simd_nearest: + { + Operand x = {}; + check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false; + + if (!is_type_simd_vector(x.type)) { + error(x.expr, "'%.*s' expected a simd vector boolean type", LIT(builtin_name)); + return false; + } + Type *elem = base_array_type(x.type); + if (!is_type_float(elem)) { + gbString x_str = type_to_string(x.type); + error(x.expr, "'%.*s' expected a simd vector floating point type, got '%s'", LIT(builtin_name), x_str); + gb_string_free(x_str); + return false; + } + + operand->mode = Addressing_Value; + operand->type = x.type; + return true; + } + + case BuiltinProc_simd_lanes_reverse: + { + Operand x = {}; + check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false; + + if (!is_type_simd_vector(x.type)) { + error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); + return false; + } + operand->type = x.type; + operand->mode = Addressing_Value; + return true; + } + + case BuiltinProc_simd_lanes_rotate_left: + case BuiltinProc_simd_lanes_rotate_right: + { + Operand x = {}; + check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false; + if (!is_type_simd_vector(x.type)) { + error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); + return false; + } + Operand offset = {}; + check_expr(c, &offset, ce->args[1]); if (offset.mode == Addressing_Invalid) return false; + convert_to_typed(c, &offset, t_i64); + if (!is_type_integer(offset.type) || offset.mode != Addressing_Constant) { + error(offset.expr, "'%.*s' expected a constant integer offset"); + return false; + } + check_assignment(c, &offset, t_i64, builtin_name); + + operand->type = x.type; + operand->mode = Addressing_Value; + return true; + } + + case BuiltinProc_simd_clamp: + { + Operand x = {}; + Operand y = {}; + Operand z = {}; + check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false; + check_expr_with_type_hint(c, &y, ce->args[1], x.type); if (y.mode == Addressing_Invalid) return false; + check_expr_with_type_hint(c, &z, ce->args[2], x.type); if (z.mode == Addressing_Invalid) return false; + convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false; + convert_to_typed(c, &z, x.type); + if (!is_type_simd_vector(x.type)) { + error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); + return false; + } + if (!is_type_simd_vector(y.type)) { + error(y.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); + return false; + } + if (!is_type_simd_vector(z.type)) { + error(z.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); + return false; + } + if (!are_types_identical(x.type, y.type)) { + gbString xs = type_to_string(x.type); + gbString ys = type_to_string(y.type); + error(x.expr, "'%.*s' expected 2 arguments of the same type, got '%s' vs '%s'", LIT(builtin_name), xs, ys); + gb_string_free(ys); + gb_string_free(xs); + return false; + } + if (!are_types_identical(x.type, z.type)) { + gbString xs = type_to_string(x.type); + gbString zs = type_to_string(z.type); + error(x.expr, "'%.*s' expected 2 arguments of the same type, got '%s' vs '%s'", LIT(builtin_name), xs, zs); + gb_string_free(zs); + gb_string_free(xs); + return false; + } + Type *elem = base_array_type(x.type); + if (!is_type_integer(elem) && !is_type_float(elem)) { + gbString xs = type_to_string(x.type); + error(x.expr, "'%.*s' expected a #simd type with an integer or floating point element, got '%s'", LIT(builtin_name), xs); + gb_string_free(xs); + return false; + } + + operand->mode = Addressing_Value; + operand->type = x.type; + return true; + } + + case BuiltinProc_simd_to_bits: + { + Operand x = {}; + check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false; + + if (!is_type_simd_vector(x.type)) { + error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); + return false; + } + Type *elem = base_array_type(x.type); + i64 count = get_array_type_count(x.type); + i64 sz = type_size_of(elem); + Type *bit_elem = nullptr; + switch (sz) { + case 1: bit_elem = t_u8; break; + case 2: bit_elem = t_u16; break; + case 4: bit_elem = t_u32; break; + case 8: bit_elem = t_u64; break; + } + GB_ASSERT(bit_elem != nullptr); + + operand->type = alloc_type_simd_vector(count, bit_elem); + operand->mode = Addressing_Value; + return true; + } + + case BuiltinProc_simd_x86__MM_SHUFFLE: + { + Operand x[4] = {}; + for (unsigned i = 0; i < 4; i++) { + check_expr(c, x+i, ce->args[i]); if (x[i].mode == Addressing_Invalid) return false; + } + + u32 offsets[4] = {6, 4, 2, 0}; + u32 result = 0; + for (unsigned i = 0; i < 4; i++) { + if (!is_type_integer(x[i].type) || x[i].mode != Addressing_Constant) { + gbString xs = type_to_string(x[i].type); + error(x[i].expr, "'%.*s' expected a constant integer", LIT(builtin_name), xs); + gb_string_free(xs); + return false; + } + i64 val = exact_value_to_i64(x[i].value); + if (val < 0 || val > 3) { + error(x[i].expr, "'%.*s' expected a constant integer in the range 0..<4, got %lld", LIT(builtin_name), cast(long long)val); + return false; + } + result |= cast(u32)(val) << offsets[i]; + } + + operand->type = t_untyped_integer; + operand->mode = Addressing_Constant; + operand->value = exact_value_i64(result); + return true; + } + default: + GB_PANIC("Unhandled simd intrinsic: %.*s", LIT(builtin_name)); + } + + return false; +} + bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) { ast_node(ce, CallExpr, call); @@ -179,9 +1110,21 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 case BuiltinProc_len: case BuiltinProc_min: case BuiltinProc_max: + case BuiltinProc_type_is_subtype_of: + case BuiltinProc_objc_send: + case BuiltinProc_objc_find_selector: + case BuiltinProc_objc_find_class: + case BuiltinProc_objc_register_selector: + case BuiltinProc_objc_register_class: + case BuiltinProc_atomic_type_is_lock_free: // NOTE(bill): The first arg may be a Type, this will be checked case by case break; + case BuiltinProc_atomic_thread_fence: + case BuiltinProc_atomic_signal_fence: + // NOTE(bill): first type will require a type hint + break; + case BuiltinProc_DIRECTIVE: { ast_node(bd, BasicDirective, ce->proc); String name = bd->name.string; @@ -202,7 +1145,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 break; } - String builtin_name = builtin_procs[id].name;; + String const &builtin_name = builtin_procs[id].name; if (ce->args.count > 0) { @@ -214,11 +1157,29 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } } + if (BuiltinProc__simd_begin < id && id < BuiltinProc__simd_end) { + bool ok = check_builtin_simd_operation(c, operand, call, id, type_hint); + if (!ok) { + operand->type = t_invalid; + } + operand->mode = Addressing_Value; + operand->value = {}; + operand->expr = call; + return ok; + } + switch (id) { default: GB_PANIC("Implement built-in procedure: %.*s", LIT(builtin_name)); break; + case BuiltinProc_objc_send: + case BuiltinProc_objc_find_selector: + case BuiltinProc_objc_find_class: + case BuiltinProc_objc_register_selector: + case BuiltinProc_objc_register_class: + return check_builtin_objc_procedure(c, operand, call, id, type_hint); + case BuiltinProc___entry_point: operand->mode = Addressing_NoValue; operand->type = nullptr; @@ -548,8 +1509,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } } else if (name == "assert") { - if (ce->args.count != 1) { - error(call, "'#assert' expects 1 argument, got %td", ce->args.count); + if (ce->args.count != 1 && ce->args.count != 2) { + error(call, "'#assert' expects either 1 or 2 arguments, got %td", ce->args.count); return false; } if (!is_type_boolean(operand->type) || operand->mode != Addressing_Constant) { @@ -558,15 +1519,37 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 gb_string_free(str); return false; } + if (ce->args.count == 2) { + Ast *arg = unparen_expr(ce->args[1]); + if (arg == nullptr || arg->kind != Ast_BasicLit || arg->BasicLit.token.kind != Token_String) { + gbString str = expr_to_string(arg); + error(call, "'%s' is not a constant string", str); + gb_string_free(str); + return false; + } + } + if (!operand->value.value_bool) { - gbString arg = expr_to_string(ce->args[0]); - error(call, "Compile time assertion: %s", arg); + gbString arg1 = expr_to_string(ce->args[0]); + gbString arg2 = {}; + + if (ce->args.count == 1) { + error(call, "Compile time assertion: %s", arg1); + } else { + arg2 = expr_to_string(ce->args[1]); + error(call, "Compile time assertion: %s (%s)", arg1, arg2); + } + if (c->proc_name != "") { gbString str = type_to_string(c->curr_proc_sig); error_line("\tCalled within '%.*s' :: %s\n", LIT(c->proc_name), str); gb_string_free(str); } - gb_string_free(arg); + + gb_string_free(arg1); + if (ce->args.count == 2) { + gb_string_free(arg2); + } } operand->type = t_untyped_bool; @@ -704,7 +1687,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 mode = Addressing_Constant; value = exact_value_i64(at->EnumeratedArray.count); type = t_untyped_integer; - } else if (is_type_slice(op_type) && id == BuiltinProc_len) { + } else if ((is_type_slice(op_type) || is_type_relative_slice(op_type)) && id == BuiltinProc_len) { mode = Addressing_Value; } else if (is_type_dynamic_array(op_type)) { mode = Addressing_Value; @@ -725,6 +1708,11 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 bt->Struct.soa_kind == StructSoa_Dynamic) { mode = Addressing_Value; } + } else if (is_type_simd_vector(op_type)) { + Type *bt = base_type(op_type); + mode = Addressing_Constant; + value = exact_value_i64(bt->SimdVector.count); + type = t_untyped_integer; } if (operand->mode == Addressing_Type && mode != Addressing_Constant) { mode = Addressing_Invalid; @@ -858,7 +1846,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 Selection sel = lookup_field(type, field_name, false); if (sel.entity == nullptr) { - gbString type_str = type_to_string(type); + gbString type_str = type_to_string_shorthand(type); error(ce->args[0], "'%s' has no field named '%.*s'", type_str, LIT(field_name)); gb_string_free(type_str); @@ -870,7 +1858,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 return false; } if (sel.indirect) { - gbString type_str = type_to_string(type); + gbString type_str = type_to_string_shorthand(type); error(ce->args[0], "Field '%.*s' is embedded via a pointer in '%s'", LIT(field_name), type_str); gb_string_free(type_str); @@ -931,7 +1919,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 Selection sel = lookup_field(type, field_name, false); if (sel.entity == nullptr) { - gbString type_str = type_to_string(type); + gbString type_str = type_to_string_shorthand(type); error(ce->args[0], "'%s' has no field named '%.*s'", type_str, LIT(field_name)); gb_string_free(type_str); @@ -943,7 +1931,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 return false; } if (sel.indirect) { - gbString type_str = type_to_string(type); + gbString type_str = type_to_string_shorthand(type); error(ce->args[0], "Field '%.*s' is embedded via a pointer in '%s'", LIT(field_name), type_str); gb_string_free(type_str); @@ -993,6 +1981,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 if (c->scope->flags&ScopeFlag_Global) { compiler_error("'type_info_of' Cannot be declared within the runtime package due to how the internals of the compiler works"); } + if (build_context.disallow_rtti) { + error(call, "'%.*s' has been disallowed", LIT(builtin_name)); + return false; + } // NOTE(bill): The type information may not be setup yet init_core_type_info(c->checker); @@ -1005,9 +1997,9 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 Type *t = o.type; if (t == nullptr || t == t_invalid || is_type_asm_proc(o.type) || is_type_polymorphic(t)) { if (is_type_polymorphic(t)) { - error(ce->args[0], "Invalid argument for 'type_info_of', unspecialized polymorphic type"); + error(ce->args[0], "Invalid argument for '%.*s', unspecialized polymorphic type", LIT(builtin_name)); } else { - error(ce->args[0], "Invalid argument for 'type_info_of'"); + error(ce->args[0], "Invalid argument for '%.*s'", LIT(builtin_name)); } return false; } @@ -1018,7 +2010,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 if (is_operand_value(o) && is_type_typeid(t)) { add_package_dependency(c, "runtime", "__type_info_of"); } else if (o.mode != Addressing_Type) { - error(expr, "Expected a type or typeid for 'type_info_of'"); + error(expr, "Expected a type or typeid for '%.*s'", LIT(builtin_name)); return false; } @@ -1032,6 +2024,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 if (c->scope->flags&ScopeFlag_Global) { compiler_error("'typeid_of' Cannot be declared within the runtime package due to how the internals of the compiler works"); } + if (build_context.disallow_rtti) { + error(call, "'%.*s' has been disallowed", LIT(builtin_name)); + return false; + } // NOTE(bill): The type information may not be setup yet init_core_type_info(c->checker); @@ -1043,7 +2039,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } Type *t = o.type; if (t == nullptr || t == t_invalid || is_type_asm_proc(o.type) || is_type_polymorphic(operand->type)) { - error(ce->args[0], "Invalid argument for 'typeid_of'"); + error(ce->args[0], "Invalid argument for '%.*s'", LIT(builtin_name)); return false; } t = default_type(t); @@ -1051,7 +2047,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 add_type_info_type(c, t); if (o.mode != Addressing_Type) { - error(expr, "Expected a type for 'typeid_of'"); + error(expr, "Expected a type for '%.*s'", LIT(builtin_name)); return false; } @@ -1131,6 +2127,11 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 operand->mode = Addressing_Value; } + if (is_type_simd_vector(type) && !is_power_of_two(arg_count)) { + error(call, "'swizzle' with a #simd vector must have a power of two arguments, got %lld", cast(long long)arg_count); + return false; + } + operand->type = determine_swizzle_array_type(original_type, type_hint, arg_count); break; } @@ -1965,7 +2966,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 if (i == j) continue; Operand *b = ops[j]; convert_to_typed(c, a, b->type); - if (a->mode == Addressing_Invalid) { return false; } + if (a->mode == Addressing_Invalid) return false; } } @@ -2183,9 +3184,43 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } operand->mode = Addressing_Value; - if (is_type_array(t)) { + if (t->kind == Type_Array) { + i32 rank = type_math_rank(t); // Do nothing - operand->type = x.type; + operand->type = x.type; + if (rank > 2) { + gbString s = type_to_string(x.type); + error(call, "'%.*s' expects a matrix or array with a rank of 2, got %s of rank %d", LIT(builtin_name), s, rank); + gb_string_free(s); + return false; + } else if (rank == 2) { + Type *inner = base_type(t->Array.elem); + GB_ASSERT(inner->kind == Type_Array); + Type *elem = inner->Array.elem; + Type *array_inner = alloc_type_array(elem, t->Array.count); + Type *array_outer = alloc_type_array(array_inner, inner->Array.count); + operand->type = array_outer; + + i64 elements = t->Array.count*inner->Array.count; + i64 size = type_size_of(operand->type); + if (!is_type_valid_for_matrix_elems(elem)) { + gbString s = type_to_string(x.type); + error(call, "'%.*s' expects a matrix or array with a base element type of an integer, float, or complex number, got %s", LIT(builtin_name), s); + gb_string_free(s); + } else if (elements > MATRIX_ELEMENT_COUNT_MAX) { + gbString s = type_to_string(x.type); + error(call, "'%.*s' expects a matrix or array with a maximum of %d elements, got %s with %lld elements", LIT(builtin_name), MATRIX_ELEMENT_COUNT_MAX, s, elements); + gb_string_free(s); + } else if (elements > MATRIX_ELEMENT_COUNT_MAX) { + gbString s = type_to_string(x.type); + error(call, "'%.*s' expects a matrix or array with non-zero elements, got %s", LIT(builtin_name), MATRIX_ELEMENT_COUNT_MAX, s); + gb_string_free(s); + } else if (size > MATRIX_ELEMENT_MAX_SIZE) { + gbString s = type_to_string(x.type); + error(call, "Too large of a type for '%.*s', got %s of size %lld, maximum size %d", LIT(builtin_name), s, cast(long long)size, MATRIX_ELEMENT_MAX_SIZE); + gb_string_free(s); + } + } } else { GB_ASSERT(t->kind == Type_Matrix); operand->type = alloc_type_matrix(t->Matrix.elem, t->Matrix.column_count, t->Matrix.row_count); @@ -2337,46 +3372,6 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 break; } - case BuiltinProc_simd_vector: { - Operand x = {}; - Operand y = {}; - x = *operand; - if (!is_type_integer(x.type) || x.mode != Addressing_Constant) { - error(call, "Expected a constant integer for 'intrinsics.simd_vector'"); - operand->mode = Addressing_Type; - operand->type = t_invalid; - return false; - } - if (big_int_is_neg(&x.value.value_integer)) { - error(call, "Negative vector element length"); - operand->mode = Addressing_Type; - operand->type = t_invalid; - return false; - } - i64 count = big_int_to_i64(&x.value.value_integer); - - check_expr_or_type(c, &y, ce->args[1]); - if (y.mode != Addressing_Type) { - error(call, "Expected a type 'intrinsics.simd_vector'"); - operand->mode = Addressing_Type; - operand->type = t_invalid; - return false; - } - Type *elem = y.type; - if (!is_type_valid_vector_elem(elem)) { - gbString str = type_to_string(elem); - error(call, "Invalid element type for 'intrinsics.simd_vector', expected an integer or float with no specific endianness, got '%s'", str); - gb_string_free(str); - operand->mode = Addressing_Type; - operand->type = t_invalid; - return false; - } - - operand->mode = Addressing_Type; - operand->type = alloc_type_simd_vector(count, elem); - break; - } - case BuiltinProc_is_package_imported: { bool value = false; @@ -2596,7 +3591,14 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 return false; } - if (!is_type_integer_like(x.type)) { + if (is_type_simd_vector(x.type)) { + Type *elem = base_array_type(x.type); + if (!is_type_integer_like(elem)) { + gbString xts = type_to_string(x.type); + error(x.expr, "#simd values passed to '%.*s' must have an element of an integer-like type (integer, boolean, enum, bit_set), got %s", LIT(builtin_name), xts); + gb_string_free(xts); + } + } else if (!is_type_integer_like(x.type)) { gbString xts = type_to_string(x.type); error(x.expr, "Values passed to '%.*s' must be an integer-like type (integer, boolean, enum, bit_set), got %s", LIT(builtin_name), xts); gb_string_free(xts); @@ -2654,7 +3656,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 if (y.mode == Addressing_Invalid) { return false; } - convert_to_typed(c, &y, x.type); + convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false; convert_to_typed(c, &x, y.type); if (is_type_untyped(x.type)) { gbString xts = type_to_string(x.type); @@ -2691,14 +3693,23 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 if (x.mode == Addressing_Invalid) { return false; } - if (!is_type_float(x.type)) { + + Type *elem = core_array_type(x.type); + if (!is_type_float(x.type) && !(is_type_simd_vector(x.type) && is_type_float(elem))) { gbString xts = type_to_string(x.type); - error(x.expr, "Expected a floating point value for '%.*s', got %s", LIT(builtin_name), xts); + error(x.expr, "Expected a floating point or #simd vector value for '%.*s', got %s", LIT(builtin_name), xts); gb_string_free(xts); return false; + } else if (is_type_different_to_arch_endianness(elem)) { + GB_ASSERT(elem->kind == Type_Basic); + if (elem->Basic.flags & (BasicFlag_EndianLittle|BasicFlag_EndianBig)) { + gbString xts = type_to_string(x.type); + error(x.expr, "Expected a float which does not specify the explicit endianness for '%.*s', got %s", LIT(builtin_name), xts); + gb_string_free(xts); + return false; + } } - - if (x.mode == Addressing_Constant) { + if (is_type_float(x.type) && x.mode == Addressing_Constant) { f64 v = exact_value_to_f64(x.value); operand->mode = Addressing_Constant; @@ -2711,6 +3722,59 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } break; + case BuiltinProc_fused_mul_add: + { + Operand x = {}; + Operand y = {}; + Operand z = {}; + check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false; + check_expr(c, &y, ce->args[1]); if (y.mode == Addressing_Invalid) return false; + check_expr(c, &z, ce->args[2]); if (z.mode == Addressing_Invalid) return false; + + convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false; + convert_to_typed(c, &x, y.type); if (x.mode == Addressing_Invalid) return false; + convert_to_typed(c, &z, x.type); if (z.mode == Addressing_Invalid) return false; + convert_to_typed(c, &x, z.type); if (x.mode == Addressing_Invalid) return false; + if (is_type_untyped(x.type)) { + gbString xts = type_to_string(x.type); + error(x.expr, "Expected a typed floating point value or #simd vector for '%.*s', got %s", LIT(builtin_name), xts); + gb_string_free(xts); + return false; + } + + Type *elem = core_array_type(x.type); + if (!is_type_float(x.type) && !(is_type_simd_vector(x.type) && is_type_float(elem))) { + gbString xts = type_to_string(x.type); + error(x.expr, "Expected a floating point or #simd vector value for '%.*s', got %s", LIT(builtin_name), xts); + gb_string_free(xts); + return false; + } + if (is_type_different_to_arch_endianness(elem)) { + GB_ASSERT(elem->kind == Type_Basic); + if (elem->Basic.flags & (BasicFlag_EndianLittle|BasicFlag_EndianBig)) { + gbString xts = type_to_string(x.type); + error(x.expr, "Expected a float which does not specify the explicit endianness for '%.*s', got %s", LIT(builtin_name), xts); + gb_string_free(xts); + return false; + } + } + + if (!are_types_identical(x.type, y.type) || !are_types_identical(y.type, z.type)) { + gbString xts = type_to_string(x.type); + gbString yts = type_to_string(y.type); + gbString zts = type_to_string(z.type); + error(x.expr, "Mismatched types for '%.*s', got %s vs %s vs %s", LIT(builtin_name), xts, yts, zts); + gb_string_free(zts); + gb_string_free(yts); + gb_string_free(xts); + return false; + } + + operand->mode = Addressing_Value; + operand->type = default_type(x.type); + } + break; + case BuiltinProc_mem_copy: case BuiltinProc_mem_copy_non_overlapping: { @@ -2734,13 +3798,13 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } - if (!is_type_pointer(dst.type)) { + if (!is_type_pointer(dst.type) && !is_type_multi_pointer(dst.type)) { gbString str = type_to_string(dst.type); error(dst.expr, "Expected a pointer value for '%.*s', got %s", LIT(builtin_name), str); gb_string_free(str); return false; } - if (!is_type_pointer(src.type)) { + if (!is_type_pointer(src.type) && !is_type_multi_pointer(src.type)) { gbString str = type_to_string(src.type); error(src.expr, "Expected a pointer value for '%.*s', got %s", LIT(builtin_name), str); gb_string_free(str); @@ -2782,7 +3846,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } - if (!is_type_pointer(ptr.type)) { + if (!is_type_pointer(ptr.type) && !is_type_multi_pointer(ptr.type)) { gbString str = type_to_string(ptr.type); error(ptr.expr, "Expected a pointer value for '%.*s', got %s", LIT(builtin_name), str); gb_string_free(str); @@ -2826,7 +3890,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 operand->mode = Addressing_Value; operand->type = ptr.type; - if (!is_type_pointer(ptr.type)) { + if (!is_type_pointer(ptr.type) && !is_type_multi_pointer(ptr.type)) { gbString str = type_to_string(ptr.type); error(ptr.expr, "Expected a pointer value for '%.*s', got %s", LIT(builtin_name), str); gb_string_free(str); @@ -2869,7 +3933,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 operand->mode = Addressing_Value; operand->type = t_int; - if (!is_type_pointer(ptr0.type)) { + if (!is_type_pointer(ptr0.type) && !is_type_multi_pointer(ptr0.type)) { gbString str = type_to_string(ptr0.type); error(ptr0.expr, "Expected a pointer value for '%.*s', got %s", LIT(builtin_name), str); gb_string_free(str); @@ -2882,7 +3946,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 return false; } - if (!is_type_pointer(ptr1.type)) { + if (!is_type_pointer(ptr1.type) && !is_type_multi_pointer(ptr1.type)) { gbString str = type_to_string(ptr1.type); error(ptr1.expr, "Expected a pointer value for '%.*s', got %s", LIT(builtin_name), str); gb_string_free(str); @@ -2908,21 +3972,62 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 break; - case BuiltinProc_atomic_fence: - case BuiltinProc_atomic_fence_acq: - case BuiltinProc_atomic_fence_rel: - case BuiltinProc_atomic_fence_acqrel: - operand->mode = Addressing_NoValue; + case BuiltinProc_atomic_type_is_lock_free: + { + Ast *expr = ce->args[0]; + Operand o = {}; + check_expr_or_type(c, &o, expr); + + if (o.mode == Addressing_Invalid || o.mode == Addressing_Builtin) { + return false; + } + if (o.type == nullptr || o.type == t_invalid || is_type_asm_proc(o.type)) { + error(o.expr, "Invalid argument to '%.*s'", LIT(builtin_name)); + return false; + } + if (is_type_polymorphic(o.type)) { + error(o.expr, "'%.*s' of polymorphic type cannot be determined", LIT(builtin_name)); + return false; + } + if (is_type_untyped(o.type)) { + error(o.expr, "'%.*s' of untyped type is not allowed", LIT(builtin_name)); + return false; + } + Type *t = o.type; + bool is_lock_free = is_type_lock_free(t); + + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + operand->value = exact_value_bool(is_lock_free); + break; + } + + case BuiltinProc_atomic_thread_fence: + case BuiltinProc_atomic_signal_fence: + { + OdinAtomicMemoryOrder memory_order = {}; + if (!check_atomic_memory_order_argument(c, ce->args[0], builtin_name, &memory_order)) { + return false; + } + switch (memory_order) { + case OdinAtomicMemoryOrder_acquire: + case OdinAtomicMemoryOrder_release: + case OdinAtomicMemoryOrder_acq_rel: + case OdinAtomicMemoryOrder_seq_cst: + break; + default: + error(ce->args[0], "Illegal memory ordering for '%.*s', got .%s", LIT(builtin_name), OdinAtomicMemoryOrder_strings[memory_order]); + break; + } + + operand->mode = Addressing_NoValue; + } break; case BuiltinProc_volatile_store: - /*fallthrough*/ case BuiltinProc_unaligned_store: - /*fallthrough*/ + case BuiltinProc_non_temporal_store: case BuiltinProc_atomic_store: - case BuiltinProc_atomic_store_rel: - case BuiltinProc_atomic_store_relaxed: - case BuiltinProc_atomic_store_unordered: { Type *elem = nullptr; if (!is_type_normal_pointer(operand->type, &elem)) { @@ -2938,60 +4043,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 break; } - case BuiltinProc_volatile_load: - /*fallthrough*/ - case BuiltinProc_unaligned_load: - /*fallthrough*/ - case BuiltinProc_atomic_load: - case BuiltinProc_atomic_load_acq: - case BuiltinProc_atomic_load_relaxed: - case BuiltinProc_atomic_load_unordered: - { - Type *elem = nullptr; - if (!is_type_normal_pointer(operand->type, &elem)) { - error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name)); - return false; - } - operand->type = elem; - operand->mode = Addressing_Value; - break; - } - - case BuiltinProc_atomic_add: - case BuiltinProc_atomic_add_acq: - case BuiltinProc_atomic_add_rel: - case BuiltinProc_atomic_add_acqrel: - case BuiltinProc_atomic_add_relaxed: - case BuiltinProc_atomic_sub: - case BuiltinProc_atomic_sub_acq: - case BuiltinProc_atomic_sub_rel: - case BuiltinProc_atomic_sub_acqrel: - case BuiltinProc_atomic_sub_relaxed: - case BuiltinProc_atomic_and: - case BuiltinProc_atomic_and_acq: - case BuiltinProc_atomic_and_rel: - case BuiltinProc_atomic_and_acqrel: - case BuiltinProc_atomic_and_relaxed: - case BuiltinProc_atomic_nand: - case BuiltinProc_atomic_nand_acq: - case BuiltinProc_atomic_nand_rel: - case BuiltinProc_atomic_nand_acqrel: - case BuiltinProc_atomic_nand_relaxed: - case BuiltinProc_atomic_or: - case BuiltinProc_atomic_or_acq: - case BuiltinProc_atomic_or_rel: - case BuiltinProc_atomic_or_acqrel: - case BuiltinProc_atomic_or_relaxed: - case BuiltinProc_atomic_xor: - case BuiltinProc_atomic_xor_acq: - case BuiltinProc_atomic_xor_rel: - case BuiltinProc_atomic_xor_acqrel: - case BuiltinProc_atomic_xor_relaxed: - case BuiltinProc_atomic_xchg: - case BuiltinProc_atomic_xchg_acq: - case BuiltinProc_atomic_xchg_rel: - case BuiltinProc_atomic_xchg_acqrel: - case BuiltinProc_atomic_xchg_relaxed: + case BuiltinProc_atomic_store_explicit: { Type *elem = nullptr; if (!is_type_normal_pointer(operand->type, &elem)) { @@ -3002,30 +4054,146 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 check_expr_with_type_hint(c, &x, ce->args[1], elem); check_assignment(c, &x, elem, builtin_name); + OdinAtomicMemoryOrder memory_order = {}; + if (!check_atomic_memory_order_argument(c, ce->args[2], builtin_name, &memory_order)) { + return false; + } + switch (memory_order) { + case OdinAtomicMemoryOrder_consume: + case OdinAtomicMemoryOrder_acquire: + case OdinAtomicMemoryOrder_acq_rel: + error(ce->args[2], "Illegal memory order .%s for '%.*s'", OdinAtomicMemoryOrder_strings[memory_order], LIT(builtin_name)); + break; + } + + operand->type = nullptr; + operand->mode = Addressing_NoValue; + break; + } + + + case BuiltinProc_volatile_load: + case BuiltinProc_unaligned_load: + case BuiltinProc_non_temporal_load: + case BuiltinProc_atomic_load: + { + Type *elem = nullptr; + if (!is_type_normal_pointer(operand->type, &elem)) { + error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name)); + return false; + } operand->type = elem; operand->mode = Addressing_Value; break; } - case BuiltinProc_atomic_cxchg: - case BuiltinProc_atomic_cxchg_acq: - case BuiltinProc_atomic_cxchg_rel: - case BuiltinProc_atomic_cxchg_acqrel: - case BuiltinProc_atomic_cxchg_relaxed: - case BuiltinProc_atomic_cxchg_failrelaxed: - case BuiltinProc_atomic_cxchg_failacq: - case BuiltinProc_atomic_cxchg_acq_failrelaxed: - case BuiltinProc_atomic_cxchg_acqrel_failrelaxed: + case BuiltinProc_atomic_load_explicit: + { + Type *elem = nullptr; + if (!is_type_normal_pointer(operand->type, &elem)) { + error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name)); + return false; + } - case BuiltinProc_atomic_cxchgweak: - case BuiltinProc_atomic_cxchgweak_acq: - case BuiltinProc_atomic_cxchgweak_rel: - case BuiltinProc_atomic_cxchgweak_acqrel: - case BuiltinProc_atomic_cxchgweak_relaxed: - case BuiltinProc_atomic_cxchgweak_failrelaxed: - case BuiltinProc_atomic_cxchgweak_failacq: - case BuiltinProc_atomic_cxchgweak_acq_failrelaxed: - case BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed: + OdinAtomicMemoryOrder memory_order = {}; + if (!check_atomic_memory_order_argument(c, ce->args[1], builtin_name, &memory_order)) { + return false; + } + + switch (memory_order) { + case OdinAtomicMemoryOrder_release: + case OdinAtomicMemoryOrder_acq_rel: + error(ce->args[1], "Illegal memory order .%s for '%.*s'", OdinAtomicMemoryOrder_strings[memory_order], LIT(builtin_name)); + break; + } + + operand->type = elem; + operand->mode = Addressing_Value; + break; + } + + case BuiltinProc_atomic_add: + case BuiltinProc_atomic_sub: + case BuiltinProc_atomic_and: + case BuiltinProc_atomic_nand: + case BuiltinProc_atomic_or: + case BuiltinProc_atomic_xor: + case BuiltinProc_atomic_exchange: + { + Type *elem = nullptr; + if (!is_type_normal_pointer(operand->type, &elem)) { + error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name)); + return false; + } + Operand x = {}; + check_expr_with_type_hint(c, &x, ce->args[1], elem); + check_assignment(c, &x, elem, builtin_name); + + Type *t = type_deref(operand->type); + switch (id) { + case BuiltinProc_atomic_add: + case BuiltinProc_atomic_sub: + if (!is_type_numeric(t)) { + gbString str = type_to_string(t); + error(operand->expr, "Expected a numeric type for '%.*s', got %s", LIT(builtin_name), str); + gb_string_free(str); + } else if (is_type_different_to_arch_endianness(t)) { + gbString str = type_to_string(t); + error(operand->expr, "Expected a numeric type of the same platform endianness for '%.*s', got %s", LIT(builtin_name), str); + gb_string_free(str); + } + } + + operand->type = elem; + operand->mode = Addressing_Value; + break; + } + + case BuiltinProc_atomic_add_explicit: + case BuiltinProc_atomic_sub_explicit: + case BuiltinProc_atomic_and_explicit: + case BuiltinProc_atomic_nand_explicit: + case BuiltinProc_atomic_or_explicit: + case BuiltinProc_atomic_xor_explicit: + case BuiltinProc_atomic_exchange_explicit: + { + Type *elem = nullptr; + if (!is_type_normal_pointer(operand->type, &elem)) { + error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name)); + return false; + } + Operand x = {}; + check_expr_with_type_hint(c, &x, ce->args[1], elem); + check_assignment(c, &x, elem, builtin_name); + + + if (!check_atomic_memory_order_argument(c, ce->args[2], builtin_name, nullptr)) { + return false; + } + + Type *t = type_deref(operand->type); + switch (id) { + case BuiltinProc_atomic_add_explicit: + case BuiltinProc_atomic_sub_explicit: + if (!is_type_numeric(t)) { + gbString str = type_to_string(t); + error(operand->expr, "Expected a numeric type for '%.*s', got %s", LIT(builtin_name), str); + gb_string_free(str); + } else if (is_type_different_to_arch_endianness(t)) { + gbString str = type_to_string(t); + error(operand->expr, "Expected a numeric type of the same platform endianness for '%.*s', got %s", LIT(builtin_name), str); + gb_string_free(str); + } + break; + } + + operand->type = elem; + operand->mode = Addressing_Value; + break; + } + + case BuiltinProc_atomic_compare_exchange_strong: + case BuiltinProc_atomic_compare_exchange_weak: { Type *elem = nullptr; if (!is_type_normal_pointer(operand->type, &elem)) { @@ -3039,11 +4207,110 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 check_assignment(c, &x, elem, builtin_name); check_assignment(c, &y, elem, builtin_name); + Type *t = type_deref(operand->type); + if (!is_type_comparable(t)) { + gbString str = type_to_string(t); + error(operand->expr, "Expected a comparable type for '%.*s', got %s", LIT(builtin_name), str); + gb_string_free(str); + } + + operand->mode = Addressing_OptionalOk; + operand->type = elem; + break; + } + + case BuiltinProc_atomic_compare_exchange_strong_explicit: + case BuiltinProc_atomic_compare_exchange_weak_explicit: + { + Type *elem = nullptr; + if (!is_type_normal_pointer(operand->type, &elem)) { + error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name)); + return false; + } + Operand x = {}; + Operand y = {}; + check_expr_with_type_hint(c, &x, ce->args[1], elem); + check_expr_with_type_hint(c, &y, ce->args[2], elem); + check_assignment(c, &x, elem, builtin_name); + check_assignment(c, &y, elem, builtin_name); + + OdinAtomicMemoryOrder success_memory_order = {}; + OdinAtomicMemoryOrder failure_memory_order = {}; + if (!check_atomic_memory_order_argument(c, ce->args[3], builtin_name, &success_memory_order, "success ordering")) { + return false; + } + if (!check_atomic_memory_order_argument(c, ce->args[4], builtin_name, &failure_memory_order, "failure ordering")) { + return false; + } + + Type *t = type_deref(operand->type); + if (!is_type_comparable(t)) { + gbString str = type_to_string(t); + error(operand->expr, "Expected a comparable type for '%.*s', got %s", LIT(builtin_name), str); + gb_string_free(str); + } + + bool invalid_combination = false; + + switch (success_memory_order) { + case OdinAtomicMemoryOrder_relaxed: + case OdinAtomicMemoryOrder_release: + if (failure_memory_order != OdinAtomicMemoryOrder_relaxed) { + invalid_combination = true; + } + break; + case OdinAtomicMemoryOrder_consume: + switch (failure_memory_order) { + case OdinAtomicMemoryOrder_relaxed: + case OdinAtomicMemoryOrder_consume: + break; + default: + invalid_combination = true; + break; + } + break; + case OdinAtomicMemoryOrder_acquire: + case OdinAtomicMemoryOrder_acq_rel: + switch (failure_memory_order) { + case OdinAtomicMemoryOrder_relaxed: + case OdinAtomicMemoryOrder_consume: + case OdinAtomicMemoryOrder_acquire: + break; + default: + invalid_combination = true; + break; + } + break; + case OdinAtomicMemoryOrder_seq_cst: + switch (failure_memory_order) { + case OdinAtomicMemoryOrder_relaxed: + case OdinAtomicMemoryOrder_consume: + case OdinAtomicMemoryOrder_acquire: + case OdinAtomicMemoryOrder_seq_cst: + break; + default: + invalid_combination = true; + break; + } + break; + default: + invalid_combination = true; + break; + } + + + if (invalid_combination) { + error(ce->args[3], "Illegal memory order pairing for '%.*s', success = .%s, failure = .%s", + LIT(builtin_name), + OdinAtomicMemoryOrder_strings[success_memory_order], + OdinAtomicMemoryOrder_strings[failure_memory_order] + ); + } + operand->mode = Addressing_OptionalOk; operand->type = elem; break; } - break; case BuiltinProc_fixed_point_mul: case BuiltinProc_fixed_point_div: @@ -3065,7 +4332,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 if (x.mode == Addressing_Invalid) { return false; } - convert_to_typed(c, &y, x.type); + convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false; if (x.mode == Addressing_Invalid) { return false; } @@ -3122,7 +4389,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 if (y.mode == Addressing_Invalid) { return false; } - convert_to_typed(c, &y, x.type); + convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false; convert_to_typed(c, &x, y.type); if (!are_types_identical(x.type, y.type)) { gbString xts = type_to_string(x.type); @@ -3224,6 +4491,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 case TargetOs_linux: case TargetOs_essence: case TargetOs_freebsd: + case TargetOs_openbsd: switch (build_context.metrics.arch) { case TargetArch_i386: case TargetArch_amd64: @@ -3310,9 +4578,12 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 case BuiltinProc_type_is_simple_compare: case BuiltinProc_type_is_dereferenceable: case BuiltinProc_type_is_valid_map_key: + case BuiltinProc_type_is_valid_matrix_elements: case BuiltinProc_type_is_named: case BuiltinProc_type_is_pointer: + case BuiltinProc_type_is_multi_pointer: case BuiltinProc_type_is_array: + case BuiltinProc_type_is_enumerated_array: case BuiltinProc_type_is_slice: case BuiltinProc_type_is_dynamic_array: case BuiltinProc_type_is_map: @@ -3320,10 +4591,9 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 case BuiltinProc_type_is_union: case BuiltinProc_type_is_enum: case BuiltinProc_type_is_proc: - case BuiltinProc_type_is_bit_field: - case BuiltinProc_type_is_bit_field_value: case BuiltinProc_type_is_bit_set: case BuiltinProc_type_is_simd_vector: + case BuiltinProc_type_is_matrix: case BuiltinProc_type_is_specialized_polymorphic_record: case BuiltinProc_type_is_unspecialized_polymorphic_record: case BuiltinProc_type_has_nil: @@ -3372,6 +4642,37 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 break; } break; + case BuiltinProc_type_field_type: + { + Operand op = {}; + Type *bt = check_type(c, ce->args[0]); + Type *type = base_type(bt); + if (type == nullptr || type == t_invalid) { + error(ce->args[0], "Expected a type for '%.*s'", LIT(builtin_name)); + return false; + } + Operand x = {}; + check_expr(c, &x, ce->args[1]); + + if (!is_type_string(x.type) || x.mode != Addressing_Constant || x.value.kind != ExactValue_String) { + error(ce->args[1], "Expected a const string for field argument"); + return false; + } + + String field_name = x.value.value_string; + + Selection sel = lookup_field(type, field_name, false); + if (sel.index.count == 0) { + gbString t = type_to_string(type); + error(ce->args[1], "'%.*s' is not a field of type %s", LIT(field_name), t); + gb_string_free(t); + return false; + } + operand->mode = Addressing_Type; + operand->type = sel.entity->type; + break; + } + break; case BuiltinProc_type_is_specialization_of: { @@ -3691,6 +4992,31 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 break; + case BuiltinProc_type_is_subtype_of: + { + Operand op_src = {}; + Operand op_dst = {}; + + check_expr_or_type(c, &op_src, ce->args[0]); + if (op_src.mode != Addressing_Type) { + gbString e = expr_to_string(op_src.expr); + error(op_src.expr, "'%.*s' expects a type, got %s", LIT(builtin_name), e); + gb_string_free(e); + return false; + } + check_expr_or_type(c, &op_dst, ce->args[1]); + if (op_dst.mode != Addressing_Type) { + gbString e = expr_to_string(op_dst.expr); + error(op_dst.expr, "'%.*s' expects a type, got %s", LIT(builtin_name), e); + gb_string_free(e); + return false; + } + + operand->value = exact_value_bool(is_type_subtype_of(op_src.type, op_dst.type)); + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + } break; + case BuiltinProc_type_field_index_of: { Operand op = {}; @@ -3780,6 +5106,238 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 operand->type = t_hasher_proc; break; } + + case BuiltinProc_constant_utf16_cstring: + { + String value = {}; + if (!is_constant_string(c, builtin_name, ce->args[0], &value)) { + return false; + } + operand->mode = Addressing_Value; + operand->type = alloc_type_multi_pointer(t_u16); + operand->value = {}; + break; + } + + + case BuiltinProc_wasm_memory_grow: + { + if (!is_arch_wasm()) { + error(call, "'%.*s' is only allowed on wasm targets", LIT(builtin_name)); + return false; + } + + Operand index = {}; + Operand delta = {}; + check_expr(c, &index, ce->args[0]); if (index.mode == Addressing_Invalid) return false; + check_expr(c, &delta, ce->args[1]); if (delta.mode == Addressing_Invalid) return false; + + convert_to_typed(c, &index, t_uintptr); if (index.mode == Addressing_Invalid) return false; + convert_to_typed(c, &delta, t_uintptr); if (delta.mode == Addressing_Invalid) return false; + + if (!is_operand_value(index) || !check_is_assignable_to(c, &index, t_uintptr)) { + gbString e = expr_to_string(index.expr); + gbString t = type_to_string(index.type); + error(index.expr, "'%.*s' expected a uintptr for the memory index, got '%s' of type %s", LIT(builtin_name), e, t); + gb_string_free(t); + gb_string_free(e); + return false; + } + + if (!is_operand_value(delta) || !check_is_assignable_to(c, &delta, t_uintptr)) { + gbString e = expr_to_string(delta.expr); + gbString t = type_to_string(delta.type); + error(delta.expr, "'%.*s' expected a uintptr for the memory delta, got '%s' of type %s", LIT(builtin_name), e, t); + gb_string_free(t); + gb_string_free(e); + return false; + } + + operand->mode = Addressing_Value; + operand->type = t_int; + operand->value = {}; + break; + } + break; + case BuiltinProc_wasm_memory_size: + { + if (!is_arch_wasm()) { + error(call, "'%.*s' is only allowed on wasm targets", LIT(builtin_name)); + return false; + } + + Operand index = {}; + check_expr(c, &index, ce->args[0]); if (index.mode == Addressing_Invalid) return false; + + convert_to_typed(c, &index, t_uintptr); if (index.mode == Addressing_Invalid) return false; + + if (!is_operand_value(index) || !check_is_assignable_to(c, &index, t_uintptr)) { + gbString e = expr_to_string(index.expr); + gbString t = type_to_string(index.type); + error(index.expr, "'%.*s' expected a uintptr for the memory index, got '%s' of type %s", LIT(builtin_name), e, t); + gb_string_free(t); + gb_string_free(e); + return false; + } + + operand->mode = Addressing_Value; + operand->type = t_int; + operand->value = {}; + break; + } + break; + + case BuiltinProc_wasm_memory_atomic_wait32: + { + if (!is_arch_wasm()) { + error(call, "'%.*s' is only allowed on wasm targets", LIT(builtin_name)); + return false; + } + + Operand ptr = {}; + Operand expected = {}; + Operand timeout = {}; + check_expr(c, &ptr, ce->args[0]); if (ptr.mode == Addressing_Invalid) return false; + check_expr(c, &expected, ce->args[1]); if (expected.mode == Addressing_Invalid) return false; + check_expr(c, &timeout, ce->args[2]); if (timeout.mode == Addressing_Invalid) return false; + + Type *t_u32_ptr = alloc_type_pointer(t_u32); + convert_to_typed(c, &ptr, t_u32_ptr); if (ptr.mode == Addressing_Invalid) return false; + convert_to_typed(c, &expected, t_u32); if (expected.mode == Addressing_Invalid) return false; + convert_to_typed(c, &timeout, t_i64); if (timeout.mode == Addressing_Invalid) return false; + + if (!is_operand_value(ptr) || !check_is_assignable_to(c, &ptr, t_u32_ptr)) { + gbString e = expr_to_string(ptr.expr); + gbString t = type_to_string(ptr.type); + error(ptr.expr, "'%.*s' expected ^u32 for the memory pointer, got '%s' of type %s", LIT(builtin_name), e, t); + gb_string_free(t); + gb_string_free(e); + return false; + } + + if (!is_operand_value(expected) || !check_is_assignable_to(c, &expected, t_u32)) { + gbString e = expr_to_string(expected.expr); + gbString t = type_to_string(expected.type); + error(expected.expr, "'%.*s' expected u32 for the 'expected' value, got '%s' of type %s", LIT(builtin_name), e, t); + gb_string_free(t); + gb_string_free(e); + return false; + } + + if (!is_operand_value(timeout) || !check_is_assignable_to(c, &timeout, t_i64)) { + gbString e = expr_to_string(timeout.expr); + gbString t = type_to_string(timeout.type); + error(timeout.expr, "'%.*s' expected i64 for the timeout, got '%s' of type %s", LIT(builtin_name), e, t); + gb_string_free(t); + gb_string_free(e); + return false; + } + + operand->mode = Addressing_Value; + operand->type = t_u32; + operand->value = {}; + break; + } + break; + case BuiltinProc_wasm_memory_atomic_notify32: + { + if (!is_arch_wasm()) { + error(call, "'%.*s' is only allowed on wasm targets", LIT(builtin_name)); + return false; + } + + Operand ptr = {}; + Operand waiters = {}; + check_expr(c, &ptr, ce->args[0]); if (ptr.mode == Addressing_Invalid) return false; + check_expr(c, &waiters, ce->args[1]); if (waiters.mode == Addressing_Invalid) return false; + + Type *t_u32_ptr = alloc_type_pointer(t_u32); + convert_to_typed(c, &ptr, t_u32_ptr); if (ptr.mode == Addressing_Invalid) return false; + convert_to_typed(c, &waiters, t_u32); if (waiters.mode == Addressing_Invalid) return false; + + if (!is_operand_value(ptr) || !check_is_assignable_to(c, &ptr, t_u32_ptr)) { + gbString e = expr_to_string(ptr.expr); + gbString t = type_to_string(ptr.type); + error(ptr.expr, "'%.*s' expected ^u32 for the memory pointer, got '%s' of type %s", LIT(builtin_name), e, t); + gb_string_free(t); + gb_string_free(e); + return false; + } + + if (!is_operand_value(waiters) || !check_is_assignable_to(c, &waiters, t_u32)) { + gbString e = expr_to_string(waiters.expr); + gbString t = type_to_string(waiters.type); + error(waiters.expr, "'%.*s' expected u32 for the 'waiters' value, got '%s' of type %s", LIT(builtin_name), e, t); + gb_string_free(t); + gb_string_free(e); + return false; + } + + operand->mode = Addressing_Value; + operand->type = t_u32; + operand->value = {}; + break; + } + break; + + case BuiltinProc_x86_cpuid: + { + if (!is_arch_x86()) { + error(call, "'%.*s' is only allowed on x86 targets (i386, amd64)", LIT(builtin_name)); + return false; + } + + Operand ax = {}; + Operand cx = {}; + + check_expr_with_type_hint(c, &ax, ce->args[0], t_u32); if (ax.mode == Addressing_Invalid) return false; + check_expr_with_type_hint(c, &cx, ce->args[1], t_u32); if (cx.mode == Addressing_Invalid) return false; + convert_to_typed(c, &ax, t_u32); if (ax.mode == Addressing_Invalid) return false; + convert_to_typed(c, &cx, t_u32); if (cx.mode == Addressing_Invalid) return false; + if (!are_types_identical(ax.type, t_u32)) { + gbString str = type_to_string(ax.type); + error(ax.expr, "'%.*s' expected a u32, got %s", LIT(builtin_name), str); + gb_string_free(str); + return false; + } + if (!are_types_identical(cx.type, t_u32)) { + gbString str = type_to_string(cx.type); + error(cx.expr, "'%.*s' expected a u32, got %s", LIT(builtin_name), str); + gb_string_free(str); + return false; + } + Type *types[4] = {t_u32, t_u32, t_u32, t_u32}; // eax ebc ecx edx + operand->type = alloc_type_tuple_from_field_types(types, gb_count_of(types), false, false); + operand->mode = Addressing_Value; + operand->value = {}; + return true; + } + break; + case BuiltinProc_x86_xgetbv: + { + if (!is_arch_x86()) { + error(call, "'%.*s' is only allowed on x86 targets (i386, amd64)", LIT(builtin_name)); + return false; + } + + Operand cx = {}; + check_expr_with_type_hint(c, &cx, ce->args[0], t_u32); if (cx.mode == Addressing_Invalid) return false; + convert_to_typed(c, &cx, t_u32); if (cx.mode == Addressing_Invalid) return false; + if (!are_types_identical(cx.type, t_u32)) { + gbString str = type_to_string(cx.type); + error(cx.expr, "'%.*s' expected a u32, got %s", LIT(builtin_name), str); + gb_string_free(str); + return false; + } + + Type *types[2] = {t_u32, t_u32}; + operand->type = alloc_type_tuple_from_field_types(types, gb_count_of(types), false, false); + operand->mode = Addressing_Value; + operand->value = {}; + return true; + } + break; + } return true; diff --git a/src/check_decl.cpp b/src/check_decl.cpp index f9bc17ba4..86280b6cb 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -174,6 +174,10 @@ void check_init_constant(CheckerContext *ctx, Entity *e, Operand *operand) { return; } + if (is_type_proc(e->type)) { + error(e->token, "Illegal declaration of a constant procedure value"); + } + e->parent_proc_decl = ctx->curr_proc_decl; e->Constant.value = operand->value; @@ -238,6 +242,51 @@ isize total_attribute_count(DeclInfo *decl) { return attribute_count; } +Type *clone_enum_type(CheckerContext *ctx, Type *original_enum_type, Type *named_type) { + // NOTE(bill, 2022-02-05): Stupid edge case for `distinct` declarations + // + // X :: enum {A, B, C} + // Y :: distinct X + // + // To make Y be just like X, it will need to copy the elements of X and change their type + // so that they match Y rather than X. + GB_ASSERT(original_enum_type != nullptr); + GB_ASSERT(named_type != nullptr); + GB_ASSERT(original_enum_type->kind == Type_Enum); + GB_ASSERT(named_type->kind == Type_Named); + + Scope *parent = original_enum_type->Enum.scope->parent; + Scope *scope = create_scope(nullptr, parent); + + + Type *et = alloc_type_enum(); + et->Enum.base_type = original_enum_type->Enum.base_type; + et->Enum.min_value = original_enum_type->Enum.min_value; + et->Enum.max_value = original_enum_type->Enum.max_value; + et->Enum.min_value_index = original_enum_type->Enum.min_value_index; + et->Enum.max_value_index = original_enum_type->Enum.max_value_index; + et->Enum.scope = scope; + + auto fields = array_make(permanent_allocator(), original_enum_type->Enum.fields.count); + for_array(i, fields) { + Entity *old = original_enum_type->Enum.fields[i]; + + Entity *e = alloc_entity_constant(scope, old->token, named_type, old->Constant.value); + e->file = old->file; + e->identifier = clone_ast(old->identifier); + e->flags |= EntityFlag_Visited; + e->state = EntityState_Resolved; + e->Constant.flags = old->Constant.flags; + e->Constant.docs = old->Constant.docs; + e->Constant.comment = old->Constant.comment; + + fields[i] = e; + add_entity(ctx, scope, nullptr, e); + add_entity_use(ctx, e->identifier, e); + } + et->Enum.fields = fields; + return et; +} void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def) { GB_ASSERT(e->type == nullptr); @@ -258,15 +307,25 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def) Type *bt = check_type_expr(ctx, te, named); check_type_path_pop(ctx); - named->Named.base = base_type(bt); - - if (is_distinct && is_type_typeid(e->type)) { - error(init_expr, "'distinct' cannot be applied to 'typeid'"); - is_distinct = false; + Type *base = base_type(bt); + if (is_distinct && bt->kind == Type_Named && base->kind == Type_Enum) { + base = clone_enum_type(ctx, base, named); } - if (is_distinct && is_type_any(e->type)) { - error(init_expr, "'distinct' cannot be applied to 'any'"); - is_distinct = false; + named->Named.base = base; + + if (is_distinct) { + if (is_type_typeid(e->type)) { + error(init_expr, "'distinct' cannot be applied to 'typeid'"); + is_distinct = false; + } else if (is_type_any(e->type)) { + error(init_expr, "'distinct' cannot be applied to 'any'"); + is_distinct = false; + } else if (is_type_simd_vector(e->type)) { + gbString str = type_to_string(e->type); + error(init_expr, "'distinct' cannot be applied to '%s'", str); + gb_string_free(str); + is_distinct = false; + } } if (!is_distinct) { e->type = bt; @@ -289,6 +348,13 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def) if (decl != nullptr) { AttributeContext ac = {}; check_decl_attributes(ctx, decl->attributes, type_decl_attribute, &ac); + if (e->kind == Entity_TypeName && ac.objc_class != "") { + e->TypeName.objc_class_name = ac.objc_class; + + if (type_size_of(e->type) > 0) { + error(e->token, "@(objc_class) marked type must be of zero size"); + } + } } @@ -380,12 +446,56 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init, if (type_expr) { e->type = check_type(ctx, type_expr); + if (are_types_identical(e->type, t_typeid)) { + e->type = nullptr; + e->kind = Entity_TypeName; + check_type_decl(ctx, e, init, named_type); + return; + } } Operand operand = {}; if (init != nullptr) { - Entity *entity = nullptr; + Entity *entity = check_entity_from_ident_or_selector(ctx, init, false); + if (entity != nullptr && entity->kind == Entity_TypeName) { + // @TypeAliasingProblem + // NOTE(bill, 2022-02-03): This is used to solve the problem caused by type aliases + // being "confused" as constants + // + // A :: B + // C :: proc "c" (^A) + // B :: struct {x: C} + // + // A gets evaluated first, and then checks B. + // B then checks C. + // C then tries to check A which is unresolved but thought to be a constant. + // Therefore within C's check, A errs as "not a type". + // + // This is because a const declaration may or may not be a type and this cannot + // be determined from a syntactical standpoint. + // This check allows the compiler to override the entity to be checked as a type. + // + // There is no problem if B is prefixed with the `#type` helper enforcing at + // both a syntax and semantic level that B must be a type. + // + // A :: #type B + // + // This approach is not fool proof and can fail in case such as: + // + // X :: type_of(x) + // X :: Foo(int).Type + // + // Since even these kind of declarations may cause weird checking cycles. + // For the time being, these are going to be treated as an unfortunate error + // until there is a proper delaying system to try declaration again if they + // have failed. + + e->kind = Entity_TypeName; + check_type_decl(ctx, e, init, named_type); + return; + } + entity = nullptr; if (init->kind == Ast_Ident) { entity = check_ident(ctx, &operand, init, nullptr, e->type, true); } else if (init->kind == Ast_SelectorExpr) { @@ -732,6 +842,75 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { } e->Procedure.optimization_mode = cast(ProcedureOptimizationMode)ac.optimization_mode; + if (ac.objc_name.len || ac.objc_is_class_method || ac.objc_type) { + if (ac.objc_name.len == 0 && ac.objc_is_class_method) { + error(e->token, "@(objc_name) is required with @(objc_is_class_method)"); + } else if (ac.objc_type == nullptr) { + error(e->token, "@(objc_name) requires that @(objc_type) to be set"); + } else if (ac.objc_name.len == 0 && ac.objc_type) { + error(e->token, "@(objc_name) is required with @(objc_type)"); + } else { + Type *t = ac.objc_type; + if (t->kind == Type_Named) { + Entity *tn = t->Named.type_name; + + GB_ASSERT(tn->kind == Entity_TypeName); + + if (tn->scope != e->scope) { + error(e->token, "@(objc_name) attribute may only be applied to procedures and types within the same scope"); + } else { + mutex_lock(&global_type_name_objc_metadata_mutex); + defer (mutex_unlock(&global_type_name_objc_metadata_mutex)); + + if (!tn->TypeName.objc_metadata) { + tn->TypeName.objc_metadata = create_type_name_obj_c_metadata(); + } + auto *md = tn->TypeName.objc_metadata; + mutex_lock(md->mutex); + defer (mutex_unlock(md->mutex)); + + if (!ac.objc_is_class_method) { + bool ok = true; + for (TypeNameObjCMetadataEntry const &entry : md->value_entries) { + if (entry.name == ac.objc_name) { + error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name)); + ok = false; + break; + } + } + if (ok) { + array_add(&md->value_entries, TypeNameObjCMetadataEntry{ac.objc_name, e}); + } + } else { + bool ok = true; + for (TypeNameObjCMetadataEntry const &entry : md->type_entries) { + if (entry.name == ac.objc_name) { + error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name)); + ok = false; + break; + } + } + if (ok) { + array_add(&md->type_entries, TypeNameObjCMetadataEntry{ac.objc_name, e}); + } + } + } + } + } + } + + if (ac.require_target_feature.len != 0 && ac.enable_target_feature.len != 0) { + error(e->token, "Attributes @(require_target_feature=...) and @(enable_target_feature=...) cannot be used together"); + } else if (ac.require_target_feature.len != 0) { + if (check_target_feature_is_enabled(e->token.pos, ac.require_target_feature)) { + e->Procedure.target_feature = ac.require_target_feature; + } else { + e->Procedure.target_feature_disabled = true; + } + } else if (ac.enable_target_feature.len != 0) { + enable_target_feature(e->token.pos, ac.enable_target_feature); + e->Procedure.target_feature = ac.enable_target_feature; + } switch (e->Procedure.optimization_mode) { case ProcedureOptimizationMode_None: @@ -835,10 +1014,12 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { } } - if (pt->result_count == 0 && ac.require_results) { - error(pl->type, "'require_results' is not needed on a procedure with no results"); - } else { - pt->require_results = ac.require_results; + if (ac.require_results) { + if (pt->result_count == 0) { + error(pl->type, "'require_results' is not needed on a procedure with no results"); + } else { + pt->require_results = true; + } } if (ac.link_name.len > 0) { @@ -857,18 +1038,16 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { } Entity *foreign_library = init_entity_foreign_library(ctx, e); - if (is_arch_wasm()) { + if (is_arch_wasm() && foreign_library != nullptr) { String module_name = str_lit("env"); - if (foreign_library != nullptr) { - GB_ASSERT (foreign_library->kind == Entity_LibraryName); - if (foreign_library->LibraryName.paths.count != 1) { - error(foreign_library->token, "'foreign import' for '%.*s' architecture may only have one path, got %td", - LIT(target_arch_names[build_context.metrics.arch]), foreign_library->LibraryName.paths.count); - } - - if (foreign_library->LibraryName.paths.count >= 1) { - module_name = foreign_library->LibraryName.paths[0]; - } + GB_ASSERT (foreign_library->kind == Entity_LibraryName); + if (foreign_library->LibraryName.paths.count != 1) { + error(foreign_library->token, "'foreign import' for '%.*s' architecture may only have one path, got %td", + LIT(target_arch_names[build_context.metrics.arch]), foreign_library->LibraryName.paths.count); + } + + if (foreign_library->LibraryName.paths.count >= 1) { + module_name = foreign_library->LibraryName.paths[0]; } name = concatenate3_strings(permanent_allocator(), module_name, WASM_MODULE_NAME_SEPARATOR, name); } @@ -975,6 +1154,12 @@ void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr, } ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix); + if (is_arch_wasm() && e->Variable.thread_local_model.len != 0) { + e->Variable.thread_local_model.len = 0; + // NOTE(bill): ignore this message for the time begin + // error(e->token, "@(thread_local) is not supported for this target platform"); + } + String context_name = str_lit("variable declaration"); if (type_expr != nullptr) { @@ -1050,6 +1235,8 @@ void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr, Operand o = {}; check_expr_with_type_hint(ctx, &o, init_expr, e->type); check_init_variable(ctx, e, &o, str_lit("variable declaration")); + + check_rtti_type_disallowed(e->token, e->type, "A variable declaration is using a type, %s, which has been disallowed"); } void check_proc_group_decl(CheckerContext *ctx, Entity *&pg_entity, DeclInfo *d) { @@ -1142,20 +1329,20 @@ void check_proc_group_decl(CheckerContext *ctx, Entity *&pg_entity, DeclInfo *d) if (!both_have_where_clauses) switch (kind) { case ProcOverload_Identical: - error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name)); + error(p->token, "Overloaded procedure '%.*s' has the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name)); is_invalid = true; break; // case ProcOverload_CallingConvention: - // error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name)); + // error(p->token, "Overloaded procedure '%.*s' has the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name)); // is_invalid = true; // break; case ProcOverload_ParamVariadic: - error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name)); + error(p->token, "Overloaded procedure '%.*s' has the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name)); is_invalid = true; break; case ProcOverload_ResultCount: case ProcOverload_ResultTypes: - error(p->token, "Overloaded procedure '%.*s' as the same parameters but different results in the procedure group '%.*s'", LIT(name), LIT(proc_group_name)); + error(p->token, "Overloaded procedure '%.*s' has the same parameters but different results in the procedure group '%.*s'", LIT(name), LIT(proc_group_name)); is_invalid = true; break; case ProcOverload_Polymorphic: diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 81f69055a..cf9f2f751 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -132,6 +132,62 @@ void check_did_you_mean_print(DidYouMeanAnswers *d, char const *prefix = "") { } } +void populate_check_did_you_mean_objc_entity(StringSet *set, Entity *e, bool is_type) { + if (e->kind != Entity_TypeName) { + return; + } + if (e->TypeName.objc_metadata == nullptr) { + return; + } + TypeNameObjCMetadata *objc_metadata = e->TypeName.objc_metadata; + Type *t = base_type(e->type); + GB_ASSERT(t->kind == Type_Struct); + + if (is_type) { + for_array(i, objc_metadata->type_entries) { + String name = objc_metadata->type_entries[i].name; + string_set_add(set, name); + } + } else { + for_array(i, objc_metadata->value_entries) { + String name = objc_metadata->value_entries[i].name; + string_set_add(set, name); + } + } + + for_array(i, t->Struct.fields) { + Entity *f = t->Struct.fields[i]; + if (f->flags & EntityFlag_Using && f->type != nullptr) { + if (f->type->kind == Type_Named && f->type->Named.type_name) { + populate_check_did_you_mean_objc_entity(set, f->type->Named.type_name, is_type); + } + } + } +} + + +void check_did_you_mean_objc_entity(String const &name, Entity *e, bool is_type, char const *prefix = "") { + ERROR_BLOCK(); + GB_ASSERT(e->kind == Entity_TypeName); + GB_ASSERT(e->TypeName.objc_metadata != nullptr); + auto *objc_metadata = e->TypeName.objc_metadata; + mutex_lock(objc_metadata->mutex); + defer (mutex_unlock(objc_metadata->mutex)); + + StringSet set = {}; + string_set_init(&set, heap_allocator()); + defer (string_set_destroy(&set)); + populate_check_did_you_mean_objc_entity(&set, e, is_type); + + + DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), set.entries.count, name); + defer (did_you_mean_destroy(&d)); + for_array(i, set.entries) { + did_you_mean_append(&d, set.entries[i].value); + } + check_did_you_mean_print(&d, prefix); +} + void check_did_you_mean_type(String const &name, Array const &fields, char const *prefix = "") { ERROR_BLOCK(); @@ -144,6 +200,7 @@ void check_did_you_mean_type(String const &name, Array const &fields, check_did_you_mean_print(&d, prefix); } + void check_did_you_mean_type(String const &name, Slice const &fields, char const *prefix = "") { ERROR_BLOCK(); @@ -228,42 +285,6 @@ void check_scope_decls(CheckerContext *c, Slice const &nodes, isize reser } } - -isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isize level = 0, bool src_is_ptr = false) { - Type *prev_src = src; - src = type_deref(src); - if (!src_is_ptr) { - src_is_ptr = src != prev_src; - } - src = base_type(src); - - if (!is_type_struct(src)) { - return 0; - } - - for_array(i, src->Struct.fields) { - Entity *f = src->Struct.fields[i]; - if (f->kind != Entity_Variable || (f->flags&EntityFlag_Using) == 0) { - continue; - } - - if (are_types_identical(f->type, dst)) { - return level+1; - } - if (src_is_ptr && is_type_pointer(dst)) { - if (are_types_identical(f->type, type_deref(dst))) { - return level+1; - } - } - isize nested_level = check_is_assignable_to_using_subtype(f->type, dst, level+1, src_is_ptr); - if (nested_level > 0) { - return nested_level; - } - } - - return 0; -} - bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, Entity *base_entity, Type *type, Array *param_operands, Ast *poly_def_node, PolyProcData *poly_proc_data) { /////////////////////////////////////////////////////////////////////////////// @@ -421,6 +442,14 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, Entity *base_ final_proc_type->Proc.is_poly_specialized = true; final_proc_type->Proc.is_polymorphic = true; + final_proc_type->Proc.variadic = src->Proc.variadic; + final_proc_type->Proc.require_results = src->Proc.require_results; + final_proc_type->Proc.c_vararg = src->Proc.c_vararg; + final_proc_type->Proc.has_named_results = src->Proc.has_named_results; + final_proc_type->Proc.diverging = src->Proc.diverging; + final_proc_type->Proc.return_by_pointer = src->Proc.return_by_pointer; + final_proc_type->Proc.optional_ok = src->Proc.optional_ok; + for (isize i = 0; i < operands.count; i++) { Operand o = operands[i]; @@ -508,6 +537,10 @@ bool check_cast_internal(CheckerContext *c, Operand *x, Type *type); #define MAXIMUM_TYPE_DISTANCE 10 i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type) { + if (c == nullptr) { + GB_ASSERT(operand->mode == Addressing_Value); + GB_ASSERT(is_type_typed(operand->type)); + } if (operand->mode == Addressing_Invalid || type == t_invalid) { return -1; @@ -673,6 +706,42 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type return 1; } } + + // TODO(bill): Determine which rule is a better on in practice + #if 1 + if (dst->Union.variants.count == 1) { + Type *vt = dst->Union.variants[0]; + i64 score = check_distance_between_types(c, operand, vt); + if (score >= 0) { + return score+2; + } + } + #else + // NOTE(bill): check to see you can assign to it with one of the variants? + i64 prev_lowest_score = -1; + i64 lowest_score = -1; + for_array(i, dst->Union.variants) { + Type *vt = dst->Union.variants[i]; + i64 score = check_distance_between_types(c, operand, vt); + if (score >= 0) { + if (lowest_score < 0) { + lowest_score = score; + } else { + if (prev_lowest_score < 0) { + prev_lowest_score = lowest_score; + } else { + prev_lowest_score = gb_min(prev_lowest_score, lowest_score); + } + lowest_score = gb_min(lowest_score, score); + } + } + } + if (lowest_score >= 0) { + if (prev_lowest_score != lowest_score) { // remove possible ambiguities + return lowest_score+2; + } + } + #endif } if (is_type_relative_pointer(dst)) { @@ -716,6 +785,14 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type return distance + 6; } } + + if (is_type_simd_vector(dst)) { + Type *dst_elem = base_array_type(dst); + i64 distance = check_distance_between_types(c, operand, dst_elem); + if (distance >= 0) { + return distance + 6; + } + } if (is_type_matrix(dst)) { Type *dst_elem = base_array_type(dst); @@ -725,6 +802,7 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type } } + if (is_type_any(dst)) { if (!is_type_polymorphic(src)) { if (operand->mode == Addressing_Context && operand->type == t_context) { @@ -782,6 +860,13 @@ bool check_is_assignable_to(CheckerContext *c, Operand *operand, Type *type) { return check_is_assignable_to_with_score(c, operand, type, &score); } +bool internal_check_is_assignable_to(Type *src, Type *dst) { + Operand x = {}; + x.type = src; + x.mode = Addressing_Value; + return check_is_assignable_to(nullptr, &x, dst); +} + AstPackage *get_package_of_type(Type *type) { for (;;) { if (type == nullptr) { @@ -1260,6 +1345,19 @@ bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, Type *source, } } return false; + + case Type_SimdVector: + if (source->kind == Type_SimdVector) { + if (poly->SimdVector.generic_count != nullptr) { + if (!polymorphic_assign_index(&poly->SimdVector.generic_count, &poly->SimdVector.count, source->SimdVector.count)) { + return false; + } + } + if (poly->SimdVector.count == source->SimdVector.count) { + return is_polymorphic_type_assignable(c, poly->SimdVector.elem, source->SimdVector.elem, true, modify_type); + } + } + return false; } return false; } @@ -1286,7 +1384,6 @@ bool check_cycle(CheckerContext *c, Entity *curr, bool report) { return false; } - Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *named_type, Type *type_hint, bool allow_import_name) { GB_ASSERT(n->kind == Ast_Ident); o->mode = Addressing_Invalid; @@ -1422,8 +1519,12 @@ Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *named_type, Typ case Entity_TypeName: o->mode = Addressing_Type; if (check_cycle(c, e, true)) { - type = t_invalid; + o->type = t_invalid; } + if (o->type != nullptr && type->kind == Type_Named && o->type->Named.type_name->TypeName.is_type_alias) { + o->type = base_type(o->type); + } + break; case Entity_ImportName: @@ -1496,9 +1597,11 @@ bool check_unary_op(CheckerContext *c, Operand *o, Token op) { bool check_binary_op(CheckerContext *c, Operand *o, Token op) { Type *main_type = o->type; + // TODO(bill): Handle errors correctly Type *type = base_type(core_array_type(main_type)); Type *ct = core_type(type); + switch (op.kind) { case Token_Sub: case Token_SubEq: @@ -1515,6 +1618,9 @@ bool check_binary_op(CheckerContext *c, Operand *o, Token op) { if (is_type_matrix(main_type)) { error(op, "Operator '%.*s' is only allowed with matrix types", LIT(op.string)); return false; + } else if (is_type_simd_vector(main_type) && is_type_integer(type)) { + error(op, "Operator '%.*s' is only allowed with #simd types with integer elements", LIT(op.string)); + return false; } /*fallthrough*/ case Token_Mul: @@ -1566,14 +1672,9 @@ bool check_binary_op(CheckerContext *c, Operand *o, Token op) { if (!is_type_integer(type)) { error(op, "Operator '%.*s' is only allowed with integers", LIT(op.string)); return false; - } - if (is_type_simd_vector(o->type)) { - switch (op.kind) { - case Token_ModMod: - case Token_ModModEq: - error(op, "Operator '%.*s' is only allowed with integers", LIT(op.string)); - return false; - } + } else if (is_type_simd_vector(main_type)) { + error(op, "Operator '%.*s' is only allowed with #simd types with integer elements", LIT(op.string)); + return false; } break; @@ -1583,14 +1684,6 @@ bool check_binary_op(CheckerContext *c, Operand *o, Token op) { error(op, "Operator '%.*s' is only allowed with integers and bit sets", LIT(op.string)); return false; } - if (is_type_simd_vector(o->type)) { - switch (op.kind) { - case Token_AndNot: - case Token_AndNotEq: - error(op, "Operator '%.*s' is only allowed with integers", LIT(op.string)); - return false; - } - } break; case Token_CmpAnd: @@ -2416,6 +2509,8 @@ void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node, Type *typ gb_string_free(err_str); } + // TODO(bill): Should we support shifts for fixed arrays and #simd vectors? + if (!is_type_integer(x->type)) { gbString err_str = expr_to_string(y->expr); error(node, "Shift operand '%s' must be an integer", err_str); @@ -2626,6 +2721,26 @@ bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y) { return true; } + if (is_type_simd_vector(src) && is_type_simd_vector(dst)) { + if (src->SimdVector.count != dst->SimdVector.count) { + return false; + } + Type *elem_src = base_array_type(src); + Type *elem_dst = base_array_type(dst); + Operand x = {}; + x.type = elem_src; + x.mode = Addressing_Value; + return check_is_castable_to(c, &x, elem_dst); + } + + if (is_type_simd_vector(dst)) { + Type *elem = base_array_type(dst); + if (check_is_castable_to(c, operand, elem)) { + return true; + } + } + + return false; } @@ -2715,14 +2830,14 @@ bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type *t) { return false; } - if (o->mode == Addressing_Constant) { - gbString expr_str = expr_to_string(o->expr); - error(o->expr, "Cannot transmute a constant expression: '%s'", expr_str); - gb_string_free(expr_str); - o->mode = Addressing_Invalid; - o->expr = node; - return false; - } + // if (o->mode == Addressing_Constant) { + // gbString expr_str = expr_to_string(o->expr); + // error(o->expr, "Cannot transmute a constant expression: '%s'", expr_str); + // gb_string_free(expr_str); + // o->mode = Addressing_Invalid; + // o->expr = node; + // return false; + // } if (is_type_untyped(o->type)) { gbString expr_str = expr_to_string(o->expr); @@ -2773,8 +2888,10 @@ bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type *t) { } } + o->expr = node; o->mode = Addressing_Value; o->type = t; + o->value = {}; return true; } @@ -2842,7 +2959,14 @@ void check_binary_matrix(CheckerContext *c, Token const &op, Operand *x, Operand goto matrix_error; } x->mode = Addressing_Value; - x->type = alloc_type_matrix(xt->Matrix.elem, xt->Matrix.row_count, yt->Matrix.column_count); + if (are_types_identical(xt, yt)) { + if (!is_type_named(x->type) && is_type_named(y->type)) { + // prefer the named type + x->type = y->type; + } + } else { + x->type = alloc_type_matrix(xt->Matrix.elem, xt->Matrix.row_count, yt->Matrix.column_count); + } goto matrix_success; } else if (yt->kind == Type_Array) { if (!are_types_identical(xt->Matrix.elem, yt->Array.elem)) { @@ -2904,7 +3028,6 @@ void check_binary_matrix(CheckerContext *c, Token const &op, Operand *x, Operand matrix_success: x->type = check_matrix_type_hint(x->type, type_hint); - return; @@ -3132,13 +3255,19 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint return; } - - if (!are_types_identical(x->type, y->type)) { + if ((op.kind == Token_CmpAnd || op.kind == Token_CmpOr) && + is_type_boolean(x->type) && is_type_boolean(y->type)) { + // NOTE(bill, 2022-06-26) + // Allow any boolean types within `&&` and `||` + // This is an exception to all other binary expressions since the result + // of a comparison will always be an untyped boolean, and allowing + // any boolean between these two simplifies a lot of expressions + } else if (!are_types_identical(x->type, y->type)) { if (x->type != t_invalid && y->type != t_invalid) { gbString xt = type_to_string(x->type); gbString yt = type_to_string(y->type); - gbString expr_str = expr_to_string(x->expr); + gbString expr_str = expr_to_string(node); error(op, "Mismatched types in binary expression '%s' : '%s' vs '%s'", expr_str, xt, yt); gb_string_free(expr_str); gb_string_free(yt); @@ -3419,7 +3548,6 @@ void convert_untyped_error(CheckerContext *c, Operand *operand, Type *target_typ if (operand->value.kind == ExactValue_String) { String key = operand->value.value_string; if (is_type_string(operand->type) && is_type_enum(target_type)) { - gb_printf_err("HERE!\n"); Type *et = base_type(target_type); check_did_you_mean_type(key, et->Enum.fields, "."); } @@ -4044,7 +4172,11 @@ ExactValue get_constant_field(CheckerContext *c, Operand const *operand, Selecti Type *determine_swizzle_array_type(Type *original_type, Type *type_hint, isize new_count) { Type *array_type = base_type(type_deref(original_type)); - GB_ASSERT(array_type->kind == Type_Array); + GB_ASSERT(array_type->kind == Type_Array || array_type->kind == Type_SimdVector); + if (array_type->kind == Type_SimdVector) { + Type *elem_type = array_type->SimdVector.elem; + return alloc_type_simd_vector(new_count, elem_type); + } Type *elem_type = array_type->Array.elem; Type *swizzle_array_type = nullptr; @@ -4065,6 +4197,101 @@ Type *determine_swizzle_array_type(Type *original_type, Type *type_hint, isize n } +bool is_entity_declared_for_selector(Entity *entity, Scope *import_scope, bool *allow_builtin) { + bool is_declared = entity != nullptr; + if (is_declared) { + if (entity->kind == Entity_Builtin) { + // NOTE(bill): Builtin's are in the universal scope which is part of every scopes hierarchy + // This means that we should just ignore the found result through it + *allow_builtin = entity->scope == import_scope || entity->scope != builtin_pkg->scope; + } else if ((entity->scope->flags&ScopeFlag_Global) == ScopeFlag_Global && (import_scope->flags&ScopeFlag_Global) == 0) { + is_declared = false; + } + } + return is_declared; +} + +// NOTE(bill, 2022-02-03): see `check_const_decl` for why it exists reasoning +Entity *check_entity_from_ident_or_selector(CheckerContext *c, Ast *node, bool ident_only) { + if (node->kind == Ast_Ident) { + String name = node->Ident.token.string; + return scope_lookup(c->scope, name); + } else if (!ident_only) if (node->kind == Ast_SelectorExpr) { + ast_node(se, SelectorExpr, node); + if (se->token.kind == Token_ArrowRight) { + return nullptr; + } + + Ast *op_expr = se->expr; + Ast *selector = unparen_expr(se->selector); + if (selector == nullptr) { + return nullptr; + } + if (selector->kind != Ast_Ident) { + return nullptr; + } + + Entity *entity = nullptr; + Entity *expr_entity = nullptr; + bool check_op_expr = true; + + if (op_expr->kind == Ast_Ident) { + String op_name = op_expr->Ident.token.string; + Entity *e = scope_lookup(c->scope, op_name); + if (e == nullptr) { + return nullptr; + } + add_entity_use(c, op_expr, e); + expr_entity = e; + + if (e != nullptr && e->kind == Entity_ImportName && selector->kind == Ast_Ident) { + // IMPORTANT NOTE(bill): This is very sloppy code but it's also very fragile + // It pretty much needs to be in this order and this way + // If you can clean this up, please do but be really careful + String import_name = op_name; + Scope *import_scope = e->ImportName.scope; + String entity_name = selector->Ident.token.string; + + check_op_expr = false; + entity = scope_lookup_current(import_scope, entity_name); + bool allow_builtin = false; + if (!is_entity_declared_for_selector(entity, import_scope, &allow_builtin)) { + return nullptr; + } + + check_entity_decl(c, entity, nullptr, nullptr); + if (entity->kind == Entity_ProcGroup) { + return entity; + } + GB_ASSERT_MSG(entity->type != nullptr, "%.*s (%.*s)", LIT(entity->token.string), LIT(entity_strings[entity->kind])); + } + } + + Operand operand = {}; + if (check_op_expr) { + check_expr_base(c, &operand, op_expr, nullptr); + if (operand.mode == Addressing_Invalid) { + return nullptr; + } + } + + if (entity == nullptr && selector->kind == Ast_Ident) { + String field_name = selector->Ident.token.string; + if (is_type_dynamic_array(type_deref(operand.type))) { + init_mem_allocator(c->checker); + } + auto sel = lookup_field(operand.type, field_name, operand.mode == Addressing_Type); + entity = sel.entity; + } + + if (entity != nullptr) { + return entity; + } + } + return nullptr; +} + + Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *type_hint) { ast_node(se, SelectorExpr, node); @@ -4113,18 +4340,8 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ check_op_expr = false; entity = scope_lookup_current(import_scope, entity_name); - bool is_declared = entity != nullptr; bool allow_builtin = false; - if (is_declared) { - if (entity->kind == Entity_Builtin) { - // NOTE(bill): Builtin's are in the universal scope which is part of every scopes hierarchy - // This means that we should just ignore the found result through it - allow_builtin = entity->scope == import_scope || entity->scope != builtin_pkg->scope; - } else if ((entity->scope->flags&ScopeFlag_Global) == ScopeFlag_Global && (import_scope->flags&ScopeFlag_Global) == 0) { - is_declared = false; - } - } - if (!is_declared) { + if (!is_entity_declared_for_selector(entity, import_scope, &allow_builtin)) { error(op_expr, "'%.*s' is not declared by '%.*s'", LIT(entity_name), LIT(import_name)); operand->mode = Addressing_Invalid; operand->expr = node; @@ -4214,7 +4431,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ } } - if (entity == nullptr && selector->kind == Ast_Ident && is_type_array(type_deref(operand->type))) { + if (entity == nullptr && selector->kind == Ast_Ident && is_type_array(type_deref(operand->type))) { // TODO(bill): Simd_Vector swizzling String field_name = selector->Ident.token.string; @@ -4315,14 +4532,19 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ if (entity == nullptr) { gbString op_str = expr_to_string(op_expr); - gbString type_str = type_to_string(operand->type); + gbString type_str = type_to_string_shorthand(operand->type); gbString sel_str = expr_to_string(selector); error(op_expr, "'%s' of type '%s' has no field '%s'", op_str, type_str, sel_str); if (operand->type != nullptr && selector->kind == Ast_Ident) { String const &name = selector->Ident.token.string; Type *bt = base_type(operand->type); - if (bt->kind == Type_Struct) { + if (operand->type->kind == Type_Named && + operand->type->Named.type_name && + operand->type->Named.type_name->kind == Entity_TypeName && + operand->type->Named.type_name->TypeName.objc_metadata) { + check_did_you_mean_objc_entity(name, operand->type->Named.type_name, operand->mode == Addressing_Type); + } else if (bt->kind == Type_Struct) { check_did_you_mean_type(name, bt->Struct.fields); } else if (bt->kind == Type_Enum) { check_did_you_mean_type(name, bt->Enum.fields); @@ -4351,7 +4573,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ } gbString op_str = expr_to_string(op_expr); - gbString type_str = type_to_string(operand->type); + gbString type_str = type_to_string_shorthand(operand->type); gbString sel_str = expr_to_string(selector); error(op_expr, "Cannot access non-constant field '%s' from '%s'", sel_str, op_str); gb_string_free(sel_str); @@ -4376,7 +4598,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ } gbString op_str = expr_to_string(op_expr); - gbString type_str = type_to_string(operand->type); + gbString type_str = type_to_string_shorthand(operand->type); gbString sel_str = expr_to_string(selector); error(op_expr, "Cannot access non-constant field '%s' from '%s'", sel_str, op_str); gb_string_free(sel_str); @@ -4389,7 +4611,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ if (expr_entity != nullptr && is_type_polymorphic(expr_entity->type)) { gbString op_str = expr_to_string(op_expr); - gbString type_str = type_to_string(operand->type); + gbString type_str = type_to_string_shorthand(operand->type); gbString sel_str = expr_to_string(selector); error(op_expr, "Cannot access field '%s' from non-specialized polymorphic type '%s'", sel_str, op_str); gb_string_free(sel_str); @@ -4712,25 +4934,16 @@ bool is_expr_constant_zero(Ast *expr) { return false; } - -CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { - ast_node(ce, CallExpr, call); - GB_ASSERT(is_type_proc(proc_type)); - proc_type = base_type(proc_type); - TypeProc *pt = &proc_type->Proc; - +isize get_procedure_param_count_excluding_defaults(Type *pt, isize *param_count_) { + GB_ASSERT(pt != nullptr); + GB_ASSERT(pt->kind == Type_Proc); isize param_count = 0; isize param_count_excluding_defaults = 0; - bool variadic = pt->variadic; - bool vari_expand = (ce->ellipsis.pos.line != 0); - i64 score = 0; - bool show_error = show_error_mode == CallArgumentMode_ShowErrors; - - + bool variadic = pt->Proc.variadic; TypeTuple *param_tuple = nullptr; - if (pt->params != nullptr) { - param_tuple = &pt->params->Tuple; + if (pt->Proc.params != nullptr) { + param_tuple = &pt->Proc.params->Tuple; param_count = param_tuple->variables.count; if (variadic) { @@ -4770,6 +4983,31 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { } } + if (param_count_) *param_count_ = param_count; + return param_count_excluding_defaults; +} + + +CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { + ast_node(ce, CallExpr, call); + GB_ASSERT(is_type_proc(proc_type)); + proc_type = base_type(proc_type); + TypeProc *pt = &proc_type->Proc; + + isize param_count = 0; + isize param_count_excluding_defaults = get_procedure_param_count_excluding_defaults(proc_type, ¶m_count); + bool variadic = pt->variadic; + bool vari_expand = (ce->ellipsis.pos.line != 0); + i64 score = 0; + bool show_error = show_error_mode == CallArgumentMode_ShowErrors; + + + TypeTuple *param_tuple = nullptr; + if (pt->params != nullptr) { + param_tuple = &pt->params->Tuple; + } + + CallArgumentError err = CallArgumentError_None; Type *final_proc_type = proc_type; Entity *gen_entity = nullptr; @@ -5442,7 +5680,37 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type if (operand->mode == Addressing_ProcGroup) { check_entity_decl(c, operand->proc_group, nullptr, nullptr); - Array procs = proc_group_entities(c, *operand); + auto procs = proc_group_entities_cloned(c, *operand); + + if (procs.count > 1) { + isize max_arg_count = args.count; + for_array(i, args) { + // NOTE(bill): The only thing that may have multiple values + // will be a call expression (assuming `or_return` and `()` will be stripped) + Ast *arg = strip_or_return_expr(args[i]); + if (arg && arg->kind == Ast_CallExpr) { + max_arg_count = ISIZE_MAX; + break; + } + } + + for (isize proc_index = 0; proc_index < procs.count; /**/) { + Entity *proc = procs[proc_index]; + Type *pt = base_type(proc->type); + if (!(pt != nullptr && is_type_proc(pt))) { + continue; + } + + isize param_count = 0; + isize param_count_excluding_defaults = get_procedure_param_count_excluding_defaults(pt, ¶m_count); + + if (param_count_excluding_defaults > max_arg_count) { + array_unordered_remove(&procs, proc_index); + } else { + proc_index++; + } + } + } if (procs.count == 1) { Ast *ident = operand->expr; @@ -5472,6 +5740,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type return data; } + Entity **lhs = nullptr; isize lhs_count = -1; @@ -5756,8 +6025,12 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type ctx.curr_proc_sig = e->type; GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit); - evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true); + bool ok = evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true); decl->where_clauses_evaluated = true; + + if (ok && (data.gen_entity->flags & EntityFlag_ProcBodyChecked) == 0) { + check_procedure_later(c, e->file, e->token, decl, e->type, decl->proc_lit->ProcLit.body, decl->proc_lit->ProcLit.tags); + } } return data; } @@ -5770,6 +6043,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type Entity *e = entity_of_node(ident); + CallArgumentData data = {}; CallArgumentError err = call_checker(c, call, proc_type, e, operands, CallArgumentMode_ShowErrors, &data); gb_unused(err); @@ -5778,7 +6052,6 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type if (entity_to_use != nullptr) { update_untyped_expr_type(c, operand->expr, entity_to_use->type, true); } - if (data.gen_entity != nullptr) { Entity *e = data.gen_entity; DeclInfo *decl = data.gen_entity->decl_info; @@ -5790,8 +6063,12 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type ctx.curr_proc_sig = e->type; GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit); - evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true); + bool ok = evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true); decl->where_clauses_evaluated = true; + + if (ok && (data.gen_entity->flags & EntityFlag_ProcBodyChecked) == 0) { + check_procedure_later(c, e->file, e->token, decl, e->type, decl->proc_lit->ProcLit.body, decl->proc_lit->ProcLit.tags); + } } return data; } @@ -6290,10 +6567,10 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr return builtin_procs[id].kind; } - Entity *e = entity_of_node(operand->expr); + Entity *initial_entity = entity_of_node(operand->expr); - if (e != nullptr && e->kind == Entity_Procedure) { - if (e->Procedure.deferred_procedure.entity != nullptr) { + if (initial_entity != nullptr && initial_entity->kind == Entity_Procedure) { + if (initial_entity->Procedure.deferred_procedure.entity != nullptr) { call->viral_state_flags |= ViralStateFlag_ContainsDeferredProcedure; } } @@ -6861,6 +7138,1912 @@ void check_matrix_index_expr(CheckerContext *c, Operand *o, Ast *node, Type *typ } +struct TypeAndToken { + Type *type; + Token token; +}; + +typedef PtrMap SeenMap; + +void add_constant_switch_case(CheckerContext *ctx, SeenMap *seen, Operand operand, bool use_expr = true) { + if (operand.mode != Addressing_Constant) { + return; + } + if (operand.value.kind == ExactValue_Invalid) { + return; + } + + uintptr key = hash_exact_value(operand.value); + TypeAndToken *found = map_get(seen, key); + if (found != nullptr) { + isize count = multi_map_count(seen, key); + TypeAndToken *taps = gb_alloc_array(temporary_allocator(), TypeAndToken, count); + + multi_map_get_all(seen, key, taps); + for (isize i = 0; i < count; i++) { + TypeAndToken tap = taps[i]; + if (!are_types_identical(operand.type, tap.type)) { + continue; + } + + TokenPos pos = tap.token.pos; + if (use_expr) { + gbString expr_str = expr_to_string(operand.expr); + error(operand.expr, + "Duplicate case '%s'\n" + "\tprevious case at %s", + expr_str, + token_pos_to_string(pos)); + gb_string_free(expr_str); + } else { + error(operand.expr, "Duplicate case found with previous case at %s", token_pos_to_string(pos)); + } + return; + } + } + + TypeAndToken tap = {operand.type, ast_token(operand.expr)}; + multi_map_insert(seen, key, tap); +} + + +void add_to_seen_map(CheckerContext *ctx, SeenMap *seen, TokenKind upper_op, Operand const &x, Operand const &lhs, Operand const &rhs) { + if (is_type_enum(x.type)) { + // TODO(bill): Fix this logic so it's fast!!! + + i64 v0 = exact_value_to_i64(lhs.value); + i64 v1 = exact_value_to_i64(rhs.value); + Operand v = {}; + v.mode = Addressing_Constant; + v.type = x.type; + v.expr = x.expr; + + Type *bt = base_type(x.type); + GB_ASSERT(bt->kind == Type_Enum); + for (i64 vi = v0; vi <= v1; vi++) { + if (upper_op != Token_LtEq && vi == v1) { + break; + } + + bool found = false; + for_array(j, bt->Enum.fields) { + Entity *f = bt->Enum.fields[j]; + GB_ASSERT(f->kind == Entity_Constant); + + i64 fv = exact_value_to_i64(f->Constant.value); + if (fv == vi) { + found = true; + break; + } + } + if (found) { + v.value = exact_value_i64(vi); + add_constant_switch_case(ctx, seen, v); + } + } + } else { + add_constant_switch_case(ctx, seen, lhs); + if (upper_op == Token_LtEq) { + add_constant_switch_case(ctx, seen, rhs); + } + } +} +void add_to_seen_map(CheckerContext *ctx, SeenMap *seen, Operand const &x) { + add_constant_switch_case(ctx, seen, x); +} + +ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { + ast_node(bd, BasicDirective, node); + + ExprKind kind = Expr_Expr; + + o->mode = Addressing_Constant; + String name = bd->name.string; + if (name == "file") { + o->type = t_untyped_string; + o->value = exact_value_string(get_file_path_string(bd->token.pos.file_id)); + } else if (name == "line") { + o->type = t_untyped_integer; + o->value = exact_value_i64(bd->token.pos.line); + } else if (name == "procedure") { + if (c->curr_proc_decl == nullptr) { + error(node, "#procedure may only be used within procedures"); + o->type = t_untyped_string; + o->value = exact_value_string(str_lit("")); + } else { + o->type = t_untyped_string; + o->value = exact_value_string(c->proc_name); + } + } else if (name == "caller_location") { + init_core_source_code_location(c->checker); + error(node, "#caller_location may only be used as a default argument parameter"); + o->type = t_source_code_location; + o->mode = Addressing_Value; + } else { + if (name == "location") { + init_core_source_code_location(c->checker); + error(node, "'#%.*s' must be used in a call expression", LIT(name)); + o->type = t_source_code_location; + o->mode = Addressing_Value; + } else if ( + name == "assert" || + name == "defined" || + name == "config" || + name == "load" || + name == "load_hash" || + name == "load_or" + ) { + error(node, "'#%.*s' must be used as a call", LIT(name)); + o->type = t_invalid; + o->mode = Addressing_Invalid; + } else { + error(node, "Unknown directive: #%.*s", LIT(name)); + o->type = t_invalid; + o->mode = Addressing_Invalid; + } + + } + return kind; +} + +ExprKind check_ternary_if_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { + ExprKind kind = Expr_Expr; + Operand cond = {Addressing_Invalid}; + ast_node(te, TernaryIfExpr, node); + check_expr(c, &cond, te->cond); + node->viral_state_flags |= te->cond->viral_state_flags; + + if (cond.mode != Addressing_Invalid && !is_type_boolean(cond.type)) { + error(te->cond, "Non-boolean condition in ternary if expression"); + } + + Operand x = {Addressing_Invalid}; + Operand y = {Addressing_Invalid}; + check_expr_or_type(c, &x, te->x, type_hint); + node->viral_state_flags |= te->x->viral_state_flags; + + if (te->y != nullptr) { + Type *th = type_hint; + if (type_hint == nullptr && is_type_typed(x.type)) { + th = x.type; + } + check_expr_or_type(c, &y, te->y, th); + node->viral_state_flags |= te->y->viral_state_flags; + } else { + error(node, "A ternary expression must have an else clause"); + return kind; + } + + if (x.type == nullptr || x.type == t_invalid || + y.type == nullptr || y.type == t_invalid) { + return kind; + } + + convert_to_typed(c, &x, y.type); + if (x.mode == Addressing_Invalid) { + return kind; + } + convert_to_typed(c, &y, x.type); + if (y.mode == Addressing_Invalid) { + x.mode = Addressing_Invalid; + return kind; + } + + if (!ternary_compare_types(x.type, y.type)) { + gbString its = type_to_string(x.type); + gbString ets = type_to_string(y.type); + error(node, "Mismatched types in ternary if expression, %s vs %s", its, ets); + gb_string_free(ets); + gb_string_free(its); + return kind; + } + + o->type = x.type; + if (is_type_untyped_nil(o->type) || is_type_untyped_undef(o->type)) { + o->type = y.type; + } + + o->mode = Addressing_Value; + o->expr = node; + if (type_hint != nullptr && is_type_untyped(o->type)) { + if (check_cast_internal(c, &x, type_hint) && + check_cast_internal(c, &y, type_hint)) { + convert_to_typed(c, o, type_hint); + update_untyped_expr_type(c, node, type_hint, !is_type_untyped(type_hint)); + } + } + return kind; +} + +ExprKind check_ternary_when_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { + ExprKind kind = Expr_Expr; + Operand cond = {}; + ast_node(te, TernaryWhenExpr, node); + check_expr(c, &cond, te->cond); + node->viral_state_flags |= te->cond->viral_state_flags; + + if (cond.mode != Addressing_Constant || !is_type_boolean(cond.type)) { + error(te->cond, "Expected a constant boolean condition in ternary when expression"); + return kind; + } + + if (cond.value.value_bool) { + check_expr_or_type(c, o, te->x, type_hint); + node->viral_state_flags |= te->x->viral_state_flags; + } else { + if (te->y != nullptr) { + check_expr_or_type(c, o, te->y, type_hint); + node->viral_state_flags |= te->y->viral_state_flags; + } else { + error(node, "A ternary when expression must have an else clause"); + return kind; + } + } + return kind; +} + +ExprKind check_or_else_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { + ast_node(oe, OrElseExpr, node); + + String name = oe->token.string; + Ast *arg = oe->x; + Ast *default_value = oe->y; + + Operand x = {}; + Operand y = {}; + check_multi_expr_with_type_hint(c, &x, arg, type_hint); + if (x.mode == Addressing_Invalid) { + o->mode = Addressing_Value; + o->type = t_invalid; + o->expr = node; + return Expr_Expr; + } + + check_multi_expr_with_type_hint(c, &y, default_value, x.type); + error_operand_no_value(&y); + if (y.mode == Addressing_Invalid) { + o->mode = Addressing_Value; + o->type = t_invalid; + o->expr = node; + return Expr_Expr; + } + + Type *left_type = nullptr; + Type *right_type = nullptr; + check_or_else_split_types(c, &x, name, &left_type, &right_type); + add_type_and_value(&c->checker->info, arg, x.mode, x.type, x.value); + + if (left_type != nullptr) { + check_assignment(c, &y, left_type, name); + } else { + check_or_else_expr_no_value_error(c, name, x, type_hint); + } + + if (left_type == nullptr) { + left_type = t_invalid; + } + o->mode = Addressing_Value; + o->type = left_type; + o->expr = node; + return Expr_Expr; +} + +ExprKind check_or_return_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { + ast_node(re, OrReturnExpr, node); + + String name = re->token.string; + Operand x = {}; + check_multi_expr_with_type_hint(c, &x, re->expr, type_hint); + if (x.mode == Addressing_Invalid) { + o->mode = Addressing_Value; + o->type = t_invalid; + o->expr = node; + return Expr_Expr; + } + + Type *left_type = nullptr; + Type *right_type = nullptr; + check_or_return_split_types(c, &x, name, &left_type, &right_type); + add_type_and_value(&c->checker->info, re->expr, x.mode, x.type, x.value); + + if (right_type == nullptr) { + check_or_else_expr_no_value_error(c, name, x, type_hint); + } else { + Type *proc_type = base_type(c->curr_proc_sig); + GB_ASSERT(proc_type->kind == Type_Proc); + Type *result_type = proc_type->Proc.results; + if (result_type == nullptr) { + error(node, "'%.*s' requires the current procedure to have at least one return value", LIT(name)); + } else { + GB_ASSERT(result_type->kind == Type_Tuple); + + auto const &vars = result_type->Tuple.variables; + Type *end_type = vars[vars.count-1]->type; + + if (vars.count > 1) { + if (!proc_type->Proc.has_named_results) { + error(node, "'%.*s' within a procedure with more than 1 return value requires that the return values are named, allowing for early return", LIT(name)); + } + } + + Operand rhs = {}; + rhs.type = right_type; + rhs.mode = Addressing_Value; + + // TODO(bill): better error message + if (!check_is_assignable_to(c, &rhs, end_type)) { + gbString a = type_to_string(right_type); + gbString b = type_to_string(end_type); + gbString ret_type = type_to_string(result_type); + error(node, "Cannot assign end value of type '%s' to '%s' in '%.*s'", a, b, LIT(name)); + if (vars.count == 1) { + error_line("\tProcedure return value type: %s\n", ret_type); + } else { + error_line("\tProcedure return value types: (%s)\n", ret_type); + } + gb_string_free(ret_type); + gb_string_free(b); + gb_string_free(a); + } + } + } + + o->expr = node; + o->type = left_type; + if (left_type != nullptr) { + o->mode = Addressing_Value; + } else { + o->mode = Addressing_NoValue; + } + + if (c->curr_proc_sig == nullptr) { + error(node, "'%.*s' can only be used within a procedure", LIT(name)); + } + + if (c->in_defer) { + error(node, "'or_return' cannot be used within a defer statement"); + } + + return Expr_Expr; +} + +ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { + ExprKind kind = Expr_Expr; + ast_node(cl, CompoundLit, node); + + Type *type = type_hint; + if (type != nullptr && is_type_untyped(type)) { + type = nullptr; + } + bool is_to_be_determined_array_count = false; + bool is_constant = true; + if (cl->type != nullptr) { + type = nullptr; + + // [?]Type + if (cl->type->kind == Ast_ArrayType && cl->type->ArrayType.count != nullptr) { + Ast *count = cl->type->ArrayType.count; + if (count->kind == Ast_UnaryExpr && + count->UnaryExpr.op.kind == Token_Question) { + type = alloc_type_array(check_type(c, cl->type->ArrayType.elem), -1); + is_to_be_determined_array_count = true; + } + if (cl->elems.count > 0) { + if (cl->type->ArrayType.tag != nullptr) { + Ast *tag = cl->type->ArrayType.tag; + GB_ASSERT(tag->kind == Ast_BasicDirective); + String name = tag->BasicDirective.name.string; + if (name == "soa") { + error(node, "#soa arrays are not supported for compound literals"); + return kind; + } + } + } + } + if (cl->type->kind == Ast_DynamicArrayType && cl->type->DynamicArrayType.tag != nullptr) { + if (cl->elems.count > 0) { + Ast *tag = cl->type->DynamicArrayType.tag; + GB_ASSERT(tag->kind == Ast_BasicDirective); + String name = tag->BasicDirective.name.string; + if (name == "soa") { + error(node, "#soa arrays are not supported for compound literals"); + return kind; + } + } + } + + if (type == nullptr) { + type = check_type(c, cl->type); + } + } + + if (type == nullptr) { + error(node, "Missing type in compound literal"); + return kind; + } + + + Type *t = base_type(type); + if (is_type_polymorphic(t)) { + gbString str = type_to_string(type); + error(node, "Cannot use a polymorphic type for a compound literal, got '%s'", str); + o->expr = node; + o->type = type; + gb_string_free(str); + return kind; + } + + + switch (t->kind) { + case Type_Struct: { + if (cl->elems.count == 0) { + break; // NOTE(bill): No need to init + } + if (t->Struct.is_raw_union) { + if (cl->elems.count > 0) { + // NOTE: unions cannot be constant + is_constant = false; + + if (cl->elems[0]->kind != Ast_FieldValue) { + gbString type_str = type_to_string(type); + error(node, "%s ('struct #raw_union') compound literals are only allowed to contain 'field = value' elements", type_str); + gb_string_free(type_str); + } else { + if (cl->elems.count != 1) { + gbString type_str = type_to_string(type); + error(node, "%s ('struct #raw_union') compound literals are only allowed to contain up to 1 'field = value' element, got %td", type_str, cl->elems.count); + gb_string_free(type_str); + } else { + Ast *elem = cl->elems[0]; + ast_node(fv, FieldValue, elem); + if (fv->field->kind != Ast_Ident) { + gbString expr_str = expr_to_string(fv->field); + error(elem, "Invalid field name '%s' in structure literal", expr_str); + gb_string_free(expr_str); + break; + } + + String name = fv->field->Ident.token.string; + + Selection sel = lookup_field(type, name, o->mode == Addressing_Type); + bool is_unknown = sel.entity == nullptr; + if (is_unknown) { + error(elem, "Unknown field '%.*s' in structure literal", LIT(name)); + break; + } + + if (sel.index.count > 1) { + error(elem, "Cannot assign to an anonymous field '%.*s' in a structure literal (at the moment)", LIT(name)); + break; + } + + Entity *field = t->Struct.fields[sel.index[0]]; + add_entity_use(c, fv->field, field); + + Operand o = {}; + check_expr_or_type(c, &o, fv->value, field->type); + + + check_assignment(c, &o, field->type, str_lit("structure literal")); + } + + } + } + break; + } + + + isize field_count = t->Struct.fields.count; + isize min_field_count = t->Struct.fields.count; + for (isize i = min_field_count-1; i >= 0; i--) { + Entity *e = t->Struct.fields[i]; + GB_ASSERT(e->kind == Entity_Variable); + if (e->Variable.param_value.kind != ParameterValue_Invalid) { + min_field_count--; + } else { + break; + } + } + + if (cl->elems[0]->kind == Ast_FieldValue) { + bool *fields_visited = gb_alloc_array(temporary_allocator(), bool, field_count); + + for_array(i, cl->elems) { + Ast *elem = cl->elems[i]; + if (elem->kind != Ast_FieldValue) { + error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed"); + continue; + } + ast_node(fv, FieldValue, elem); + if (fv->field->kind != Ast_Ident) { + gbString expr_str = expr_to_string(fv->field); + error(elem, "Invalid field name '%s' in structure literal", expr_str); + gb_string_free(expr_str); + continue; + } + String name = fv->field->Ident.token.string; + + Selection sel = lookup_field(type, name, o->mode == Addressing_Type); + bool is_unknown = sel.entity == nullptr; + if (is_unknown) { + error(elem, "Unknown field '%.*s' in structure literal", LIT(name)); + continue; + } + + if (sel.index.count > 1) { + error(elem, "Cannot assign to an anonymous field '%.*s' in a structure literal (at the moment)", LIT(name)); + continue; + } + + Entity *field = t->Struct.fields[sel.index[0]]; + add_entity_use(c, fv->field, field); + + if (fields_visited[sel.index[0]]) { + error(elem, "Duplicate field '%.*s' in structure literal", LIT(name)); + continue; + } + + fields_visited[sel.index[0]] = true; + + Operand o = {}; + check_expr_or_type(c, &o, fv->value, field->type); + + if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) { + is_constant = false; + } + if (is_constant) { + is_constant = check_is_operand_compound_lit_constant(c, &o); + } + + check_assignment(c, &o, field->type, str_lit("structure literal")); + } + } else { + bool seen_field_value = false; + + for_array(index, cl->elems) { + Entity *field = nullptr; + Ast *elem = cl->elems[index]; + if (elem->kind == Ast_FieldValue) { + seen_field_value = true; + error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed"); + continue; + } else if (seen_field_value) { + error(elem, "Value elements cannot be used after a 'field = value'"); + continue; + } + if (index >= field_count) { + error(elem, "Too many values in structure literal, expected %td, got %td", field_count, cl->elems.count); + break; + } + + if (field == nullptr) { + field = t->Struct.fields[index]; + } + + Operand o = {}; + check_expr_or_type(c, &o, elem, field->type); + + if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) { + is_constant = false; + } + if (is_constant) { + is_constant = check_is_operand_compound_lit_constant(c, &o); + } + + check_assignment(c, &o, field->type, str_lit("structure literal")); + } + if (cl->elems.count < field_count) { + if (min_field_count < field_count) { + if (cl->elems.count < min_field_count) { + error(cl->close, "Too few values in structure literal, expected at least %td, got %td", min_field_count, cl->elems.count); + } + } else { + error(cl->close, "Too few values in structure literal, expected %td, got %td", field_count, cl->elems.count); + } + } + } + + break; + } + + case Type_Slice: + case Type_Array: + case Type_DynamicArray: + case Type_SimdVector: + case Type_Matrix: + { + Type *elem_type = nullptr; + String context_name = {}; + i64 max_type_count = -1; + if (t->kind == Type_Slice) { + elem_type = t->Slice.elem; + context_name = str_lit("slice literal"); + } else if (t->kind == Type_Array) { + elem_type = t->Array.elem; + context_name = str_lit("array literal"); + if (!is_to_be_determined_array_count) { + max_type_count = t->Array.count; + } + } else if (t->kind == Type_DynamicArray) { + elem_type = t->DynamicArray.elem; + context_name = str_lit("dynamic array literal"); + is_constant = false; + + if (!build_context.no_dynamic_literals) { + add_package_dependency(c, "runtime", "__dynamic_array_reserve"); + add_package_dependency(c, "runtime", "__dynamic_array_append"); + } + } else if (t->kind == Type_SimdVector) { + elem_type = t->SimdVector.elem; + context_name = str_lit("simd vector literal"); + max_type_count = t->SimdVector.count; + } else if (t->kind == Type_Matrix) { + elem_type = t->Matrix.elem; + context_name = str_lit("matrix literal"); + max_type_count = t->Matrix.row_count*t->Matrix.column_count; + } else { + GB_PANIC("unreachable"); + } + + + i64 max = 0; + + Type *bet = base_type(elem_type); + if (!elem_type_can_be_constant(bet)) { + is_constant = false; + } + + if (bet == t_invalid) { + break; + } + + if (cl->elems.count > 0 && cl->elems[0]->kind == Ast_FieldValue) { + RangeCache rc = range_cache_make(heap_allocator()); + defer (range_cache_destroy(&rc)); + + for_array(i, cl->elems) { + Ast *elem = cl->elems[i]; + if (elem->kind != Ast_FieldValue) { + error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed"); + continue; + } + ast_node(fv, FieldValue, elem); + + if (is_ast_range(fv->field)) { + Token op = fv->field->BinaryExpr.op; + + Operand x = {}; + Operand y = {}; + bool ok = check_range(c, fv->field, &x, &y, nullptr); + if (!ok) { + continue; + } + if (x.mode != Addressing_Constant || !is_type_integer(core_type(x.type))) { + error(x.expr, "Expected a constant integer as an array field"); + continue; + } + + if (y.mode != Addressing_Constant || !is_type_integer(core_type(y.type))) { + error(y.expr, "Expected a constant integer as an array field"); + continue; + } + + i64 lo = exact_value_to_i64(x.value); + i64 hi = exact_value_to_i64(y.value); + i64 max_index = hi; + if (op.kind == Token_RangeHalf) { // ..< (exclusive) + hi -= 1; + } else { // .. (inclusive) + max_index += 1; + } + + bool new_range = range_cache_add_range(&rc, lo, hi); + if (!new_range) { + error(elem, "Overlapping field range index %lld %.*s %lld for %.*s", lo, LIT(op.string), hi, LIT(context_name)); + continue; + } + + + if (max_type_count >= 0 && (lo < 0 || lo >= max_type_count)) { + error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", lo, max_type_count, LIT(context_name)); + continue; + } + if (max_type_count >= 0 && (hi < 0 || hi >= max_type_count)) { + error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", hi, max_type_count, LIT(context_name)); + continue; + } + + if (max < hi) { + max = max_index; + } + + Operand operand = {}; + check_expr_with_type_hint(c, &operand, fv->value, elem_type); + check_assignment(c, &operand, elem_type, context_name); + + is_constant = is_constant && operand.mode == Addressing_Constant; + } else { + Operand op_index = {}; + check_expr(c, &op_index, fv->field); + + if (op_index.mode != Addressing_Constant || !is_type_integer(core_type(op_index.type))) { + error(elem, "Expected a constant integer as an array field"); + continue; + } + // add_type_and_value(c->info, op_index.expr, op_index.mode, op_index.type, op_index.value); + + i64 index = exact_value_to_i64(op_index.value); + + if (max_type_count >= 0 && (index < 0 || index >= max_type_count)) { + error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", index, max_type_count, LIT(context_name)); + continue; + } + + bool new_index = range_cache_add_index(&rc, index); + if (!new_index) { + error(elem, "Duplicate field index %lld for %.*s", index, LIT(context_name)); + continue; + } + + if (max < index+1) { + max = index+1; + } + + Operand operand = {}; + check_expr_with_type_hint(c, &operand, fv->value, elem_type); + check_assignment(c, &operand, elem_type, context_name); + + is_constant = is_constant && operand.mode == Addressing_Constant; + } + } + + cl->max_count = max; + } else { + isize index = 0; + for (; index < cl->elems.count; index++) { + Ast *e = cl->elems[index]; + if (e == nullptr) { + error(node, "Invalid literal element"); + continue; + } + + if (e->kind == Ast_FieldValue) { + error(e, "Mixture of 'field = value' and value elements in a literal is not allowed"); + continue; + } + + if (0 <= max_type_count && max_type_count <= index) { + error(e, "Index %lld is out of bounds (>= %lld) for %.*s", index, max_type_count, LIT(context_name)); + } + + Operand operand = {}; + check_expr_with_type_hint(c, &operand, e, elem_type); + check_assignment(c, &operand, elem_type, context_name); + + is_constant = is_constant && operand.mode == Addressing_Constant; + } + + if (max < index) { + max = index; + } + } + + + if (t->kind == Type_Array) { + if (is_to_be_determined_array_count) { + t->Array.count = max; + } else if (cl->elems.count > 0 && cl->elems[0]->kind != Ast_FieldValue) { + if (0 < max && max < t->Array.count) { + error(node, "Expected %lld values for this array literal, got %lld", cast(long long)t->Array.count, cast(long long)max); + } + } + } + + + if (t->kind == Type_SimdVector) { + if (!is_constant) { + // error(node, "Expected all constant elements for a simd vector"); + } + } + + + if (t->kind == Type_DynamicArray) { + if (build_context.no_dynamic_literals && cl->elems.count) { + error(node, "Compound literals of dynamic types have been disabled"); + } + } + + if (t->kind == Type_Matrix) { + if (cl->elems.count > 0 && cl->elems[0]->kind != Ast_FieldValue) { + if (0 < max && max < max_type_count) { + error(node, "Expected %lld values for this matrix literal, got %lld", cast(long long)max_type_count, cast(long long)max); + } + } + } + + break; + } + + case Type_EnumeratedArray: + { + Type *elem_type = t->EnumeratedArray.elem; + Type *index_type = t->EnumeratedArray.index; + String context_name = str_lit("enumerated array literal"); + i64 max_type_count = t->EnumeratedArray.count; + + gbString index_type_str = type_to_string(index_type); + defer (gb_string_free(index_type_str)); + + i64 total_lo = exact_value_to_i64(*t->EnumeratedArray.min_value); + i64 total_hi = exact_value_to_i64(*t->EnumeratedArray.max_value); + + String total_lo_string = {}; + String total_hi_string = {}; + GB_ASSERT(is_type_enum(index_type)); + { + Type *bt = base_type(index_type); + GB_ASSERT(bt->kind == Type_Enum); + for_array(i, bt->Enum.fields) { + Entity *f = bt->Enum.fields[i]; + if (f->kind != Entity_Constant) { + continue; + } + if (total_lo_string.len == 0 && compare_exact_values(Token_CmpEq, f->Constant.value, *t->EnumeratedArray.min_value)) { + total_lo_string = f->token.string; + } + if (total_hi_string.len == 0 && compare_exact_values(Token_CmpEq, f->Constant.value, *t->EnumeratedArray.max_value)) { + total_hi_string = f->token.string; + } + if (total_lo_string.len != 0 && total_hi_string.len != 0) { + break; + } + } + } + + i64 max = 0; + + Type *bet = base_type(elem_type); + if (!elem_type_can_be_constant(bet)) { + is_constant = false; + } + + if (bet == t_invalid) { + break; + } + bool is_partial = cl->tag && (cl->tag->BasicDirective.name.string == "partial"); + + SeenMap seen = {}; // NOTE(bill): Multimap, Key: ExactValue + map_init(&seen, heap_allocator()); + defer (map_destroy(&seen)); + + if (cl->elems.count > 0 && cl->elems[0]->kind == Ast_FieldValue) { + RangeCache rc = range_cache_make(heap_allocator()); + defer (range_cache_destroy(&rc)); + + for_array(i, cl->elems) { + Ast *elem = cl->elems[i]; + if (elem->kind != Ast_FieldValue) { + error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed"); + continue; + } + ast_node(fv, FieldValue, elem); + + if (is_ast_range(fv->field)) { + Token op = fv->field->BinaryExpr.op; + + Operand x = {}; + Operand y = {}; + bool ok = check_range(c, fv->field, &x, &y, nullptr, index_type); + if (!ok) { + continue; + } + if (x.mode != Addressing_Constant || !are_types_identical(x.type, index_type)) { + error(x.expr, "Expected a constant enum of type '%s' as an array field", index_type_str); + continue; + } + + if (y.mode != Addressing_Constant || !are_types_identical(x.type, index_type)) { + error(y.expr, "Expected a constant enum of type '%s' as an array field", index_type_str); + continue; + } + + i64 lo = exact_value_to_i64(x.value); + i64 hi = exact_value_to_i64(y.value); + i64 max_index = hi; + if (op.kind == Token_RangeHalf) { + hi -= 1; + } + + bool new_range = range_cache_add_range(&rc, lo, hi); + if (!new_range) { + gbString lo_str = expr_to_string(x.expr); + gbString hi_str = expr_to_string(y.expr); + error(elem, "Overlapping field range index %s %.*s %s for %.*s", lo_str, LIT(op.string), hi_str, LIT(context_name)); + gb_string_free(hi_str); + gb_string_free(lo_str); + continue; + } + + + // NOTE(bill): These are sanity checks for invalid enum values + if (max_type_count >= 0 && (lo < total_lo || lo > total_hi)) { + gbString lo_str = expr_to_string(x.expr); + error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", lo_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name)); + gb_string_free(lo_str); + continue; + } + if (max_type_count >= 0 && (hi < 0 || hi > total_hi)) { + gbString hi_str = expr_to_string(y.expr); + error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", hi_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name)); + gb_string_free(hi_str); + continue; + } + + if (max < hi) { + max = max_index; + } + + Operand operand = {}; + check_expr_with_type_hint(c, &operand, fv->value, elem_type); + check_assignment(c, &operand, elem_type, context_name); + + is_constant = is_constant && operand.mode == Addressing_Constant; + + TokenKind upper_op = Token_LtEq; + if (op.kind == Token_RangeHalf) { + upper_op = Token_Lt; + } + add_to_seen_map(c, &seen, upper_op, x, x, y); + } else { + Operand op_index = {}; + check_expr_with_type_hint(c, &op_index, fv->field, index_type); + + if (op_index.mode != Addressing_Constant || !are_types_identical(op_index.type, index_type)) { + error(op_index.expr, "Expected a constant enum of type '%s' as an array field", index_type_str); + continue; + } + + i64 index = exact_value_to_i64(op_index.value); + + if (max_type_count >= 0 && (index < total_lo || index > total_hi)) { + gbString idx_str = expr_to_string(op_index.expr); + error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", idx_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name)); + gb_string_free(idx_str); + continue; + } + + bool new_index = range_cache_add_index(&rc, index); + if (!new_index) { + gbString idx_str = expr_to_string(op_index.expr); + error(elem, "Duplicate field index %s for %.*s", idx_str, LIT(context_name)); + gb_string_free(idx_str); + continue; + } + + if (max < index+1) { + max = index+1; + } + + Operand operand = {}; + check_expr_with_type_hint(c, &operand, fv->value, elem_type); + check_assignment(c, &operand, elem_type, context_name); + + is_constant = is_constant && operand.mode == Addressing_Constant; + + add_to_seen_map(c, &seen, op_index); + } + } + + cl->max_count = max; + + } else { + isize index = 0; + for (; index < cl->elems.count; index++) { + Ast *e = cl->elems[index]; + if (e == nullptr) { + error(node, "Invalid literal element"); + continue; + } + + if (e->kind == Ast_FieldValue) { + error(e, "Mixture of 'field = value' and value elements in a literal is not allowed"); + continue; + } + + if (0 <= max_type_count && max_type_count <= index) { + error(e, "Index %lld is out of bounds (>= %lld) for %.*s", index, max_type_count, LIT(context_name)); + } + + Operand operand = {}; + check_expr_with_type_hint(c, &operand, e, elem_type); + check_assignment(c, &operand, elem_type, context_name); + + is_constant = is_constant && operand.mode == Addressing_Constant; + } + + if (max < index) { + max = index; + } + } + + bool was_error = false; + if (cl->elems.count > 0 && cl->elems[0]->kind != Ast_FieldValue) { + if (0 < max && max < t->EnumeratedArray.count) { + error(node, "Expected %lld values for this enumerated array literal, got %lld", cast(long long)t->EnumeratedArray.count, cast(long long)max); + was_error = true; + } else { + error(node, "Enumerated array literals must only have 'field = value' elements, bare elements are not allowed"); + was_error = true; + } + } + + // NOTE(bill): Check for missing cases when `#partial literal` is not present + if (cl->elems.count > 0 && !was_error && !is_partial) { + Type *et = base_type(index_type); + GB_ASSERT(et->kind == Type_Enum); + auto fields = et->Enum.fields; + + auto unhandled = array_make(temporary_allocator(), 0, fields.count); + + for_array(i, fields) { + Entity *f = fields[i]; + if (f->kind != Entity_Constant) { + continue; + } + ExactValue v = f->Constant.value; + auto found = map_get(&seen, hash_exact_value(v)); + if (!found) { + array_add(&unhandled, f); + } + } + + if (unhandled.count > 0) { + begin_error_block(); + defer (end_error_block()); + + if (unhandled.count == 1) { + error_no_newline(node, "Unhandled enumerated array case: %.*s", LIT(unhandled[0]->token.string)); + } else { + error(node, "Unhandled enumerated array cases:"); + for_array(i, unhandled) { + Entity *f = unhandled[i]; + error_line("\t%.*s\n", LIT(f->token.string)); + } + } + error_line("\n"); + + error_line("\tSuggestion: Was '#partial %s{...}' wanted?\n", type_to_string(type)); + } + } + + break; + } + + case Type_Basic: { + if (!is_type_any(t)) { + if (cl->elems.count != 0) { + gbString s = type_to_string(t); + error(node, "Illegal compound literal, %s cannot be used as a compound literal with fields", s); + gb_string_free(s); + is_constant = false; + } + break; + } + if (cl->elems.count == 0) { + break; // NOTE(bill): No need to init + } + { // Checker values + Type *field_types[2] = {t_rawptr, t_typeid}; + isize field_count = 2; + if (cl->elems[0]->kind == Ast_FieldValue) { + bool fields_visited[2] = {}; + + for_array(i, cl->elems) { + Ast *elem = cl->elems[i]; + if (elem->kind != Ast_FieldValue) { + error(elem, "Mixture of 'field = value' and value elements in a 'any' literal is not allowed"); + continue; + } + ast_node(fv, FieldValue, elem); + if (fv->field->kind != Ast_Ident) { + gbString expr_str = expr_to_string(fv->field); + error(elem, "Invalid field name '%s' in 'any' literal", expr_str); + gb_string_free(expr_str); + continue; + } + String name = fv->field->Ident.token.string; + + Selection sel = lookup_field(type, name, o->mode == Addressing_Type); + if (sel.entity == nullptr) { + error(elem, "Unknown field '%.*s' in 'any' literal", LIT(name)); + continue; + } + + isize index = sel.index[0]; + + if (fields_visited[index]) { + error(elem, "Duplicate field '%.*s' in 'any' literal", LIT(name)); + continue; + } + + fields_visited[index] = true; + check_expr(c, o, fv->value); + + // NOTE(bill): 'any' literals can never be constant + is_constant = false; + + check_assignment(c, o, field_types[index], str_lit("'any' literal")); + } + } else { + for_array(index, cl->elems) { + Ast *elem = cl->elems[index]; + if (elem->kind == Ast_FieldValue) { + error(elem, "Mixture of 'field = value' and value elements in a 'any' literal is not allowed"); + continue; + } + + + check_expr(c, o, elem); + if (index >= field_count) { + error(o->expr, "Too many values in 'any' literal, expected %td", field_count); + break; + } + + // NOTE(bill): 'any' literals can never be constant + is_constant = false; + + check_assignment(c, o, field_types[index], str_lit("'any' literal")); + } + if (cl->elems.count < field_count) { + error(cl->close, "Too few values in 'any' literal, expected %td, got %td", field_count, cl->elems.count); + } + } + } + + break; + } + + case Type_Map: { + if (cl->elems.count == 0) { + break; + } + is_constant = false; + { // Checker values + bool key_is_typeid = is_type_typeid(t->Map.key); + bool value_is_typeid = is_type_typeid(t->Map.value); + + for_array(i, cl->elems) { + Ast *elem = cl->elems[i]; + if (elem->kind != Ast_FieldValue) { + error(elem, "Only 'field = value' elements are allowed in a map literal"); + continue; + } + ast_node(fv, FieldValue, elem); + + if (key_is_typeid) { + check_expr_or_type(c, o, fv->field, t->Map.key); + } else { + check_expr_with_type_hint(c, o, fv->field, t->Map.key); + } + check_assignment(c, o, t->Map.key, str_lit("map literal")); + if (o->mode == Addressing_Invalid) { + continue; + } + + if (value_is_typeid) { + check_expr_or_type(c, o, fv->value, t->Map.value); + } else { + check_expr_with_type_hint(c, o, fv->value, t->Map.value); + } + check_assignment(c, o, t->Map.value, str_lit("map literal")); + } + } + + if (build_context.no_dynamic_literals && cl->elems.count) { + error(node, "Compound literals of dynamic types have been disabled"); + } else { + add_package_dependency(c, "runtime", "__dynamic_map_reserve"); + add_package_dependency(c, "runtime", "__dynamic_map_set"); + } + break; + } + + case Type_BitSet: { + if (cl->elems.count == 0) { + break; // NOTE(bill): No need to init + } + Type *et = base_type(t->BitSet.elem); + isize field_count = 0; + if (et->kind == Type_Enum) { + field_count = et->Enum.fields.count; + } + + if (cl->elems[0]->kind == Ast_FieldValue) { + error(cl->elems[0], "'field = value' in a bit_set a literal is not allowed"); + is_constant = false; + } else { + for_array(index, cl->elems) { + Ast *elem = cl->elems[index]; + if (elem->kind == Ast_FieldValue) { + error(elem, "'field = value' in a bit_set a literal is not allowed"); + continue; + } + + check_expr_with_type_hint(c, o, elem, et); + + if (is_constant) { + is_constant = o->mode == Addressing_Constant; + } + + check_assignment(c, o, t->BitSet.elem, str_lit("bit_set literal")); + if (o->mode == Addressing_Constant) { + i64 lower = t->BitSet.lower; + i64 upper = t->BitSet.upper; + i64 v = exact_value_to_i64(o->value); + if (lower <= v && v <= upper) { + // okay + } else { + error(elem, "Bit field value out of bounds, %lld not in the range %lld .. %lld", v, lower, upper); + continue; + } + } + } + } + break; + } + + default: { + if (cl->elems.count == 0) { + break; // NOTE(bill): No need to init + } + + gbString str = type_to_string(type); + error(node, "Invalid compound literal type '%s'", str); + gb_string_free(str); + return kind; + } + } + + if (is_constant) { + o->mode = Addressing_Constant; + + if (is_type_bit_set(type)) { + // NOTE(bill): Encode as an integer + + Type *bt = base_type(type); + BigInt bits = {}; + BigInt one = {}; + big_int_from_u64(&one, 1); + + for_array(i, cl->elems) { + Ast *e = cl->elems[i]; + GB_ASSERT(e->kind != Ast_FieldValue); + + TypeAndValue tav = e->tav; + if (tav.mode != Addressing_Constant) { + continue; + } + GB_ASSERT(tav.value.kind == ExactValue_Integer); + i64 v = big_int_to_i64(&tav.value.value_integer); + i64 lower = bt->BitSet.lower; + u64 index = cast(u64)(v-lower); + BigInt bit = {}; + big_int_from_u64(&bit, index); + big_int_shl(&bit, &one, &bit); + big_int_or(&bits, &bits, &bit); + } + o->value.kind = ExactValue_Integer; + o->value.value_integer = bits; + } else if (is_type_constant_type(type) && cl->elems.count == 0) { + ExactValue value = exact_value_compound(node); + Type *bt = core_type(type); + if (bt->kind == Type_Basic) { + if (bt->Basic.flags & BasicFlag_Boolean) { + value = exact_value_bool(false); + } else if (bt->Basic.flags & BasicFlag_Integer) { + value = exact_value_i64(0); + } else if (bt->Basic.flags & BasicFlag_Unsigned) { + value = exact_value_i64(0); + } else if (bt->Basic.flags & BasicFlag_Float) { + value = exact_value_float(0); + } else if (bt->Basic.flags & BasicFlag_Complex) { + value = exact_value_complex(0, 0); + } else if (bt->Basic.flags & BasicFlag_Quaternion) { + value = exact_value_quaternion(0, 0, 0, 0); + } else if (bt->Basic.flags & BasicFlag_Pointer) { + value = exact_value_pointer(0); + } else if (bt->Basic.flags & BasicFlag_String) { + String empty_string = {}; + value = exact_value_string(empty_string); + } else if (bt->Basic.flags & BasicFlag_Rune) { + value = exact_value_i64(0); + } + } + + o->value = value; + } else { + o->value = exact_value_compound(node); + } + } else { + o->mode = Addressing_Value; + } + o->type = type; + return kind; +} + +ExprKind check_type_assertion(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { + ExprKind kind = Expr_Expr; + ast_node(ta, TypeAssertion, node); + check_expr(c, o, ta->expr); + node->viral_state_flags |= ta->expr->viral_state_flags; + + if (o->mode == Addressing_Invalid) { + o->expr = node; + return kind; + } + if (o->mode == Addressing_Constant) { + gbString expr_str = expr_to_string(o->expr); + error(o->expr, "A type assertion cannot be applied to a constant expression: '%s'", expr_str); + gb_string_free(expr_str); + o->mode = Addressing_Invalid; + o->expr = node; + return kind; + } + + if (is_type_untyped(o->type)) { + gbString expr_str = expr_to_string(o->expr); + error(o->expr, "A type assertion cannot be applied to an untyped expression: '%s'", expr_str); + gb_string_free(expr_str); + o->mode = Addressing_Invalid; + o->expr = node; + return kind; + } + + Type *src = type_deref(o->type); + Type *bsrc = base_type(src); + + + if (ta->type != nullptr && ta->type->kind == Ast_UnaryExpr && ta->type->UnaryExpr.op.kind == Token_Question) { + if (!is_type_union(src)) { + gbString str = type_to_string(o->type); + error(o->expr, "Type assertions with .? can only operate on unions, got %s", str); + gb_string_free(str); + o->mode = Addressing_Invalid; + o->expr = node; + return kind; + } + + if (bsrc->Union.variants.count != 1 && type_hint != nullptr) { + bool allowed = false; + for_array(i, bsrc->Union.variants) { + Type *vt = bsrc->Union.variants[i]; + if (are_types_identical(vt, type_hint)) { + allowed = true; + add_type_info_type(c, vt); + break; + } + } + if (allowed) { + add_type_info_type(c, o->type); + o->type = type_hint; + o->mode = Addressing_OptionalOk; + return kind; + } + } + + if (bsrc->Union.variants.count != 1) { + error(o->expr, "Type assertions with .? can only operate on unions with 1 variant, got %lld", cast(long long)bsrc->Union.variants.count); + o->mode = Addressing_Invalid; + o->expr = node; + return kind; + } + + add_type_info_type(c, o->type); + add_type_info_type(c, bsrc->Union.variants[0]); + + o->type = bsrc->Union.variants[0]; + o->mode = Addressing_OptionalOk; + } else { + Type *t = check_type(c, ta->type); + Type *dst = t; + + if (is_type_union(src)) { + bool ok = false; + for_array(i, bsrc->Union.variants) { + Type *vt = bsrc->Union.variants[i]; + if (are_types_identical(vt, dst)) { + ok = true; + break; + } + } + + if (!ok) { + gbString expr_str = expr_to_string(o->expr); + gbString dst_type_str = type_to_string(t); + defer (gb_string_free(expr_str)); + defer (gb_string_free(dst_type_str)); + if (bsrc->Union.variants.count == 0) { + error(o->expr, "Cannot type assert '%s' to '%s' as this is an empty union", expr_str, dst_type_str); + } else { + error(o->expr, "Cannot type assert '%s' to '%s' as it is not a variant of that union", expr_str, dst_type_str); + } + o->mode = Addressing_Invalid; + o->expr = node; + return kind; + } + + add_type_info_type(c, o->type); + add_type_info_type(c, t); + + o->type = t; + o->mode = Addressing_OptionalOk; + } else if (is_type_any(src)) { + o->type = t; + o->mode = Addressing_OptionalOk; + + add_type_info_type(c, o->type); + add_type_info_type(c, t); + } else { + gbString str = type_to_string(o->type); + error(o->expr, "Type assertions can only operate on unions and 'any', got %s", str); + gb_string_free(str); + o->mode = Addressing_Invalid; + o->expr = node; + return kind; + } + } + + if ((c->state_flags & StateFlag_no_type_assert) == 0) { + add_package_dependency(c, "runtime", "type_assertion_check"); + add_package_dependency(c, "runtime", "type_assertion_check2"); + } + return kind; +} + +ExprKind check_selector_call_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { + ast_node(se, SelectorCallExpr, node); + // IMPORTANT NOTE(bill, 2020-05-22): This is a complete hack to get a shorthand which is extremely useful for vtables + // COM APIs is a great example of where this kind of thing is extremely useful + // General idea: + // + // x->y(123) == x.y(x, 123) + // + // How this has been implemented at the moment is quite hacky but it's done so to reduce need for huge backend changes + // Just regenerating a new AST aids things + // + // TODO(bill): Is this a good hack or not? + // + // NOTE(bill, 2020-05-22): I'm going to regret this decision, ain't I? + + + if (se->modified_call) { + // Prevent double evaluation + o->expr = node; + o->type = node->tav.type; + o->value = node->tav.value; + o->mode = node->tav.mode; + return Expr_Expr; + } + + bool allow_arrow_right_selector_expr; + allow_arrow_right_selector_expr = c->allow_arrow_right_selector_expr; + c->allow_arrow_right_selector_expr = true; + Operand x = {}; + ExprKind kind = check_expr_base(c, &x, se->expr, nullptr); + c->allow_arrow_right_selector_expr = allow_arrow_right_selector_expr; + + if (x.mode == Addressing_Invalid || x.type == t_invalid) { + o->mode = Addressing_Invalid; + o->type = t_invalid; + o->expr = node; + return kind; + } + if (!is_type_proc(x.type)) { + gbString type_str = type_to_string(x.type); + error(se->call, "Selector call expressions expect a procedure type for the call, got '%s'", type_str); + gb_string_free(type_str); + + o->mode = Addressing_Invalid; + o->type = t_invalid; + o->expr = node; + return Expr_Stmt; + } + + ast_node(ce, CallExpr, se->call); + + GB_ASSERT(x.expr->kind == Ast_SelectorExpr); + + Ast *first_arg = x.expr->SelectorExpr.expr; + GB_ASSERT(first_arg != nullptr); + + first_arg->state_flags |= StateFlag_SelectorCallExpr; + + Type *pt = base_type(x.type); + GB_ASSERT(pt->kind == Type_Proc); + Type *first_type = nullptr; + String first_arg_name = {}; + if (pt->Proc.param_count > 0) { + Entity *f = pt->Proc.params->Tuple.variables[0]; + first_type = f->type; + first_arg_name = f->token.string; + } + if (first_arg_name.len == 0) { + first_arg_name = str_lit("_"); + } + + if (first_type == nullptr) { + error(se->call, "Selector call expressions expect a procedure type for the call with at least 1 parameter"); + o->mode = Addressing_Invalid; + o->type = t_invalid; + o->expr = node; + return Expr_Stmt; + } + + Operand y = {}; + y.mode = first_arg->tav.mode; + y.type = first_arg->tav.type; + y.value = first_arg->tav.value; + + if (check_is_assignable_to(c, &y, first_type)) { + // Do nothing, it's valid + } else { + Operand z = y; + z.type = type_deref(y.type); + if (check_is_assignable_to(c, &z, first_type)) { + // NOTE(bill): AST GENERATION HACK! + Token op = {Token_Pointer}; + first_arg = ast_deref_expr(first_arg->file(), first_arg, op); + } else if (y.mode == Addressing_Variable) { + Operand w = y; + w.type = alloc_type_pointer(y.type); + if (check_is_assignable_to(c, &w, first_type)) { + // NOTE(bill): AST GENERATION HACK! + Token op = {Token_And}; + first_arg = ast_unary_expr(first_arg->file(), op, first_arg); + } + } + } + + if (ce->args.count > 0) { + bool fail = false; + bool first_is_field_value = (ce->args[0]->kind == Ast_FieldValue); + for_array(i, ce->args) { + Ast *arg = ce->args[i]; + bool mix = false; + if (first_is_field_value) { + mix = arg->kind != Ast_FieldValue; + } else { + mix = arg->kind == Ast_FieldValue; + } + if (mix) { + fail = true; + break; + } + } + if (!fail && first_is_field_value) { + Token op = {Token_Eq}; + AstFile *f = first_arg->file(); + first_arg = ast_field_value(f, ast_ident(f, make_token_ident(first_arg_name)), first_arg, op); + } + } + + + + auto modified_args = slice_make(heap_allocator(), ce->args.count+1); + modified_args[0] = first_arg; + slice_copy(&modified_args, ce->args, 1); + ce->args = modified_args; + se->modified_call = true; + + allow_arrow_right_selector_expr = c->allow_arrow_right_selector_expr; + c->allow_arrow_right_selector_expr = true; + check_expr_base(c, o, se->call, type_hint); + c->allow_arrow_right_selector_expr = allow_arrow_right_selector_expr; + + o->expr = node; + return Expr_Expr; +} + + +ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { + ExprKind kind = Expr_Expr; + ast_node(ie, IndexExpr, node); + check_expr(c, o, ie->expr); + node->viral_state_flags |= ie->expr->viral_state_flags; + if (o->mode == Addressing_Invalid) { + o->expr = node; + return kind; + } + + Type *t = base_type(type_deref(o->type)); + bool is_ptr = is_type_pointer(o->type); + bool is_const = o->mode == Addressing_Constant; + + if (is_type_map(t)) { + Operand key = {}; + if (is_type_typeid(t->Map.key)) { + check_expr_or_type(c, &key, ie->index, t->Map.key); + } else { + check_expr_with_type_hint(c, &key, ie->index, t->Map.key); + } + check_assignment(c, &key, t->Map.key, str_lit("map index")); + if (key.mode == Addressing_Invalid) { + o->mode = Addressing_Invalid; + o->expr = node; + return kind; + } + o->mode = Addressing_MapIndex; + o->type = t->Map.value; + o->expr = node; + + add_package_dependency(c, "runtime", "__dynamic_map_get"); + add_package_dependency(c, "runtime", "__dynamic_map_set"); + return Expr_Expr; + } + + i64 max_count = -1; + bool valid = check_set_index_data(o, t, is_ptr, &max_count, o->type); + + if (is_const) { + if (is_type_array(t)) { + // OKay + } else if (is_type_slice(t)) { + // Okay + } else if (is_type_enumerated_array(t)) { + // Okay + } else if (is_type_string(t)) { + // Okay + } else if (is_type_relative_slice(t)) { + // Okay + } else if (is_type_matrix(t)) { + // Okay + } else { + valid = false; + } + } + + if (!valid) { + gbString str = expr_to_string(o->expr); + gbString type_str = type_to_string(o->type); + defer (gb_string_free(str)); + defer (gb_string_free(type_str)); + if (is_const) { + error(o->expr, "Cannot index constant '%s' of type '%s'", str, type_str); + } else { + error(o->expr, "Cannot index '%s' of type '%s'", str, type_str); + } + o->mode = Addressing_Invalid; + o->expr = node; + return kind; + } + + if (ie->index == nullptr) { + gbString str = expr_to_string(o->expr); + error(o->expr, "Missing index for '%s'", str); + gb_string_free(str); + o->mode = Addressing_Invalid; + o->expr = node; + return kind; + } + + Type *index_type_hint = nullptr; + if (is_type_enumerated_array(t)) { + Type *bt = base_type(t); + GB_ASSERT(bt->kind == Type_EnumeratedArray); + index_type_hint = bt->EnumeratedArray.index; + } + + i64 index = 0; + bool ok = check_index_value(c, t, false, ie->index, max_count, &index, index_type_hint); + if (is_const) { + if (index < 0) { + gbString str = expr_to_string(o->expr); + error(o->expr, "Cannot index a constant '%s'", str); + error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n"); + gb_string_free(str); + o->mode = Addressing_Invalid; + o->expr = node; + return kind; + } else if (ok) { + ExactValue value = type_and_value_of_expr(ie->expr).value; + o->mode = Addressing_Constant; + bool success = false; + bool finish = false; + o->value = get_constant_field_single(c, value, cast(i32)index, &success, &finish); + if (!success) { + gbString str = expr_to_string(o->expr); + error(o->expr, "Cannot index a constant '%s' with index %lld", str, cast(long long)index); + error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n"); + gb_string_free(str); + o->mode = Addressing_Invalid; + o->expr = node; + return kind; + } + } + } + + if (type_hint != nullptr && is_type_matrix(t)) { + // TODO(bill): allow matrix columns to be assignable to other types which are the same internally + // if a type hint exists + } + return kind; +} + +ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { + ExprKind kind = Expr_Stmt; + ast_node(se, SliceExpr, node); + check_expr(c, o, se->expr); + node->viral_state_flags |= se->expr->viral_state_flags; + + if (o->mode == Addressing_Invalid) { + o->mode = Addressing_Invalid; + o->expr = node; + return kind; + } + + bool valid = false; + i64 max_count = -1; + Type *t = base_type(type_deref(o->type)); + switch (t->kind) { + case Type_Basic: + if (t->Basic.kind == Basic_string || t->Basic.kind == Basic_UntypedString) { + valid = true; + if (o->mode == Addressing_Constant) { + max_count = o->value.value_string.len; + } + o->type = type_deref(o->type); + } + break; + + case Type_Array: + valid = true; + max_count = t->Array.count; + if (o->mode != Addressing_Variable && !is_type_pointer(o->type)) { + gbString str = expr_to_string(node); + error(node, "Cannot slice array '%s', value is not addressable", str); + gb_string_free(str); + o->mode = Addressing_Invalid; + o->expr = node; + return kind; + } + o->type = alloc_type_slice(t->Array.elem); + break; + + case Type_MultiPointer: + valid = true; + o->type = type_deref(o->type); + break; + + case Type_Slice: + valid = true; + o->type = type_deref(o->type); + break; + + case Type_DynamicArray: + valid = true; + o->type = alloc_type_slice(t->DynamicArray.elem); + break; + + case Type_Struct: + if (is_type_soa_struct(t)) { + valid = true; + o->type = make_soa_struct_slice(c, nullptr, nullptr, t->Struct.soa_elem); + } + break; + + case Type_RelativeSlice: + valid = true; + o->type = t->RelativeSlice.slice_type; + if (o->mode != Addressing_Variable) { + gbString str = expr_to_string(node); + error(node, "Cannot relative slice '%s', value is not addressable", str); + gb_string_free(str); + o->mode = Addressing_Invalid; + o->expr = node; + return kind; + } + break; + } + + if (!valid) { + gbString str = expr_to_string(o->expr); + gbString type_str = type_to_string(o->type); + error(o->expr, "Cannot slice '%s' of type '%s'", str, type_str); + gb_string_free(type_str); + gb_string_free(str); + o->mode = Addressing_Invalid; + o->expr = node; + return kind; + } + + if (se->low == nullptr && se->high != nullptr) { + // It is okay to continue as it will assume the 1st index is zero + } + + i64 indices[2] = {}; + Ast *nodes[2] = {se->low, se->high}; + for (isize i = 0; i < gb_count_of(nodes); i++) { + i64 index = max_count; + if (nodes[i] != nullptr) { + i64 capacity = -1; + if (max_count >= 0) { + capacity = max_count; + } + i64 j = 0; + if (check_index_value(c, t, true, nodes[i], capacity, &j)) { + index = j; + } + + node->viral_state_flags |= nodes[i]->viral_state_flags; + } else if (i == 0) { + index = 0; + } + indices[i] = index; + } + + for (isize i = 0; i < gb_count_of(indices); i++) { + i64 a = indices[i]; + for (isize j = i+1; j < gb_count_of(indices); j++) { + i64 b = indices[j]; + if (a > b && b >= 0) { + error(se->close, "Invalid slice indices: [%td > %td]", a, b); + } + } + } + + if (max_count < 0) { + if (o->mode == Addressing_Constant) { + gbString s = expr_to_string(se->expr); + error(se->expr, "Cannot slice constant value '%s'", s); + gb_string_free(s); + } + } + + if (t->kind == Type_MultiPointer && se->high != nullptr) { + /* + x[:] -> [^]T + x[i:] -> [^]T + x[:n] -> []T + x[i:n] -> []T + */ + o->type = alloc_type_slice(t->MultiPointer.elem); + } + + o->mode = Addressing_Value; + + if (is_type_string(t) && max_count >= 0) { + bool all_constant = true; + for (isize i = 0; i < gb_count_of(nodes); i++) { + if (nodes[i] != nullptr) { + TypeAndValue tav = type_and_value_of_expr(nodes[i]); + if (tav.mode != Addressing_Constant) { + all_constant = false; + break; + } + } + } + if (!all_constant) { + gbString str = expr_to_string(o->expr); + error(o->expr, "Cannot slice '%s' with non-constant indices", str); + error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n"); + gb_string_free(str); + o->mode = Addressing_Value; // NOTE(bill): Keep subsequent values going without erring + o->expr = node; + return kind; + } + + String s = {}; + if (o->value.kind == ExactValue_String) { + s = o->value.value_string; + } + + o->mode = Addressing_Constant; + o->type = t; + o->value = exact_value_string(substring(s, cast(isize)indices[0], cast(isize)indices[1])); + } + return kind; +} + ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { u32 prev_state_flags = c->state_flags; defer (c->state_flags = prev_state_flags); @@ -6876,6 +9059,14 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type out &= ~StateFlag_no_bounds_check; } + if (in & StateFlag_no_type_assert) { + out |= StateFlag_no_type_assert; + out &= ~StateFlag_type_assert; + } else if (in & StateFlag_type_assert) { + out |= StateFlag_type_assert; + out &= ~StateFlag_no_type_assert; + } + c->state_flags = out; } @@ -6883,6 +9074,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type o->mode = Addressing_Invalid; o->type = t_invalid; + o->value = {ExactValue_Invalid}; switch (node->kind) { default: @@ -6955,52 +9147,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type case_end; case_ast_node(bd, BasicDirective, node); - o->mode = Addressing_Constant; - String name = bd->name.string; - if (name == "file") { - o->type = t_untyped_string; - o->value = exact_value_string(get_file_path_string(bd->token.pos.file_id)); - } else if (name == "line") { - o->type = t_untyped_integer; - o->value = exact_value_i64(bd->token.pos.line); - } else if (name == "procedure") { - if (c->curr_proc_decl == nullptr) { - error(node, "#procedure may only be used within procedures"); - o->type = t_untyped_string; - o->value = exact_value_string(str_lit("")); - } else { - o->type = t_untyped_string; - o->value = exact_value_string(c->proc_name); - } - } else if (name == "caller_location") { - init_core_source_code_location(c->checker); - error(node, "#caller_location may only be used as a default argument parameter"); - o->type = t_source_code_location; - o->mode = Addressing_Value; - } else { - if (name == "location") { - init_core_source_code_location(c->checker); - error(node, "'#%.*s' must be used in a call expression", LIT(name)); - o->type = t_source_code_location; - o->mode = Addressing_Value; - } else if ( - name == "assert" || - name == "defined" || - name == "config" || - name == "load" || - name == "load_hash" || - name == "load_or" - ) { - error(node, "'#%.*s' must be used as a call", LIT(name)); - o->type = t_invalid; - o->mode = Addressing_Invalid; - } else { - error(node, "Unknown directive: #%.*s", LIT(name)); - o->type = t_invalid; - o->mode = Addressing_Invalid; - } - - } + kind = check_basic_directive_expr(c, o, node, type_hint); case_end; case_ast_node(pg, ProcGroup, node); @@ -7049,1110 +9196,23 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type case_end; case_ast_node(te, TernaryIfExpr, node); - Operand cond = {Addressing_Invalid}; - check_expr(c, &cond, te->cond); - node->viral_state_flags |= te->cond->viral_state_flags; - - if (cond.mode != Addressing_Invalid && !is_type_boolean(cond.type)) { - error(te->cond, "Non-boolean condition in ternary if expression"); - } - - Operand x = {Addressing_Invalid}; - Operand y = {Addressing_Invalid}; - check_expr_or_type(c, &x, te->x, type_hint); - node->viral_state_flags |= te->x->viral_state_flags; - - if (te->y != nullptr) { - check_expr_or_type(c, &y, te->y, type_hint); - node->viral_state_flags |= te->y->viral_state_flags; - } else { - error(node, "A ternary expression must have an else clause"); - return kind; - } - - if (x.type == nullptr || x.type == t_invalid || - y.type == nullptr || y.type == t_invalid) { - return kind; - } - - convert_to_typed(c, &x, y.type); - if (x.mode == Addressing_Invalid) { - return kind; - } - convert_to_typed(c, &y, x.type); - if (y.mode == Addressing_Invalid) { - x.mode = Addressing_Invalid; - return kind; - } - - if (!ternary_compare_types(x.type, y.type)) { - gbString its = type_to_string(x.type); - gbString ets = type_to_string(y.type); - error(node, "Mismatched types in ternary if expression, %s vs %s", its, ets); - gb_string_free(ets); - gb_string_free(its); - return kind; - } - - o->type = x.type; - if (is_type_untyped_nil(o->type) || is_type_untyped_undef(o->type)) { - o->type = y.type; - } - - o->mode = Addressing_Value; - o->expr = node; - if (type_hint != nullptr && is_type_untyped(o->type)) { - if (check_cast_internal(c, &x, type_hint) && - check_cast_internal(c, &y, type_hint)) { - convert_to_typed(c, o, type_hint); - update_untyped_expr_type(c, node, type_hint, !is_type_untyped(type_hint)); - } - } + kind = check_ternary_if_expr(c, o, node, type_hint); case_end; case_ast_node(te, TernaryWhenExpr, node); - Operand cond = {}; - check_expr(c, &cond, te->cond); - node->viral_state_flags |= te->cond->viral_state_flags; - - if (cond.mode != Addressing_Constant || !is_type_boolean(cond.type)) { - error(te->cond, "Expected a constant boolean condition in ternary when expression"); - return kind; - } - - if (cond.value.value_bool) { - check_expr_or_type(c, o, te->x, type_hint); - node->viral_state_flags |= te->x->viral_state_flags; - } else { - if (te->y != nullptr) { - check_expr_or_type(c, o, te->y, type_hint); - node->viral_state_flags |= te->y->viral_state_flags; - } else { - error(node, "A ternary when expression must have an else clause"); - return kind; - } - } + kind = check_ternary_when_expr(c, o, node, type_hint); case_end; case_ast_node(oe, OrElseExpr, node); - String name = oe->token.string; - Ast *arg = oe->x; - Ast *default_value = oe->y; - - Operand x = {}; - Operand y = {}; - check_multi_expr_with_type_hint(c, &x, arg, type_hint); - if (x.mode == Addressing_Invalid) { - o->mode = Addressing_Value; - o->type = t_invalid; - o->expr = node; - return Expr_Expr; - } - - check_multi_expr_with_type_hint(c, &y, default_value, x.type); - error_operand_no_value(&y); - if (y.mode == Addressing_Invalid) { - o->mode = Addressing_Value; - o->type = t_invalid; - o->expr = node; - return Expr_Expr; - } - - Type *left_type = nullptr; - Type *right_type = nullptr; - check_or_else_split_types(c, &x, name, &left_type, &right_type); - add_type_and_value(&c->checker->info, arg, x.mode, x.type, x.value); - - if (left_type != nullptr) { - check_assignment(c, &y, left_type, name); - } else { - check_or_else_expr_no_value_error(c, name, x, type_hint); - } - - if (left_type == nullptr) { - left_type = t_invalid; - } - o->mode = Addressing_Value; - o->type = left_type; - o->expr = node; - return Expr_Expr; + return check_or_else_expr(c, o, node, type_hint); case_end; case_ast_node(re, OrReturnExpr, node); - String name = re->token.string; - Operand x = {}; - check_multi_expr_with_type_hint(c, &x, re->expr, type_hint); - if (x.mode == Addressing_Invalid) { - o->mode = Addressing_Value; - o->type = t_invalid; - o->expr = node; - return Expr_Expr; - } - - Type *left_type = nullptr; - Type *right_type = nullptr; - check_or_return_split_types(c, &x, name, &left_type, &right_type); - add_type_and_value(&c->checker->info, re->expr, x.mode, x.type, x.value); - - if (right_type == nullptr) { - check_or_else_expr_no_value_error(c, name, x, type_hint); - } else { - Type *proc_type = base_type(c->curr_proc_sig); - GB_ASSERT(proc_type->kind == Type_Proc); - Type *result_type = proc_type->Proc.results; - if (result_type == nullptr) { - error(node, "'%.*s' requires the current procedure to have at least one return value", LIT(name)); - } else { - GB_ASSERT(result_type->kind == Type_Tuple); - - auto const &vars = result_type->Tuple.variables; - Type *end_type = vars[vars.count-1]->type; - - if (vars.count > 1) { - if (!proc_type->Proc.has_named_results) { - error(node, "'%.*s' within a procedure with more than 1 return value requires that the return values are named, allowing for early return", LIT(name)); - } - } - - Operand rhs = {}; - rhs.type = right_type; - rhs.mode = Addressing_Value; - - // TODO(bill): better error message - if (!check_is_assignable_to(c, &rhs, end_type)) { - gbString a = type_to_string(right_type); - gbString b = type_to_string(end_type); - gbString ret_type = type_to_string(result_type); - error(node, "Cannot assign end value of type '%s' to '%s' in '%.*s'", a, b, LIT(name)); - if (vars.count == 1) { - error_line("\tProcedure return value type: %s\n", ret_type); - } else { - error_line("\tProcedure return value types: (%s)\n", ret_type); - } - gb_string_free(ret_type); - gb_string_free(b); - gb_string_free(a); - } - } - } - - o->expr = node; - o->type = left_type; - if (left_type != nullptr) { - o->mode = Addressing_Value; - } else { - o->mode = Addressing_NoValue; - } - - if (c->curr_proc_sig == nullptr) { - error(node, "'%.*s' can only be used within a procedure", LIT(name)); - } - - if (c->in_defer) { - error(node, "'or_return' cannot be used within a defer statement"); - } - - return Expr_Expr; + return check_or_return_expr(c, o, node, type_hint); case_end; case_ast_node(cl, CompoundLit, node); - Type *type = type_hint; - if (type != nullptr && is_type_untyped(type)) { - type = nullptr; - } - bool is_to_be_determined_array_count = false; - bool is_constant = true; - if (cl->type != nullptr) { - type = nullptr; - - // [?]Type - if (cl->type->kind == Ast_ArrayType && cl->type->ArrayType.count != nullptr) { - Ast *count = cl->type->ArrayType.count; - if (count->kind == Ast_UnaryExpr && - count->UnaryExpr.op.kind == Token_Question) { - type = alloc_type_array(check_type(c, cl->type->ArrayType.elem), -1); - is_to_be_determined_array_count = true; - } - if (cl->elems.count > 0) { - if (cl->type->ArrayType.tag != nullptr) { - Ast *tag = cl->type->ArrayType.tag; - GB_ASSERT(tag->kind == Ast_BasicDirective); - String name = tag->BasicDirective.name.string; - if (name == "soa") { - error(node, "#soa arrays are not supported for compound literals"); - return kind; - } - } - } - } - if (cl->type->kind == Ast_DynamicArrayType && cl->type->DynamicArrayType.tag != nullptr) { - if (cl->elems.count > 0) { - Ast *tag = cl->type->DynamicArrayType.tag; - GB_ASSERT(tag->kind == Ast_BasicDirective); - String name = tag->BasicDirective.name.string; - if (name == "soa") { - error(node, "#soa arrays are not supported for compound literals"); - return kind; - } - } - } - - if (type == nullptr) { - type = check_type(c, cl->type); - } - } - - if (type == nullptr) { - error(node, "Missing type in compound literal"); - return kind; - } - - - Type *t = base_type(type); - if (is_type_polymorphic(t)) { - gbString str = type_to_string(type); - error(node, "Cannot use a polymorphic type for a compound literal, got '%s'", str); - o->expr = node; - o->type = type; - gb_string_free(str); - return kind; - } - - - switch (t->kind) { - case Type_Struct: { - if (cl->elems.count == 0) { - break; // NOTE(bill): No need to init - } - if (t->Struct.is_raw_union) { - if (cl->elems.count > 0) { - // NOTE: unions cannot be constant - is_constant = false; - - if (cl->elems[0]->kind != Ast_FieldValue) { - gbString type_str = type_to_string(type); - error(node, "%s ('struct #raw_union') compound literals are only allowed to contain 'field = value' elements", type_str); - gb_string_free(type_str); - } else { - if (cl->elems.count != 1) { - gbString type_str = type_to_string(type); - error(node, "%s ('struct #raw_union') compound literals are only allowed to contain up to 1 'field = value' element, got %td", type_str, cl->elems.count); - gb_string_free(type_str); - } else { - Ast *elem = cl->elems[0]; - ast_node(fv, FieldValue, elem); - if (fv->field->kind != Ast_Ident) { - gbString expr_str = expr_to_string(fv->field); - error(elem, "Invalid field name '%s' in structure literal", expr_str); - gb_string_free(expr_str); - break; - } - - String name = fv->field->Ident.token.string; - - Selection sel = lookup_field(type, name, o->mode == Addressing_Type); - bool is_unknown = sel.entity == nullptr; - if (is_unknown) { - error(elem, "Unknown field '%.*s' in structure literal", LIT(name)); - break; - } - - if (sel.index.count > 1) { - error(elem, "Cannot assign to an anonymous field '%.*s' in a structure literal (at the moment)", LIT(name)); - break; - } - - Entity *field = t->Struct.fields[sel.index[0]]; - add_entity_use(c, fv->field, field); - - Operand o = {}; - check_expr_or_type(c, &o, fv->value, field->type); - - - check_assignment(c, &o, field->type, str_lit("structure literal")); - } - - } - } - break; - } - - - isize field_count = t->Struct.fields.count; - isize min_field_count = t->Struct.fields.count; - for (isize i = min_field_count-1; i >= 0; i--) { - Entity *e = t->Struct.fields[i]; - GB_ASSERT(e->kind == Entity_Variable); - if (e->Variable.param_value.kind != ParameterValue_Invalid) { - min_field_count--; - } else { - break; - } - } - - if (cl->elems[0]->kind == Ast_FieldValue) { - bool *fields_visited = gb_alloc_array(temporary_allocator(), bool, field_count); - - for_array(i, cl->elems) { - Ast *elem = cl->elems[i]; - if (elem->kind != Ast_FieldValue) { - error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed"); - continue; - } - ast_node(fv, FieldValue, elem); - if (fv->field->kind != Ast_Ident) { - gbString expr_str = expr_to_string(fv->field); - error(elem, "Invalid field name '%s' in structure literal", expr_str); - gb_string_free(expr_str); - continue; - } - String name = fv->field->Ident.token.string; - - Selection sel = lookup_field(type, name, o->mode == Addressing_Type); - bool is_unknown = sel.entity == nullptr; - if (is_unknown) { - error(elem, "Unknown field '%.*s' in structure literal", LIT(name)); - continue; - } - - if (sel.index.count > 1) { - error(elem, "Cannot assign to an anonymous field '%.*s' in a structure literal (at the moment)", LIT(name)); - continue; - } - - Entity *field = t->Struct.fields[sel.index[0]]; - add_entity_use(c, fv->field, field); - - if (fields_visited[sel.index[0]]) { - error(elem, "Duplicate field '%.*s' in structure literal", LIT(name)); - continue; - } - - fields_visited[sel.index[0]] = true; - - Operand o = {}; - check_expr_or_type(c, &o, fv->value, field->type); - - if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) { - is_constant = false; - } - if (is_constant) { - is_constant = check_is_operand_compound_lit_constant(c, &o); - } - - check_assignment(c, &o, field->type, str_lit("structure literal")); - } - } else { - bool seen_field_value = false; - - for_array(index, cl->elems) { - Entity *field = nullptr; - Ast *elem = cl->elems[index]; - if (elem->kind == Ast_FieldValue) { - seen_field_value = true; - error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed"); - continue; - } else if (seen_field_value) { - error(elem, "Value elements cannot be used after a 'field = value'"); - continue; - } - if (index >= field_count) { - error(elem, "Too many values in structure literal, expected %td, got %td", field_count, cl->elems.count); - break; - } - - if (field == nullptr) { - field = t->Struct.fields[index]; - } - - Operand o = {}; - check_expr_or_type(c, &o, elem, field->type); - - if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) { - is_constant = false; - } - if (is_constant) { - is_constant = check_is_operand_compound_lit_constant(c, &o); - } - - check_assignment(c, &o, field->type, str_lit("structure literal")); - } - if (cl->elems.count < field_count) { - if (min_field_count < field_count) { - if (cl->elems.count < min_field_count) { - error(cl->close, "Too few values in structure literal, expected at least %td, got %td", min_field_count, cl->elems.count); - } - } else { - error(cl->close, "Too few values in structure literal, expected %td, got %td", field_count, cl->elems.count); - } - } - } - - break; - } - - case Type_Slice: - case Type_Array: - case Type_DynamicArray: - case Type_SimdVector: - case Type_Matrix: - { - Type *elem_type = nullptr; - String context_name = {}; - i64 max_type_count = -1; - if (t->kind == Type_Slice) { - elem_type = t->Slice.elem; - context_name = str_lit("slice literal"); - } else if (t->kind == Type_Array) { - elem_type = t->Array.elem; - context_name = str_lit("array literal"); - if (!is_to_be_determined_array_count) { - max_type_count = t->Array.count; - } - } else if (t->kind == Type_DynamicArray) { - elem_type = t->DynamicArray.elem; - context_name = str_lit("dynamic array literal"); - is_constant = false; - - if (!build_context.no_dynamic_literals) { - add_package_dependency(c, "runtime", "__dynamic_array_reserve"); - add_package_dependency(c, "runtime", "__dynamic_array_append"); - } - } else if (t->kind == Type_SimdVector) { - elem_type = t->SimdVector.elem; - context_name = str_lit("simd vector literal"); - max_type_count = t->SimdVector.count; - } else if (t->kind == Type_Matrix) { - elem_type = t->Matrix.elem; - context_name = str_lit("matrix literal"); - max_type_count = t->Matrix.row_count*t->Matrix.column_count; - } else { - GB_PANIC("unreachable"); - } - - - i64 max = 0; - - Type *bet = base_type(elem_type); - if (!elem_type_can_be_constant(bet)) { - is_constant = false; - } - - if (bet == t_invalid) { - break; - } - - if (cl->elems.count > 0 && cl->elems[0]->kind == Ast_FieldValue) { - if (is_type_simd_vector(t)) { - error(cl->elems[0], "'field = value' is not allowed for SIMD vector literals"); - } else { - RangeCache rc = range_cache_make(heap_allocator()); - defer (range_cache_destroy(&rc)); - - for_array(i, cl->elems) { - Ast *elem = cl->elems[i]; - if (elem->kind != Ast_FieldValue) { - error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed"); - continue; - } - ast_node(fv, FieldValue, elem); - - if (is_ast_range(fv->field)) { - Token op = fv->field->BinaryExpr.op; - - Operand x = {}; - Operand y = {}; - bool ok = check_range(c, fv->field, &x, &y, nullptr); - if (!ok) { - continue; - } - if (x.mode != Addressing_Constant || !is_type_integer(core_type(x.type))) { - error(x.expr, "Expected a constant integer as an array field"); - continue; - } - - if (y.mode != Addressing_Constant || !is_type_integer(core_type(y.type))) { - error(y.expr, "Expected a constant integer as an array field"); - continue; - } - - i64 lo = exact_value_to_i64(x.value); - i64 hi = exact_value_to_i64(y.value); - i64 max_index = hi; - if (op.kind == Token_RangeHalf) { // ..< (exclusive) - hi -= 1; - } else { // .. (inclusive) - max_index += 1; - } - - bool new_range = range_cache_add_range(&rc, lo, hi); - if (!new_range) { - error(elem, "Overlapping field range index %lld %.*s %lld for %.*s", lo, LIT(op.string), hi, LIT(context_name)); - continue; - } - - - if (max_type_count >= 0 && (lo < 0 || lo >= max_type_count)) { - error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", lo, max_type_count, LIT(context_name)); - continue; - } - if (max_type_count >= 0 && (hi < 0 || hi >= max_type_count)) { - error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", hi, max_type_count, LIT(context_name)); - continue; - } - - if (max < hi) { - max = max_index; - } - - Operand operand = {}; - check_expr_with_type_hint(c, &operand, fv->value, elem_type); - check_assignment(c, &operand, elem_type, context_name); - - is_constant = is_constant && operand.mode == Addressing_Constant; - } else { - Operand op_index = {}; - check_expr(c, &op_index, fv->field); - - if (op_index.mode != Addressing_Constant || !is_type_integer(core_type(op_index.type))) { - error(elem, "Expected a constant integer as an array field"); - continue; - } - // add_type_and_value(c->info, op_index.expr, op_index.mode, op_index.type, op_index.value); - - i64 index = exact_value_to_i64(op_index.value); - - if (max_type_count >= 0 && (index < 0 || index >= max_type_count)) { - error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", index, max_type_count, LIT(context_name)); - continue; - } - - bool new_index = range_cache_add_index(&rc, index); - if (!new_index) { - error(elem, "Duplicate field index %lld for %.*s", index, LIT(context_name)); - continue; - } - - if (max < index+1) { - max = index+1; - } - - Operand operand = {}; - check_expr_with_type_hint(c, &operand, fv->value, elem_type); - check_assignment(c, &operand, elem_type, context_name); - - is_constant = is_constant && operand.mode == Addressing_Constant; - } - } - - cl->max_count = max; - } - - } else { - isize index = 0; - for (; index < cl->elems.count; index++) { - Ast *e = cl->elems[index]; - if (e == nullptr) { - error(node, "Invalid literal element"); - continue; - } - - if (e->kind == Ast_FieldValue) { - error(e, "Mixture of 'field = value' and value elements in a literal is not allowed"); - continue; - } - - if (0 <= max_type_count && max_type_count <= index) { - error(e, "Index %lld is out of bounds (>= %lld) for %.*s", index, max_type_count, LIT(context_name)); - } - - Operand operand = {}; - check_expr_with_type_hint(c, &operand, e, elem_type); - check_assignment(c, &operand, elem_type, context_name); - - is_constant = is_constant && operand.mode == Addressing_Constant; - } - - if (max < index) { - max = index; - } - } - - - if (t->kind == Type_Array) { - if (is_to_be_determined_array_count) { - t->Array.count = max; - } else if (cl->elems.count > 0 && cl->elems[0]->kind != Ast_FieldValue) { - if (0 < max && max < t->Array.count) { - error(node, "Expected %lld values for this array literal, got %lld", cast(long long)t->Array.count, cast(long long)max); - } - } - } - - - if (t->kind == Type_SimdVector) { - if (!is_constant) { - error(node, "Expected all constant elements for a simd vector"); - } - } - - - if (t->kind == Type_DynamicArray) { - if (build_context.no_dynamic_literals && cl->elems.count) { - error(node, "Compound literals of dynamic types have been disabled"); - } - } - - if (t->kind == Type_Matrix) { - if (cl->elems.count > 0 && cl->elems[0]->kind != Ast_FieldValue) { - if (0 < max && max < max_type_count) { - error(node, "Expected %lld values for this matrix literal, got %lld", cast(long long)max_type_count, cast(long long)max); - } - } - } - - break; - } - - case Type_EnumeratedArray: - { - Type *elem_type = t->EnumeratedArray.elem; - Type *index_type = t->EnumeratedArray.index; - String context_name = str_lit("enumerated array literal"); - i64 max_type_count = t->EnumeratedArray.count; - - gbString index_type_str = type_to_string(index_type); - defer (gb_string_free(index_type_str)); - - i64 total_lo = exact_value_to_i64(*t->EnumeratedArray.min_value); - i64 total_hi = exact_value_to_i64(*t->EnumeratedArray.max_value); - - String total_lo_string = {}; - String total_hi_string = {}; - GB_ASSERT(is_type_enum(index_type)); - { - Type *bt = base_type(index_type); - GB_ASSERT(bt->kind == Type_Enum); - for_array(i, bt->Enum.fields) { - Entity *f = bt->Enum.fields[i]; - if (f->kind != Entity_Constant) { - continue; - } - if (total_lo_string.len == 0 && compare_exact_values(Token_CmpEq, f->Constant.value, *t->EnumeratedArray.min_value)) { - total_lo_string = f->token.string; - } - if (total_hi_string.len == 0 && compare_exact_values(Token_CmpEq, f->Constant.value, *t->EnumeratedArray.max_value)) { - total_hi_string = f->token.string; - } - if (total_lo_string.len != 0 && total_hi_string.len != 0) { - break; - } - } - } - - i64 max = 0; - - Type *bet = base_type(elem_type); - if (!elem_type_can_be_constant(bet)) { - is_constant = false; - } - - if (bet == t_invalid) { - break; - } - - if (cl->elems.count > 0 && cl->elems[0]->kind == Ast_FieldValue) { - RangeCache rc = range_cache_make(heap_allocator()); - defer (range_cache_destroy(&rc)); - - for_array(i, cl->elems) { - Ast *elem = cl->elems[i]; - if (elem->kind != Ast_FieldValue) { - error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed"); - continue; - } - ast_node(fv, FieldValue, elem); - - if (is_ast_range(fv->field)) { - Token op = fv->field->BinaryExpr.op; - - Operand x = {}; - Operand y = {}; - bool ok = check_range(c, fv->field, &x, &y, nullptr, index_type); - if (!ok) { - continue; - } - if (x.mode != Addressing_Constant || !are_types_identical(x.type, index_type)) { - error(x.expr, "Expected a constant enum of type '%s' as an array field", index_type_str); - continue; - } - - if (y.mode != Addressing_Constant || !are_types_identical(x.type, index_type)) { - error(y.expr, "Expected a constant enum of type '%s' as an array field", index_type_str); - continue; - } - - i64 lo = exact_value_to_i64(x.value); - i64 hi = exact_value_to_i64(y.value); - i64 max_index = hi; - if (op.kind == Token_RangeHalf) { - hi -= 1; - } - - bool new_range = range_cache_add_range(&rc, lo, hi); - if (!new_range) { - gbString lo_str = expr_to_string(x.expr); - gbString hi_str = expr_to_string(y.expr); - error(elem, "Overlapping field range index %s %.*s %s for %.*s", lo_str, LIT(op.string), hi_str, LIT(context_name)); - gb_string_free(hi_str); - gb_string_free(lo_str); - continue; - } - - - // NOTE(bill): These are sanity checks for invalid enum values - if (max_type_count >= 0 && (lo < total_lo || lo > total_hi)) { - gbString lo_str = expr_to_string(x.expr); - error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", lo_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name)); - gb_string_free(lo_str); - continue; - } - if (max_type_count >= 0 && (hi < 0 || hi > total_hi)) { - gbString hi_str = expr_to_string(y.expr); - error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", hi_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name)); - gb_string_free(hi_str); - continue; - } - - if (max < hi) { - max = max_index; - } - - Operand operand = {}; - check_expr_with_type_hint(c, &operand, fv->value, elem_type); - check_assignment(c, &operand, elem_type, context_name); - - is_constant = is_constant && operand.mode == Addressing_Constant; - } else { - Operand op_index = {}; - check_expr_with_type_hint(c, &op_index, fv->field, index_type); - - if (op_index.mode != Addressing_Constant || !are_types_identical(op_index.type, index_type)) { - error(op_index.expr, "Expected a constant enum of type '%s' as an array field", index_type_str); - continue; - } - - i64 index = exact_value_to_i64(op_index.value); - - if (max_type_count >= 0 && (index < total_lo || index > total_hi)) { - gbString idx_str = expr_to_string(op_index.expr); - error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", idx_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name)); - gb_string_free(idx_str); - continue; - } - - bool new_index = range_cache_add_index(&rc, index); - if (!new_index) { - gbString idx_str = expr_to_string(op_index.expr); - error(elem, "Duplicate field index %s for %.*s", idx_str, LIT(context_name)); - gb_string_free(idx_str); - continue; - } - - if (max < index+1) { - max = index+1; - } - - Operand operand = {}; - check_expr_with_type_hint(c, &operand, fv->value, elem_type); - check_assignment(c, &operand, elem_type, context_name); - - is_constant = is_constant && operand.mode == Addressing_Constant; - } - } - - cl->max_count = max; - - } else { - isize index = 0; - for (; index < cl->elems.count; index++) { - Ast *e = cl->elems[index]; - if (e == nullptr) { - error(node, "Invalid literal element"); - continue; - } - - if (e->kind == Ast_FieldValue) { - error(e, "Mixture of 'field = value' and value elements in a literal is not allowed"); - continue; - } - - if (0 <= max_type_count && max_type_count <= index) { - error(e, "Index %lld is out of bounds (>= %lld) for %.*s", index, max_type_count, LIT(context_name)); - } - - Operand operand = {}; - check_expr_with_type_hint(c, &operand, e, elem_type); - check_assignment(c, &operand, elem_type, context_name); - - is_constant = is_constant && operand.mode == Addressing_Constant; - } - - if (max < index) { - max = index; - } - } - - if (cl->elems.count > 0 && cl->elems[0]->kind != Ast_FieldValue) { - if (0 < max && max < t->EnumeratedArray.count) { - error(node, "Expected %lld values for this enumerated array literal, got %lld", cast(long long)t->EnumeratedArray.count, cast(long long)max); - } else { - error(node, "Enumerated array literals must only have 'field = value' elements, bare elements are not allowed"); - } - } - - break; - } - - case Type_Basic: { - if (!is_type_any(t)) { - if (cl->elems.count != 0) { - error(node, "Illegal compound literal"); - } - break; - } - if (cl->elems.count == 0) { - break; // NOTE(bill): No need to init - } - { // Checker values - Type *field_types[2] = {t_rawptr, t_typeid}; - isize field_count = 2; - if (cl->elems[0]->kind == Ast_FieldValue) { - bool fields_visited[2] = {}; - - for_array(i, cl->elems) { - Ast *elem = cl->elems[i]; - if (elem->kind != Ast_FieldValue) { - error(elem, "Mixture of 'field = value' and value elements in a 'any' literal is not allowed"); - continue; - } - ast_node(fv, FieldValue, elem); - if (fv->field->kind != Ast_Ident) { - gbString expr_str = expr_to_string(fv->field); - error(elem, "Invalid field name '%s' in 'any' literal", expr_str); - gb_string_free(expr_str); - continue; - } - String name = fv->field->Ident.token.string; - - Selection sel = lookup_field(type, name, o->mode == Addressing_Type); - if (sel.entity == nullptr) { - error(elem, "Unknown field '%.*s' in 'any' literal", LIT(name)); - continue; - } - - isize index = sel.index[0]; - - if (fields_visited[index]) { - error(elem, "Duplicate field '%.*s' in 'any' literal", LIT(name)); - continue; - } - - fields_visited[index] = true; - check_expr(c, o, fv->value); - - // NOTE(bill): 'any' literals can never be constant - is_constant = false; - - check_assignment(c, o, field_types[index], str_lit("'any' literal")); - } - } else { - for_array(index, cl->elems) { - Ast *elem = cl->elems[index]; - if (elem->kind == Ast_FieldValue) { - error(elem, "Mixture of 'field = value' and value elements in a 'any' literal is not allowed"); - continue; - } - - - check_expr(c, o, elem); - if (index >= field_count) { - error(o->expr, "Too many values in 'any' literal, expected %td", field_count); - break; - } - - // NOTE(bill): 'any' literals can never be constant - is_constant = false; - - check_assignment(c, o, field_types[index], str_lit("'any' literal")); - } - if (cl->elems.count < field_count) { - error(cl->close, "Too few values in 'any' literal, expected %td, got %td", field_count, cl->elems.count); - } - } - } - - break; - } - - case Type_Map: { - if (cl->elems.count == 0) { - break; - } - is_constant = false; - { // Checker values - bool key_is_typeid = is_type_typeid(t->Map.key); - bool value_is_typeid = is_type_typeid(t->Map.value); - - for_array(i, cl->elems) { - Ast *elem = cl->elems[i]; - if (elem->kind != Ast_FieldValue) { - error(elem, "Only 'field = value' elements are allowed in a map literal"); - continue; - } - ast_node(fv, FieldValue, elem); - - if (key_is_typeid) { - check_expr_or_type(c, o, fv->field, t->Map.key); - } else { - check_expr_with_type_hint(c, o, fv->field, t->Map.key); - } - check_assignment(c, o, t->Map.key, str_lit("map literal")); - if (o->mode == Addressing_Invalid) { - continue; - } - - if (value_is_typeid) { - check_expr_or_type(c, o, fv->value, t->Map.value); - } else { - check_expr_with_type_hint(c, o, fv->value, t->Map.value); - } - check_assignment(c, o, t->Map.value, str_lit("map literal")); - } - } - - if (build_context.no_dynamic_literals && cl->elems.count) { - error(node, "Compound literals of dynamic types have been disabled"); - } else { - add_package_dependency(c, "runtime", "__dynamic_map_reserve"); - add_package_dependency(c, "runtime", "__dynamic_map_set"); - } - break; - } - - case Type_BitSet: { - if (cl->elems.count == 0) { - break; // NOTE(bill): No need to init - } - Type *et = base_type(t->BitSet.elem); - isize field_count = 0; - if (et->kind == Type_Enum) { - field_count = et->Enum.fields.count; - } - - if (cl->elems[0]->kind == Ast_FieldValue) { - error(cl->elems[0], "'field = value' in a bit_set a literal is not allowed"); - is_constant = false; - } else { - for_array(index, cl->elems) { - Ast *elem = cl->elems[index]; - if (elem->kind == Ast_FieldValue) { - error(elem, "'field = value' in a bit_set a literal is not allowed"); - continue; - } - - check_expr_with_type_hint(c, o, elem, et); - - if (is_constant) { - is_constant = o->mode == Addressing_Constant; - } - - check_assignment(c, o, t->BitSet.elem, str_lit("bit_set literal")); - if (o->mode == Addressing_Constant) { - i64 lower = t->BitSet.lower; - i64 upper = t->BitSet.upper; - i64 v = exact_value_to_i64(o->value); - if (lower <= v && v <= upper) { - // okay - } else { - error(elem, "Bit field value out of bounds, %lld not in the range %lld .. %lld", v, lower, upper); - continue; - } - } - } - } - break; - } - - default: { - if (cl->elems.count == 0) { - break; // NOTE(bill): No need to init - } - - gbString str = type_to_string(type); - error(node, "Invalid compound literal type '%s'", str); - gb_string_free(str); - return kind; - } - } - - if (is_constant) { - o->mode = Addressing_Constant; - - if (is_type_bit_set(type)) { - // NOTE(bill): Encode as an integer - - i64 lower = base_type(type)->BitSet.lower; - - u64 bits = 0; - for_array(index, cl->elems) { - Ast *elem = cl->elems[index]; - GB_ASSERT(elem->kind != Ast_FieldValue); - TypeAndValue tav = elem->tav; - ExactValue i = exact_value_to_integer(tav.value); - if (i.kind != ExactValue_Integer) { - continue; - } - i64 val = big_int_to_i64(&i.value_integer); - val -= lower; - u64 bit = u64(1ll<value = exact_value_u64(bits); - } else if (is_type_constant_type(type) && cl->elems.count == 0) { - ExactValue value = exact_value_compound(node); - Type *bt = core_type(type); - if (bt->kind == Type_Basic) { - if (bt->Basic.flags & BasicFlag_Boolean) { - value = exact_value_bool(false); - } else if (bt->Basic.flags & BasicFlag_Integer) { - value = exact_value_i64(0); - } else if (bt->Basic.flags & BasicFlag_Unsigned) { - value = exact_value_i64(0); - } else if (bt->Basic.flags & BasicFlag_Float) { - value = exact_value_float(0); - } else if (bt->Basic.flags & BasicFlag_Complex) { - value = exact_value_complex(0, 0); - } else if (bt->Basic.flags & BasicFlag_Quaternion) { - value = exact_value_quaternion(0, 0, 0, 0); - } else if (bt->Basic.flags & BasicFlag_Pointer) { - value = exact_value_pointer(0); - } else if (bt->Basic.flags & BasicFlag_String) { - String empty_string = {}; - value = exact_value_string(empty_string); - } else if (bt->Basic.flags & BasicFlag_Rune) { - value = exact_value_i64(0); - } - } - - o->value = value; - } else { - o->value = exact_value_compound(node); - } - } else { - o->mode = Addressing_Value; - } - o->type = type; + kind = check_compound_literal(c, o, node, type_hint); case_end; case_ast_node(pe, ParenExpr, node); @@ -8172,127 +9232,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type case_end; case_ast_node(ta, TypeAssertion, node); - check_expr(c, o, ta->expr); - node->viral_state_flags |= ta->expr->viral_state_flags; - - if (o->mode == Addressing_Invalid) { - o->expr = node; - return kind; - } - if (o->mode == Addressing_Constant) { - gbString expr_str = expr_to_string(o->expr); - error(o->expr, "A type assertion cannot be applied to a constant expression: '%s'", expr_str); - gb_string_free(expr_str); - o->mode = Addressing_Invalid; - o->expr = node; - return kind; - } - - if (is_type_untyped(o->type)) { - gbString expr_str = expr_to_string(o->expr); - error(o->expr, "A type assertion cannot be applied to an untyped expression: '%s'", expr_str); - gb_string_free(expr_str); - o->mode = Addressing_Invalid; - o->expr = node; - return kind; - } - - Type *src = type_deref(o->type); - Type *bsrc = base_type(src); - - - if (ta->type != nullptr && ta->type->kind == Ast_UnaryExpr && ta->type->UnaryExpr.op.kind == Token_Question) { - if (!is_type_union(src)) { - gbString str = type_to_string(o->type); - error(o->expr, "Type assertions with .? can only operate on unions, got %s", str); - gb_string_free(str); - o->mode = Addressing_Invalid; - o->expr = node; - return kind; - } - - if (bsrc->Union.variants.count != 1 && type_hint != nullptr) { - bool allowed = false; - for_array(i, bsrc->Union.variants) { - Type *vt = bsrc->Union.variants[i]; - if (are_types_identical(vt, type_hint)) { - allowed = true; - add_type_info_type(c, vt); - break; - } - } - if (allowed) { - add_type_info_type(c, o->type); - o->type = type_hint; - o->mode = Addressing_OptionalOk; - return kind; - } - } - - if (bsrc->Union.variants.count != 1) { - error(o->expr, "Type assertions with .? can only operate on unions with 1 variant, got %lld", cast(long long)bsrc->Union.variants.count); - o->mode = Addressing_Invalid; - o->expr = node; - return kind; - } - - add_type_info_type(c, o->type); - add_type_info_type(c, bsrc->Union.variants[0]); - - o->type = bsrc->Union.variants[0]; - o->mode = Addressing_OptionalOk; - } else { - Type *t = check_type(c, ta->type); - Type *dst = t; - - if (is_type_union(src)) { - bool ok = false; - for_array(i, bsrc->Union.variants) { - Type *vt = bsrc->Union.variants[i]; - if (are_types_identical(vt, dst)) { - ok = true; - break; - } - } - - if (!ok) { - gbString expr_str = expr_to_string(o->expr); - gbString dst_type_str = type_to_string(t); - defer (gb_string_free(expr_str)); - defer (gb_string_free(dst_type_str)); - if (bsrc->Union.variants.count == 0) { - error(o->expr, "Cannot type assert '%s' to '%s' as this is an empty union", expr_str, dst_type_str); - } else { - error(o->expr, "Cannot type assert '%s' to '%s' as it is not a variant of that union", expr_str, dst_type_str); - } - o->mode = Addressing_Invalid; - o->expr = node; - return kind; - } - - add_type_info_type(c, o->type); - add_type_info_type(c, t); - - o->type = t; - o->mode = Addressing_OptionalOk; - } else if (is_type_any(src)) { - o->type = t; - o->mode = Addressing_OptionalOk; - - add_type_info_type(c, o->type); - add_type_info_type(c, t); - } else { - gbString str = type_to_string(o->type); - error(o->expr, "Type assertions can only operate on unions and 'any', got %s", str); - gb_string_free(str); - o->mode = Addressing_Invalid; - o->expr = node; - return kind; - } - } - - add_package_dependency(c, "runtime", "type_assertion_check"); - add_package_dependency(c, "runtime", "type_assertion_check2"); + kind = check_type_assertion(c, o, node, type_hint); case_end; case_ast_node(tc, TypeCast, node); @@ -8362,7 +9302,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type check_unary_expr(c, o, ue->op, node); } o->expr = node; - return kind; + return Expr_Expr; case_end; @@ -8380,443 +9320,19 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type case_end; case_ast_node(se, SelectorCallExpr, node); - // IMPORTANT NOTE(bill, 2020-05-22): This is a complete hack to get a shorthand which is extremely useful for vtables - // COM APIs is a great example of where this kind of thing is extremely useful - // General idea: - // - // x->y(123) == x.y(x, 123) - // - // How this has been implemented at the moment is quite hacky but it's done so to reduce need for huge backend changes - // Just regenerating a new AST aids things - // - // TODO(bill): Is this a good hack or not? - // - // NOTE(bill, 2020-05-22): I'm going to regret this decision, ain't I? - - - if (se->modified_call) { - // Prevent double evaluation - o->expr = node; - o->type = node->tav.type; - o->value = node->tav.value; - o->mode = node->tav.mode; - return Expr_Expr; - } - - bool allow_arrow_right_selector_expr; - allow_arrow_right_selector_expr = c->allow_arrow_right_selector_expr; - c->allow_arrow_right_selector_expr = true; - Operand x = {}; - ExprKind kind = check_expr_base(c, &x, se->expr, nullptr); - c->allow_arrow_right_selector_expr = allow_arrow_right_selector_expr; - - if (x.mode == Addressing_Invalid || x.type == t_invalid) { - o->mode = Addressing_Invalid; - o->type = t_invalid; - o->expr = node; - return kind; - } - if (!is_type_proc(x.type)) { - gbString type_str = type_to_string(x.type); - error(se->call, "Selector call expressions expect a procedure type for the call, got '%s'", type_str); - gb_string_free(type_str); - - o->mode = Addressing_Invalid; - o->type = t_invalid; - o->expr = node; - return Expr_Stmt; - } - - ast_node(ce, CallExpr, se->call); - - GB_ASSERT(x.expr->kind == Ast_SelectorExpr); - - Ast *first_arg = x.expr->SelectorExpr.expr; - GB_ASSERT(first_arg != nullptr); - - Type *pt = base_type(x.type); - GB_ASSERT(pt->kind == Type_Proc); - Type *first_type = nullptr; - String first_arg_name = {}; - if (pt->Proc.param_count > 0) { - Entity *f = pt->Proc.params->Tuple.variables[0]; - first_type = f->type; - first_arg_name = f->token.string; - } - if (first_arg_name.len == 0) { - first_arg_name = str_lit("_"); - } - - if (first_type == nullptr) { - error(se->call, "Selector call expressions expect a procedure type for the call with at least 1 parameter"); - o->mode = Addressing_Invalid; - o->type = t_invalid; - o->expr = node; - return Expr_Stmt; - } - - Operand y = {}; - y.mode = first_arg->tav.mode; - y.type = first_arg->tav.type; - y.value = first_arg->tav.value; - if (check_is_assignable_to(c, &y, first_type)) { - // Do nothing, it's valid - } else { - Operand z = y; - z.type = type_deref(y.type); - if (check_is_assignable_to(c, &z, first_type)) { - // NOTE(bill): AST GENERATION HACK! - Token op = {Token_Pointer}; - first_arg = ast_deref_expr(first_arg->file(), first_arg, op); - } else if (y.mode == Addressing_Variable) { - Operand w = y; - w.type = alloc_type_pointer(y.type); - if (check_is_assignable_to(c, &w, first_type)) { - // NOTE(bill): AST GENERATION HACK! - Token op = {Token_And}; - first_arg = ast_unary_expr(first_arg->file(), op, first_arg); - } - } - } - - if (ce->args.count > 0) { - bool fail = false; - bool first_is_field_value = (ce->args[0]->kind == Ast_FieldValue); - for_array(i, ce->args) { - Ast *arg = ce->args[i]; - bool mix = false; - if (first_is_field_value) { - mix = arg->kind != Ast_FieldValue; - } else { - mix = arg->kind == Ast_FieldValue; - } - if (mix) { - fail = true; - break; - } - } - if (!fail && first_is_field_value) { - Token op = {Token_Eq}; - AstFile *f = first_arg->file(); - first_arg = ast_field_value(f, ast_ident(f, make_token_ident(first_arg_name)), first_arg, op); - } - } - - - - auto modified_args = slice_make(heap_allocator(), ce->args.count+1); - modified_args[0] = first_arg; - slice_copy(&modified_args, ce->args, 1); - ce->args = modified_args; - se->modified_call = true; - - allow_arrow_right_selector_expr = c->allow_arrow_right_selector_expr; - c->allow_arrow_right_selector_expr = true; - check_expr_base(c, o, se->call, type_hint); - c->allow_arrow_right_selector_expr = allow_arrow_right_selector_expr; - - o->expr = node; - return Expr_Expr; + return check_selector_call_expr(c, o, node, type_hint); case_end; - case_ast_node(ise, ImplicitSelectorExpr, node); return check_implicit_selector_expr(c, o, node, type_hint); case_end; case_ast_node(ie, IndexExpr, node); - check_expr(c, o, ie->expr); - node->viral_state_flags |= ie->expr->viral_state_flags; - if (o->mode == Addressing_Invalid) { - o->expr = node; - return kind; - } - - Type *t = base_type(type_deref(o->type)); - bool is_ptr = is_type_pointer(o->type); - bool is_const = o->mode == Addressing_Constant; - - if (is_type_map(t)) { - Operand key = {}; - if (is_type_typeid(t->Map.key)) { - check_expr_or_type(c, &key, ie->index, t->Map.key); - } else { - check_expr_with_type_hint(c, &key, ie->index, t->Map.key); - } - check_assignment(c, &key, t->Map.key, str_lit("map index")); - if (key.mode == Addressing_Invalid) { - o->mode = Addressing_Invalid; - o->expr = node; - return kind; - } - o->mode = Addressing_MapIndex; - o->type = t->Map.value; - o->expr = node; - - add_package_dependency(c, "runtime", "__dynamic_map_get"); - add_package_dependency(c, "runtime", "__dynamic_map_set"); - return Expr_Expr; - } - - i64 max_count = -1; - bool valid = check_set_index_data(o, t, is_ptr, &max_count, o->type); - - if (is_const) { - if (is_type_array(t)) { - // OKay - } else if (is_type_slice(t)) { - // Okay - } else if (is_type_enumerated_array(t)) { - // Okay - } else if (is_type_string(t)) { - // Okay - } else if (is_type_relative_slice(t)) { - // Okay - } else if (is_type_matrix(t)) { - // Okay - } else { - valid = false; - } - } - - if (!valid) { - gbString str = expr_to_string(o->expr); - gbString type_str = type_to_string(o->type); - defer (gb_string_free(str)); - defer (gb_string_free(type_str)); - if (is_const) { - error(o->expr, "Cannot index constant '%s' of type '%s'", str, type_str); - } else { - error(o->expr, "Cannot index '%s' of type '%s'", str, type_str); - } - o->mode = Addressing_Invalid; - o->expr = node; - return kind; - } - - if (ie->index == nullptr) { - gbString str = expr_to_string(o->expr); - error(o->expr, "Missing index for '%s'", str); - gb_string_free(str); - o->mode = Addressing_Invalid; - o->expr = node; - return kind; - } - - Type *index_type_hint = nullptr; - if (is_type_enumerated_array(t)) { - Type *bt = base_type(t); - GB_ASSERT(bt->kind == Type_EnumeratedArray); - index_type_hint = bt->EnumeratedArray.index; - } - - i64 index = 0; - bool ok = check_index_value(c, t, false, ie->index, max_count, &index, index_type_hint); - if (is_const) { - if (index < 0) { - gbString str = expr_to_string(o->expr); - error(o->expr, "Cannot index a constant '%s'", str); - error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n"); - gb_string_free(str); - o->mode = Addressing_Invalid; - o->expr = node; - return kind; - } else if (ok) { - ExactValue value = type_and_value_of_expr(ie->expr).value; - o->mode = Addressing_Constant; - bool success = false; - bool finish = false; - o->value = get_constant_field_single(c, value, cast(i32)index, &success, &finish); - if (!success) { - gbString str = expr_to_string(o->expr); - error(o->expr, "Cannot index a constant '%s' with index %lld", str, cast(long long)index); - error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n"); - gb_string_free(str); - o->mode = Addressing_Invalid; - o->expr = node; - return kind; - } - } - } - - if (type_hint != nullptr && is_type_matrix(t)) { - // TODO(bill): allow matrix columns to be assignable to other types which are the same internally - // if a type hint exists - } - + kind = check_index_expr(c, o, node, type_hint); case_end; case_ast_node(se, SliceExpr, node); - check_expr(c, o, se->expr); - node->viral_state_flags |= se->expr->viral_state_flags; - - if (o->mode == Addressing_Invalid) { - o->mode = Addressing_Invalid; - o->expr = node; - return kind; - } - - bool valid = false; - i64 max_count = -1; - Type *t = base_type(type_deref(o->type)); - switch (t->kind) { - case Type_Basic: - if (t->Basic.kind == Basic_string || t->Basic.kind == Basic_UntypedString) { - valid = true; - if (o->mode == Addressing_Constant) { - max_count = o->value.value_string.len; - } - o->type = type_deref(o->type); - } - break; - - case Type_Array: - valid = true; - max_count = t->Array.count; - if (o->mode != Addressing_Variable && !is_type_pointer(o->type)) { - gbString str = expr_to_string(node); - error(node, "Cannot slice array '%s', value is not addressable", str); - gb_string_free(str); - o->mode = Addressing_Invalid; - o->expr = node; - return kind; - } - o->type = alloc_type_slice(t->Array.elem); - break; - - case Type_MultiPointer: - valid = true; - o->type = type_deref(o->type); - break; - - case Type_Slice: - valid = true; - o->type = type_deref(o->type); - break; - - case Type_DynamicArray: - valid = true; - o->type = alloc_type_slice(t->DynamicArray.elem); - break; - - case Type_Struct: - if (is_type_soa_struct(t)) { - valid = true; - o->type = make_soa_struct_slice(c, nullptr, nullptr, t->Struct.soa_elem); - } - break; - - case Type_RelativeSlice: - valid = true; - o->type = t->RelativeSlice.slice_type; - if (o->mode != Addressing_Variable) { - gbString str = expr_to_string(node); - error(node, "Cannot relative slice '%s', value is not addressable", str); - gb_string_free(str); - o->mode = Addressing_Invalid; - o->expr = node; - return kind; - } - break; - } - - if (!valid) { - gbString str = expr_to_string(o->expr); - gbString type_str = type_to_string(o->type); - error(o->expr, "Cannot slice '%s' of type '%s'", str, type_str); - gb_string_free(type_str); - gb_string_free(str); - o->mode = Addressing_Invalid; - o->expr = node; - return kind; - } - - if (se->low == nullptr && se->high != nullptr) { - // It is okay to continue as it will assume the 1st index is zero - } - - i64 indices[2] = {}; - Ast *nodes[2] = {se->low, se->high}; - for (isize i = 0; i < gb_count_of(nodes); i++) { - i64 index = max_count; - if (nodes[i] != nullptr) { - i64 capacity = -1; - if (max_count >= 0) { - capacity = max_count; - } - i64 j = 0; - if (check_index_value(c, t, true, nodes[i], capacity, &j)) { - index = j; - } - - node->viral_state_flags |= nodes[i]->viral_state_flags; - } else if (i == 0) { - index = 0; - } - indices[i] = index; - } - - for (isize i = 0; i < gb_count_of(indices); i++) { - i64 a = indices[i]; - for (isize j = i+1; j < gb_count_of(indices); j++) { - i64 b = indices[j]; - if (a > b && b >= 0) { - error(se->close, "Invalid slice indices: [%td > %td]", a, b); - } - } - } - - if (max_count < 0) { - if (o->mode == Addressing_Constant) { - gbString s = expr_to_string(se->expr); - error(se->expr, "Cannot slice constant value '%s'", s); - gb_string_free(s); - } - } - - if (t->kind == Type_MultiPointer && se->high != nullptr) { - /* - x[:] -> [^]T - x[i:] -> [^]T - x[:n] -> []T - x[i:n] -> []T - */ - o->type = alloc_type_slice(t->MultiPointer.elem); - } - - o->mode = Addressing_Value; - - if (is_type_string(t) && max_count >= 0) { - bool all_constant = true; - for (isize i = 0; i < gb_count_of(nodes); i++) { - if (nodes[i] != nullptr) { - TypeAndValue tav = type_and_value_of_expr(nodes[i]); - if (tav.mode != Addressing_Constant) { - all_constant = false; - break; - } - } - } - if (!all_constant) { - gbString str = expr_to_string(o->expr); - error(o->expr, "Cannot slice '%s' with non-constant indices", str); - error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n"); - gb_string_free(str); - o->mode = Addressing_Value; // NOTE(bill): Keep subsequent values going without erring - o->expr = node; - return kind; - } - - String s = {}; - if (o->value.kind == ExactValue_String) { - s = o->value.value_string; - } - - o->mode = Addressing_Constant; - o->type = t; - o->value = exact_value_string(substring(s, cast(isize)indices[0], cast(isize)indices[1])); - } - + kind = check_slice_expr(c, o, node, type_hint); case_end; case_ast_node(mie, MatrixIndexExpr, node); @@ -8861,7 +9377,15 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type } else { gbString str = expr_to_string(o->expr); gbString typ = type_to_string(o->type); + begin_error_block(); + error(o->expr, "Cannot dereference '%s' of type '%s'", str, typ); + if (o->type && is_type_multi_pointer(o->type)) { + error_line("\tDid you mean '%s[0]'?\n", str); + } + + end_error_block(); + gb_string_free(typ); gb_string_free(str); o->mode = Addressing_Invalid; @@ -8941,6 +9465,8 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type return kind; } + + ExprKind check_expr_base(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { ExprKind kind = check_expr_base_internal(c, o, node, type_hint); if (o->type != nullptr && core_type(o->type) == nullptr) { @@ -8956,6 +9482,8 @@ ExprKind check_expr_base(CheckerContext *c, Operand *o, Ast *node, Type *type_hi if (o->type != nullptr && is_type_untyped(o->type)) { add_untyped(c, node, o->mode, o->type, o->value); } + check_rtti_type_disallowed(node, o->type, "An expression is using a type, %s, which has been disallowed"); + add_type_and_value(c->info, node, o->mode, o->type, o->value); return kind; } @@ -9342,6 +9870,13 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) { str = gb_string_appendc(str, " = "); str = write_expr_to_string(str, fv->value, shorthand); case_end; + case_ast_node(fv, EnumFieldValue, node); + str = write_expr_to_string(str, fv->name, shorthand); + if (fv->value) { + str = gb_string_appendc(str, " = "); + str = write_expr_to_string(str, fv->value, shorthand); + } + case_end; case_ast_node(ht, HelperType, node); str = gb_string_appendc(str, "#type "); @@ -9433,6 +9968,9 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) { if (f->flags&FieldFlag_const) { str = gb_string_appendc(str, "#const "); } + if (f->flags&FieldFlag_subtype) { + str = gb_string_appendc(str, "#subtype "); + } for_array(i, f->names) { Ast *name = f->names[i]; @@ -9512,9 +10050,10 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) { str = write_expr_to_string(str, ce->proc, shorthand); str = gb_string_appendc(str, "("); - for_array(i, ce->args) { + isize idx0 = cast(isize)ce->was_selector; + for (isize i = idx0; i < ce->args.count; i++) { Ast *arg = ce->args[i]; - if (i > 0) { + if (i > idx0) { str = gb_string_appendc(str, ", "); } str = write_expr_to_string(str, arg, shorthand); @@ -9591,8 +10130,10 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) { str = write_expr_to_string(str, st->polymorphic_params, shorthand); str = gb_string_appendc(str, ") "); } - if (st->no_nil) str = gb_string_appendc(str, "#no_nil "); - if (st->maybe) str = gb_string_appendc(str, "#maybe "); + switch (st->kind) { + case UnionType_no_nil: str = gb_string_appendc(str, "#no_nil "); break; + case UnionType_shared_nil: str = gb_string_appendc(str, "#shared_nil "); break; + } if (st->align) { str = gb_string_appendc(str, "#align "); str = write_expr_to_string(str, st->align, shorthand); diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 94b7561c7..a6f6f1a7d 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -490,6 +490,14 @@ void check_stmt(CheckerContext *ctx, Ast *node, u32 flags) { out &= ~StateFlag_no_bounds_check; } + if (in & StateFlag_no_type_assert) { + out |= StateFlag_no_type_assert; + out &= ~StateFlag_type_assert; + } else if (in & StateFlag_type_assert) { + out |= StateFlag_type_assert; + out &= ~StateFlag_no_type_assert; + } + ctx->state_flags = out; } @@ -689,54 +697,6 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b return true; } - -struct TypeAndToken { - Type *type; - Token token; -}; - - -void add_constant_switch_case(CheckerContext *ctx, PtrMap *seen, Operand operand, bool use_expr = true) { - if (operand.mode != Addressing_Constant) { - return; - } - if (operand.value.kind == ExactValue_Invalid) { - return; - } - - uintptr key = hash_exact_value(operand.value); - TypeAndToken *found = map_get(seen, key); - if (found != nullptr) { - isize count = multi_map_count(seen, key); - TypeAndToken *taps = gb_alloc_array(temporary_allocator(), TypeAndToken, count); - - multi_map_get_all(seen, key, taps); - for (isize i = 0; i < count; i++) { - TypeAndToken tap = taps[i]; - if (!are_types_identical(operand.type, tap.type)) { - continue; - } - - TokenPos pos = tap.token.pos; - if (use_expr) { - gbString expr_str = expr_to_string(operand.expr); - error(operand.expr, - "Duplicate case '%s'\n" - "\tprevious case at %s", - expr_str, - token_pos_to_string(pos)); - gb_string_free(expr_str); - } else { - error(operand.expr, "Duplicate case found with previous case at %s", token_pos_to_string(pos)); - } - return; - } - } - - TypeAndToken tap = {operand.type, ast_token(operand.expr)}; - multi_map_insert(seen, key, tap); -} - void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { ast_node(irs, UnrollRangeStmt, node); check_open_scope(ctx, node); @@ -961,7 +921,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { } } - PtrMap seen = {}; // NOTE(bill): Multimap, Key: ExactValue + SeenMap seen = {}; // NOTE(bill): Multimap, Key: ExactValue map_init(&seen, heap_allocator()); defer (map_destroy(&seen)); @@ -1001,9 +961,9 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { TokenKind upper_op = Token_Invalid; switch (be->op.kind) { - case Token_Ellipsis: upper_op = Token_GtEq; break; - case Token_RangeFull: upper_op = Token_GtEq; break; - case Token_RangeHalf: upper_op = Token_Gt; break; + case Token_Ellipsis: upper_op = Token_LtEq; break; + case Token_RangeFull: upper_op = Token_LtEq; break; + case Token_RangeHalf: upper_op = Token_Lt; break; default: GB_PANIC("Invalid range operator"); break; } @@ -1024,45 +984,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { Operand b1 = rhs; check_comparison(ctx, &a1, &b1, Token_LtEq); - if (is_type_enum(x.type)) { - // TODO(bill): Fix this logic so it's fast!!! - - i64 v0 = exact_value_to_i64(lhs.value); - i64 v1 = exact_value_to_i64(rhs.value); - Operand v = {}; - v.mode = Addressing_Constant; - v.type = x.type; - v.expr = x.expr; - - Type *bt = base_type(x.type); - GB_ASSERT(bt->kind == Type_Enum); - for (i64 vi = v0; vi <= v1; vi++) { - if (upper_op != Token_GtEq && vi == v1) { - break; - } - - bool found = false; - for_array(j, bt->Enum.fields) { - Entity *f = bt->Enum.fields[j]; - GB_ASSERT(f->kind == Entity_Constant); - - i64 fv = exact_value_to_i64(f->Constant.value); - if (fv == vi) { - found = true; - break; - } - } - if (found) { - v.value = exact_value_i64(vi); - add_constant_switch_case(ctx, &seen, v); - } - } - } else { - add_constant_switch_case(ctx, &seen, lhs); - if (upper_op == Token_GtEq) { - add_constant_switch_case(ctx, &seen, rhs); - } - } + add_to_seen_map(ctx, &seen, upper_op, x, lhs, rhs); if (is_type_string(x.type)) { // NOTE(bill): Force dependency for strings here @@ -1107,7 +1029,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { continue; } update_untyped_expr_type(ctx, z.expr, x.type, !is_type_untyped(x.type)); - add_constant_switch_case(ctx, &seen, y); + add_to_seen_map(ctx, &seen, y); } } } @@ -1143,7 +1065,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { if (unhandled.count == 1) { error_no_newline(node, "Unhandled switch case: %.*s", LIT(unhandled[0]->token.string)); } else { - error_no_newline(node, "Unhandled switch cases: "); + error(node, "Unhandled switch cases:"); for_array(i, unhandled) { Entity *f = unhandled[i]; error_line("\t%.*s\n", LIT(f->token.string)); @@ -1459,6 +1381,18 @@ bool all_operands_valid(Array const &operands) { return true; } +bool check_stmt_internal_builtin_proc_id(Ast *expr, BuiltinProcId *id_) { + BuiltinProcId id = BuiltinProc_Invalid; + Entity *e = entity_of_node(expr); + if (e != nullptr && e->kind == Entity_Builtin) { + if (e->Builtin.id && e->Builtin.id != BuiltinProc_DIRECTIVE) { + id = cast(BuiltinProcId)e->Builtin.id; + } + } + if (id_) *id_ = id; + return id != BuiltinProc_Invalid; +} + void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { u32 mod_flags = flags & (~Stmt_FallthroughAllowed); switch (node->kind) { @@ -1483,29 +1417,43 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { if (kind == Expr_Stmt) { return; } - Ast *expr = strip_or_return_expr(operand.expr); + Ast *expr = strip_or_return_expr(operand.expr); if (expr->kind == Ast_CallExpr) { + BuiltinProcId builtin_id = BuiltinProc_Invalid; + bool do_require = false; + AstCallExpr *ce = &expr->CallExpr; - Type *t = type_of_expr(ce->proc); - if (is_type_proc(t)) { - if (t->Proc.require_results) { - gbString expr_str = expr_to_string(ce->proc); - error(node, "'%s' requires that its results must be handled", expr_str); - gb_string_free(expr_str); - } + Type *t = base_type(type_of_expr(ce->proc)); + if (t->kind == Type_Proc) { + do_require = t->Proc.require_results; + } else if (check_stmt_internal_builtin_proc_id(ce->proc, &builtin_id)) { + auto const &bp = builtin_procs[builtin_id]; + do_require = bp.kind == Expr_Expr && !bp.ignore_results; + } + if (do_require) { + gbString expr_str = expr_to_string(ce->proc); + error(node, "'%s' requires that its results must be handled", expr_str); + gb_string_free(expr_str); } return; } else if (expr->kind == Ast_SelectorCallExpr) { + BuiltinProcId builtin_id = BuiltinProc_Invalid; + bool do_require = false; + AstSelectorCallExpr *se = &expr->SelectorCallExpr; ast_node(ce, CallExpr, se->call); - Type *t = type_of_expr(ce->proc); - if (is_type_proc(t)) { - if (t->Proc.require_results) { - gbString expr_str = expr_to_string(ce->proc); - error(node, "'%s' requires that its results must be handled", expr_str); - gb_string_free(expr_str); - } + Type *t = base_type(type_of_expr(ce->proc)); + if (t->kind == Type_Proc) { + do_require = t->Proc.require_results; + } else if (check_stmt_internal_builtin_proc_id(ce->proc, &builtin_id)) { + auto const &bp = builtin_procs[builtin_id]; + do_require = bp.kind == Expr_Expr && !bp.ignore_results; + } + if (do_require) { + gbString expr_str = expr_to_string(ce->proc); + error(node, "'%s' requires that its results must be handled", expr_str); + gb_string_free(expr_str); } return; } @@ -2194,7 +2142,26 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { } if (new_name_count == 0) { - error(node, "No new declarations on the lhs"); + begin_error_block(); + error(node, "No new declarations on the left hand side"); + bool all_underscore = true; + for_array(i, vd->names) { + Ast *name = vd->names[i]; + if (name->kind == Ast_Ident) { + if (!is_blank_ident(name)) { + all_underscore = false; + break; + } + } else { + all_underscore = false; + break; + } + } + if (all_underscore) { + error_line("\tSuggestion: Try changing the declaration (:=) to an assignment (=)\n"); + } + + end_error_block(); } Type *init_type = nullptr; @@ -2230,7 +2197,6 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { e->state = EntityState_Resolved; } ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix); - e->Variable.thread_local_model = ac.thread_local_model; if (ac.link_name.len > 0) { e->Variable.link_name = ac.link_name; @@ -2260,6 +2226,10 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { } e->Variable.thread_local_model = ac.thread_local_model; } + + if (is_arch_wasm() && e->Variable.thread_local_model.len != 0) { + error(e->token, "@(thread_local) is not supported for this target platform"); + } if (ac.is_static && ac.thread_local_model != "") { diff --git a/src/check_type.cpp b/src/check_type.cpp index 2a7479d68..dea523599 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -144,6 +144,7 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice *fields } bool is_using = (p->flags&FieldFlag_using) != 0; + bool is_subtype = (p->flags&FieldFlag_subtype) != 0; for_array(j, p->names) { Ast *name = p->names[j]; @@ -158,6 +159,9 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice *fields Entity *field = alloc_entity_field(ctx->scope, name_token, type, is_using, field_src_index); add_entity(ctx, ctx->scope, name, field); field->Variable.field_group_index = field_group_index; + if (is_subtype) { + field->flags |= EntityFlag_Subtype; + } if (j == 0) { field->Variable.docs = docs; @@ -194,6 +198,20 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice *fields populate_using_entity_scope(ctx, node, p, type); } + + if (is_subtype && p->names.count > 0) { + Type *first_type = fields_array[fields_array.count-1]->type; + Type *t = base_type(type_deref(first_type)); + + if (!does_field_type_allow_using(t) && + p->names.count >= 1 && + p->names[0]->kind == Ast_Ident) { + Token name_token = p->names[0]->Ident.token; + gbString type_str = type_to_string(first_type); + error(name_token, "'subtype' cannot be applied to the field '%.*s' of type '%s'", LIT(name_token.string), type_str); + gb_string_free(type_str); + } + } } *fields = slice_from_array(fields_array); @@ -323,6 +341,10 @@ void add_polymorphic_record_entity(CheckerContext *ctx, Ast *node, Type *named_t } named_type->Named.type_name = e; + GB_ASSERT(original_type->kind == Type_Named); + e->TypeName.objc_class_name = original_type->Named.type_name->TypeName.objc_class_name; + // TODO(bill): Is this even correct? Or should the metadata be copied? + e->TypeName.objc_metadata = original_type->Named.type_name->TypeName.objc_metadata; mutex_lock(&ctx->info->gen_types_mutex); auto *found_gen_types = map_get(&ctx->info->gen_types, original_type); @@ -653,22 +675,31 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Arraykind == UnionType_shared_nil) { + if (!type_has_nil(t)) { + gbString s = type_to_string(t); + error(node, "Each variant of a union with #shared_nil must have a 'nil' value, got %s", s); + gb_string_free(s); + } + } } } } union_type->Union.variants = slice_from_array(variants); - union_type->Union.no_nil = ut->no_nil; - union_type->Union.maybe = ut->maybe; - if (union_type->Union.no_nil) { + union_type->Union.kind = ut->kind; + switch (ut->kind) { + case UnionType_no_nil: if (variants.count < 2) { error(ut->align, "A union with #no_nil must have at least 2 variants"); } - } - if (union_type->Union.maybe) { + break; + case UnionType_maybe: if (variants.count != 1) { error(ut->align, "A union with #maybe must have at 1 variant, got %lld", cast(long long)variants.count); } + break; } if (ut->align != nullptr) { @@ -732,20 +763,19 @@ void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *named_type, Ast Ast *ident = nullptr; Ast *init = nullptr; u32 entity_flags = 0; - if (field->kind == Ast_FieldValue) { - ast_node(fv, FieldValue, field); - if (fv->field == nullptr || fv->field->kind != Ast_Ident) { - error(field, "An enum field's name must be an identifier"); - continue; - } - ident = fv->field; - init = fv->value; - } else if (field->kind == Ast_Ident) { - ident = field; - } else { + if (field->kind != Ast_EnumFieldValue) { error(field, "An enum field's name must be an identifier"); continue; } + ident = field->EnumFieldValue.name; + init = field->EnumFieldValue.value; + if (ident == nullptr || ident->kind != Ast_Ident) { + error(field, "An enum field's name must be an identifier"); + continue; + } + CommentGroup *docs = field->EnumFieldValue.docs; + CommentGroup *comment = field->EnumFieldValue.comment; + String name = ident->Ident.token.string; if (init != nullptr) { @@ -803,6 +833,8 @@ void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *named_type, Ast e->flags |= EntityFlag_Visited; e->state = EntityState_Resolved; e->Constant.flags |= entity_flags; + e->Constant.docs = docs; + e->Constant.comment = comment; if (scope_lookup_current(ctx->scope, name) != nullptr) { error(ident, "'%.*s' is already declared in this enumeration", LIT(name)); @@ -1202,13 +1234,13 @@ bool check_type_specialization_to(CheckerContext *ctx, Type *specialization, Typ } -Type *determine_type_from_polymorphic(CheckerContext *ctx, Type *poly_type, Operand operand) { +Type *determine_type_from_polymorphic(CheckerContext *ctx, Type *poly_type, Operand const &operand) { bool modify_type = !ctx->no_polymorphic_errors; bool show_error = modify_type && !ctx->hide_polymorphic_errors; if (!is_operand_value(operand)) { if (show_error) { gbString pts = type_to_string(poly_type); - gbString ots = type_to_string(operand.type); + gbString ots = type_to_string(operand.type, true); defer (gb_string_free(pts)); defer (gb_string_free(ots)); error(operand.expr, "Cannot determine polymorphic type from parameter: '%s' to '%s'", ots, pts); @@ -1221,7 +1253,7 @@ Type *determine_type_from_polymorphic(CheckerContext *ctx, Type *poly_type, Oper } if (show_error) { gbString pts = type_to_string(poly_type); - gbString ots = type_to_string(operand.type); + gbString ots = type_to_string(operand.type, true); defer (gb_string_free(pts)); defer (gb_string_free(ots)); error(operand.expr, "Cannot determine polymorphic type from parameter: '%s' to '%s'", ots, pts); @@ -1313,7 +1345,9 @@ ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type * param_value.kind = ParameterValue_Constant; param_value.value = o.value; } else { - error(expr, "Default parameter must be a constant, %d", o.mode); + gbString s = expr_to_string(o.expr); + error(expr, "Default parameter must be a constant, got %s", s); + gb_string_free(s); } } } else { @@ -1582,6 +1616,10 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is error(name, "'#any_int' can only be applied to variable fields"); p->flags &= ~FieldFlag_any_int; } + if (p->flags&FieldFlag_by_ptr) { + error(name, "'#by_ptr' can only be applied to variable fields"); + p->flags &= ~FieldFlag_by_ptr; + } param = alloc_entity_type_name(scope, name->Ident.token, type, EntityState_Resolved); param->TypeName.is_type_alias = true; @@ -1658,10 +1696,17 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is if (p->flags&FieldFlag_no_alias) { if (!is_type_pointer(type)) { - error(name, "'#no_alias' can only be applied to fields of pointer type"); + error(name, "'#no_alias' can only be applied pointer typed parameters"); p->flags &= ~FieldFlag_no_alias; // Remove the flag } } + if (p->flags&FieldFlag_by_ptr) { + if (is_type_internally_pointer_like(type)) { + error(name, "'#by_ptr' can only be applied to non-pointer-like parameters"); + p->flags &= ~FieldFlag_by_ptr; // Remove the flag + } + } + if (is_poly_name) { if (p->flags&FieldFlag_no_alias) { error(name, "'#no_alias' can only be applied to non constant values"); @@ -1679,6 +1724,10 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is error(name, "'#const' can only be applied to variable fields"); p->flags &= ~FieldFlag_const; } + if (p->flags&FieldFlag_by_ptr) { + error(name, "'#by_ptr' can only be applied to variable fields"); + p->flags &= ~FieldFlag_by_ptr; + } if (!is_type_constant_type(type) && !is_type_polymorphic(type)) { gbString str = type_to_string(type); @@ -1711,6 +1760,9 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is if (p->flags&FieldFlag_const) { param->flags |= EntityFlag_ConstInput; } + if (p->flags&FieldFlag_by_ptr) { + param->flags |= EntityFlag_ByPtr; + } param->state = EntityState_Resolved; // NOTE(bill): This should have be resolved whilst determining it add_entity(ctx, scope, name, param); @@ -1905,6 +1957,25 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node, c->scope->flags &= ~ScopeFlag_ContextDefined; } + TargetArchKind arch = build_context.metrics.arch; + switch (cc) { + case ProcCC_StdCall: + case ProcCC_FastCall: + if (arch != TargetArch_i386 && arch != TargetArch_amd64) { + error(proc_type_node, "Invalid procedure calling convention \"%s\" for target architecture, expected either i386 or amd64, got %.*s", + proc_calling_convention_strings[cc], LIT(target_arch_names[arch])); + } + break; + case ProcCC_Win64: + case ProcCC_SysV: + if (arch != TargetArch_amd64) { + error(proc_type_node, "Invalid procedure calling convention \"%s\" for target architecture, expected amd64, got %.*s", + proc_calling_convention_strings[cc], LIT(target_arch_names[arch])); + } + break; + } + + bool variadic = false; isize variadic_index = -1; bool success = true; @@ -1918,20 +1989,6 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node, if (params) param_count = params ->Tuple.variables.count; if (results) result_count = results->Tuple.variables.count; - if (param_count > 0) { - for_array(i, params->Tuple.variables) { - Entity *param = params->Tuple.variables[i]; - if (param->kind == Entity_Variable) { - ParameterValue pv = param->Variable.param_value; - if (pv.kind == ParameterValue_Constant && - pv.value.kind == ExactValue_Procedure) { - type->Proc.has_proc_default_values = true; - break; - } - } - } - } - if (result_count > 0) { Entity *first = results->Tuple.variables[0]; type->Proc.has_named_results = first->token.string != ""; @@ -1989,10 +2046,14 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node, if (param_count > 0) { Entity *end = params->Tuple.variables[param_count-1]; if (end->flags&EntityFlag_CVarArg) { - if (cc == ProcCC_StdCall || cc == ProcCC_CDecl) { + switch (cc) { + default: type->Proc.c_vararg = true; - } else { + break; + case ProcCC_Odin: + case ProcCC_Contextless: error(end->token, "Calling convention does not support #c_vararg"); + break; } } } @@ -2128,7 +2189,7 @@ void init_map_entry_type(Type *type) { /* struct { - hash: runtime.Map_Hash, + hash: uintptr, next: int, key: Key, value: Value, @@ -2644,7 +2705,28 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t case_end; case_ast_node(pt, PointerType, e); - *type = alloc_type_pointer(check_type(ctx, pt->type)); + CheckerContext c = *ctx; + c.type_path = new_checker_type_path(); + defer (destroy_checker_type_path(c.type_path)); + + Type *elem = t_invalid; + Operand o = {}; + check_expr_or_type(&c, &o, pt->type); + if (o.mode != Addressing_Invalid && o.mode != Addressing_Type) { + // NOTE(bill): call check_type_expr again to get a consistent error message + begin_error_block(); + elem = check_type_expr(&c, pt->type, nullptr); + if (o.mode == Addressing_Variable) { + gbString s = expr_to_string(pt->type); + error_line("\tSuggestion: ^ is used for pointer types, did you mean '&%s'?\n", s); + gb_string_free(s); + } + end_error_block(); + } else { + elem = o.type; + } + + *type = alloc_type_pointer(elem); set_base_type(named_type, *type); return true; case_end; @@ -2712,29 +2794,30 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t Type *t = alloc_type_enumerated_array(elem, index, bt->Enum.min_value, bt->Enum.max_value, Token_Invalid); - bool is_partial = false; + bool is_sparse = false; if (at->tag != nullptr) { GB_ASSERT(at->tag->kind == Ast_BasicDirective); String name = at->tag->BasicDirective.name.string; - if (name == "partial") { - is_partial = true; + if (name == "sparse") { + is_sparse = true; } else { error(at->tag, "Invalid tag applied to an enumerated array, got #%.*s", LIT(name)); } } - if (!is_partial && t->EnumeratedArray.count > bt->Enum.fields.count) { + if (!is_sparse && t->EnumeratedArray.count > bt->Enum.fields.count) { error(e, "Non-contiguous enumeration used as an index in an enumerated array"); long long ea_count = cast(long long)t->EnumeratedArray.count; long long enum_count = cast(long long)bt->Enum.fields.count; error_line("\tenumerated array length: %lld\n", ea_count); error_line("\tenum field count: %lld\n", enum_count); - error_line("\tSuggestion: prepend #partial to the enumerated array to allow for non-named elements\n"); + error_line("\tSuggestion: prepend #sparse to the enumerated array to allow for non-contiguous elements\n"); if (2*enum_count < ea_count) { error_line("\tWarning: the number of named elements is much smaller than the length of the array, are you sure this is what you want?\n"); - error_line("\t this warning will be removed if #partial is applied\n"); + error_line("\t this warning will be removed if #sparse is applied\n"); } } + t->EnumeratedArray.is_sparse = is_sparse; *type = t; @@ -2753,15 +2836,27 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t if (name == "soa") { *type = make_soa_struct_fixed(ctx, e, at->elem, elem, count, generic_type); } else if (name == "simd") { - if (!is_type_valid_vector_elem(elem)) { + if (!is_type_valid_vector_elem(elem) && !is_type_polymorphic(elem)) { gbString str = type_to_string(elem); - error(at->elem, "Invalid element type for 'intrinsics.simd_vector', expected an integer or float with no specific endianness, got '%s'", str); + error(at->elem, "Invalid element type for #simd, expected an integer, float, or boolean with no specific endianness, got '%s'", str); gb_string_free(str); *type = alloc_type_array(elem, count, generic_type); goto array_end; } - *type = alloc_type_simd_vector(count, elem); + if (generic_type != nullptr) { + // Ignore + } else if (count < 1 || !is_power_of_two(count)) { + error(at->count, "Invalid length for #simd, expected a power of two length, got '%lld'", cast(long long)count); + *type = alloc_type_array(elem, count, generic_type); + goto array_end; + } + + *type = alloc_type_simd_vector(count, elem, generic_type); + + if (count > SIMD_ELEMENT_COUNT_MAX) { + error(at->count, "#simd support a maximum element count of %d, got %lld", SIMD_ELEMENT_COUNT_MAX, cast(long long)count); + } } else { error(at->tag, "Invalid tag applied to array, got #%.*s", LIT(name)); *type = alloc_type_array(elem, count, generic_type); @@ -2984,5 +3079,7 @@ Type *check_type_expr(CheckerContext *ctx, Ast *e, Type *named_type) { } set_base_type(named_type, type); + check_rtti_type_disallowed(e, type, "Use of a type, %s, which has been disallowed"); + return type; } diff --git a/src/checker.cpp b/src/checker.cpp index 55a3892e5..874839ece 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -4,7 +4,7 @@ void check_expr(CheckerContext *c, Operand *operand, Ast *expression); void check_expr_or_type(CheckerContext *c, Operand *operand, Ast *expression, Type *type_hint=nullptr); void add_comparison_procedures_for_fields(CheckerContext *c, Type *t); - +Type *check_type(CheckerContext *ctx, Ast *e); bool is_operand_value(Operand o) { switch (o.mode) { @@ -29,6 +29,23 @@ bool is_operand_undef(Operand o) { return o.mode == Addressing_Value && o.type == t_untyped_undef; } +bool check_rtti_type_disallowed(Token const &token, Type *type, char const *format) { + if (build_context.disallow_rtti && type) { + if (is_type_any(type)) { + gbString t = type_to_string(type); + error(token, format, t); + gb_string_free(t); + return true; + } + } + return false; +} + +bool check_rtti_type_disallowed(Ast *expr, Type *type, char const *format) { + GB_ASSERT(expr != nullptr); + return check_rtti_type_disallowed(ast_token(expr), type, format); +} + void scope_reset(Scope *scope) { if (scope == nullptr) return; @@ -225,8 +242,8 @@ bool decl_info_has_init(DeclInfo *d) { Scope *create_scope(CheckerInfo *info, Scope *parent, isize init_elements_capacity=DEFAULT_SCOPE_CAPACITY) { Scope *s = gb_alloc_item(permanent_allocator(), Scope); s->parent = parent; - string_map_init(&s->elements, permanent_allocator(), init_elements_capacity); - ptr_set_init(&s->imported, permanent_allocator(), 0); + string_map_init(&s->elements, heap_allocator(), init_elements_capacity); + ptr_set_init(&s->imported, heap_allocator(), 0); mutex_init(&s->mutex); if (parent != nullptr && parent != builtin_pkg->scope) { @@ -504,6 +521,7 @@ enum VettedEntityKind { VettedEntity_Unused, VettedEntity_Shadowed, + VettedEntity_Shadowed_And_Unused, }; struct VettedEntity { VettedEntityKind kind; @@ -526,6 +544,28 @@ GB_COMPARE_PROC(vetted_entity_variable_pos_cmp) { return token_pos_cmp(x->token.pos, y->token.pos); } +bool check_vet_shadowing_assignment(Checker *c, Entity *shadowed, Ast *expr) { + Ast *init = unparen_expr(expr); + if (init == nullptr) { + return false; + } + if (init->kind == Ast_Ident) { + // TODO(bill): Which logic is better? Same name or same entity + // bool ignore = init->Ident.token.string == name; + bool ignore = init->Ident.entity == shadowed; + if (ignore) { + return true; + } + } else if (init->kind == Ast_TernaryIfExpr) { + bool x = check_vet_shadowing_assignment(c, shadowed, init->TernaryIfExpr.x); + bool y = check_vet_shadowing_assignment(c, shadowed, init->TernaryIfExpr.y); + if (x || y) { + return true; + } + } + + return false; +} bool check_vet_shadowing(Checker *c, Entity *e, VettedEntity *ve) { @@ -576,17 +616,14 @@ bool check_vet_shadowing(Checker *c, Entity *e, VettedEntity *ve) { } // NOTE(bill): Ignore intentional redeclaration - // x := x; + // x := x // Suggested in issue #637 (2020-05-11) + // Also allow the following + // x := x if cond else y + // x := z if cond else x if ((e->flags & EntityFlag_Using) == 0 && e->kind == Entity_Variable) { - Ast *init = unparen_expr(e->Variable.init_expr); - if (init != nullptr && init->kind == Ast_Ident) { - // TODO(bill): Which logic is better? Same name or same entity - // bool ignore = init->Ident.token.string == name; - bool ignore = init->Ident.entity == shadowed; - if (ignore) { - return false; - } + if (check_vet_shadowing_assignment(c, shadowed, e->Variable.init_expr)) { + return false; } } @@ -625,12 +662,18 @@ void check_scope_usage(Checker *c, Scope *scope) { MUTEX_GUARD_BLOCK(scope->mutex) for_array(i, scope->elements.entries) { Entity *e = scope->elements.entries[i].value; if (e == nullptr) continue; - VettedEntity ve = {}; - if (vet_unused && check_vet_unused(c, e, &ve)) { - array_add(&vetted_entities, ve); - } - if (vet_shadowing && check_vet_shadowing(c, e, &ve)) { - array_add(&vetted_entities, ve); + VettedEntity ve_unused = {}; + VettedEntity ve_shadowed = {}; + bool is_unused = vet_unused && check_vet_unused(c, e, &ve_unused); + bool is_shadowed = vet_shadowing && check_vet_shadowing(c, e, &ve_shadowed); + if (is_unused && is_shadowed) { + VettedEntity ve_both = ve_shadowed; + ve_both.kind = VettedEntity_Shadowed_And_Unused; + array_add(&vetted_entities, ve_both); + } else if (is_unused) { + array_add(&vetted_entities, ve_unused); + } else if (is_shadowed) { + array_add(&vetted_entities, ve_shadowed); } } @@ -642,16 +685,18 @@ void check_scope_usage(Checker *c, Scope *scope) { Entity *other = ve.other; String name = e->token.string; - if (build_context.vet) { + if (ve.kind == VettedEntity_Shadowed_And_Unused) { + error(e->token, "'%.*s' declared but not used, possibly shadows declaration at line %d", LIT(name), other->token.pos.line); + } else if (build_context.vet) { switch (ve.kind) { case VettedEntity_Unused: error(e->token, "'%.*s' declared but not used", LIT(name)); break; case VettedEntity_Shadowed: if (e->flags&EntityFlag_Using) { - error(e->token, "Declaration of '%.*s' from 'using' shadows declaration at line %lld", LIT(name), cast(long long)other->token.pos.line); + error(e->token, "Declaration of '%.*s' from 'using' shadows declaration at line %d", LIT(name), other->token.pos.line); } else { - error(e->token, "Declaration of '%.*s' shadows declaration at line %lld", LIT(name), cast(long long)other->token.pos.line); + error(e->token, "Declaration of '%.*s' shadows declaration at line %d", LIT(name), other->token.pos.line); } break; default: @@ -724,12 +769,25 @@ void add_package_dependency(CheckerContext *c, char const *package_name, char co String n = make_string_c(name); AstPackage *p = get_core_package(&c->checker->info, make_string_c(package_name)); Entity *e = scope_lookup(p->scope, n); - e->flags |= EntityFlag_Used; GB_ASSERT_MSG(e != nullptr, "%s", name); GB_ASSERT(c->decl != nullptr); + e->flags |= EntityFlag_Used; add_dependency(c->info, c->decl, e); } +void try_to_add_package_dependency(CheckerContext *c, char const *package_name, char const *name) { + String n = make_string_c(name); + AstPackage *p = get_core_package(&c->checker->info, make_string_c(package_name)); + Entity *e = scope_lookup(p->scope, n); + if (e == nullptr) { + return; + } + GB_ASSERT(c->decl != nullptr); + e->flags |= EntityFlag_Used; + add_dependency(c->info, c->decl, e); +} + + void add_declaration_dependency(CheckerContext *c, Entity *e) { if (e == nullptr) { return; @@ -790,15 +848,16 @@ struct GlobalEnumValue { i64 value; }; -Slice add_global_enum_type(String const &type_name, GlobalEnumValue *values, isize value_count) { +Slice add_global_enum_type(String const &type_name, GlobalEnumValue *values, isize value_count, Type **enum_type_ = nullptr) { Scope *scope = create_scope(nullptr, builtin_pkg->scope); - Entity *e = alloc_entity_type_name(scope, make_token_ident(type_name), nullptr, EntityState_Resolved); + Entity *entity = alloc_entity_type_name(scope, make_token_ident(type_name), nullptr, EntityState_Resolved); Type *enum_type = alloc_type_enum(); - Type *named_type = alloc_type_named(type_name, enum_type, e); + Type *named_type = alloc_type_named(type_name, enum_type, entity); set_base_type(named_type, enum_type); enum_type->Enum.base_type = t_int; enum_type->Enum.scope = scope; + entity->type = named_type; auto fields = array_make(permanent_allocator(), value_count); for (isize i = 0; i < value_count; i++) { @@ -819,6 +878,9 @@ Slice add_global_enum_type(String const &type_name, GlobalEnumValue *v enum_type->Enum.min_value = &enum_type->Enum.fields[enum_type->Enum.min_value_index]->Constant.value; enum_type->Enum.max_value = &enum_type->Enum.fields[enum_type->Enum.max_value_index]->Constant.value; + + if (enum_type_) *enum_type_ = named_type; + return slice_from_array(fields); } void add_global_enum_constant(Slice const &fields, char const *name, i64 value) { @@ -832,6 +894,17 @@ void add_global_enum_constant(Slice const &fields, char const *name, i GB_PANIC("Unfound enum value for global constant: %s %lld", name, cast(long long)value); } +Type *add_global_type_name(Scope *scope, String const &type_name, Type *backing_type) { + Entity *e = alloc_entity_type_name(scope, make_token_ident(type_name), nullptr, EntityState_Resolved); + Type *named_type = alloc_type_named(type_name, backing_type, e); + e->type = named_type; + set_base_type(named_type, backing_type); + if (scope_insert(scope, e)) { + compiler_error("double declaration of %.*s", LIT(e->token.string)); + } + return named_type; +} + void init_universal(void) { BuildContext *bc = &build_context; @@ -842,7 +915,8 @@ void init_universal(void) { // Types for (isize i = 0; i < gb_count_of(basic_types); i++) { - add_global_type_entity(basic_types[i].Basic.name, &basic_types[i]); + String const &name = basic_types[i].Basic.name; + add_global_type_entity(name, &basic_types[i]); } add_global_type_entity(str_lit("byte"), &basic_types[Basic_u8]); @@ -861,11 +935,44 @@ void init_universal(void) { add_global_bool_constant("false", false); // TODO(bill): Set through flags in the compiler - add_global_string_constant("ODIN_OS", bc->ODIN_OS); - add_global_string_constant("ODIN_ARCH", bc->ODIN_ARCH); add_global_string_constant("ODIN_VENDOR", bc->ODIN_VENDOR); add_global_string_constant("ODIN_VERSION", bc->ODIN_VERSION); add_global_string_constant("ODIN_ROOT", bc->ODIN_ROOT); + + { + GlobalEnumValue values[TargetOs_COUNT] = { + {"Unknown", TargetOs_Invalid}, + {"Windows", TargetOs_windows}, + {"Darwin", TargetOs_darwin}, + {"Linux", TargetOs_linux}, + {"Essence", TargetOs_essence}, + {"FreeBSD", TargetOs_freebsd}, + {"OpenBSD", TargetOs_openbsd}, + {"WASI", TargetOs_wasi}, + {"JS", TargetOs_js}, + {"Freestanding", TargetOs_freestanding}, + }; + + auto fields = add_global_enum_type(str_lit("Odin_OS_Type"), values, gb_count_of(values)); + add_global_enum_constant(fields, "ODIN_OS", bc->metrics.os); + add_global_string_constant("ODIN_OS_STRING", target_os_names[bc->metrics.os]); + } + + { + GlobalEnumValue values[TargetArch_COUNT] = { + {"Unknown", TargetArch_Invalid}, + {"amd64", TargetArch_amd64}, + {"i386", TargetArch_i386}, + {"arm32", TargetArch_arm32}, + {"arm64", TargetArch_arm64}, + {"wasm32", TargetArch_wasm32}, + {"wasm64", TargetArch_wasm64}, + }; + + auto fields = add_global_enum_type(str_lit("Odin_Arch_Type"), values, gb_count_of(values)); + add_global_enum_constant(fields, "ODIN_ARCH", bc->metrics.arch); + add_global_string_constant("ODIN_ARCH_STRING", target_arch_names[bc->metrics.arch]); + } { GlobalEnumValue values[BuildMode_COUNT] = { @@ -880,7 +987,6 @@ void init_universal(void) { add_global_enum_constant(fields, "ODIN_BUILD_MODE", bc->build_mode); } - add_global_string_constant("ODIN_ENDIAN_STRING", target_endian_names[target_endians[bc->metrics.arch]]); { GlobalEnumValue values[TargetEndian_COUNT] = { {"Unknown", TargetEndian_Invalid}, @@ -891,6 +997,32 @@ void init_universal(void) { auto fields = add_global_enum_type(str_lit("Odin_Endian_Type"), values, gb_count_of(values)); add_global_enum_constant(fields, "ODIN_ENDIAN", target_endians[bc->metrics.arch]); + add_global_string_constant("ODIN_ENDIAN_STRING", target_endian_names[target_endians[bc->metrics.arch]]); + } + + { + GlobalEnumValue values[ErrorPosStyle_COUNT] = { + {"Default", ErrorPosStyle_Default}, + {"Unix", ErrorPosStyle_Unix}, + }; + + auto fields = add_global_enum_type(str_lit("Odin_Error_Pos_Style_Type"), values, gb_count_of(values)); + add_global_enum_constant(fields, "ODIN_ERROR_POS_STYLE", build_context.ODIN_ERROR_POS_STYLE); + } + + { + GlobalEnumValue values[OdinAtomicMemoryOrder_COUNT] = { + {OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_relaxed], OdinAtomicMemoryOrder_relaxed}, + {OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_consume], OdinAtomicMemoryOrder_consume}, + {OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_acquire], OdinAtomicMemoryOrder_acquire}, + {OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_release], OdinAtomicMemoryOrder_release}, + {OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_acq_rel], OdinAtomicMemoryOrder_acq_rel}, + {OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_seq_cst], OdinAtomicMemoryOrder_seq_cst}, + }; + + add_global_enum_type(str_lit("Atomic_Memory_Order"), values, gb_count_of(values), &t_atomic_memory_order); + GB_ASSERT(t_atomic_memory_order->kind == Type_Named); + scope_insert(intrinsics_pkg->scope, t_atomic_memory_order->Named.type_name); } @@ -902,6 +1034,8 @@ void init_universal(void) { add_global_bool_constant("ODIN_USE_SEPARATE_MODULES", bc->use_separate_modules); add_global_bool_constant("ODIN_TEST", bc->command_kind == Command_test); add_global_bool_constant("ODIN_NO_ENTRY_POINT", bc->no_entry_point); + add_global_bool_constant("ODIN_FOREIGN_ERROR_PROCEDURES", bc->ODIN_FOREIGN_ERROR_PROCEDURES); + add_global_bool_constant("ODIN_DISALLOW_RTTI", bc->disallow_rtti); // Builtin Procedures @@ -966,6 +1100,17 @@ void init_universal(void) { t_f64_ptr = alloc_type_pointer(t_f64); t_u8_slice = alloc_type_slice(t_u8); t_string_slice = alloc_type_slice(t_string); + + // intrinsics types for objective-c stuff + { + t_objc_object = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_object"), alloc_type_struct()); + t_objc_selector = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_selector"), alloc_type_struct()); + t_objc_class = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_class"), alloc_type_struct()); + + t_objc_id = alloc_type_pointer(t_objc_object); + t_objc_SEL = alloc_type_pointer(t_objc_selector); + t_objc_Class = alloc_type_pointer(t_objc_class); + } } @@ -1022,6 +1167,9 @@ void init_checker_info(CheckerInfo *i) { semaphore_init(&i->collect_semaphore); mpmc_init(&i->intrinsics_entry_point_usage, a, 1<<10); // just waste some memory here, even if it probably never used + + mutex_init(&i->objc_types_mutex); + map_init(&i->objc_msgSend_types, a); } void destroy_checker_info(CheckerInfo *i) { @@ -1054,6 +1202,9 @@ void destroy_checker_info(CheckerInfo *i) { mutex_destroy(&i->type_and_value_mutex); mutex_destroy(&i->identifier_uses_mutex); mutex_destroy(&i->foreign_mutex); + + mutex_destroy(&i->objc_types_mutex); + map_destroy(&i->objc_msgSend_types); } CheckerContext make_checker_context(Checker *c) { @@ -1577,6 +1728,10 @@ void add_implicit_entity(CheckerContext *c, Ast *clause, Entity *e) { void add_type_info_type(CheckerContext *c, Type *t) { void add_type_info_type_internal(CheckerContext *c, Type *t); + if (build_context.disallow_rtti) { + return; + } + mutex_lock(&c->info->type_info_mutex); add_type_info_type_internal(c, t); mutex_unlock(&c->info->type_info_mutex); @@ -1618,9 +1773,6 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) { // NOTE(bill): map entries grow linearly and in order ti_index = c->info->type_info_types.count; array_add(&c->info->type_info_types, t); - if (t->kind == Type_Named && t->Named.name == "A") { - gb_printf_err("HERE!\n"); - } } map_set(&c->checker->info.type_info_map, t, ti_index); @@ -2092,21 +2244,25 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) { ptr_set_init(&c->info.minimum_dependency_set, heap_allocator(), min_dep_set_cap); ptr_set_init(&c->info.minimum_dependency_type_info_set, heap_allocator()); - String required_runtime_entities[] = { +#define FORCE_ADD_RUNTIME_ENTITIES(condition, ...) do { \ + if (condition) { \ + String entities[] = {__VA_ARGS__}; \ + for (isize i = 0; i < gb_count_of(entities); i++) { \ + force_add_dependency_entity(c, c->info.runtime_package->scope, entities[i]); \ + } \ + } \ +} while (0) + + // required runtime entities + FORCE_ADD_RUNTIME_ENTITIES(true, // Odin types - str_lit("Type_Info"), str_lit("Source_Code_Location"), str_lit("Context"), str_lit("Allocator"), str_lit("Logger"), - // Global variables - str_lit("args__"), - str_lit("type_table"), - // Odin internal procedures str_lit("__init_context"), - str_lit("__type_info_of"), str_lit("cstring_to_string"), str_lit("_cleanup_runtime"), @@ -2139,35 +2295,36 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) { // WASM Specific str_lit("__ashlti3"), str_lit("__multi3"), - }; - for (isize i = 0; i < gb_count_of(required_runtime_entities); i++) { - force_add_dependency_entity(c, c->info.runtime_package->scope, required_runtime_entities[i]); - } + ); - if (build_context.no_crt) { - String required_no_crt_entities[] = { - // NOTE(bill): Only if these exist - str_lit("_tls_index"), - str_lit("_fltused"), - }; - for (isize i = 0; i < gb_count_of(required_no_crt_entities); i++) { - force_add_dependency_entity(c, c->info.runtime_package->scope, required_no_crt_entities[i]); - } - } + FORCE_ADD_RUNTIME_ENTITIES(!build_context.disallow_rtti, + // Odin types + str_lit("Type_Info"), - if (!build_context.no_bounds_check) { - String bounds_check_entities[] = { - // Bounds checking related procedures - str_lit("bounds_check_error"), - str_lit("matrix_bounds_check_error"), - str_lit("slice_expr_error_hi"), - str_lit("slice_expr_error_lo_hi"), - str_lit("multi_pointer_slice_expr_error"), - }; - for (isize i = 0; i < gb_count_of(bounds_check_entities); i++) { - force_add_dependency_entity(c, c->info.runtime_package->scope, bounds_check_entities[i]); - } - } + // Global variables + str_lit("type_table"), + str_lit("__type_info_of"), + ); + + FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_entry_point, + // Global variables + str_lit("args__"), + ); + + FORCE_ADD_RUNTIME_ENTITIES((build_context.no_crt && !is_arch_wasm()), + // NOTE(bill): Only if these exist + str_lit("_tls_index"), + str_lit("_fltused"), + ); + + FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_bounds_check, + // Bounds checking related procedures + str_lit("bounds_check_error"), + str_lit("matrix_bounds_check_error"), + str_lit("slice_expr_error_hi"), + str_lit("slice_expr_error_lo_hi"), + str_lit("multi_pointer_slice_expr_error"), + ); for_array(i, c->info.definitions) { Entity *e = c->info.definitions[i]; @@ -2289,6 +2446,8 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) { start->flags |= EntityFlag_Used; add_dependency_to_set(c, start); } + +#undef FORCE_ADD_RUNTIME_ENTITIES } bool is_entity_a_dependency(Entity *e) { @@ -2537,6 +2696,15 @@ Array proc_group_entities(CheckerContext *c, Operand o) { return procs; } +Array proc_group_entities_cloned(CheckerContext *c, Operand o) { + auto entities = proc_group_entities(c, o); + if (entities.count == 0) { + return {}; + } + return array_clone(permanent_allocator(), entities); +} + + void init_core_type_info(Checker *c) { @@ -2696,6 +2864,14 @@ ExactValue check_decl_attribute_value(CheckerContext *c, Ast *value) { return ev; } +Type *check_decl_attribute_type(CheckerContext *c, Ast *value) { + if (value != nullptr) { + return check_type(c, value); + } + return nullptr; +} + + #define ATTRIBUTE_USER_TAG_NAME "tag" @@ -2995,6 +3171,58 @@ DECL_ATTRIBUTE_PROC(proc_decl_attribute) { error(elem, "Expected a string for '%.*s'", LIT(name)); } return true; + } else if (name == "objc_name") { + ExactValue ev = check_decl_attribute_value(c, value); + if (ev.kind == ExactValue_String) { + if (string_is_valid_identifier(ev.value_string)) { + ac->objc_name = ev.value_string; + } else { + error(elem, "Invalid identifier for '%.*s', got '%.*s'", LIT(name), LIT(ev.value_string)); + } + } else { + error(elem, "Expected a string value for '%.*s'", LIT(name)); + } + return true; + } else if (name == "objc_is_class_method") { + ExactValue ev = check_decl_attribute_value(c, value); + if (ev.kind == ExactValue_Bool) { + ac->objc_is_class_method = ev.value_bool; + } else { + error(elem, "Expected a boolean value for '%.*s'", LIT(name)); + } + return true; + } else if (name == "objc_type") { + if (value == nullptr) { + error(elem, "Expected a type for '%.*s'", LIT(name)); + } else { + Type *objc_type = check_type(c, value); + if (objc_type != nullptr) { + if (!has_type_got_objc_class_attribute(objc_type)) { + gbString t = type_to_string(objc_type); + error(value, "'%.*s' expected a named type with the attribute @(obj_class=), got type %s", LIT(name), t); + gb_string_free(t); + } else { + ac->objc_type = objc_type; + } + } + } + return true; + } else if (name == "require_target_feature") { + ExactValue ev = check_decl_attribute_value(c, value); + if (ev.kind == ExactValue_String) { + ac->require_target_feature = ev.value_string; + } else { + error(elem, "Expected a string value for '%.*s'", LIT(name)); + } + return true; + } else if (name == "enable_target_feature") { + ExactValue ev = check_decl_attribute_value(c, value); + if (ev.kind == ExactValue_String) { + ac->enable_target_feature = ev.value_string; + } else { + error(elem, "Expected a string value for '%.*s'", LIT(name)); + } + return true; } return false; } @@ -3145,6 +3373,14 @@ DECL_ATTRIBUTE_PROC(type_decl_attribute) { } else if (name == "private") { // NOTE(bill): Handled elsewhere `check_collect_value_decl` return true; + } else if (name == "objc_class") { + ExactValue ev = check_decl_attribute_value(c, value); + if (ev.kind != ExactValue_String || ev.value_string == "") { + error(elem, "Expected a non-empty string value for '%.*s'", LIT(name)); + } else { + ac->objc_class = ev.value_string; + } + return true; } return false; } @@ -3460,9 +3696,12 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) { if (entity_visibility_kind == EntityVisiblity_Public && (c->scope->flags&ScopeFlag_File) && - c->scope->file && - (c->scope->file->flags & AstFile_IsPrivate)) { - entity_visibility_kind = EntityVisiblity_PrivateToPackage; + c->scope->file) { + if (c->scope->file->flags & AstFile_IsPrivateFile) { + entity_visibility_kind = EntityVisiblity_PrivateToFile; + } else if (c->scope->file->flags & AstFile_IsPrivatePkg) { + entity_visibility_kind = EntityVisiblity_PrivateToPackage; + } } if (entity_visibility_kind != EntityVisiblity_Public && !(c->scope->flags&ScopeFlag_File)) { @@ -3553,9 +3792,6 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) { if (is_ast_type(init)) { e = alloc_entity_type_name(d->scope, token, nullptr); - // if (vd->type != nullptr) { - // error(name, "A type declaration cannot have an type parameter"); - // } } else if (init->kind == Ast_ProcLit) { if (c->scope->flags&ScopeFlag_Type) { error(name, "Procedure declarations are not allowed within a struct"); @@ -3658,6 +3894,59 @@ void check_add_foreign_block_decl(CheckerContext *ctx, Ast *decl) { check_collect_entities(&c, block->stmts); } +bool correct_single_type_alias(CheckerContext *c, Entity *e) { + if (e->kind == Entity_Constant) { + DeclInfo *d = e->decl_info; + if (d != nullptr && d->init_expr != nullptr) { + Ast *init = d->init_expr; + Entity *alias_of = check_entity_from_ident_or_selector(c, init, true); + if (alias_of != nullptr && alias_of->kind == Entity_TypeName) { + e->kind = Entity_TypeName; + return true; + } + } + } + return false; +} + +bool correct_type_alias_in_scope_backwards(CheckerContext *c, Scope *s) { + isize n = s->elements.entries.count; + bool correction = false; + for (isize i = n-1; i >= 0; i--) { + correction |= correct_single_type_alias(c, s->elements.entries[i].value); + } + return correction; +} +bool correct_type_alias_in_scope_forwards(CheckerContext *c, Scope *s) { + isize n = s->elements.entries.count; + bool correction = false; + for (isize i = 0; i < n; i++) { + correction |= correct_single_type_alias(c, s->elements.entries[i].value); + } + return correction; +} + + +void correct_type_aliases_in_scope(CheckerContext *c, Scope *s) { + // NOTE(bill, 2022-02-04): This is used to solve the problem caused by type aliases + // of type aliases being "confused" as constants + // + // A :: C + // B :: A + // C :: struct {b: ^B} + // + // See @TypeAliasingProblem for more information + for (;;) { + bool corrections = false; + corrections |= correct_type_alias_in_scope_backwards(c, s); + corrections |= correct_type_alias_in_scope_forwards(c, s); + if (!corrections) { + return; + } + } +} + + // NOTE(bill): If file_scopes == nullptr, this will act like a local scope void check_collect_entities(CheckerContext *c, Slice const &nodes) { AstFile *curr_file = nullptr; @@ -3729,6 +4018,7 @@ void check_collect_entities(CheckerContext *c, Slice const &nodes) { } } + // correct_type_aliases(c); // NOTE(bill): 'when' stmts need to be handled after the other as the condition may refer to something // declared after this stmt in source @@ -4082,25 +4372,21 @@ void check_add_import_decl(CheckerContext *ctx, Ast *decl) { } String import_name = path_to_entity_name(id->import_name.string, id->fullpath, false); + if (is_blank_ident(import_name)) { + force_use = true; + } // NOTE(bill, 2019-05-19): If the directory path is not a valid entity name, force the user to assign a custom one // if (import_name.len == 0 || import_name == "_") { // import_name = scope->pkg->name; // } - if (import_name.len == 0 || is_blank_ident(import_name)) { - if (id->is_using) { - // TODO(bill): Should this be a warning? - } else { - if (id->import_name.string == "") { - String invalid_name = id->fullpath; - invalid_name = get_invalid_import_name(invalid_name); + if (import_name.len == 0) { + String invalid_name = id->fullpath; + invalid_name = get_invalid_import_name(invalid_name); - error(id->token, "Import name %.*s, is not a valid identifier. Perhaps you want to reference the package by a different name like this: import \"%.*s\" ", LIT(invalid_name), LIT(invalid_name)); - } else { - error(token, "Import name, %.*s, cannot be use as an import name as it is not a valid identifier", LIT(id->import_name.string)); - } - } + error(id->token, "Import name %.*s, is not a valid identifier. Perhaps you want to reference the package by a different name like this: import \"%.*s\" ", LIT(invalid_name), LIT(invalid_name)); + error(token, "Import name, %.*s, cannot be use as an import name as it is not a valid identifier", LIT(id->import_name.string)); } else { GB_ASSERT(id->import_name.pos.line != 0); id->import_name.string = import_name; @@ -4109,38 +4395,11 @@ void check_add_import_decl(CheckerContext *ctx, Ast *decl) { scope); add_entity(ctx, parent_scope, nullptr, e); - if (force_use || id->is_using) { + if (force_use) { add_entity_use(ctx, nullptr, e); } } - if (id->is_using) { - if (parent_scope->flags & ScopeFlag_Global) { - error(id->import_name, "built-in package imports cannot use using"); - return; - } - - // NOTE(bill): Add imported entities to this file's scope - for_array(elem_index, scope->elements.entries) { - String name = scope->elements.entries[elem_index].key.string; - Entity *e = scope->elements.entries[elem_index].value; - if (e->scope == parent_scope) continue; - - if (is_entity_exported(e, true)) { - Entity *found = scope_lookup_current(parent_scope, name); - if (found != nullptr) { - // NOTE(bill): - // Date: 2019-03-17 - // The order has to be the other way around as `using` adds the entity into the that - // file scope otherwise the error would be the wrong way around - redeclaration_error(name, found, e); - } else { - add_entity_with_name(ctx, parent_scope, e->identifier, e, name); - } - } - } - } - scope->flags |= ScopeFlag_HasBeenImported; } @@ -4159,6 +4418,14 @@ DECL_ATTRIBUTE_PROC(foreign_import_decl_attribute) { } ac->require_declaration = true; return true; + } else if (name == "priority_index") { + ExactValue ev = check_decl_attribute_value(c, value); + if (ev.kind != ExactValue_Integer) { + error(elem, "Expected an integer value for '%.*s'", LIT(name)); + } else { + ac->foreign_import_priority_index = exact_value_to_i64(ev); + } + return true; } return false; } @@ -4215,6 +4482,9 @@ void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) { mpmc_enqueue(&ctx->info->required_foreign_imports_through_force_queue, e); add_entity_use(ctx, nullptr, e); } + if (ac.foreign_import_priority_index != 0) { + e->LibraryName.priority_index = ac.foreign_import_priority_index; + } if (has_asm_extension(fullpath)) { if (build_context.metrics.arch != TargetArch_amd64 || @@ -4374,10 +4644,11 @@ bool collect_file_decls(CheckerContext *ctx, Slice const &decls) { for_array(i, decls) { if (collect_file_decl(ctx, decls[i])) { + correct_type_aliases_in_scope(ctx, ctx->scope); return true; } } - + correct_type_aliases_in_scope(ctx, ctx->scope); return false; } @@ -4647,6 +4918,15 @@ void check_import_entities(Checker *c) { } add_untyped_expressions(ctx.info, &untyped); } + + for_array(i, pkg->files) { + AstFile *f = pkg->files[i]; + reset_checker_context(&ctx, f, &untyped); + ctx.collect_delayed_decls = false; + + correct_type_aliases_in_scope(&ctx, pkg->scope); + } + for_array(i, pkg->files) { AstFile *f = pkg->files[i]; reset_checker_context(&ctx, f, &untyped); @@ -4868,6 +5148,9 @@ bool check_proc_info(Checker *c, ProcInfo *pi, UntypedExprInfoMap *untyped, Proc bool bounds_check = (pi->tags & ProcTag_bounds_check) != 0; bool no_bounds_check = (pi->tags & ProcTag_no_bounds_check) != 0; + bool type_assert = (pi->tags & ProcTag_type_assert) != 0; + bool no_type_assert = (pi->tags & ProcTag_no_type_assert) != 0; + if (bounds_check) { ctx.state_flags |= StateFlag_bounds_check; ctx.state_flags &= ~StateFlag_no_bounds_check; @@ -4875,6 +5158,15 @@ bool check_proc_info(Checker *c, ProcInfo *pi, UntypedExprInfoMap *untyped, Proc ctx.state_flags |= StateFlag_no_bounds_check; ctx.state_flags &= ~StateFlag_bounds_check; } + + if (type_assert) { + ctx.state_flags |= StateFlag_type_assert; + ctx.state_flags &= ~StateFlag_no_type_assert; + } else if (no_type_assert) { + ctx.state_flags |= StateFlag_no_type_assert; + ctx.state_flags &= ~StateFlag_type_assert; + } + if (pi->body != nullptr && e != nullptr) { GB_ASSERT((e->flags & EntityFlag_ProcBodyChecked) == 0); } @@ -5288,12 +5580,18 @@ void check_unique_package_names(Checker *c) { string_map_set(&pkgs, key, pkg); continue; } + auto *curr = pkg->files[0]->pkg_decl; + auto *prev = (*found)->files[0]->pkg_decl; + if (curr == prev) { + // NOTE(bill): A false positive was found, ignore it + continue; + } - error(pkg->files[0]->pkg_decl, "Duplicate declaration of 'package %.*s'", LIT(name)); + error(curr, "Duplicate declaration of 'package %.*s'", LIT(name)); error_line("\tA package name must be unique\n" "\tThere is no relation between a package name and the directory that contains it, so they can be completely different\n" "\tA package name is required for link name prefixing to have a consistent ABI\n"); - error((*found)->files[0]->pkg_decl, "found at previous location"); + error(prev, "found at previous location"); } } diff --git a/src/checker.hpp b/src/checker.hpp index 9a8753efd..f11a00532 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -60,6 +60,7 @@ struct BuiltinProc { ExprKind kind; BuiltinProcPkg pkg; bool diverging; + bool ignore_results; // ignores require results handling }; @@ -118,6 +119,15 @@ struct AttributeContext { bool init : 1; bool set_cold : 1; u32 optimization_mode; // ProcedureOptimizationMode + i64 foreign_import_priority_index; + + String objc_class; + String objc_name; + bool objc_is_class_method; + Type * objc_type; + + String require_target_feature; // required by the target micro-architecture + String enable_target_feature; // will be enabled for the procedure only }; AttributeContext make_attribute_context(String link_prefix) { @@ -267,6 +277,17 @@ struct UntypedExprInfo { typedef PtrMap UntypedExprInfoMap; typedef MPMCQueue ProcBodyQueue; +enum ObjcMsgKind : u32 { + ObjcMsg_normal, + ObjcMsg_fpret, + ObjcMsg_fp2ret, + ObjcMsg_stret, +}; +struct ObjcMsgData { + ObjcMsgKind kind; + Type *proc_type; +}; + // CheckerInfo stores all the symbol information for a type-checked program struct CheckerInfo { Checker *checker; @@ -340,7 +361,8 @@ struct CheckerInfo { MPMCQueue intrinsics_entry_point_usage; - + BlockingMutex objc_types_mutex; + PtrMap objc_msgSend_types; }; struct CheckerContext { diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index e8f5174c0..3ea6fcdd5 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -45,7 +45,6 @@ enum BuiltinProcId { // "Intrinsics" BuiltinProc_is_package_imported, - BuiltinProc_simd_vector, BuiltinProc_soa_struct, BuiltinProc_alloca, @@ -66,6 +65,7 @@ enum BuiltinProcId { BuiltinProc_overflow_mul, BuiltinProc_sqrt, + BuiltinProc_fused_mul_add, BuiltinProc_mem_copy, BuiltinProc_mem_copy_non_overlapping, @@ -80,83 +80,39 @@ enum BuiltinProcId { BuiltinProc_unaligned_store, BuiltinProc_unaligned_load, + BuiltinProc_non_temporal_store, + BuiltinProc_non_temporal_load, BuiltinProc_prefetch_read_instruction, BuiltinProc_prefetch_read_data, BuiltinProc_prefetch_write_instruction, BuiltinProc_prefetch_write_data, - BuiltinProc_atomic_fence, - BuiltinProc_atomic_fence_acq, - BuiltinProc_atomic_fence_rel, - BuiltinProc_atomic_fence_acqrel, - + BuiltinProc_atomic_type_is_lock_free, + BuiltinProc_atomic_thread_fence, + BuiltinProc_atomic_signal_fence, BuiltinProc_atomic_store, - BuiltinProc_atomic_store_rel, - BuiltinProc_atomic_store_relaxed, - BuiltinProc_atomic_store_unordered, - + BuiltinProc_atomic_store_explicit, BuiltinProc_atomic_load, - BuiltinProc_atomic_load_acq, - BuiltinProc_atomic_load_relaxed, - BuiltinProc_atomic_load_unordered, - + BuiltinProc_atomic_load_explicit, BuiltinProc_atomic_add, - BuiltinProc_atomic_add_acq, - BuiltinProc_atomic_add_rel, - BuiltinProc_atomic_add_acqrel, - BuiltinProc_atomic_add_relaxed, + BuiltinProc_atomic_add_explicit, BuiltinProc_atomic_sub, - BuiltinProc_atomic_sub_acq, - BuiltinProc_atomic_sub_rel, - BuiltinProc_atomic_sub_acqrel, - BuiltinProc_atomic_sub_relaxed, + BuiltinProc_atomic_sub_explicit, BuiltinProc_atomic_and, - BuiltinProc_atomic_and_acq, - BuiltinProc_atomic_and_rel, - BuiltinProc_atomic_and_acqrel, - BuiltinProc_atomic_and_relaxed, + BuiltinProc_atomic_and_explicit, BuiltinProc_atomic_nand, - BuiltinProc_atomic_nand_acq, - BuiltinProc_atomic_nand_rel, - BuiltinProc_atomic_nand_acqrel, - BuiltinProc_atomic_nand_relaxed, + BuiltinProc_atomic_nand_explicit, BuiltinProc_atomic_or, - BuiltinProc_atomic_or_acq, - BuiltinProc_atomic_or_rel, - BuiltinProc_atomic_or_acqrel, - BuiltinProc_atomic_or_relaxed, + BuiltinProc_atomic_or_explicit, BuiltinProc_atomic_xor, - BuiltinProc_atomic_xor_acq, - BuiltinProc_atomic_xor_rel, - BuiltinProc_atomic_xor_acqrel, - BuiltinProc_atomic_xor_relaxed, - - BuiltinProc_atomic_xchg, - BuiltinProc_atomic_xchg_acq, - BuiltinProc_atomic_xchg_rel, - BuiltinProc_atomic_xchg_acqrel, - BuiltinProc_atomic_xchg_relaxed, - - BuiltinProc_atomic_cxchg, - BuiltinProc_atomic_cxchg_acq, - BuiltinProc_atomic_cxchg_rel, - BuiltinProc_atomic_cxchg_acqrel, - BuiltinProc_atomic_cxchg_relaxed, - BuiltinProc_atomic_cxchg_failrelaxed, - BuiltinProc_atomic_cxchg_failacq, - BuiltinProc_atomic_cxchg_acq_failrelaxed, - BuiltinProc_atomic_cxchg_acqrel_failrelaxed, - - BuiltinProc_atomic_cxchgweak, - BuiltinProc_atomic_cxchgweak_acq, - BuiltinProc_atomic_cxchgweak_rel, - BuiltinProc_atomic_cxchgweak_acqrel, - BuiltinProc_atomic_cxchgweak_relaxed, - BuiltinProc_atomic_cxchgweak_failrelaxed, - BuiltinProc_atomic_cxchgweak_failacq, - BuiltinProc_atomic_cxchgweak_acq_failrelaxed, - BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed, + BuiltinProc_atomic_xor_explicit, + BuiltinProc_atomic_exchange, + BuiltinProc_atomic_exchange_explicit, + BuiltinProc_atomic_compare_exchange_strong, + BuiltinProc_atomic_compare_exchange_strong_explicit, + BuiltinProc_atomic_compare_exchange_weak, + BuiltinProc_atomic_compare_exchange_weak_explicit, BuiltinProc_fixed_point_mul, BuiltinProc_fixed_point_div, @@ -164,10 +120,76 @@ enum BuiltinProcId { BuiltinProc_fixed_point_div_sat, BuiltinProc_expect, + +BuiltinProc__simd_begin, + BuiltinProc_simd_add, + BuiltinProc_simd_sub, + BuiltinProc_simd_mul, + BuiltinProc_simd_div, + BuiltinProc_simd_rem, + BuiltinProc_simd_shl, // Odin logic + BuiltinProc_simd_shr, // Odin logic + BuiltinProc_simd_shl_masked, // C logic + BuiltinProc_simd_shr_masked, // C logic + + BuiltinProc_simd_add_sat, // saturation arithmetic + BuiltinProc_simd_sub_sat, // saturation arithmetic + + BuiltinProc_simd_and, + BuiltinProc_simd_or, + BuiltinProc_simd_xor, + BuiltinProc_simd_and_not, + + BuiltinProc_simd_neg, + BuiltinProc_simd_abs, + + BuiltinProc_simd_min, + BuiltinProc_simd_max, + BuiltinProc_simd_clamp, + + BuiltinProc_simd_lanes_eq, + BuiltinProc_simd_lanes_ne, + BuiltinProc_simd_lanes_lt, + BuiltinProc_simd_lanes_le, + BuiltinProc_simd_lanes_gt, + BuiltinProc_simd_lanes_ge, + + BuiltinProc_simd_extract, + BuiltinProc_simd_replace, + + BuiltinProc_simd_reduce_add_ordered, + BuiltinProc_simd_reduce_mul_ordered, + BuiltinProc_simd_reduce_min, + BuiltinProc_simd_reduce_max, + BuiltinProc_simd_reduce_and, + BuiltinProc_simd_reduce_or, + BuiltinProc_simd_reduce_xor, + + BuiltinProc_simd_shuffle, + BuiltinProc_simd_select, + + BuiltinProc_simd_ceil, + BuiltinProc_simd_floor, + BuiltinProc_simd_trunc, + BuiltinProc_simd_nearest, + + BuiltinProc_simd_to_bits, + + BuiltinProc_simd_lanes_reverse, + BuiltinProc_simd_lanes_rotate_left, + BuiltinProc_simd_lanes_rotate_right, + + + // Platform specific SIMD intrinsics + BuiltinProc_simd_x86__MM_SHUFFLE, +BuiltinProc__simd_end, // Platform specific intrinsics BuiltinProc_syscall, + BuiltinProc_x86_cpuid, + BuiltinProc_x86_xgetbv, + // Constant type tests BuiltinProc__type_begin, @@ -204,6 +226,7 @@ BuiltinProc__type_simple_boolean_begin, BuiltinProc_type_is_named, BuiltinProc_type_is_pointer, + BuiltinProc_type_is_multi_pointer, BuiltinProc_type_is_array, BuiltinProc_type_is_enumerated_array, BuiltinProc_type_is_slice, @@ -213,8 +236,6 @@ BuiltinProc__type_simple_boolean_begin, BuiltinProc_type_is_union, BuiltinProc_type_is_enum, BuiltinProc_type_is_proc, - BuiltinProc_type_is_bit_field, - BuiltinProc_type_is_bit_field_value, BuiltinProc_type_is_bit_set, BuiltinProc_type_is_simd_vector, BuiltinProc_type_is_matrix, @@ -227,6 +248,7 @@ BuiltinProc__type_simple_boolean_begin, BuiltinProc__type_simple_boolean_end, BuiltinProc_type_has_field, + BuiltinProc_type_field_type, BuiltinProc_type_is_specialization_of, @@ -243,6 +265,8 @@ BuiltinProc__type_simple_boolean_end, BuiltinProc_type_polymorphic_record_parameter_count, BuiltinProc_type_polymorphic_record_parameter_value, + BuiltinProc_type_is_subtype_of, + BuiltinProc_type_field_index_of, BuiltinProc_type_equal_proc, @@ -252,6 +276,19 @@ BuiltinProc__type_end, BuiltinProc___entry_point, + BuiltinProc_objc_send, + BuiltinProc_objc_find_selector, + BuiltinProc_objc_find_class, + BuiltinProc_objc_register_selector, + BuiltinProc_objc_register_class, + + BuiltinProc_constant_utf16_cstring, + + BuiltinProc_wasm_memory_grow, + BuiltinProc_wasm_memory_size, + BuiltinProc_wasm_memory_atomic_wait32, + BuiltinProc_wasm_memory_atomic_notify32, + BuiltinProc_COUNT, }; gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { @@ -299,7 +336,6 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { // "Intrinsics" {STR_LIT("is_package_imported"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("simd_vector"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, // Type {STR_LIT("soa_struct"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, // Type {STR_LIT("alloca"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, @@ -321,6 +357,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("overflow_mul"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("sqrt"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("fused_mul_add"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("mem_copy"), 3, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("mem_copy_non_overlapping"), 3, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, @@ -335,84 +372,39 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("unaligned_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("unaligned_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("non_temporal_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("non_temporal_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("prefetch_read_instruction"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("prefetch_read_data"), 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("atomic_fence"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_fence_acq"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_fence_rel"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_fence_acqrel"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - - {STR_LIT("atomic_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_store_rel"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_store_relaxed"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_store_unordered"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - - {STR_LIT("atomic_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_load_acq"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_load_relaxed"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_load_unordered"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - - {STR_LIT("atomic_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_add_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_add_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_add_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_add_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_sub_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_sub_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_sub_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_sub_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_and"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_and_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_and_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_and_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_and_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_nand"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_nand_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_nand_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_nand_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_nand_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_or"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_or_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_or_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_or_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_or_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xor"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xor_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xor_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xor_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xor_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - - {STR_LIT("atomic_xchg"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xchg_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xchg_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xchg_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xchg_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - - {STR_LIT("atomic_cxchg"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_acq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_rel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_acqrel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_relaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_failacq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_acq_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_acqrel_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - - {STR_LIT("atomic_cxchgweak"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_acq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_rel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_acqrel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_relaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_failacq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_acq_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_acqrel_failrelaxed"), 3, 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_signal_fence"), 1, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_store_explicit"), 3, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, + {STR_LIT("atomic_load_explicit"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, + {STR_LIT("atomic_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, + {STR_LIT("atomic_add_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, + {STR_LIT("atomic_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, + {STR_LIT("atomic_sub_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, + {STR_LIT("atomic_and"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, + {STR_LIT("atomic_and_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, + {STR_LIT("atomic_nand"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, + {STR_LIT("atomic_nand_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, + {STR_LIT("atomic_or"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, + {STR_LIT("atomic_or_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, + {STR_LIT("atomic_xor"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, + {STR_LIT("atomic_xor_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, + {STR_LIT("atomic_exchange"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, + {STR_LIT("atomic_exchange_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, + {STR_LIT("atomic_compare_exchange_strong"), 3, 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_explicit"), 5, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, {STR_LIT("fixed_point_mul"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("fixed_point_div"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, @@ -420,8 +412,74 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("fixed_point_div_sat"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("expect"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - - {STR_LIT("syscall"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_mul"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_div"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_rem"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_shl"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_shr"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_shl_masked"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_shr_masked"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("simd_add_sat"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_sub_sat"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("simd_and"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_or"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_xor"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_and_not"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("simd_neg"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("simd_abs"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("simd_min"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_max"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_clamp"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("simd_lanes_eq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_lanes_ne"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_lanes_lt"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_lanes_le"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_lanes_gt"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_lanes_ge"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("simd_extract"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_replace"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("simd_reduce_add_ordered"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_reduce_mul_ordered"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_reduce_min"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_reduce_max"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_reduce_and"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_reduce_or"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_reduce_xor"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("simd_shuffle"), 2, true, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_select"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("simd_ceil") , 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_floor"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_trunc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_nearest"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("simd_to_bits"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("simd_lanes_reverse"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_lanes_rotate_left"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_lanes_rotate_right"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("simd_x86__MM_SHUFFLE"), 4, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + + + {STR_LIT("syscall"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, + {STR_LIT("x86_cpuid"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("x86_xgetbv"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, @@ -457,6 +515,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("type_is_named"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_pointer"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_multi_pointer"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_array"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_enumerated_array"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_slice"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, @@ -466,8 +525,6 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("type_is_union"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_enum"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("type_is_bit_field"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("type_is_bit_field_value"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_bit_set"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_simd_vector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_matrix"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, @@ -479,6 +536,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("type_has_field"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_field_type"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_specialization_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, @@ -495,6 +553,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("type_polymorphic_record_parameter_count"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_polymorphic_record_parameter_value"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_subtype_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_field_index_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_equal_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, @@ -504,4 +564,18 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("__entry_point"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + + {STR_LIT("objc_send"), 3, true, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, + + {STR_LIT("objc_find_selector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("objc_find_class"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("objc_register_selector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, + {STR_LIT("objc_register_class"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, + + {STR_LIT("constant_utf16_cstring"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("wasm_memory_grow"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("wasm_memory_size"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("wasm_memory_atomic_wait32"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("wasm_memory_atomic_notify32"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, }; diff --git a/src/common.cpp b/src/common.cpp index ab2a46118..77caddfe8 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -47,6 +47,13 @@ void debugf(char const *fmt, ...); #include "range_cache.cpp" +bool is_power_of_two(i64 x) { + if (x <= 0) { + return false; + } + return !(x & (x-1)); +} + int isize_cmp(isize x, isize y) { if (x < y) { return -1; @@ -675,262 +682,7 @@ wchar_t **command_line_to_wargv(wchar_t *cmd_line, int *_argc) { #endif - -#if defined(GB_SYSTEM_WINDOWS) - bool path_is_directory(String path) { - gbAllocator a = heap_allocator(); - String16 wstr = string_to_string16(a, path); - defer (gb_free(a, wstr.text)); - - i32 attribs = GetFileAttributesW(wstr.text); - if (attribs < 0) return false; - - return (attribs & FILE_ATTRIBUTE_DIRECTORY) != 0; - } - -#else - bool path_is_directory(String path) { - gbAllocator a = heap_allocator(); - char *copy = cast(char *)copy_string(a, path).text; - defer (gb_free(a, copy)); - - struct stat s; - if (stat(copy, &s) == 0) { - return (s.st_mode & S_IFDIR) != 0; - } - return false; - } -#endif - - -String path_to_full_path(gbAllocator a, String path) { - gbAllocator ha = heap_allocator(); - char *path_c = gb_alloc_str_len(ha, cast(char *)path.text, path.len); - defer (gb_free(ha, path_c)); - - char *fullpath = gb_path_get_full_name(a, path_c); - String res = string_trim_whitespace(make_string_c(fullpath)); -#if defined(GB_SYSTEM_WINDOWS) - for (isize i = 0; i < res.len; i++) { - if (res.text[i] == '\\') { - res.text[i] = '/'; - } - } -#endif - return res; -} - - - -struct FileInfo { - String name; - String fullpath; - i64 size; - bool is_dir; -}; - -enum ReadDirectoryError { - ReadDirectory_None, - - ReadDirectory_InvalidPath, - ReadDirectory_NotExists, - ReadDirectory_Permission, - ReadDirectory_NotDir, - ReadDirectory_Empty, - ReadDirectory_Unknown, - - ReadDirectory_COUNT, -}; - -i64 get_file_size(String path) { - char *c_str = alloc_cstring(heap_allocator(), path); - defer (gb_free(heap_allocator(), c_str)); - - gbFile f = {}; - gbFileError err = gb_file_open(&f, c_str); - defer (gb_file_close(&f)); - if (err != gbFileError_None) { - return -1; - } - return gb_file_size(&f); -} - - -#if defined(GB_SYSTEM_WINDOWS) -ReadDirectoryError read_directory(String path, Array *fi) { - GB_ASSERT(fi != nullptr); - - gbAllocator a = heap_allocator(); - - while (path.len > 0) { - Rune end = path[path.len-1]; - if (end == '/') { - path.len -= 1; - } else if (end == '\\') { - path.len -= 1; - } else { - break; - } - } - - if (path.len == 0) { - return ReadDirectory_InvalidPath; - } - { - char *c_str = alloc_cstring(a, path); - defer (gb_free(a, c_str)); - - gbFile f = {}; - gbFileError file_err = gb_file_open(&f, c_str); - defer (gb_file_close(&f)); - - switch (file_err) { - case gbFileError_Invalid: return ReadDirectory_InvalidPath; - case gbFileError_NotExists: return ReadDirectory_NotExists; - // case gbFileError_Permission: return ReadDirectory_Permission; - } - } - - if (!path_is_directory(path)) { - return ReadDirectory_NotDir; - } - - - char *new_path = gb_alloc_array(a, char, path.len+3); - defer (gb_free(a, new_path)); - - gb_memmove(new_path, path.text, path.len); - gb_memmove(new_path+path.len, "/*", 2); - new_path[path.len+2] = 0; - - String np = make_string(cast(u8 *)new_path, path.len+2); - String16 wstr = string_to_string16(a, np); - defer (gb_free(a, wstr.text)); - - WIN32_FIND_DATAW file_data = {}; - HANDLE find_file = FindFirstFileW(wstr.text, &file_data); - if (find_file == INVALID_HANDLE_VALUE) { - return ReadDirectory_Unknown; - } - defer (FindClose(find_file)); - - array_init(fi, a, 0, 100); - - do { - wchar_t *filename_w = file_data.cFileName; - i64 size = cast(i64)file_data.nFileSizeLow; - size |= (cast(i64)file_data.nFileSizeHigh) << 32; - String name = string16_to_string(a, make_string16_c(filename_w)); - if (name == "." || name == "..") { - gb_free(a, name.text); - continue; - } - - String filepath = {}; - filepath.len = path.len+1+name.len; - filepath.text = gb_alloc_array(a, u8, filepath.len+1); - defer (gb_free(a, filepath.text)); - gb_memmove(filepath.text, path.text, path.len); - gb_memmove(filepath.text+path.len, "/", 1); - gb_memmove(filepath.text+path.len+1, name.text, name.len); - - FileInfo info = {}; - info.name = name; - info.fullpath = path_to_full_path(a, filepath); - info.size = size; - info.is_dir = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; - array_add(fi, info); - } while (FindNextFileW(find_file, &file_data)); - - if (fi->count == 0) { - return ReadDirectory_Empty; - } - - return ReadDirectory_None; -} -#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD) - -#include - -ReadDirectoryError read_directory(String path, Array *fi) { - GB_ASSERT(fi != nullptr); - - gbAllocator a = heap_allocator(); - - char *c_path = alloc_cstring(a, path); - defer (gb_free(a, c_path)); - - DIR *dir = opendir(c_path); - if (!dir) { - switch (errno) { - case ENOENT: - return ReadDirectory_NotExists; - case EACCES: - return ReadDirectory_Permission; - case ENOTDIR: - return ReadDirectory_NotDir; - default: - // ENOMEM: out of memory - // EMFILE: per-process limit on open fds reached - // ENFILE: system-wide limit on total open files reached - return ReadDirectory_Unknown; - } - GB_PANIC("unreachable"); - } - - array_init(fi, a, 0, 100); - - for (;;) { - struct dirent *entry = readdir(dir); - if (entry == nullptr) { - break; - } - - String name = make_string_c(entry->d_name); - if (name == "." || name == "..") { - continue; - } - - String filepath = {}; - filepath.len = path.len+1+name.len; - filepath.text = gb_alloc_array(a, u8, filepath.len+1); - defer (gb_free(a, filepath.text)); - gb_memmove(filepath.text, path.text, path.len); - gb_memmove(filepath.text+path.len, "/", 1); - gb_memmove(filepath.text+path.len+1, name.text, name.len); - filepath.text[filepath.len] = 0; - - - struct stat dir_stat = {}; - - if (stat((char *)filepath.text, &dir_stat)) { - continue; - } - - if (S_ISDIR(dir_stat.st_mode)) { - continue; - } - - i64 size = dir_stat.st_size; - - FileInfo info = {}; - info.name = name; - info.fullpath = path_to_full_path(a, filepath); - info.size = size; - array_add(fi, info); - } - - if (fi->count == 0) { - return ReadDirectory_Empty; - } - - return ReadDirectory_None; -} -#else -#error Implement read_directory -#endif - - +#include "path.cpp" struct LoadedFile { void *handle; @@ -1021,7 +773,7 @@ LoadedFileError load_file_32(char const *fullpath, LoadedFile *memory_mapped_fil #endif } - gbFileContents fc = gb_file_read_contents(heap_allocator(), true, fullpath); + gbFileContents fc = gb_file_read_contents(permanent_allocator(), true, fullpath); if (fc.size > I32_MAX) { err = LoadedFile_FileTooLarge; diff --git a/src/common_memory.cpp b/src/common_memory.cpp index 096c35b5c..953462077 100644 --- a/src/common_memory.cpp +++ b/src/common_memory.cpp @@ -139,6 +139,7 @@ struct PlatformMemoryBlock { }; +gb_global std::atomic global_platform_memory_total_usage; gb_global PlatformMemoryBlock global_platform_memory_block_sentinel; PlatformMemoryBlock *platform_virtual_memory_alloc(isize total_size); @@ -158,10 +159,17 @@ void platform_virtual_memory_protect(void *memory, isize size); PlatformMemoryBlock *platform_virtual_memory_alloc(isize total_size) { PlatformMemoryBlock *pmblock = (PlatformMemoryBlock *)VirtualAlloc(0, total_size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); - GB_ASSERT_MSG(pmblock != nullptr, "Out of Virtual Memory, oh no..."); + if (pmblock == nullptr) { + gb_printf_err("Out of Virtual memory, oh no...\n"); + gb_printf_err("Requested: %lld bytes\n", cast(long long)total_size); + gb_printf_err("Total Usage: %lld bytes\n", cast(long long)global_platform_memory_total_usage); + GB_ASSERT_MSG(pmblock != nullptr, "Out of Virtual Memory, oh no..."); + } + global_platform_memory_total_usage += total_size; return pmblock; } void platform_virtual_memory_free(PlatformMemoryBlock *block) { + global_platform_memory_total_usage -= block->total_size; GB_ASSERT(VirtualFree(block, 0, MEM_RELEASE)); } void platform_virtual_memory_protect(void *memory, isize size) { @@ -180,11 +188,18 @@ void platform_virtual_memory_protect(void *memory, isize size); PlatformMemoryBlock *platform_virtual_memory_alloc(isize total_size) { PlatformMemoryBlock *pmblock = (PlatformMemoryBlock *)mmap(nullptr, total_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); - GB_ASSERT_MSG(pmblock != nullptr, "Out of Virtual Memory, oh no..."); + if (pmblock == nullptr) { + gb_printf_err("Out of Virtual memory, oh no...\n"); + gb_printf_err("Requested: %lld bytes\n", cast(long long)total_size); + gb_printf_err("Total Usage: %lld bytes\n", cast(long long)global_platform_memory_total_usage); + GB_ASSERT_MSG(pmblock != nullptr, "Out of Virtual Memory, oh no..."); + } + global_platform_memory_total_usage += total_size; return pmblock; } void platform_virtual_memory_free(PlatformMemoryBlock *block) { isize size = block->total_size; + global_platform_memory_total_usage -= size; munmap(block, size); } void platform_virtual_memory_protect(void *memory, isize size) { diff --git a/src/docs_format.cpp b/src/docs_format.cpp index 39f2e307c..ee32d0e05 100644 --- a/src/docs_format.cpp +++ b/src/docs_format.cpp @@ -15,7 +15,7 @@ struct OdinDocVersionType { #define OdinDocVersionType_Major 0 #define OdinDocVersionType_Minor 2 -#define OdinDocVersionType_Patch 3 +#define OdinDocVersionType_Patch 4 struct OdinDocHeaderBase { u8 magic[8]; @@ -99,6 +99,7 @@ enum OdinDocTypeFlag_Union : u32 { OdinDocTypeFlag_Union_polymorphic = 1<<0, OdinDocTypeFlag_Union_no_nil = 1<<1, OdinDocTypeFlag_Union_maybe = 1<<2, + OdinDocTypeFlag_Union_shared_nil = 1<<3, }; enum OdinDocTypeFlag_Proc : u32 { @@ -154,6 +155,7 @@ enum OdinDocEntityKind : u32 { OdinDocEntity_ProcGroup = 5, OdinDocEntity_ImportName = 6, OdinDocEntity_LibraryName = 7, + OdinDocEntity_Builtin = 8, }; enum OdinDocEntityFlag : u64 { @@ -170,6 +172,9 @@ enum OdinDocEntityFlag : u64 { OdinDocEntityFlag_Type_Alias = 1ull<<20, + OdinDocEntityFlag_Builtin_Pkg_Builtin = 1ull<<30, + OdinDocEntityFlag_Builtin_Pkg_Intrinsics = 1ull<<31, + OdinDocEntityFlag_Var_Thread_Local = 1ull<<40, OdinDocEntityFlag_Var_Static = 1ull<<41, @@ -201,15 +206,21 @@ enum OdinDocPkgFlags : u32 { OdinDocPkgFlag_Init = 1<<2, }; +struct OdinDocScopeEntry { + OdinDocString name; + OdinDocEntityIndex entity; +}; + struct OdinDocPkg { OdinDocString fullpath; OdinDocString name; u32 flags; OdinDocString docs; - OdinDocArray files; - OdinDocArray entities; + OdinDocArray files; + OdinDocArray entries; }; + struct OdinDocHeader { OdinDocHeaderBase base; diff --git a/src/docs_writer.cpp b/src/docs_writer.cpp index 825ca113f..2f531a45c 100644 --- a/src/docs_writer.cpp +++ b/src/docs_writer.cpp @@ -619,9 +619,10 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) { case Type_Union: doc_type.kind = OdinDocType_Union; if (type->Union.is_polymorphic) { doc_type.flags |= OdinDocTypeFlag_Union_polymorphic; } - if (type->Union.no_nil) { doc_type.flags |= OdinDocTypeFlag_Union_no_nil; } - if (type->Union.maybe) { doc_type.flags |= OdinDocTypeFlag_Union_maybe; } - + switch (type->Union.kind) { + case UnionType_no_nil: doc_type.flags |= OdinDocTypeFlag_Union_no_nil; break; + case UnionType_shared_nil: doc_type.flags |= OdinDocTypeFlag_Union_shared_nil; break; + } { auto variants = array_make(heap_allocator(), type->Union.variants.count); defer (array_free(&variants)); @@ -683,40 +684,7 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) { types[1] = odin_doc_type(w, type->Proc.results); doc_type.types = odin_write_slice(w, types, gb_count_of(types)); - String calling_convention = {}; - switch (type->Proc.calling_convention) { - case ProcCC_Invalid: - // no need - break; - case ProcCC_Odin: - if (default_calling_convention() != ProcCC_Odin) { - calling_convention = str_lit("odin"); - } - break; - case ProcCC_Contextless: - if (default_calling_convention() != ProcCC_Contextless) { - calling_convention = str_lit("contextless"); - } - break; - case ProcCC_CDecl: - calling_convention = str_lit("cdecl"); - break; - case ProcCC_StdCall: - calling_convention = str_lit("stdcall"); - break; - case ProcCC_FastCall: - calling_convention = str_lit("fastcall"); - break; - case ProcCC_None: - calling_convention = str_lit("none"); - break; - case ProcCC_Naked: - calling_convention = str_lit("naked"); - break; - case ProcCC_InlineAsm: - calling_convention = str_lit("inline-assembly"); - break; - } + String calling_convention = make_string_c(proc_calling_convention_strings[type->Proc.calling_convention]); doc_type.calling_convention = odin_doc_write_string(w, calling_convention); } break; @@ -811,14 +779,17 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) { comment = e->decl_info->comment; docs = e->decl_info->docs; } - if (!comment && e->kind == Entity_Variable) { - comment = e->Variable.comment; - } - if (!docs && e->kind == Entity_Variable) { - docs = e->Variable.docs; + if (e->kind == Entity_Variable) { + if (!comment) { comment = e->Variable.comment; } + if (!docs) { docs = e->Variable.docs; } + } else if (e->kind == Entity_Constant) { + if (!comment) { comment = e->Constant.comment; } + if (!docs) { docs = e->Constant.docs; } } + String name = e->token.string; String link_name = {}; + TokenPos pos = e->token.pos; OdinDocEntityKind kind = OdinDocEntity_Invalid; u64 flags = 0; @@ -833,6 +804,7 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) { case Entity_ProcGroup: kind = OdinDocEntity_ProcGroup; break; case Entity_ImportName: kind = OdinDocEntity_ImportName; break; case Entity_LibraryName: kind = OdinDocEntity_LibraryName; break; + case Entity_Builtin: kind = OdinDocEntity_Builtin; break; } switch (e->kind) { @@ -862,6 +834,23 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) { if (e->Procedure.is_export) { flags |= OdinDocEntityFlag_Export; } link_name = e->Procedure.link_name; break; + case Entity_Builtin: + { + auto bp = builtin_procs[e->Builtin.id]; + pos = {}; + name = bp.name; + switch (bp.pkg) { + case BuiltinProcPkg_builtin: + flags |= OdinDocEntityFlag_Builtin_Pkg_Builtin; + break; + case BuiltinProcPkg_intrinsics: + flags |= OdinDocEntityFlag_Builtin_Pkg_Intrinsics; + break; + default: + GB_PANIC("Unhandled BuiltinProcPkg"); + } + } + break; } if (e->flags & EntityFlag_Param) { @@ -897,8 +886,8 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) { doc_entity.kind = kind; doc_entity.flags = flags; - doc_entity.pos = odin_doc_token_pos_cast(w, e->token.pos); - doc_entity.name = odin_doc_write_string(w, e->token.string); + doc_entity.pos = odin_doc_token_pos_cast(w, pos); + doc_entity.name = odin_doc_write_string(w, name); doc_entity.type = 0; // Set later doc_entity.init_string = init_string; doc_entity.comment = odin_doc_comment_group_string(w, comment); @@ -975,7 +964,7 @@ void odin_doc_update_entities(OdinDocWriter *w) { -OdinDocArray odin_doc_add_pkg_entities(OdinDocWriter *w, AstPackage *pkg) { +OdinDocArray odin_doc_add_pkg_entries(OdinDocWriter *w, AstPackage *pkg) { if (pkg->scope == nullptr) { return {}; } @@ -983,14 +972,14 @@ OdinDocArray odin_doc_add_pkg_entities(OdinDocWriter *w, Ast return {}; } - auto entities = array_make(heap_allocator(), 0, pkg->scope->elements.entries.count); - defer (array_free(&entities)); + auto entries = array_make(heap_allocator(), 0, w->entity_cache.entries.count); + defer (array_free(&entries)); for_array(i, pkg->scope->elements.entries) { + String name = pkg->scope->elements.entries[i].key.string; Entity *e = pkg->scope->elements.entries[i].value; switch (e->kind) { case Entity_Invalid: - case Entity_Builtin: case Entity_Nil: case Entity_Label: continue; @@ -1001,34 +990,27 @@ OdinDocArray odin_doc_add_pkg_entities(OdinDocWriter *w, Ast case Entity_ProcGroup: case Entity_ImportName: case Entity_LibraryName: + case Entity_Builtin: // Fine break; } - array_add(&entities, e); - } - gb_sort_array(entities.data, entities.count, cmp_entities_for_printing); - - auto entity_indices = array_make(heap_allocator(), 0, w->entity_cache.entries.count); - defer (array_free(&entity_indices)); - - for_array(i, entities) { - Entity *e = entities[i]; if (e->pkg != pkg) { continue; } - if (!is_entity_exported(e)) { + if (!is_entity_exported(e, true)) { continue; } if (e->token.string.len == 0) { continue; } - OdinDocEntityIndex doc_entity_index = 0; - doc_entity_index = odin_doc_add_entity(w, e); - array_add(&entity_indices, doc_entity_index); + OdinDocScopeEntry entry = {}; + entry.name = odin_doc_write_string(w, name); + entry.entity = odin_doc_add_entity(w, e); + array_add(&entries, entry); } - return odin_write_slice(w, entity_indices.data, entity_indices.count); + return odin_write_slice(w, entries.data, entries.count); } @@ -1096,7 +1078,7 @@ void odin_doc_write_docs(OdinDocWriter *w) { } doc_pkg.files = odin_write_slice(w, file_indices.data, file_indices.count); - doc_pkg.entities = odin_doc_add_pkg_entities(w, pkg); + doc_pkg.entries = odin_doc_add_pkg_entries(w, pkg); if (dst) { *dst = doc_pkg; diff --git a/src/entity.cpp b/src/entity.cpp index 0f8bfa456..3d3712328 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -45,9 +45,9 @@ enum EntityFlag : u64 { EntityFlag_NoAlias = 1ull<<9, EntityFlag_TypeField = 1ull<<10, EntityFlag_Value = 1ull<<11, - EntityFlag_Sret = 1ull<<12, - EntityFlag_ByVal = 1ull<<13, - EntityFlag_BitFieldValue = 1ull<<14, + + + EntityFlag_PolyConst = 1ull<<15, EntityFlag_NotExported = 1ull<<16, EntityFlag_ConstInput = 1ull<<17, @@ -74,6 +74,7 @@ enum EntityFlag : u64 { EntityFlag_Test = 1ull<<30, EntityFlag_Init = 1ull<<31, + EntityFlag_Subtype = 1ull<<32, EntityFlag_CustomLinkName = 1ull<<40, EntityFlag_CustomLinkage_Internal = 1ull<<41, @@ -82,10 +83,15 @@ enum EntityFlag : u64 { EntityFlag_CustomLinkage_LinkOnce = 1ull<<44, EntityFlag_Require = 1ull<<50, + EntityFlag_ByPtr = 1ull<<51, // enforce parameter is passed by pointer EntityFlag_Overridden = 1ull<<63, }; +enum : u64 { + EntityFlags_IsSubtype = EntityFlag_Using|EntityFlag_Subtype, +}; + enum EntityState : u32 { EntityState_Unresolved = 0, EntityState_InProgress = 1, @@ -110,6 +116,16 @@ struct ParameterValue { }; }; +bool has_parameter_value(ParameterValue const ¶m_value) { + if (param_value.kind != ParameterValue_Invalid) { + return true; + } + if (param_value.original_ast_expr != nullptr) { + return true; + } + return false; +} + enum EntityConstantFlags : u32 { EntityConstantFlag_ImplicitEnumValue = 1<<0, }; @@ -122,6 +138,28 @@ enum ProcedureOptimizationMode : u32 { ProcedureOptimizationMode_Speed, }; + +BlockingMutex global_type_name_objc_metadata_mutex; + +struct TypeNameObjCMetadataEntry { + String name; + Entity *entity; +}; +struct TypeNameObjCMetadata { + BlockingMutex *mutex; + Array type_entries; + Array value_entries; +}; + +TypeNameObjCMetadata *create_type_name_obj_c_metadata() { + TypeNameObjCMetadata *md = gb_alloc_item(permanent_allocator(), TypeNameObjCMetadata); + md->mutex = gb_alloc_item(permanent_allocator(), BlockingMutex); + mutex_init(md->mutex); + array_init(&md->type_entries, heap_allocator()); + array_init(&md->value_entries, heap_allocator()); + return md; +} + // An Entity is a named "thing" in the language struct Entity { EntityKind kind; @@ -161,6 +199,8 @@ struct Entity { ParameterValue param_value; u32 flags; i32 field_group_index; + CommentGroup *docs; + CommentGroup *comment; } Constant; struct { Ast *init_expr; // only used for some variables within procedure bodies @@ -184,6 +224,8 @@ struct Entity { Type * type_parameter_specialization; String ir_mangled_name; bool is_type_alias; + String objc_class_name; + TypeNameObjCMetadata *objc_metadata; } TypeName; struct { u64 tags; @@ -192,10 +234,12 @@ struct Entity { String link_name; String link_prefix; DeferredProcedure deferred_procedure; - bool is_foreign; - bool is_export; - bool generated_from_polymorphic; ProcedureOptimizationMode optimization_mode; + bool is_foreign : 1; + bool is_export : 1; + bool generated_from_polymorphic : 1; + bool target_feature_disabled : 1; + String target_feature; } Procedure; struct { Array entities; @@ -211,6 +255,7 @@ struct Entity { struct { Slice paths; String name; + i64 priority_index; } LibraryName; i32 Nil; struct { @@ -243,7 +288,7 @@ bool is_entity_exported(Entity *e, bool allow_builtin = false) { if (e->flags & EntityFlag_NotExported) { return false; } - if (e->file != nullptr && (e->file->flags & AstFile_IsPrivate) != 0) { + if (e->file != nullptr && (e->file->flags & (AstFile_IsPrivatePkg|AstFile_IsPrivateFile)) != 0) { return false; } diff --git a/src/error.cpp b/src/error.cpp index b08ff99df..faf4d11fb 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -33,6 +33,10 @@ void init_global_error_collector(void) { } +// temporary +// defined in build_settings.cpp +char *token_pos_to_string(TokenPos const &pos); + bool set_file_path_string(i32 index, String const &path) { bool ok = false; GB_ASSERT(index >= 0); diff --git a/src/exact_value.cpp b/src/exact_value.cpp index fd90278e5..175cb61f6 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -50,9 +50,9 @@ struct ExactValue { union { bool value_bool; String value_string; - BigInt value_integer; // NOTE(bill): This must be an integer and not a pointer + BigInt value_integer; f64 value_float; - i64 value_pointer; + i64 value_pointer; // NOTE(bill): This must be an integer and not a pointer Complex128 *value_complex; Quaternion256 *value_quaternion; Ast * value_compound; @@ -177,7 +177,11 @@ ExactValue exact_value_typeid(Type *type) { ExactValue exact_value_integer_from_string(String const &string) { ExactValue result = {ExactValue_Integer}; - big_int_from_string(&result.value_integer, string); + bool success; + big_int_from_string(&result.value_integer, string, &success); + if (!success) { + result = {ExactValue_Invalid}; + } return result; } @@ -591,6 +595,7 @@ failure: i32 exact_value_order(ExactValue const &v) { switch (v.kind) { case ExactValue_Invalid: + case ExactValue_Compound: return 0; case ExactValue_Bool: case ExactValue_String: @@ -607,8 +612,6 @@ i32 exact_value_order(ExactValue const &v) { return 6; case ExactValue_Procedure: return 7; - // case ExactValue_Compound: - // return 8; default: GB_PANIC("How'd you get here? Invalid Value.kind %d", v.kind); @@ -630,6 +633,9 @@ void match_exact_values(ExactValue *x, ExactValue *y) { case ExactValue_Bool: case ExactValue_String: case ExactValue_Quaternion: + case ExactValue_Pointer: + case ExactValue_Procedure: + case ExactValue_Typeid: return; case ExactValue_Integer: @@ -671,9 +677,6 @@ void match_exact_values(ExactValue *x, ExactValue *y) { return; } break; - - case ExactValue_Procedure: - return; } compiler_error("match_exact_values: How'd you get here? Invalid ExactValueKind %d", x->kind); @@ -932,6 +935,17 @@ bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) { break; } + case ExactValue_Pointer: { + switch (op) { + case Token_CmpEq: return x.value_pointer == y.value_pointer; + case Token_NotEq: return x.value_pointer != y.value_pointer; + case Token_Lt: return x.value_pointer < y.value_pointer; + case Token_LtEq: return x.value_pointer <= y.value_pointer; + case Token_Gt: return x.value_pointer > y.value_pointer; + case Token_GtEq: return x.value_pointer >= y.value_pointer; + } + } + case ExactValue_Typeid: switch (op) { case Token_CmpEq: return are_types_identical(x.value_typeid, y.value_typeid); diff --git a/src/gb/gb.h b/src/gb/gb.h index d9bf09436..d09c7618b 100644 --- a/src/gb/gb.h +++ b/src/gb/gb.h @@ -79,6 +79,10 @@ extern "C" { #ifndef GB_SYSTEM_FREEBSD #define GB_SYSTEM_FREEBSD 1 #endif + #elif defined(__OpenBSD__) + #ifndef GB_SYSTEM_OPENBSD + #define GB_SYSTEM_OPENBSD 1 + #endif #else #error This UNIX operating system is not supported #endif @@ -86,6 +90,10 @@ extern "C" { #error This operating system is not supported #endif +#if defined(GB_SYSTEM_OPENBSD) +#include +#endif + #if defined(_MSC_VER) #define GB_COMPILER_MSVC 1 #elif defined(__GNUC__) @@ -199,7 +207,7 @@ extern "C" { #endif #include // NOTE(bill): malloc on linux #include - #if !defined(GB_SYSTEM_OSX) && !defined(__FreeBSD__) + #if !defined(GB_SYSTEM_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__) #include #endif #include @@ -235,6 +243,12 @@ extern "C" { #define sendfile(out, in, offset, count) sendfile(out, in, offset, count, NULL, NULL, 0) #endif +#if defined(GB_SYSTEM_OPENBSD) + #include + #include + #define lseek64 lseek +#endif + #if defined(GB_SYSTEM_UNIX) #include #endif @@ -783,6 +797,13 @@ typedef struct gbAffinity { isize thread_count; isize threads_per_core; } gbAffinity; +#elif defined(GB_SYSTEM_OPENBSD) +typedef struct gbAffinity { + b32 is_accurate; + isize core_count; + isize thread_count; + isize threads_per_core; +} gbAffinity; #else #error TODO(bill): Unknown system #endif @@ -1663,7 +1684,7 @@ GB_DEF gbFileContents gb_file_read_contents(gbAllocator a, b32 zero_terminate, c GB_DEF void gb_file_free_contents(gbFileContents *fc); -// TODO(bill): Should these have different na,es as they do not take in a gbFile * ??? +// TODO(bill): Should these have different names as they do not take in a gbFile * ??? GB_DEF b32 gb_file_exists (char const *filepath); GB_DEF gbFileTime gb_file_last_write_time(char const *filepath); GB_DEF b32 gb_file_copy (char const *existing_filename, char const *new_filename, b32 fail_if_exists); @@ -3678,6 +3699,30 @@ b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) { return true; } +isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core) { + GB_ASSERT(0 <= core && core < a->core_count); + return a->threads_per_core; +} + +#elif defined(GB_SYSTEM_OPENBSD) +#include + +void gb_affinity_init(gbAffinity *a) { + a->core_count = sysconf(_SC_NPROCESSORS_ONLN); + a->threads_per_core = 1; + a->is_accurate = a->core_count > 0; + a->core_count = a->is_accurate ? a->core_count : 1; + a->thread_count = a->core_count; +} + +void gb_affinity_destroy(gbAffinity *a) { + gb_unused(a); +} + +b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) { + return true; +} + isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core) { GB_ASSERT(0 <= core && core < a->core_count); return a->threads_per_core; @@ -6025,7 +6070,7 @@ gbFileTime gb_file_last_write_time(char const *filepath) { gb_inline b32 gb_file_copy(char const *existing_filename, char const *new_filename, b32 fail_if_exists) { #if defined(GB_SYSTEM_OSX) return copyfile(existing_filename, new_filename, NULL, COPYFILE_DATA) == 0; -#else +#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_FREEBSD) isize size; int existing_fd = open(existing_filename, O_RDONLY, 0); int new_fd = open(new_filename, O_WRONLY|O_CREAT, 0666); @@ -6041,6 +6086,49 @@ gb_inline b32 gb_file_copy(char const *existing_filename, char const *new_filena close(new_fd); close(existing_fd); + return size == stat_existing.st_size; +#else + int new_flags = O_WRONLY | O_CREAT; + if (fail_if_exists) { + new_flags |= O_EXCL; + } + int existing_fd = open(existing_filename, O_RDONLY, 0); + int new_fd = open(new_filename, new_flags, 0666); + + struct stat stat_existing; + if (fstat(existing_fd, &stat_existing) == -1) { + return 0; + } + + size_t bsize = stat_existing.st_blksize > BUFSIZ ? stat_existing.st_blksize : BUFSIZ; + char *buf = (char *)malloc(bsize); + if (buf == NULL) { + close(new_fd); + close(existing_fd); + return 0; + } + + isize size = 0; + ssize_t nread, nwrite, offset; + while ((nread = read(existing_fd, buf, bsize)) != -1 && nread != 0) { + for (offset = 0; nread; nread -= nwrite, offset += nwrite) { + if ((nwrite = write(new_fd, buf + offset, nread)) == -1 || nwrite == 0) { + free(buf); + close(new_fd); + close(existing_fd); + return 0; + } + size += nwrite; + } + } + + free(buf); + close(new_fd); + close(existing_fd); + + if (nread == -1) { + return 0; + } return size == stat_existing.st_size; #endif } @@ -6093,6 +6181,7 @@ gbFileContents gb_file_read_contents(gbAllocator a, b32 zero_terminate, char con } void gb_file_free_contents(gbFileContents *fc) { + if (fc == NULL || fc->size == 0) return; GB_ASSERT_NOT_NULL(fc->data); gb_free(fc->allocator, fc->data); fc->data = NULL; @@ -6188,20 +6277,44 @@ char *gb_path_get_full_name(gbAllocator a, char const *path) { #else char *p, *result, *fullpath = NULL; isize len; - p = realpath(path, NULL); - fullpath = p; - if (p == NULL) { - // NOTE(bill): File does not exist - fullpath = cast(char *)path; + fullpath = realpath(path, NULL); + + if (fullpath == NULL) { + // NOTE(Jeroen): Path doesn't exist. + if (gb_strlen(path) > 0 && path[0] == '/') { + // But it is an absolute path, so return as-is. + + fullpath = (char *)path; + len = gb_strlen(fullpath) + 1; + result = gb_alloc_array(a, char, len + 1); + + gb_memmove(result, fullpath, len); + result[len] = 0; + + } else { + // Appears to be a relative path, so construct an absolute one relative to . + char cwd[4096]; + getcwd(&cwd[0], 4096); + + isize path_len = gb_strlen(path); + isize cwd_len = gb_strlen(cwd); + len = cwd_len + 1 + path_len + 1; + result = gb_alloc_array(a, char, len); + + gb_memmove(result, (void *)cwd, cwd_len); + result[cwd_len] = '/'; + + gb_memmove(result + cwd_len + 1, (void *)path, gb_strlen(path)); + result[len] = 0; + + } + } else { + len = gb_strlen(fullpath) + 1; + result = gb_alloc_array(a, char, len + 1); + gb_memmove(result, fullpath, len); + result[len] = 0; + free(fullpath); } - - len = gb_strlen(fullpath); - - result = gb_alloc_array(a, char, len + 1); - gb_memmove(result, fullpath, len); - result[len] = 0; - free(p); - return result; #endif } diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index 310df6639..b22a839b3 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -233,7 +233,7 @@ i64 lb_sizeof(LLVMTypeRef type) { i64 elem_size = lb_sizeof(elem); i64 count = LLVMGetVectorSize(type); i64 size = count * elem_size; - return gb_clamp(next_pow2(size), 1, build_context.max_align); + return next_pow2(size); } } @@ -516,6 +516,10 @@ namespace lbAbiAmd64SysV { bool is_register(LLVMTypeRef type) { LLVMTypeKind kind = LLVMGetTypeKind(type); + i64 sz = lb_sizeof(type); + if (sz == 0) { + return false; + } switch (kind) { case LLVMIntegerTypeKind: case LLVMHalfTypeKind: @@ -797,16 +801,23 @@ namespace lbAbiAmd64SysV { i64 elem_sz = lb_sizeof(elem); LLVMTypeKind elem_kind = LLVMGetTypeKind(elem); RegClass reg = RegClass_NoClass; + unsigned elem_width = LLVMGetIntTypeWidth(elem); switch (elem_kind) { case LLVMIntegerTypeKind: case LLVMHalfTypeKind: - switch (LLVMGetIntTypeWidth(elem)) { - case 8: reg = RegClass_SSEInt8; - case 16: reg = RegClass_SSEInt16; - case 32: reg = RegClass_SSEInt32; - case 64: reg = RegClass_SSEInt64; + switch (elem_width) { + case 8: reg = RegClass_SSEInt8; break; + case 16: reg = RegClass_SSEInt16; break; + case 32: reg = RegClass_SSEInt32; break; + case 64: reg = RegClass_SSEInt64; break; default: - GB_PANIC("Unhandled integer width for vector type"); + if (elem_width > 64) { + for (i64 i = 0; i < len; i++) { + classify_with(elem, cls, ix, off + i*elem_sz); + } + break; + } + GB_PANIC("Unhandled integer width for vector type %u", elem_width); } break; case LLVMFloatTypeKind: @@ -1052,10 +1063,18 @@ namespace lbAbiArm64 { } } -namespace lbAbiWasm32 { +namespace lbAbiWasm { + /* + NOTE(bill): All of this is custom since there is not an "official" + ABI definition for WASM, especially for Odin. + The approach taken optimizes for passing things in multiple + registers/arguments if possible rather than by pointer. + */ Array compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count); lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined); + enum {MAX_DIRECT_STRUCT_SIZE = 32}; + LB_ABI_INFO(abi_info) { lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType); ft->ctx = c; @@ -1083,7 +1102,7 @@ namespace lbAbiWasm32 { return lb_arg_type_direct(type, nullptr, nullptr, attr); } - bool is_struct_valid_elem_type(LLVMTypeRef type) { + bool is_basic_register_type(LLVMTypeRef type) { switch (LLVMGetTypeKind(type)) { case LLVMHalfTypeKind: case LLVMFloatTypeKind: @@ -1095,7 +1114,33 @@ namespace lbAbiWasm32 { } return false; } - + + bool type_can_be_direct(LLVMTypeRef type) { + LLVMTypeKind kind = LLVMGetTypeKind(type); + i64 sz = lb_sizeof(type); + if (sz == 0) { + return false; + } + if (sz <= MAX_DIRECT_STRUCT_SIZE) { + if (kind == LLVMArrayTypeKind) { + if (is_basic_register_type(LLVMGetElementType(type))) { + return true; + } + } else if (kind == LLVMStructTypeKind) { + unsigned count = LLVMCountStructElementTypes(type); + for (unsigned i = 0; i < count; i++) { + LLVMTypeRef elem = LLVMStructGetTypeAtIndex(type, i); + if (!is_basic_register_type(elem)) { + return false; + } + + } + return true; + } + } + return false; + } + lbArgType is_struct(LLVMContextRef c, LLVMTypeRef type) { LLVMTypeKind kind = LLVMGetTypeKind(type); GB_ASSERT(kind == LLVMArrayTypeKind || kind == LLVMStructTypeKind); @@ -1104,29 +1149,9 @@ namespace lbAbiWasm32 { if (sz == 0) { return lb_arg_type_ignore(type); } - if (sz <= 16) { - if (kind == LLVMArrayTypeKind) { - LLVMTypeRef elem = LLVMGetElementType(type); - if (is_struct_valid_elem_type(elem)) { - return lb_arg_type_direct(type); - } - } else if (kind == LLVMStructTypeKind) { - bool can_be_direct = true; - unsigned count = LLVMCountStructElementTypes(type); - for (unsigned i = 0; i < count; i++) { - LLVMTypeRef elem = LLVMStructGetTypeAtIndex(type, i); - if (!is_struct_valid_elem_type(elem)) { - can_be_direct = false; - break; - } - - } - if (can_be_direct) { - return lb_arg_type_direct(type); - } - } + if (type_can_be_direct(type)) { + return lb_arg_type_direct(type); } - return lb_arg_type_indirect(type, nullptr); } @@ -1150,6 +1175,10 @@ namespace lbAbiWasm32 { if (!return_is_defined) { return lb_arg_type_direct(LLVMVoidTypeInContext(c)); } else if (lb_is_type_kind(return_type, LLVMStructTypeKind) || lb_is_type_kind(return_type, LLVMArrayTypeKind)) { + if (type_can_be_direct(return_type)) { + return lb_arg_type_direct(return_type); + } + i64 sz = lb_sizeof(return_type); switch (sz) { case 1: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 8), nullptr, nullptr); @@ -1164,6 +1193,88 @@ namespace lbAbiWasm32 { } } +namespace lbAbiArm32 { + Array compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, ProcCallingConvention calling_convention); + lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined); + + LB_ABI_INFO(abi_info) { + lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType); + ft->ctx = c; + ft->args = compute_arg_types(c, arg_types, arg_count, calling_convention); + ft->ret = compute_return_type(c, return_type, return_is_defined); + ft->calling_convention = calling_convention; + return ft; + } + + bool is_register(LLVMTypeRef type, bool is_return) { + LLVMTypeKind kind = LLVMGetTypeKind(type); + switch (kind) { + case LLVMHalfTypeKind: + case LLVMFloatTypeKind: + case LLVMDoubleTypeKind: + return true; + case LLVMIntegerTypeKind: + return lb_sizeof(type) <= 8; + case LLVMFunctionTypeKind: + return true; + case LLVMPointerTypeKind: + return true; + case LLVMVectorTypeKind: + return true; + } + return false; + } + + lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type, bool is_return) { + LLVMAttributeRef attr = nullptr; + LLVMTypeRef i1 = LLVMInt1TypeInContext(c); + if (type == i1) { + attr = lb_create_enum_attribute(c, "zeroext"); + } + return lb_arg_type_direct(type, nullptr, nullptr, attr); + } + + Array compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, ProcCallingConvention calling_convention) { + auto args = array_make(heap_allocator(), arg_count); + + for (unsigned i = 0; i < arg_count; i++) { + LLVMTypeRef t = arg_types[i]; + if (is_register(t, false)) { + args[i] = non_struct(c, t, false); + } else { + i64 sz = lb_sizeof(t); + i64 a = lb_alignof(t); + if (is_calling_convention_odin(calling_convention) && sz > 8) { + // Minor change to improve performance using the Odin calling conventions + args[i] = lb_arg_type_indirect(t, nullptr); + } else if (a <= 4) { + unsigned n = cast(unsigned)((sz + 3) / 4); + args[i] = lb_arg_type_direct(LLVMArrayType(LLVMIntTypeInContext(c, 32), n)); + } else { + unsigned n = cast(unsigned)((sz + 7) / 8); + args[i] = lb_arg_type_direct(LLVMArrayType(LLVMIntTypeInContext(c, 64), n)); + } + } + } + return args; + } + + lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined) { + if (!return_is_defined) { + return lb_arg_type_direct(LLVMVoidTypeInContext(c)); + } else if (!is_register(return_type, true)) { + switch (lb_sizeof(return_type)) { + case 1: return lb_arg_type_direct(LLVMIntTypeInContext(c, 8), return_type, nullptr, nullptr); + case 2: return lb_arg_type_direct(LLVMIntTypeInContext(c, 16), return_type, nullptr, nullptr); + case 3: case 4: return lb_arg_type_direct(LLVMIntTypeInContext(c, 32), return_type, nullptr, nullptr); + } + LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", return_type); + return lb_arg_type_indirect(return_type, attr); + } + return non_struct(c, return_type, true); + } +}; + LB_ABI_INFO(lb_get_abi_info) { switch (calling_convention) { @@ -1184,27 +1295,32 @@ LB_ABI_INFO(lb_get_abi_info) { ft->calling_convention = calling_convention; return ft; } + case ProcCC_Win64: + GB_ASSERT(build_context.metrics.arch == TargetArch_amd64); + return lbAbiAmd64Win64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); + case ProcCC_SysV: + GB_ASSERT(build_context.metrics.arch == TargetArch_amd64); + return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); } switch (build_context.metrics.arch) { case TargetArch_amd64: - if (build_context.metrics.os == TargetOs_windows) { + if (build_context.metrics.os == TargetOs_windows || build_context.metrics.abi == TargetABI_Win64) { return lbAbiAmd64Win64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); + } else if (build_context.metrics.abi == TargetABI_SysV) { + return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); } else { return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); } case TargetArch_i386: return lbAbi386::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); + case TargetArch_arm32: + return lbAbiArm32::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); case TargetArch_arm64: return lbAbiArm64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); case TargetArch_wasm32: - // TODO(bill): implement wasm32's ABI correct - // NOTE(bill): this ABI is only an issue for WASI compatibility - return lbAbiWasm32::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); case TargetArch_wasm64: - // TODO(bill): implement wasm64's ABI correct - // NOTE(bill): this ABI is only an issue for WASI compatibility - return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); + return lbAbiWasm::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); } GB_PANIC("Unsupported ABI"); diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 304effb7f..cf7389ec1 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -29,29 +29,53 @@ void lb_add_foreign_library_path(lbModule *m, Entity *e) { GB_ASSERT(e->kind == Entity_LibraryName); GB_ASSERT(e->flags & EntityFlag_Used); - for_array(i, e->LibraryName.paths) { - String library_path = e->LibraryName.paths[i]; - if (library_path.len == 0) { - continue; - } + mutex_lock(&m->gen->foreign_mutex); + if (!ptr_set_update(&m->gen->foreign_libraries_set, e)) { + array_add(&m->gen->foreign_libraries, e); + } + mutex_unlock(&m->gen->foreign_mutex); +} - bool ok = true; - for_array(path_index, m->foreign_library_paths) { - String path = m->foreign_library_paths[path_index]; - #if defined(GB_SYSTEM_WINDOWS) - if (str_eq_ignore_case(path, library_path)) { - #else - if (str_eq(path, library_path)) { - #endif - ok = false; - break; - } - } +GB_COMPARE_PROC(foreign_library_cmp) { + int cmp = 0; + Entity *x = *(Entity **)a; + Entity *y = *(Entity **)b; + if (x == y) { + return 0; + } + GB_ASSERT(x->kind == Entity_LibraryName); + GB_ASSERT(y->kind == Entity_LibraryName); - if (ok) { - array_add(&m->foreign_library_paths, library_path); + cmp = i64_cmp(x->LibraryName.priority_index, y->LibraryName.priority_index); + if (cmp) { + return cmp; + } + + if (x->pkg != y->pkg) { + isize order_x = x->pkg ? x->pkg->order : 0; + isize order_y = y->pkg ? y->pkg->order : 0; + cmp = isize_cmp(order_x, order_y); + if (cmp) { + return cmp; } } + if (x->file != y->file) { + String fullpath_x = x->file ? x->file->fullpath : (String{}); + String fullpath_y = y->file ? y->file->fullpath : (String{}); + String file_x = filename_from_path(fullpath_x); + String file_y = filename_from_path(fullpath_y); + + cmp = string_compare(file_x, file_y); + if (cmp) { + return cmp; + } + } + + cmp = u64_cmp(x->order_in_src, y->order_in_src); + if (cmp) { + return cmp; + } + return i32_cmp(x->token.pos.offset, y->token.pos.offset); } void lb_set_entity_from_other_modules_linkage_correctly(lbModule *other_module, Entity *e, String const &name) { @@ -624,6 +648,9 @@ struct lbGlobalVariable { }; lbProcedure *lb_create_startup_type_info(lbModule *m) { + if (build_context.disallow_rtti) { + return nullptr; + } LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(m->mod); lb_populate_function_pass_manager(m, default_function_pass_manager, false, build_context.optimization_level); LLVMFinalizeFunctionPassManager(default_function_pass_manager); @@ -652,7 +679,54 @@ lbProcedure *lb_create_startup_type_info(lbModule *m) { return p; } -lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *startup_type_info, Array &global_variables) { // Startup Runtime +lbProcedure *lb_create_objc_names(lbModule *main_module) { + if (build_context.metrics.os != TargetOs_darwin) { + return nullptr; + } + Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_CDecl); + lbProcedure *p = lb_create_dummy_procedure(main_module, str_lit("__$init_objc_names"), proc_type); + p->is_startup = true; + return p; +} + +void lb_finalize_objc_names(lbProcedure *p) { + if (p == nullptr) { + return; + } + lbModule *m = p->module; + + LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(m->mod); + lb_populate_function_pass_manager(m, default_function_pass_manager, false, build_context.optimization_level); + LLVMFinalizeFunctionPassManager(default_function_pass_manager); + + + auto args = array_make(permanent_allocator(), 1); + + LLVMSetLinkage(p->value, LLVMInternalLinkage); + lb_begin_procedure_body(p); + for_array(i, m->objc_classes.entries) { + auto const &entry = m->objc_classes.entries[i]; + String name = entry.key.string; + args[0] = lb_const_value(m, t_cstring, exact_value_string(name)); + lbValue ptr = lb_emit_runtime_call(p, "objc_lookUpClass", args); + lb_addr_store(p, entry.value, ptr); + } + + for_array(i, m->objc_selectors.entries) { + auto const &entry = m->objc_selectors.entries[i]; + String name = entry.key.string; + args[0] = lb_const_value(m, t_cstring, exact_value_string(name)); + lbValue ptr = lb_emit_runtime_call(p, "sel_registerName", args); + lb_addr_store(p, entry.value, ptr); + } + + lb_end_procedure_body(p); + + lb_run_function_pass_manager(default_function_pass_manager, p); + +} + +lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *startup_type_info, lbProcedure *objc_names, Array &global_variables) { // Startup Runtime LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(main_module->mod); lb_populate_function_pass_manager(main_module, default_function_pass_manager, false, build_context.optimization_level); LLVMFinalizeFunctionPassManager(default_function_pass_manager); @@ -664,7 +738,13 @@ lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *start lb_begin_procedure_body(p); - LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(main_module, startup_type_info->type)), startup_type_info->value, nullptr, 0, ""); + if (startup_type_info) { + LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(main_module, startup_type_info->type)), startup_type_info->value, nullptr, 0, ""); + } + + if (objc_names) { + LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(main_module, objc_names->type)), objc_names->value, nullptr, 0, ""); + } for_array(i, global_variables) { auto *var = &global_variables[i]; @@ -911,7 +991,12 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime) } String lb_filepath_ll_for_module(lbModule *m) { - String path = m->gen->output_base; + String path = concatenate3_strings(permanent_allocator(), + build_context.build_paths[BuildPath_Output].basename, + STR_LIT("/"), + build_context.build_paths[BuildPath_Output].name + ); + if (m->pkg) { path = concatenate3_strings(permanent_allocator(), path, STR_LIT("-"), m->pkg->name); } else if (USE_SEPARATE_MODULES) { @@ -922,7 +1007,12 @@ String lb_filepath_ll_for_module(lbModule *m) { return path; } String lb_filepath_obj_for_module(lbModule *m) { - String path = m->gen->output_base; + String path = concatenate3_strings(permanent_allocator(), + build_context.build_paths[BuildPath_Output].basename, + STR_LIT("/"), + build_context.build_paths[BuildPath_Output].name + ); + if (m->pkg) { path = concatenate3_strings(permanent_allocator(), path, STR_LIT("-"), m->pkg->name); } @@ -945,6 +1035,19 @@ String lb_filepath_obj_for_module(lbModule *m) { case TargetOs_essence: ext = STR_LIT(".o"); break; + + case TargetOs_freestanding: + switch (build_context.metrics.abi) { + default: + case TargetABI_Default: + case TargetABI_SysV: + ext = STR_LIT(".o"); + break; + case TargetABI_Win64: + ext = STR_LIT(".obj"); + break; + } + break; } } } @@ -1197,6 +1300,8 @@ void lb_generate_code(lbGenerator *gen) { LLVMCodeModel code_mode = LLVMCodeModelDefault; if (is_arch_wasm()) { code_mode = LLVMCodeModelJITDefault; + } else if (build_context.metrics.os == TargetOs_freestanding) { + code_mode = LLVMCodeModelKernel; } char const *host_cpu_name = LLVMGetHostCPUName(); @@ -1219,10 +1324,18 @@ void lb_generate_code(lbGenerator *gen) { // x86-64-v3: (close to Haswell) AVX, AVX2, BMI1, BMI2, F16C, FMA, LZCNT, MOVBE, XSAVE // x86-64-v4: AVX512F, AVX512BW, AVX512CD, AVX512DQ, AVX512VL if (ODIN_LLVM_MINIMUM_VERSION_12) { - llvm_cpu = "x86-64-v2"; + if (build_context.metrics.os == TargetOs_freestanding) { + llvm_cpu = "x86-64"; + } else { + llvm_cpu = "x86-64-v2"; + } } } + if (build_context.target_features_set.entries.count != 0) { + llvm_features = target_features_set_to_cstring(permanent_allocator(), false); + } + // GB_ASSERT_MSG(LLVMTargetHasAsmBackend(target)); LLVMCodeGenOptLevel code_gen_level = LLVMCodeGenLevelNone; @@ -1244,6 +1357,24 @@ void lb_generate_code(lbGenerator *gen) { reloc_mode = LLVMRelocPIC; } + switch (build_context.reloc_mode) { + case RelocMode_Default: + if (build_context.metrics.os == TargetOs_openbsd) { + // Always use PIC for OpenBSD: it defaults to PIE + reloc_mode = LLVMRelocPIC; + } + break; + case RelocMode_Static: + reloc_mode = LLVMRelocStatic; + break; + case RelocMode_PIC: + reloc_mode = LLVMRelocPIC; + break; + case RelocMode_DynamicNoPIC: + reloc_mode = LLVMRelocDynamicNoPic; + break; + } + for_array(i, gen->modules.entries) { target_machines[i] = LLVMCreateTargetMachine( target, target_triple, llvm_cpu, @@ -1310,7 +1441,7 @@ void lb_generate_code(lbGenerator *gen) { TIME_SECTION("LLVM Global Variables"); - { + if (!build_context.disallow_rtti) { lbModule *m = default_module; { // Add type info data @@ -1417,9 +1548,8 @@ void lb_generate_code(lbGenerator *gen) { if ((e->scope->flags&ScopeFlag_Init) && name == "main") { GB_ASSERT(e == info->entry_point); } - if (e->Procedure.is_export || - (e->Procedure.link_name.len > 0) || - ((e->scope->flags&ScopeFlag_File) && e->Procedure.link_name.len > 0)) { + if (build_context.command_kind == Command_test && + (e->Procedure.is_export || e->Procedure.link_name.len > 0)) { String link_name = e->Procedure.link_name; if (e->pkg->kind == Package_Runtime) { if (link_name == "main" || @@ -1570,8 +1700,10 @@ void lb_generate_code(lbGenerator *gen) { TIME_SECTION("LLVM Runtime Type Information Creation"); lbProcedure *startup_type_info = lb_create_startup_type_info(default_module); + lbProcedure *objc_names = lb_create_objc_names(default_module); + TIME_SECTION("LLVM Runtime Startup Creation (Global Variables)"); - lbProcedure *startup_runtime = lb_create_startup_runtime(default_module, startup_type_info, global_variables); + lbProcedure *startup_runtime = lb_create_startup_runtime(default_module, startup_type_info, objc_names, global_variables); gb_unused(startup_runtime); TIME_SECTION("LLVM Global Procedures and Types"); @@ -1641,6 +1773,11 @@ void lb_generate_code(lbGenerator *gen) { } } + if (build_context.command_kind == Command_test && !already_has_entry_point) { + TIME_SECTION("LLVM main"); + lb_create_main_procedure(default_module, startup_runtime); + } + for_array(j, gen->modules.entries) { lbModule *m = gen->modules.entries[j].value; for_array(i, m->missing_procedures_to_check) { @@ -1650,6 +1787,8 @@ void lb_generate_code(lbGenerator *gen) { } } + lb_finalize_objc_names(objc_names); + if (build_context.ODIN_DEBUG) { TIME_SECTION("LLVM Debug Info Complete Types and Finalize"); for_array(j, gen->modules.entries) { @@ -1662,6 +1801,7 @@ void lb_generate_code(lbGenerator *gen) { } + TIME_SECTION("LLVM Function Pass"); for_array(i, gen->modules.entries) { lbModule *m = gen->modules.entries[i].value; @@ -1806,4 +1946,6 @@ void lb_generate_code(lbGenerator *gen) { } } } + + gb_sort_array(gen->foreign_libraries.data, gen->foreign_libraries.count, foreign_library_cmp); } diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 49f675a49..a09286d0b 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -135,7 +135,6 @@ struct lbModule { u32 nested_type_name_guid; Array procedures_to_generate; - Array foreign_library_paths; lbProcedure *curr_procedure; @@ -144,6 +143,9 @@ struct lbModule { PtrMap debug_values; Array debug_incomplete_types; + + StringMap objc_classes; + StringMap objc_selectors; }; struct lbGenerator { @@ -159,6 +161,10 @@ struct lbGenerator { PtrMap anonymous_proc_lits; + BlockingMutex foreign_mutex; + PtrSet foreign_libraries_set; + Array foreign_libraries; + std::atomic global_array_index; std::atomic global_generated_index; }; @@ -232,6 +238,7 @@ struct lbTargetList { enum lbProcedureFlag : u32 { lbProcedureFlag_WithoutMemcpyPass = 1<<0, + lbProcedureFlag_DebugAllocaCopy = 1<<1, }; struct lbCopyElisionHint { @@ -285,6 +292,9 @@ struct lbProcedure { LLVMMetadataRef debug_info; lbCopyElisionHint copy_elision_hint; + + PtrMap selector_values; + PtrMap selector_addr; }; @@ -292,7 +302,6 @@ struct lbProcedure { bool lb_init_generator(lbGenerator *gen, Checker *c); -void lb_generate_module(lbGenerator *gen); String lb_mangle_name(lbModule *m, Entity *e); String lb_get_entity_name(lbModule *m, Entity *e, String name = {}); @@ -365,7 +374,7 @@ lbContextData *lb_push_context_onto_stack(lbProcedure *p, lbAddr ctx); lbContextData *lb_push_context_onto_stack_from_implicit_parameter(lbProcedure *p); -lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value={}); +lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value={}, Entity **entity_=nullptr); lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e=nullptr, bool zero_init=true, i32 param_index=0, bool force_no_init=false); void lb_add_foreign_library_path(lbModule *m, Entity *e); @@ -455,6 +464,8 @@ void lb_set_entity_from_other_modules_linkage_correctly(lbModule *other_module, lbValue lb_expr_untyped_const_to_typed(lbModule *m, Ast *expr, Type *t); bool lb_is_expr_untyped_const(Ast *expr); +LLVMValueRef llvm_alloca(lbProcedure *p, LLVMTypeRef llvm_type, isize alignment, char const *name = ""); + void lb_mem_zero_ptr(lbProcedure *p, LLVMValueRef ptr, Type *type, unsigned alignment); void lb_emit_init_context(lbProcedure *p, lbAddr addr); @@ -471,7 +482,11 @@ LLVMValueRef llvm_basic_shuffle(lbProcedure *p, LLVMValueRef vector, LLVMValueRe void lb_mem_copy_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue len, bool is_volatile=false); void lb_mem_copy_non_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue len, bool is_volatile=false); +LLVMValueRef lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef ptr, LLVMValueRef len, unsigned alignment, bool is_volatile); +i64 lb_max_zero_init_size(void) { + return cast(i64)(4*build_context.word_size); +} #define LB_STARTUP_RUNTIME_PROC_NAME "__$startup_runtime" #define LB_STARTUP_TYPE_INFO_PROC_NAME "__$startup_type_info" @@ -545,6 +560,10 @@ lbCallingConventionKind const lb_calling_convention_map[ProcCC_MAX] = { lbCallingConvention_C, // ProcCC_None, lbCallingConvention_C, // ProcCC_Naked, lbCallingConvention_C, // ProcCC_InlineAsm, + + lbCallingConvention_Win64, // ProcCC_Win64, + lbCallingConvention_X86_64_SysV, // ProcCC_SysV, + }; enum : LLVMDWARFTypeEncoding { diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index 5862a7add..201932ad9 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -115,8 +115,8 @@ LLVMValueRef llvm_const_cast(LLVMValueRef val, LLVMTypeRef dst) { lbValue lb_const_ptr_cast(lbModule *m, lbValue value, Type *t) { - GB_ASSERT(is_type_pointer(value.type)); - GB_ASSERT(is_type_pointer(t)); + GB_ASSERT(is_type_internally_pointer_like(value.type)); + GB_ASSERT(is_type_internally_pointer_like(t)); GB_ASSERT(lb_is_const(value)); lbValue res = {}; @@ -175,7 +175,7 @@ LLVMValueRef llvm_const_array(LLVMTypeRef elem_type, LLVMValueRef *values, isize } LLVMValueRef llvm_const_slice(lbModule *m, lbValue data, lbValue len) { - GB_ASSERT(is_type_pointer(data.type)); + GB_ASSERT(is_type_pointer(data.type) || is_type_multi_pointer(data.type)); GB_ASSERT(are_types_identical(len.type, t_int)); LLVMValueRef vals[2] = { data.value, @@ -410,12 +410,10 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc // NOTE(bill, 2020-06-08): This is a bit of a hack but a "constant" slice needs // its backing data on the stack lbProcedure *p = m->curr_procedure; - LLVMPositionBuilderAtEnd(p->builder, p->decl_block->block); - LLVMTypeRef llvm_type = lb_type(m, t); - array_data = LLVMBuildAlloca(p->builder, llvm_type, ""); - LLVMSetAlignment(array_data, 16); // TODO(bill): Make this configurable - LLVMPositionBuilderAtEnd(p->builder, p->curr_block->block); + + array_data = llvm_alloca(p, llvm_type, 16); + LLVMBuildStore(p->builder, backing_array.value, array_data); { @@ -495,9 +493,9 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc res.value = data; return res; } else if (is_type_array(type) && - value.kind != ExactValue_Invalid && - value.kind != ExactValue_String && - value.kind != ExactValue_Compound) { + value.kind != ExactValue_Invalid && + value.kind != ExactValue_String && + value.kind != ExactValue_Compound) { i64 count = type->Array.count; Type *elem = type->Array.elem; @@ -513,8 +511,8 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc res.value = llvm_const_array(lb_type(m, elem), elems, cast(unsigned)count); return res; } else if (is_type_matrix(type) && - value.kind != ExactValue_Invalid && - value.kind != ExactValue_Compound) { + value.kind != ExactValue_Invalid && + value.kind != ExactValue_Compound) { i64 row = type->Matrix.row_count; i64 column = type->Matrix.column_count; GB_ASSERT(row == column); @@ -537,6 +535,22 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc res.value = LLVMConstArray(lb_type(m, elem), elems, cast(unsigned)total_elem_count); return res; + } else if (is_type_simd_vector(type) && + value.kind != ExactValue_Invalid && + value.kind != ExactValue_Compound) { + i64 count = type->SimdVector.count; + Type *elem = type->SimdVector.elem; + + lbValue single_elem = lb_const_value(m, elem, value, allow_local); + single_elem.value = llvm_const_cast(single_elem.value, lb_type(m, elem)); + + LLVMValueRef *elems = gb_alloc_array(permanent_allocator(), LLVMValueRef, count); + for (i64 i = 0; i < count; i++) { + elems[i] = single_elem.value; + } + + res.value = LLVMConstVector(elems, cast(unsigned)count); + return res; } switch (value.kind) { @@ -568,7 +582,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc } case ExactValue_Integer: - if (is_type_pointer(type)) { + if (is_type_pointer(type) || is_type_multi_pointer(type)) { LLVMTypeRef t = lb_type(m, original_type); LLVMValueRef i = lb_big_int_to_llvm(m, t_uintptr, &value.value_integer); res.value = LLVMConstIntToPtr(i, t); @@ -819,26 +833,81 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc return lb_const_nil(m, original_type); } GB_ASSERT(elem_type_can_be_constant(elem_type)); - isize total_elem_count = cast(isize)type->SimdVector.count; LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, total_elem_count); - for (isize i = 0; i < elem_count; i++) { - TypeAndValue tav = cl->elems[i]->tav; - GB_ASSERT(tav.mode != Addressing_Invalid); - values[i] = lb_const_value(m, elem_type, tav.value, allow_local).value; - } - LLVMTypeRef et = lb_type(m, elem_type); + if (cl->elems[0]->kind == Ast_FieldValue) { + // TODO(bill): This is O(N*M) and will be quite slow; it should probably be sorted before hand + isize value_index = 0; + for (i64 i = 0; i < total_elem_count; i++) { + bool found = false; - for (isize i = elem_count; i < type->SimdVector.count; i++) { - values[i] = LLVMConstNull(et); - } - for (isize i = 0; i < total_elem_count; i++) { - values[i] = llvm_const_cast(values[i], et); - } + for (isize j = 0; j < elem_count; j++) { + Ast *elem = cl->elems[j]; + ast_node(fv, FieldValue, elem); + if (is_ast_range(fv->field)) { + ast_node(ie, BinaryExpr, fv->field); + TypeAndValue lo_tav = ie->left->tav; + TypeAndValue hi_tav = ie->right->tav; + GB_ASSERT(lo_tav.mode == Addressing_Constant); + GB_ASSERT(hi_tav.mode == Addressing_Constant); - res.value = LLVMConstVector(values, cast(unsigned)total_elem_count); - return res; + TokenKind op = ie->op.kind; + i64 lo = exact_value_to_i64(lo_tav.value); + i64 hi = exact_value_to_i64(hi_tav.value); + if (op != Token_RangeHalf) { + hi += 1; + } + if (lo == i) { + TypeAndValue tav = fv->value->tav; + LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local).value; + for (i64 k = lo; k < hi; k++) { + values[value_index++] = val; + } + + found = true; + i += (hi-lo-1); + break; + } + } else { + TypeAndValue index_tav = fv->field->tav; + GB_ASSERT(index_tav.mode == Addressing_Constant); + i64 index = exact_value_to_i64(index_tav.value); + if (index == i) { + TypeAndValue tav = fv->value->tav; + LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local).value; + values[value_index++] = val; + found = true; + break; + } + } + } + + if (!found) { + values[value_index++] = LLVMConstNull(lb_type(m, elem_type)); + } + } + + res.value = LLVMConstVector(values, cast(unsigned)total_elem_count); + return res; + } else { + for (isize i = 0; i < elem_count; i++) { + TypeAndValue tav = cl->elems[i]->tav; + GB_ASSERT(tav.mode != Addressing_Invalid); + values[i] = lb_const_value(m, elem_type, tav.value, allow_local).value; + } + LLVMTypeRef et = lb_type(m, elem_type); + + for (isize i = elem_count; i < total_elem_count; i++) { + values[i] = LLVMConstNull(et); + } + for (isize i = 0; i < total_elem_count; i++) { + values[i] = llvm_const_cast(values[i], et); + } + + res.value = LLVMConstVector(values, cast(unsigned)total_elem_count); + return res; + } } else if (is_type_struct(type)) { ast_node(cl, CompoundLit, value.value_compound); @@ -956,7 +1025,10 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc return lb_const_nil(m, original_type); } - u64 bits = 0; + BigInt bits = {}; + BigInt one = {}; + big_int_from_u64(&one, 1); + for_array(i, cl->elems) { Ast *e = cl->elems[i]; GB_ASSERT(e->kind != Ast_FieldValue); @@ -968,18 +1040,13 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc GB_ASSERT(tav.value.kind == ExactValue_Integer); i64 v = big_int_to_i64(&tav.value.value_integer); i64 lower = type->BitSet.lower; - bits |= 1ull<debug_builder, cast(unsigned)type->SimdVector.count, 8*cast(unsigned)type_align_of(type), lb_debug_type(m, type->SimdVector.elem), nullptr, 0); + { + LLVMMetadataRef elem = lb_debug_type(m, type->SimdVector.elem); + LLVMMetadataRef subscripts[1] = {}; + subscripts[0] = LLVMDIBuilderGetOrCreateSubrange(m->debug_builder, + 0ll, + type->SimdVector.count + ); + return LLVMDIBuilderCreateVectorType( + m->debug_builder, + 8*cast(unsigned)type_size_of(type), 8*cast(unsigned)type_align_of(type), + elem, subscripts, gb_count_of(subscripts)); + } case Type_RelativePointer: { LLVMMetadataRef base_integer = lb_debug_type(m, type->RelativePointer.base_integer); @@ -958,13 +973,79 @@ void lb_add_debug_local_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, T ); LLVMValueRef storage = ptr; - LLVMValueRef instr = ptr; + LLVMBasicBlockRef block = p->curr_block->block; LLVMMetadataRef llvm_debug_loc = lb_debug_location_from_token_pos(p, token.pos); LLVMMetadataRef llvm_expr = LLVMDIBuilderCreateExpression(m->debug_builder, nullptr, 0); lb_set_llvm_metadata(m, ptr, llvm_expr); - LLVMDIBuilderInsertDeclareBefore(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, instr); + LLVMDIBuilderInsertDeclareAtEnd(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, block); } + +void lb_add_debug_param_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, Token const &token, unsigned arg_number, lbBlock *block) { + if (p->debug_info == nullptr) { + return; + } + if (type == nullptr) { + return; + } + if (type == t_invalid) { + return; + } + if (p->body == nullptr) { + return; + } + + lbModule *m = p->module; + String const &name = token.string; + if (name == "" || name == "_") { + return; + } + + if (lb_get_llvm_metadata(m, ptr) != nullptr) { + // Already been set + return; + } + + + AstFile *file = p->body->file(); + + LLVMMetadataRef llvm_scope = lb_get_current_debug_scope(p); + LLVMMetadataRef llvm_file = lb_get_llvm_metadata(m, file); + GB_ASSERT(llvm_scope != nullptr); + if (llvm_file == nullptr) { + llvm_file = LLVMDIScopeGetFile(llvm_scope); + } + + if (llvm_file == nullptr) { + return; + } + + LLVMDIFlags flags = LLVMDIFlagZero; + LLVMBool always_preserve = build_context.optimization_level == 0; + + LLVMMetadataRef debug_type = lb_debug_type(m, type); + + LLVMMetadataRef var_info = LLVMDIBuilderCreateParameterVariable( + m->debug_builder, llvm_scope, + cast(char const *)name.text, cast(size_t)name.len, + arg_number, + llvm_file, token.pos.line, + debug_type, + always_preserve, flags + ); + + LLVMValueRef storage = ptr; + LLVMMetadataRef llvm_debug_loc = lb_debug_location_from_token_pos(p, token.pos); + LLVMMetadataRef llvm_expr = LLVMDIBuilderCreateExpression(m->debug_builder, nullptr, 0); + lb_set_llvm_metadata(m, ptr, llvm_expr); + + // NOTE(bill, 2022-02-01): For parameter values, you must insert them at the end of the decl block + // The reason is that if the parameter is at index 0 and a pointer, there is not such things as an + // instruction "before" it. + LLVMDIBuilderInsertDbgValueAtEnd(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, block->block); +} + + void lb_add_debug_context_variable(lbProcedure *p, lbAddr const &ctx) { if (!p->debug_info || !p->body) { return; @@ -984,5 +1065,10 @@ void lb_add_debug_context_variable(lbProcedure *p, lbAddr const &ctx) { token.string = str_lit("context"); token.pos = pos; - lb_add_debug_local_variable(p, ctx.addr.value, t_context, token); + LLVMValueRef ptr = ctx.addr.value; + while (LLVMIsABitCastInst(ptr)) { + ptr = LLVMGetOperand(ptr, 0); + } + + lb_add_debug_local_variable(p, ptr, t_context, token); } diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 1f0ed6434..b28470770 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -1,3 +1,4 @@ +lbValue lb_emit_arith_matrix(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type, bool component_wise=false); lbValue lb_emit_logical_binary_expr(lbProcedure *p, TokenKind op, Ast *left, Ast *right, Type *type) { lbModule *m = p->module; @@ -258,7 +259,18 @@ lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x, Type *type) LLVMBuildStore(p->builder, v2, LLVMBuildStructGEP(p->builder, addr.addr.value, 2, "")); LLVMBuildStore(p->builder, v3, LLVMBuildStructGEP(p->builder, addr.addr.value, 3, "")); return lb_addr_load(p, addr); - + } else if (is_type_simd_vector(x.type)) { + Type *elem = base_array_type(x.type); + if (is_type_float(elem)) { + res.value = LLVMBuildFNeg(p->builder, x.value, ""); + } else { + res.value = LLVMBuildNeg(p->builder, x.value, ""); + } + } else if (is_type_matrix(x.type)) { + lbValue zero = {}; + zero.value = LLVMConstNull(lb_type(p->module, type)); + zero.type = type; + return lb_emit_arith_matrix(p, Token_Sub, zero, x, type, true); } else { GB_PANIC("Unhandled type %s", type_to_string(x.type)); } @@ -580,6 +592,27 @@ LLVMValueRef lb_matrix_to_trimmed_vector(lbProcedure *p, lbValue m) { lbValue lb_emit_matrix_tranpose(lbProcedure *p, lbValue m, Type *type) { if (is_type_array(m.type)) { + i32 rank = type_math_rank(m.type); + if (rank == 2) { + lbAddr addr = lb_add_local_generated(p, type, false); + lbValue dst = addr.addr; + lbValue src = m; + i32 n = cast(i32)get_array_type_count(m.type); + i32 m = cast(i32)get_array_type_count(type); + // m.type == [n][m]T + // type == [m][n]T + + for (i32 j = 0; j < m; j++) { + lbValue dst_col = lb_emit_struct_ep(p, dst, j); + for (i32 i = 0; i < n; i++) { + lbValue dst_row = lb_emit_struct_ep(p, dst_col, i); + lbValue src_col = lb_emit_struct_ev(p, src, i); + lbValue src_row = lb_emit_struct_ev(p, src_col, j); + lb_emit_store(p, dst_row, src_row); + } + } + return lb_addr_load(p, addr); + } // no-op m.type = type; return m; @@ -949,7 +982,7 @@ lbValue lb_emit_vector_mul_matrix(lbProcedure *p, lbValue lhs, lbValue rhs, Type -lbValue lb_emit_arith_matrix(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type, bool component_wise=false) { +lbValue lb_emit_arith_matrix(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type, bool component_wise) { GB_ASSERT(is_type_matrix(lhs.type) || is_type_matrix(rhs.type)); @@ -1398,10 +1431,13 @@ lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) { Type *it = bit_set_to_int(rt); left = lb_emit_conv(p, left, it); + if (is_type_different_to_arch_endianness(it)) { + left = lb_emit_byte_swap(p, left, integer_endian_type_to_platform_type(it)); + } - lbValue lower = lb_const_value(p->module, it, exact_value_i64(rt->BitSet.lower)); - lbValue key = lb_emit_arith(p, Token_Sub, left, lower, it); - lbValue bit = lb_emit_arith(p, Token_Shl, lb_const_int(p->module, it, 1), key, it); + lbValue lower = lb_const_value(p->module, left.type, exact_value_i64(rt->BitSet.lower)); + lbValue key = lb_emit_arith(p, Token_Sub, left, lower, left.type); + lbValue bit = lb_emit_arith(p, Token_Shl, lb_const_int(p->module, left.type, 1), key, left.type); bit = lb_emit_conv(p, bit, it); lbValue old_value = lb_emit_transmute(p, right, it); @@ -1799,6 +1835,59 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { return res; } + if (is_type_simd_vector(dst)) { + Type *et = base_array_type(dst); + if (is_type_simd_vector(src)) { + Type *src_elem = core_array_type(src); + Type *dst_elem = core_array_type(dst); + + GB_ASSERT(src->SimdVector.count == dst->SimdVector.count); + + lbValue res = {}; + res.type = t; + if (are_types_identical(src_elem, dst_elem)) { + res.value = value.value; + } else if (is_type_float(src_elem) && is_type_integer(dst_elem)) { + if (is_type_unsigned(dst_elem)) { + res.value = LLVMBuildFPToUI(p->builder, value.value, lb_type(m, t), ""); + } else { + res.value = LLVMBuildFPToSI(p->builder, value.value, lb_type(m, t), ""); + } + } else if (is_type_integer(src_elem) && is_type_float(dst_elem)) { + if (is_type_unsigned(src_elem)) { + res.value = LLVMBuildUIToFP(p->builder, value.value, lb_type(m, t), ""); + } else { + res.value = LLVMBuildSIToFP(p->builder, value.value, lb_type(m, t), ""); + } + } else if ((is_type_integer(src_elem) || is_type_boolean(src_elem)) && is_type_integer(dst_elem)) { + res.value = LLVMBuildIntCast2(p->builder, value.value, lb_type(m, t), !is_type_unsigned(src_elem), ""); + } else if (is_type_float(src_elem) && is_type_float(dst_elem)) { + res.value = LLVMBuildFPCast(p->builder, value.value, lb_type(m, t), ""); + } else if (is_type_integer(src_elem) && is_type_boolean(dst_elem)) { + LLVMValueRef i1vector = LLVMBuildICmp(p->builder, LLVMIntNE, value.value, LLVMConstNull(LLVMTypeOf(value.value)), ""); + res.value = LLVMBuildIntCast2(p->builder, i1vector, lb_type(m, t), !is_type_unsigned(src_elem), ""); + } else { + GB_PANIC("Unhandled simd vector conversion: %s -> %s", type_to_string(src), type_to_string(dst)); + } + return res; + } else { + i64 count = get_array_type_count(dst); + LLVMTypeRef vt = lb_type(m, t); + LLVMTypeRef llvm_u32 = lb_type(m, t_u32); + LLVMValueRef elem = lb_emit_conv(p, value, et).value; + LLVMValueRef vector = LLVMConstNull(vt); + for (i64 i = 0; i < count; i++) { + LLVMValueRef idx = LLVMConstInt(llvm_u32, i, false); + vector = LLVMBuildInsertElement(p->builder, vector, elem, idx, ""); + } + lbValue res = {}; + res.type = t; + res.value = vector; + return res; + } + } + + // Pointer <-> uintptr if (is_type_pointer(src) && is_type_uintptr(dst)) { lbValue res = {}; @@ -1834,6 +1923,15 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { return lb_addr_load(p, parent); } } + if (dst->Union.variants.count == 1) { + Type *vt = dst->Union.variants[0]; + if (internal_check_is_assignable_to(src, vt)) { + value = lb_emit_conv(p, value, vt); + lbAddr parent = lb_add_local_generated(p, t, true); + lb_emit_store_union_variant(p, parent.addr, value, vt); + return lb_addr_load(p, parent); + } + } } // NOTE(bill): This has to be done before 'Pointer <-> Pointer' as it's @@ -2172,6 +2270,21 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri } } + if (is_type_matrix(a) && (op_kind == Token_CmpEq || op_kind == Token_NotEq)) { + Type *tl = base_type(a); + lbValue lhs = lb_address_from_load_or_generate_local(p, left); + lbValue rhs = lb_address_from_load_or_generate_local(p, right); + + + // TODO(bill): Test to see if this is actually faster!!!! + auto args = array_make(permanent_allocator(), 3); + args[0] = lb_emit_conv(p, lhs, t_rawptr); + args[1] = lb_emit_conv(p, rhs, t_rawptr); + args[2] = lb_const_int(p->module, t_int, type_size_of(tl)); + lbValue val = lb_emit_runtime_call(p, "memory_compare", args); + lbValue res = lb_emit_comp(p, op_kind, val, lb_const_nil(p->module, val.type)); + return lb_emit_conv(p, res, t_bool); + } if (is_type_array(a) || is_type_enumerated_array(a)) { Type *tl = base_type(a); lbValue lhs = lb_address_from_load_or_generate_local(p, left); @@ -2461,6 +2574,57 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri case Token_NotEq: pred = LLVMIntNE; break; } res.value = LLVMBuildICmp(p->builder, pred, left.value, right.value, ""); + } else if (is_type_simd_vector(a)) { + LLVMValueRef mask = nullptr; + Type *elem = base_array_type(a); + if (is_type_float(elem)) { + LLVMRealPredicate pred = {}; + switch (op_kind) { + case Token_CmpEq: pred = LLVMRealOEQ; break; + case Token_NotEq: pred = LLVMRealONE; break; + } + mask = LLVMBuildFCmp(p->builder, pred, left.value, right.value, ""); + } else { + LLVMIntPredicate pred = {}; + switch (op_kind) { + case Token_CmpEq: pred = LLVMIntEQ; break; + case Token_NotEq: pred = LLVMIntNE; break; + } + mask = LLVMBuildICmp(p->builder, pred, left.value, right.value, ""); + } + GB_ASSERT_MSG(mask != nullptr, "Unhandled comparison kind %s (%s) %.*s %s (%s)", type_to_string(left.type), type_to_string(base_type(left.type)), LIT(token_strings[op_kind]), type_to_string(right.type), type_to_string(base_type(right.type))); + + /* NOTE(bill, 2022-05-28): + Thanks to Per Vognsen, sign extending to + a vector of the same width as the input vector, bit casting to an integer, + and then comparing against zero is the better option + See: https://lists.llvm.org/pipermail/llvm-dev/2012-September/053046.html + + // Example assuming 128-bit vector + + %1 = <4 x float> ... + %2 = <4 x float> ... + %3 = fcmp oeq <4 x float> %1, %2 + %4 = sext <4 x i1> %3 to <4 x i32> + %5 = bitcast <4 x i32> %4 to i128 + %6 = icmp ne i128 %5, 0 + br i1 %6, label %true1, label %false2 + + This will result in 1 cmpps + 1 ptest + 1 br + (even without SSE4.1, contrary to what the mail list states, because of pmovmskb) + + */ + + unsigned count = cast(unsigned)get_array_type_count(a); + unsigned elem_sz = cast(unsigned)(type_size_of(elem)*8); + LLVMTypeRef mask_type = LLVMVectorType(LLVMIntTypeInContext(p->module->ctx, elem_sz), count); + mask = LLVMBuildSExtOrBitCast(p->builder, mask, mask_type, ""); + + LLVMTypeRef mask_int_type = LLVMIntTypeInContext(p->module->ctx, cast(unsigned)(8*type_size_of(a))); + LLVMValueRef mask_int = LLVMBuildBitCast(p->builder, mask, mask_int_type, ""); + res.value = LLVMBuildICmp(p->builder, LLVMIntNE, mask_int, LLVMConstNull(LLVMTypeOf(mask_int)), ""); + return res; + } else { GB_PANIC("Unhandled comparison kind %s (%s) %.*s %s (%s)", type_to_string(left.type), type_to_string(base_type(left.type)), LIT(token_strings[op_kind]), type_to_string(right.type), type_to_string(base_type(right.type))); } @@ -2768,28 +2932,39 @@ lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) { Type *src_type = type_deref(v.type); Type *dst_type = type; - lbValue src_tag = {}; - lbValue dst_tag = {}; - if (is_type_union_maybe_pointer(src_type)) { - src_tag = lb_emit_comp_against_nil(p, Token_NotEq, v); - dst_tag = lb_const_bool(p->module, t_bool, true); - } else { - src_tag = lb_emit_load(p, lb_emit_union_tag_ptr(p, v)); - dst_tag = lb_const_union_tag(p->module, src_type, dst_type); + + if ((p->state_flags & StateFlag_no_type_assert) == 0) { + lbValue src_tag = {}; + lbValue dst_tag = {}; + if (is_type_union_maybe_pointer(src_type)) { + src_tag = lb_emit_comp_against_nil(p, Token_NotEq, v); + dst_tag = lb_const_bool(p->module, t_bool, true); + } else { + src_tag = lb_emit_load(p, lb_emit_union_tag_ptr(p, v)); + dst_tag = lb_const_union_tag(p->module, src_type, dst_type); + } + + + isize arg_count = 6; + if (build_context.disallow_rtti) { + arg_count = 4; + } + + lbValue ok = lb_emit_comp(p, Token_CmpEq, src_tag, dst_tag); + auto args = array_make(permanent_allocator(), arg_count); + args[0] = ok; + + args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id)); + args[2] = lb_const_int(p->module, t_i32, pos.line); + args[3] = lb_const_int(p->module, t_i32, pos.column); + + if (!build_context.disallow_rtti) { + args[4] = lb_typeid(p->module, src_type); + args[5] = lb_typeid(p->module, dst_type); + } + lb_emit_runtime_call(p, "type_assertion_check", args); } - lbValue ok = lb_emit_comp(p, Token_CmpEq, src_tag, dst_tag); - auto args = array_make(permanent_allocator(), 6); - args[0] = ok; - - args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id)); - args[2] = lb_const_int(p->module, t_i32, pos.line); - args[3] = lb_const_int(p->module, t_i32, pos.column); - - args[4] = lb_typeid(p->module, src_type); - args[5] = lb_typeid(p->module, dst_type); - lb_emit_runtime_call(p, "type_assertion_check", args); - lbValue data_ptr = v; return lb_emit_conv(p, data_ptr, tv.type); } else if (is_type_any(t)) { @@ -2797,23 +2972,25 @@ lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) { if (is_type_pointer(v.type)) { v = lb_emit_load(p, v); } - lbValue data_ptr = lb_emit_struct_ev(p, v, 0); - lbValue any_id = lb_emit_struct_ev(p, v, 1); - lbValue id = lb_typeid(p->module, type); + if ((p->state_flags & StateFlag_no_type_assert) == 0) { + GB_ASSERT(!build_context.disallow_rtti); + lbValue any_id = lb_emit_struct_ev(p, v, 1); - lbValue ok = lb_emit_comp(p, Token_CmpEq, any_id, id); - auto args = array_make(permanent_allocator(), 6); - args[0] = ok; + lbValue id = lb_typeid(p->module, type); + lbValue ok = lb_emit_comp(p, Token_CmpEq, any_id, id); + auto args = array_make(permanent_allocator(), 6); + args[0] = ok; - args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id)); - args[2] = lb_const_int(p->module, t_i32, pos.line); - args[3] = lb_const_int(p->module, t_i32, pos.column); + args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id)); + args[2] = lb_const_int(p->module, t_i32, pos.line); + args[3] = lb_const_int(p->module, t_i32, pos.column); - args[4] = any_id; - args[5] = id; - lb_emit_runtime_call(p, "type_assertion_check", args); + args[4] = any_id; + args[5] = id; + lb_emit_runtime_call(p, "type_assertion_check", args); + } return lb_emit_conv(p, data_ptr, tv.type); } else { @@ -2825,9 +3002,8 @@ lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) { return lb_build_addr_ptr(p, ue->expr); } +lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr); lbValue lb_build_expr(lbProcedure *p, Ast *expr) { - lbModule *m = p->module; - u16 prev_state_flags = p->state_flags; defer (p->state_flags = prev_state_flags); @@ -2843,9 +3019,49 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { out &= ~StateFlag_bounds_check; } + if (in & StateFlag_type_assert) { + out |= StateFlag_type_assert; + out &= ~StateFlag_no_type_assert; + } else if (in & StateFlag_no_type_assert) { + out |= StateFlag_no_type_assert; + out &= ~StateFlag_type_assert; + } + p->state_flags = out; } + + // IMPORTANT NOTE(bill): + // Selector Call Expressions (foo->bar(...)) + // must only evaluate `foo` once as it gets transformed into + // `foo.bar(foo, ...)` + // And if `foo` is a procedure call or something more complex, storing the value + // once is a very good idea + // If a stored value is found, it must be removed from the cache + if (expr->state_flags & StateFlag_SelectorCallExpr) { + lbValue *pp = map_get(&p->selector_values, expr); + if (pp != nullptr) { + lbValue res = *pp; + map_remove(&p->selector_values, expr); + return res; + } + lbAddr *pa = map_get(&p->selector_addr, expr); + if (pa != nullptr) { + lbAddr res = *pa; + map_remove(&p->selector_addr, expr); + return lb_addr_load(p, res); + } + } + lbValue res = lb_build_expr_internal(p, expr); + if (expr->state_flags & StateFlag_SelectorCallExpr) { + map_set(&p->selector_values, expr, res); + } + return res; +} + +lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) { + lbModule *m = p->module; + expr = unparen_expr(expr); TokenPos expr_pos = ast_token(expr).pos; @@ -2864,17 +3080,6 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { return lb_const_value(p->module, type, tv.value); } - #if 0 - LLVMMetadataRef prev_debug_location = nullptr; - if (p->debug_info != nullptr) { - prev_debug_location = LLVMGetCurrentDebugLocation2(p->builder); - LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, expr)); - } - defer (if (prev_debug_location != nullptr) { - LLVMSetCurrentDebugLocation2(p->builder, prev_debug_location); - }); - #endif - switch (expr->kind) { case_ast_node(bl, BasicLit, expr); TokenPos pos = bl->token.pos; @@ -2943,14 +3148,7 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { case_ast_node(se, SelectorCallExpr, expr); GB_ASSERT(se->modified_call); - TypeAndValue tav = type_and_value_of_expr(expr); - GB_ASSERT(tav.mode != Addressing_Invalid); - lbValue res = lb_build_call_expr(p, se->call); - - ast_node(ce, CallExpr, se->call); - ce->sce_temp_data = gb_alloc_copy(permanent_allocator(), &res, gb_size_of(res)); - - return res; + return lb_build_call_expr(p, se->call); case_end; case_ast_node(te, TernaryIfExpr, expr); @@ -2962,23 +3160,31 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { lbBlock *done = lb_create_block(p, "if.done"); // NOTE(bill): Append later lbBlock *else_ = lb_create_block(p, "if.else"); - lbValue cond = lb_build_cond(p, te->cond, then, else_); + lb_build_cond(p, te->cond, then, else_); lb_start_block(p, then); Type *type = default_type(type_of_expr(expr)); + LLVMTypeRef llvm_type = lb_type(p->module, type); incoming_values[0] = lb_emit_conv(p, lb_build_expr(p, te->x), type).value; + if (is_type_internally_pointer_like(type)) { + incoming_values[0] = LLVMBuildBitCast(p->builder, incoming_values[0], llvm_type, ""); + } lb_emit_jump(p, done); lb_start_block(p, else_); incoming_values[1] = lb_emit_conv(p, lb_build_expr(p, te->y), type).value; + if (is_type_internally_pointer_like(type)) { + incoming_values[1] = LLVMBuildBitCast(p->builder, incoming_values[1], llvm_type, ""); + } + lb_emit_jump(p, done); lb_start_block(p, done); lbValue res = {}; - res.value = LLVMBuildPhi(p->builder, lb_type(p->module, type), ""); + res.value = LLVMBuildPhi(p->builder, llvm_type, ""); res.type = type; GB_ASSERT(p->curr_block->preds.count >= 2); @@ -3236,9 +3442,34 @@ lbAddr lb_build_array_swizzle_addr(lbProcedure *p, AstCallExpr *ce, TypeAndValue } +lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr); lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { expr = unparen_expr(expr); + // IMPORTANT NOTE(bill): + // Selector Call Expressions (foo->bar(...)) + // must only evaluate `foo` once as it gets transformed into + // `foo.bar(foo, ...)` + // And if `foo` is a procedure call or something more complex, storing the value + // once is a very good idea + // If a stored value is found, it must be removed from the cache + if (expr->state_flags & StateFlag_SelectorCallExpr) { + lbAddr *pp = map_get(&p->selector_addr, expr); + if (pp != nullptr) { + lbAddr res = *pp; + map_remove(&p->selector_addr, expr); + return res; + } + } + lbAddr addr = lb_build_addr_internal(p, expr); + if (expr->state_flags & StateFlag_SelectorCallExpr) { + map_set(&p->selector_addr, expr, addr); + } + return addr; +} + + +lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) { switch (expr->kind) { case_ast_node(i, Implicit, expr); lbAddr v = {}; @@ -3263,9 +3494,9 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { case_end; case_ast_node(se, SelectorExpr, expr); - Ast *sel = unparen_expr(se->selector); - if (sel->kind == Ast_Ident) { - String selector = sel->Ident.token.string; + Ast *sel_node = unparen_expr(se->selector); + if (sel_node->kind == Ast_Ident) { + String selector = sel_node->Ident.token.string; TypeAndValue tav = type_and_value_of_expr(se->expr); if (tav.mode == Addressing_Invalid) { @@ -3280,7 +3511,12 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { Type *type = base_type(tav.type); if (tav.mode == Addressing_Type) { // Addressing_Type - GB_PANIC("Unreachable"); + Selection sel = lookup_field(tav.type, selector, true); + if (sel.pseudo_field) { + GB_ASSERT(sel.entity->kind == Entity_Procedure); + return lb_addr(lb_find_value_from_entity(p->module, sel.entity)); + } + GB_PANIC("Unreachable %.*s", LIT(selector)); } if (se->swizzle_count > 0) { @@ -3307,6 +3543,11 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { Selection sel = lookup_field(type, selector, false); GB_ASSERT(sel.entity != nullptr); + if (sel.pseudo_field) { + GB_ASSERT(sel.entity->kind == Entity_Procedure); + Entity *e = entity_of_node(sel_node); + return lb_addr(lb_find_value_from_entity(p->module, e)); + } { lbAddr addr = lb_build_addr(p, se->expr); @@ -3370,9 +3611,6 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { case_end; case_ast_node(se, SelectorCallExpr, expr); - GB_ASSERT(se->modified_call); - TypeAndValue tav = type_and_value_of_expr(expr); - GB_ASSERT(tav.mode != Addressing_Invalid); lbValue e = lb_build_expr(p, expr); return lb_addr(lb_address_from_load_or_generate_local(p, e)); case_end; @@ -3460,7 +3698,8 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { GB_ASSERT_MSG(is_type_indexable(t), "%s %s", type_to_string(t), expr_to_string(expr)); if (is_type_map(t)) { - lbValue map_val = lb_build_addr_ptr(p, ie->expr); + lbAddr map_addr = lb_build_addr(p, ie->expr); + lbValue map_val = lb_addr_load(p, map_addr); if (deref) { map_val = lb_emit_load(p, map_val); } @@ -3469,7 +3708,8 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { key = lb_emit_conv(p, key, t->Map.key); Type *result_type = type_of_expr(expr); - return lb_addr_map(map_val, key, t, result_type); + lbValue map_ptr = lb_address_from_load_or_generate_local(p, map_val); + return lb_addr_map(map_ptr, key, t, result_type); } switch (t->kind) { @@ -4531,6 +4771,102 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { break; } + case Type_SimdVector: { + if (cl->elems.count > 0) { + lbValue vector_value = lb_const_value(p->module, type, exact_value_compound(expr)); + defer (lb_addr_store(p, v, vector_value)); + + auto temp_data = array_make(temporary_allocator(), 0, cl->elems.count); + + // NOTE(bill): Separate value, store into their own chunks + for_array(i, cl->elems) { + Ast *elem = cl->elems[i]; + if (elem->kind == Ast_FieldValue) { + ast_node(fv, FieldValue, elem); + if (lb_is_elem_const(fv->value, et)) { + continue; + } + if (is_ast_range(fv->field)) { + ast_node(ie, BinaryExpr, fv->field); + TypeAndValue lo_tav = ie->left->tav; + TypeAndValue hi_tav = ie->right->tav; + GB_ASSERT(lo_tav.mode == Addressing_Constant); + GB_ASSERT(hi_tav.mode == Addressing_Constant); + + TokenKind op = ie->op.kind; + i64 lo = exact_value_to_i64(lo_tav.value); + i64 hi = exact_value_to_i64(hi_tav.value); + if (op != Token_RangeHalf) { + hi += 1; + } + + lbValue value = lb_build_expr(p, fv->value); + + for (i64 k = lo; k < hi; k++) { + lbCompoundLitElemTempData data = {}; + data.value = value; + data.elem_index = cast(i32)k; + array_add(&temp_data, data); + } + + } else { + auto tav = fv->field->tav; + GB_ASSERT(tav.mode == Addressing_Constant); + i64 index = exact_value_to_i64(tav.value); + + lbValue value = lb_build_expr(p, fv->value); + lbCompoundLitElemTempData data = {}; + data.value = lb_emit_conv(p, value, et); + data.expr = fv->value; + data.elem_index = cast(i32)index; + array_add(&temp_data, data); + } + + } else { + if (lb_is_elem_const(elem, et)) { + continue; + } + lbCompoundLitElemTempData data = {}; + data.expr = elem; + data.elem_index = cast(i32)i; + array_add(&temp_data, data); + } + } + + + for_array(i, temp_data) { + lbValue field_expr = temp_data[i].value; + Ast *expr = temp_data[i].expr; + + auto prev_hint = lb_set_copy_elision_hint(p, lb_addr(temp_data[i].gep), expr); + + if (field_expr.value == nullptr) { + field_expr = lb_build_expr(p, expr); + } + Type *t = field_expr.type; + GB_ASSERT(t->kind != Type_Tuple); + lbValue ev = lb_emit_conv(p, field_expr, et); + + if (!p->copy_elision_hint.used) { + temp_data[i].value = ev; + } + + lb_reset_copy_elision_hint(p, prev_hint); + } + + + // TODO(bill): reduce the need for individual `insertelement` if a `shufflevector` + // might be a better option + + for_array(i, temp_data) { + if (temp_data[i].value.value != nullptr) { + LLVMValueRef index = lb_const_int(p->module, t_u32, temp_data[i].elem_index).value; + vector_value.value = LLVMBuildInsertElement(p->builder, vector_value.value, temp_data[i].value.value, index, ""); + } + } + } + break; + } } return v; @@ -4568,7 +4904,7 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { lbBlock *done = lb_create_block(p, "if.done"); // NOTE(bill): Append later lbBlock *else_ = lb_create_block(p, "if.else"); - lbValue cond = lb_build_cond(p, te->cond, then, else_); + lb_build_cond(p, te->cond, then, else_); lb_start_block(p, then); Type *ptr_type = alloc_type_pointer(default_type(type_of_expr(expr))); diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 17eeb0bea..b61439238 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -67,11 +67,12 @@ void lb_init_module(lbModule *m, Checker *c) { map_init(&m->equal_procs, a); map_init(&m->hasher_procs, a); array_init(&m->procedures_to_generate, a, 0, 1024); - array_init(&m->foreign_library_paths, a, 0, 1024); array_init(&m->missing_procedures_to_check, a, 0, 16); - map_init(&m->debug_values, a); array_init(&m->debug_incomplete_types, a, 0, 1024); + + string_map_init(&m->objc_classes, a); + string_map_init(&m->objc_selectors, a); } bool lb_init_generator(lbGenerator *gen, Checker *c) { @@ -84,7 +85,6 @@ bool lb_init_generator(lbGenerator *gen, Checker *c) { return false; } - String init_fullpath = c->parser->init_fullpath; if (build_context.out_filepath.len == 0) { @@ -124,6 +124,11 @@ bool lb_init_generator(lbGenerator *gen, Checker *c) { map_init(&gen->modules_through_ctx, permanent_allocator(), gen->info->packages.entries.count*2); map_init(&gen->anonymous_proc_lits, heap_allocator(), 1024); + + mutex_init(&gen->foreign_mutex); + array_init(&gen->foreign_libraries, heap_allocator(), 0, 1024); + ptr_set_init(&gen->foreign_libraries_set, heap_allocator(), 1024); + if (USE_SEPARATE_MODULES) { for_array(i, gen->info->packages.entries) { AstPackage *pkg = gen->info->packages.entries[i].value; @@ -216,6 +221,17 @@ LLVMValueRef llvm_one(lbModule *m) { return LLVMConstInt(lb_type(m, t_i32), 1, false); } +LLVMValueRef llvm_alloca(lbProcedure *p, LLVMTypeRef llvm_type, isize alignment, char const *name) { + LLVMPositionBuilderAtEnd(p->builder, p->decl_block->block); + + LLVMValueRef val = LLVMBuildAlloca(p->builder, llvm_type, name); + LLVMSetAlignment(val, cast(unsigned int)alignment); + + LLVMPositionBuilderAtEnd(p->builder, p->curr_block->block); + + return val; +} + lbValue lb_zero(lbModule *m, Type *t) { lbValue v = {}; v.value = LLVMConstInt(lb_type(m, t), 0, false); @@ -271,6 +287,10 @@ lbAddr lb_addr(lbValue addr) { lbAddr lb_addr_map(lbValue addr, lbValue map_key, Type *map_type, Type *map_result) { + GB_ASSERT(is_type_pointer(addr.type)); + Type *mt = type_deref(addr.type); + GB_ASSERT(is_type_map(mt)); + lbAddr v = {lbAddr_Map, addr}; v.map.key = map_key; v.map.type = map_type; @@ -832,6 +852,16 @@ bool lb_is_type_proc_recursive(Type *t) { void lb_emit_store(lbProcedure *p, lbValue ptr, lbValue value) { GB_ASSERT(value.value != nullptr); Type *a = type_deref(ptr.type); + + if (LLVMIsNull(value.value)) { + LLVMTypeRef src_t = LLVMGetElementType(LLVMTypeOf(ptr.value)); + if (lb_sizeof(src_t) <= lb_max_zero_init_size()) { + LLVMBuildStore(p->builder, LLVMConstNull(src_t), ptr.value); + } else { + lb_mem_zero_ptr(p, ptr.value, a, 1); + } + return; + } if (is_type_boolean(a)) { // NOTE(bill): There are multiple sized booleans, thus force a conversion (if necessarily) value = lb_emit_conv(p, value, a); @@ -841,6 +871,20 @@ void lb_emit_store(lbProcedure *p, lbValue ptr, lbValue value) { GB_ASSERT_MSG(are_types_identical(ca, core_type(value.type)), "%s != %s", type_to_string(a), type_to_string(value.type)); } + enum {MAX_STORE_SIZE = 64}; + + if (LLVMIsALoadInst(value.value) && lb_sizeof(LLVMTypeOf(value.value)) > MAX_STORE_SIZE) { + LLVMValueRef dst_ptr = ptr.value; + LLVMValueRef src_ptr = LLVMGetOperand(value.value, 0); + src_ptr = LLVMBuildPointerCast(p->builder, src_ptr, LLVMTypeOf(dst_ptr), ""); + + LLVMBuildMemMove(p->builder, + dst_ptr, 1, + src_ptr, 1, + LLVMConstInt(LLVMInt64TypeInContext(p->module->ctx), lb_sizeof(LLVMTypeOf(value.value)), false)); + return; + } + if (lb_is_type_proc_recursive(a)) { // NOTE(bill, 2020-11-11): Because of certain LLVM rules, a procedure value may be // stored as regular pointer with no procedure information @@ -1169,10 +1213,35 @@ void lb_emit_store_union_variant_tag(lbProcedure *p, lbValue parent, Type *varia } void lb_emit_store_union_variant(lbProcedure *p, lbValue parent, lbValue variant, Type *variant_type) { - lbValue underlying = lb_emit_conv(p, parent, alloc_type_pointer(variant_type)); + Type *pt = base_type(type_deref(parent.type)); + GB_ASSERT(pt->kind == Type_Union); + if (pt->Union.kind == UnionType_shared_nil) { + lbBlock *if_nil = lb_create_block(p, "shared_nil.if_nil"); + lbBlock *if_not_nil = lb_create_block(p, "shared_nil.if_not_nil"); + lbBlock *done = lb_create_block(p, "shared_nil.done"); - lb_emit_store(p, underlying, variant); - lb_emit_store_union_variant_tag(p, parent, variant_type); + lbValue cond_is_nil = lb_emit_comp_against_nil(p, Token_CmpEq, variant); + lb_emit_if(p, cond_is_nil, if_nil, if_not_nil); + + lb_start_block(p, if_nil); + lb_emit_store(p, parent, lb_const_nil(p->module, type_deref(parent.type))); + lb_emit_jump(p, done); + + lb_start_block(p, if_not_nil); + lbValue underlying = lb_emit_conv(p, parent, alloc_type_pointer(variant_type)); + lb_emit_store(p, underlying, variant); + lb_emit_store_union_variant_tag(p, parent, variant_type); + lb_emit_jump(p, done); + + lb_start_block(p, done); + + + } else { + lbValue underlying = lb_emit_conv(p, parent, alloc_type_pointer(variant_type)); + + lb_emit_store(p, underlying, variant); + lb_emit_store_union_variant_tag(p, parent, variant_type); + } } @@ -1598,8 +1667,9 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { return llvm_type; } llvm_type = LLVMStructCreateNamed(ctx, name); + LLVMTypeRef found_val = *found; map_set(&m->types, type, llvm_type); - lb_clone_struct_type(llvm_type, *found); + lb_clone_struct_type(llvm_type, found_val); return llvm_type; } } @@ -1892,11 +1962,12 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { if (e->flags & EntityFlag_CVarArg) { continue; } - Type *e_type = reduce_tuple_to_single_type(e->type); LLVMTypeRef param_type = nullptr; - if (is_type_boolean(e_type) && + if (e->flags & EntityFlag_ByPtr) { + param_type = lb_type(m, alloc_type_pointer(e_type)); + } else if (is_type_boolean(e_type) && type_size_of(e_type) <= 1) { param_type = LLVMInt1TypeInContext(m->ctx); } else { @@ -2265,13 +2336,11 @@ general_end:; return loaded_val; } else { GB_ASSERT(p->decl_block != p->curr_block); - LLVMPositionBuilderAtEnd(p->builder, p->decl_block->block); - LLVMValueRef ptr = LLVMBuildAlloca(p->builder, dst_type, ""); - LLVMPositionBuilderAtEnd(p->builder, p->curr_block->block); i64 max_align = gb_max(lb_alignof(src_type), lb_alignof(dst_type)); max_align = gb_max(max_align, 4); - LLVMSetAlignment(ptr, cast(unsigned)max_align); + + LLVMValueRef ptr = llvm_alloca(p, dst_type, max_align); LLVMValueRef nptr = LLVMBuildPointerCast(p->builder, ptr, LLVMPointerType(src_type, 0), ""); LLVMBuildStore(p->builder, val, nptr); @@ -2304,7 +2373,10 @@ LLVMValueRef lb_find_or_add_entity_string_ptr(lbModule *m, String const &str) { LLVMValueRef global_data = LLVMAddGlobal(m->mod, LLVMTypeOf(data), name); LLVMSetInitializer(global_data, data); - LLVMSetLinkage(global_data, LLVMInternalLinkage); + LLVMSetLinkage(global_data, LLVMPrivateLinkage); + LLVMSetUnnamedAddress(global_data, LLVMGlobalUnnamedAddr); + LLVMSetAlignment(global_data, 1); + LLVMSetGlobalConstant(global_data, true); LLVMValueRef ptr = LLVMConstInBoundsGEP(global_data, indices, 2); string_map_set(&m->const_strings, key, ptr); @@ -2346,7 +2418,10 @@ lbValue lb_find_or_add_entity_string_byte_slice(lbModule *m, String const &str) } LLVMValueRef global_data = LLVMAddGlobal(m->mod, LLVMTypeOf(data), name); LLVMSetInitializer(global_data, data); - LLVMSetLinkage(global_data, LLVMInternalLinkage); + LLVMSetLinkage(global_data, LLVMPrivateLinkage); + LLVMSetUnnamedAddress(global_data, LLVMGlobalUnnamedAddr); + LLVMSetAlignment(global_data, 1); + LLVMSetGlobalConstant(global_data, true); LLVMValueRef ptr = nullptr; if (str.len != 0) { @@ -2445,7 +2520,7 @@ lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e) { return {}; } -lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value) { +lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value, Entity **entity_) { GB_ASSERT(type != nullptr); type = default_type(type); @@ -2471,6 +2546,9 @@ lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value) { lb_add_entity(m, e, g); lb_add_member(m, name, g); + + if (entity_) *entity_ = e; + return lb_addr(g); } @@ -2579,7 +2657,8 @@ lbValue lb_generate_global_array(lbModule *m, Type *elem_type, i64 count, String g.value = LLVMAddGlobal(m->mod, lb_type(m, t), text); g.type = alloc_type_pointer(t); LLVMSetInitializer(g.value, LLVMConstNull(lb_type(m, t))); - LLVMSetLinkage(g.value, LLVMInternalLinkage); + LLVMSetLinkage(g.value, LLVMPrivateLinkage); + LLVMSetUnnamedAddress(g.value, LLVMGlobalUnnamedAddr); string_map_set(&m->members, s, g); return g; } @@ -2591,6 +2670,9 @@ lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *f GB_ASSERT(true_block != nullptr); GB_ASSERT(false_block != nullptr); + // Use to signal not to do compile time short circuit for consts + lbValue no_comptime_short_circuit = {}; + switch (cond->kind) { case_ast_node(pe, ParenExpr, cond); return lb_build_cond(p, pe->expr, true_block, false_block); @@ -2598,7 +2680,11 @@ lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *f case_ast_node(ue, UnaryExpr, cond); if (ue->op.kind == Token_Not) { - return lb_build_cond(p, ue->expr, false_block, true_block); + lbValue cond_val = lb_build_cond(p, ue->expr, false_block, true_block); + if (cond_val.value && LLVMIsConstant(cond_val.value)) { + return lb_const_bool(p->module, cond_val.type, LLVMConstIntGetZExtValue(cond_val.value) == 0); + } + return no_comptime_short_circuit; } case_end; @@ -2607,12 +2693,14 @@ lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *f lbBlock *block = lb_create_block(p, "cmp.and"); lb_build_cond(p, be->left, block, false_block); lb_start_block(p, block); - return lb_build_cond(p, be->right, true_block, false_block); + lb_build_cond(p, be->right, true_block, false_block); + return no_comptime_short_circuit; } else if (be->op.kind == Token_CmpOr) { lbBlock *block = lb_create_block(p, "cmp.or"); lb_build_cond(p, be->left, true_block, block); lb_start_block(p, block); - return lb_build_cond(p, be->right, true_block, false_block); + lb_build_cond(p, be->right, true_block, false_block); + return no_comptime_short_circuit; } case_end; } @@ -2642,34 +2730,29 @@ lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e, bool zero_init, i32 p } LLVMTypeRef llvm_type = lb_type(p->module, type); - LLVMValueRef ptr = LLVMBuildAlloca(p->builder, llvm_type, name); unsigned alignment = cast(unsigned)gb_max(type_align_of(type), lb_alignof(llvm_type)); if (is_type_matrix(type)) { alignment *= 2; // NOTE(bill): Just in case } - LLVMSetAlignment(ptr, alignment); - - LLVMPositionBuilderAtEnd(p->builder, p->curr_block->block); + LLVMValueRef ptr = llvm_alloca(p, llvm_type, alignment, name); if (!zero_init && !force_no_init) { // If there is any padding of any kind, just zero init regardless of zero_init parameter LLVMTypeKind kind = LLVMGetTypeKind(llvm_type); + if (kind == LLVMArrayTypeKind) { + kind = LLVMGetTypeKind(lb_type(p->module, core_array_type(type))); + } + if (kind == LLVMStructTypeKind) { i64 sz = type_size_of(type); if (type_size_of_struct_pretend_is_packed(type) != sz) { zero_init = true; } - } else if (kind == LLVMArrayTypeKind) { - zero_init = true; } } - if (zero_init) { - lb_mem_zero_ptr(p, ptr, type, alignment); - } - lbValue val = {}; val.value = ptr; val.type = alloc_type_pointer(type); @@ -2679,6 +2762,10 @@ lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e, bool zero_init, i32 p lb_add_debug_local_variable(p, ptr, type, e->token); } + if (zero_init) { + lb_mem_zero_ptr(p, ptr, type, alignment); + } + return lb_addr(val); } diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp index 8f1c7ad59..6b80b21d6 100644 --- a/src/llvm_backend_opt.cpp +++ b/src/llvm_backend_opt.cpp @@ -56,7 +56,7 @@ LLVMBool lb_must_preserve_predicate_callback(LLVMValueRef value, void *user_data #endif void lb_basic_populate_function_pass_manager(LLVMPassManagerRef fpm, i32 optimization_level) { - if (optimization_level == 0 && build_context.ODIN_DEBUG) { + if (false && optimization_level == 0 && build_context.ODIN_DEBUG) { LLVMAddMergedLoadStoreMotionPass(fpm); } else { LLVMAddPromoteMemoryToRegisterPass(fpm); @@ -380,6 +380,43 @@ void llvm_delete_function(LLVMValueRef func) { LLVMDeleteFunction(func); } +void lb_append_to_compiler_used(lbModule *m, LLVMValueRef func) { + LLVMValueRef global = LLVMGetNamedGlobal(m->mod, "llvm.compiler.used"); + + LLVMValueRef *constants; + int operands = 1; + + if (global != NULL) { + GB_ASSERT(LLVMIsAGlobalVariable(global)); + LLVMValueRef initializer = LLVMGetInitializer(global); + + GB_ASSERT(LLVMIsAConstantArray(initializer)); + operands = LLVMGetNumOperands(initializer) + 1; + constants = gb_alloc_array(temporary_allocator(), LLVMValueRef, operands); + + for (int i = 0; i < operands - 1; i++) { + LLVMValueRef operand = LLVMGetOperand(initializer, i); + GB_ASSERT(LLVMIsAConstant(operand)); + constants[i] = operand; + } + + LLVMDeleteGlobal(global); + } else { + constants = gb_alloc_array(temporary_allocator(), LLVMValueRef, 1); + } + + LLVMTypeRef Int8PtrTy = LLVMPointerType(LLVMInt8TypeInContext(m->ctx), 0); + LLVMTypeRef ATy = LLVMArrayType(Int8PtrTy, operands); + + constants[operands - 1] = LLVMConstBitCast(func, Int8PtrTy); + LLVMValueRef initializer = LLVMConstArray(Int8PtrTy, constants, operands); + + global = LLVMAddGlobal(m->mod, ATy, "llvm.compiler.used"); + LLVMSetLinkage(global, LLVMAppendingLinkage); + LLVMSetSection(global, "llvm.metadata"); + LLVMSetInitializer(global, initializer); +} + void lb_run_remove_unused_function_pass(lbModule *m) { isize removal_count = 0; isize pass_count = 0; @@ -415,6 +452,7 @@ void lb_run_remove_unused_function_pass(lbModule *m) { Entity *e = *found; bool is_required = (e->flags & EntityFlag_Require) == EntityFlag_Require; if (is_required) { + lb_append_to_compiler_used(m, curr_func); continue; } } diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index b35c6c304..0b0c0794b 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -57,11 +57,12 @@ void lb_mem_copy_non_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbVal LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); } + lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) { GB_ASSERT(entity != nullptr); GB_ASSERT(entity->kind == Entity_Procedure); if (!entity->Procedure.is_foreign) { - GB_ASSERT(entity->flags & EntityFlag_ProcBodyChecked); + GB_ASSERT_MSG(entity->flags & EntityFlag_ProcBodyChecked, "%.*s :: %s", LIT(entity->token.string), type_to_string(entity->type)); } String link_name = {}; @@ -112,6 +113,8 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) p->branch_blocks.allocator = a; p->context_stack.allocator = a; p->scope_stack.allocator = a; + map_init(&p->selector_values, a, 0); + map_init(&p->selector_addr, a, 0); if (p->is_foreign) { lb_add_foreign_library_path(p->module, entity->Procedure.foreign_library); @@ -134,43 +137,57 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) lb_add_attribute_to_proc(m, p->value, "naked"); } - switch (p->inlining) { - case ProcInlining_inline: - lb_add_attribute_to_proc(m, p->value, "alwaysinline"); - break; - case ProcInlining_no_inline: + if (!entity->Procedure.is_foreign && build_context.disable_red_zone) { + lb_add_attribute_to_proc(m, p->value, "noredzone"); + } + + if (build_context.optimization_level == 0 && build_context.ODIN_DEBUG) { lb_add_attribute_to_proc(m, p->value, "noinline"); - break; + lb_add_attribute_to_proc(m, p->value, "optnone"); + } else { + switch (p->inlining) { + case ProcInlining_inline: + lb_add_attribute_to_proc(m, p->value, "alwaysinline"); + break; + case ProcInlining_no_inline: + lb_add_attribute_to_proc(m, p->value, "noinline"); + break; + } + + switch (entity->Procedure.optimization_mode) { + case ProcedureOptimizationMode_None: + lb_add_attribute_to_proc(m, p->value, "optnone"); + break; + case ProcedureOptimizationMode_Minimal: + lb_add_attribute_to_proc(m, p->value, "optnone"); + break; + case ProcedureOptimizationMode_Size: + lb_add_attribute_to_proc(m, p->value, "optsize"); + break; + case ProcedureOptimizationMode_Speed: + // TODO(bill): handle this correctly + lb_add_attribute_to_proc(m, p->value, "optsize"); + break; + } + } + + if (!entity->Procedure.target_feature_disabled && + entity->Procedure.target_feature.len != 0) { + auto features = split_by_comma(entity->Procedure.target_feature); + for_array(i, features) { + String feature = features[i]; + LLVMAttributeRef ref = LLVMCreateStringAttribute( + m->ctx, + cast(char const *)feature.text, cast(unsigned)feature.len, + "", 0); + LLVMAddAttributeAtIndex(p->value, LLVMAttributeIndex_FunctionIndex, ref); + } } if (entity->flags & EntityFlag_Cold) { lb_add_attribute_to_proc(m, p->value, "cold"); } - switch (entity->Procedure.optimization_mode) { - case ProcedureOptimizationMode_None: - lb_add_attribute_to_proc(m, p->value, "optnone"); - break; - case ProcedureOptimizationMode_Minimal: - lb_add_attribute_to_proc(m, p->value, "optnone"); - break; - case ProcedureOptimizationMode_Size: - lb_add_attribute_to_proc(m, p->value, "optsize"); - break; - case ProcedureOptimizationMode_Speed: - // TODO(bill): handle this correctly - lb_add_attribute_to_proc(m, p->value, "optsize"); - break; - } - - - - // lbCallingConventionKind cc_kind = lbCallingConvention_C; - // // TODO(bill): Clean up this logic - // if (build_context.metrics.os != TargetOs_js) { - // cc_kind = lb_calling_convention_map[pt->Proc.calling_convention]; - // } - // LLVMSetFunctionCallConv(p->value, cc_kind); lbValue proc_value = {p->value, p->type}; lb_add_entity(m, entity, proc_value); lb_add_member(m, p->name, proc_value); @@ -192,7 +209,7 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) GB_ASSERT(entity->kind == Entity_Procedure); String link_name = entity->Procedure.link_name; if (entity->flags & EntityFlag_CustomLinkName && - link_name != "") { + link_name != "") { if (string_starts_with(link_name, str_lit("__"))) { LLVMSetLinkage(p->value, LLVMExternalLinkage); } else { @@ -203,12 +220,12 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) } } lb_set_linkage_from_entity_flags(p->module, p->value, entity->flags); - - + + if (p->is_foreign) { lb_set_wasm_import_attributes(p->value, entity, p->name); } - + // NOTE(bill): offset==0 is the return value isize offset = 1; @@ -283,7 +300,7 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) if (p->body != nullptr) { // String debug_name = entity->token.string.text; String debug_name = p->name; - + p->debug_info = LLVMDIBuilderCreateFunction(m->debug_builder, scope, cast(char const *)debug_name.text, debug_name.len, cast(char const *)p->name.text, p->name.len, @@ -418,6 +435,40 @@ void lb_start_block(lbProcedure *p, lbBlock *b) { p->curr_block = b; } +void lb_set_debug_position_to_procedure_begin(lbProcedure *p) { + if (p->debug_info == nullptr) { + return; + } + TokenPos pos = {}; + if (p->body != nullptr) { + pos = ast_token(p->body).pos; + } else if (p->type_expr != nullptr) { + pos = ast_token(p->type_expr).pos; + } else if (p->entity != nullptr) { + pos = p->entity->token.pos; + } + if (pos.file_id != 0) { + LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_token_pos(p, pos)); + } +} + +void lb_set_debug_position_to_procedure_end(lbProcedure *p) { + if (p->debug_info == nullptr) { + return; + } + TokenPos pos = {}; + if (p->body != nullptr) { + pos = ast_end_token(p->body).pos; + } else if (p->type_expr != nullptr) { + pos = ast_end_token(p->type_expr).pos; + } else if (p->entity != nullptr) { + pos = p->entity->token.pos; + } + if (pos.file_id != 0) { + LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_token_pos(p, pos)); + } +} + void lb_begin_procedure_body(lbProcedure *p) { DeclInfo *decl = decl_info_of_entity(p->entity); if (decl != nullptr) { @@ -450,7 +501,7 @@ void lb_begin_procedure_body(lbProcedure *p) { Type *ptr_type = alloc_type_pointer(reduce_tuple_to_single_type(p->type->Proc.results)); Entity *e = alloc_entity_param(nullptr, make_token_ident(name), ptr_type, false, false); - e->flags |= EntityFlag_Sret | EntityFlag_NoAlias; + e->flags |= EntityFlag_NoAlias; return_ptr_value.value = LLVMGetParam(p->value, 0); LLVMSetValueName2(return_ptr_value.value, cast(char const *)name.text, name.len); @@ -473,34 +524,43 @@ void lb_begin_procedure_body(lbProcedure *p) { } lbArgType *arg_type = &ft->args[param_index]; + defer (param_index += 1); + if (arg_type->kind == lbArg_Ignore) { continue; } else if (arg_type->kind == lbArg_Direct) { if (e->token.string.len != 0 && !is_blank_ident(e->token.string)) { LLVMTypeRef param_type = lb_type(p->module, e->type); - LLVMValueRef value = LLVMGetParam(p->value, param_offset+param_index); - - value = OdinLLVMBuildTransmute(p, value, param_type); + LLVMValueRef original_value = LLVMGetParam(p->value, param_offset+param_index); + LLVMValueRef value = OdinLLVMBuildTransmute(p, original_value, param_type); lbValue param = {}; param.value = value; param.type = e->type; lbValue ptr = lb_address_from_load_or_generate_local(p, param); + GB_ASSERT(LLVMIsAAllocaInst(ptr.value)); lb_add_entity(p->module, e, ptr); - // lb_add_debug_local_variable(p, ptr.value, e->type, e->token); + + lbBlock *block = p->decl_block; + if (original_value != value) { + block = p->curr_block; + } + LLVMValueRef debug_storage_value = value; + if (original_value != value && LLVMIsALoadInst(value)) { + debug_storage_value = LLVMGetOperand(value, 0); + } + lb_add_debug_param_variable(p, debug_storage_value, e->type, e->token, param_index+1, block); } } else if (arg_type->kind == lbArg_Indirect) { if (e->token.string.len != 0 && !is_blank_ident(e->token.string)) { lbValue ptr = {}; ptr.value = LLVMGetParam(p->value, param_offset+param_index); ptr.type = alloc_type_pointer(e->type); - lb_add_entity(p->module, e, ptr); - // lb_add_debug_local_variable(p, ptr.value, e->type, e->token); + lb_add_debug_param_variable(p, ptr.value, e->type, e->token, param_index+1, p->decl_block); } } - param_index += 1; } } @@ -540,29 +600,21 @@ void lb_begin_procedure_body(lbProcedure *p) { lb_push_context_onto_stack_from_implicit_parameter(p); } - lb_start_block(p, p->entry_block); - + lb_set_debug_position_to_procedure_begin(p); if (p->debug_info != nullptr) { - TokenPos pos = {}; - if (p->body != nullptr) { - pos = ast_token(p->body).pos; - } else if (p->type_expr != nullptr) { - pos = ast_token(p->type_expr).pos; - } else if (p->entity != nullptr) { - pos = p->entity->token.pos; - } - if (pos.file_id != 0) { - LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_token_pos(p, pos)); - } - if (p->context_stack.count != 0) { + p->curr_block = p->decl_block; lb_add_debug_context_variable(p, lb_find_or_generate_context_ptr(p)); } } + + lb_start_block(p, p->entry_block); } void lb_end_procedure_body(lbProcedure *p) { + lb_set_debug_position_to_procedure_begin(p); + LLVMPositionBuilderAtEnd(p->builder, p->decl_block->block); LLVMBuildBr(p->builder, p->entry_block->block); LLVMPositionBuilderAtEnd(p->builder, p->curr_block->block); @@ -574,6 +626,7 @@ void lb_end_procedure_body(lbProcedure *p) { instr = LLVMGetLastInstruction(p->curr_block->block); if (!lb_is_instr_terminating(instr)) { lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr); + lb_set_debug_position_to_procedure_end(p); LLVMBuildRetVoid(p->builder); } } @@ -817,12 +870,6 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, GB_ASSERT(pt->kind == Type_Proc); Type *results = pt->Proc.results; - if (p->entity != nullptr) { - if (p->entity->flags & EntityFlag_Disabled) { - return {}; - } - } - lbAddr context_ptr = {}; if (pt->Proc.calling_convention == ProcCC_Odin) { context_ptr = lb_find_or_generate_context_ptr(p); @@ -976,10 +1023,466 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, return result; } +LLVMValueRef llvm_splat_float(i64 count, LLVMTypeRef type, f64 value) { + LLVMValueRef v = LLVMConstReal(type, value); + LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, count); + for (i64 i = 0; i < count; i++) { + values[i] = v; + } + return LLVMConstVector(values, cast(unsigned)count); +} +LLVMValueRef llvm_splat_int(i64 count, LLVMTypeRef type, i64 value, bool is_signed=false) { + LLVMValueRef v = LLVMConstInt(type, value, is_signed); + LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, count); + for (i64 i = 0; i < count; i++) { + values[i] = v; + } + return LLVMConstVector(values, cast(unsigned)count); +} + + +lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, BuiltinProcId builtin_id) { + ast_node(ce, CallExpr, expr); + + lbModule *m = p->module; + + lbValue res = {}; + res.type = tv.type; + + lbValue arg0 = {}; if (ce->args.count > 0) arg0 = lb_build_expr(p, ce->args[0]); + lbValue arg1 = {}; if (ce->args.count > 1) arg1 = lb_build_expr(p, ce->args[1]); + lbValue arg2 = {}; if (ce->args.count > 2) arg2 = lb_build_expr(p, ce->args[2]); + + Type *elem = base_array_type(arg0.type); + + bool is_float = is_type_float(elem); + bool is_signed = !is_type_unsigned(elem); + + LLVMOpcode op_code = cast(LLVMOpcode)0; + + switch (builtin_id) { + case BuiltinProc_simd_add: + case BuiltinProc_simd_sub: + case BuiltinProc_simd_mul: + case BuiltinProc_simd_div: + case BuiltinProc_simd_rem: + if (is_float) { + switch (builtin_id) { + case BuiltinProc_simd_add: op_code = LLVMFAdd; break; + case BuiltinProc_simd_sub: op_code = LLVMFSub; break; + case BuiltinProc_simd_mul: op_code = LLVMFMul; break; + case BuiltinProc_simd_div: op_code = LLVMFDiv; break; + } + } else { + switch (builtin_id) { + case BuiltinProc_simd_add: op_code = LLVMAdd; break; + case BuiltinProc_simd_sub: op_code = LLVMSub; break; + case BuiltinProc_simd_mul: op_code = LLVMMul; break; + case BuiltinProc_simd_div: + if (is_signed) { + op_code = LLVMSDiv; + } else { + op_code = LLVMUDiv; + } + break; + case BuiltinProc_simd_rem: + if (is_signed) { + op_code = LLVMSRem; + } else { + op_code = LLVMURem; + } + break; + } + } + if (op_code) { + res.value = LLVMBuildBinOp(p->builder, op_code, arg0.value, arg1.value, ""); + return res; + } + break; + case BuiltinProc_simd_shl: // Odin logic + case BuiltinProc_simd_shr: // Odin logic + case BuiltinProc_simd_shl_masked: // C logic + case BuiltinProc_simd_shr_masked: // C logic + { + i64 sz = type_size_of(elem); + GB_ASSERT(arg0.type->kind == Type_SimdVector); + + i64 count = arg0.type->SimdVector.count; + Type *elem1 = base_array_type(arg1.type); + + bool is_masked = false; + switch (builtin_id) { + case BuiltinProc_simd_shl: op_code = LLVMShl; is_masked = false; break; + case BuiltinProc_simd_shr: op_code = is_signed ? LLVMAShr : LLVMLShr; is_masked = false; break; + case BuiltinProc_simd_shl_masked: op_code = LLVMShl; is_masked = true; break; + case BuiltinProc_simd_shr_masked: op_code = is_signed ? LLVMAShr : LLVMLShr; is_masked = true; break; + } + if (op_code) { + LLVMValueRef bits = llvm_splat_int(count, lb_type(m, elem1), sz*8 - 1); + if (is_masked) { + // C logic + LLVMValueRef shift = LLVMBuildAnd(p->builder, arg1.value, bits, ""); + res.value = LLVMBuildBinOp(p->builder, op_code, arg0.value, shift, ""); + } else { + // Odin logic + LLVMValueRef zero = lb_const_nil(m, arg1.type).value; + LLVMValueRef mask = LLVMBuildICmp(p->builder, LLVMIntULE, arg1.value, bits, ""); + LLVMValueRef shift = LLVMBuildBinOp(p->builder, op_code, arg0.value, arg1.value, ""); + res.value = LLVMBuildSelect(p->builder, mask, shift, zero, ""); + } + return res; + } + } + break; + case BuiltinProc_simd_and: + case BuiltinProc_simd_or: + case BuiltinProc_simd_xor: + case BuiltinProc_simd_and_not: + switch (builtin_id) { + case BuiltinProc_simd_and: op_code = LLVMAnd; break; + case BuiltinProc_simd_or: op_code = LLVMOr; break; + case BuiltinProc_simd_xor: op_code = LLVMXor; break; + case BuiltinProc_simd_and_not: + op_code = LLVMAnd; + arg1.value = LLVMBuildNot(p->builder, arg1.value, ""); + break; + } + if (op_code) { + res.value = LLVMBuildBinOp(p->builder, op_code, arg0.value, arg1.value, ""); + return res; + } + break; + case BuiltinProc_simd_neg: + if (is_float) { + res.value = LLVMBuildFNeg(p->builder, arg0.value, ""); + } else { + res.value = LLVMBuildNeg(p->builder, arg0.value, ""); + } + return res; + case BuiltinProc_simd_abs: + if (is_float) { + LLVMValueRef pos = arg0.value; + LLVMValueRef neg = LLVMBuildFNeg(p->builder, pos, ""); + LLVMValueRef cond = LLVMBuildFCmp(p->builder, LLVMRealOGT, pos, neg, ""); + res.value = LLVMBuildSelect(p->builder, cond, pos, neg, ""); + } else { + LLVMValueRef pos = arg0.value; + LLVMValueRef neg = LLVMBuildNeg(p->builder, pos, ""); + LLVMValueRef cond = LLVMBuildICmp(p->builder, is_signed ? LLVMIntSGT : LLVMIntUGT, pos, neg, ""); + res.value = LLVMBuildSelect(p->builder, cond, pos, neg, ""); + } + return res; + case BuiltinProc_simd_min: + if (is_float) { + LLVMValueRef cond = LLVMBuildFCmp(p->builder, LLVMRealOLT, arg0.value, arg1.value, ""); + res.value = LLVMBuildSelect(p->builder, cond, arg0.value, arg1.value, ""); + } else { + LLVMValueRef cond = LLVMBuildICmp(p->builder, is_signed ? LLVMIntSLT : LLVMIntULT, arg0.value, arg1.value, ""); + res.value = LLVMBuildSelect(p->builder, cond, arg0.value, arg1.value, ""); + } + return res; + case BuiltinProc_simd_max: + if (is_float) { + LLVMValueRef cond = LLVMBuildFCmp(p->builder, LLVMRealOGT, arg0.value, arg1.value, ""); + res.value = LLVMBuildSelect(p->builder, cond, arg0.value, arg1.value, ""); + } else { + LLVMValueRef cond = LLVMBuildICmp(p->builder, is_signed ? LLVMIntSGT : LLVMIntUGT, arg0.value, arg1.value, ""); + res.value = LLVMBuildSelect(p->builder, cond, arg0.value, arg1.value, ""); + } + return res; + case BuiltinProc_simd_lanes_eq: + case BuiltinProc_simd_lanes_ne: + case BuiltinProc_simd_lanes_lt: + case BuiltinProc_simd_lanes_le: + case BuiltinProc_simd_lanes_gt: + case BuiltinProc_simd_lanes_ge: + if (is_float) { + LLVMRealPredicate pred = cast(LLVMRealPredicate)0; + switch (builtin_id) { + case BuiltinProc_simd_lanes_eq: pred = LLVMRealOEQ; break; + case BuiltinProc_simd_lanes_ne: pred = LLVMRealONE; break; + case BuiltinProc_simd_lanes_lt: pred = LLVMRealOLT; break; + case BuiltinProc_simd_lanes_le: pred = LLVMRealOLE; break; + case BuiltinProc_simd_lanes_gt: pred = LLVMRealOGT; break; + case BuiltinProc_simd_lanes_ge: pred = LLVMRealOGE; break; + } + if (pred) { + res.value = LLVMBuildFCmp(p->builder, pred, arg0.value, arg1.value, ""); + res.value = LLVMBuildSExtOrBitCast(p->builder, res.value, lb_type(m, tv.type), ""); + return res; + } + } else { + LLVMIntPredicate pred = cast(LLVMIntPredicate)0; + switch (builtin_id) { + case BuiltinProc_simd_lanes_eq: pred = LLVMIntEQ; break; + case BuiltinProc_simd_lanes_ne: pred = LLVMIntNE; break; + case BuiltinProc_simd_lanes_lt: pred = is_signed ? LLVMIntSLT :LLVMIntULT; break; + case BuiltinProc_simd_lanes_le: pred = is_signed ? LLVMIntSLE :LLVMIntULE; break; + case BuiltinProc_simd_lanes_gt: pred = is_signed ? LLVMIntSGT :LLVMIntUGT; break; + case BuiltinProc_simd_lanes_ge: pred = is_signed ? LLVMIntSGE :LLVMIntUGE; break; + } + if (pred) { + res.value = LLVMBuildICmp(p->builder, pred, arg0.value, arg1.value, ""); + res.value = LLVMBuildSExtOrBitCast(p->builder, res.value, lb_type(m, tv.type), ""); + return res; + } + } + break; + + case BuiltinProc_simd_extract: + res.value = LLVMBuildExtractElement(p->builder, arg0.value, arg1.value, ""); + return res; + case BuiltinProc_simd_replace: + res.value = LLVMBuildInsertElement(p->builder, arg0.value, arg2.value, arg1.value, ""); + return res; + + case BuiltinProc_simd_reduce_add_ordered: + case BuiltinProc_simd_reduce_mul_ordered: + { + LLVMTypeRef llvm_elem = lb_type(m, elem); + LLVMValueRef args[2] = {}; + isize args_count = 0; + + char const *name = nullptr; + switch (builtin_id) { + case BuiltinProc_simd_reduce_add_ordered: + if (is_float) { + name = "llvm.vector.reduce.fadd"; + args[args_count++] = LLVMConstReal(llvm_elem, 0.0); + } else { + name = "llvm.vector.reduce.add"; + } + break; + case BuiltinProc_simd_reduce_mul_ordered: + if (is_float) { + name = "llvm.vector.reduce.fmul"; + args[args_count++] = LLVMConstReal(llvm_elem, 1.0); + } else { + name = "llvm.vector.reduce.mul"; + } + break; + } + args[args_count++] = arg0.value; + + + LLVMTypeRef types[1] = {lb_type(p->module, arg0.type)}; + unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name)); + GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0])); + LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types)); + + res.value = LLVMBuildCall(p->builder, ip, args, cast(unsigned)args_count, ""); + return res; + } + case BuiltinProc_simd_reduce_min: + case BuiltinProc_simd_reduce_max: + case BuiltinProc_simd_reduce_and: + case BuiltinProc_simd_reduce_or: + case BuiltinProc_simd_reduce_xor: + { + char const *name = nullptr; + switch (builtin_id) { + case BuiltinProc_simd_reduce_min: + if (is_float) { + name = "llvm.vector.reduce.fmin"; + } else if (is_signed) { + name = "llvm.vector.reduce.smin"; + } else { + name = "llvm.vector.reduce.umin"; + } + break; + case BuiltinProc_simd_reduce_max: + if (is_float) { + name = "llvm.vector.reduce.fmax"; + } else if (is_signed) { + name = "llvm.vector.reduce.smax"; + } else { + name = "llvm.vector.reduce.umax"; + } + break; + case BuiltinProc_simd_reduce_and: name = "llvm.vector.reduce.and"; break; + case BuiltinProc_simd_reduce_or: name = "llvm.vector.reduce.or"; break; + case BuiltinProc_simd_reduce_xor: name = "llvm.vector.reduce.xor"; break; + } + LLVMTypeRef types[1] = {lb_type(p->module, arg0.type)}; + unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name)); + GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0])); + LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types)); + + LLVMValueRef args[1] = {}; + args[0] = arg0.value; + + res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); + return res; + } + + case BuiltinProc_simd_shuffle: + { + Type *vt = arg0.type; + GB_ASSERT(vt->kind == Type_SimdVector); + + i64 indices_count = ce->args.count-2; + i64 max_count = vt->SimdVector.count*2; + GB_ASSERT(indices_count <= max_count); + + LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, indices_count); + for (isize i = 0; i < indices_count; i++) { + lbValue idx = lb_build_expr(p, ce->args[i+2]); + GB_ASSERT(LLVMIsConstant(idx.value)); + values[i] = idx.value; + } + LLVMValueRef indices = LLVMConstVector(values, cast(unsigned)indices_count); + + res.value = LLVMBuildShuffleVector(p->builder, arg0.value, arg1.value, indices, ""); + return res; + } + + case BuiltinProc_simd_select: + { + LLVMValueRef cond = arg0.value; + LLVMValueRef x = lb_build_expr(p, ce->args[1]).value; + LLVMValueRef y = lb_build_expr(p, ce->args[2]).value; + + cond = LLVMBuildICmp(p->builder, LLVMIntNE, cond, LLVMConstNull(LLVMTypeOf(cond)), ""); + res.value = LLVMBuildSelect(p->builder, cond, x, y, ""); + return res; + } + + case BuiltinProc_simd_ceil: + case BuiltinProc_simd_floor: + case BuiltinProc_simd_trunc: + case BuiltinProc_simd_nearest: + { + char const *name = nullptr; + switch (builtin_id) { + case BuiltinProc_simd_ceil: name = "llvm.ceil"; break; + case BuiltinProc_simd_floor: name = "llvm.floor"; break; + case BuiltinProc_simd_trunc: name = "llvm.trunc"; break; + case BuiltinProc_simd_nearest: name = "llvm.nearbyint"; break; + } + + LLVMTypeRef types[1] = {lb_type(p->module, arg0.type)}; + unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name)); + GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0])); + LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types)); + + LLVMValueRef args[1] = {}; + args[0] = arg0.value; + + res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); + return res; + } + + case BuiltinProc_simd_lanes_reverse: + { + i64 count = get_array_type_count(arg0.type); + LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, count); + LLVMTypeRef llvm_u32 = lb_type(m, t_u32); + for (i64 i = 0; i < count; i++) { + values[i] = LLVMConstInt(llvm_u32, count-1-i, false); + } + LLVMValueRef mask = LLVMConstVector(values, cast(unsigned)count); + + LLVMValueRef v = arg0.value; + res.value = LLVMBuildShuffleVector(p->builder, v, v, mask, ""); + return res; + } + + case BuiltinProc_simd_lanes_rotate_left: + case BuiltinProc_simd_lanes_rotate_right: + { + + i64 count = get_array_type_count(arg0.type); + GB_ASSERT(is_power_of_two(count)); + BigInt bi_count = {}; + big_int_from_i64(&bi_count, count); + + TypeAndValue const &tv = ce->args[1]->tav; + ExactValue val = exact_value_to_integer(tv.value); + GB_ASSERT(val.kind == ExactValue_Integer); + BigInt *bi = &val.value_integer; + if (builtin_id == BuiltinProc_simd_lanes_rotate_right) { + big_int_neg(bi, bi); + } + big_int_rem(bi, bi, &bi_count); + big_int_dealloc(&bi_count); + + i64 left = big_int_to_i64(bi); + + LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, count); + LLVMTypeRef llvm_u32 = lb_type(m, t_u32); + for (i64 i = 0; i < count; i++) { + u64 idx = cast(u64)(i+left) & cast(u64)(count-1); + values[i] = LLVMConstInt(llvm_u32, idx, false); + } + LLVMValueRef mask = LLVMConstVector(values, cast(unsigned)count); + + LLVMValueRef v = arg0.value; + res.value = LLVMBuildShuffleVector(p->builder, v, v, mask, ""); + return res; + } + + + case BuiltinProc_simd_add_sat: + case BuiltinProc_simd_sub_sat: + { + char const *name = nullptr; + switch (builtin_id) { + case BuiltinProc_simd_add_sat: name = is_signed ? "llvm.sadd.sat" : "llvm.uadd.sat"; break; + case BuiltinProc_simd_sub_sat: name = is_signed ? "llvm.ssub.sat" : "llvm.usub.sat"; break; + } + + LLVMTypeRef types[1] = {lb_type(p->module, arg0.type)}; + unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name)); + GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0])); + LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types)); + + LLVMValueRef args[2] = {}; + args[0] = arg0.value; + args[1] = arg1.value; + + res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); + return res; + } + + case BuiltinProc_simd_clamp: + { + LLVMValueRef v = arg0.value; + LLVMValueRef min = arg1.value; + LLVMValueRef max = arg2.value; + + if (is_float) { + v = LLVMBuildSelect(p->builder, LLVMBuildFCmp(p->builder, LLVMRealOLT, v, min, ""), min, v, ""); + res.value = LLVMBuildSelect(p->builder, LLVMBuildFCmp(p->builder, LLVMRealOGT, v, max, ""), max, v, ""); + } else if (is_signed) { + v = LLVMBuildSelect(p->builder, LLVMBuildICmp(p->builder, LLVMIntSLT, v, min, ""), min, v, ""); + res.value = LLVMBuildSelect(p->builder, LLVMBuildICmp(p->builder, LLVMIntSGT, v, max, ""), max, v, ""); + } else { + v = LLVMBuildSelect(p->builder, LLVMBuildICmp(p->builder, LLVMIntULT, v, min, ""), min, v, ""); + res.value = LLVMBuildSelect(p->builder, LLVMBuildICmp(p->builder, LLVMIntUGT, v, max, ""), max, v, ""); + } + return res; + } + + case BuiltinProc_simd_to_bits: + { + res.value = LLVMBuildBitCast(p->builder, arg0.value, lb_type(m, tv.type), ""); + return res; + } + + } + GB_PANIC("Unhandled simd intrinsic: '%.*s'", LIT(builtin_procs[builtin_id].name)); + + return {}; +} + lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, BuiltinProcId id) { ast_node(ce, CallExpr, expr); + if (BuiltinProc__simd_begin < id && id < BuiltinProc__simd_end) { + return lb_build_builtin_simd_proc(p, expr, tv, id); + } + switch (id) { case BuiltinProc_DIRECTIVE: { ast_node(bd, BasicDirective, ce->proc); @@ -1040,7 +1543,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, return lb_string_len(p, v); } else if (is_type_array(t)) { GB_PANIC("Array lengths are constant"); - } else if (is_type_slice(t)) { + } else if (is_type_slice(t) || is_type_relative_slice(t)) { return lb_slice_len(p, v); } else if (is_type_dynamic_array(t)) { return lb_dynamic_array_len(p, v); @@ -1066,7 +1569,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, GB_PANIC("Unreachable"); } else if (is_type_array(t)) { GB_PANIC("Array lengths are constant"); - } else if (is_type_slice(t)) { + } else if (is_type_slice(t) || is_type_relative_slice(t)) { return lb_slice_len(p, v); } else if (is_type_dynamic_array(t)) { return lb_dynamic_array_cap(p, v); @@ -1309,22 +1812,22 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, case BuiltinProc_clamp: return lb_emit_clamp(p, type_of_expr(expr), - lb_build_expr(p, ce->args[0]), - lb_build_expr(p, ce->args[1]), - lb_build_expr(p, ce->args[2])); + lb_build_expr(p, ce->args[0]), + lb_build_expr(p, ce->args[1]), + lb_build_expr(p, ce->args[2])); case BuiltinProc_soa_zip: return lb_soa_zip(p, ce, tv); case BuiltinProc_soa_unzip: return lb_soa_unzip(p, ce, tv); - + case BuiltinProc_transpose: { lbValue m = lb_build_expr(p, ce->args[0]); return lb_emit_matrix_tranpose(p, m, tv.type); } - + case BuiltinProc_outer_product: { lbValue a = lb_build_expr(p, ce->args[0]); @@ -1341,13 +1844,13 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, GB_ASSERT(is_type_matrix(tv.type)); return lb_emit_arith_matrix(p, Token_Mul, a, b, tv.type, true); } - + case BuiltinProc_matrix_flatten: { lbValue m = lb_build_expr(p, ce->args[0]); return lb_emit_matrix_flatten(p, m, tv.type); } - + // "Intrinsics" case BuiltinProc_alloca: @@ -1364,14 +1867,22 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, case BuiltinProc_cpu_relax: if (build_context.metrics.arch == TargetArch_i386 || - build_context.metrics.arch == TargetArch_amd64) { + build_context.metrics.arch == TargetArch_amd64) { LLVMTypeRef func_type = LLVMFunctionType(LLVMVoidTypeInContext(p->module->ctx), nullptr, 0, false); - LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("pause"), {}); + LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("pause"), {}, true); GB_ASSERT(the_asm != nullptr); LLVMBuildCall2(p->builder, func_type, the_asm, nullptr, 0, ""); } else if (build_context.metrics.arch == TargetArch_arm64) { LLVMTypeRef func_type = LLVMFunctionType(LLVMVoidTypeInContext(p->module->ctx), nullptr, 0, false); - LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("yield"), {}); + // NOTE(bill, 2022-03-30): `isb` appears to a better option that `yield` + // See: https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8258604 + LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("isb"), {}, true); + GB_ASSERT(the_asm != nullptr); + LLVMBuildCall2(p->builder, func_type, the_asm, nullptr, 0, ""); + } else { + // NOTE: default to something to prevent optimization + LLVMTypeRef func_type = LLVMFunctionType(LLVMVoidTypeInContext(p->module->ctx), nullptr, 0, false); + LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit(""), {}, true); GB_ASSERT(the_asm != nullptr); LLVMBuildCall2(p->builder, func_type, the_asm, nullptr, 0, ""); } @@ -1400,14 +1911,23 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, case BuiltinProc_read_cycle_counter: { - char const *name = "llvm.readcyclecounter"; - unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name)); - GB_ASSERT_MSG(id != 0, "Unable to find %s", name); - LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, nullptr, 0); - lbValue res = {}; - res.value = LLVMBuildCall(p->builder, ip, nullptr, 0, ""); res.type = tv.type; + + if (build_context.metrics.arch == TargetArch_arm64) { + LLVMTypeRef func_type = LLVMFunctionType(LLVMInt64TypeInContext(p->module->ctx), nullptr, 0, false); + bool has_side_effects = false; + LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("mrs x9, cntvct_el0"), str_lit("=r"), has_side_effects); + GB_ASSERT(the_asm != nullptr); + res.value = LLVMBuildCall2(p->builder, func_type, the_asm, nullptr, 0, ""); + } else { + char const *name = "llvm.readcyclecounter"; + unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name)); + GB_ASSERT_MSG(id != 0, "Unable to find %s", name); + LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, nullptr, 0); + + res.value = LLVMBuildCall(p->builder, ip, nullptr, 0, ""); + } return res; } @@ -1510,12 +2030,37 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, return res; } + case BuiltinProc_fused_mul_add: + { + Type *type = tv.type; + lbValue x = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), type); + lbValue y = lb_emit_conv(p, lb_build_expr(p, ce->args[1]), type); + lbValue z = lb_emit_conv(p, lb_build_expr(p, ce->args[2]), type); + + + char const *name = "llvm.fma"; + LLVMTypeRef types[1] = {lb_type(p->module, type)}; + unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name)); + GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0])); + LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types)); + + LLVMValueRef args[3] = {}; + args[0] = x.value; + args[1] = y.value; + args[2] = z.value; + + lbValue res = {}; + res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); + res.type = type; + return res; + } + case BuiltinProc_mem_copy: { lbValue dst = lb_build_expr(p, ce->args[0]); lbValue src = lb_build_expr(p, ce->args[1]); lbValue len = lb_build_expr(p, ce->args[2]); - + lb_mem_copy_overlapping(p, dst, src, len, false); return {}; } @@ -1524,7 +2069,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, lbValue dst = lb_build_expr(p, ce->args[0]); lbValue src = lb_build_expr(p, ce->args[1]); lbValue len = lb_build_expr(p, ce->args[2]); - + lb_mem_copy_non_overlapping(p, dst, src, len, false); return {}; } @@ -1583,36 +2128,34 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, } - - case BuiltinProc_atomic_fence: - LLVMBuildFence(p->builder, LLVMAtomicOrderingSequentiallyConsistent, false, ""); + // TODO(bill): Which is correct? + case BuiltinProc_atomic_thread_fence: + LLVMBuildFence(p->builder, llvm_atomic_ordering_from_odin(ce->args[0]), false, ""); return {}; - case BuiltinProc_atomic_fence_acq: - LLVMBuildFence(p->builder, LLVMAtomicOrderingAcquire, false, ""); - return {}; - case BuiltinProc_atomic_fence_rel: - LLVMBuildFence(p->builder, LLVMAtomicOrderingRelease, false, ""); - return {}; - case BuiltinProc_atomic_fence_acqrel: - LLVMBuildFence(p->builder, LLVMAtomicOrderingAcquireRelease, false, ""); + case BuiltinProc_atomic_signal_fence: + LLVMBuildFence(p->builder, llvm_atomic_ordering_from_odin(ce->args[0]), true, ""); return {}; case BuiltinProc_volatile_store: + case BuiltinProc_non_temporal_store: case BuiltinProc_atomic_store: - case BuiltinProc_atomic_store_rel: - case BuiltinProc_atomic_store_relaxed: - case BuiltinProc_atomic_store_unordered: { + case BuiltinProc_atomic_store_explicit: { lbValue dst = lb_build_expr(p, ce->args[0]); lbValue val = lb_build_expr(p, ce->args[1]); val = lb_emit_conv(p, val, type_deref(dst.type)); LLVMValueRef instr = LLVMBuildStore(p->builder, val.value, dst.value); switch (id) { - case BuiltinProc_volatile_store: LLVMSetVolatile(instr, true); break; - case BuiltinProc_atomic_store: LLVMSetOrdering(instr, LLVMAtomicOrderingSequentiallyConsistent); break; - case BuiltinProc_atomic_store_rel: LLVMSetOrdering(instr, LLVMAtomicOrderingRelease); break; - case BuiltinProc_atomic_store_relaxed: LLVMSetOrdering(instr, LLVMAtomicOrderingMonotonic); break; - case BuiltinProc_atomic_store_unordered: LLVMSetOrdering(instr, LLVMAtomicOrderingUnordered); break; + case BuiltinProc_non_temporal_store: + { + unsigned kind_id = LLVMGetMDKindIDInContext(p->module->ctx, "nontemporal", 11); + LLVMMetadataRef node = LLVMValueAsMetadata(LLVMConstInt(lb_type(p->module, t_u32), 1, false)); + LLVMSetMetadata(instr, kind_id, LLVMMetadataAsValue(p->module->ctx, node)); + } + break; + case BuiltinProc_volatile_store: LLVMSetVolatile(instr, true); break; + case BuiltinProc_atomic_store: LLVMSetOrdering(instr, LLVMAtomicOrderingSequentiallyConsistent); break; + case BuiltinProc_atomic_store_explicit: LLVMSetOrdering(instr, llvm_atomic_ordering_from_odin(ce->args[2])); break; } LLVMSetAlignment(instr, cast(unsigned)type_align_of(type_deref(dst.type))); @@ -1621,19 +2164,24 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, } case BuiltinProc_volatile_load: + case BuiltinProc_non_temporal_load: case BuiltinProc_atomic_load: - case BuiltinProc_atomic_load_acq: - case BuiltinProc_atomic_load_relaxed: - case BuiltinProc_atomic_load_unordered: { + case BuiltinProc_atomic_load_explicit: { lbValue dst = lb_build_expr(p, ce->args[0]); LLVMValueRef instr = LLVMBuildLoad(p->builder, dst.value, ""); switch (id) { - case BuiltinProc_volatile_load: LLVMSetVolatile(instr, true); break; - case BuiltinProc_atomic_load: LLVMSetOrdering(instr, LLVMAtomicOrderingSequentiallyConsistent); break; - case BuiltinProc_atomic_load_acq: LLVMSetOrdering(instr, LLVMAtomicOrderingAcquire); break; - case BuiltinProc_atomic_load_relaxed: LLVMSetOrdering(instr, LLVMAtomicOrderingMonotonic); break; - case BuiltinProc_atomic_load_unordered: LLVMSetOrdering(instr, LLVMAtomicOrderingUnordered); break; + case BuiltinProc_non_temporal_load: + { + unsigned kind_id = LLVMGetMDKindIDInContext(p->module->ctx, "nontemporal", 11); + LLVMMetadataRef node = LLVMValueAsMetadata(LLVMConstInt(lb_type(p->module, t_u32), 1, false)); + LLVMSetMetadata(instr, kind_id, LLVMMetadataAsValue(p->module->ctx, node)); + } + break; + break; + case BuiltinProc_volatile_load: LLVMSetVolatile(instr, true); break; + case BuiltinProc_atomic_load: LLVMSetOrdering(instr, LLVMAtomicOrderingSequentiallyConsistent); break; + case BuiltinProc_atomic_load_explicit: LLVMSetOrdering(instr, llvm_atomic_ordering_from_odin(ce->args[1])); break; } LLVMSetAlignment(instr, cast(unsigned)type_align_of(type_deref(dst.type))); @@ -1642,7 +2190,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, res.type = type_deref(dst.type); return res; } - + case BuiltinProc_unaligned_store: { lbValue dst = lb_build_expr(p, ce->args[0]); @@ -1652,7 +2200,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, lb_mem_copy_non_overlapping(p, dst, src, lb_const_int(p->module, t_int, type_size_of(t)), false); return {}; } - + case BuiltinProc_unaligned_load: { lbValue src = lb_build_expr(p, ce->args[0]); @@ -1663,40 +2211,19 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, } case BuiltinProc_atomic_add: - case BuiltinProc_atomic_add_acq: - case BuiltinProc_atomic_add_rel: - case BuiltinProc_atomic_add_acqrel: - case BuiltinProc_atomic_add_relaxed: case BuiltinProc_atomic_sub: - case BuiltinProc_atomic_sub_acq: - case BuiltinProc_atomic_sub_rel: - case BuiltinProc_atomic_sub_acqrel: - case BuiltinProc_atomic_sub_relaxed: case BuiltinProc_atomic_and: - case BuiltinProc_atomic_and_acq: - case BuiltinProc_atomic_and_rel: - case BuiltinProc_atomic_and_acqrel: - case BuiltinProc_atomic_and_relaxed: case BuiltinProc_atomic_nand: - case BuiltinProc_atomic_nand_acq: - case BuiltinProc_atomic_nand_rel: - case BuiltinProc_atomic_nand_acqrel: - case BuiltinProc_atomic_nand_relaxed: case BuiltinProc_atomic_or: - case BuiltinProc_atomic_or_acq: - case BuiltinProc_atomic_or_rel: - case BuiltinProc_atomic_or_acqrel: - case BuiltinProc_atomic_or_relaxed: case BuiltinProc_atomic_xor: - case BuiltinProc_atomic_xor_acq: - case BuiltinProc_atomic_xor_rel: - case BuiltinProc_atomic_xor_acqrel: - case BuiltinProc_atomic_xor_relaxed: - case BuiltinProc_atomic_xchg: - case BuiltinProc_atomic_xchg_acq: - case BuiltinProc_atomic_xchg_rel: - case BuiltinProc_atomic_xchg_acqrel: - case BuiltinProc_atomic_xchg_relaxed: { + case BuiltinProc_atomic_exchange: + case BuiltinProc_atomic_add_explicit: + case BuiltinProc_atomic_sub_explicit: + case BuiltinProc_atomic_and_explicit: + case BuiltinProc_atomic_nand_explicit: + case BuiltinProc_atomic_or_explicit: + case BuiltinProc_atomic_xor_explicit: + case BuiltinProc_atomic_exchange_explicit: { lbValue dst = lb_build_expr(p, ce->args[0]); lbValue val = lb_build_expr(p, ce->args[1]); val = lb_emit_conv(p, val, type_deref(dst.type)); @@ -1705,41 +2232,20 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, LLVMAtomicOrdering ordering = {}; switch (id) { - case BuiltinProc_atomic_add: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingSequentiallyConsistent; break; - case BuiltinProc_atomic_add_acq: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingAcquire; break; - case BuiltinProc_atomic_add_rel: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingRelease; break; - case BuiltinProc_atomic_add_acqrel: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingAcquireRelease; break; - case BuiltinProc_atomic_add_relaxed: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingMonotonic; break; - case BuiltinProc_atomic_sub: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingSequentiallyConsistent; break; - case BuiltinProc_atomic_sub_acq: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingAcquire; break; - case BuiltinProc_atomic_sub_rel: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingRelease; break; - case BuiltinProc_atomic_sub_acqrel: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingAcquireRelease; break; - case BuiltinProc_atomic_sub_relaxed: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingMonotonic; break; - case BuiltinProc_atomic_and: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingSequentiallyConsistent; break; - case BuiltinProc_atomic_and_acq: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingAcquire; break; - case BuiltinProc_atomic_and_rel: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingRelease; break; - case BuiltinProc_atomic_and_acqrel: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingAcquireRelease; break; - case BuiltinProc_atomic_and_relaxed: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingMonotonic; break; - case BuiltinProc_atomic_nand: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingSequentiallyConsistent; break; - case BuiltinProc_atomic_nand_acq: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingAcquire; break; - case BuiltinProc_atomic_nand_rel: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingRelease; break; - case BuiltinProc_atomic_nand_acqrel: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingAcquireRelease; break; - case BuiltinProc_atomic_nand_relaxed: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingMonotonic; break; - case BuiltinProc_atomic_or: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingSequentiallyConsistent; break; - case BuiltinProc_atomic_or_acq: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingAcquire; break; - case BuiltinProc_atomic_or_rel: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingRelease; break; - case BuiltinProc_atomic_or_acqrel: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingAcquireRelease; break; - case BuiltinProc_atomic_or_relaxed: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingMonotonic; break; - case BuiltinProc_atomic_xor: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingSequentiallyConsistent; break; - case BuiltinProc_atomic_xor_acq: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingAcquire; break; - case BuiltinProc_atomic_xor_rel: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingRelease; break; - case BuiltinProc_atomic_xor_acqrel: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingAcquireRelease; break; - case BuiltinProc_atomic_xor_relaxed: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingMonotonic; break; - case BuiltinProc_atomic_xchg: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingSequentiallyConsistent; break; - case BuiltinProc_atomic_xchg_acq: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingAcquire; break; - case BuiltinProc_atomic_xchg_rel: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingRelease; break; - case BuiltinProc_atomic_xchg_acqrel: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingAcquireRelease; break; - case BuiltinProc_atomic_xchg_relaxed: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingMonotonic; break; + case BuiltinProc_atomic_add: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingSequentiallyConsistent; break; + case BuiltinProc_atomic_sub: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingSequentiallyConsistent; break; + case BuiltinProc_atomic_and: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingSequentiallyConsistent; break; + case BuiltinProc_atomic_nand: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingSequentiallyConsistent; break; + case BuiltinProc_atomic_or: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingSequentiallyConsistent; break; + case BuiltinProc_atomic_xor: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingSequentiallyConsistent; break; + case BuiltinProc_atomic_exchange: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingSequentiallyConsistent; break; + case BuiltinProc_atomic_add_explicit: op = LLVMAtomicRMWBinOpAdd; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break; + case BuiltinProc_atomic_sub_explicit: op = LLVMAtomicRMWBinOpSub; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break; + case BuiltinProc_atomic_and_explicit: op = LLVMAtomicRMWBinOpAnd; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break; + case BuiltinProc_atomic_nand_explicit: op = LLVMAtomicRMWBinOpNand; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break; + case BuiltinProc_atomic_or_explicit: op = LLVMAtomicRMWBinOpOr; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break; + case BuiltinProc_atomic_xor_explicit: op = LLVMAtomicRMWBinOpXor; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break; + case BuiltinProc_atomic_exchange_explicit: op = LLVMAtomicRMWBinOpXchg; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break; } lbValue res = {}; @@ -1748,24 +2254,10 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, return res; } - case BuiltinProc_atomic_cxchg: - case BuiltinProc_atomic_cxchg_acq: - case BuiltinProc_atomic_cxchg_rel: - case BuiltinProc_atomic_cxchg_acqrel: - case BuiltinProc_atomic_cxchg_relaxed: - case BuiltinProc_atomic_cxchg_failrelaxed: - case BuiltinProc_atomic_cxchg_failacq: - case BuiltinProc_atomic_cxchg_acq_failrelaxed: - case BuiltinProc_atomic_cxchg_acqrel_failrelaxed: - case BuiltinProc_atomic_cxchgweak: - case BuiltinProc_atomic_cxchgweak_acq: - case BuiltinProc_atomic_cxchgweak_rel: - case BuiltinProc_atomic_cxchgweak_acqrel: - case BuiltinProc_atomic_cxchgweak_relaxed: - case BuiltinProc_atomic_cxchgweak_failrelaxed: - case BuiltinProc_atomic_cxchgweak_failacq: - case BuiltinProc_atomic_cxchgweak_acq_failrelaxed: - case BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed: { + case BuiltinProc_atomic_compare_exchange_strong: + case BuiltinProc_atomic_compare_exchange_weak: + case BuiltinProc_atomic_compare_exchange_strong_explicit: + case BuiltinProc_atomic_compare_exchange_weak_explicit: { lbValue address = lb_build_expr(p, ce->args[0]); Type *elem = type_deref(address.type); lbValue old_value = lb_build_expr(p, ce->args[1]); @@ -1778,28 +2270,14 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, LLVMBool weak = false; switch (id) { - case BuiltinProc_atomic_cxchg: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break; - case BuiltinProc_atomic_cxchg_acq: success_ordering = LLVMAtomicOrderingAcquire; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break; - case BuiltinProc_atomic_cxchg_rel: success_ordering = LLVMAtomicOrderingRelease; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break; - case BuiltinProc_atomic_cxchg_acqrel: success_ordering = LLVMAtomicOrderingAcquireRelease; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break; - case BuiltinProc_atomic_cxchg_relaxed: success_ordering = LLVMAtomicOrderingMonotonic; failure_ordering = LLVMAtomicOrderingMonotonic; weak = false; break; - case BuiltinProc_atomic_cxchg_failrelaxed: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingMonotonic; weak = false; break; - case BuiltinProc_atomic_cxchg_failacq: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingAcquire; weak = false; break; - case BuiltinProc_atomic_cxchg_acq_failrelaxed: success_ordering = LLVMAtomicOrderingAcquire; failure_ordering = LLVMAtomicOrderingMonotonic; weak = false; break; - case BuiltinProc_atomic_cxchg_acqrel_failrelaxed: success_ordering = LLVMAtomicOrderingAcquireRelease; failure_ordering = LLVMAtomicOrderingMonotonic; weak = false; break; - case BuiltinProc_atomic_cxchgweak: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break; - case BuiltinProc_atomic_cxchgweak_acq: success_ordering = LLVMAtomicOrderingAcquire; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = true; break; - case BuiltinProc_atomic_cxchgweak_rel: success_ordering = LLVMAtomicOrderingRelease; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = true; break; - case BuiltinProc_atomic_cxchgweak_acqrel: success_ordering = LLVMAtomicOrderingAcquireRelease; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = true; break; - case BuiltinProc_atomic_cxchgweak_relaxed: success_ordering = LLVMAtomicOrderingMonotonic; failure_ordering = LLVMAtomicOrderingMonotonic; weak = true; break; - case BuiltinProc_atomic_cxchgweak_failrelaxed: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingMonotonic; weak = true; break; - case BuiltinProc_atomic_cxchgweak_failacq: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingAcquire; weak = true; break; - case BuiltinProc_atomic_cxchgweak_acq_failrelaxed: success_ordering = LLVMAtomicOrderingAcquire; failure_ordering = LLVMAtomicOrderingMonotonic; weak = true; break; - case BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed: success_ordering = LLVMAtomicOrderingAcquireRelease; failure_ordering = LLVMAtomicOrderingMonotonic; weak = true; break; + case BuiltinProc_atomic_compare_exchange_strong: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break; + case BuiltinProc_atomic_compare_exchange_weak: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = true; break; + case BuiltinProc_atomic_compare_exchange_strong_explicit: success_ordering = llvm_atomic_ordering_from_odin(ce->args[3]); failure_ordering = llvm_atomic_ordering_from_odin(ce->args[4]); weak = false; break; + case BuiltinProc_atomic_compare_exchange_weak_explicit: success_ordering = llvm_atomic_ordering_from_odin(ce->args[3]); failure_ordering = llvm_atomic_ordering_from_odin(ce->args[4]); weak = true; break; } // TODO(bill): Figure out how to make it weak - LLVMBool single_threaded = weak; + LLVMBool single_threaded = false; LLVMValueRef value = LLVMBuildAtomicCmpXchg( p->builder, address.value, @@ -1808,6 +2286,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, failure_ordering, single_threaded ); + LLVMSetWeak(value, weak); if (tv.type->kind == Type_Tuple) { Type *fix_typed = alloc_type_tuple(); @@ -1903,7 +2382,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, res.type = t; return lb_emit_conv(p, res, t); } - + case BuiltinProc_prefetch_read_instruction: case BuiltinProc_prefetch_read_data: case BuiltinProc_prefetch_write_instruction: @@ -1931,27 +2410,27 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, cache = 1; break; } - + char const *name = "llvm.prefetch"; - + LLVMTypeRef types[1] = {lb_type(p->module, t_rawptr)}; unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name)); GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0])); LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types)); - + LLVMTypeRef llvm_i32 = lb_type(p->module, t_i32); LLVMValueRef args[4] = {}; args[0] = ptr.value; args[1] = LLVMConstInt(llvm_i32, rw, false); args[2] = LLVMConstInt(llvm_i32, locality, false); args[3] = LLVMConstInt(llvm_i32, cache, false); - + lbValue res = {}; res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); res.type = nullptr; return res; } - + case BuiltinProc___entry_point: if (p->module->info->entry_point) { lbValue entry_point = lb_find_procedure_value_from_entity(p->module, p->module->info->entry_point); @@ -1969,22 +2448,22 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, arg = lb_emit_conv(p, arg, t_uintptr); args[i] = arg.value; } - + LLVMTypeRef llvm_uintptr = lb_type(p->module, t_uintptr); LLVMTypeRef *llvm_arg_types = gb_alloc_array(permanent_allocator(), LLVMTypeRef, arg_count); for (unsigned i = 0; i < arg_count; i++) { llvm_arg_types[i] = llvm_uintptr; } - + LLVMTypeRef func_type = LLVMFunctionType(llvm_uintptr, llvm_arg_types, arg_count, false); - + LLVMValueRef inline_asm = nullptr; - + switch (build_context.metrics.arch) { case TargetArch_amd64: { GB_ASSERT(arg_count <= 7); - + char asm_string[] = "syscall"; gbString constraints = gb_string_make(heap_allocator(), "={rax}"); for (unsigned i = 0; i < arg_count; i++) { @@ -2023,11 +2502,11 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, case TargetArch_i386: { GB_ASSERT(arg_count <= 7); - + char asm_string_default[] = "int $0x80"; char *asm_string = asm_string_default; gbString constraints = gb_string_make(heap_allocator(), "={eax}"); - + for (unsigned i = 0; i < gb_min(arg_count, 6); i++) { constraints = gb_string_appendc(constraints, ",{"); static char const *regs[] = { @@ -2044,56 +2523,81 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, if (arg_count == 7) { char asm_string7[] = "push %[arg6]\npush %%ebp\nmov 4(%%esp), %%ebp\nint $0x80\npop %%ebp\nadd $4, %%esp"; asm_string = asm_string7; - + constraints = gb_string_appendc(constraints, ",rm"); } - + inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints)); } break; case TargetArch_arm64: { - GB_ASSERT(arg_count <= 7); - - if(build_context.metrics.os == TargetOs_darwin) { - char asm_string[] = "svc #0x80"; - gbString constraints = gb_string_make(heap_allocator(), "={x0}"); - for (unsigned i = 0; i < arg_count; i++) { - constraints = gb_string_appendc(constraints, ",{"); - static char const *regs[] = { - "x16", - "x0", - "x1", - "x2", - "x3", - "x4", - "x5", - }; - constraints = gb_string_appendc(constraints, regs[i]); - constraints = gb_string_appendc(constraints, "}"); - } + GB_ASSERT(arg_count <= 7); - inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints)); - } else { - char asm_string[] = "svc #0"; - gbString constraints = gb_string_make(heap_allocator(), "={x0}"); - for (unsigned i = 0; i < arg_count; i++) { - constraints = gb_string_appendc(constraints, ",{"); - static char const *regs[] = { - "x8", - "x0", - "x1", - "x2", - "x3", - "x4", - "x5", - }; - constraints = gb_string_appendc(constraints, regs[i]); - constraints = gb_string_appendc(constraints, "}"); - } + if(build_context.metrics.os == TargetOs_darwin) { + char asm_string[] = "svc #0x80"; + gbString constraints = gb_string_make(heap_allocator(), "={x0}"); + for (unsigned i = 0; i < arg_count; i++) { + constraints = gb_string_appendc(constraints, ",{"); + static char const *regs[] = { + "x16", + "x0", + "x1", + "x2", + "x3", + "x4", + "x5", + }; + constraints = gb_string_appendc(constraints, regs[i]); + constraints = gb_string_appendc(constraints, "}"); + } - inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints)); - } + inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints)); + } else { + char asm_string[] = "svc #0"; + gbString constraints = gb_string_make(heap_allocator(), "={x0}"); + for (unsigned i = 0; i < arg_count; i++) { + constraints = gb_string_appendc(constraints, ",{"); + static char const *regs[] = { + "x8", + "x0", + "x1", + "x2", + "x3", + "x4", + "x5", + }; + constraints = gb_string_appendc(constraints, regs[i]); + constraints = gb_string_appendc(constraints, "}"); + } + + inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints)); + } + } + break; + case TargetArch_arm32: + { + // TODO(bill): Check this is correct + GB_ASSERT(arg_count <= 7); + + char asm_string[] = "svc #0"; + gbString constraints = gb_string_make(heap_allocator(), "={r0}"); + for (unsigned i = 0; i < arg_count; i++) { + constraints = gb_string_appendc(constraints, ",{"); + static char const *regs[] = { + "r8", + "r0", + "r1", + "r2", + "r3", + "r4", + "r5", + }; + constraints = gb_string_appendc(constraints, regs[i]); + constraints = gb_string_appendc(constraints, "}"); + } + + inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints)); } break; default: @@ -2105,6 +2609,210 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, res.type = t_uintptr; return res; } + + case BuiltinProc_objc_send: + return lb_handle_objc_send(p, expr); + + case BuiltinProc_objc_find_selector: return lb_handle_objc_find_selector(p, expr); + case BuiltinProc_objc_find_class: return lb_handle_objc_find_class(p, expr); + case BuiltinProc_objc_register_selector: return lb_handle_objc_register_selector(p, expr); + case BuiltinProc_objc_register_class: return lb_handle_objc_register_class(p, expr); + + + case BuiltinProc_constant_utf16_cstring: + { + auto const encode_surrogate_pair = [](Rune r, u16 *r1, u16 *r2) { + if (r < 0x10000 || r > 0x10ffff) { + *r1 = 0xfffd; + *r2 = 0xfffd; + } else { + r -= 0x10000; + *r1 = 0xd800 + ((r>>10)&0x3ff); + *r2 = 0xdc00 + (r&0x3ff); + } + }; + + lbModule *m = p->module; + + auto tav = type_and_value_of_expr(ce->args[0]); + GB_ASSERT(tav.value.kind == ExactValue_String); + String value = tav.value.value_string; + + LLVMTypeRef llvm_u16 = lb_type(m, t_u16); + + isize max_len = value.len*2 + 1; + LLVMValueRef *buffer = gb_alloc_array(temporary_allocator(), LLVMValueRef, max_len); + isize n = 0; + while (value.len > 0) { + Rune r = 0; + isize w = gb_utf8_decode(value.text, value.len, &r); + value.text += w; + value.len -= w; + if ((0 <= r && r < 0xd800) || (0xe000 <= r && r < 0x10000)) { + buffer[n++] = LLVMConstInt(llvm_u16, cast(u16)r, false); + } else if (0x10000 <= r && r <= 0x10ffff) { + u16 r1, r2; + encode_surrogate_pair(r, &r1, &r2); + buffer[n++] = LLVMConstInt(llvm_u16, r1, false); + buffer[n++] = LLVMConstInt(llvm_u16, r2, false); + } else { + buffer[n++] = LLVMConstInt(llvm_u16, 0xfffd, false); + } + } + + buffer[n++] = LLVMConstInt(llvm_u16, 0, false); + + LLVMValueRef array = LLVMConstArray(llvm_u16, buffer, cast(unsigned int)n); + + char *name = nullptr; + { + isize max_len = 7+8+1; + name = gb_alloc_array(permanent_allocator(), char, max_len); + u32 id = m->gen->global_array_index.fetch_add(1); + isize len = gb_snprintf(name, max_len, "csbs$%x", id); + len -= 1; + } + LLVMValueRef global_data = LLVMAddGlobal(m->mod, LLVMTypeOf(array), name); + LLVMSetInitializer(global_data, array); + LLVMSetLinkage(global_data, LLVMInternalLinkage); + + + + LLVMValueRef indices[] = { + LLVMConstInt(lb_type(m, t_u32), 0, false), + LLVMConstInt(lb_type(m, t_u32), 0, false), + }; + lbValue res = {}; + res.type = tv.type; + res.value = LLVMBuildInBoundsGEP(p->builder, global_data, indices, gb_count_of(indices), ""); + return res; + + } + + case BuiltinProc_wasm_memory_grow: + { + char const *name = "llvm.wasm.memory.grow"; + LLVMTypeRef types[1] = { + lb_type(p->module, t_uintptr), + }; + unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name)); + GB_ASSERT_MSG(id != 0, "Unable to find %s", name, LLVMPrintTypeToString(types[0])); + LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types)); + + LLVMValueRef args[2] = {}; + args[0] = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_uintptr).value; + args[1] = lb_emit_conv(p, lb_build_expr(p, ce->args[1]), t_uintptr).value; + + lbValue res = {}; + res.type = tv.type; + res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); + return res; + } + case BuiltinProc_wasm_memory_size: + { + char const *name = "llvm.wasm.memory.size"; + LLVMTypeRef types[1] = { + lb_type(p->module, t_uintptr), + }; + unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name)); + GB_ASSERT_MSG(id != 0, "Unable to find %s", name, LLVMPrintTypeToString(types[0])); + LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types)); + + LLVMValueRef args[1] = {}; + args[0] = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_uintptr).value; + + lbValue res = {}; + res.type = tv.type; + res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); + return res; + } + + case BuiltinProc_wasm_memory_atomic_wait32: + { + char const *name = "llvm.wasm.memory.atomic.wait32"; + LLVMTypeRef types[1] = { + lb_type(p->module, t_u32), + }; + unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name)); + GB_ASSERT_MSG(id != 0, "Unable to find %s", name, LLVMPrintTypeToString(types[0])); + LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, nullptr, 0); // types, gb_count_of(types)); + + Type *t_u32_ptr = alloc_type_pointer(t_u32); + + LLVMValueRef args[3] = {}; + args[0] = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_u32_ptr).value; + args[1] = lb_emit_conv(p, lb_build_expr(p, ce->args[1]), t_u32).value; + args[2] = lb_emit_conv(p, lb_build_expr(p, ce->args[2]), t_i64).value; + + lbValue res = {}; + res.type = tv.type; + res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); + return res; + } + + case BuiltinProc_wasm_memory_atomic_notify32: + { + char const *name = "llvm.wasm.memory.atomic.notify"; + LLVMTypeRef types[1] = { + lb_type(p->module, t_u32), + }; + unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name)); + GB_ASSERT_MSG(id != 0, "Unable to find %s", name, LLVMPrintTypeToString(types[0])); + LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, nullptr, 0); // types, gb_count_of(types)); + + Type *t_u32_ptr = alloc_type_pointer(t_u32); + + LLVMValueRef args[2] = {}; + args[0] = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_u32_ptr).value; + args[1] = lb_emit_conv(p, lb_build_expr(p, ce->args[1]), t_u32).value; + + lbValue res = {}; + res.type = tv.type; + res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); + return res; + } + + + case BuiltinProc_x86_cpuid: + { + Type *param_types[2] = {t_u32, t_u32}; + Type *type = alloc_type_proc_from_types(param_types, gb_count_of(param_types), tv.type, false, ProcCC_None); + LLVMTypeRef func_type = LLVMGetElementType(lb_type(p->module, type)); + LLVMValueRef the_asm = llvm_get_inline_asm( + func_type, + str_lit("cpuid"), + str_lit("={ax},={bx},={cx},={dx},{ax},{cx}"), + true + ); + GB_ASSERT(the_asm != nullptr); + + LLVMValueRef args[2] = {}; + args[0] = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_u32).value; + args[1] = lb_emit_conv(p, lb_build_expr(p, ce->args[1]), t_u32).value; + lbValue res = {}; + res.type = tv.type; + res.value = LLVMBuildCall2(p->builder, func_type, the_asm, args, gb_count_of(args), ""); + return res; + } + case BuiltinProc_x86_xgetbv: + { + Type *type = alloc_type_proc_from_types(&t_u32, 1, tv.type, false, ProcCC_None); + LLVMTypeRef func_type = LLVMGetElementType(lb_type(p->module, type)); + LLVMValueRef the_asm = llvm_get_inline_asm( + func_type, + str_lit("xgetbv"), + str_lit("={ax},={dx},{cx}"), + true + ); + GB_ASSERT(the_asm != nullptr); + + LLVMValueRef args[1] = {}; + args[0] = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_u32).value; + lbValue res = {}; + res.type = tv.type; + res.value = LLVMBuildCall2(p->builder, func_type, the_asm, args, gb_count_of(args), ""); + return res; + } } GB_PANIC("Unhandled built-in procedure %.*s", LIT(builtin_procs[id].name)); @@ -2153,10 +2861,6 @@ lbValue lb_build_call_expr(lbProcedure *p, Ast *expr) { expr = unparen_expr(expr); ast_node(ce, CallExpr, expr); - if (ce->sce_temp_data) { - return *(lbValue *)ce->sce_temp_data; - } - lbValue res = lb_build_call_expr_internal(p, expr); if (ce->optional_ok_one) { // TODO(bill): Minor hack for #optional_ok procedures @@ -2197,6 +2901,15 @@ lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { // NOTE(bill): Regular call lbValue value = {}; Ast *proc_expr = unparen_expr(ce->proc); + + Entity *proc_entity = entity_of_node(proc_expr); + if (proc_entity != nullptr) { + if (proc_entity->flags & EntityFlag_Disabled) { + GB_ASSERT(tv.type == nullptr); + return {}; + } + } + if (proc_expr->tav.mode == Addressing_Constant) { ExactValue v = proc_expr->tav.value; switch (v.kind) { @@ -2223,13 +2936,6 @@ lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { } } - Entity *proc_entity = entity_of_node(proc_expr); - if (proc_entity != nullptr) { - if (proc_entity->flags & EntityFlag_Disabled) { - return {}; - } - } - if (value.value == nullptr) { value = lb_build_expr(p, proc_expr); } diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp index 3375ceda9..f131bb3db 100644 --- a/src/llvm_backend_stmt.cpp +++ b/src/llvm_backend_stmt.cpp @@ -1210,8 +1210,8 @@ void lb_build_switch_stmt(lbProcedure *p, AstSwitchStmt *ss, Scope *scope) { } lb_emit_jump(p, done); - lb_close_scope(p, lbDeferExit_Default, done); lb_start_block(p, done); + lb_close_scope(p, lbDeferExit_Default, done); } void lb_store_type_case_implicit(lbProcedure *p, Ast *clause, lbValue value) { @@ -1253,7 +1253,6 @@ void lb_type_case_body(lbProcedure *p, Ast *label, Ast *clause, lbBlock *body, l ast_node(cc, CaseClause, clause); lb_push_target_list(p, label, done, nullptr, nullptr); - lb_open_scope(p, body->scope); lb_build_stmt_list(p, cc->stmts); lb_close_scope(p, lbDeferExit_Default, body); lb_pop_target_list(p); @@ -1263,6 +1262,7 @@ void lb_type_case_body(lbProcedure *p, Ast *label, Ast *clause, lbBlock *body, l void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss) { lbModule *m = p->module; + lb_open_scope(p, ss->scope); ast_node(as, AssignStmt, ss->tag); GB_ASSERT(as->lhs.count == 1); @@ -1321,6 +1321,7 @@ void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss) { for_array(i, body->stmts) { Ast *clause = body->stmts[i]; ast_node(cc, CaseClause, clause); + lb_open_scope(p, cc->scope); if (cc->list.count == 0) { lb_start_block(p, default_block); lb_store_type_case_implicit(p, clause, parent_value); @@ -1329,6 +1330,9 @@ void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss) { } lbBlock *body = lb_create_block(p, "typeswitch.body"); + if (p->debug_info != nullptr) { + LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, clause)); + } Type *case_type = nullptr; for_array(type_index, cc->list) { case_type = type_of_expr(cc->list[type_index]); @@ -1375,6 +1379,7 @@ void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss) { lb_emit_jump(p, done); lb_start_block(p, done); + lb_close_scope(p, lbDeferExit_Default, done); } @@ -1652,13 +1657,16 @@ void lb_build_if_stmt(lbProcedure *p, Ast *node) { } lbValue cond = lb_build_cond(p, is->cond, then, else_); + // Note `cond.value` only set for non-and/or conditions and const negs so that the `LLVMIsConstant()` + // and `LLVMConstIntGetZExtValue()` calls below will be valid and `LLVMInstructionEraseFromParent()` + // will target the correct (& only) branch statement if (is->label != nullptr) { lbTargetList *tl = lb_push_target_list(p, is->label, done, nullptr, nullptr); tl->is_block = true; } - if (LLVMIsConstant(cond.value)) { + if (cond.value && LLVMIsConstant(cond.value)) { // NOTE(bill): Do a compile time short circuit for when the condition is constantly known. // This done manually rather than relying on the SSA passes because sometimes the SSA passes // miss some even if they are constantly known, especially with few optimization passes. @@ -1718,6 +1726,9 @@ void lb_build_for_stmt(lbProcedure *p, Ast *node) { ast_node(fs, ForStmt, node); lb_open_scope(p, fs->scope); // Open Scope here + if (p->debug_info != nullptr) { + LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, node)); + } if (fs->init != nullptr) { #if 1 @@ -1738,11 +1749,14 @@ void lb_build_for_stmt(lbProcedure *p, Ast *node) { post = lb_create_block(p, "for.post"); } - lb_emit_jump(p, loop); lb_start_block(p, loop); if (loop != body) { + // right now the condition (all expressions) will not set it's debug location, so we will do it here + if (p->debug_info != nullptr) { + LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, fs->cond)); + } lb_build_cond(p, fs->cond, body, done); lb_start_block(p, body); } @@ -1750,10 +1764,12 @@ void lb_build_for_stmt(lbProcedure *p, Ast *node) { lb_push_target_list(p, fs->label, done, post, nullptr); lb_build_stmt(p, fs->body); - lb_close_scope(p, lbDeferExit_Default, nullptr); lb_pop_target_list(p); + if (p->debug_info != nullptr) { + LLVMSetCurrentDebugLocation2(p->builder, lb_debug_end_location_from_ast(p, fs->body)); + } lb_emit_jump(p, post); if (fs->post != nullptr) { @@ -1763,6 +1779,7 @@ void lb_build_for_stmt(lbProcedure *p, Ast *node) { } lb_start_block(p, done); + lb_close_scope(p, lbDeferExit_Default, nullptr); } void lb_build_assign_stmt_array(lbProcedure *p, TokenKind op, lbAddr const &lhs, lbValue const &value) { @@ -1968,14 +1985,9 @@ void lb_build_stmt(lbProcedure *p, Ast *node) { } } - LLVMMetadataRef prev_debug_location = nullptr; if (p->debug_info != nullptr) { - prev_debug_location = LLVMGetCurrentDebugLocation2(p->builder); LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, node)); } - defer (if (prev_debug_location != nullptr) { - LLVMSetCurrentDebugLocation2(p->builder, prev_debug_location); - }); u16 prev_state_flags = p->state_flags; defer (p->state_flags = prev_state_flags); @@ -1991,6 +2003,13 @@ void lb_build_stmt(lbProcedure *p, Ast *node) { out |= StateFlag_no_bounds_check; out &= ~StateFlag_bounds_check; } + if (in & StateFlag_no_type_assert) { + out |= StateFlag_no_type_assert; + out &= ~StateFlag_type_assert; + } else if (in & StateFlag_type_assert) { + out |= StateFlag_type_assert; + out &= ~StateFlag_no_type_assert; + } p->state_flags = out; } @@ -2063,7 +2082,8 @@ void lb_build_stmt(lbProcedure *p, Ast *node) { lbAddr lval = {}; if (!is_blank_ident(name)) { Entity *e = entity_of_node(name); - bool zero_init = true; // Always do it + // bool zero_init = true; // Always do it + bool zero_init = vd->values.count == 0; lval = lb_add_local(p, e->type, e, zero_init); } array_add(&lvals, lval); diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp index e1332c6f3..2e7b2788a 100644 --- a/src/llvm_backend_type.cpp +++ b/src/llvm_backend_type.cpp @@ -14,6 +14,8 @@ isize lb_type_info_index(CheckerInfo *info, Type *type, bool err_on_not_found=tr } lbValue lb_typeid(lbModule *m, Type *type) { + GB_ASSERT(!build_context.disallow_rtti); + type = default_type(type); u64 id = cast(u64)lb_type_info_index(m->info, type); @@ -88,6 +90,8 @@ lbValue lb_typeid(lbModule *m, Type *type) { } lbValue lb_type_info(lbModule *m, Type *type) { + GB_ASSERT(!build_context.disallow_rtti); + type = default_type(type); isize index = lb_type_info_index(m->info, type); @@ -106,6 +110,8 @@ lbValue lb_type_info(lbModule *m, Type *type) { } lbValue lb_get_type_info_ptr(lbModule *m, Type *type) { + GB_ASSERT(!build_context.disallow_rtti); + i32 index = cast(i32)lb_type_info_index(m->info, type); GB_ASSERT(index >= 0); // gb_printf_err("%d %s\n", index, type_to_string(type)); @@ -155,6 +161,10 @@ lbValue lb_type_info_member_tags_offset(lbProcedure *p, isize count) { void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info data + if (build_context.disallow_rtti) { + return; + } + lbModule *m = p->module; CheckerInfo *info = m->info; @@ -454,7 +464,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da case Type_EnumeratedArray: { tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_enumerated_array_ptr); - LLVMValueRef vals[6] = { + LLVMValueRef vals[7] = { lb_get_type_info_ptr(m, t->EnumeratedArray.elem).value, lb_get_type_info_ptr(m, t->EnumeratedArray.index).value, lb_const_int(m, t_int, type_size_of(t->EnumeratedArray.elem)).value, @@ -463,6 +473,8 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da // Unions LLVMConstNull(lb_type(m, t_type_info_enum_value)), LLVMConstNull(lb_type(m, t_type_info_enum_value)), + + lb_const_bool(m, t_bool, t->EnumeratedArray.is_sparse).value, }; lbValue res = {}; @@ -663,8 +675,8 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da } vals[4] = lb_const_bool(m, t_bool, t->Union.custom_align != 0).value; - vals[5] = lb_const_bool(m, t_bool, t->Union.no_nil).value; - vals[6] = lb_const_bool(m, t_bool, t->Union.maybe).value; + vals[5] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_no_nil).value; + vals[6] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_shared_nil).value; for (isize i = 0; i < gb_count_of(vals); i++) { if (vals[i] == nullptr) { diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index 5b1b11b44..88ec2f22c 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -1,3 +1,5 @@ +lbValue lb_lookup_runtime_procedure(lbModule *m, String const &name); + bool lb_is_type_aggregate(Type *t) { t = base_type(t); switch (t->kind) { @@ -48,18 +50,19 @@ lbValue lb_correct_endianness(lbProcedure *p, lbValue value) { return value; } -void lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef ptr, LLVMValueRef len, unsigned alignment, bool is_volatile) { +LLVMValueRef lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef ptr, LLVMValueRef len, unsigned alignment, bool is_volatile) { bool is_inlinable = false; i64 const_len = 0; if (LLVMIsConstant(len)) { const_len = cast(i64)LLVMConstIntGetSExtValue(len); // TODO(bill): Determine when it is better to do the `*.inline` versions - if (const_len <= 4*build_context.word_size) { + if (const_len <= lb_max_zero_init_size()) { is_inlinable = true; } } + char const *name = "llvm.memset"; if (is_inlinable) { name = "llvm.memset.inline"; @@ -69,17 +72,29 @@ void lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef ptr, LLVMValueRef len lb_type(p->module, t_rawptr), lb_type(p->module, t_int) }; - unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name)); - GB_ASSERT_MSG(id != 0, "Unable to find %s.%s.%s", name, LLVMPrintTypeToString(types[0]), LLVMPrintTypeToString(types[1])); - LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types)); + if (true || is_inlinable) { + unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name)); + GB_ASSERT_MSG(id != 0, "Unable to find %s.%s.%s", name, LLVMPrintTypeToString(types[0]), LLVMPrintTypeToString(types[1])); + LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types)); - LLVMValueRef args[4] = {}; - args[0] = LLVMBuildPointerCast(p->builder, ptr, types[0], ""); - args[1] = LLVMConstInt(LLVMInt8TypeInContext(p->module->ctx), 0, false); - args[2] = LLVMBuildIntCast2(p->builder, len, types[1], /*signed*/false, ""); - args[3] = LLVMConstInt(LLVMInt1TypeInContext(p->module->ctx), is_volatile, false); + LLVMValueRef args[4] = {}; + args[0] = LLVMBuildPointerCast(p->builder, ptr, types[0], ""); + args[1] = LLVMConstInt(LLVMInt8TypeInContext(p->module->ctx), 0, false); + args[2] = LLVMBuildIntCast2(p->builder, len, types[1], /*signed*/false, ""); + args[3] = LLVMConstInt(LLVMInt1TypeInContext(p->module->ctx), is_volatile, false); + + return LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); + } else { + LLVMValueRef ip = lb_lookup_runtime_procedure(p->module, str_lit("memset")).value; + + LLVMValueRef args[3] = {}; + args[0] = LLVMBuildPointerCast(p->builder, ptr, types[0], ""); + args[1] = LLVMConstInt(LLVMInt32TypeInContext(p->module->ctx), 0, false); + args[2] = LLVMBuildIntCast2(p->builder, len, types[1], /*signed*/false, ""); + + return LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); + } - LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); } void lb_mem_zero_ptr(lbProcedure *p, LLVMValueRef ptr, Type *type, unsigned alignment) { @@ -201,6 +216,11 @@ lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t) { return res; } + if (is_type_simd_vector(src) && is_type_simd_vector(dst)) { + res.value = LLVMBuildBitCast(p->builder, value.value, lb_type(p->module, t), ""); + return res; + } + if (lb_is_type_aggregate(src) || lb_is_type_aggregate(dst)) { lbValue s = lb_address_from_load_or_generate_local(p, value); lbValue d = lb_emit_transmute(p, s, alloc_type_pointer(t)); @@ -480,8 +500,10 @@ lbValue lb_emit_count_ones(lbProcedure *p, lbValue x, Type *type) { } lbValue lb_emit_count_zeros(lbProcedure *p, lbValue x, Type *type) { - i64 sz = 8*type_size_of(type); - lbValue size = lb_const_int(p->module, type, cast(u64)sz); + Type *elem = base_array_type(type); + i64 sz = 8*type_size_of(elem); + lbValue size = lb_const_int(p->module, elem, cast(u64)sz); + size = lb_emit_conv(p, size, type); lbValue count = lb_emit_count_ones(p, x, type); return lb_emit_arith(p, Token_Sub, size, count, type); } @@ -626,6 +648,12 @@ lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type, TokenPos p lbValue value_ = lb_address_from_load_or_generate_local(p, value); + if ((p->state_flags & StateFlag_no_type_assert) != 0 && !is_tuple) { + // just do a bit cast of the data at the front + lbValue ptr = lb_emit_conv(p, value_, alloc_type_pointer(type)); + return lb_emit_load(p, ptr); + } + lbValue tag = {}; lbValue dst_tag = {}; lbValue cond = {}; @@ -666,23 +694,29 @@ lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type, TokenPos p lb_start_block(p, end_block); if (!is_tuple) { - { - // NOTE(bill): Panic on invalid conversion - Type *dst_type = tuple->Tuple.variables[0]->type; + GB_ASSERT((p->state_flags & StateFlag_no_type_assert) == 0); + // NOTE(bill): Panic on invalid conversion + Type *dst_type = tuple->Tuple.variables[0]->type; - lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1)); - auto args = array_make(permanent_allocator(), 7); - args[0] = ok; + isize arg_count = 7; + if (build_context.disallow_rtti) { + arg_count = 4; + } - args[1] = lb_const_string(m, get_file_path_string(pos.file_id)); - args[2] = lb_const_int(m, t_i32, pos.line); - args[3] = lb_const_int(m, t_i32, pos.column); + lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1)); + auto args = array_make(permanent_allocator(), arg_count); + args[0] = ok; + args[1] = lb_const_string(m, get_file_path_string(pos.file_id)); + args[2] = lb_const_int(m, t_i32, pos.line); + args[3] = lb_const_int(m, t_i32, pos.column); + + if (!build_context.disallow_rtti) { args[4] = lb_typeid(m, src_type); args[5] = lb_typeid(m, dst_type); args[6] = lb_emit_conv(p, value_, t_rawptr); - lb_emit_runtime_call(p, "type_assertion_check2", args); } + lb_emit_runtime_call(p, "type_assertion_check2", args); return lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 0)); } @@ -706,6 +740,13 @@ lbAddr lb_emit_any_cast_addr(lbProcedure *p, lbValue value, Type *type, TokenPos } Type *dst_type = tuple->Tuple.variables[0]->type; + if ((p->state_flags & StateFlag_no_type_assert) != 0 && !is_tuple) { + // just do a bit cast of the data at the front + lbValue ptr = lb_emit_struct_ev(p, value, 0); + ptr = lb_emit_conv(p, ptr, alloc_type_pointer(type)); + return lb_addr(ptr); + } + lbAddr v = lb_add_local_generated(p, tuple, true); lbValue dst_typeid = lb_typeid(m, dst_type); @@ -731,18 +772,24 @@ lbAddr lb_emit_any_cast_addr(lbProcedure *p, lbValue value, Type *type, TokenPos if (!is_tuple) { // NOTE(bill): Panic on invalid conversion - lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1)); - auto args = array_make(permanent_allocator(), 7); + + isize arg_count = 7; + if (build_context.disallow_rtti) { + arg_count = 4; + } + auto args = array_make(permanent_allocator(), arg_count); args[0] = ok; args[1] = lb_const_string(m, get_file_path_string(pos.file_id)); args[2] = lb_const_int(m, t_i32, pos.line); args[3] = lb_const_int(m, t_i32, pos.column); - args[4] = any_typeid; - args[5] = dst_typeid; - args[6] = lb_emit_struct_ev(p, value, 0);; + if (!build_context.disallow_rtti) { + args[4] = any_typeid; + args[5] = dst_typeid; + args[6] = lb_emit_struct_ev(p, value, 0); + } lb_emit_runtime_call(p, "type_assertion_check2", args); return lb_addr(lb_emit_struct_ep(p, v.addr, 0)); @@ -1362,7 +1409,7 @@ lbValue lb_slice_elem(lbProcedure *p, lbValue slice) { return lb_emit_struct_ev(p, slice, 0); } lbValue lb_slice_len(lbProcedure *p, lbValue slice) { - GB_ASSERT(is_type_slice(slice.type)); + GB_ASSERT(is_type_slice(slice.type) || is_type_relative_slice(slice.type)); return lb_emit_struct_ev(p, slice, 1); } lbValue lb_dynamic_array_elem(lbProcedure *p, lbValue da) { @@ -1768,7 +1815,7 @@ LLVMValueRef llvm_get_inline_asm(LLVMTypeRef func_type, String const &str, Strin return LLVMGetInlineAsm(func_type, cast(char *)str.text, cast(size_t)str.len, cast(char *)clobbers.text, cast(size_t)clobbers.len, - /*HasSideEffects*/true, /*IsAlignStack*/false, + has_side_effects, is_align_stack, dialect #if LLVM_VERSION_MAJOR >= 13 , /*CanThrow*/false @@ -1808,3 +1855,195 @@ void lb_set_wasm_export_attributes(LLVMValueRef value, String export_name) { LLVMSetVisibility(value, LLVMDefaultVisibility); LLVMAddTargetDependentFunctionAttr(value, "wasm-export-name", alloc_cstring(permanent_allocator(), export_name)); } + + + +lbAddr lb_handle_objc_find_or_register_selector(lbProcedure *p, String const &name) { + lbAddr *found = string_map_get(&p->module->objc_selectors, name); + if (found) { + return *found; + } else { + lbModule *default_module = &p->module->gen->default_module; + Entity *e = nullptr; + lbAddr default_addr = lb_add_global_generated(default_module, t_objc_SEL, {}, &e); + + lbValue ptr = lb_find_value_from_entity(p->module, e); + lbAddr local_addr = lb_addr(ptr); + + string_map_set(&default_module->objc_selectors, name, default_addr); + if (default_module != p->module) { + string_map_set(&p->module->objc_selectors, name, local_addr); + } + return local_addr; + } +} + +lbValue lb_handle_objc_find_selector(lbProcedure *p, Ast *expr) { + ast_node(ce, CallExpr, expr); + + auto tav = ce->args[0]->tav; + GB_ASSERT(tav.value.kind == ExactValue_String); + String name = tav.value.value_string; + return lb_addr_load(p, lb_handle_objc_find_or_register_selector(p, name)); +} + +lbValue lb_handle_objc_register_selector(lbProcedure *p, Ast *expr) { + ast_node(ce, CallExpr, expr); + lbModule *m = p->module; + + auto tav = ce->args[0]->tav; + GB_ASSERT(tav.value.kind == ExactValue_String); + String name = tav.value.value_string; + lbAddr dst = lb_handle_objc_find_or_register_selector(p, name); + + auto args = array_make(permanent_allocator(), 1); + args[0] = lb_const_value(m, t_cstring, exact_value_string(name)); + lbValue ptr = lb_emit_runtime_call(p, "sel_registerName", args); + lb_addr_store(p, dst, ptr); + + return lb_addr_load(p, dst); +} + +lbAddr lb_handle_objc_find_or_register_class(lbProcedure *p, String const &name) { + lbAddr *found = string_map_get(&p->module->objc_classes, name); + if (found) { + return *found; + } else { + lbModule *default_module = &p->module->gen->default_module; + Entity *e = nullptr; + lbAddr default_addr = lb_add_global_generated(default_module, t_objc_SEL, {}, &e); + + lbValue ptr = lb_find_value_from_entity(p->module, e); + lbAddr local_addr = lb_addr(ptr); + + string_map_set(&default_module->objc_classes, name, default_addr); + if (default_module != p->module) { + string_map_set(&p->module->objc_classes, name, local_addr); + } + return local_addr; + } +} + +lbValue lb_handle_objc_find_class(lbProcedure *p, Ast *expr) { + ast_node(ce, CallExpr, expr); + + auto tav = ce->args[0]->tav; + GB_ASSERT(tav.value.kind == ExactValue_String); + String name = tav.value.value_string; + return lb_addr_load(p, lb_handle_objc_find_or_register_class(p, name)); +} + +lbValue lb_handle_objc_register_class(lbProcedure *p, Ast *expr) { + ast_node(ce, CallExpr, expr); + lbModule *m = p->module; + + auto tav = ce->args[0]->tav; + GB_ASSERT(tav.value.kind == ExactValue_String); + String name = tav.value.value_string; + lbAddr dst = lb_handle_objc_find_or_register_class(p, name); + + auto args = array_make(permanent_allocator(), 3); + args[0] = lb_const_nil(m, t_objc_Class); + args[1] = lb_const_nil(m, t_objc_Class); + args[2] = lb_const_int(m, t_uint, 0); + lbValue ptr = lb_emit_runtime_call(p, "objc_allocateClassPair", args); + lb_addr_store(p, dst, ptr); + + return lb_addr_load(p, dst); +} + + +lbValue lb_handle_objc_id(lbProcedure *p, Ast *expr) { + TypeAndValue const &tav = type_and_value_of_expr(expr); + if (tav.mode == Addressing_Type) { + Type *type = tav.type; + GB_ASSERT_MSG(type->kind == Type_Named, "%s", type_to_string(type)); + Entity *e = type->Named.type_name; + GB_ASSERT(e->kind == Entity_TypeName); + String name = e->TypeName.objc_class_name; + + lbAddr *found = string_map_get(&p->module->objc_classes, name); + if (found) { + return lb_addr_load(p, *found); + } else { + lbModule *default_module = &p->module->gen->default_module; + Entity *e = nullptr; + lbAddr default_addr = lb_add_global_generated(default_module, t_objc_Class, {}, &e); + + lbValue ptr = lb_find_value_from_entity(p->module, e); + lbAddr local_addr = lb_addr(ptr); + + string_map_set(&default_module->objc_classes, name, default_addr); + if (default_module != p->module) { + string_map_set(&p->module->objc_classes, name, local_addr); + } + return lb_addr_load(p, local_addr); + } + } + + return lb_build_expr(p, expr); +} + +lbValue lb_handle_objc_send(lbProcedure *p, Ast *expr) { + ast_node(ce, CallExpr, expr); + + lbModule *m = p->module; + CheckerInfo *info = m->info; + ObjcMsgData data = map_must_get(&info->objc_msgSend_types, expr); + GB_ASSERT(data.proc_type != nullptr); + + GB_ASSERT(ce->args.count >= 3); + auto args = array_make(permanent_allocator(), 0, ce->args.count-1); + + lbValue id = lb_handle_objc_id(p, ce->args[1]); + Ast *sel_expr = ce->args[2]; + GB_ASSERT(sel_expr->tav.value.kind == ExactValue_String); + lbValue sel = lb_addr_load(p, lb_handle_objc_find_or_register_selector(p, sel_expr->tav.value.value_string)); + + array_add(&args, id); + array_add(&args, sel); + for (isize i = 3; i < ce->args.count; i++) { + lbValue arg = lb_build_expr(p, ce->args[i]); + array_add(&args, arg); + } + + + lbValue the_proc = {}; + switch (data.kind) { + default: + GB_PANIC("unhandled ObjcMsgKind %u", data.kind); + break; + case ObjcMsg_normal: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend")); break; + case ObjcMsg_fpret: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend_fpret")); break; + case ObjcMsg_fp2ret: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend_fp2ret")); break; + case ObjcMsg_stret: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend_stret")); break; + } + + the_proc = lb_emit_conv(p, the_proc, data.proc_type); + + return lb_emit_call(p, the_proc, args); +} + + + + +LLVMAtomicOrdering llvm_atomic_ordering_from_odin(ExactValue const &value) { + GB_ASSERT(value.kind == ExactValue_Integer); + i64 v = exact_value_to_i64(value); + switch (v) { + case OdinAtomicMemoryOrder_relaxed: return LLVMAtomicOrderingUnordered; + case OdinAtomicMemoryOrder_consume: return LLVMAtomicOrderingMonotonic; + case OdinAtomicMemoryOrder_acquire: return LLVMAtomicOrderingAcquire; + case OdinAtomicMemoryOrder_release: return LLVMAtomicOrderingRelease; + case OdinAtomicMemoryOrder_acq_rel: return LLVMAtomicOrderingAcquireRelease; + case OdinAtomicMemoryOrder_seq_cst: return LLVMAtomicOrderingSequentiallyConsistent; + } + GB_PANIC("Unknown atomic ordering"); + return LLVMAtomicOrderingSequentiallyConsistent; +} + + +LLVMAtomicOrdering llvm_atomic_ordering_from_odin(Ast *expr) { + ExactValue value = type_and_value_of_expr(expr).value; + return llvm_atomic_ordering_from_odin(value); +} diff --git a/src/main.cpp b/src/main.cpp index fe56d451f..beefec702 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -46,7 +46,6 @@ gb_global Timings global_timings = {0}; #include "checker.cpp" #include "docs.cpp" - #include "llvm_backend.cpp" #if defined(GB_SYSTEM_OSX) @@ -57,16 +56,8 @@ gb_global Timings global_timings = {0}; #endif #include "query_data.cpp" - - -#if defined(GB_SYSTEM_WINDOWS) -// NOTE(IC): In order to find Visual C++ paths without relying on environment variables. -#include "microsoft_craziness.h" -#endif - #include "bug_report.cpp" - // NOTE(bill): 'name' is used in debugging and profiling modes i32 system_exec_command_line_app(char const *name, char const *fmt, ...) { isize const cmd_cap = 64<<20; // 64 MiB should be more than enough @@ -117,6 +108,9 @@ i32 system_exec_command_line_app(char const *name, char const *fmt, ...) { gb_printf_err("%s\n\n", cmd_line); } exit_code = system(cmd_line); + if (WIFEXITED(exit_code)) { + exit_code = WEXITSTATUS(exit_code); + } #endif if (exit_code) { @@ -127,34 +121,35 @@ i32 system_exec_command_line_app(char const *name, char const *fmt, ...) { } - - i32 linker_stage(lbGenerator *gen) { i32 result = 0; Timings *timings = &global_timings; - String output_base = gen->output_base; + String output_filename = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Output]); + debugf("Linking %.*s\n", LIT(output_filename)); + + // TOOD(Jeroen): Make a `build_paths[BuildPath_Object] to avoid `%.*s.o`. if (is_arch_wasm()) { timings_start_section(timings, str_lit("wasm-ld")); #if defined(GB_SYSTEM_WINDOWS) result = system_exec_command_line_app("wasm-ld", - "\"%.*s\\bin\\wasm-ld\" \"%.*s.wasm.o\" -o \"%.*s.wasm\" %.*s %.*s", + "\"%.*s\\bin\\wasm-ld\" \"%.*s.o\" -o \"%.*s\" %.*s %.*s", LIT(build_context.ODIN_ROOT), - LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags)); + LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags)); #else result = system_exec_command_line_app("wasm-ld", - "wasm-ld \"%.*s.wasm.o\" -o \"%.*s.wasm\" %.*s %.*s", - LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags)); + "wasm-ld \"%.*s.o\" -o \"%.*s\" %.*s %.*s", + LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags)); #endif return result; } if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) { -#ifdef GB_SYSTEM_UNIX +#if defined(GB_SYSTEM_UNIX) result = system_exec_command_line_app("linker", "x86_64-essence-gcc \"%.*s.o\" -o \"%.*s\" %.*s %.*s", - LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags)); + LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags)); #else gb_printf_err("Linking for cross compilation for this platform is not yet supported (%.*s %.*s)\n", LIT(target_os_names[build_context.metrics.os]), @@ -169,359 +164,367 @@ i32 linker_stage(lbGenerator *gen) { build_context.keep_object_files = true; } else { #if defined(GB_SYSTEM_WINDOWS) - String section_name = str_lit("msvc-link"); - if (build_context.use_lld) { - section_name = str_lit("lld-link"); - } - timings_start_section(timings, section_name); - - gbString lib_str = gb_string_make(heap_allocator(), ""); - defer (gb_string_free(lib_str)); - - char const *output_ext = "exe"; - gbString link_settings = gb_string_make_reserve(heap_allocator(), 256); - defer (gb_string_free(link_settings)); + bool is_windows = true; + #else + bool is_windows = false; + #endif + #if defined(GB_SYSTEM_OSX) + bool is_osx = true; + #else + bool is_osx = false; + #endif - // NOTE(ic): It would be nice to extend this so that we could specify the Visual Studio version that we want instead of defaulting to the latest. - Find_Result_Utf8 find_result = find_visual_studio_and_windows_sdk_utf8(); + if (is_windows) { + String section_name = str_lit("msvc-link"); + if (build_context.use_lld) { + section_name = str_lit("lld-link"); + } + timings_start_section(timings, section_name); - if (find_result.windows_sdk_version == 0) { - gb_printf_err("Windows SDK not found.\n"); - exit(1); - } + gbString lib_str = gb_string_make(heap_allocator(), ""); + defer (gb_string_free(lib_str)); - if (build_context.ignore_microsoft_magic) { - find_result = {}; - } + gbString link_settings = gb_string_make_reserve(heap_allocator(), 256); + defer (gb_string_free(link_settings)); - // Add library search paths. - if (find_result.vs_library_path.len > 0) { - GB_ASSERT(find_result.windows_sdk_um_library_path.len > 0); - GB_ASSERT(find_result.windows_sdk_ucrt_library_path.len > 0); - - String path = {}; - auto add_path = [&](String path) { - if (path[path.len-1] == '\\') { - path.len -= 1; - } - link_settings = gb_string_append_fmt(link_settings, " /LIBPATH:\"%.*s\"", LIT(path)); - }; - add_path(find_result.windows_sdk_um_library_path); - add_path(find_result.windows_sdk_ucrt_library_path); - add_path(find_result.vs_library_path); - } + // Add library search paths. + if (build_context.build_paths[BuildPath_VS_LIB].basename.len > 0) { + String path = {}; + auto add_path = [&](String path) { + if (path[path.len-1] == '\\') { + path.len -= 1; + } + link_settings = gb_string_append_fmt(link_settings, " /LIBPATH:\"%.*s\"", LIT(path)); + }; + add_path(build_context.build_paths[BuildPath_Win_SDK_UM_Lib].basename); + add_path(build_context.build_paths[BuildPath_Win_SDK_UCRT_Lib].basename); + add_path(build_context.build_paths[BuildPath_VS_LIB].basename); + } - StringSet libs = {}; - string_set_init(&libs, heap_allocator(), 64); - defer (string_set_destroy(&libs)); + StringSet libs = {}; + string_set_init(&libs, heap_allocator(), 64); + defer (string_set_destroy(&libs)); - StringSet asm_files = {}; - string_set_init(&asm_files, heap_allocator(), 64); - defer (string_set_destroy(&asm_files)); + StringSet asm_files = {}; + string_set_init(&asm_files, heap_allocator(), 64); + defer (string_set_destroy(&asm_files)); - for_array(j, gen->modules.entries) { - lbModule *m = gen->modules.entries[j].value; - for_array(i, m->foreign_library_paths) { - String lib = m->foreign_library_paths[i]; - if (has_asm_extension(lib)) { - string_set_add(&asm_files, lib); - } else { - string_set_add(&libs, lib); + for_array(j, gen->foreign_libraries) { + Entity *e = gen->foreign_libraries[j]; + GB_ASSERT(e->kind == Entity_LibraryName); + for_array(i, e->LibraryName.paths) { + String lib = string_trim_whitespace(e->LibraryName.paths[i]); + // IMPORTANT NOTE(bill): calling `string_to_lower` here is not an issue because + // we will never uses these strings afterwards + string_to_lower(&lib); + if (lib.len == 0) { + continue; + } + + if (has_asm_extension(lib)) { + if (!string_set_update(&asm_files, lib)) { + String asm_file = asm_files.entries[i].value; + String obj_file = concatenate_strings(permanent_allocator(), asm_file, str_lit(".obj")); + + result = system_exec_command_line_app("nasm", + "\"%.*s\\bin\\nasm\\windows\\nasm.exe\" \"%.*s\" " + "-f win64 " + "-o \"%.*s\" " + "%.*s " + "", + LIT(build_context.ODIN_ROOT), LIT(asm_file), + LIT(obj_file), + LIT(build_context.extra_assembler_flags) + ); + + if (result) { + return result; + } + array_add(&gen->output_object_paths, obj_file); + } + } else { + if (!string_set_update(&libs, lib)) { + lib_str = gb_string_append_fmt(lib_str, " \"%.*s\"", LIT(lib)); + } + } } } - } - for_array(i, gen->default_module.foreign_library_paths) { - String lib = gen->default_module.foreign_library_paths[i]; - if (has_asm_extension(lib)) { - string_set_add(&asm_files, lib); + if (build_context.build_mode == BuildMode_DynamicLibrary) { + link_settings = gb_string_append_fmt(link_settings, " /DLL"); } else { - string_set_add(&libs, lib); + link_settings = gb_string_append_fmt(link_settings, " /ENTRY:mainCRTStartup"); } - } - for_array(i, libs.entries) { - String lib = libs.entries[i].value; - lib_str = gb_string_append_fmt(lib_str, " \"%.*s\"", LIT(lib)); - } - - - if (build_context.build_mode == BuildMode_DynamicLibrary) { - output_ext = "dll"; - link_settings = gb_string_append_fmt(link_settings, " /DLL"); - } else { - link_settings = gb_string_append_fmt(link_settings, " /ENTRY:mainCRTStartup"); - } - - if (build_context.pdb_filepath != "") { - link_settings = gb_string_append_fmt(link_settings, " /PDB:%.*s", LIT(build_context.pdb_filepath)); - } - - if (build_context.no_crt) { - link_settings = gb_string_append_fmt(link_settings, " /nodefaultlib"); - } else { - link_settings = gb_string_append_fmt(link_settings, " /defaultlib:libcmt"); - } - - if (build_context.ODIN_DEBUG) { - link_settings = gb_string_append_fmt(link_settings, " /DEBUG"); - } - - for_array(i, asm_files.entries) { - String asm_file = asm_files.entries[i].value; - String obj_file = concatenate_strings(permanent_allocator(), asm_file, str_lit(".obj")); - - result = system_exec_command_line_app("nasm", - "\"%.*s\\bin\\nasm\\windows\\nasm.exe\" \"%.*s\" " - "-f win64 " - "-o \"%.*s\" " - "%.*s " - "", - LIT(build_context.ODIN_ROOT), LIT(asm_file), - LIT(obj_file), - LIT(build_context.extra_assembler_flags) - ); - - if (result) { - return result; + if (build_context.pdb_filepath != "") { + String pdb_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_PDB]); + link_settings = gb_string_append_fmt(link_settings, " /PDB:%.*s", LIT(pdb_path)); } - array_add(&gen->output_object_paths, obj_file); - } - gbString object_files = gb_string_make(heap_allocator(), ""); - defer (gb_string_free(object_files)); - for_array(i, gen->output_object_paths) { - String object_path = gen->output_object_paths[i]; - object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path)); - } + if (build_context.no_crt) { + link_settings = gb_string_append_fmt(link_settings, " /nodefaultlib"); + } else { + link_settings = gb_string_append_fmt(link_settings, " /defaultlib:libcmt"); + } - char const *subsystem_str = build_context.use_subsystem_windows ? "WINDOWS" : "CONSOLE"; - if (!build_context.use_lld) { // msvc - if (build_context.has_resource) { - result = system_exec_command_line_app("msvc-link", - "\"rc.exe\" /nologo /fo \"%.*s.res\" \"%.*s.rc\"", - LIT(output_base), - LIT(build_context.resource_filepath) - ); + if (build_context.ODIN_DEBUG) { + link_settings = gb_string_append_fmt(link_settings, " /DEBUG"); + } + + gbString object_files = gb_string_make(heap_allocator(), ""); + defer (gb_string_free(object_files)); + for_array(i, gen->output_object_paths) { + String object_path = gen->output_object_paths[i]; + object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path)); + } + + String vs_exe_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_VS_EXE]); + defer (gb_free(heap_allocator(), vs_exe_path.text)); + + char const *subsystem_str = build_context.use_subsystem_windows ? "WINDOWS" : "CONSOLE"; + if (!build_context.use_lld) { // msvc + if (build_context.has_resource) { + String rc_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RC]); + String res_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RES]); + defer (gb_free(heap_allocator(), rc_path.text)); + defer (gb_free(heap_allocator(), res_path.text)); + + result = system_exec_command_line_app("msvc-link", + "\"rc.exe\" /nologo /fo \"%.*s\" \"%.*s\"", + LIT(res_path), + LIT(rc_path) + ); + + if (result) { + return result; + } + + result = system_exec_command_line_app("msvc-link", + "\"%.*slink.exe\" %s \"%.*s\" -OUT:\"%.*s\" %s " + "/nologo /incremental:no /opt:ref /subsystem:%s " + " %.*s " + " %.*s " + " %s " + "", + LIT(vs_exe_path), object_files, LIT(res_path), LIT(output_filename), + link_settings, + subsystem_str, + LIT(build_context.link_flags), + LIT(build_context.extra_linker_flags), + lib_str + ); + } else { + result = system_exec_command_line_app("msvc-link", + "\"%.*slink.exe\" %s -OUT:\"%.*s\" %s " + "/nologo /incremental:no /opt:ref /subsystem:%s " + " %.*s " + " %.*s " + " %s " + "", + LIT(vs_exe_path), object_files, LIT(output_filename), + link_settings, + subsystem_str, + LIT(build_context.link_flags), + LIT(build_context.extra_linker_flags), + lib_str + ); + } if (result) { return result; } - result = system_exec_command_line_app("msvc-link", - "\"%.*slink.exe\" %s \"%.*s.res\" -OUT:\"%.*s.%s\" %s " + } else { // lld + result = system_exec_command_line_app("msvc-lld-link", + "\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s\" %s " "/nologo /incremental:no /opt:ref /subsystem:%s " " %.*s " " %.*s " " %s " "", - LIT(find_result.vs_exe_path), object_files, LIT(output_base), LIT(output_base), output_ext, - link_settings, - subsystem_str, - LIT(build_context.link_flags), - LIT(build_context.extra_linker_flags), - lib_str - ); - } else { - result = system_exec_command_line_app("msvc-link", - "\"%.*slink.exe\" %s -OUT:\"%.*s.%s\" %s " - "/nologo /incremental:no /opt:ref /subsystem:%s " - " %.*s " - " %.*s " - " %s " - "", - LIT(find_result.vs_exe_path), object_files, LIT(output_base), output_ext, + LIT(build_context.ODIN_ROOT), object_files, LIT(output_filename), link_settings, subsystem_str, LIT(build_context.link_flags), LIT(build_context.extra_linker_flags), lib_str ); - } - if (result) { - return result; - } - - } else { // lld - result = system_exec_command_line_app("msvc-lld-link", - "\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s.%s\" %s " - "/nologo /incremental:no /opt:ref /subsystem:%s " - " %.*s " - " %.*s " - " %s " - "", - LIT(build_context.ODIN_ROOT), object_files, LIT(output_base),output_ext, - link_settings, - subsystem_str, - LIT(build_context.link_flags), - LIT(build_context.extra_linker_flags), - lib_str - ); - - if (result) { - return result; - } - } - #else - timings_start_section(timings, str_lit("ld-link")); - - // NOTE(vassvik): get cwd, for used for local shared libs linking, since those have to be relative to the exe - char cwd[256]; - getcwd(&cwd[0], 256); - //printf("%s\n", cwd); - - // NOTE(vassvik): needs to add the root to the library search paths, so that the full filenames of the library - // files can be passed with -l: - gbString lib_str = gb_string_make(heap_allocator(), "-L/"); - defer (gb_string_free(lib_str)); - - for_array(i, gen->default_module.foreign_library_paths) { - String lib = gen->default_module.foreign_library_paths[i]; - - // NOTE(zangent): Sometimes, you have to use -framework on MacOS. - // This allows you to specify '-f' in a #foreign_system_library, - // without having to implement any new syntax specifically for MacOS. - #if defined(GB_SYSTEM_OSX) - if (string_ends_with(lib, str_lit(".framework"))) { - // framework thingie - String lib_name = lib; - lib_name = remove_extension_from_path(lib_name); - lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", LIT(lib_name)); - } else if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o")) || string_ends_with(lib, str_lit(".dylib"))) { - // For: - // object - // dynamic lib - // static libs, absolute full path relative to the file in which the lib was imported from - lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib)); - } else { - // dynamic or static system lib, just link regularly searching system library paths - lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib)); + if (result) { + return result; } - #else - // NOTE(vassvik): static libraries (.a files) in linux can be linked to directly using the full path, - // since those are statically linked to at link time. shared libraries (.so) has to be - // available at runtime wherever the executable is run, so we make require those to be - // local to the executable (unless the system collection is used, in which case we search - // the system library paths for the library file). - if (string_ends_with(lib, str_lit(".a"))) { - // static libs, absolute full path relative to the file in which the lib was imported from - lib_str = gb_string_append_fmt(lib_str, " -l:\"%.*s\" ", LIT(lib)); - } else if (string_ends_with(lib, str_lit(".so"))) { - // dynamic lib, relative path to executable - // NOTE(vassvik): it is the user's responsibility to make sure the shared library files are visible - // at runtimeto the executable - lib_str = gb_string_append_fmt(lib_str, " -l:\"%s/%.*s\" ", cwd, LIT(lib)); - } else { - // dynamic or static system lib, just link regularly searching system library paths - lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib)); - } - #endif - } - - gbString object_files = gb_string_make(heap_allocator(), ""); - defer (gb_string_free(object_files)); - for_array(i, gen->output_object_paths) { - String object_path = gen->output_object_paths[i]; - object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path)); - } - - // Unlike the Win32 linker code, the output_ext includes the dot, because - // typically executable files on *NIX systems don't have extensions. - String output_ext = {}; - gbString link_settings = gb_string_make_reserve(heap_allocator(), 32); - - // NOTE(dweiler): We use clang as a frontend for the linker as there are - // other runtime and compiler support libraries that need to be linked in - // very specific orders such as libgcc_s, ld-linux-so, unwind, etc. - // These are not always typically inside /lib, /lib64, or /usr versions - // of that, e.g libgcc.a is in /usr/lib/gcc/{version}, and can vary on - // the distribution of Linux even. The gcc or clang specs is the only - // reliable way to query this information to call ld directly. - if (build_context.build_mode == BuildMode_DynamicLibrary) { - // NOTE(dweiler): Let the frontend know we're building a shared library - // so it doesn't generate symbols which cannot be relocated. - link_settings = gb_string_appendc(link_settings, "-shared "); - - // NOTE(dweiler): _odin_entry_point must be called at initialization - // time of the shared object, similarly, _odin_exit_point must be called - // at deinitialization. We can pass both -init and -fini to the linker by - // using a comma separated list of arguments to -Wl. - // - // This previously used ld but ld cannot actually build a shared library - // correctly this way since all the other dependencies provided implicitly - // by the compiler frontend are still needed and most of the command - // line arguments prepared previously are incompatible with ld. - // - // Shared libraries are .dylib on MacOS and .so on Linux. - #if defined(GB_SYSTEM_OSX) - output_ext = STR_LIT(".dylib"); - #else - output_ext = STR_LIT(".so"); - #endif - link_settings = gb_string_appendc(link_settings, "-Wl,-init,'_odin_entry_point' "); - link_settings = gb_string_appendc(link_settings, "-Wl,-fini,'_odin_exit_point' "); + } } else { - link_settings = gb_string_appendc(link_settings, "-no-pie "); - } - if (build_context.out_filepath.len > 0) { - //NOTE(thebirk): We have a custom -out arguments, so we should use the extension from that - isize pos = string_extension_position(build_context.out_filepath); - if (pos > 0) { - output_ext = substring(build_context.out_filepath, pos, build_context.out_filepath.len); - } - } + timings_start_section(timings, str_lit("ld-link")); - result = system_exec_command_line_app("ld-link", - "clang -Wno-unused-command-line-argument %s -o \"%.*s%.*s\" %s " - " %s " - " %.*s " - " %.*s " - " %s " - #if defined(GB_SYSTEM_OSX) + // NOTE(vassvik): get cwd, for used for local shared libs linking, since those have to be relative to the exe + char cwd[256]; + #if !defined(GB_SYSTEM_WINDOWS) + getcwd(&cwd[0], 256); + #endif + //printf("%s\n", cwd); + + // NOTE(vassvik): needs to add the root to the library search paths, so that the full filenames of the library + // files can be passed with -l: + gbString lib_str = gb_string_make(heap_allocator(), "-L/"); + defer (gb_string_free(lib_str)); + + StringSet libs = {}; + string_set_init(&libs, heap_allocator(), 64); + defer (string_set_destroy(&libs)); + + for_array(j, gen->foreign_libraries) { + Entity *e = gen->foreign_libraries[j]; + GB_ASSERT(e->kind == Entity_LibraryName); + for_array(i, e->LibraryName.paths) { + String lib = string_trim_whitespace(e->LibraryName.paths[i]); + if (lib.len == 0) { + continue; + } + if (string_set_update(&libs, lib)) { + continue; + } + + // NOTE(zangent): Sometimes, you have to use -framework on MacOS. + // This allows you to specify '-f' in a #foreign_system_library, + // without having to implement any new syntax specifically for MacOS. + if (build_context.metrics.os == TargetOs_darwin) { + if (string_ends_with(lib, str_lit(".framework"))) { + // framework thingie + String lib_name = lib; + lib_name = remove_extension_from_path(lib_name); + lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", LIT(lib_name)); + } else if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o")) || string_ends_with(lib, str_lit(".dylib"))) { + // For: + // object + // dynamic lib + // static libs, absolute full path relative to the file in which the lib was imported from + lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib)); + } else { + // dynamic or static system lib, just link regularly searching system library paths + lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib)); + } + } else { + // NOTE(vassvik): static libraries (.a files) in linux can be linked to directly using the full path, + // since those are statically linked to at link time. shared libraries (.so) has to be + // available at runtime wherever the executable is run, so we make require those to be + // local to the executable (unless the system collection is used, in which case we search + // the system library paths for the library file). + if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o"))) { + // static libs and object files, absolute full path relative to the file in which the lib was imported from + lib_str = gb_string_append_fmt(lib_str, " -l:\"%.*s\" ", LIT(lib)); + } else if (string_ends_with(lib, str_lit(".so"))) { + // dynamic lib, relative path to executable + // NOTE(vassvik): it is the user's responsibility to make sure the shared library files are visible + // at runtime to the executable + lib_str = gb_string_append_fmt(lib_str, " -l:\"%s/%.*s\" ", cwd, LIT(lib)); + } else { + // dynamic or static system lib, just link regularly searching system library paths + lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib)); + } + } + } + } + + + gbString object_files = gb_string_make(heap_allocator(), ""); + defer (gb_string_free(object_files)); + for_array(i, gen->output_object_paths) { + String object_path = gen->output_object_paths[i]; + object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path)); + } + + gbString link_settings = gb_string_make_reserve(heap_allocator(), 32); + + if (build_context.no_crt) { + link_settings = gb_string_append_fmt(link_settings, "-nostdlib "); + } + + // NOTE(dweiler): We use clang as a frontend for the linker as there are + // other runtime and compiler support libraries that need to be linked in + // very specific orders such as libgcc_s, ld-linux-so, unwind, etc. + // These are not always typically inside /lib, /lib64, or /usr versions + // of that, e.g libgcc.a is in /usr/lib/gcc/{version}, and can vary on + // the distribution of Linux even. The gcc or clang specs is the only + // reliable way to query this information to call ld directly. + if (build_context.build_mode == BuildMode_DynamicLibrary) { + // NOTE(dweiler): Let the frontend know we're building a shared library + // so it doesn't generate symbols which cannot be relocated. + link_settings = gb_string_appendc(link_settings, "-shared "); + + // NOTE(dweiler): _odin_entry_point must be called at initialization + // time of the shared object, similarly, _odin_exit_point must be called + // at deinitialization. We can pass both -init and -fini to the linker by + // using a comma separated list of arguments to -Wl. + // + // This previously used ld but ld cannot actually build a shared library + // correctly this way since all the other dependencies provided implicitly + // by the compiler frontend are still needed and most of the command + // line arguments prepared previously are incompatible with ld. + if (build_context.metrics.os == TargetOs_darwin) { + link_settings = gb_string_appendc(link_settings, "-Wl,-init,'__odin_entry_point' "); + // NOTE(weshardee): __odin_exit_point should also be added, but -fini + // does not exist on MacOS + } else { + link_settings = gb_string_appendc(link_settings, "-Wl,-init,'_odin_entry_point' "); + link_settings = gb_string_appendc(link_settings, "-Wl,-fini,'_odin_exit_point' "); + } + + } else if (build_context.metrics.os != TargetOs_openbsd) { + // OpenBSD defaults to PIE executable. do not pass -no-pie for it. + link_settings = gb_string_appendc(link_settings, "-no-pie "); + } + + gbString platform_lib_str = gb_string_make(heap_allocator(), ""); + defer (gb_string_free(platform_lib_str)); + if (build_context.metrics.os == TargetOs_darwin) { + platform_lib_str = gb_string_appendc(platform_lib_str, "-lSystem -lm -Wl,-syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib"); + } else { + platform_lib_str = gb_string_appendc(platform_lib_str, "-lc -lm"); + } + + if (build_context.metrics.os == TargetOs_darwin) { // This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit. // NOTE: If you change this (although this minimum is as low as you can go with Odin working) // make sure to also change the 'mtriple' param passed to 'opt' - #if defined(GB_CPU_ARM) - " -mmacosx-version-min=11.0.0 " - #else - " -mmacosx-version-min=10.8.0 " - #endif + if (build_context.metrics.arch == TargetArch_arm64) { + link_settings = gb_string_appendc(link_settings, " -mmacosx-version-min=12.0.0 "); + } else { + link_settings = gb_string_appendc(link_settings, " -mmacosx-version-min=10.12.0 "); + } // This points the linker to where the entry point is - " -e _main " - #endif - , object_files, LIT(output_base), LIT(output_ext), - #if defined(GB_SYSTEM_OSX) - "-lSystem -lm -Wl,-syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib", - #else - "-lc -lm", - #endif - lib_str, - LIT(build_context.link_flags), - LIT(build_context.extra_linker_flags), - link_settings); + link_settings = gb_string_appendc(link_settings, " -e _main "); + } - if (result) { - return result; - } + gbString link_command_line = gb_string_make(heap_allocator(), "clang -Wno-unused-command-line-argument "); + defer (gb_string_free(link_command_line)); - #if defined(GB_SYSTEM_OSX) - if (build_context.ODIN_DEBUG) { - // NOTE: macOS links DWARF symbols dynamically. Dsymutil will map the stubs in the exe - // to the symbols in the object file - result = system_exec_command_line_app("dsymutil", - "dsymutil %.*s%.*s", LIT(output_base), LIT(output_ext) - ); + link_command_line = gb_string_appendc(link_command_line, object_files); + link_command_line = gb_string_append_fmt(link_command_line, " -o \"%.*s\" ", LIT(output_filename)); + link_command_line = gb_string_append_fmt(link_command_line, " %s ", platform_lib_str); + link_command_line = gb_string_append_fmt(link_command_line, " %s ", lib_str); + link_command_line = gb_string_append_fmt(link_command_line, " %.*s ", LIT(build_context.link_flags)); + link_command_line = gb_string_append_fmt(link_command_line, " %.*s ", LIT(build_context.extra_linker_flags)); + link_command_line = gb_string_append_fmt(link_command_line, " %s ", link_settings); + + result = system_exec_command_line_app("ld-link", link_command_line); if (result) { return result; } - } - #endif - #endif + if (is_osx && build_context.ODIN_DEBUG) { + // NOTE: macOS links DWARF symbols dynamically. Dsymutil will map the stubs in the exe + // to the symbols in the object file + result = system_exec_command_line_app("dsymutil", "dsymutil %.*s", LIT(output_filename)); + + if (result) { + return result; + } + } + } } return result; @@ -572,54 +575,26 @@ void usage(String argv0) { print_usage_line(0, "Usage:"); print_usage_line(1, "%.*s command [arguments]", LIT(argv0)); print_usage_line(0, "Commands:"); - print_usage_line(1, "build compile .odin file, or directory of .odin files, as an executable."); - print_usage_line(1, " one must contain the program's entry point, all must be in the same package."); - print_usage_line(1, "run same as 'build', but also then runs the newly compiled executable."); - print_usage_line(1, "check parse and type check .odin file"); - print_usage_line(1, "query parse, type check, and output a .json file containing information about the program"); - print_usage_line(1, "doc generate documentation .odin file, or directory of .odin files"); - print_usage_line(1, "version print version"); - print_usage_line(1, "report print information useful to reporting a bug"); + print_usage_line(1, "build compile directory of .odin files, as an executable."); + print_usage_line(1, " one must contain the program's entry point, all must be in the same package."); + print_usage_line(1, "run same as 'build', but also then runs the newly compiled executable."); + print_usage_line(1, "check parse, and type check a directory of .odin files"); + print_usage_line(1, "query parse, type check, and output a .json file containing information about the program"); + print_usage_line(1, "strip-semicolon parse, type check, and remove unneeded semicolons from the entire program"); + print_usage_line(1, "test build and runs procedures with the attribute @(test) in the initial package"); + print_usage_line(1, "doc generate documentation on a directory of .odin files"); + print_usage_line(1, "version print version"); + print_usage_line(1, "report print information useful to reporting a bug"); print_usage_line(0, ""); print_usage_line(0, "For further details on a command, use -help after the command name"); print_usage_line(1, "e.g. odin build -help"); } - -bool string_is_valid_identifier(String str) { - if (str.len <= 0) return false; - - isize rune_count = 0; - - isize w = 0; - isize offset = 0; - while (offset < str.len) { - Rune r = 0; - w = utf8_decode(str.text, str.len, &r); - if (r == GB_RUNE_INVALID) { - return false; - } - - if (rune_count == 0) { - if (!rune_is_letter(r)) { - return false; - } - } else { - if (!rune_is_letter(r) && !rune_is_digit(r)) { - return false; - } - } - rune_count += 1; - offset += w; - } - - return true; -} - enum BuildFlagKind { BuildFlag_Invalid, BuildFlag_Help, + BuildFlag_SingleFile, BuildFlag_OutFile, BuildFlag_OptimizationLevel, @@ -655,6 +630,10 @@ enum BuildFlagKind { BuildFlag_ExtraLinkerFlags, BuildFlag_ExtraAssemblerFlags, BuildFlag_Microarch, + BuildFlag_TargetFeatures, + + BuildFlag_RelocMode, + BuildFlag_DisableRedZone, BuildFlag_TestName, @@ -663,6 +642,8 @@ enum BuildFlagKind { BuildFlag_InsertSemicolon, BuildFlag_StrictStyle, BuildFlag_StrictStyleInitOnly, + BuildFlag_ForeignErrorProcedures, + BuildFlag_DisallowRTTI, BuildFlag_Compact, BuildFlag_GlobalDefinitions, @@ -675,6 +656,7 @@ enum BuildFlagKind { BuildFlag_IgnoreWarnings, BuildFlag_WarningsAsErrors, BuildFlag_VerboseErrors, + BuildFlag_ErrorPosStyle, // internal use only BuildFlag_InternalIgnoreLazy, @@ -772,69 +754,81 @@ ExactValue build_param_to_exact_value(String name, String param) { bool parse_build_flags(Array args) { auto build_flags = array_make(heap_allocator(), 0, BuildFlag_COUNT); - add_flag(&build_flags, BuildFlag_Help, str_lit("help"), BuildFlagParam_None, Command_all); - add_flag(&build_flags, BuildFlag_OutFile, str_lit("out"), BuildFlagParam_String, Command__does_build &~ Command_test); - add_flag(&build_flags, BuildFlag_OptimizationLevel, str_lit("opt"), BuildFlagParam_Integer, Command__does_build); - add_flag(&build_flags, BuildFlag_OptimizationMode, str_lit("o"), BuildFlagParam_String, Command__does_build); - add_flag(&build_flags, BuildFlag_OptimizationMode, str_lit("O"), BuildFlagParam_String, Command__does_build); - add_flag(&build_flags, BuildFlag_ShowTimings, str_lit("show-timings"), BuildFlagParam_None, Command__does_check); - add_flag(&build_flags, BuildFlag_ShowMoreTimings, str_lit("show-more-timings"), BuildFlagParam_None, Command__does_check); - add_flag(&build_flags, BuildFlag_ExportTimings, str_lit("export-timings"), BuildFlagParam_String, Command__does_check); - add_flag(&build_flags, BuildFlag_ExportTimingsFile, str_lit("export-timings-file"), BuildFlagParam_String, Command__does_check); - add_flag(&build_flags, BuildFlag_ShowUnused, str_lit("show-unused"), BuildFlagParam_None, Command_check); - add_flag(&build_flags, BuildFlag_ShowUnusedWithLocation, str_lit("show-unused-with-location"), BuildFlagParam_None, Command_check); - add_flag(&build_flags, BuildFlag_ShowSystemCalls, str_lit("show-system-calls"), BuildFlagParam_None, Command_all); - add_flag(&build_flags, BuildFlag_ThreadCount, str_lit("thread-count"), BuildFlagParam_Integer, Command_all); - add_flag(&build_flags, BuildFlag_KeepTempFiles, str_lit("keep-temp-files"), BuildFlagParam_None, Command__does_build|Command_strip_semicolon); - add_flag(&build_flags, BuildFlag_Collection, str_lit("collection"), BuildFlagParam_String, Command__does_check); - add_flag(&build_flags, BuildFlag_Define, str_lit("define"), BuildFlagParam_String, Command__does_check, true); - add_flag(&build_flags, BuildFlag_BuildMode, str_lit("build-mode"), BuildFlagParam_String, Command__does_build); // Commands_build is not used to allow for a better error message - add_flag(&build_flags, BuildFlag_Target, str_lit("target"), BuildFlagParam_String, Command__does_check); - add_flag(&build_flags, BuildFlag_Debug, str_lit("debug"), BuildFlagParam_None, Command__does_check); - add_flag(&build_flags, BuildFlag_DisableAssert, str_lit("disable-assert"), BuildFlagParam_None, Command__does_check); - add_flag(&build_flags, BuildFlag_NoBoundsCheck, str_lit("no-bounds-check"), BuildFlagParam_None, Command__does_check); - add_flag(&build_flags, BuildFlag_NoDynamicLiterals, str_lit("no-dynamic-literals"), BuildFlagParam_None, Command__does_check); - add_flag(&build_flags, BuildFlag_NoCRT, str_lit("no-crt"), BuildFlagParam_None, Command__does_build); - add_flag(&build_flags, BuildFlag_NoEntryPoint, str_lit("no-entry-point"), BuildFlagParam_None, Command__does_check &~ Command_test); - add_flag(&build_flags, BuildFlag_UseLLD, str_lit("lld"), BuildFlagParam_None, Command__does_build); - add_flag(&build_flags, BuildFlag_UseSeparateModules,str_lit("use-separate-modules"),BuildFlagParam_None, Command__does_build); - add_flag(&build_flags, BuildFlag_ThreadedChecker, str_lit("threaded-checker"), BuildFlagParam_None, Command__does_check); - add_flag(&build_flags, BuildFlag_NoThreadedChecker, str_lit("no-threaded-checker"), BuildFlagParam_None, Command__does_check); - add_flag(&build_flags, BuildFlag_ShowDebugMessages, str_lit("show-debug-messages"), BuildFlagParam_None, Command_all); - add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None, Command__does_check); - add_flag(&build_flags, BuildFlag_VetExtra, str_lit("vet-extra"), BuildFlagParam_None, Command__does_check); - add_flag(&build_flags, BuildFlag_UseLLVMApi, str_lit("llvm-api"), BuildFlagParam_None, Command__does_build); - add_flag(&build_flags, BuildFlag_IgnoreUnknownAttributes, str_lit("ignore-unknown-attributes"), BuildFlagParam_None, Command__does_check); - add_flag(&build_flags, BuildFlag_ExtraLinkerFlags, str_lit("extra-linker-flags"), BuildFlagParam_String, Command__does_build); - add_flag(&build_flags, BuildFlag_ExtraAssemblerFlags, str_lit("extra-assembler-flags"), BuildFlagParam_String, Command__does_build); - add_flag(&build_flags, BuildFlag_Microarch, str_lit("microarch"), BuildFlagParam_String, Command__does_build); + add_flag(&build_flags, BuildFlag_Help, str_lit("help"), BuildFlagParam_None, Command_all); + add_flag(&build_flags, BuildFlag_SingleFile, str_lit("file"), BuildFlagParam_None, Command__does_build | Command__does_check); + add_flag(&build_flags, BuildFlag_OutFile, str_lit("out"), BuildFlagParam_String, Command__does_build &~ Command_test); + add_flag(&build_flags, BuildFlag_OptimizationLevel, str_lit("opt"), BuildFlagParam_Integer, Command__does_build); + add_flag(&build_flags, BuildFlag_OptimizationMode, str_lit("o"), BuildFlagParam_String, Command__does_build); + add_flag(&build_flags, BuildFlag_OptimizationMode, str_lit("O"), BuildFlagParam_String, Command__does_build); + add_flag(&build_flags, BuildFlag_ShowTimings, str_lit("show-timings"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_ShowMoreTimings, str_lit("show-more-timings"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_ExportTimings, str_lit("export-timings"), BuildFlagParam_String, Command__does_check); + add_flag(&build_flags, BuildFlag_ExportTimingsFile, str_lit("export-timings-file"), BuildFlagParam_String, Command__does_check); + add_flag(&build_flags, BuildFlag_ShowUnused, str_lit("show-unused"), BuildFlagParam_None, Command_check); + add_flag(&build_flags, BuildFlag_ShowUnusedWithLocation, str_lit("show-unused-with-location"), BuildFlagParam_None, Command_check); + add_flag(&build_flags, BuildFlag_ShowSystemCalls, str_lit("show-system-calls"), BuildFlagParam_None, Command_all); + add_flag(&build_flags, BuildFlag_ThreadCount, str_lit("thread-count"), BuildFlagParam_Integer, Command_all); + add_flag(&build_flags, BuildFlag_KeepTempFiles, str_lit("keep-temp-files"), BuildFlagParam_None, Command__does_build | Command_strip_semicolon); + add_flag(&build_flags, BuildFlag_Collection, str_lit("collection"), BuildFlagParam_String, Command__does_check); + add_flag(&build_flags, BuildFlag_Define, str_lit("define"), BuildFlagParam_String, Command__does_check, true); + add_flag(&build_flags, BuildFlag_BuildMode, str_lit("build-mode"), BuildFlagParam_String, Command__does_build); // Commands_build is not used to allow for a better error message + add_flag(&build_flags, BuildFlag_Target, str_lit("target"), BuildFlagParam_String, Command__does_check); + add_flag(&build_flags, BuildFlag_Debug, str_lit("debug"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_DisableAssert, str_lit("disable-assert"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_NoBoundsCheck, str_lit("no-bounds-check"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_NoDynamicLiterals, str_lit("no-dynamic-literals"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_NoCRT, str_lit("no-crt"), BuildFlagParam_None, Command__does_build); + add_flag(&build_flags, BuildFlag_NoEntryPoint, str_lit("no-entry-point"), BuildFlagParam_None, Command__does_check &~ Command_test); + add_flag(&build_flags, BuildFlag_UseLLD, str_lit("lld"), BuildFlagParam_None, Command__does_build); + add_flag(&build_flags, BuildFlag_UseSeparateModules, str_lit("use-separate-modules"), BuildFlagParam_None, Command__does_build); + add_flag(&build_flags, BuildFlag_ThreadedChecker, str_lit("threaded-checker"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_NoThreadedChecker, str_lit("no-threaded-checker"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_ShowDebugMessages, str_lit("show-debug-messages"), BuildFlagParam_None, Command_all); + add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_VetExtra, str_lit("vet-extra"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_UseLLVMApi, str_lit("llvm-api"), BuildFlagParam_None, Command__does_build); + add_flag(&build_flags, BuildFlag_IgnoreUnknownAttributes, str_lit("ignore-unknown-attributes"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_ExtraLinkerFlags, str_lit("extra-linker-flags"), BuildFlagParam_String, Command__does_build); + add_flag(&build_flags, BuildFlag_ExtraAssemblerFlags, str_lit("extra-assembler-flags"), BuildFlagParam_String, Command__does_build); + add_flag(&build_flags, BuildFlag_Microarch, str_lit("microarch"), BuildFlagParam_String, Command__does_build); + add_flag(&build_flags, BuildFlag_TargetFeatures, str_lit("target-features"), BuildFlagParam_String, Command__does_build); - add_flag(&build_flags, BuildFlag_TestName, str_lit("test-name"), BuildFlagParam_String, Command_test); + add_flag(&build_flags, BuildFlag_RelocMode, str_lit("reloc-mode"), BuildFlagParam_String, Command__does_build); + add_flag(&build_flags, BuildFlag_DisableRedZone, str_lit("disable-red-zone"), BuildFlagParam_None, Command__does_build); - add_flag(&build_flags, BuildFlag_DisallowDo, str_lit("disallow-do"), BuildFlagParam_None, Command__does_check); - add_flag(&build_flags, BuildFlag_DefaultToNilAllocator, str_lit("default-to-nil-allocator"), BuildFlagParam_None, Command__does_check); - add_flag(&build_flags, BuildFlag_InsertSemicolon, str_lit("insert-semicolon"), BuildFlagParam_None, Command__does_check); - add_flag(&build_flags, BuildFlag_StrictStyle, str_lit("strict-style"), BuildFlagParam_None, Command__does_check); - add_flag(&build_flags, BuildFlag_StrictStyleInitOnly, str_lit("strict-style-init-only"), BuildFlagParam_None, Command__does_check); - add_flag(&build_flags, BuildFlag_Compact, str_lit("compact"), BuildFlagParam_None, Command_query); - add_flag(&build_flags, BuildFlag_GlobalDefinitions, str_lit("global-definitions"), BuildFlagParam_None, Command_query); - add_flag(&build_flags, BuildFlag_GoToDefinitions, str_lit("go-to-definitions"), BuildFlagParam_None, Command_query); + add_flag(&build_flags, BuildFlag_TestName, str_lit("test-name"), BuildFlagParam_String, Command_test); - add_flag(&build_flags, BuildFlag_Short, str_lit("short"), BuildFlagParam_None, Command_doc); - add_flag(&build_flags, BuildFlag_AllPackages, str_lit("all-packages"), BuildFlagParam_None, Command_doc); - add_flag(&build_flags, BuildFlag_DocFormat, str_lit("doc-format"), BuildFlagParam_None, Command_doc); + add_flag(&build_flags, BuildFlag_DisallowDo, str_lit("disallow-do"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_DefaultToNilAllocator, str_lit("default-to-nil-allocator"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_InsertSemicolon, str_lit("insert-semicolon"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_StrictStyle, str_lit("strict-style"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_StrictStyleInitOnly, str_lit("strict-style-init-only"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_ForeignErrorProcedures, str_lit("foreign-error-procedures"), BuildFlagParam_None, Command__does_check); - add_flag(&build_flags, BuildFlag_IgnoreWarnings, str_lit("ignore-warnings"), BuildFlagParam_None, Command_all); - add_flag(&build_flags, BuildFlag_WarningsAsErrors, str_lit("warnings-as-errors"), BuildFlagParam_None, Command_all); - add_flag(&build_flags, BuildFlag_VerboseErrors, str_lit("verbose-errors"), BuildFlagParam_None, Command_all); + add_flag(&build_flags, BuildFlag_DisallowRTTI, str_lit("disallow-rtti"), BuildFlagParam_None, Command__does_check); - add_flag(&build_flags, BuildFlag_InternalIgnoreLazy, str_lit("internal-ignore-lazy"), BuildFlagParam_None, Command_all); + + add_flag(&build_flags, BuildFlag_Compact, str_lit("compact"), BuildFlagParam_None, Command_query); + add_flag(&build_flags, BuildFlag_GlobalDefinitions, str_lit("global-definitions"), BuildFlagParam_None, Command_query); + add_flag(&build_flags, BuildFlag_GoToDefinitions, str_lit("go-to-definitions"), BuildFlagParam_None, Command_query); + + + add_flag(&build_flags, BuildFlag_Short, str_lit("short"), BuildFlagParam_None, Command_doc); + add_flag(&build_flags, BuildFlag_AllPackages, str_lit("all-packages"), BuildFlagParam_None, Command_doc); + add_flag(&build_flags, BuildFlag_DocFormat, str_lit("doc-format"), BuildFlagParam_None, Command_doc); + + add_flag(&build_flags, BuildFlag_IgnoreWarnings, str_lit("ignore-warnings"), BuildFlagParam_None, Command_all); + add_flag(&build_flags, BuildFlag_WarningsAsErrors, str_lit("warnings-as-errors"), BuildFlagParam_None, Command_all); + add_flag(&build_flags, BuildFlag_VerboseErrors, str_lit("verbose-errors"), BuildFlagParam_None, Command_all); + add_flag(&build_flags, BuildFlag_ErrorPosStyle, str_lit("error-pos-style"), BuildFlagParam_String, Command_all); + + add_flag(&build_flags, BuildFlag_InternalIgnoreLazy, str_lit("internal-ignore-lazy"), BuildFlagParam_None, Command_all); #if defined(GB_SYSTEM_WINDOWS) - add_flag(&build_flags, BuildFlag_IgnoreVsSearch, str_lit("ignore-vs-search"), BuildFlagParam_None, Command__does_build); - add_flag(&build_flags, BuildFlag_ResourceFile, str_lit("resource"), BuildFlagParam_String, Command__does_build); - add_flag(&build_flags, BuildFlag_WindowsPdbName, str_lit("pdb-name"), BuildFlagParam_String, Command__does_build); - add_flag(&build_flags, BuildFlag_Subsystem, str_lit("subsystem"), BuildFlagParam_String, Command__does_build); + add_flag(&build_flags, BuildFlag_IgnoreVsSearch, str_lit("ignore-vs-search"), BuildFlagParam_None, Command__does_build); + add_flag(&build_flags, BuildFlag_ResourceFile, str_lit("resource"), BuildFlagParam_String, Command__does_build); + add_flag(&build_flags, BuildFlag_WindowsPdbName, str_lit("pdb-name"), BuildFlagParam_String, Command__does_build); + add_flag(&build_flags, BuildFlag_Subsystem, str_lit("subsystem"), BuildFlagParam_String, Command__does_build); #endif @@ -857,11 +851,19 @@ bool parse_build_flags(Array args) { String name = substring(flag, 1, flag.len); isize end = 0; + bool have_equals = false; for (; end < name.len; end++) { if (name[end] == ':') break; - if (name[end] == '=') break; // IMPORTANT TODO(bill): DEPRECATE THIS!!!! + if (name[end] == '=') { + have_equals = true; + break; + } } name = substring(name, 0, end); + if (have_equals && name != "opt") { + gb_printf_err("`flag=value` has been deprecated and will be removed next release. Use `%.*s:` instead.\n", LIT(name)); + } + String param = {}; if (end < flag.len-1) param = substring(flag, 2+end, flag.len); @@ -935,35 +937,35 @@ bool parse_build_flags(Array args) { switch (bf.param_kind) { case BuildFlagParam_None: if (value.kind != ExactValue_Invalid) { - gb_printf_err("%.*s expected no value, got %.*s", LIT(name), LIT(param)); + gb_printf_err("%.*s expected no value, got %.*s\n", LIT(name), LIT(param)); bad_flags = true; ok = false; } break; case BuildFlagParam_Boolean: if (value.kind != ExactValue_Bool) { - gb_printf_err("%.*s expected a boolean, got %.*s", LIT(name), LIT(param)); + gb_printf_err("%.*s expected a boolean, got %.*s\n", LIT(name), LIT(param)); bad_flags = true; ok = false; } break; case BuildFlagParam_Integer: if (value.kind != ExactValue_Integer) { - gb_printf_err("%.*s expected an integer, got %.*s", LIT(name), LIT(param)); + gb_printf_err("%.*s expected an integer, got %.*s\n", LIT(name), LIT(param)); bad_flags = true; ok = false; } break; case BuildFlagParam_Float: if (value.kind != ExactValue_Float) { - gb_printf_err("%.*s expected a floating pointer number, got %.*s", LIT(name), LIT(param)); + gb_printf_err("%.*s expected a floating pointer number, got %.*s\n", LIT(name), LIT(param)); bad_flags = true; ok = false; } break; case BuildFlagParam_String: if (value.kind != ExactValue_String) { - gb_printf_err("%.*s expected a string, got %.*s", LIT(name), LIT(param)); + gb_printf_err("%.*s expected a string, got %.*s\n", LIT(name), LIT(param)); bad_flags = true; ok = false; } @@ -993,7 +995,20 @@ bool parse_build_flags(Array args) { bad_flags = true; break; } + build_context.optimization_level = cast(i32)big_int_to_i64(&value.value_integer); + if (build_context.optimization_level < 0 || build_context.optimization_level > 3) { + gb_printf_err("Invalid optimization level for -o:, got %d\n", build_context.optimization_level); + gb_printf_err("Valid optimization levels:\n"); + gb_printf_err("\t0\n"); + gb_printf_err("\t1\n"); + gb_printf_err("\t2\n"); + gb_printf_err("\t3\n"); + bad_flags = true; + } + + // Deprecation warning. + gb_printf_err("`-opt` has been deprecated and will be removed next release. Use `-o:minimal`, etc.\n"); break; } case BuildFlag_OptimizationMode: { @@ -1366,6 +1381,37 @@ bool parse_build_flags(Array args) { string_to_lower(&build_context.microarch); break; } + case BuildFlag_TargetFeatures: { + GB_ASSERT(value.kind == ExactValue_String); + build_context.target_features_string = value.value_string; + string_to_lower(&build_context.target_features_string); + break; + } + case BuildFlag_RelocMode: { + GB_ASSERT(value.kind == ExactValue_String); + String v = value.value_string; + if (v == "default") { + build_context.reloc_mode = RelocMode_Default; + } else if (v == "static") { + build_context.reloc_mode = RelocMode_Static; + } else if (v == "pic") { + build_context.reloc_mode = RelocMode_PIC; + } else if (v == "dynamic-no-pic") { + build_context.reloc_mode = RelocMode_DynamicNoPIC; + } else { + gb_printf_err("-reloc-mode flag expected one of the following\n"); + gb_printf_err("\tdefault\n"); + gb_printf_err("\tstatic\n"); + gb_printf_err("\tpic\n"); + gb_printf_err("\tdynamic-no-pic\n"); + bad_flags = true; + } + + break; + } + case BuildFlag_DisableRedZone: + build_context.disable_red_zone = true; + break; case BuildFlag_TestName: { GB_ASSERT(value.kind == ExactValue_String); { @@ -1384,9 +1430,15 @@ bool parse_build_flags(Array args) { case BuildFlag_DisallowDo: build_context.disallow_do = true; break; + case BuildFlag_DisallowRTTI: + build_context.disallow_rtti = true; + break; case BuildFlag_DefaultToNilAllocator: build_context.ODIN_DEFAULT_TO_NIL_ALLOCATOR = true; break; + case BuildFlag_ForeignErrorProcedures: + build_context.ODIN_FOREIGN_ERROR_PROCEDURES = true; + break; case BuildFlag_InsertSemicolon: { gb_printf_err("-insert-semicolon flag is not required any more\n"); bad_flags = true; @@ -1469,6 +1521,20 @@ bool parse_build_flags(Array args) { case BuildFlag_VerboseErrors: build_context.show_error_line = true; break; + + case BuildFlag_ErrorPosStyle: + GB_ASSERT(value.kind == ExactValue_String); + + if (str_eq_ignore_case(value.value_string, str_lit("odin")) || str_eq_ignore_case(value.value_string, str_lit("default"))) { + build_context.ODIN_ERROR_POS_STYLE = ErrorPosStyle_Default; + } else if (str_eq_ignore_case(value.value_string, str_lit("unix"))) { + build_context.ODIN_ERROR_POS_STYLE = ErrorPosStyle_Unix; + } else { + gb_printf_err("-error-pos-style options are 'unix', 'odin' and 'default' (odin)\n"); + bad_flags = true; + } + break; + case BuildFlag_InternalIgnoreLazy: build_context.ignore_lazy = true; break; @@ -1487,6 +1553,10 @@ bool parse_build_flags(Array args) { gb_printf_err("Invalid -resource path %.*s, missing .rc\n", LIT(path)); bad_flags = true; break; + } else if (!gb_file_exists((const char *)path.text)) { + gb_printf_err("Invalid -resource path %.*s, file does not exist.\n", LIT(path)); + bad_flags = true; + break; } build_context.resource_filepath = substring(path, 0, string_extension_position(path)); build_context.has_resource = true; @@ -1501,6 +1571,11 @@ bool parse_build_flags(Array args) { String path = value.value_string; path = string_trim_whitespace(path); if (is_build_flag_path_valid(path)) { + if (path_is_directory(path)) { + gb_printf_err("Invalid -pdb-name path. %.*s, is a directory.\n", LIT(path)); + bad_flags = true; + break; + } // #if defined(GB_SYSTEM_WINDOWS) // String ext = path_extension(path); // if (ext != ".pdb") { @@ -1842,31 +1917,46 @@ void remove_temp_files(lbGenerator *gen) { void print_show_help(String const arg0, String const &command) { print_usage_line(0, "%.*s is a tool for managing Odin source code", LIT(arg0)); - print_usage_line(0, "Usage"); + print_usage_line(0, "Usage:"); print_usage_line(1, "%.*s %.*s [arguments]", LIT(arg0), LIT(command)); print_usage_line(0, ""); if (command == "build") { - print_usage_line(1, "build compile .odin file, or directory of .odin files, as an executable."); - print_usage_line(1, " one must contain the program's entry point, all must be in the same package."); - } else if (command == "run") { - print_usage_line(1, "run same as 'build', but also then runs the newly compiled executable."); - } else if (command == "check") { - print_usage_line(1, "check parse and type check .odin file(s)"); - } else if (command == "test") { - print_usage_line(1, "test build ands runs procedures with the attribute @(test) in the initial package"); - } else if (command == "query") { - print_usage_line(1, "query [experimental] parse, type check, and output a .json file containing information about the program"); - } else if (command == "doc") { - print_usage_line(1, "doc generate documentation from a .odin file, or directory of .odin files"); + print_usage_line(1, "build Compile directory of .odin files as an executable."); + print_usage_line(2, "One must contain the program's entry point, all must be in the same package."); + print_usage_line(2, "Use `-file` to build a single file instead."); print_usage_line(2, "Examples:"); - print_usage_line(3, "odin doc core/path"); - print_usage_line(3, "odin doc core/path core/path/filepath"); + print_usage_line(3, "odin build . # Build package in current directory"); + print_usage_line(3, "odin build
# Build package in "); + print_usage_line(3, "odin build filename.odin -file # Build single-file package, must contain entry point."); + } else if (command == "run") { + print_usage_line(1, "run Same as 'build', but also then runs the newly compiled executable."); + print_usage_line(2, "Append an empty flag and then the args, '-- ', to specify args for the output."); + print_usage_line(2, "Examples:"); + print_usage_line(3, "odin run . # Build and run package in current directory"); + print_usage_line(3, "odin run # Build and run package in "); + print_usage_line(3, "odin run filename.odin -file # Build and run single-file package, must contain entry point."); + } else if (command == "check") { + print_usage_line(1, "check Parse and type check directory of .odin files"); + print_usage_line(2, "Examples:"); + print_usage_line(3, "odin check . # Type check package in current directory"); + print_usage_line(3, "odin check # Type check package in "); + print_usage_line(3, "odin check filename.odin -file # Type check single-file package, must contain entry point."); + } else if (command == "test") { + print_usage_line(1, "test Build ands runs procedures with the attribute @(test) in the initial package"); + } else if (command == "query") { + print_usage_line(1, "query [experimental] Parse, type check, and output a .json file containing information about the program"); + } else if (command == "doc") { + print_usage_line(1, "doc generate documentation from a directory of .odin files"); + print_usage_line(2, "Examples:"); + print_usage_line(3, "odin doc . # Generate documentation on package in current directory"); + print_usage_line(3, "odin doc # Generate documentation on package in "); + print_usage_line(3, "odin doc filename.odin -file # Generate documentation on single-file package."); } else if (command == "version") { print_usage_line(1, "version print version"); } else if (command == "strip-semicolon") { print_usage_line(1, "strip-semicolon"); - print_usage_line(2, "parse and type check .odin file(s) and then remove unneeded semicolons from the entire project"); + print_usage_line(2, "Parse and type check .odin file(s) and then remove unneeded semicolons from the entire project"); } bool doc = command == "doc"; @@ -1881,6 +1971,13 @@ void print_show_help(String const arg0, String const &command) { print_usage_line(1, "Flags"); print_usage_line(0, ""); + if (check) { + print_usage_line(1, "-file"); + print_usage_line(2, "Tells `%.*s %.*s` to treat the given file as a self-contained package.", LIT(arg0), LIT(command)); + print_usage_line(2, "This means that `/a.odin` won't have access to `/b.odin`'s contents."); + print_usage_line(0, ""); + } + if (doc) { print_usage_line(1, "-short"); print_usage_line(2, "Show shortened documentation for the packages"); @@ -1971,6 +2068,7 @@ void print_show_help(String const arg0, String const &command) { print_usage_line(1, "-define:="); print_usage_line(2, "Defines a global constant with a value"); print_usage_line(2, "Example: -define:SPAM=123"); + print_usage_line(2, "To use: #config(SPAM, default_value)"); print_usage_line(0, ""); } @@ -2084,6 +2182,18 @@ void print_show_help(String const arg0, String const &command) { print_usage_line(3, "-microarch:sandybridge"); print_usage_line(3, "-microarch:native"); print_usage_line(0, ""); + + print_usage_line(1, "-reloc-mode:"); + print_usage_line(2, "Specifies the reloc mode"); + print_usage_line(2, "Options:"); + print_usage_line(3, "default"); + print_usage_line(3, "static"); + print_usage_line(3, "pic"); + print_usage_line(3, "dynamic-no-pic"); + print_usage_line(0, ""); + + print_usage_line(1, "-disable-red-zone"); + print_usage_line(2, "Disable red zone on a supported freestanding target"); } if (check) { @@ -2114,6 +2224,11 @@ void print_show_help(String const arg0, String const &command) { print_usage_line(1, "-verbose-errors"); print_usage_line(2, "Prints verbose error messages showing the code on that line and the location in that line"); print_usage_line(0, ""); + + print_usage_line(1, "-foreign-error-procedures"); + print_usage_line(2, "States that the error procedues used in the runtime are defined in a separate translation unit"); + print_usage_line(0, ""); + } if (run_or_build) { @@ -2431,8 +2546,6 @@ int strip_semicolons(Parser *parser) { return cast(int)failed; } - - int main(int arg_count, char const **arg_ptr) { if (arg_count < 2) { usage(make_string_c(arg_ptr[0])); @@ -2447,6 +2560,7 @@ int main(int arg_count, char const **arg_ptr) { virtual_memory_init(); mutex_init(&fullpath_mutex); mutex_init(&hash_exact_value_mutex); + mutex_init(&global_type_name_objc_metadata_mutex); init_string_buffer_memory(); init_string_interner(); @@ -2472,6 +2586,7 @@ int main(int arg_count, char const **arg_ptr) { String command = args[1]; String init_filename = {}; String run_args_string = {}; + isize last_non_run_arg = args.count; bool run_output = false; if (command == "run" || command == "test") { @@ -2487,7 +2602,6 @@ int main(int arg_count, char const **arg_ptr) { Array run_args = array_make(heap_allocator(), 0, arg_count); defer (array_free(&run_args)); - isize last_non_run_arg = args.count; for_array(i, args) { if (args[i] == "--") { last_non_run_arg = i; @@ -2500,6 +2614,7 @@ int main(int arg_count, char const **arg_ptr) { args = array_slice(args, 0, last_non_run_arg); run_args_string = string_join_and_quote(heap_allocator(), run_args); + init_filename = args[2]; run_output = true; @@ -2587,11 +2702,40 @@ int main(int arg_count, char const **arg_ptr) { return 1; } + init_filename = copy_string(permanent_allocator(), init_filename); + if (init_filename == "-help" || init_filename == "--help") { build_context.show_help = true; } + if (init_filename.len > 0 && !build_context.show_help) { + // The command must be build, run, test, check, or another that takes a directory or filename. + if (!path_is_directory(init_filename)) { + // Input package is a filename. We allow this only if `-file` was given, otherwise we exit with an error message. + bool single_file_package = false; + for_array(i, args) { + if (i >= 3 && i <= last_non_run_arg && args[i] == "-file") { + single_file_package = true; + break; + } + } + + if (!single_file_package) { + gb_printf_err("ERROR: `%.*s %.*s` takes a package as its first argument.\n", LIT(args[0]), LIT(command)); + gb_printf_err("Did you mean `%.*s %.*s %.*s -file`?\n", LIT(args[0]), LIT(command), LIT(init_filename)); + gb_printf_err("The `-file` flag tells it to treat a file as a self-contained package.\n"); + return 1; + } else { + String const ext = str_lit(".odin"); + if (!string_ends_with(init_filename, ext)) { + gb_printf_err("Expected either a directory or a .odin file, got '%.*s'\n", LIT(init_filename)); + return 1; + } + } + } + } + build_context.command = command; if (!parse_build_flags(args)) { @@ -2606,16 +2750,27 @@ int main(int arg_count, char const **arg_ptr) { // NOTE(bill): add 'shared' directory if it is not already set if (!find_library_collection_path(str_lit("shared"), nullptr)) { add_library_collection(str_lit("shared"), - get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("shared"))); + get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("shared"))); } - init_build_context(selected_target_metrics ? selected_target_metrics->metrics : nullptr); // if (build_context.word_size == 4 && build_context.metrics.os != TargetOs_js) { // print_usage_line(0, "%.*s 32-bit is not yet supported for this platform", LIT(args[0])); // return 1; // } + // Set and check build paths... + if (!init_build_paths(init_filename)) { + return 1; + } + + if (build_context.show_debug_messages) { + for_array(i, build_context.build_paths) { + String build_path = path_to_string(heap_allocator(), build_context.build_paths[i]); + debugf("build_paths[%ld]: %.*s\n", i, LIT(build_path)); + } + } + init_global_thread_pool(); defer (thread_pool_destroy(&global_thread_pool)); @@ -2632,6 +2787,8 @@ int main(int arg_count, char const **arg_ptr) { } defer (destroy_parser(parser)); + // TODO(jeroen): Remove the `init_filename` param. + // Let's put that on `build_context.build_paths[0]` instead. if (parse_packages(parser, init_filename) != ParseFile_None) { return 1; } @@ -2710,16 +2867,10 @@ int main(int arg_count, char const **arg_ptr) { } if (run_output) { - #if defined(GB_SYSTEM_WINDOWS) - return system_exec_command_line_app("odin run", "%.*s.exe %.*s", LIT(gen->output_base), LIT(run_args_string)); - #else - //NOTE(thebirk): This whole thing is a little leaky - String output_ext = {}; - String complete_path = concatenate_strings(permanent_allocator(), gen->output_base, output_ext); - complete_path = path_to_full_path(permanent_allocator(), complete_path); - return system_exec_command_line_app("odin run", "\"%.*s\" %.*s", LIT(complete_path), LIT(run_args_string)); - #endif - } + String exe_name = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Output]); + defer (gb_free(heap_allocator(), exe_name.text)); + return system_exec_command_line_app("odin run", "\"%.*s\" %.*s", LIT(exe_name), LIT(run_args_string)); + } return 0; } diff --git a/src/microsoft_craziness.h b/src/microsoft_craziness.h index b4f815284..812513875 100644 --- a/src/microsoft_craziness.h +++ b/src/microsoft_craziness.h @@ -45,53 +45,97 @@ // // Here is the API you need to know about: // - - gb_global gbAllocator mc_allocator = heap_allocator(); struct Find_Result { - int windows_sdk_version; // Zero if no Windows SDK found. + int windows_sdk_version; // Zero if no Windows SDK found. - wchar_t const *windows_sdk_root; - wchar_t const *windows_sdk_um_library_path; - wchar_t const *windows_sdk_ucrt_library_path; + wchar_t const *windows_sdk_root; + wchar_t const *windows_sdk_um_library_path; + wchar_t const *windows_sdk_ucrt_library_path; - wchar_t const *vs_exe_path; - wchar_t const *vs_library_path; + wchar_t const *vs_exe_path; + wchar_t const *vs_library_path; }; struct Find_Result_Utf8 { - int windows_sdk_version; // Zero if no Windows SDK found. + int windows_sdk_version; // Zero if no Windows SDK found. - String windows_sdk_root; - String windows_sdk_um_library_path; - String windows_sdk_ucrt_library_path; + String windows_sdk_root; + String windows_sdk_um_library_path; + String windows_sdk_ucrt_library_path; - String vs_exe_path; - String vs_library_path; + String vs_exe_path; + String vs_library_path; }; - -Find_Result find_visual_studio_and_windows_sdk(); Find_Result_Utf8 find_visual_studio_and_windows_sdk_utf8(); -void free_resources(Find_Result *result) { - // free(result->windows_sdk_root); - // free(result->windows_sdk_um_library_path); - // free(result->windows_sdk_ucrt_library_path); - // free(result->vs_exe_path); - // free(result->vs_library_path); +String mc_wstring_to_string(wchar_t const *str) { + return string16_to_string(mc_allocator, make_string16_c(str)); } -void free_resources(Find_Result_Utf8 *result) { - // gbAllocator a = heap_allocator(); - // gb_free(a, result->windows_sdk_root.text); - // gb_free(a, result->windows_sdk_um_library_path.text); - // gb_free(a, result->windows_sdk_ucrt_library_path.text); - // gb_free(a, result->vs_exe_path.text); - // gb_free(a, result->vs_library_path.text); +String16 mc_string_to_wstring(String str) { + return string_to_string16(mc_allocator, str); } +String mc_concat(String a, String b) { + return concatenate_strings(mc_allocator, a, b); +} + +String mc_concat(String a, String b, String c) { + return concatenate3_strings(mc_allocator, a, b, c); +} + +String mc_get_env(String key) { + char const * value = gb_get_env((char const *)key.text, mc_allocator); + return make_string_c(value); +} + +void mc_free(String str) { + if (str.len) gb_free(mc_allocator, str.text); +} + +void mc_free(String16 str) { + if (str.len) gb_free(mc_allocator, str.text); +} + +void mc_free_all() { + gb_free_all(mc_allocator); +} + +typedef struct _MC_Find_Data { + DWORD file_attributes; + String filename; +} MC_Find_Data; + + +HANDLE mc_find_first(String wildcard, MC_Find_Data *find_data) { + WIN32_FIND_DATAW _find_data; + + String16 wildcard_wide = mc_string_to_wstring(wildcard); + defer (mc_free(wildcard_wide)); + + HANDLE handle = FindFirstFileW(wildcard_wide.text, &_find_data); + if (handle == INVALID_HANDLE_VALUE) return false; + + find_data->file_attributes = _find_data.dwFileAttributes; + find_data->filename = mc_wstring_to_string(_find_data.cFileName); + return handle; +} + +bool mc_find_next(HANDLE handle, MC_Find_Data *find_data) { + WIN32_FIND_DATAW _find_data; + bool success = !!FindNextFileW(handle, &_find_data); + + find_data->file_attributes = _find_data.dwFileAttributes; + find_data->filename = mc_wstring_to_string(_find_data.cFileName); + return success; +} + +void mc_find_close(HANDLE handle) { + FindClose(handle); +} // // Call find_visual_studio_and_windows_sdk, look at the resulting @@ -142,481 +186,598 @@ void free_resources(Find_Result_Utf8 *result) { // COM objects for the ridiculous Microsoft craziness. - typedef WCHAR* BSTR; typedef const WCHAR* LPCOLESTR; struct DECLSPEC_UUID("B41463C3-8866-43B5-BC33-2B0676F7F42E") DECLSPEC_NOVTABLE ISetupInstance : public IUnknown { - virtual HRESULT STDMETHODCALLTYPE GetInstanceId(BSTR* pbstrInstanceId) = 0; - virtual HRESULT STDMETHODCALLTYPE GetInstallDate(LPFILETIME pInstallDate) = 0; - virtual HRESULT STDMETHODCALLTYPE GetInstallationName(BSTR* pbstrInstallationName) = 0; - virtual HRESULT STDMETHODCALLTYPE GetInstallationPath(BSTR* pbstrInstallationPath) = 0; - virtual HRESULT STDMETHODCALLTYPE GetInstallationVersion(BSTR* pbstrInstallationVersion) = 0; - virtual HRESULT STDMETHODCALLTYPE GetDisplayName(LCID lcid, BSTR* pbstrDisplayName) = 0; - virtual HRESULT STDMETHODCALLTYPE GetDescription(LCID lcid, BSTR* pbstrDescription) = 0; - virtual HRESULT STDMETHODCALLTYPE ResolvePath(LPCOLESTR pwszRelativePath, BSTR* pbstrAbsolutePath) = 0; + virtual HRESULT STDMETHODCALLTYPE GetInstanceId(BSTR* pbstrInstanceId) = 0; + virtual HRESULT STDMETHODCALLTYPE GetInstallDate(LPFILETIME pInstallDate) = 0; + virtual HRESULT STDMETHODCALLTYPE GetInstallationName(BSTR* pbstrInstallationName) = 0; + virtual HRESULT STDMETHODCALLTYPE GetInstallationPath(BSTR* pbstrInstallationPath) = 0; + virtual HRESULT STDMETHODCALLTYPE GetInstallationVersion(BSTR* pbstrInstallationVersion) = 0; + virtual HRESULT STDMETHODCALLTYPE GetDisplayName(LCID lcid, BSTR* pbstrDisplayName) = 0; + virtual HRESULT STDMETHODCALLTYPE GetDescription(LCID lcid, BSTR* pbstrDescription) = 0; + virtual HRESULT STDMETHODCALLTYPE ResolvePath(LPCOLESTR pwszRelativePath, BSTR* pbstrAbsolutePath) = 0; }; struct DECLSPEC_UUID("6380BCFF-41D3-4B2E-8B2E-BF8A6810C848") DECLSPEC_NOVTABLE IEnumSetupInstances : public IUnknown { - virtual HRESULT STDMETHODCALLTYPE Next(ULONG celt, ISetupInstance** rgelt, ULONG* pceltFetched) = 0; - virtual HRESULT STDMETHODCALLTYPE Skip(ULONG celt) = 0; - virtual HRESULT STDMETHODCALLTYPE Reset(void) = 0; - virtual HRESULT STDMETHODCALLTYPE Clone(IEnumSetupInstances** ppenum) = 0; + virtual HRESULT STDMETHODCALLTYPE Next(ULONG celt, ISetupInstance** rgelt, ULONG* pceltFetched) = 0; + virtual HRESULT STDMETHODCALLTYPE Skip(ULONG celt) = 0; + virtual HRESULT STDMETHODCALLTYPE Reset(void) = 0; + virtual HRESULT STDMETHODCALLTYPE Clone(IEnumSetupInstances** ppenum) = 0; }; struct DECLSPEC_UUID("42843719-DB4C-46C2-8E7C-64F1816EFD5B") DECLSPEC_NOVTABLE ISetupConfiguration : public IUnknown { - virtual HRESULT STDMETHODCALLTYPE EnumInstances(IEnumSetupInstances** ppEnumInstances) = 0; - virtual HRESULT STDMETHODCALLTYPE GetInstanceForCurrentProcess(ISetupInstance** ppInstance) = 0; - virtual HRESULT STDMETHODCALLTYPE GetInstanceForPath(LPCWSTR wzPath, ISetupInstance** ppInstance) = 0; + virtual HRESULT STDMETHODCALLTYPE EnumInstances(IEnumSetupInstances** ppEnumInstances) = 0; + virtual HRESULT STDMETHODCALLTYPE GetInstanceForCurrentProcess(ISetupInstance** ppInstance) = 0; + virtual HRESULT STDMETHODCALLTYPE GetInstanceForPath(LPCWSTR wzPath, ISetupInstance** ppInstance) = 0; }; // The beginning of the actual code that does things. - -struct Version_Data { - i32 best_version[4]; // For Windows 8 versions, only two of these numbers are used. - wchar_t const *best_name; +struct Version_Data_Utf8 { + i32 best_version[4]; // For Windows 8 versions, only two of these numbers are used. + String best_name; }; -bool os_file_exists(wchar_t const *name) { - // @Robustness: What flags do we really want to check here? +typedef void (*MC_Visit_Proc)(String short_name, String full_name, Version_Data_Utf8 *data); +bool mc_visit_files(String dir_name, Version_Data_Utf8 *data, MC_Visit_Proc proc) { - auto attrib = GetFileAttributesW(name); - if (attrib == INVALID_FILE_ATTRIBUTES) return false; - if (attrib & FILE_ATTRIBUTE_DIRECTORY) return false; + // Visit everything in one folder (non-recursively). If it's a directory + // that doesn't start with ".", call the visit proc on it. The visit proc + // will see if the filename conforms to the expected versioning pattern. - return true; + String wildcard_name = mc_concat(dir_name, str_lit("\\*")); + defer (mc_free(wildcard_name)); + + MC_Find_Data find_data; + + HANDLE handle = mc_find_first(wildcard_name, &find_data); + if (handle == INVALID_HANDLE_VALUE) return false; + + bool success = true; + while (success) { + if ((find_data.file_attributes & FILE_ATTRIBUTE_DIRECTORY) && (find_data.filename[0] != '.')) { + String full_name = mc_concat(dir_name, str_lit("\\"), find_data.filename); + defer (mc_free(full_name)); + + proc(find_data.filename, full_name, data); + } + + success = mc_find_next(handle, &find_data); + if (!success) break; + } + mc_find_close(handle); + return true; } -wchar_t *concat(wchar_t const *a, wchar_t const *b, wchar_t const *c = nullptr, wchar_t const *d = nullptr) { - // Concatenate up to 4 wide strings together. Allocated with malloc. - // If you don't like that, use a programming language that actually - // helps you with using custom allocators. Or just edit the code. +String find_windows_kit_root(HKEY key, String const version) { + // Given a key to an already opened registry entry, + // get the value stored under the 'version' subkey. + // If that's not the right terminology, hey, I never do registry stuff. - isize len_a = string16_len(a); - isize len_b = string16_len(b); - isize len_c = string16_len(c); - isize len_d = string16_len(d); + char *version_str = (char*)version.text; - wchar_t *result = (wchar_t *)calloc(2, (len_a + len_b + len_c + len_d + 1)); - gb_memmove(result, a, len_a*2); - gb_memmove(result + len_a, b, len_b*2); + DWORD required_length; + auto rc = RegQueryValueExA(key, version_str, NULL, NULL, NULL, &required_length); + if (rc != 0) return {}; - if (c) gb_memmove(result + len_a + len_b, c, len_c * 2); - if (d) gb_memmove(result + len_a + len_b + len_c, d, len_d * 2); + DWORD length = required_length + 2; // The +2 is for the maybe optional zero later on. Probably we are over-allocating. + char *c_str = gb_alloc_array(mc_allocator, char, length); - result[len_a + len_b + len_c + len_d] = 0; + rc = RegQueryValueExA(key, version_str, NULL, NULL, (LPBYTE)c_str, &length); // We know that version is zero-terminated... + if (rc != 0) return {}; - return result; + // The documentation says that if the string for some reason was not stored + // with zero-termination, we need to manually terminate it. Sigh!! + + if (c_str[required_length]) { + c_str[required_length+1] = 0; + } + + String value = make_string_c(c_str); + + return value; } -typedef void (*Visit_Proc_W)(wchar_t const *short_name, wchar_t const *full_name, Version_Data *data); -bool visit_files_w(wchar_t const *dir_name, Version_Data *data, Visit_Proc_W proc) { +void win10_best(String short_name, String full_name, Version_Data_Utf8 *data) { + // Find the Windows 10 subdirectory with the highest version number. - // Visit everything in one folder (non-recursively). If it's a directory - // that doesn't start with ".", call the visit proc on it. The visit proc - // will see if the filename conforms to the expected versioning pattern. + int i0, i1, i2, i3; + auto success = sscanf_s((const char *const)short_name.text, "%d.%d.%d.%d", &i0, &i1, &i2, &i3); + if (success < 4) return; - auto wildcard_name = concat(dir_name, L"\\*"); - defer (free(wildcard_name)); + if (i0 < data->best_version[0]) return; + else if (i0 == data->best_version[0]) { + if (i1 < data->best_version[1]) return; + else if (i1 == data->best_version[1]) { + if (i2 < data->best_version[2]) return; + else if (i2 == data->best_version[2]) { + if (i3 < data->best_version[3]) return; + } + } + } - WIN32_FIND_DATAW find_data; - auto handle = FindFirstFileW(wildcard_name, &find_data); - if (handle == INVALID_HANDLE_VALUE) return false; + // we have to copy_string and free here because visit_files free's the full_name string + // after we execute this function, so Win*_Data would contain an invalid pointer. + if (data->best_name.len > 0) mc_free(data->best_name); - while (true) { - if ((find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (find_data.cFileName[0] != '.')) { - auto full_name = concat(dir_name, L"\\", find_data.cFileName); - defer (free(full_name)); + data->best_name = copy_string(mc_allocator, full_name); - proc(find_data.cFileName, full_name, data); - } - - auto success = FindNextFileW(handle, &find_data); - if (!success) break; - } - - FindClose(handle); - - return true; + if (data->best_name.len > 0) { + data->best_version[0] = i0; + data->best_version[1] = i1; + data->best_version[2] = i2; + data->best_version[3] = i3; + } } +void win8_best(String short_name, String full_name, Version_Data_Utf8 *data) { + // Find the Windows 8 subdirectory with the highest version number. -wchar_t *find_windows_kit_root(HKEY key, wchar_t const *version) { - // Given a key to an already opened registry entry, - // get the value stored under the 'version' subkey. - // If that's not the right terminology, hey, I never do registry stuff. + int i0, i1; + auto success = sscanf_s((const char *const)short_name.text, "winv%d.%d", &i0, &i1); + if (success < 2) return; - DWORD required_length; - auto rc = RegQueryValueExW(key, version, NULL, NULL, NULL, &required_length); - if (rc != 0) return NULL; + if (i0 < data->best_version[0]) return; + else if (i0 == data->best_version[0]) { + if (i1 < data->best_version[1]) return; + } - DWORD length = required_length + 2; // The +2 is for the maybe optional zero later on. Probably we are over-allocating. - wchar_t *value = (wchar_t *)calloc(1, length); - if (!value) return NULL; + // we have to copy_string and free here because visit_files free's the full_name string + // after we execute this function, so Win*_Data would contain an invalid pointer. + if (data->best_name.len > 0) mc_free(data->best_name); + data->best_name = copy_string(mc_allocator, full_name); - rc = RegQueryValueExW(key, version, NULL, NULL, (LPBYTE)value, &length); // We know that version is zero-terminated... - if (rc != 0) return NULL; - - // The documentation says that if the string for some reason was not stored - // with zero-termination, we need to manually terminate it. Sigh!! - - if (value[length]) { - value[length+1] = 0; - } - - return value; + if (data->best_name.len > 0) { + data->best_version[0] = i0; + data->best_version[1] = i1; + } } -void win10_best(wchar_t const *short_name, wchar_t const *full_name, Version_Data *data) { - // Find the Windows 10 subdirectory with the highest version number. +void find_windows_kit_root(Find_Result_Utf8 *result) { + // Information about the Windows 10 and Windows 8 development kits + // is stored in the same place in the registry. We open a key + // to that place, first checking preferntially for a Windows 10 kit, + // then, if that's not found, a Windows 8 kit. - int i0, i1, i2, i3; - auto success = swscanf_s(short_name, L"%d.%d.%d.%d", &i0, &i1, &i2, &i3); - if (success < 4) return; + HKEY main_key; - if (i0 < data->best_version[0]) return; - else if (i0 == data->best_version[0]) { - if (i1 < data->best_version[1]) return; - else if (i1 == data->best_version[1]) { - if (i2 < data->best_version[2]) return; - else if (i2 == data->best_version[2]) { - if (i3 < data->best_version[3]) return; - } - } - } + auto rc = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", + 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY | KEY_ENUMERATE_SUB_KEYS, &main_key); + if (rc != S_OK) return; + defer (RegCloseKey(main_key)); - // we have to copy_string and free here because visit_files free's the full_name string - // after we execute this function, so Win*_Data would contain an invalid pointer. - if (data->best_name) free((void *)data->best_name); - data->best_name = _wcsdup(full_name); + // Look for a Windows 10 entry. + String windows10_root = find_windows_kit_root(main_key, str_lit("KitsRoot10")); - if (data->best_name) { - data->best_version[0] = i0; - data->best_version[1] = i1; - data->best_version[2] = i2; - data->best_version[3] = i3; - } + if (windows10_root.len > 0) { + defer (mc_free(windows10_root)); + + String windows10_lib = mc_concat(windows10_root, str_lit("Lib")); + defer (mc_free(windows10_lib)); + + Version_Data_Utf8 data = {0}; + mc_visit_files(windows10_lib, &data, win10_best); + if (data.best_name.len > 0) { + result->windows_sdk_version = 10; + result->windows_sdk_root = mc_concat(data.best_name, str_lit("\\")); + return; + } + mc_free(data.best_name); + } + + // Look for a Windows 8 entry. + String windows8_root = find_windows_kit_root(main_key, str_lit("KitsRoot81")); + + if (windows8_root.len > 0) { + defer (mc_free(windows8_root)); + + String windows8_lib = mc_concat(windows8_root, str_lit("Lib")); + defer (mc_free(windows8_lib)); + + Version_Data_Utf8 data = {0}; + mc_visit_files(windows8_lib, &data, win8_best); + if (data.best_name.len > 0) { + result->windows_sdk_version = 8; + result->windows_sdk_root = mc_concat(data.best_name, str_lit("\\")); + return; + } + mc_free(data.best_name); + } + // If we get here, we failed to find anything. } -void win8_best(wchar_t const *short_name, wchar_t const *full_name, Version_Data *data) { - // Find the Windows 8 subdirectory with the highest version number. +bool find_visual_studio_by_fighting_through_microsoft_craziness(Find_Result_Utf8 *result) { + // The name of this procedure is kind of cryptic. Its purpose is + // to fight through Microsoft craziness. The things that the fine + // Visual Studio team want you to do, JUST TO FIND A SINGLE FOLDER + // THAT EVERYONE NEEDS TO FIND, are ridiculous garbage. - int i0, i1; - auto success = swscanf_s(short_name, L"winv%d.%d", &i0, &i1); - if (success < 2) return; + // For earlier versions of Visual Studio, you'd find this information in the registry, + // similarly to the Windows Kits above. But no, now it's the future, so to ask the + // question "Where is the Visual Studio folder?" you have to do a bunch of COM object + // instantiation, enumeration, and querying. (For extra bonus points, try doing this in + // a new, underdeveloped programming language where you don't have COM routines up + // and running yet. So fun.) + // + // If all this COM object instantiation, enumeration, and querying doesn't give us + // a useful result, we drop back to the registry-checking method. - if (i0 < data->best_version[0]) return; - else if (i0 == data->best_version[0]) { - if (i1 < data->best_version[1]) return; - } - // we have to copy_string and free here because visit_files free's the full_name string - // after we execute this function, so Win*_Data would contain an invalid pointer. - if (data->best_name) free((void *)data->best_name); - data->best_name = _wcsdup(full_name); + auto rc = CoInitialize(NULL); + // "Subsequent valid calls return false." So ignore false. + if (rc != S_OK) return false; - if (data->best_name) { - data->best_version[0] = i0; - data->best_version[1] = i1; - } + GUID my_uid = {0x42843719, 0xDB4C, 0x46C2, {0x8E, 0x7C, 0x64, 0xF1, 0x81, 0x6E, 0xFD, 0x5B}}; + GUID CLSID_SetupConfiguration = {0x177F0C4A, 0x1CD3, 0x4DE7, {0xA3, 0x2C, 0x71, 0xDB, 0xBB, 0x9F, 0xA3, 0x6D}}; + + ISetupConfiguration *config = NULL; + HRESULT hr = 0; + hr = CoCreateInstance(CLSID_SetupConfiguration, NULL, CLSCTX_INPROC_SERVER, my_uid, (void **)&config); + if (hr == 0) { + defer (config->Release()); + + IEnumSetupInstances *instances = NULL; + hr = config->EnumInstances(&instances); + if (hr != 0) return false; + if (!instances) return false; + defer (instances->Release()); + + for (;;) { + ULONG found = 0; + ISetupInstance *instance = NULL; + auto hr = instances->Next(1, &instance, &found); + if (hr != S_OK) break; + + defer (instance->Release()); + + wchar_t* inst_path_wide; + hr = instance->GetInstallationPath(&inst_path_wide); + if (hr != S_OK) continue; + defer (SysFreeString(inst_path_wide)); + + String inst_path = mc_wstring_to_string(inst_path_wide); + defer (mc_free(inst_path)); + + String tools_filename = mc_concat(inst_path, str_lit("\\VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt")); + defer (mc_free(tools_filename)); + + gbFileContents tool_version = gb_file_read_contents(mc_allocator, true, (const char*)tools_filename.text); + defer (gb_file_free_contents(&tool_version)); + + String version_string = make_string((const u8*)tool_version.data, tool_version.size); + version_string = string_trim_whitespace(version_string); + + String base_path = mc_concat(inst_path, str_lit("\\VC\\Tools\\MSVC\\"), version_string); + defer (mc_free(base_path)); + + String library_path = {}; + if (build_context.metrics.arch == TargetArch_amd64) { + library_path = mc_concat(base_path, str_lit("\\lib\\x64\\")); + } else if (build_context.metrics.arch == TargetArch_i386) { + library_path = mc_concat(base_path, str_lit("\\lib\\x86\\")); + } else { + continue; + } + + String library_file = mc_concat(library_path, str_lit("vcruntime.lib")); + + if (gb_file_exists((const char*)library_file.text)) { + if (build_context.metrics.arch == TargetArch_amd64) { + result->vs_exe_path = mc_concat(base_path, str_lit("\\bin\\Hostx64\\x64\\")); + } else if (build_context.metrics.arch == TargetArch_i386) { + result->vs_exe_path = mc_concat(base_path, str_lit("\\bin\\Hostx86\\x86\\")); + } else { + continue; + } + + result->vs_library_path = library_path; + return true; + } + /* + Ryan Saunderson said: + "Clang uses the 'SetupInstance->GetInstallationVersion' / ISetupHelper->ParseVersion to find the newest version + and then reads the tools file to define the tools path - which is definitely better than what i did." + + So... @Incomplete: Should probably pick the newest version... + */ + } + } + + // If we get here, we didn't find Visual Studio 2017. Try earlier versions. + { + HKEY vs7_key; + rc = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7", 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &vs7_key); + if (rc != S_OK) return false; + defer (RegCloseKey(vs7_key)); + + // Hardcoded search for 4 prior Visual Studio versions. Is there something better to do here? + char const *versions[] = { "14.0", "13.0", "12.0", "11.0", "10.0", "9.0", }; + const int NUM_VERSIONS = sizeof(versions) / sizeof(versions[0]); + + for (int i = 0; i < NUM_VERSIONS; i++) { + char const *v = versions[i]; + + DWORD dw_type; + DWORD required_length; + + auto rc = RegQueryValueExA(vs7_key, v, NULL, &dw_type, NULL, &required_length); + if ((rc == ERROR_FILE_NOT_FOUND) || (dw_type != REG_SZ)) { + continue; + } + + DWORD length = required_length + 2; // The +2 is for the maybe optional zero later on. Probably we are over-allocating. + char *c_str = gb_alloc_array(mc_allocator, char, length); + + rc = RegQueryValueExA(vs7_key, v, NULL, NULL, (LPBYTE)c_str, &length); + if (rc != 0) continue; + + if (c_str[required_length]) { + c_str[required_length+1] = 0; + } + String base_path = make_string_c(c_str); + + String lib_path = {}; + + if (build_context.metrics.arch == TargetArch_amd64) { + lib_path = mc_concat(base_path, str_lit("VC\\Lib\\amd64\\")); + } else if (build_context.metrics.arch == TargetArch_i386) { + lib_path = mc_concat(base_path, str_lit("VC\\Lib\\")); + } else { + continue; + } + + // Check to see whether a vcruntime.lib actually exists here. + String vcruntime_filename = mc_concat(lib_path, str_lit("vcruntime.lib")); + defer (mc_free(vcruntime_filename)); + + if (gb_file_exists((const char*)vcruntime_filename.text)) { + if (build_context.metrics.arch == TargetArch_amd64) { + result->vs_exe_path = mc_concat(base_path, str_lit("VC\\bin\\")); + } else if (build_context.metrics.arch == TargetArch_i386) { + result->vs_exe_path = mc_concat(base_path, str_lit("VC\\bin\\x86_amd64\\")); + } else { + continue; + } + result->vs_library_path = lib_path; + return true; + } + mc_free(lib_path); + } + // If we get here, we failed to find anything. + } + return false; } -void find_windows_kit_root(Find_Result *result) { - // Information about the Windows 10 and Windows 8 development kits - // is stored in the same place in the registry. We open a key - // to that place, first checking preferntially for a Windows 10 kit, - // then, if that's not found, a Windows 8 kit. +// NOTE(WalterPlinge): Environment variables can help to find Visual C++ and WinSDK paths for both +// official and portable installations (like mmozeiko's portable msvc script). This will only use +// the first paths it finds, and won't overwrite any values that `result` already has. +bool find_msvc_install_from_env_vars(Find_Result_Utf8 *result) { + if (build_context.metrics.arch != TargetArch_amd64 && build_context.metrics.arch != TargetArch_i386) { + return false; + } - HKEY main_key; + // We can find windows sdk using the following combination of env vars: + // (UniversalCRTSdkDir or WindowsSdkDir) and (WindowsSDKLibVersion or WindowsSDKVersion) + bool sdk_found = false; - auto rc = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", - 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY | KEY_ENUMERATE_SUB_KEYS, &main_key); - if (rc != S_OK) return; - defer (RegCloseKey(main_key)); + // These appear to be suitable env vars used by Visual Studio + String win_sdk_ver_env = mc_get_env(str_lit("WindowsSDKVersion")); + String win_sdk_lib_env = mc_get_env(str_lit("WindowsSDKLibVersion")); + String win_sdk_dir_env = mc_get_env(str_lit("WindowsSdkDir")); + String crt_sdk_dir_env = mc_get_env(str_lit("UniversalCRTSdkDir")); - // Look for a Windows 10 entry. - auto windows10_root = find_windows_kit_root(main_key, L"KitsRoot10"); + defer ({ + mc_free(win_sdk_ver_env); + mc_free(win_sdk_lib_env); + mc_free(win_sdk_dir_env); + mc_free(crt_sdk_dir_env); + }); + // NOTE(WalterPlinge): If any combination is found, let's just assume they are correct + if ((win_sdk_ver_env.len || win_sdk_lib_env.len) && (win_sdk_dir_env.len || crt_sdk_dir_env.len)) { + //? Maybe we need to handle missing '\' at end of strings, so far it doesn't seem an issue + String dir = win_sdk_dir_env.len ? win_sdk_dir_env : crt_sdk_dir_env; + String ver = win_sdk_ver_env.len ? win_sdk_ver_env : win_sdk_lib_env; - if (windows10_root) { - defer (free(windows10_root)); + // These have trailing '\' as we are just composing the path + String um_dir = build_context.metrics.arch == TargetArch_amd64 + ? str_lit("um\\x64\\") + : str_lit("um\\x86\\"); + String ucrt_dir = build_context.metrics.arch == TargetArch_amd64 + ? str_lit("ucrt\\x64\\") + : str_lit("ucrt\\x86\\"); + result->windows_sdk_root = mc_concat(dir, str_lit("Lib\\"), ver); + result->windows_sdk_um_library_path = mc_concat(result->windows_sdk_root, um_dir); + result->windows_sdk_ucrt_library_path = mc_concat(result->windows_sdk_root, ucrt_dir); - Version_Data data = {0}; - auto windows10_lib = concat(windows10_root, L"Lib"); - defer (free(windows10_lib)); + sdk_found = true; + } + // If we haven't found it yet, we can loop through LIB for specific folders + //? This may not be robust enough using `um\x64` and `ucrt\x64` + if (!sdk_found) { + String lib = mc_get_env(str_lit("LIB")); + defer (mc_free(lib)); - visit_files_w(windows10_lib, &data, win10_best); - if (data.best_name) { - result->windows_sdk_version = 10; - result->windows_sdk_root = concat(data.best_name, L"\\"); - return; - } - } + if (lib.len) { + // NOTE(WalterPlinge): I don't know if there's a chance for the LIB variable + // to be set without a trailing '\' (apart from manually), so we can just + // check paths without it (see use of `String end` in the loop below) + String um_dir = build_context.metrics.arch == TargetArch_amd64 + ? str_lit("um\\x64") + : str_lit("um\\x86"); + String ucrt_dir = build_context.metrics.arch == TargetArch_amd64 + ? str_lit("ucrt\\x64") + : str_lit("ucrt\\x86"); - // Look for a Windows 8 entry. - auto windows8_root = find_windows_kit_root(main_key, L"KitsRoot81"); + isize lo = {0}; + isize hi = {0}; + for (isize c = 0; c <= lib.len; c += 1) { + if (c != lib.len && lib[c] != ';') { + continue; + } + hi = c; + String dir = substring(lib, lo, hi); + defer (lo = hi + 1); - if (windows8_root) { - defer (free(windows8_root)); + // Remove the last slash so we can match with the strings above + String end = dir[dir.len - 1] == '\\' + ? substring(dir, 0, dir.len - 1) + : substring(dir, 0, dir.len); - auto windows8_lib = concat(windows8_root, L"Lib"); - defer (free(windows8_lib)); + // Find one and we can make the other + if (string_ends_with(end, um_dir)) { + result->windows_sdk_um_library_path = mc_concat(end, str_lit("\\")); + break; + } else if (string_ends_with(end, ucrt_dir)) { + result->windows_sdk_ucrt_library_path = mc_concat(end, str_lit("\\")); + break; + } + } - Version_Data data = {0}; - visit_files_w(windows8_lib, &data, win8_best); - if (data.best_name) { - result->windows_sdk_version = 8; - result->windows_sdk_root = concat(data.best_name, L"\\"); - return; - } - } + // Get the root from the one we found, and make the other + // NOTE(WalterPlinge): we need to copy the string so that we don't risk a double free + if (result->windows_sdk_um_library_path.len > 0) { + String root = substring(result->windows_sdk_um_library_path, 0, result->windows_sdk_um_library_path.len - 1 - um_dir.len); + result->windows_sdk_root = copy_string(mc_allocator, root); + result->windows_sdk_ucrt_library_path = mc_concat(result->windows_sdk_root, ucrt_dir, str_lit("\\")); + } else if (result->windows_sdk_ucrt_library_path.len > 0) { + String root = substring(result->windows_sdk_ucrt_library_path, 0, result->windows_sdk_ucrt_library_path.len - 1 - ucrt_dir.len); + result->windows_sdk_root = copy_string(mc_allocator, root); + result->windows_sdk_um_library_path = mc_concat(result->windows_sdk_root, um_dir, str_lit("\\")); + } - // If we get here, we failed to find anything. + if (result->windows_sdk_root.len > 0) { + sdk_found = true; + } + } + } + + // NOTE(WalterPlinge): So far this function assumes it will only be called if MSVC was + // installed using mmozeiko's portable msvc script, which uses the windows 10 sdk. + // This may need to be changed later if it ends up causing problems. + if (sdk_found && result->windows_sdk_version == 0) { + result->windows_sdk_version = 10; + } + + bool vs_found = false; + + if (result->vs_exe_path.len > 0 && result->vs_library_path.len > 0) { + vs_found = true; + } + + // We can find visual studio using VCToolsInstallDir + if (!vs_found) { + String vctid = mc_get_env(str_lit("VCToolsInstallDir")); + defer (mc_free(vctid)); + + if (vctid.len) { + String exe = build_context.metrics.arch == TargetArch_amd64 + ? str_lit("bin\\Hostx64\\x64\\") + : str_lit("bin\\Hostx86\\x86\\"); + String lib = build_context.metrics.arch == TargetArch_amd64 + ? str_lit("lib\\x64\\") + : str_lit("lib\\x86\\"); + + result->vs_exe_path = mc_concat(vctid, exe); + result->vs_library_path = mc_concat(vctid, lib); + vs_found = true; + } + } + + // If we haven't found it yet, we can loop through Path for specific folders + if (!vs_found) { + String path = mc_get_env(str_lit("Path")); + defer (mc_free(path)); + + if (path.len) { + String exe = build_context.metrics.arch == TargetArch_amd64 + ? str_lit("bin\\Hostx64\\x64") + : str_lit("bin\\Hostx86\\x86"); + String lib = build_context.metrics.arch == TargetArch_amd64 + ? str_lit("lib\\x64") + : str_lit("lib\\x86"); + + isize lo = {0}; + isize hi = {0}; + for (isize c = 0; c < path.len; c += 1) { + if (path[c] != ';') { + continue; + } + + hi = c; + String dir = substring(path, lo, hi); + defer (lo = hi + 1); + + String end = dir[dir.len - 1] == '\\' + ? substring(dir, 0, dir.len - 1) + : substring(dir, 0, dir.len); + + // check if cl.exe and link.exe exist in this folder + String cl = mc_concat(end, str_lit("\\cl.exe")); + String link = mc_concat(end, str_lit("\\link.exe")); + defer (mc_free(cl)); + defer (mc_free(link)); + + if (!string_ends_with(end, exe) || !gb_file_exists((char *)cl.text) || !gb_file_exists((char *)link.text)) { + continue; + } + + String root = substring(end, 0, end.len - exe.len); + result->vs_exe_path = mc_concat(end, str_lit("\\")); + result->vs_library_path = mc_concat(root, lib, str_lit("\\")); + + vs_found = true; + } + } + } + + return sdk_found && vs_found; } - -bool find_visual_studio_by_fighting_through_microsoft_craziness(Find_Result *result) { - // The name of this procedure is kind of cryptic. Its purpose is - // to fight through Microsoft craziness. The things that the fine - // Visual Studio team want you to do, JUST TO FIND A SINGLE FOLDER - // THAT EVERYONE NEEDS TO FIND, are ridiculous garbage. - - // For earlier versions of Visual Studio, you'd find this information in the registry, - // similarly to the Windows Kits above. But no, now it's the future, so to ask the - // question "Where is the Visual Studio folder?" you have to do a bunch of COM object - // instantiation, enumeration, and querying. (For extra bonus points, try doing this in - // a new, underdeveloped programming language where you don't have COM routines up - // and running yet. So fun.) - // - // If all this COM object instantiation, enumeration, and querying doesn't give us - // a useful result, we drop back to the registry-checking method. - - - auto rc = CoInitialize(NULL); - // "Subsequent valid calls return false." So ignore false. - if (rc != S_OK) return false; - - GUID my_uid = {0x42843719, 0xDB4C, 0x46C2, {0x8E, 0x7C, 0x64, 0xF1, 0x81, 0x6E, 0xFD, 0x5B}}; - GUID CLSID_SetupConfiguration = {0x177F0C4A, 0x1CD3, 0x4DE7, {0xA3, 0x2C, 0x71, 0xDB, 0xBB, 0x9F, 0xA3, 0x6D}}; - - ISetupConfiguration *config = NULL; - HRESULT hr = 0; - hr = CoCreateInstance(CLSID_SetupConfiguration, NULL, CLSCTX_INPROC_SERVER, my_uid, (void **)&config); - if (hr == 0) { - defer (config->Release()); - - IEnumSetupInstances *instances = NULL; - hr = config->EnumInstances(&instances); - if (hr != 0) return false; - if (!instances) return false; - defer (instances->Release()); - - for (;;) { - ULONG found = 0; - ISetupInstance *instance = NULL; - auto hr = instances->Next(1, &instance, &found); - if (hr != S_OK) break; - - defer (instance->Release()); - - BSTR bstr_inst_path; - hr = instance->GetInstallationPath(&bstr_inst_path); - if (hr != S_OK) continue; - defer (SysFreeString(bstr_inst_path)); - - auto tools_filename = concat(bstr_inst_path, L"\\VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt"); - defer (free(tools_filename)); - - FILE *f = nullptr; - auto open_result = _wfopen_s(&f, tools_filename, L"rt"); - if (open_result != 0) continue; - if (!f) continue; - defer (fclose(f)); - - LARGE_INTEGER tools_file_size; - auto file_handle = (HANDLE)_get_osfhandle(_fileno(f)); - BOOL success = GetFileSizeEx(file_handle, &tools_file_size); - if (!success) continue; - - auto version_bytes = (tools_file_size.QuadPart + 1) * 2; // Warning: This multiplication by 2 presumes there is no variable-length encoding in the wchars (wacky characters in the file could betray this expectation). - if (version_bytes > 0x7FFFFFFF) continue; // Avoid overflow. - - wchar_t *version = (wchar_t *)calloc(1, (usize)version_bytes); - defer (free(version)); - - auto read_result = fgetws(version, (int)version_bytes, f); - if (!read_result) continue; - - auto version_tail = wcschr(version, '\n'); - if (version_tail) *version_tail = 0; // Stomp the data, because nobody cares about it. - - wchar_t *library_path = nullptr; - if (build_context.metrics.arch == TargetArch_amd64) { - library_path = concat(bstr_inst_path, L"\\VC\\Tools\\MSVC\\", version, L"\\lib\\x64\\"); - } else if (build_context.metrics.arch == TargetArch_i386) { - library_path = concat(bstr_inst_path, L"\\VC\\Tools\\MSVC\\", version, L"\\lib\\x86\\"); - } else { - continue; - } - - auto library_file = concat(library_path, L"vcruntime.lib"); // @Speed: Could have library_path point to this string, with a smaller count, to save on memory flailing! - - if (os_file_exists(library_file)) { - wchar_t *link_exe_path = nullptr; - if (build_context.metrics.arch == TargetArch_amd64) { - link_exe_path = concat(bstr_inst_path, L"\\VC\\Tools\\MSVC\\", version, L"\\bin\\Hostx64\\x64\\"); - } else if (build_context.metrics.arch == TargetArch_i386) { - link_exe_path = concat(bstr_inst_path, L"\\VC\\Tools\\MSVC\\", version, L"\\bin\\Hostx86\\x86\\"); - } else { - continue; - } - - - result->vs_exe_path = link_exe_path; - result->vs_library_path = library_path; - return true; - } - - /* - Ryan Saunderson said: - "Clang uses the 'SetupInstance->GetInstallationVersion' / ISetupHelper->ParseVersion to find the newest version - and then reads the tools file to define the tools path - which is definitely better than what i did." - - So... @Incomplete: Should probably pick the newest version... - */ - } - } - - // If we get here, we didn't find Visual Studio 2017. Try earlier versions. - { - HKEY vs7_key; - rc = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7", 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &vs7_key); - if (rc != S_OK) return false; - defer (RegCloseKey(vs7_key)); - - // Hardcoded search for 4 prior Visual Studio versions. Is there something better to do here? - wchar_t const *versions[] = { L"14.0", L"13.0", L"12.0", L"11.0", L"10.0", L"9.0", }; - const int NUM_VERSIONS = sizeof(versions) / sizeof(versions[0]); - - for (int i = 0; i < NUM_VERSIONS; i++) { - wchar_t const *v = versions[i]; - - DWORD dw_type; - DWORD cb_data; - - auto rc = RegQueryValueExW(vs7_key, v, NULL, &dw_type, NULL, &cb_data); - if ((rc == ERROR_FILE_NOT_FOUND) || (dw_type != REG_SZ)) { - continue; - } - - auto buffer = (wchar_t *)calloc(1, cb_data); - if (!buffer) return false; - defer (free(buffer)); - - rc = RegQueryValueExW(vs7_key, v, NULL, NULL, (LPBYTE)buffer, &cb_data); - if (rc != 0) continue; - - // @Robustness: Do the zero-termination thing suggested in the RegQueryValue docs? - - wchar_t *lib_path = nullptr; - - if (build_context.metrics.arch == TargetArch_amd64) { - lib_path = concat(buffer, L"VC\\Lib\\amd64\\"); - } else if (build_context.metrics.arch == TargetArch_i386) { - lib_path = concat(buffer, L"VC\\Lib\\"); - } else { - continue; - } - - // Check to see whether a vcruntime.lib actually exists here. - auto vcruntime_filename = concat(lib_path, L"vcruntime.lib"); - defer (free(vcruntime_filename)); - - if (os_file_exists(vcruntime_filename)) { - if (build_context.metrics.arch == TargetArch_amd64) { - result->vs_exe_path = concat(buffer, L"VC\\bin\\"); - } else if (build_context.metrics.arch == TargetArch_i386) { - // result->vs_exe_path = concat(buffer, L"VC\\bin\\amd64_x86\\"); - result->vs_exe_path = concat(buffer, L"VC\\bin\\x86_amd64\\"); - } else { - continue; - } - - result->vs_library_path = lib_path; - return true; - } - - free(lib_path); - } - - // If we get here, we failed to find anything. - } - - return false; -} - - -Find_Result find_visual_studio_and_windows_sdk() { - Find_Result result = {}; - - find_windows_kit_root(&result); - - - if (result.windows_sdk_root) { - if (build_context.metrics.arch == TargetArch_amd64) { - result.windows_sdk_um_library_path = concat(result.windows_sdk_root, L"um\\x64\\"); - result.windows_sdk_ucrt_library_path = concat(result.windows_sdk_root, L"ucrt\\x64\\"); - } else if (build_context.metrics.arch == TargetArch_i386) { - result.windows_sdk_um_library_path = concat(result.windows_sdk_root, L"um\\x86\\"); - result.windows_sdk_ucrt_library_path = concat(result.windows_sdk_root, L"ucrt\\x86\\"); - } - } - - bool ok = find_visual_studio_by_fighting_through_microsoft_craziness(&result); - - if (!ok) { - result.vs_exe_path = concat(L"", L""); - result.vs_library_path = concat(L"", L""); - } - - return result; -} - -String mc_wstring_to_string(wchar_t const *str) { - return string16_to_string(mc_allocator, make_string16_c(str)); -} - - Find_Result_Utf8 find_visual_studio_and_windows_sdk_utf8() { - Find_Result result = find_visual_studio_and_windows_sdk(); - defer (free_resources(&result)); + Find_Result_Utf8 r = {}; + find_windows_kit_root(&r); - Find_Result_Utf8 r = {}; - r.windows_sdk_version = result.windows_sdk_version; + if (r.windows_sdk_root.len > 0) { + if (build_context.metrics.arch == TargetArch_amd64) { + r.windows_sdk_um_library_path = mc_concat(r.windows_sdk_root, str_lit("um\\x64\\")); + r.windows_sdk_ucrt_library_path = mc_concat(r.windows_sdk_root, str_lit("ucrt\\x64\\")); + } else if (build_context.metrics.arch == TargetArch_i386) { + r.windows_sdk_um_library_path = mc_concat(r.windows_sdk_root, str_lit("um\\x86\\")); + r.windows_sdk_ucrt_library_path = mc_concat(r.windows_sdk_root, str_lit("ucrt\\x86\\")); + } + } - r.windows_sdk_root = mc_wstring_to_string(result.windows_sdk_root); - r.windows_sdk_um_library_path = mc_wstring_to_string(result.windows_sdk_um_library_path); - r.windows_sdk_ucrt_library_path = mc_wstring_to_string(result.windows_sdk_ucrt_library_path); - r.vs_exe_path = mc_wstring_to_string(result.vs_exe_path); - r.vs_library_path = mc_wstring_to_string(result.vs_library_path); + find_visual_studio_by_fighting_through_microsoft_craziness(&r); + + bool all_found = + r.windows_sdk_root.len > 0 && + r.windows_sdk_um_library_path.len > 0 && + r.windows_sdk_ucrt_library_path.len > 0 && + r.vs_exe_path.len > 0 && + r.vs_library_path.len > 0; + + if (!all_found) { + find_msvc_install_from_env_vars(&r); + } #if 0 - printf("windows_sdk_root: %.*s\n", LIT(r.windows_sdk_root)); - printf("windows_sdk_um_library_path: %.*s\n", LIT(r.windows_sdk_um_library_path)); - printf("windows_sdk_ucrt_library_path: %.*s\n", LIT(r.windows_sdk_ucrt_library_path)); - printf("vs_exe_path: %.*s\n", LIT(r.vs_exe_path)); - printf("vs_library_path: %.*s\n", LIT(r.vs_library_path)); + printf("windows_sdk_root: %.*s\n", LIT(r.windows_sdk_root)); + printf("windows_sdk_um_library_path: %.*s\n", LIT(r.windows_sdk_um_library_path)); + printf("windows_sdk_ucrt_library_path: %.*s\n", LIT(r.windows_sdk_ucrt_library_path)); + printf("vs_exe_path: %.*s\n", LIT(r.vs_exe_path)); + printf("vs_library_path: %.*s\n", LIT(r.vs_library_path)); - gb_exit(1); + gb_exit(1); #endif - return r; -} - + return r; +} \ No newline at end of file diff --git a/src/odin_compiler.natvis b/src/odin_compiler.natvis new file mode 100644 index 000000000..845eaf1c0 --- /dev/null +++ b/src/odin_compiler.natvis @@ -0,0 +1,28 @@ + + + + {text,[len]s8} + text,[len]s8 + + + {{ size={count} capacity={capacity} }} + + + count + data + + + + + {{ size={count} }} + + + count + data + + + + + Procedure {name} + + diff --git a/src/parser.cpp b/src/parser.cpp index 076c698ff..b62ec7a74 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1,5 +1,8 @@ #include "parser_pos.cpp" +// #undef at the bottom of this file +#define ALLOW_NEWLINE build_context.strict_style + Token token_end_of_line(AstFile *f, Token tok) { u8 const *start = f->tokenizer.start + tok.pos.offset; u8 const *s = start; @@ -57,6 +60,9 @@ isize ast_node_size(AstKind kind) { return align_formula_isize(gb_size_of(AstCommonStuff) + ast_variant_sizes[kind], gb_align_of(void *)); } + +gb_global std::atomic global_total_node_memory_allocated; + // NOTE(bill): And this below is why is I/we need a new language! Discriminated unions are a pain in C/C++ Ast *alloc_ast_node(AstFile *f, AstKind kind) { gbAllocator a = ast_allocator(f); @@ -66,6 +72,9 @@ Ast *alloc_ast_node(AstFile *f, AstKind kind) { Ast *node = cast(Ast *)gb_alloc(a, size); node->kind = kind; node->file_id = f ? f->id : 0; + + global_total_node_memory_allocated += size; + return node; } @@ -183,6 +192,11 @@ Ast *clone_ast(Ast *node) { n->FieldValue.value = clone_ast(n->FieldValue.value); break; + case Ast_EnumFieldValue: + n->EnumFieldValue.name = clone_ast(n->EnumFieldValue.name); + n->EnumFieldValue.value = clone_ast(n->EnumFieldValue.value); + break; + case Ast_TernaryIfExpr: n->TernaryIfExpr.x = clone_ast(n->TernaryIfExpr.x); n->TernaryIfExpr.cond = clone_ast(n->TernaryIfExpr.cond); @@ -349,6 +363,7 @@ Ast *clone_ast(Ast *node) { case Ast_ArrayType: n->ArrayType.count = clone_ast(n->ArrayType.count); n->ArrayType.elem = clone_ast(n->ArrayType.elem); + n->ArrayType.tag = clone_ast(n->ArrayType.tag); break; case Ast_DynamicArrayType: n->DynamicArrayType.elem = clone_ast(n->DynamicArrayType.elem); @@ -693,6 +708,16 @@ Ast *ast_field_value(AstFile *f, Ast *field, Ast *value, Token eq) { return result; } + +Ast *ast_enum_field_value(AstFile *f, Ast *name, Ast *value, CommentGroup *docs, CommentGroup *comment) { + Ast *result = alloc_ast_node(f, Ast_EnumFieldValue); + result->EnumFieldValue.name = name; + result->EnumFieldValue.value = value; + result->EnumFieldValue.docs = docs; + result->EnumFieldValue.comment = comment; + return result; +} + Ast *ast_compound_lit(AstFile *f, Ast *type, Array const &elems, Token open, Token close) { Ast *result = alloc_ast_node(f, Ast_CompoundLit); result->CompoundLit.type = type; @@ -1050,15 +1075,14 @@ Ast *ast_struct_type(AstFile *f, Token token, Slice fields, isize field_c } -Ast *ast_union_type(AstFile *f, Token token, Array const &variants, Ast *polymorphic_params, Ast *align, bool no_nil, bool maybe, +Ast *ast_union_type(AstFile *f, Token token, Array const &variants, Ast *polymorphic_params, Ast *align, UnionTypeKind kind, Token where_token, Array const &where_clauses) { Ast *result = alloc_ast_node(f, Ast_UnionType); result->UnionType.token = token; result->UnionType.variants = slice_from_array(variants); result->UnionType.polymorphic_params = polymorphic_params; result->UnionType.align = align; - result->UnionType.no_nil = no_nil; - result->UnionType.maybe = maybe; + result->UnionType.kind = kind; result->UnionType.where_token = where_token; result->UnionType.where_clauses = slice_from_array(where_clauses); return result; @@ -1140,11 +1164,10 @@ Ast *ast_package_decl(AstFile *f, Token token, Token name, CommentGroup *docs, C return result; } -Ast *ast_import_decl(AstFile *f, Token token, bool is_using, Token relpath, Token import_name, +Ast *ast_import_decl(AstFile *f, Token token, Token relpath, Token import_name, CommentGroup *docs, CommentGroup *comment) { Ast *result = alloc_ast_node(f, Ast_ImportDecl); result->ImportDecl.token = token; - result->ImportDecl.is_using = is_using; result->ImportDecl.relpath = relpath; result->ImportDecl.import_name = import_name; result->ImportDecl.docs = docs; @@ -1234,7 +1257,7 @@ CommentGroup *consume_comment_group(AstFile *f, isize n, isize *end_line_) { return comments; } -void comsume_comment_groups(AstFile *f, Token prev) { +void consume_comment_groups(AstFile *f, Token prev) { if (f->curr_token.kind == Token_Comment) { CommentGroup *comment = nullptr; isize end_line = 0; @@ -1258,15 +1281,10 @@ void comsume_comment_groups(AstFile *f, Token prev) { } } -bool ignore_newlines(AstFile *f) { - if (f->allow_newline) { - return f->expr_level > 0; - } - return f->expr_level >= 0; +gb_inline bool ignore_newlines(AstFile *f) { + return f->expr_level > 0; } - - Token advance_token(AstFile *f) { f->lead_comment = nullptr; f->line_comment = nullptr; @@ -1278,7 +1296,7 @@ Token advance_token(AstFile *f) { if (ok) { switch (f->curr_token.kind) { case Token_Comment: - comsume_comment_groups(f, prev); + consume_comment_groups(f, prev); break; case Token_Semicolon: if (ignore_newlines(f) && f->curr_token.string == "\n") { @@ -1408,6 +1426,7 @@ Token expect_operator(AstFile *f) { LIT(p)); } if (f->curr_token.kind == Token_Ellipsis) { + syntax_warning(f->curr_token, "'..' for ranges has now be deprecated, prefer '..='"); f->tokens[f->curr_token_index].flags |= TokenFlag_Replace; } @@ -1440,7 +1459,11 @@ Token expect_closing_brace_of_field_list(AstFile *f) { if (allow_token(f, Token_CloseBrace)) { return token; } - if (allow_token(f, Token_Semicolon)) { + bool ok = true; + if (!f->allow_newline) { + ok = !skip_possible_newline(f); + } + if (ok && allow_token(f, Token_Semicolon)) { String p = token_to_string(token); syntax_error(token_end_of_line(f, f->prev_token), "Expected a comma, got a %.*s", LIT(p)); } @@ -1514,13 +1537,15 @@ void fix_advance_to_next_stmt(AstFile *f) { } } -Token expect_closing(AstFile *f, TokenKind kind, String context) { +Token expect_closing(AstFile *f, TokenKind kind, String const &context) { if (f->curr_token.kind != kind && f->curr_token.kind == Token_Semicolon && - f->curr_token.string == "\n") { - Token tok = f->prev_token; - tok.pos.column += cast(i32)tok.string.len; - syntax_error(tok, "Missing ',' before newline in %.*s", LIT(context)); + (f->curr_token.string == "\n" || f->curr_token.kind == Token_EOF)) { + if (f->allow_newline) { + Token tok = f->prev_token; + tok.pos.column += cast(i32)tok.string.len; + syntax_error(tok, "Missing ',' before newline in %.*s", LIT(context)); + } advance_token(f); } return expect_token(f, kind); @@ -1539,6 +1564,7 @@ void assign_removal_flag_to_semicolon(AstFile *f) { switch (curr_token->kind) { case Token_CloseBrace: case Token_CloseParen: + case Token_EOF: ok = true; break; } @@ -1555,7 +1581,7 @@ void assign_removal_flag_to_semicolon(AstFile *f) { } } -void expect_semicolon(AstFile *f, Ast *s) { +void expect_semicolon(AstFile *f) { Token prev_token = {}; if (allow_token(f, Token_Semicolon)) { @@ -1580,17 +1606,17 @@ void expect_semicolon(AstFile *f, Ast *s) { if (f->curr_token.kind == Token_EOF) { return; } - - if (s != nullptr) { - return; - } switch (f->curr_token.kind) { case Token_EOF: return; } - String p = token_to_string(f->curr_token); - syntax_error(prev_token, "Expected ';', got %.*s", LIT(p)); - fix_advance_to_next_stmt(f); + + if (f->curr_token.pos.line == f->prev_token.pos.line) { + String p = token_to_string(f->curr_token); + prev_token.pos = token_pos_end(prev_token); + syntax_error(prev_token, "Expected ';', got %.*s", LIT(p)); + fix_advance_to_next_stmt(f); + } } @@ -1599,6 +1625,7 @@ Ast * parse_proc_type(AstFile *f, Token proc_token); Array parse_stmt_list(AstFile *f); Ast * parse_stmt(AstFile *f); Ast * parse_body(AstFile *f); +Ast * parse_do_body(AstFile *f, Token const &token, char const *msg); Ast * parse_block_stmt(AstFile *f, b32 is_when); @@ -1682,13 +1709,53 @@ Array parse_element_list(AstFile *f) { array_add(&elems, elem); - if (!allow_token(f, Token_Comma)) { + if (!allow_field_separator(f)) { break; } } return elems; } +CommentGroup *consume_line_comment(AstFile *f) { + CommentGroup *comment = f->line_comment; + if (f->line_comment == f->lead_comment) { + f->lead_comment = nullptr; + } + f->line_comment = nullptr; + return comment; + +} + +Array parse_enum_field_list(AstFile *f) { + auto elems = array_make(heap_allocator()); + + while (f->curr_token.kind != Token_CloseBrace && + f->curr_token.kind != Token_EOF) { + CommentGroup *docs = f->lead_comment; + CommentGroup *comment = nullptr; + Ast *name = parse_value(f); + Ast *value = nullptr; + if (f->curr_token.kind == Token_Eq) { + Token eq = expect_token(f, Token_Eq); + value = parse_value(f); + } + + comment = consume_line_comment(f); + + Ast *elem = ast_enum_field_value(f, name, value, docs, comment); + array_add(&elems, elem); + + if (!allow_field_separator(f)) { + break; + } + + if (!elem->EnumFieldValue.comment) { + elem->EnumFieldValue.comment = consume_line_comment(f); + } + } + + return elems; +} Ast *parse_literal_value(AstFile *f, Ast *type) { Array elems = {}; @@ -1719,14 +1786,14 @@ Ast *parse_value(AstFile *f) { Ast *parse_type_or_ident(AstFile *f); -void check_proc_add_tag(AstFile *f, Ast *tag_expr, u64 *tags, ProcTag tag, String tag_name) { +void check_proc_add_tag(AstFile *f, Ast *tag_expr, u64 *tags, ProcTag tag, String const &tag_name) { if (*tags & tag) { syntax_error(tag_expr, "Procedure tag already used: %.*s", LIT(tag_name)); } *tags |= tag; } -bool is_foreign_name_valid(String name) { +bool is_foreign_name_valid(String const &name) { if (name.len == 0) { return false; } @@ -1793,6 +1860,8 @@ void parse_proc_tags(AstFile *f, u64 *tags) { ELSE_IF_ADD_TAG(require_results) ELSE_IF_ADD_TAG(bounds_check) ELSE_IF_ADD_TAG(no_bounds_check) + ELSE_IF_ADD_TAG(type_assert) + ELSE_IF_ADD_TAG(no_type_assert) else { syntax_error(tag_expr, "Unknown procedure type tag #%.*s", LIT(tag_name)); } @@ -1803,6 +1872,10 @@ void parse_proc_tags(AstFile *f, u64 *tags) { if ((*tags & ProcTag_bounds_check) && (*tags & ProcTag_no_bounds_check)) { syntax_error(f->curr_token, "You cannot apply both #bounds_check and #no_bounds_check to a procedure"); } + + if ((*tags & ProcTag_type_assert) && (*tags & ProcTag_no_type_assert)) { + syntax_error(f->curr_token, "You cannot apply both #type_assert and #no_type_assert to a procedure"); + } } @@ -1816,7 +1889,7 @@ Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKi Ast *parse_unary_expr(AstFile *f, bool lhs); -Ast *convert_stmt_to_expr(AstFile *f, Ast *statement, String kind) { +Ast *convert_stmt_to_expr(AstFile *f, Ast *statement, String const &kind) { if (statement == nullptr) { return nullptr; } @@ -1950,11 +2023,23 @@ Ast *parse_check_directive_for_statement(Ast *s, Token const &tag_token, u16 sta syntax_error(tag_token, "#bounds_check and #no_bounds_check cannot be applied together"); } break; + case StateFlag_type_assert: + if ((s->state_flags & StateFlag_no_type_assert) != 0) { + syntax_error(tag_token, "#type_assert and #no_type_assert cannot be applied together"); + } + break; + case StateFlag_no_type_assert: + if ((s->state_flags & StateFlag_type_assert) != 0) { + syntax_error(tag_token, "#type_assert and #no_type_assert cannot be applied together"); + } + break; } switch (state_flag) { case StateFlag_bounds_check: case StateFlag_no_bounds_check: + case StateFlag_type_assert: + case StateFlag_no_type_assert: switch (s->kind) { case Ast_BlockStmt: case Ast_IfStmt: @@ -1985,6 +2070,20 @@ Ast *parse_check_directive_for_statement(Ast *s, Token const &tag_token, u16 sta return s; } +Array parse_union_variant_list(AstFile *f) { + auto variants = array_make(heap_allocator()); + while (f->curr_token.kind != Token_CloseBrace && + f->curr_token.kind != Token_EOF) { + Ast *type = parse_type(f); + if (type->kind != Ast_BadExpr) { + array_add(&variants, type); + } + if (!allow_field_separator(f)) { + break; + } + } + return variants; +} Ast *parse_operand(AstFile *f, bool lhs) { Ast *operand = nullptr; // Operand @@ -2013,6 +2112,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { case Token_OpenParen: { bool allow_newline; + isize prev_expr_level; Token open, close; // NOTE(bill): Skip the Paren Expression open = expect_token(f, Token_OpenParen); @@ -2022,16 +2122,18 @@ Ast *parse_operand(AstFile *f, bool lhs) { return ast_bad_expr(f, open, close); } + prev_expr_level = f->expr_level; allow_newline = f->allow_newline; if (f->expr_level < 0) { f->allow_newline = false; } - f->expr_level++; + // NOTE(bill): enforce it to >0 + f->expr_level = gb_max(f->expr_level, 0)+1; operand = parse_expr(f, false); - f->expr_level--; f->allow_newline = allow_newline; + f->expr_level = prev_expr_level; close = expect_token(f, Token_CloseParen); return ast_paren_expr(f, operand, open, close); @@ -2049,7 +2151,18 @@ Ast *parse_operand(AstFile *f, bool lhs) { Token name = expect_token(f, Token_Ident); if (name.string == "type") { return ast_helper_type(f, token, parse_type(f)); - } else if (name.string == "soa" || name.string == "simd") { + } else if ( name.string == "simd") { + Ast *tag = ast_basic_directive(f, token, name); + Ast *original_type = parse_type(f); + Ast *type = unparen_expr(original_type); + switch (type->kind) { + case Ast_ArrayType: type->ArrayType.tag = tag; break; + default: + syntax_error(type, "Expected a fixed array type after #%.*s, got %.*s", LIT(name.string), LIT(ast_strings[type->kind])); + break; + } + return original_type; + } else if (name.string == "soa") { Ast *tag = ast_basic_directive(f, token, name); Ast *original_type = parse_type(f); Ast *type = unparen_expr(original_type); @@ -2062,6 +2175,22 @@ Ast *parse_operand(AstFile *f, bool lhs) { } return original_type; } else if (name.string == "partial") { + Ast *tag = ast_basic_directive(f, token, name); + Ast *original_expr = parse_expr(f, lhs); + Ast *expr = unparen_expr(original_expr); + switch (expr->kind) { + case Ast_ArrayType: + syntax_error(expr, "#partial has been replaced with #sparse for non-contiguous enumerated array types"); + break; + case Ast_CompoundLit: + expr->CompoundLit.tag = tag; + break; + default: + syntax_error(expr, "Expected a compound literal after #%.*s, got %.*s", LIT(name.string), LIT(ast_strings[expr->kind])); + break; + } + return original_expr; + } else if (name.string == "sparse") { Ast *tag = ast_basic_directive(f, token, name); Ast *original_type = parse_type(f); Ast *type = unparen_expr(original_type); @@ -2078,6 +2207,12 @@ Ast *parse_operand(AstFile *f, bool lhs) { } else if (name.string == "no_bounds_check") { Ast *operand = parse_expr(f, lhs); return parse_check_directive_for_statement(operand, name, StateFlag_no_bounds_check); + } else if (name.string == "type_assert") { + Ast *operand = parse_expr(f, lhs); + return parse_check_directive_for_statement(operand, name, StateFlag_type_assert); + } else if (name.string == "no_type_assert") { + Ast *operand = parse_expr(f, lhs); + return parse_check_directive_for_statement(operand, name, StateFlag_no_type_assert); } else if (name.string == "relative") { Ast *tag = ast_basic_directive(f, token, name); tag = parse_call_expr(f, tag); @@ -2104,7 +2239,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { Ast *elem = parse_expr(f, false); array_add(&args, elem); - if (!allow_token(f, Token_Comma)) { + if (!allow_field_separator(f)) { break; } } @@ -2174,6 +2309,12 @@ Ast *parse_operand(AstFile *f, bool lhs) { if (tags & ProcTag_bounds_check) { body->state_flags |= StateFlag_bounds_check; } + if (tags & ProcTag_no_type_assert) { + body->state_flags |= StateFlag_no_type_assert; + } + if (tags & ProcTag_type_assert) { + body->state_flags |= StateFlag_type_assert; + } return ast_proc_lit(f, type, body, tags, where_token, where_clauses); } else if (allow_token(f, Token_do)) { @@ -2183,11 +2324,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { body = convert_stmt_to_body(f, parse_stmt(f)); f->curr_proc = curr_proc; - if (build_context.disallow_do) { - syntax_error(body, "'do' has been disallowed"); - } else if (!ast_on_same_line(type, body)) { - syntax_error(body, "The body of a 'do' must be on the same line as the signature"); - } + syntax_error(body, "'do' for procedure bodies is not allowed, prefer {}"); return ast_proc_lit(f, type, body, tags, where_token, where_clauses); } @@ -2299,7 +2436,9 @@ Ast *parse_operand(AstFile *f, bool lhs) { check_polymorphic_params_for_type(f, polymorphic_params, token); } - isize prev_level = f->expr_level; + isize prev_level; + + prev_level = f->expr_level; f->expr_level = -1; while (allow_token(f, Token_Hash)) { @@ -2338,7 +2477,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { if (f->curr_token.kind == Token_where) { where_token = expect_token(f, Token_where); - isize prev_level = f->expr_level; + prev_level = f->expr_level; f->expr_level = -1; where_clauses = parse_rhs_expr_list(f); f->expr_level = prev_level; @@ -2362,11 +2501,13 @@ Ast *parse_operand(AstFile *f, bool lhs) { case Token_union: { Token token = expect_token(f, Token_union); - auto variants = array_make(heap_allocator()); Ast *polymorphic_params = nullptr; Ast *align = nullptr; bool no_nil = false; bool maybe = false; + bool shared_nil = false; + + UnionTypeKind union_kind = UnionType_Normal; Token start_token = f->curr_token; @@ -2393,6 +2534,11 @@ Ast *parse_operand(AstFile *f, bool lhs) { syntax_error(tag, "Duplicate union tag '#%.*s'", LIT(tag.string)); } no_nil = true; + } else if (tag.string == "shared_nil") { + if (shared_nil) { + syntax_error(tag, "Duplicate union tag '#%.*s'", LIT(tag.string)); + } + shared_nil = true; } else if (tag.string == "maybe") { if (maybe) { syntax_error(tag, "Duplicate union tag '#%.*s'", LIT(tag.string)); @@ -2405,6 +2551,22 @@ Ast *parse_operand(AstFile *f, bool lhs) { if (no_nil && maybe) { syntax_error(f->curr_token, "#maybe and #no_nil cannot be applied together"); } + if (no_nil && shared_nil) { + syntax_error(f->curr_token, "#shared_nil and #no_nil cannot be applied together"); + } + if (shared_nil && maybe) { + syntax_error(f->curr_token, "#maybe and #shared_nil cannot be applied together"); + } + + + if (maybe) { + union_kind = UnionType_maybe; + syntax_error(f->curr_token, "#maybe functionality has now been merged with standard 'union' functionality"); + } else if (no_nil) { + union_kind = UnionType_no_nil; + } else if (shared_nil) { + union_kind = UnionType_shared_nil; + } skip_possible_newline_for_literal(f); @@ -2422,21 +2584,10 @@ Ast *parse_operand(AstFile *f, bool lhs) { skip_possible_newline_for_literal(f); Token open = expect_token_after(f, Token_OpenBrace, "union"); - - while (f->curr_token.kind != Token_CloseBrace && - f->curr_token.kind != Token_EOF) { - Ast *type = parse_type(f); - if (type->kind != Ast_BadExpr) { - array_add(&variants, type); - } - if (!allow_token(f, Token_Comma)) { - break; - } - } - + auto variants = parse_union_variant_list(f); Token close = expect_closing_brace_of_field_list(f); - return ast_union_type(f, token, variants, polymorphic_params, align, no_nil, maybe, where_token, where_clauses); + return ast_union_type(f, token, variants, polymorphic_params, align, union_kind, where_token, where_clauses); } break; case Token_enum: { @@ -2449,7 +2600,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { skip_possible_newline_for_literal(f); Token open = expect_token(f, Token_OpenBrace); - Array values = parse_element_list(f); + Array values = parse_enum_field_list(f); Token close = expect_closing_brace_of_field_list(f); return ast_enum_type(f, token, base_type, values); @@ -2591,7 +2742,7 @@ Ast *parse_call_expr(AstFile *f, Ast *operand) { isize prev_expr_level = f->expr_level; bool prev_allow_newline = f->allow_newline; f->expr_level = 0; - f->allow_newline = true; + f->allow_newline = ALLOW_NEWLINE; open_paren = expect_token(f, Token_OpenParen); @@ -2625,7 +2776,7 @@ Ast *parse_call_expr(AstFile *f, Ast *operand) { } array_add(&args, arg); - if (!allow_token(f, Token_Comma)) { + if (!allow_field_separator(f)) { break; } } @@ -2908,64 +3059,62 @@ i32 token_precedence(AstFile *f, TokenKind t) { Ast *parse_binary_expr(AstFile *f, bool lhs, i32 prec_in) { Ast *expr = parse_unary_expr(f, lhs); - for (i32 prec = token_precedence(f, f->curr_token.kind); prec >= prec_in; prec--) { - for (;;) { - Token op = f->curr_token; - i32 op_prec = token_precedence(f, op.kind); - if (op_prec != prec) { - // NOTE(bill): This will also catch operators that are not valid "binary" operators - break; + for (;;) { + Token op = f->curr_token; + i32 op_prec = token_precedence(f, op.kind); + if (op_prec < prec_in) { + // NOTE(bill): This will also catch operators that are not valid "binary" operators + break; + } + Token prev = f->prev_token; + switch (op.kind) { + case Token_if: + case Token_when: + if (prev.pos.line < op.pos.line) { + // NOTE(bill): Check to see if the `if` or `when` is on the same line of the `lhs` condition + goto loop_end; } - Token prev = f->prev_token; + break; + } + expect_operator(f); // NOTE(bill): error checks too + + if (op.kind == Token_Question) { + Ast *cond = expr; + // Token_Question + Ast *x = parse_expr(f, lhs); + Token token_c = expect_token(f, Token_Colon); + Ast *y = parse_expr(f, lhs); + expr = ast_ternary_if_expr(f, x, cond, y); + } else if (op.kind == Token_if || op.kind == Token_when) { + Ast *x = expr; + Ast *cond = parse_expr(f, lhs); + Token tok_else = expect_token(f, Token_else); + Ast *y = parse_expr(f, lhs); + switch (op.kind) { case Token_if: + expr = ast_ternary_if_expr(f, x, cond, y); + break; case Token_when: - if (prev.pos.line < op.pos.line) { - // NOTE(bill): Check to see if the `if` or `when` is on the same line of the `lhs` condition - goto loop_end; - } + expr = ast_ternary_when_expr(f, x, cond, y); break; } - expect_operator(f); // NOTE(bill): error checks too - - if (op.kind == Token_Question) { - Ast *cond = expr; - // Token_Question - Ast *x = parse_expr(f, lhs); - Token token_c = expect_token(f, Token_Colon); - Ast *y = parse_expr(f, lhs); - expr = ast_ternary_if_expr(f, x, cond, y); - } else if (op.kind == Token_if || op.kind == Token_when) { - Ast *x = expr; - Ast *cond = parse_expr(f, lhs); - Token tok_else = expect_token(f, Token_else); - Ast *y = parse_expr(f, lhs); - - switch (op.kind) { - case Token_if: - expr = ast_ternary_if_expr(f, x, cond, y); - break; - case Token_when: - expr = ast_ternary_when_expr(f, x, cond, y); - break; - } - } else { - Ast *right = parse_binary_expr(f, false, prec+1); - if (right == nullptr) { - syntax_error(op, "Expected expression on the right-hand side of the binary operator '%.*s'", LIT(op.string)); - } - if (op.kind == Token_or_else) { - // NOTE(bill): easier to handle its logic different with its own AST kind - expr = ast_or_else_expr(f, expr, op, right); - } else { - expr = ast_binary_expr(f, op, expr, right); - } + } else { + Ast *right = parse_binary_expr(f, false, op_prec+1); + if (right == nullptr) { + syntax_error(op, "Expected expression on the right-hand side of the binary operator '%.*s'", LIT(op.string)); + } + if (op.kind == Token_or_else) { + // NOTE(bill): easier to handle its logic different with its own AST kind + expr = ast_or_else_expr(f, expr, op, right); + } else { + expr = ast_binary_expr(f, op, expr, right); } - - lhs = false; } - loop_end:; + + lhs = false; } + loop_end:; return expr; } @@ -2976,7 +3125,7 @@ Ast *parse_expr(AstFile *f, bool lhs) { Array parse_expr_list(AstFile *f, bool lhs) { bool allow_newline = f->allow_newline; - f->allow_newline = true; + f->allow_newline = ALLOW_NEWLINE; auto list = array_make(heap_allocator()); for (;;) { @@ -3075,7 +3224,7 @@ Ast *parse_foreign_block(AstFile *f, Token token) { Ast *body = ast_block_stmt(f, decls, open, close); Ast *decl = ast_foreign_block_decl(f, token, foreign_library, body, docs); - expect_semicolon(f, decl); + expect_semicolon(f); return decl; } @@ -3121,15 +3270,11 @@ Ast *parse_value_decl(AstFile *f, Array names, CommentGroup *docs) { } if (f->expr_level >= 0) { - Ast *end = nullptr; - if (!is_mutable && values.count > 0) { - end = values[values.count-1]; - } if (f->curr_token.kind == Token_CloseBrace && f->curr_token.pos.line == f->prev_token.pos.line) { } else { - expect_semicolon(f, end); + expect_semicolon(f); } } @@ -3300,7 +3445,7 @@ Ast *parse_results(AstFile *f, bool *diverging) { } -ProcCallingConvention string_to_calling_convention(String s) { +ProcCallingConvention string_to_calling_convention(String const &s) { if (s == "odin") return ProcCC_Odin; if (s == "contextless") return ProcCC_Contextless; if (s == "cdecl") return ProcCC_CDecl; @@ -3311,12 +3456,18 @@ ProcCallingConvention string_to_calling_convention(String s) { if (s == "fast") return ProcCC_FastCall; if (s == "none") return ProcCC_None; if (s == "naked") return ProcCC_Naked; + + if (s == "win64") return ProcCC_Win64; + if (s == "sysv") return ProcCC_SysV; + if (s == "system") { if (build_context.metrics.os == TargetOs_windows) { return ProcCC_StdCall; } return ProcCC_CDecl; } + + return ProcCC_Invalid; } @@ -3403,12 +3554,14 @@ enum FieldPrefixKind : i32 { FieldPrefix_Unknown = -1, FieldPrefix_Invalid = 0, - FieldPrefix_using, + FieldPrefix_using, // implies #subtype FieldPrefix_const, FieldPrefix_no_alias, FieldPrefix_c_vararg, FieldPrefix_auto_cast, FieldPrefix_any_int, + FieldPrefix_subtype, // does not imply `using` semantics + FieldPrefix_by_ptr, }; struct ParseFieldPrefixMapping { @@ -3425,6 +3578,8 @@ gb_global ParseFieldPrefixMapping parse_field_prefix_mappings[] = { {str_lit("c_vararg"), Token_Hash, FieldPrefix_c_vararg, FieldFlag_c_vararg}, {str_lit("const"), Token_Hash, FieldPrefix_const, FieldFlag_const}, {str_lit("any_int"), Token_Hash, FieldPrefix_any_int, FieldFlag_any_int}, + {str_lit("subtype"), Token_Hash, FieldPrefix_subtype, FieldFlag_subtype}, + {str_lit("by_ptr"), Token_Hash, FieldPrefix_by_ptr, FieldFlag_by_ptr}, }; @@ -3573,12 +3728,12 @@ Array convert_to_ident_list(AstFile *f, Array list, bool ign } -bool parse_expect_field_separator(AstFile *f, Ast *param) { +bool allow_field_separator(AstFile *f) { Token token = f->curr_token; if (allow_token(f, Token_Comma)) { return true; } - if (token.kind == Token_Semicolon) { + if (ALLOW_NEWLINE && token.kind == Token_Semicolon) { String p = token_to_string(token); syntax_error(token_end_of_line(f, f->prev_token), "Expected a comma, got a %.*s", LIT(p)); advance_token(f); @@ -3630,6 +3785,10 @@ bool check_procedure_name_list(Array const &names) { } Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind follow, bool allow_default_parameters, bool allow_typeid_token) { + bool prev_allow_newline = f->allow_newline; + defer (f->allow_newline = prev_allow_newline); + f->allow_newline = ALLOW_NEWLINE; + Token start_token = f->curr_token; CommentGroup *docs = f->lead_comment; @@ -3659,7 +3818,7 @@ Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKi } AstAndFlags naf = {param, flags}; array_add(&list, naf); - if (!allow_token(f, Token_Comma)) { + if (!allow_field_separator(f)) { break; } } @@ -3729,13 +3888,14 @@ Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKi } } - parse_expect_field_separator(f, type); + allow_field_separator(f); Ast *param = ast_field(f, names, type, default_value, set_flags, tag, docs, f->line_comment); array_add(¶ms, param); while (f->curr_token.kind != follow && - f->curr_token.kind != Token_EOF) { + f->curr_token.kind != Token_EOF && + f->curr_token.kind != Token_Semicolon) { CommentGroup *docs = f->lead_comment; u32 set_flags = parse_field_prefixes(f); Token tag = {}; @@ -3763,7 +3923,7 @@ Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKi default_value = parse_expr(f, false); if (!allow_default_parameters) { syntax_error(f->curr_token, "Default parameters are only allowed for procedures"); - default_value = nullptr; + default_value = nullptr; } } @@ -3791,7 +3951,7 @@ Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKi } - bool ok = parse_expect_field_separator(f, param); + bool ok = allow_field_separator(f); Ast *param = ast_field(f, names, type, default_value, set_flags, tag, docs, f->line_comment); array_add(¶ms, param); @@ -3848,17 +4008,41 @@ Ast *parse_body(AstFile *f) { Array stmts = {}; Token open, close; isize prev_expr_level = f->expr_level; + bool prev_allow_newline = f->allow_newline; // NOTE(bill): The body may be within an expression so reset to zero f->expr_level = 0; + // f->allow_newline = false; open = expect_token(f, Token_OpenBrace); stmts = parse_stmt_list(f); close = expect_token(f, Token_CloseBrace); f->expr_level = prev_expr_level; + f->allow_newline = prev_allow_newline; return ast_block_stmt(f, stmts, open, close); } +Ast *parse_do_body(AstFile *f, Token const &token, char const *msg) { + Token open, close; + isize prev_expr_level = f->expr_level; + bool prev_allow_newline = f->allow_newline; + + // NOTE(bill): The body may be within an expression so reset to zero + f->expr_level = 0; + f->allow_newline = false; + + Ast *body = convert_stmt_to_body(f, parse_stmt(f)); + if (build_context.disallow_do) { + syntax_error(body, "'do' has been disallowed"); + } else if (token.pos.file_id != 0 && !ast_on_same_line(token, body)) { + syntax_error(body, "The body of a 'do' must be on the same line as %s", msg); + } + f->expr_level = prev_expr_level; + f->allow_newline = prev_allow_newline; + + return body; +} + bool parse_control_statement_semicolon_separator(AstFile *f) { Token tok = peek_token(f); if (tok.kind != Token_OpenBrace) { @@ -3908,12 +4092,7 @@ Ast *parse_if_stmt(AstFile *f) { } if (allow_token(f, Token_do)) { - body = convert_stmt_to_body(f, parse_stmt(f)); - if (build_context.disallow_do) { - syntax_error(body, "'do' has been disallowed"); - } else if (!ast_on_same_line(cond, body)) { - syntax_error(body, "The body of a 'do' be on the same line as if condition"); - } + body = parse_do_body(f, cond ? ast_token(cond) : token, "the if statement"); } else { body = parse_block_stmt(f, false); } @@ -3928,15 +4107,10 @@ Ast *parse_if_stmt(AstFile *f) { case Token_OpenBrace: else_stmt = parse_block_stmt(f, false); break; - case Token_do: { + case Token_do: expect_token(f, Token_do); - else_stmt = convert_stmt_to_body(f, parse_stmt(f)); - if (build_context.disallow_do) { - syntax_error(else_stmt, "'do' has been disallowed"); - } else if (!ast_on_same_line(else_token, else_stmt)) { - syntax_error(else_stmt, "The body of a 'do' be on the same line as 'else'"); - } - } break; + else_stmt = parse_do_body(f, else_token, "'else'"); + break; default: syntax_error(f->curr_token, "Expected if statement block statement"); else_stmt = ast_bad_stmt(f, f->curr_token, f->tokens[f->curr_token_index+1]); @@ -3965,12 +4139,7 @@ Ast *parse_when_stmt(AstFile *f) { } if (allow_token(f, Token_do)) { - body = convert_stmt_to_body(f, parse_stmt(f)); - if (build_context.disallow_do) { - syntax_error(body, "'do' has been disallowed"); - } else if (!ast_on_same_line(cond, body)) { - syntax_error(body, "The body of a 'do' be on the same line as when statement"); - } + body = parse_do_body(f, cond ? ast_token(cond) : token, "then when statement"); } else { body = parse_block_stmt(f, true); } @@ -3987,12 +4156,7 @@ Ast *parse_when_stmt(AstFile *f) { break; case Token_do: { expect_token(f, Token_do); - else_stmt = convert_stmt_to_body(f, parse_stmt(f)); - if (build_context.disallow_do) { - syntax_error(else_stmt, "'do' has been disallowed"); - } else if (!ast_on_same_line(else_token, else_stmt)) { - syntax_error(else_stmt, "The body of a 'do' be on the same line as 'else'"); - } + else_stmt = parse_do_body(f, else_token, "'else'"); } break; default: syntax_error(f->curr_token, "Expected when statement block statement"); @@ -4029,11 +4193,7 @@ Ast *parse_return_stmt(AstFile *f) { advance_token(f); } - Ast *end = nullptr; - if (results.count > 0) { - end = results[results.count-1]; - } - expect_semicolon(f, end); + expect_semicolon(f); return ast_return_stmt(f, token, results); } @@ -4066,12 +4226,7 @@ Ast *parse_for_stmt(AstFile *f) { f->allow_range = prev_allow_range; if (allow_token(f, Token_do)) { - body = convert_stmt_to_body(f, parse_stmt(f)); - if (build_context.disallow_do) { - syntax_error(body, "'do' has been disallowed"); - } else if (!ast_on_same_line(token, body)) { - syntax_error(body, "The body of a 'do' be on the same line as the 'for' token"); - } + body = parse_do_body(f, token, "the for statement"); } else { body = parse_block_stmt(f, false); } @@ -4112,12 +4267,7 @@ Ast *parse_for_stmt(AstFile *f) { if (allow_token(f, Token_do)) { - body = convert_stmt_to_body(f, parse_stmt(f)); - if (build_context.disallow_do) { - syntax_error(body, "'do' has been disallowed"); - } else if (!ast_on_same_line(token, body)) { - syntax_error(body, "The body of a 'do' be on the same line as the 'for' token"); - } + body = parse_do_body(f, token, "the for statement"); } else { body = parse_block_stmt(f, false); } @@ -4254,7 +4404,6 @@ Ast *parse_import_decl(AstFile *f, ImportDeclKind kind) { CommentGroup *docs = f->lead_comment; Token token = expect_token(f, Token_import); Token import_name = {}; - bool is_using = kind != ImportDecl_Standard; switch (f->curr_token.kind) { case Token_Ident: @@ -4265,26 +4414,22 @@ Ast *parse_import_decl(AstFile *f, ImportDeclKind kind) { break; } - if (!is_using && is_blank_ident(import_name)) { - syntax_error(import_name, "Illegal import name: '_'"); - } - Token file_path = expect_token_after(f, Token_String, "import"); Ast *s = nullptr; if (f->curr_proc != nullptr) { - syntax_error(import_name, "You cannot use 'import' within a procedure. This must be done at the file scope"); + syntax_error(import_name, "Cannot use 'import' within a procedure. This must be done at the file scope"); s = ast_bad_decl(f, import_name, file_path); } else { - s = ast_import_decl(f, token, is_using, file_path, import_name, docs, f->line_comment); + s = ast_import_decl(f, token, file_path, import_name, docs, f->line_comment); array_add(&f->imports, s); } - if (is_using) { + if (kind != ImportDecl_Standard) { syntax_error(import_name, "'using import' is not allowed, please use the import name explicitly"); } - expect_semicolon(f, s); + expect_semicolon(f); return s; } @@ -4321,11 +4466,11 @@ Ast *parse_foreign_decl(AstFile *f) { Token path = expect_token(f, Token_String); array_add(&filepaths, path); - if (!allow_token(f, Token_Comma)) { + if (!allow_field_separator(f)) { break; } } - expect_token(f, Token_CloseBrace); + expect_closing_brace_of_field_list(f); } else { filepaths = array_make(heap_allocator(), 0, 1); Token path = expect_token(f, Token_String); @@ -4342,7 +4487,7 @@ Ast *parse_foreign_decl(AstFile *f) { } else { s = ast_foreign_import_decl(f, token, filepaths, lib_name, docs, f->line_comment); } - expect_semicolon(f, s); + expect_semicolon(f); return s; } } @@ -4378,7 +4523,7 @@ Ast *parse_attribute(AstFile *f, Token token, TokenKind open_kind, TokenKind clo array_add(&elems, elem); - if (!allow_token(f, Token_Comma)) { + if (!allow_field_separator(f)) { break; } } @@ -4443,12 +4588,7 @@ Ast *parse_unrolled_for_loop(AstFile *f, Token unroll_token) { f->allow_range = prev_allow_range; if (allow_token(f, Token_do)) { - body = convert_stmt_to_body(f, parse_stmt(f)); - if (build_context.disallow_do) { - syntax_error(body, "'do' has been disallowed"); - } else if (!ast_on_same_line(for_token, body)) { - syntax_error(body, "The body of a 'do' be on the same line as the 'for' token"); - } + body = parse_do_body(f, for_token, "the for statement"); } else { body = parse_block_stmt(f, false); } @@ -4481,7 +4621,7 @@ Ast *parse_stmt(AstFile *f) { case Token_Not: case Token_And: s = parse_simple_stmt(f, StmtAllowFlag_Label); - expect_semicolon(f, s); + expect_semicolon(f); return s; @@ -4509,7 +4649,7 @@ Ast *parse_stmt(AstFile *f) { label = parse_ident(f); } s = ast_branch_stmt(f, token, label); - expect_semicolon(f, s); + expect_semicolon(f); return s; } @@ -4524,12 +4664,12 @@ Ast *parse_stmt(AstFile *f) { Array list = parse_lhs_expr_list(f); if (list.count == 0) { syntax_error(token, "Illegal use of 'using' statement"); - expect_semicolon(f, nullptr); + expect_semicolon(f); return ast_bad_stmt(f, token, f->curr_token); } if (f->curr_token.kind != Token_Colon) { - expect_semicolon(f, list[list.count-1]); + expect_semicolon(f); return ast_using_stmt(f, token, list); } expect_token_after(f, Token_Colon, "identifier list"); @@ -4561,6 +4701,12 @@ Ast *parse_stmt(AstFile *f) { } else if (tag == "no_bounds_check") { s = parse_stmt(f); return parse_check_directive_for_statement(s, name, StateFlag_no_bounds_check); + } else if (tag == "type_assert") { + s = parse_stmt(f); + return parse_check_directive_for_statement(s, name, StateFlag_type_assert); + } else if (tag == "no_type_assert") { + s = parse_stmt(f); + return parse_check_directive_for_statement(s, name, StateFlag_no_type_assert); } else if (tag == "partial") { s = parse_stmt(f); switch (s->kind) { @@ -4580,13 +4726,13 @@ Ast *parse_stmt(AstFile *f) { } else if (tag == "assert" || tag == "panic") { Ast *t = ast_basic_directive(f, hash_token, name); Ast *stmt = ast_expr_stmt(f, parse_call_expr(f, t)); - expect_semicolon(f, stmt); + expect_semicolon(f); return stmt; } else if (name.string == "force_inline" || name.string == "force_no_inline") { Ast *expr = parse_force_inlining_operand(f, name); Ast *stmt = ast_expr_stmt(f, expr); - expect_semicolon(f, stmt); + expect_semicolon(f); return stmt; } else if (tag == "unroll") { return parse_unrolled_for_loop(f, name); @@ -4608,7 +4754,7 @@ Ast *parse_stmt(AstFile *f) { case Token_Semicolon: s = ast_empty_stmt(f, token); - expect_semicolon(f, nullptr); + expect_semicolon(f); return s; } @@ -4626,7 +4772,7 @@ Ast *parse_stmt(AstFile *f) { return parse_block_stmt(f, true); case Token_do: { expect_token(f, Token_do); - Ast *stmt = convert_stmt_to_body(f, parse_stmt(f)); + Ast *stmt = parse_do_body(f, {}, "the for statement"); if (build_context.disallow_do) { syntax_error(stmt, "'do' has been disallowed"); } @@ -4665,7 +4811,7 @@ Array parse_stmt_list(AstFile *f) { } -ParseFileError init_ast_file(AstFile *f, String fullpath, TokenPos *err_pos) { +ParseFileError init_ast_file(AstFile *f, String const &fullpath, TokenPos *err_pos) { GB_ASSERT(f != nullptr); f->fullpath = string_trim_whitespace(fullpath); // Just in case set_file_path_string(f->id, fullpath); @@ -4736,12 +4882,6 @@ ParseFileError init_ast_file(AstFile *f, String fullpath, TokenPos *err_pos) { f->prev_token = f->tokens[f->prev_token_index]; f->curr_token = f->tokens[f->curr_token_index]; - isize const page_size = 4*1024; - isize block_size = 2*f->tokens.count*gb_size_of(Ast); - block_size = ((block_size + page_size-1)/page_size) * page_size; - block_size = gb_clamp(block_size, page_size, DEFAULT_MINIMUM_BLOCK_SIZE); - f->arena.minimum_block_size = block_size; - array_init(&f->comments, heap_allocator(), 0, 0); array_init(&f->imports, heap_allocator(), 0, 0); @@ -4966,7 +5106,7 @@ gb_global Rune illegal_import_runes[] = { '|', ',', '<', '>', '?', }; -bool is_import_path_valid(String path) { +bool is_import_path_valid(String const &path) { if (path.len > 0) { u8 *start = path.text; u8 *end = path.text + path.len; @@ -4998,7 +5138,7 @@ bool is_import_path_valid(String path) { return false; } -bool is_build_flag_path_valid(String path) { +bool is_build_flag_path_valid(String const &path) { if (path.len > 0) { u8 *start = path.text; u8 *end = path.text + path.len; @@ -5050,7 +5190,7 @@ bool is_package_name_reserved(String const &name) { } -bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node, String base_dir, String original_string, String *path) { +bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node, String base_dir, String const &original_string, String *path) { GB_ASSERT(path != nullptr); // NOTE(bill): if file_mutex == nullptr, this means that the code is used within the semantics stage @@ -5164,9 +5304,9 @@ bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node, String bas -void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Slice &decls); +void parse_setup_file_decls(Parser *p, AstFile *f, String const &base_dir, Slice &decls); -void parse_setup_file_when_stmt(Parser *p, AstFile *f, String base_dir, AstWhenStmt *ws) { +void parse_setup_file_when_stmt(Parser *p, AstFile *f, String const &base_dir, AstWhenStmt *ws) { if (ws->body != nullptr) { auto stmts = ws->body->BlockStmt.stmts; parse_setup_file_decls(p, f, base_dir, stmts); @@ -5185,7 +5325,7 @@ void parse_setup_file_when_stmt(Parser *p, AstFile *f, String base_dir, AstWhenS } } -void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Slice &decls) { +void parse_setup_file_decls(Parser *p, AstFile *f, String const &base_dir, Slice &decls) { for_array(i, decls) { Ast *node = decls[i]; if (!is_ast_decl(node) && @@ -5398,7 +5538,7 @@ bool parse_file(Parser *p, AstFile *f) { String filepath = f->tokenizer.fullpath; String base_dir = dir_from_path(filepath); if (f->curr_token.kind == Token_Comment) { - comsume_comment_groups(f, f->prev_token); + consume_comment_groups(f, f->prev_token); } CommentGroup *docs = f->lead_comment; @@ -5444,8 +5584,17 @@ bool parse_file(Parser *p, AstFile *f) { if (!parse_build_tag(tok, lc)) { return false; } - } else if (lc == "+private") { - f->flags |= AstFile_IsPrivate; + } else if (string_starts_with(lc, str_lit("+private"))) { + f->flags |= AstFile_IsPrivatePkg; + String command = string_trim_starts_with(lc, str_lit("+private ")); + command = string_trim_whitespace(command); + if (lc == "+private") { + f->flags |= AstFile_IsPrivatePkg; + } else if (command == "package") { + f->flags |= AstFile_IsPrivatePkg; + } else if (command == "file") { + f->flags |= AstFile_IsPrivateFile; + } } else if (lc == "+lazy") { if (build_context.ignore_lazy) { // Ignore @@ -5463,7 +5612,7 @@ bool parse_file(Parser *p, AstFile *f) { } Ast *pd = ast_package_decl(f, f->package_token, package_name, docs, f->line_comment); - expect_semicolon(f, pd); + expect_semicolon(f); f->pkg_decl = pd; if (f->error_count == 0) { @@ -5597,8 +5746,24 @@ ParseFileError parse_packages(Parser *p, String init_filename) { error_line("Expected either a directory or a .odin file, got '%.*s'\n", LIT(init_filename)); return ParseFile_WrongExtension; } + } else if (init_fullpath.len != 0) { + String path = init_fullpath; + if (path[path.len-1] == '/') { + path.len -= 1; + } + if ((build_context.command_kind & Command__does_build) && + build_context.build_mode == BuildMode_Executable) { + String short_path = filename_from_path(path); + char *cpath = alloc_cstring(heap_allocator(), short_path); + defer (gb_free(heap_allocator(), cpath)); + + if (gb_file_exists(cpath)) { + error_line("Please specify the executable name with -out: as a directory exists with the same name in the current working directory"); + return ParseFile_DirectoryAlreadyExists; + } + } } - + { // Add these packages serially and then process them parallel mutex_lock(&p->wait_mutex); @@ -5660,3 +5825,5 @@ ParseFileError parse_packages(Parser *p, String init_filename) { } + +#undef ALLOW_NEWLINE diff --git a/src/parser.hpp b/src/parser.hpp index b83822cbf..3126e0a02 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -46,6 +46,7 @@ enum ParseFileError { ParseFile_InvalidToken, ParseFile_GeneralError, ParseFile_FileTooLarge, + ParseFile_DirectoryAlreadyExists, ParseFile_Count, }; @@ -78,9 +79,11 @@ struct ImportedFile { }; enum AstFileFlag : u32 { - AstFile_IsPrivate = 1<<0, - AstFile_IsTest = 1<<1, - AstFile_IsLazy = 1<<2, + AstFile_IsPrivatePkg = 1<<0, + AstFile_IsPrivateFile = 1<<1, + + AstFile_IsTest = 1<<3, + AstFile_IsLazy = 1<<4, }; enum AstDelayQueueKind { @@ -95,8 +98,6 @@ struct AstFile { AstPackage * pkg; Scope * scope; - Arena arena; - Ast * pkg_decl; String fullpath; Tokenizer tokenizer; @@ -226,6 +227,8 @@ enum ProcInlining { enum ProcTag { ProcTag_bounds_check = 1<<0, ProcTag_no_bounds_check = 1<<1, + ProcTag_type_assert = 1<<2, + ProcTag_no_type_assert = 1<<3, ProcTag_require_results = 1<<4, ProcTag_optional_ok = 1<<5, @@ -245,12 +248,30 @@ enum ProcCallingConvention : i32 { ProcCC_InlineAsm = 8, + ProcCC_Win64 = 9, + ProcCC_SysV = 10, + + ProcCC_MAX, ProcCC_ForeignBlockDefault = -1, }; +char const *proc_calling_convention_strings[ProcCC_MAX] = { + "", + "odin", + "contextless", + "cdecl", + "stdcall", + "fastcall", + "none", + "naked", + "inlineasm", + "win64", + "sysv", +}; + ProcCallingConvention default_calling_convention(void) { return ProcCC_Odin; } @@ -258,6 +279,10 @@ ProcCallingConvention default_calling_convention(void) { enum StateFlag : u8 { StateFlag_bounds_check = 1<<0, StateFlag_no_bounds_check = 1<<1, + StateFlag_type_assert = 1<<2, + StateFlag_no_type_assert = 1<<3, + + StateFlag_SelectorCallExpr = 1<<6, StateFlag_BeenHandled = 1<<7, }; @@ -276,14 +301,16 @@ enum FieldFlag : u32 { FieldFlag_auto_cast = 1<<4, FieldFlag_const = 1<<5, FieldFlag_any_int = 1<<6, + FieldFlag_subtype = 1<<7, + FieldFlag_by_ptr = 1<<8, // Internal use by the parser only FieldFlag_Tags = 1<<10, FieldFlag_Results = 1<<16, // Parameter List Restrictions - FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_auto_cast|FieldFlag_const|FieldFlag_any_int, - FieldFlag_Struct = FieldFlag_using|FieldFlag_Tags, + FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_auto_cast|FieldFlag_const|FieldFlag_any_int|FieldFlag_by_ptr, + FieldFlag_Struct = FieldFlag_using|FieldFlag_subtype|FieldFlag_Tags, }; enum StmtAllowFlag { @@ -306,6 +333,13 @@ char const *inline_asm_dialect_strings[InlineAsmDialect_COUNT] = { "intel", }; +enum UnionTypeKind : u8 { + UnionType_Normal = 0, + UnionType_maybe = 1, // removed + UnionType_no_nil = 2, + UnionType_shared_nil = 3, +}; + #define AST_KINDS \ AST_KIND(Ident, "identifier", struct { \ Token token; \ @@ -344,6 +378,7 @@ char const *inline_asm_dialect_strings[InlineAsmDialect_COUNT] = { Slice elems; \ Token open, close; \ i64 max_count; \ + Ast *tag; \ }) \ AST_KIND(_ExprBegin, "", bool) \ AST_KIND(BadExpr, "bad expression", struct { Token begin, end; }) \ @@ -379,10 +414,15 @@ AST_KIND(_ExprBegin, "", bool) \ Token ellipsis; \ ProcInlining inlining; \ bool optional_ok_one; \ - i32 builtin_id; \ - void *sce_temp_data; \ + bool was_selector; \ }) \ AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \ + AST_KIND(EnumFieldValue, "enum field value", struct { \ + Ast *name; \ + Ast *value; \ + CommentGroup *docs; \ + CommentGroup *comment; \ + }) \ AST_KIND(TernaryIfExpr, "ternary if expression", struct { Ast *x, *cond, *y; }) \ AST_KIND(TernaryWhenExpr, "ternary when expression", struct { Ast *x, *cond, *y; }) \ AST_KIND(OrElseExpr, "or_else expression", struct { Ast *x; Token token; Ast *y; }) \ @@ -547,7 +587,6 @@ AST_KIND(_DeclBegin, "", bool) \ Token import_name; \ CommentGroup *docs; \ CommentGroup *comment; \ - bool is_using; \ }) \ AST_KIND(ForeignImportDecl, "foreign import declaration", struct { \ Token token; \ @@ -647,8 +686,7 @@ AST_KIND(_TypeBegin, "", bool) \ Slice variants; \ Ast *polymorphic_params; \ Ast * align; \ - bool maybe; \ - bool no_nil; \ + UnionTypeKind kind; \ Token where_token; \ Slice where_clauses; \ }) \ @@ -769,13 +807,14 @@ gb_inline bool is_ast_when_stmt(Ast *node) { return node->kind == Ast_WhenStmt; } -gb_global gb_thread_local Arena global_ast_arena = {}; +gb_global gb_thread_local Arena global_thread_local_ast_arena = {}; gbAllocator ast_allocator(AstFile *f) { - Arena *arena = f ? &f->arena : &global_ast_arena; + Arena *arena = &global_thread_local_ast_arena; return arena_allocator(arena); } Ast *alloc_ast_node(AstFile *f, AstKind kind); gbString expr_to_string(Ast *expression); +bool allow_field_separator(AstFile *f); \ No newline at end of file diff --git a/src/parser_pos.cpp b/src/parser_pos.cpp index 6ef0db215..54c3ec1f1 100644 --- a/src/parser_pos.cpp +++ b/src/parser_pos.cpp @@ -39,6 +39,7 @@ Token ast_token(Ast *node) { case Ast_SliceExpr: return node->SliceExpr.open; case Ast_Ellipsis: return node->Ellipsis.token; case Ast_FieldValue: return node->FieldValue.eq; + case Ast_EnumFieldValue: return ast_token(node->EnumFieldValue.name); case Ast_DerefExpr: return node->DerefExpr.op; case Ast_TernaryIfExpr: return ast_token(node->TernaryIfExpr.x); case Ast_TernaryWhenExpr: return ast_token(node->TernaryWhenExpr.x); @@ -178,6 +179,11 @@ Token ast_end_token(Ast *node) { } return node->Ellipsis.token; case Ast_FieldValue: return ast_end_token(node->FieldValue.value); + case Ast_EnumFieldValue: + if (node->EnumFieldValue.value) { + return ast_end_token(node->EnumFieldValue.value); + } + return ast_end_token(node->EnumFieldValue.name); case Ast_DerefExpr: return node->DerefExpr.op; case Ast_TernaryIfExpr: return ast_end_token(node->TernaryIfExpr.y); case Ast_TernaryWhenExpr: return ast_end_token(node->TernaryWhenExpr.y); diff --git a/src/path.cpp b/src/path.cpp new file mode 100644 index 000000000..6f83c39ea --- /dev/null +++ b/src/path.cpp @@ -0,0 +1,394 @@ +/* + Path handling utilities. +*/ +String remove_extension_from_path(String const &s) { + for (isize i = s.len-1; i >= 0; i--) { + if (s[i] == '.') { + return substring(s, 0, i); + } + } + return s; +} + +String remove_directory_from_path(String const &s) { + isize len = 0; + for (isize i = s.len-1; i >= 0; i--) { + if (s[i] == '/' || + s[i] == '\\') { + break; + } + len += 1; + } + return substring(s, s.len-len, s.len); +} + +bool path_is_directory(String path); + +String directory_from_path(String const &s) { + if (path_is_directory(s)) { + return s; + } + + isize i = s.len-1; + for (; i >= 0; i--) { + if (s[i] == '/' || + s[i] == '\\') { + break; + } + } + if (i >= 0) { + return substring(s, 0, i); + } + return substring(s, 0, 0); +} + +#if defined(GB_SYSTEM_WINDOWS) + bool path_is_directory(String path) { + gbAllocator a = heap_allocator(); + String16 wstr = string_to_string16(a, path); + defer (gb_free(a, wstr.text)); + + i32 attribs = GetFileAttributesW(wstr.text); + if (attribs < 0) return false; + + return (attribs & FILE_ATTRIBUTE_DIRECTORY) != 0; + } + +#else + bool path_is_directory(String path) { + gbAllocator a = heap_allocator(); + char *copy = cast(char *)copy_string(a, path).text; + defer (gb_free(a, copy)); + + struct stat s; + if (stat(copy, &s) == 0) { + return (s.st_mode & S_IFDIR) != 0; + } + return false; + } +#endif + + +String path_to_full_path(gbAllocator a, String path) { + gbAllocator ha = heap_allocator(); + char *path_c = gb_alloc_str_len(ha, cast(char *)path.text, path.len); + defer (gb_free(ha, path_c)); + + char *fullpath = gb_path_get_full_name(a, path_c); + String res = string_trim_whitespace(make_string_c(fullpath)); +#if defined(GB_SYSTEM_WINDOWS) + for (isize i = 0; i < res.len; i++) { + if (res.text[i] == '\\') { + res.text[i] = '/'; + } + } +#endif + return copy_string(a, res); +} + +struct Path { + String basename; + String name; + String ext; +}; + +// NOTE(Jeroen): Naively turns a Path into a string. +String path_to_string(gbAllocator a, Path path) { + if (path.basename.len + path.name.len + path.ext.len == 0) { + return make_string(nullptr, 0); + } + + isize len = path.basename.len + 1 + path.name.len + 1; + if (path.ext.len > 0) { + len += path.ext.len + 1; + } + + u8 *str = gb_alloc_array(a, u8, len); + + isize i = 0; + gb_memmove(str+i, path.basename.text, path.basename.len); i += path.basename.len; + gb_memmove(str+i, "/", 1); i += 1; + gb_memmove(str+i, path.name.text, path.name.len); i += path.name.len; + if (path.ext.len > 0) { + gb_memmove(str+i, ".", 1); i += 1; + gb_memmove(str+i, path.ext.text, path.ext.len); i += path.ext.len; + } + str[i] = 0; + + String res = make_string(str, i); + res = string_trim_whitespace(res); + return res; +} + +// NOTE(Jeroen): Naively turns a Path into a string, then normalizes it using `path_to_full_path`. +String path_to_full_path(gbAllocator a, Path path) { + String temp = path_to_string(heap_allocator(), path); + defer (gb_free(heap_allocator(), temp.text)); + + return path_to_full_path(a, temp); +} + +// NOTE(Jeroen): Takes a path like "odin" or "W:\Odin", turns it into a full path, +// and then breaks it into its components to make a Path. +Path path_from_string(gbAllocator a, String const &path) { + Path res = {}; + + if (path.len == 0) return res; + + String fullpath = path_to_full_path(a, path); + defer (gb_free(heap_allocator(), fullpath.text)); + + res.basename = directory_from_path(fullpath); + res.basename = copy_string(a, res.basename); + + if (path_is_directory(fullpath)) { + // It's a directory. We don't need to tinker with the name and extension. + // It could have a superfluous trailing `/`. Remove it if so. + if (res.basename.len > 0 && res.basename.text[res.basename.len - 1] == '/') { + res.basename.len--; + } + return res; + } + + isize name_start = (res.basename.len > 0) ? res.basename.len + 1 : res.basename.len; + res.name = substring(fullpath, name_start, fullpath.len); + res.name = remove_extension_from_path(res.name); + res.name = copy_string(a, res.name); + + res.ext = path_extension(fullpath, false); // false says not to include the dot. + res.ext = copy_string(a, res.ext); + return res; +} + +// NOTE(Jeroen): Takes a path String and returns the last path element. +String last_path_element(String const &path) { + isize count = 0; + u8 * start = (u8 *)(&path.text[path.len - 1]); + for (isize length = path.len; length > 0 && path.text[length - 1] != '/'; length--) { + count++; + start--; + } + if (count > 0) { + start++; // Advance past the `/` and return the substring. + String res = make_string(start, count); + return res; + } + // Must be a root path like `/` or `C:/`, return empty String. + return STR_LIT(""); +} + +bool path_is_directory(Path path) { + String path_string = path_to_full_path(heap_allocator(), path); + defer (gb_free(heap_allocator(), path_string.text)); + + return path_is_directory(path_string); +} + +struct FileInfo { + String name; + String fullpath; + i64 size; + bool is_dir; +}; + +enum ReadDirectoryError { + ReadDirectory_None, + + ReadDirectory_InvalidPath, + ReadDirectory_NotExists, + ReadDirectory_Permission, + ReadDirectory_NotDir, + ReadDirectory_Empty, + ReadDirectory_Unknown, + + ReadDirectory_COUNT, +}; + +i64 get_file_size(String path) { + char *c_str = alloc_cstring(heap_allocator(), path); + defer (gb_free(heap_allocator(), c_str)); + + gbFile f = {}; + gbFileError err = gb_file_open(&f, c_str); + defer (gb_file_close(&f)); + if (err != gbFileError_None) { + return -1; + } + return gb_file_size(&f); +} + + +#if defined(GB_SYSTEM_WINDOWS) +ReadDirectoryError read_directory(String path, Array *fi) { + GB_ASSERT(fi != nullptr); + + gbAllocator a = heap_allocator(); + + while (path.len > 0) { + Rune end = path[path.len-1]; + if (end == '/') { + path.len -= 1; + } else if (end == '\\') { + path.len -= 1; + } else { + break; + } + } + + if (path.len == 0) { + return ReadDirectory_InvalidPath; + } + { + char *c_str = alloc_cstring(a, path); + defer (gb_free(a, c_str)); + + gbFile f = {}; + gbFileError file_err = gb_file_open(&f, c_str); + defer (gb_file_close(&f)); + + switch (file_err) { + case gbFileError_Invalid: return ReadDirectory_InvalidPath; + case gbFileError_NotExists: return ReadDirectory_NotExists; + // case gbFileError_Permission: return ReadDirectory_Permission; + } + } + + if (!path_is_directory(path)) { + return ReadDirectory_NotDir; + } + + + char *new_path = gb_alloc_array(a, char, path.len+3); + defer (gb_free(a, new_path)); + + gb_memmove(new_path, path.text, path.len); + gb_memmove(new_path+path.len, "/*", 2); + new_path[path.len+2] = 0; + + String np = make_string(cast(u8 *)new_path, path.len+2); + String16 wstr = string_to_string16(a, np); + defer (gb_free(a, wstr.text)); + + WIN32_FIND_DATAW file_data = {}; + HANDLE find_file = FindFirstFileW(wstr.text, &file_data); + if (find_file == INVALID_HANDLE_VALUE) { + return ReadDirectory_Unknown; + } + defer (FindClose(find_file)); + + array_init(fi, a, 0, 100); + + do { + wchar_t *filename_w = file_data.cFileName; + i64 size = cast(i64)file_data.nFileSizeLow; + size |= (cast(i64)file_data.nFileSizeHigh) << 32; + String name = string16_to_string(a, make_string16_c(filename_w)); + if (name == "." || name == "..") { + gb_free(a, name.text); + continue; + } + + String filepath = {}; + filepath.len = path.len+1+name.len; + filepath.text = gb_alloc_array(a, u8, filepath.len+1); + defer (gb_free(a, filepath.text)); + gb_memmove(filepath.text, path.text, path.len); + gb_memmove(filepath.text+path.len, "/", 1); + gb_memmove(filepath.text+path.len+1, name.text, name.len); + + FileInfo info = {}; + info.name = name; + info.fullpath = path_to_full_path(a, filepath); + info.size = size; + info.is_dir = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; + array_add(fi, info); + } while (FindNextFileW(find_file, &file_data)); + + if (fi->count == 0) { + return ReadDirectory_Empty; + } + + return ReadDirectory_None; +} +#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD) + +#include + +ReadDirectoryError read_directory(String path, Array *fi) { + GB_ASSERT(fi != nullptr); + + gbAllocator a = heap_allocator(); + + char *c_path = alloc_cstring(a, path); + defer (gb_free(a, c_path)); + + DIR *dir = opendir(c_path); + if (!dir) { + switch (errno) { + case ENOENT: + return ReadDirectory_NotExists; + case EACCES: + return ReadDirectory_Permission; + case ENOTDIR: + return ReadDirectory_NotDir; + default: + // ENOMEM: out of memory + // EMFILE: per-process limit on open fds reached + // ENFILE: system-wide limit on total open files reached + return ReadDirectory_Unknown; + } + GB_PANIC("unreachable"); + } + + array_init(fi, a, 0, 100); + + for (;;) { + struct dirent *entry = readdir(dir); + if (entry == nullptr) { + break; + } + + String name = make_string_c(entry->d_name); + if (name == "." || name == "..") { + continue; + } + + String filepath = {}; + filepath.len = path.len+1+name.len; + filepath.text = gb_alloc_array(a, u8, filepath.len+1); + defer (gb_free(a, filepath.text)); + gb_memmove(filepath.text, path.text, path.len); + gb_memmove(filepath.text+path.len, "/", 1); + gb_memmove(filepath.text+path.len+1, name.text, name.len); + filepath.text[filepath.len] = 0; + + + struct stat dir_stat = {}; + + if (stat((char *)filepath.text, &dir_stat)) { + continue; + } + + if (S_ISDIR(dir_stat.st_mode)) { + continue; + } + + i64 size = dir_stat.st_size; + + FileInfo info = {}; + info.name = name; + info.fullpath = path_to_full_path(a, filepath); + info.size = size; + array_add(fi, info); + } + + if (fi->count == 0) { + return ReadDirectory_Empty; + } + + return ReadDirectory_None; +} +#else +#error Implement read_directory +#endif + diff --git a/src/ptr_set.cpp b/src/ptr_set.cpp index b45997916..ffe48d69a 100644 --- a/src/ptr_set.cpp +++ b/src/ptr_set.cpp @@ -13,7 +13,7 @@ struct PtrSet { template void ptr_set_init (PtrSet *s, gbAllocator a, isize capacity = 16); template void ptr_set_destroy(PtrSet *s); template T ptr_set_add (PtrSet *s, T ptr); -template bool ptr_set_update (PtrSet *s, T ptr); // returns true if it previously existsed +template bool ptr_set_update (PtrSet *s, T ptr); // returns true if it previously existed template bool ptr_set_exists (PtrSet *s, T ptr); template void ptr_set_remove (PtrSet *s, T ptr); template void ptr_set_clear (PtrSet *s); diff --git a/src/string.cpp b/src/string.cpp index 800378689..44eccd2d2 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -10,10 +10,6 @@ struct String { u8 * text; isize len; - // u8 &operator[](isize i) { - // GB_ASSERT_MSG(0 <= i && i < len, "[%td]", i); - // return text[i]; - // } u8 const &operator[](isize i) const { GB_ASSERT_MSG(0 <= i && i < len, "[%td]", i); return text[i]; @@ -33,10 +29,6 @@ struct String { struct String16 { wchar_t *text; isize len; - wchar_t &operator[](isize i) { - GB_ASSERT_MSG(0 <= i && i < len, "[%td]", i); - return text[i]; - } wchar_t const &operator[](isize i) const { GB_ASSERT_MSG(0 <= i && i < len, "[%td]", i); return text[i]; @@ -165,6 +157,15 @@ int string_compare(String const &x, String const &y) { return 0; } +isize string_index_byte(String const &s, u8 x) { + for (isize i = 0; i < s.len; i++) { + if (s.text[i] == x) { + return i; + } + } + return -1; +} + GB_COMPARE_PROC(string_cmp_proc) { String x = *(String *)a; String y = *(String *)b; @@ -195,8 +196,6 @@ template bool operator > (String const &a, char const (&b)[N]) { retu template bool operator <= (String const &a, char const (&b)[N]) { return str_le(a, make_string(cast(u8 *)b, N-1)); } template bool operator >= (String const &a, char const (&b)[N]) { return str_ge(a, make_string(cast(u8 *)b, N-1)); } - - gb_inline bool string_starts_with(String const &s, String const &prefix) { if (prefix.len > s.len) { return false; @@ -230,6 +229,16 @@ gb_inline bool string_ends_with(String const &s, u8 suffix) { return s[s.len-1] == suffix; } + + +gb_inline String string_trim_starts_with(String const &s, String const &prefix) { + if (string_starts_with(s, prefix)) { + return substring(s, prefix.len, s.len); + } + return s; +} + + gb_inline isize string_extension_position(String const &str) { isize dot_pos = -1; isize i = str.len; @@ -245,15 +254,14 @@ gb_inline isize string_extension_position(String const &str) { return dot_pos; } -String path_extension(String const &str) { +String path_extension(String const &str, bool include_dot = true) { isize pos = string_extension_position(str); if (pos < 0) { return make_string(nullptr, 0); } - return substring(str, pos, str.len); + return substring(str, include_dot ? pos : pos + 1, str.len); } - String string_trim_whitespace(String str) { while (str.len > 0 && rune_is_whitespace(str[str.len-1])) { str.len--; @@ -299,38 +307,6 @@ String filename_from_path(String s) { return make_string(nullptr, 0); } -String remove_extension_from_path(String const &s) { - for (isize i = s.len-1; i >= 0; i--) { - if (s[i] == '.') { - return substring(s, 0, i); - } - } - return s; -} - -String remove_directory_from_path(String const &s) { - isize len = 0; - for (isize i = s.len-1; i >= 0; i--) { - if (s[i] == '/' || - s[i] == '\\') { - break; - } - len += 1; - } - return substring(s, s.len-len, s.len); -} - -String directory_from_path(String const &s) { - isize i = s.len-1; - for (; i >= 0; i--) { - if (s[i] == '/' || - s[i] == '\\') { - break; - } - } - return substring(s, 0, i); -} - String concatenate_strings(gbAllocator a, String const &x, String const &y) { isize len = x.len+y.len; u8 *data = gb_alloc_array(a, u8, len+1); @@ -773,3 +749,34 @@ i32 unquote_string(gbAllocator a, String *s_, u8 quote=0, bool has_carriage_retu return 2; } + + +bool string_is_valid_identifier(String str) { + if (str.len <= 0) return false; + + isize rune_count = 0; + + isize w = 0; + isize offset = 0; + while (offset < str.len) { + Rune r = 0; + w = utf8_decode(str.text, str.len, &r); + if (r == GB_RUNE_INVALID) { + return false; + } + + if (rune_count == 0) { + if (!rune_is_letter(r)) { + return false; + } + } else { + if (!rune_is_letter(r) && !rune_is_digit(r)) { + return false; + } + } + rune_count += 1; + offset += w; + } + + return true; +} diff --git a/src/string_set.cpp b/src/string_set.cpp index e27145289..746ad9529 100644 --- a/src/string_set.cpp +++ b/src/string_set.cpp @@ -13,6 +13,7 @@ struct StringSet { void string_set_init (StringSet *s, gbAllocator a, isize capacity = 16); void string_set_destroy(StringSet *s); void string_set_add (StringSet *s, String const &str); +bool string_set_update (StringSet *s, String const &str); // returns true if it previously existed bool string_set_exists (StringSet *s, String const &str); void string_set_remove (StringSet *s, String const &str); void string_set_clear (StringSet *s); @@ -149,6 +150,34 @@ void string_set_add(StringSet *s, String const &str) { } } +bool string_set_update(StringSet *s, String const &str) { + bool exists = false; + MapIndex index; + MapFindResult fr; + StringHashKey key = string_hash_string(str); + if (s->hashes.count == 0) { + string_set_grow(s); + } + fr = string_set__find(s, key); + if (fr.entry_index != MAP_SENTINEL) { + index = fr.entry_index; + exists = true; + } else { + index = string_set__add_entry(s, key); + if (fr.entry_prev != MAP_SENTINEL) { + s->entries[fr.entry_prev].next = index; + } else { + s->hashes[fr.hash_index] = index; + } + } + s->entries[index].value = str; + + if (string_set__full(s)) { + string_set_grow(s); + } + return exists; +} + void string_set__erase(StringSet *s, MapFindResult fr) { MapFindResult last; diff --git a/src/threading.cpp b/src/threading.cpp index 50d0dfed1..63e3415b2 100644 --- a/src/threading.cpp +++ b/src/threading.cpp @@ -486,7 +486,7 @@ void thread_set_name(Thread *t, char const *name) { #elif defined(GB_SYSTEM_OSX) // TODO(bill): Test if this works pthread_setname_np(name); -#elif defined(GB_SYSTEM_FREEBSD) +#elif defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD) pthread_set_name_np(t->posix_handle, name); #else // TODO(bill): Test if this works diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 20815fd16..40bc5c220 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -201,14 +201,6 @@ struct TokenPos { i32 column; // starting at 1 }; -// temporary -char *token_pos_to_string(TokenPos const &pos) { - gbString s = gb_string_make_reserve(temporary_allocator(), 128); - String file = get_file_path_string(pos.file_id); - s = gb_string_append_fmt(s, "%.*s(%d:%d)", LIT(file), pos.line, pos.column); - return s; -} - i32 token_pos_cmp(TokenPos const &a, TokenPos const &b) { if (a.offset != b.offset) { return (a.offset < b.offset) ? -1 : +1; diff --git a/src/types.cpp b/src/types.cpp index 07951196a..5f112ce09 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -165,9 +165,8 @@ struct TypeUnion { i16 tag_size; bool is_polymorphic; - bool is_poly_specialized : 1; - bool no_nil : 1; - bool maybe : 1; + bool is_poly_specialized; + UnionTypeKind kind; }; struct TypeProc { @@ -186,7 +185,6 @@ struct TypeProc { bool c_vararg; bool is_polymorphic; bool is_poly_specialized; - bool has_proc_default_values; bool has_named_results; bool diverging; // no return bool return_by_pointer; @@ -221,6 +219,7 @@ struct TypeProc { ExactValue *max_value; \ i64 count; \ TokenKind op; \ + bool is_sparse; \ }) \ TYPE_KIND(Slice, struct { Type *elem; }) \ TYPE_KIND(DynamicArray, struct { Type *elem; }) \ @@ -262,6 +261,7 @@ struct TypeProc { TYPE_KIND(SimdVector, struct { \ i64 count; \ Type *elem; \ + Type *generic_count; \ }) \ TYPE_KIND(RelativePointer, struct { \ Type *pointer_type; \ @@ -362,6 +362,10 @@ enum TypeInfoFlag : u32 { enum : int { MATRIX_ELEMENT_COUNT_MIN = 1, MATRIX_ELEMENT_COUNT_MAX = 16, + MATRIX_ELEMENT_MAX_SIZE = MATRIX_ELEMENT_COUNT_MAX * (2 * 8), // complex128 + + SIMD_ELEMENT_COUNT_MIN = 1, + SIMD_ELEMENT_COUNT_MAX = 64, }; @@ -391,6 +395,7 @@ struct Selection { bool indirect; // Set if there was a pointer deref anywhere down the line u8 swizzle_count; // maximum components = 4 u8 swizzle_indices; // 2 bits per component, representing which swizzle index + bool pseudo_field; }; Selection empty_selection = {0}; @@ -683,14 +688,47 @@ gb_global Type *t_map_header = nullptr; gb_global Type *t_equal_proc = nullptr; gb_global Type *t_hasher_proc = nullptr; +gb_global Type *t_objc_object = nullptr; +gb_global Type *t_objc_selector = nullptr; +gb_global Type *t_objc_class = nullptr; + +gb_global Type *t_objc_id = nullptr; +gb_global Type *t_objc_SEL = nullptr; +gb_global Type *t_objc_Class = nullptr; + +enum OdinAtomicMemoryOrder : i32 { + OdinAtomicMemoryOrder_relaxed = 0, // unordered + OdinAtomicMemoryOrder_consume = 1, // monotonic + OdinAtomicMemoryOrder_acquire = 2, + OdinAtomicMemoryOrder_release = 3, + OdinAtomicMemoryOrder_acq_rel = 4, + OdinAtomicMemoryOrder_seq_cst = 5, + OdinAtomicMemoryOrder_COUNT, +}; + +char const *OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_COUNT] = { + "Relaxed", + "Consume", + "Acquire", + "Release", + "Acq_Rel", + "Seq_Cst", +}; + +gb_global Type *t_atomic_memory_order = nullptr; + + + + gb_global RecursiveMutex g_type_mutex; struct TypePath; -i64 type_size_of (Type *t); -i64 type_align_of (Type *t); -i64 type_offset_of (Type *t, i32 index); -gbString type_to_string (Type *type); +i64 type_size_of (Type *t); +i64 type_align_of (Type *t); +i64 type_offset_of (Type *t, i32 index); +gbString type_to_string (Type *type, bool shorthand=true); +gbString type_to_string (Type *type, gbAllocator allocator, bool shorthand=true); i64 type_size_of_internal(Type *t, TypePath *path); void init_map_internal_types(Type *type); Type * bit_set_to_int(Type *t); @@ -1052,10 +1090,11 @@ Type *alloc_type_bit_set() { -Type *alloc_type_simd_vector(i64 count, Type *elem) { +Type *alloc_type_simd_vector(i64 count, Type *elem, Type *generic_count=nullptr) { Type *t = alloc_type(Type_SimdVector); t->SimdVector.count = count; t->SimdVector.elem = elem; + t->SimdVector.generic_count = generic_count; return t; } @@ -1560,6 +1599,8 @@ i64 get_array_type_count(Type *t) { return bt->Array.count; } else if (bt->kind == Type_EnumeratedArray) { return bt->EnumeratedArray.count; + } else if (bt->kind == Type_SimdVector) { + return bt->SimdVector.count; } GB_ASSERT(is_type_array_like(t)); return -1; @@ -1582,6 +1623,24 @@ Type *core_array_type(Type *t) { } } +i32 type_math_rank(Type *t) { + i32 rank = 0; + for (;;) { + t = base_type(t); + switch (t->kind) { + case Type_Array: + rank += 1; + t = t->Array.elem; + break; + case Type_Matrix: + rank += 2; + t = t->Matrix.elem; + break; + default: + return rank; + } + } +} Type *base_complex_elem_type(Type *t) { @@ -1634,11 +1693,9 @@ bool is_type_map(Type *t) { bool is_type_union_maybe_pointer(Type *t) { t = base_type(t); - if (t->kind == Type_Union && t->Union.maybe) { - if (t->Union.variants.count == 1) { - Type *v = t->Union.variants[0]; - return is_type_pointer(v) || is_type_multi_pointer(v); - } + if (t->kind == Type_Union && t->Union.variants.count == 1) { + Type *v = t->Union.variants[0]; + return is_type_internally_pointer_like(v); } return false; } @@ -1646,12 +1703,10 @@ bool is_type_union_maybe_pointer(Type *t) { bool is_type_union_maybe_pointer_original_alignment(Type *t) { t = base_type(t); - if (t->kind == Type_Union && t->Union.maybe) { - if (t->Union.variants.count == 1) { - Type *v = t->Union.variants[0]; - if (is_type_pointer(v) || is_type_multi_pointer(v)) { - return type_align_of(v) == type_align_of(t); - } + if (t->kind == Type_Union && t->Union.variants.count == 1) { + Type *v = t->Union.variants[0]; + if (is_type_internally_pointer_like(v)) { + return type_align_of(v) == type_align_of(t); } } return false; @@ -1885,11 +1940,14 @@ bool is_type_valid_vector_elem(Type *t) { return false; } if (is_type_integer(t)) { - return true; + return !is_type_integer_128bit(t); } if (is_type_float(t)) { return true; } + if (is_type_boolean(t)) { + return true; + } } return false; } @@ -2031,6 +2089,11 @@ bool is_type_polymorphic(Type *t, bool or_specialized=false) { return true; } return is_type_polymorphic(t->Array.elem, or_specialized); + case Type_SimdVector: + if (t->SimdVector.generic_count != nullptr) { + return true; + } + return is_type_polymorphic(t->SimdVector.elem, or_specialized); case Type_DynamicArray: return is_type_polymorphic(t->DynamicArray.elem, or_specialized); case Type_Slice: @@ -2138,7 +2201,7 @@ bool type_has_nil(Type *t) { case Type_Map: return true; case Type_Union: - return !t->Union.no_nil; + return t->Union.kind != UnionType_no_nil; case Type_Struct: if (is_type_soa_struct(t)) { switch (t->Struct.soa_kind) { @@ -2167,6 +2230,17 @@ bool elem_type_can_be_constant(Type *t) { return true; } +bool is_type_lock_free(Type *t) { + t = core_type(t); + if (t == t_invalid) { + return false; + } + i64 sz = type_size_of(t); + // TODO(bill): Figure this out correctly + return sz <= build_context.max_align; +} + + bool is_type_comparable(Type *t) { t = base_type(t); @@ -2233,6 +2307,9 @@ bool is_type_comparable(Type *t) { } } return true; + + case Type_SimdVector: + return true; } return false; } @@ -2303,7 +2380,7 @@ String lookup_subtype_polymorphic_field(Type *dst, Type *src) { GB_ASSERT(is_type_struct(src) || is_type_union(src)); for_array(i, src->Struct.fields) { Entity *f = src->Struct.fields[i]; - if (f->kind == Entity_Variable && f->flags & EntityFlag_Using) { + if (f->kind == Entity_Variable && f->flags & EntityFlags_IsSubtype) { if (are_types_identical(dst, f->type)) { return f->token.string; } @@ -2312,7 +2389,7 @@ String lookup_subtype_polymorphic_field(Type *dst, Type *src) { return f->token.string; } } - if (is_type_struct(f->type)) { + if ((f->flags & EntityFlag_Using) != 0 && is_type_struct(f->type)) { String name = lookup_subtype_polymorphic_field(dst, f->type); if (name.len > 0) { return name; @@ -2424,7 +2501,7 @@ bool are_types_identical_internal(Type *x, Type *y, bool check_tuple_names) { if (y->kind == Type_Union) { if (x->Union.variants.count == y->Union.variants.count && x->Union.custom_align == y->Union.custom_align && - x->Union.no_nil == y->Union.no_nil) { + x->Union.kind == y->Union.kind) { // NOTE(bill): zeroth variant is nullptr for_array(i, x->Union.variants) { if (!are_types_identical(x->Union.variants[i], y->Union.variants[i])) { @@ -2458,9 +2535,9 @@ bool are_types_identical_internal(Type *x, Type *y, bool check_tuple_names) { if (xf->token.string != yf->token.string) { return false; } - bool xf_is_using = (xf->flags&EntityFlag_Using) != 0; - bool yf_is_using = (yf->flags&EntityFlag_Using) != 0; - if (xf_is_using ^ yf_is_using) { + u64 xf_flags = (xf->flags&EntityFlags_IsSubtype); + u64 yf_flags = (yf->flags&EntityFlags_IsSubtype); + if (xf_flags != yf_flags) { return false; } } @@ -2568,7 +2645,7 @@ i64 union_variant_index(Type *u, Type *v) { for_array(i, u->Union.variants) { Type *vt = u->Union.variants[i]; if (are_types_identical(v, vt)) { - if (u->Union.no_nil) { + if (u->Union.kind == UnionType_no_nil) { return cast(i64)(i+0); } else { return cast(i64)(i+1); @@ -2592,6 +2669,17 @@ i64 union_tag_size(Type *u) { // TODO(bill): Is this an okay approach? i64 max_align = 1; + + if (u->Union.variants.count < 1ull<<8) { + max_align = 1; + } else if (u->Union.variants.count < 1ull<<16) { + max_align = 2; + } else if (u->Union.variants.count < 1ull<<32) { + max_align = 4; + } else { + GB_PANIC("how many variants do you have?!"); + } + for_array(i, u->Union.variants) { Type *variant_type = u->Union.variants[i]; i64 align = type_align_of(variant_type); @@ -2752,6 +2840,7 @@ Selection lookup_field_from_index(Type *type, i64 index) { } Entity *scope_lookup_current(Scope *s, String const &name); +bool has_type_got_objc_class_attribute(Type *t); Selection lookup_field_with_selection(Type *type_, String field_name, bool is_type, Selection sel, bool allow_blank_ident) { GB_ASSERT(type_ != nullptr); @@ -2764,9 +2853,40 @@ Selection lookup_field_with_selection(Type *type_, String field_name, bool is_ty bool is_ptr = type != type_; sel.indirect = sel.indirect || is_ptr; + Type *original_type = type; + type = base_type(type); if (is_type) { + if (has_type_got_objc_class_attribute(original_type) && original_type->kind == Type_Named) { + Entity *e = original_type->Named.type_name; + GB_ASSERT(e->kind == Entity_TypeName); + if (e->TypeName.objc_metadata) { + auto *md = e->TypeName.objc_metadata; + mutex_lock(md->mutex); + defer (mutex_unlock(md->mutex)); + for (TypeNameObjCMetadataEntry const &entry : md->type_entries) { + GB_ASSERT(entry.entity->kind == Entity_Procedure); + if (entry.name == field_name) { + sel.entity = entry.entity; + sel.pseudo_field = true; + return sel; + } + } + } + if (type->kind == Type_Struct) { + for_array(i, type->Struct.fields) { + Entity *f = type->Struct.fields[i]; + if (f->flags&EntityFlag_Using) { + sel = lookup_field_with_selection(f->type, field_name, is_type, sel, allow_blank_ident); + if (sel.entity) { + return sel; + } + } + } + } + } + if (is_type_enum(type)) { // NOTE(bill): These may not have been added yet, so check in case for_array(i, type->Enum.fields) { @@ -2813,6 +2933,24 @@ Selection lookup_field_with_selection(Type *type_, String field_name, bool is_ty } else if (type->kind == Type_Union) { } else if (type->kind == Type_Struct) { + if (has_type_got_objc_class_attribute(original_type) && original_type->kind == Type_Named) { + Entity *e = original_type->Named.type_name; + GB_ASSERT(e->kind == Entity_TypeName); + if (e->TypeName.objc_metadata) { + auto *md = e->TypeName.objc_metadata; + mutex_lock(md->mutex); + defer (mutex_unlock(md->mutex)); + for (TypeNameObjCMetadataEntry const &entry : md->value_entries) { + GB_ASSERT(entry.entity->kind == Entity_Procedure); + if (entry.name == field_name) { + sel.entity = entry.entity; + sel.pseudo_field = true; + return sel; + } + } + } + } + for_array(i, type->Struct.fields) { Entity *f = type->Struct.fields[i]; if (f->kind != Entity_Variable || (f->flags & EntityFlag_Field) == 0) { @@ -3327,7 +3465,7 @@ i64 type_align_of_internal(Type *t, TypePath *path) { case Type_SimdVector: { // IMPORTANT TODO(bill): Figure out the alignment of vector types - return gb_clamp(next_pow2(type_size_of_internal(t, path)), 1, build_context.max_align); + return gb_clamp(next_pow2(type_size_of_internal(t, path)), 1, build_context.max_align*2); } case Type_Matrix: @@ -3718,6 +3856,61 @@ i64 type_offset_of_from_selection(Type *type, Selection sel) { return offset; } +isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isize level = 0, bool src_is_ptr = false) { + Type *prev_src = src; + src = type_deref(src); + if (!src_is_ptr) { + src_is_ptr = src != prev_src; + } + src = base_type(src); + + if (!is_type_struct(src)) { + return 0; + } + + for_array(i, src->Struct.fields) { + Entity *f = src->Struct.fields[i]; + if (f->kind != Entity_Variable || (f->flags&EntityFlags_IsSubtype) == 0) { + continue; + } + + if (are_types_identical(f->type, dst)) { + return level+1; + } + if (src_is_ptr && is_type_pointer(dst)) { + if (are_types_identical(f->type, type_deref(dst))) { + return level+1; + } + } + isize nested_level = check_is_assignable_to_using_subtype(f->type, dst, level+1, src_is_ptr); + if (nested_level > 0) { + return nested_level; + } + } + + return 0; +} + +bool is_type_subtype_of(Type *src, Type *dst) { + if (are_types_identical(src, dst)) { + return true; + } + + return 0 < check_is_assignable_to_using_subtype(src, dst, 0, is_type_pointer(src)); +} + + +bool has_type_got_objc_class_attribute(Type *t) { + return t->kind == Type_Named && t->Named.type_name != nullptr && t->Named.type_name->TypeName.objc_class_name != ""; +} + + + +bool is_type_objc_object(Type *t) { + bool internal_check_is_assignable_to(Type *src, Type *dst); + + return internal_check_is_assignable_to(t, t_objc_object); +} Type *get_struct_field_type(Type *t, isize index) { t = base_type(type_deref(t)); @@ -3789,7 +3982,7 @@ Type *alloc_type_proc_from_types(Type **param_types, unsigned param_count, Type -gbString write_type_to_string(gbString str, Type *type) { +gbString write_type_to_string(gbString str, Type *type, bool shorthand=false) { if (type == nullptr) { return gb_string_appendc(str, ""); } @@ -3830,6 +4023,9 @@ gbString write_type_to_string(gbString str, Type *type) { break; case Type_EnumeratedArray: + if (type->EnumeratedArray.is_sparse) { + str = gb_string_appendc(str, "#sparse"); + } str = gb_string_append_rune(str, '['); str = write_type_to_string(str, type->EnumeratedArray.index); str = gb_string_append_rune(str, ']'); @@ -3872,8 +4068,10 @@ gbString write_type_to_string(gbString str, Type *type) { case Type_Union: str = gb_string_appendc(str, "union"); - if (type->Union.no_nil != 0) str = gb_string_appendc(str, " #no_nil"); - if (type->Union.maybe != 0) str = gb_string_appendc(str, " #maybe"); + switch (type->Union.kind) { + case UnionType_no_nil: str = gb_string_appendc(str, " #no_nil"); break; + case UnionType_shared_nil: str = gb_string_appendc(str, " #shared_nil"); break; + } if (type->Union.custom_align != 0) str = gb_string_append_fmt(str, " #align %d", cast(int)type->Union.custom_align); str = gb_string_appendc(str, " {"); for_array(i, type->Union.variants) { @@ -3901,15 +4099,21 @@ gbString write_type_to_string(gbString str, Type *type) { if (type->Struct.is_raw_union) str = gb_string_appendc(str, " #raw_union"); if (type->Struct.custom_align != 0) str = gb_string_append_fmt(str, " #align %d", cast(int)type->Struct.custom_align); str = gb_string_appendc(str, " {"); - for_array(i, type->Struct.fields) { - Entity *f = type->Struct.fields[i]; - GB_ASSERT(f->kind == Entity_Variable); - if (i > 0) { - str = gb_string_appendc(str, ", "); + + + if (shorthand && type->Struct.fields.count > 16) { + str = gb_string_append_fmt(str, "%lld fields...", cast(long long)type->Struct.fields.count); + } else { + for_array(i, type->Struct.fields) { + Entity *f = type->Struct.fields[i]; + GB_ASSERT(f->kind == Entity_Variable); + if (i > 0) { + str = gb_string_appendc(str, ", "); + } + str = gb_string_append_length(str, f->token.string.text, f->token.string.len); + str = gb_string_appendc(str, ": "); + str = write_type_to_string(str, f->type); } - str = gb_string_append_length(str, f->token.string.text, f->token.string.len); - str = gb_string_appendc(str, ": "); - str = write_type_to_string(str, f->type); } str = gb_string_append_rune(str, '}'); } break; @@ -4084,13 +4288,16 @@ gbString write_type_to_string(gbString str, Type *type) { } -gbString type_to_string(Type *type, gbAllocator allocator) { - return write_type_to_string(gb_string_make(allocator, ""), type); +gbString type_to_string(Type *type, gbAllocator allocator, bool shorthand) { + return write_type_to_string(gb_string_make(allocator, ""), type, shorthand); } -gbString type_to_string(Type *type) { - return write_type_to_string(gb_string_make(heap_allocator(), ""), type); +gbString type_to_string(Type *type, bool shorthand) { + return write_type_to_string(gb_string_make(heap_allocator(), ""), type, shorthand); +} + +gbString type_to_string_shorthand(Type *type) { + return type_to_string(type, true); } - diff --git a/tests/common/common.odin b/tests/common/common.odin new file mode 100644 index 000000000..07b6afef9 --- /dev/null +++ b/tests/common/common.odin @@ -0,0 +1,75 @@ +// Boilerplate for tests +package common + +import "core:testing" +import "core:fmt" +import "core:os" +import "core:strings" + +TEST_count := 0 +TEST_fail := 0 + +when ODIN_TEST { + expect :: testing.expect + log :: testing.log +} else { + expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) { + TEST_count += 1 + if !condition { + TEST_fail += 1 + fmt.printf("[%v] FAIL %v\n", loc, message) + return + } + } + log :: proc(t: ^testing.T, v: any, loc := #caller_location) { + fmt.printf("[%v] ", loc) + fmt.printf("log: %v\n", v) + } +} + +report :: proc(t: ^testing.T) { + if TEST_fail > 0 { + if TEST_fail > 1 { + fmt.printf("%v/%v tests successful, %v tests failed.\n", TEST_count - TEST_fail, TEST_count, TEST_fail) + } else { + fmt.printf("%v/%v tests successful, 1 test failed.\n", TEST_count - TEST_fail, TEST_count) + } + os.exit(1) + } else { + fmt.printf("%v/%v tests successful.\n", TEST_count, TEST_count) + } +} + +// Returns absolute path to `sub_path` where `sub_path` is within the "tests/" sub-directory of the Odin project root +// and we're being run from the Odin project root or from a sub-directory of "tests/" +// e.g. get_data_path("assets/blah") will return "/Odin_root/tests/assets/blah" if run within "/Odin_root", +// "/Odin_root/tests" or "/Odin_root/tests/subdir" etc +get_data_path :: proc(t: ^testing.T, sub_path: string) -> (data_path: string) { + + cwd := os.get_current_directory() + defer delete(cwd) + + when ODIN_OS == .Windows { + norm, was_allocation := strings.replace_all(cwd, "\\", "/") + if !was_allocation { + norm = strings.clone(norm) + } + defer delete(norm) + } else { + norm := cwd + } + + last_index := strings.last_index(norm, "/tests/") + if last_index == -1 { + len := len(norm) + if len >= 6 && norm[len-6:] == "/tests" { + data_path = fmt.tprintf("%s/%s", norm, sub_path) + } else { + data_path = fmt.tprintf("%s/tests/%s", norm, sub_path) + } + } else { + data_path = fmt.tprintf("%s/tests/%s", norm[:last_index], sub_path) + } + + return data_path +} diff --git a/tests/core/Makefile b/tests/core/Makefile index 0f0ffe4d6..5c2918e30 100644 --- a/tests/core/Makefile +++ b/tests/core/Makefile @@ -1,22 +1,50 @@ ODIN=../../odin PYTHON=$(shell which python3) -all: download_test_assets image_test compress_test strings_test hash_test crypto_test +all: download_test_assets image_test compress_test strings_test hash_test crypto_test noise_test encoding_test \ + math_test linalg_glsl_math_test filepath_test reflect_test os_exit_test i18n_test download_test_assets: $(PYTHON) download_assets.py image_test: - $(ODIN) run image/test_core_image.odin + $(ODIN) run image/test_core_image.odin -file -out:test_core_image compress_test: - $(ODIN) run compress/test_core_compress.odin + $(ODIN) run compress/test_core_compress.odin -file -out:test_core_compress strings_test: - $(ODIN) run strings/test_core_strings.odin + $(ODIN) run strings/test_core_strings.odin -file -out:test_core_strings hash_test: - $(ODIN) run hash -out=test_hash -o:speed -no-bounds-check + $(ODIN) run hash -o:speed -no-bounds-check -out:test_hash crypto_test: - $(ODIN) run crypto -out=crypto_hash -o:speed -no-bounds-check \ No newline at end of file + $(ODIN) run crypto -o:speed -no-bounds-check -out:test_crypto_hash + +noise_test: + $(ODIN) run math/noise -out:test_noise + +encoding_test: + $(ODIN) run encoding/hxa -out:test_hxa -collection:tests=.. + $(ODIN) run encoding/json -out:test_json + $(ODIN) run encoding/varint -out:test_varint + $(ODIN) run encoding/xml -out:test_xml + +math_test: + $(ODIN) run math/test_core_math.odin -file -collection:tests=.. -out:test_core_math + +linalg_glsl_math_test: + $(ODIN) run math/linalg/glsl/test_linalg_glsl_math.odin -file -collection:tests=.. -out:test_linalg_glsl_math + +filepath_test: + $(ODIN) run path/filepath/test_core_filepath.odin -file -collection:tests=.. -out:test_core_filepath + +reflect_test: + $(ODIN) run reflect/test_core_reflect.odin -file -collection:tests=.. -out:test_core_reflect + +os_exit_test: + $(ODIN) run os/test_core_os_exit.odin -file -out:test_core_os_exit && exit 1 || exit 0 + +i18n_test: + $(ODIN) run text/i18n -out:test_core_i18n \ No newline at end of file diff --git a/tests/core/assets/HXA/teapot.hxa b/tests/core/assets/HXA/teapot.hxa new file mode 100644 index 000000000..954ab5a10 Binary files /dev/null and b/tests/core/assets/HXA/teapot.hxa differ diff --git a/tests/core/assets/I18N/duplicate-key.ts b/tests/core/assets/I18N/duplicate-key.ts new file mode 100644 index 000000000..44c09d91d --- /dev/null +++ b/tests/core/assets/I18N/duplicate-key.ts @@ -0,0 +1,22 @@ + + + + + Page + + %d apple(s) + commenting + Tekst om te vertalen + + + + apple_count + + %d apple(s) + + %d appel + %d appels + + + + diff --git a/tests/core/assets/I18N/messages.pot b/tests/core/assets/I18N/messages.pot new file mode 100644 index 000000000..53d521b6b --- /dev/null +++ b/tests/core/assets/I18N/messages.pot @@ -0,0 +1,30 @@ +# Odin i18n Example +# Copyright (C) 2021 Jeroen van Rijn +# This file is distributed under the same license as the PACKAGE package. +# Jeroen van Rijn , 2021. +# +#, fuzzy +msgid "" +msgstr "Project-Id-Version: Example 0.0.1\n" + "Report-Msgid-Bugs-To: Jeroen van Rijn \n" + "POT-Creation-Date: 2021-11-27 19:23+0100\n" + "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" + "Last-Translator: FULL NAME \n" + "Language: en-GB\n" + "MIME-Version: 1.0\n" + "Content-Type: text/plain; charset=UTF-8\n" + "Content-Transfer-Encoding: 8bit\n" + +#: i18n_example.odin:28 +msgid "There are 69,105 leaves here." +msgstr "Er zijn hier 69.105 bladeren." + +#: i18n_example.odin:30 +msgid "Hellope, World!" +msgstr "Hallo, Wereld!" + +#: i18n_example.odin:36 +msgid "There is %d leaf.\n" +msgid_plural "There are %d leaves.\n" +msgstr[0] "Er is %d blad.\n" +msgstr[1] "Er zijn %d bladeren.\n" \ No newline at end of file diff --git a/tests/core/assets/I18N/nl_NL-qt-ts.ts b/tests/core/assets/I18N/nl_NL-qt-ts.ts new file mode 100644 index 000000000..36c95ce2e --- /dev/null +++ b/tests/core/assets/I18N/nl_NL-qt-ts.ts @@ -0,0 +1,35 @@ + + + + + Page + + Text for translation + commenting + Tekst om te vertalen + + + Also text to translate + some text + Ook tekst om te vertalen + + + + installscript + + 99 bottles of beer on the wall + some new comments here + 99 flessen bier op de muur + + + + apple_count + + %d apple(s) + + %d appel + %d appels + + + + diff --git a/tests/core/assets/I18N/nl_NL-xliff-1.2.xliff b/tests/core/assets/I18N/nl_NL-xliff-1.2.xliff new file mode 100644 index 000000000..7a1abcd66 --- /dev/null +++ b/tests/core/assets/I18N/nl_NL-xliff-1.2.xliff @@ -0,0 +1,38 @@ + + + + + + text + tekst + Context + + + text 1 + tekst 1 + Context 1 + + + text 2 + + Context of the segment 2 + + + text 3 + translation 3 + Context 3 + + + Plurals + + %d month + %d maand + + + %d months + %d maanden + + + + + diff --git a/tests/core/assets/I18N/nl_NL-xliff-2.0.xliff b/tests/core/assets/I18N/nl_NL-xliff-2.0.xliff new file mode 100644 index 000000000..611ac80c4 --- /dev/null +++ b/tests/core/assets/I18N/nl_NL-xliff-2.0.xliff @@ -0,0 +1,52 @@ + + + + + Note for file + + + + Note for unit + + + text + + + + + + Note for unit 2 + + + text 2 + translation 2 + + + + + Note for unit 3 + + + text 3 + approved translation 3 + + + + + + Plurals + + + %d month + %d maand + + + + + %d months + %d maanden + + + + + \ No newline at end of file diff --git a/tests/core/assets/I18N/nl_NL.mo b/tests/core/assets/I18N/nl_NL.mo new file mode 100644 index 000000000..0b1a668f4 Binary files /dev/null and b/tests/core/assets/I18N/nl_NL.mo differ diff --git a/tests/core/assets/I18N/nl_NL.po b/tests/core/assets/I18N/nl_NL.po new file mode 100644 index 000000000..1b8acbcc1 --- /dev/null +++ b/tests/core/assets/I18N/nl_NL.po @@ -0,0 +1,33 @@ +# Odin i18n Example +# Copyright (C) 2021 Jeroen van Rijn +# This file is distributed under the same license as the PACKAGE package. +# Jeroen van Rijn , 2021. +# +msgid "" +msgstr "" +"Project-Id-Version: Example 0.0.1\n" +"Report-Msgid-Bugs-To: Jeroen van Rijn \n" +"POT-Creation-Date: 2021-11-27 19:23+0100\n" +"PO-Revision-Date: 2021-11-28 02:56+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language-Team: Odin Language Team\n" +"X-Generator: Poedit 3.0\n" +"Last-Translator: Jeroen van Rijn\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Language: nl_NL\n" + +#: i18n_example.odin:28 +msgid "There are 69,105 leaves here." +msgstr "Er zijn hier 69.105 bladeren." + +#: i18n_example.odin:30 +msgid "Hellope, World!" +msgstr "Hallo, Wereld!" + +#: i18n_example.odin:36 +msgid "There is %d leaf.\n" +msgid_plural "There are %d leaves.\n" +msgstr[0] "Er is %d blad.\n" +msgstr[1] "Er zijn %d bladeren.\n" diff --git a/tests/core/assets/Shoco/LICENSE b/tests/core/assets/Shoco/LICENSE new file mode 100644 index 000000000..9ca94bcdf --- /dev/null +++ b/tests/core/assets/Shoco/LICENSE @@ -0,0 +1,26 @@ +Copyright (c) 2016-2021 Ginger Bill. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/tests/core/assets/Shoco/LICENSE.shoco b/tests/core/assets/Shoco/LICENSE.shoco new file mode 100644 index 000000000..5d5e4d623 Binary files /dev/null and b/tests/core/assets/Shoco/LICENSE.shoco differ diff --git a/tests/core/assets/Shoco/README.md b/tests/core/assets/Shoco/README.md new file mode 100644 index 000000000..9e46f80d0 --- /dev/null +++ b/tests/core/assets/Shoco/README.md @@ -0,0 +1,95 @@ +

+ Odin logo +
+ The Data-Oriented Language for Sane Software Development. +
+
+ + + +
+ + +
+ + + + + + +

+ +# The Odin Programming Language + + +Odin is a general-purpose programming language with distinct typing, built for high performance, modern systems, and built-in data-oriented data types. The Odin Programming Language, the C alternative for the joy of programming. + +Website: [https://odin-lang.org/](https://odin-lang.org/) + +```odin +package main + +import "core:fmt" + +main :: proc() { + program := "+ + * 😃 - /" + accumulator := 0 + + for token in program { + switch token { + case '+': accumulator += 1 + case '-': accumulator -= 1 + case '*': accumulator *= 2 + case '/': accumulator /= 2 + case '😃': accumulator *= accumulator + case: // Ignore everything else + } + } + + fmt.printf("The program \"%s\" calculates the value %d\n", + program, accumulator) +} + +``` + +## Documentation + +#### [Getting Started](https://odin-lang.org/docs/install) + +Instructions for downloading and installing the Odin compiler and libraries. + +#### [Nightly Builds](https://odin-lang.org/docs/nightly/) + +Get the latest nightly builds of Odin. + +### Learning Odin + +#### [Overview of Odin](https://odin-lang.org/docs/overview) + +An overview of the Odin programming language. + +#### [Frequently Asked Questions (FAQ)](https://odin-lang.org/docs/faq) + +Answers to common questions about Odin. + +#### [Packages](https://pkg.odin-lang.org/) + +Documentation for all the official packages part of the [core](https://pkg.odin-lang.org/core/) and [vendor](https://pkg.odin-lang.org/vendor/) library collections. + +#### [The Odin Wiki](https://github.com/odin-lang/Odin/wiki) + +A wiki maintained by the Odin community. + +#### [Odin Discord](https://discord.gg/sVBPHEv) + +Get live support and talk with other odiners on the Odin Discord. + +### Articles + +#### [The Odin Blog](https://odin-lang.org/news/) + +The official blog of the Odin programming language, featuring announcements, news, and in-depth articles by the Odin team and guests. + +## Warnings + +* The Odin compiler is still in development. diff --git a/tests/core/assets/Shoco/README.md.shoco b/tests/core/assets/Shoco/README.md.shoco new file mode 100644 index 000000000..013f4f469 Binary files /dev/null and b/tests/core/assets/Shoco/README.md.shoco differ diff --git a/tests/core/assets/XML/.gitignore b/tests/core/assets/XML/.gitignore new file mode 100644 index 000000000..32dc58b57 --- /dev/null +++ b/tests/core/assets/XML/.gitignore @@ -0,0 +1,2 @@ +# This file will be downloaded by download_assets.py +unicode.xml \ No newline at end of file diff --git a/tests/core/assets/XML/entities.html b/tests/core/assets/XML/entities.html new file mode 100644 index 000000000..05a6b107e --- /dev/null +++ b/tests/core/assets/XML/entities.html @@ -0,0 +1,29 @@ + + + Entity Reference Test + + + +

Entity Reference Test

+
+ Foozle]! © 42&;1234& +
+ + +
+ Foozle]! © 42&;1234& +
+ +
+ | | | fj ` \ ® ϱ ∳ ⁏ +
+ + \ No newline at end of file diff --git a/tests/core/assets/XML/utf8.xml b/tests/core/assets/XML/utf8.xml new file mode 100644 index 000000000..6e1a897ea --- /dev/null +++ b/tests/core/assets/XML/utf8.xml @@ -0,0 +1,8 @@ + + +<恥ずべきフクロウ 올빼미_id="Foozle Hello, world!"]]>Barzle"> +<부끄러운:barzle> + ရှက်စရာ ဇီးကွက် + Owl of Shame + More CDATA Hello, world! Nonsense. + \ No newline at end of file diff --git a/tests/core/build.bat b/tests/core/build.bat index 176b7f175..77ff38038 100644 --- a/tests/core/build.bat +++ b/tests/core/build.bat @@ -1,38 +1,72 @@ @echo off -set COMMON=-show-timings -no-bounds-check -vet -strict-style +set COMMON=-no-bounds-check -vet -strict-style +set COLLECTION=-collection:tests=.. set PATH_TO_ODIN==..\..\odin python3 download_assets.py echo --- echo Running core:image tests echo --- -%PATH_TO_ODIN% run image %COMMON% +%PATH_TO_ODIN% run image %COMMON% -out:test_core_image.exe echo --- echo Running core:compress tests echo --- -%PATH_TO_ODIN% run compress %COMMON% +%PATH_TO_ODIN% run compress %COMMON% -out:test_core_compress.exe echo --- echo Running core:strings tests echo --- -%PATH_TO_ODIN% run strings %COMMON% +%PATH_TO_ODIN% run strings %COMMON% -out:test_core_strings.exe echo --- echo Running core:hash tests echo --- -%PATH_TO_ODIN% run hash %COMMON% -o:size +%PATH_TO_ODIN% run hash %COMMON% -o:size -out:test_core_hash.exe echo --- echo Running core:odin tests echo --- -%PATH_TO_ODIN% run odin %COMMON% -o:size +%PATH_TO_ODIN% run odin %COMMON% -o:size -out:test_core_odin.exe echo --- echo Running core:crypto hash tests echo --- -%PATH_TO_ODIN% run crypto %COMMON% +%PATH_TO_ODIN% run crypto %COMMON% -out:test_crypto_hash.exe echo --- echo Running core:encoding tests echo --- -%PATH_TO_ODIN% run encoding %COMMON% \ No newline at end of file +%PATH_TO_ODIN% run encoding/hxa %COMMON% %COLLECTION% -out:test_hxa.exe +%PATH_TO_ODIN% run encoding/json %COMMON% -out:test_json.exe +%PATH_TO_ODIN% run encoding/varint %COMMON% -out:test_varint.exe +%PATH_TO_ODIN% run encoding/xml %COMMON% -out:test_xml.exe + +echo --- +echo Running core:math/noise tests +echo --- +%PATH_TO_ODIN% run math/noise %COMMON% -out:test_noise.exe + +echo --- +echo Running core:math tests +echo --- +%PATH_TO_ODIN% run math %COMMON% %COLLECTION% -out:test_core_math.exe + +echo --- +echo Running core:math/linalg/glsl tests +echo --- +%PATH_TO_ODIN% run math/linalg/glsl %COMMON% %COLLECTION% -out:test_linalg_glsl.exe + +echo --- +echo Running core:path/filepath tests +echo --- +%PATH_TO_ODIN% run path/filepath %COMMON% %COLLECTION% -out:test_core_filepath.exe + +echo --- +echo Running core:reflect tests +echo --- +%PATH_TO_ODIN% run reflect %COMMON% %COLLECTION% -out:test_core_reflect.exe + +echo --- +echo Running core:text/i18n tests +echo --- +%PATH_TO_ODIN% run text\i18n %COMMON% -out:test_core_i18n.exe \ No newline at end of file diff --git a/tests/core/compress/test_core_compress.odin b/tests/core/compress/test_core_compress.odin index c925c0258..ee7233e52 100644 --- a/tests/core/compress/test_core_compress.odin +++ b/tests/core/compress/test_core_compress.odin @@ -7,13 +7,14 @@ package test_core_compress List of contributors: Jeroen van Rijn: Initial implementation. - A test suite for ZLIB, GZIP. + A test suite for ZLIB, GZIP and Shoco. */ import "core:testing" import "core:compress/zlib" import "core:compress/gzip" +import "core:compress/shoco" import "core:bytes" import "core:fmt" @@ -30,14 +31,12 @@ when ODIN_TEST { log :: testing.log } else { expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) { - fmt.printf("[%v] ", loc) TEST_count += 1 if !condition { TEST_fail += 1 - fmt.println(message) + fmt.printf("[%v] %v\n", loc, message) return } - fmt.println(" PASS") } log :: proc(t: ^testing.T, v: any, loc := #caller_location) { fmt.printf("[%v] ", loc) @@ -50,8 +49,12 @@ main :: proc() { t := testing.T{w=w} zlib_test(&t) gzip_test(&t) + shoco_test(&t) fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count) + if TEST_fail > 0 { + os.exit(1) + } } @test @@ -133,3 +136,56 @@ gzip_test :: proc(t: ^testing.T) { expect(t, false, error) } } + +@test +shoco_test :: proc(t: ^testing.T) { + + Shoco_Tests :: []struct{ + compressed: []u8, + raw: []u8, + short_pack: int, + short_sentinel: int, + }{ + { #load("../assets/Shoco/README.md.shoco"), #load("../assets/Shoco/README.md"), 10, 1006 }, + { #load("../assets/Shoco/LICENSE.shoco"), #load("../assets/Shoco/LICENSE"), 25, 68 }, + } + + for v in Shoco_Tests { + expected_raw := len(v.raw) + expected_compressed := len(v.compressed) + + biggest_unpacked := shoco.decompress_bound(expected_compressed) + biggest_packed := shoco.compress_bound(expected_raw) + + buffer := make([]u8, max(biggest_packed, biggest_unpacked)) + defer delete(buffer) + + size, err := shoco.decompress(v.compressed, buffer[:]) + msg := fmt.tprintf("Expected `decompress` to return `nil`, got %v", err) + expect(t, err == nil, msg) + + msg = fmt.tprintf("Decompressed %v bytes into %v. Expected to decompress into %v bytes.", len(v.compressed), size, expected_raw) + expect(t, size == expected_raw, msg) + expect(t, string(buffer[:size]) == string(v.raw), "Decompressed contents don't match.") + + size, err = shoco.compress(string(v.raw), buffer[:]) + expect(t, err == nil, "Expected `compress` to return `nil`.") + + msg = fmt.tprintf("Compressed %v bytes into %v. Expected to compress into %v bytes.", expected_raw, size, expected_compressed) + expect(t, size == expected_compressed, msg) + + size, err = shoco.decompress(v.compressed, buffer[:expected_raw - 10]) + msg = fmt.tprintf("Decompressing into too small a buffer returned %v, expected `.Output_Too_Short`", err) + expect(t, err == .Output_Too_Short, msg) + + size, err = shoco.compress(string(v.raw), buffer[:expected_compressed - 10]) + msg = fmt.tprintf("Compressing into too small a buffer returned %v, expected `.Output_Too_Short`", err) + expect(t, err == .Output_Too_Short, msg) + + size, err = shoco.decompress(v.compressed[:v.short_pack], buffer[:]) + expect(t, err == .Stream_Too_Short, "Expected `decompress` to return `Stream_Too_Short` because there was no more data after selecting a pack.") + + size, err = shoco.decompress(v.compressed[:v.short_sentinel], buffer[:]) + expect(t, err == .Stream_Too_Short, "Expected `decompress` to return `Stream_Too_Short` because there was no more data after non-ASCII sentinel.") + } +} \ No newline at end of file diff --git a/tests/core/crypto/test_core_crypto.odin b/tests/core/crypto/test_core_crypto.odin index 2ad00be66..f6b4b10b8 100644 --- a/tests/core/crypto/test_core_crypto.odin +++ b/tests/core/crypto/test_core_crypto.odin @@ -1,19 +1,20 @@ package test_core_crypto /* - Copyright 2021 zhibog - Made available under the BSD-3 license. + Copyright 2021 zhibog + Made available under the BSD-3 license. - List of contributors: - zhibog, dotbmp: Initial implementation. - Jeroen van Rijn: Test runner setup. + List of contributors: + zhibog, dotbmp: Initial implementation. + Jeroen van Rijn: Test runner setup. - Tests for the hashing algorithms within the crypto library. - Where possible, the official test vectors are used to validate the implementation. + Tests for the hashing algorithms within the crypto library. + Where possible, the official test vectors are used to validate the implementation. */ import "core:testing" import "core:fmt" +import "core:strings" import "core:crypto/md2" import "core:crypto/md4" @@ -36,1070 +37,1118 @@ import "core:crypto/sm3" import "core:crypto/jh" import "core:crypto/groestl" import "core:crypto/haval" +import "core:crypto/siphash" +import "core:os" TEST_count := 0 TEST_fail := 0 when ODIN_TEST { - expect :: testing.expect - log :: testing.log + expect :: testing.expect + log :: testing.log } else { - expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) { - fmt.printf("[%v] ", loc) - TEST_count += 1 - if !condition { - TEST_fail += 1 - fmt.println(message) - return - } - fmt.println(" PASS") - } - log :: proc(t: ^testing.T, v: any, loc := #caller_location) { - fmt.printf("[%v] ", loc) - fmt.printf("log: %v\n", v) - } + expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) { + TEST_count += 1 + if !condition { + TEST_fail += 1 + fmt.printf("[%v] %v\n", loc, message) + return + } + } + log :: proc(t: ^testing.T, v: any, loc := #caller_location) { + fmt.printf("[%v] ", loc) + fmt.printf("log: %v\n", v) + } } main :: proc() { - t := testing.T{} - test_md2(&t) - test_md4(&t) - test_md5(&t) - test_sha1(&t) - test_sha224(&t) - test_sha256(&t) - test_sha384(&t) - test_sha512(&t) - test_sha3_224(&t) - test_sha3_256(&t) - test_sha3_384(&t) - test_sha3_512(&t) - test_shake_128(&t) - test_shake_256(&t) - test_keccak_224(&t) - test_keccak_256(&t) - test_keccak_384(&t) - test_keccak_512(&t) - test_whirlpool(&t) - test_gost(&t) - test_streebog_256(&t) - test_streebog_512(&t) - test_blake_224(&t) - test_blake_256(&t) - test_blake_384(&t) - test_blake_512(&t) - test_blake2b(&t) - test_blake2s(&t) - test_ripemd_128(&t) - test_ripemd_160(&t) - test_ripemd_256(&t) - test_ripemd_320(&t) - test_tiger_128(&t) - test_tiger_160(&t) - test_tiger_192(&t) - test_tiger2_128(&t) - test_tiger2_160(&t) - test_tiger2_192(&t) - test_sm3(&t) - test_jh_224(&t) - test_jh_256(&t) - test_jh_384(&t) - test_jh_512(&t) - test_groestl_224(&t) - test_groestl_256(&t) - test_groestl_384(&t) - test_groestl_512(&t) - test_haval_128(&t) - test_haval_160(&t) - test_haval_192(&t) - test_haval_224(&t) - test_haval_256(&t) + t := testing.T{} + test_md2(&t) + test_md4(&t) + test_md5(&t) + test_sha1(&t) + test_sha224(&t) + test_sha256(&t) + test_sha384(&t) + test_sha512(&t) + test_sha3_224(&t) + test_sha3_256(&t) + test_sha3_384(&t) + test_sha3_512(&t) + test_shake_128(&t) + test_shake_256(&t) + test_keccak_224(&t) + test_keccak_256(&t) + test_keccak_384(&t) + test_keccak_512(&t) + test_whirlpool(&t) + test_gost(&t) + test_streebog_256(&t) + test_streebog_512(&t) + test_blake_224(&t) + test_blake_256(&t) + test_blake_384(&t) + test_blake_512(&t) + test_blake2b(&t) + test_blake2s(&t) + test_ripemd_128(&t) + test_ripemd_160(&t) + test_ripemd_256(&t) + test_ripemd_320(&t) + test_tiger_128(&t) + test_tiger_160(&t) + test_tiger_192(&t) + test_tiger2_128(&t) + test_tiger2_160(&t) + test_tiger2_192(&t) + test_sm3(&t) + test_jh_224(&t) + test_jh_256(&t) + test_jh_384(&t) + test_jh_512(&t) + test_groestl_224(&t) + test_groestl_256(&t) + test_groestl_384(&t) + test_groestl_512(&t) + test_haval_128(&t) + test_haval_160(&t) + test_haval_192(&t) + test_haval_224(&t) + test_haval_256(&t) + test_siphash_2_4(&t) - // "modern" crypto tests - test_chacha20(&t) - test_poly1305(&t) - test_chacha20poly1305(&t) - test_x25519(&t) - test_rand_bytes(&t) + // "modern" crypto tests + test_chacha20(&t) + test_poly1305(&t) + test_chacha20poly1305(&t) + test_x25519(&t) + test_rand_bytes(&t) - bench_modern(&t) + bench_modern(&t) - fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count) + fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count) + if TEST_fail > 0 { + os.exit(1) + } } TestHash :: struct { - hash: string, - str: string, + hash: string, + str: string, } hex_string :: proc(bytes: []byte, allocator := context.temp_allocator) -> string { - lut: [16]byte = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'} - buf := make([]byte, len(bytes) * 2, allocator) - for i := 0; i < len(bytes); i += 1 { - buf[i * 2 + 0] = lut[bytes[i] >> 4 & 0xf] - buf[i * 2 + 1] = lut[bytes[i] & 0xf] - } - return string(buf) + lut: [16]byte = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'} + buf := make([]byte, len(bytes) * 2, allocator) + for i := 0; i < len(bytes); i += 1 { + buf[i * 2 + 0] = lut[bytes[i] >> 4 & 0xf] + buf[i * 2 + 1] = lut[bytes[i] & 0xf] + } + return string(buf) } @(test) test_md2 :: proc(t: ^testing.T) { - // Official test vectors from https://datatracker.ietf.org/doc/html/rfc1319 - test_vectors := [?]TestHash { - TestHash{"8350e5a3e24c153df2275c9f80692773", ""}, - TestHash{"32ec01ec4a6dac72c0ab96fb34c0b5d1", "a"}, - TestHash{"da853b0d3f88d99b30283a69e6ded6bb", "abc"}, - TestHash{"ab4f496bfb2a530b219ff33031fe06b0", "message digest"}, - TestHash{"4e8ddff3650292ab5a4108c3aa47940b", "abcdefghijklmnopqrstuvwxyz"}, - TestHash{"da33def2a42df13975352846c30338cd", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, - TestHash{"d5976f79d83d3a0dc9806c3c66f3efd8", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, - } - for v, _ in test_vectors { - computed := md2.hash(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + // Official test vectors from https://datatracker.ietf.org/doc/html/rfc1319 + test_vectors := [?]TestHash { + TestHash{"8350e5a3e24c153df2275c9f80692773", ""}, + TestHash{"32ec01ec4a6dac72c0ab96fb34c0b5d1", "a"}, + TestHash{"da853b0d3f88d99b30283a69e6ded6bb", "abc"}, + TestHash{"ab4f496bfb2a530b219ff33031fe06b0", "message digest"}, + TestHash{"4e8ddff3650292ab5a4108c3aa47940b", "abcdefghijklmnopqrstuvwxyz"}, + TestHash{"da33def2a42df13975352846c30338cd", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, + TestHash{"d5976f79d83d3a0dc9806c3c66f3efd8", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, + } + for v, _ in test_vectors { + computed := md2.hash(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_md4 :: proc(t: ^testing.T) { - // Official test vectors from https://datatracker.ietf.org/doc/html/rfc1320 - test_vectors := [?]TestHash { - TestHash{"31d6cfe0d16ae931b73c59d7e0c089c0", ""}, - TestHash{"bde52cb31de33e46245e05fbdbd6fb24", "a"}, - TestHash{"a448017aaf21d8525fc10ae87aa6729d", "abc"}, - TestHash{"d9130a8164549fe818874806e1c7014b", "message digest"}, - TestHash{"d79e1c308aa5bbcdeea8ed63df412da9", "abcdefghijklmnopqrstuvwxyz"}, - TestHash{"043f8582f241db351ce627e153e7f0e4", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, - TestHash{"e33b4ddc9c38f2199c3e7b164fcc0536", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, - } - for v, _ in test_vectors { - computed := md4.hash(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + // Official test vectors from https://datatracker.ietf.org/doc/html/rfc1320 + test_vectors := [?]TestHash { + TestHash{"31d6cfe0d16ae931b73c59d7e0c089c0", ""}, + TestHash{"bde52cb31de33e46245e05fbdbd6fb24", "a"}, + TestHash{"a448017aaf21d8525fc10ae87aa6729d", "abc"}, + TestHash{"d9130a8164549fe818874806e1c7014b", "message digest"}, + TestHash{"d79e1c308aa5bbcdeea8ed63df412da9", "abcdefghijklmnopqrstuvwxyz"}, + TestHash{"043f8582f241db351ce627e153e7f0e4", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, + TestHash{"e33b4ddc9c38f2199c3e7b164fcc0536", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, + } + for v, _ in test_vectors { + computed := md4.hash(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_md5 :: proc(t: ^testing.T) { - // Official test vectors from https://datatracker.ietf.org/doc/html/rfc1321 - test_vectors := [?]TestHash { - TestHash{"d41d8cd98f00b204e9800998ecf8427e", ""}, - TestHash{"0cc175b9c0f1b6a831c399e269772661", "a"}, - TestHash{"900150983cd24fb0d6963f7d28e17f72", "abc"}, - TestHash{"f96b697d7cb7938d525a2f31aaf161d0", "message digest"}, - TestHash{"c3fcd3d76192e4007dfb496cca67e13b", "abcdefghijklmnopqrstuvwxyz"}, - TestHash{"d174ab98d277d9f5a5611c2c9f419d9f", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, - TestHash{"57edf4a22be3c955ac49da2e2107b67a", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, - } - for v, _ in test_vectors { - computed := md5.hash(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + // Official test vectors from https://datatracker.ietf.org/doc/html/rfc1321 + test_vectors := [?]TestHash { + TestHash{"d41d8cd98f00b204e9800998ecf8427e", ""}, + TestHash{"0cc175b9c0f1b6a831c399e269772661", "a"}, + TestHash{"900150983cd24fb0d6963f7d28e17f72", "abc"}, + TestHash{"f96b697d7cb7938d525a2f31aaf161d0", "message digest"}, + TestHash{"c3fcd3d76192e4007dfb496cca67e13b", "abcdefghijklmnopqrstuvwxyz"}, + TestHash{"d174ab98d277d9f5a5611c2c9f419d9f", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, + TestHash{"57edf4a22be3c955ac49da2e2107b67a", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, + } + for v, _ in test_vectors { + computed := md5.hash(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_sha1 :: proc(t: ^testing.T) { - // Test vectors from - // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf - // https://www.di-mgt.com.au/sha_testvectors.html - test_vectors := [?]TestHash { - TestHash{"da39a3ee5e6b4b0d3255bfef95601890afd80709", ""}, - TestHash{"a9993e364706816aba3e25717850c26c9cd0d89d", "abc"}, - TestHash{"f9537c23893d2014f365adf8ffe33b8eb0297ed1", "abcdbcdecdefdefgefghfghighijhi"}, - TestHash{"346fb528a24b48f563cb061470bcfd23740427ad", "jkijkljklmklmnlmnomnopnopq"}, - TestHash{"86f7e437faa5a7fce15d1ddcb9eaeaea377667b8", "a"}, - TestHash{"c729c8996ee0a6f74f4f3248e8957edf704fb624", "01234567012345670123456701234567"}, - TestHash{"84983e441c3bd26ebaae4aa1f95129e5e54670f1", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, - TestHash{"a49b2446a02c645bf419f995b67091253a04a259", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"}, - } - for v, _ in test_vectors { - computed := sha1.hash(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + // Test vectors from + // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf + // https://www.di-mgt.com.au/sha_testvectors.html + test_vectors := [?]TestHash { + TestHash{"da39a3ee5e6b4b0d3255bfef95601890afd80709", ""}, + TestHash{"a9993e364706816aba3e25717850c26c9cd0d89d", "abc"}, + TestHash{"f9537c23893d2014f365adf8ffe33b8eb0297ed1", "abcdbcdecdefdefgefghfghighijhi"}, + TestHash{"346fb528a24b48f563cb061470bcfd23740427ad", "jkijkljklmklmnlmnomnopnopq"}, + TestHash{"86f7e437faa5a7fce15d1ddcb9eaeaea377667b8", "a"}, + TestHash{"c729c8996ee0a6f74f4f3248e8957edf704fb624", "01234567012345670123456701234567"}, + TestHash{"84983e441c3bd26ebaae4aa1f95129e5e54670f1", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, + TestHash{"a49b2446a02c645bf419f995b67091253a04a259", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"}, + } + for v, _ in test_vectors { + computed := sha1.hash(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_sha224 :: proc(t: ^testing.T) { - // Test vectors from - // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf - // https://www.di-mgt.com.au/sha_testvectors.html - test_vectors := [?]TestHash { - TestHash{"d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f", ""}, - TestHash{"23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7", "abc"}, - TestHash{"75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, - TestHash{"c97ca9a559850ce97a04a96def6d99a9e0e0e2ab14e6b8df265fc0b3", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"}, - } - for v, _ in test_vectors { - computed := sha2.hash_224(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + // Test vectors from + // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf + // https://www.di-mgt.com.au/sha_testvectors.html + // https://datatracker.ietf.org/doc/html/rfc3874#section-3.3 + data_1_000_000_a := strings.repeat("a", 1_000_000) + test_vectors := [?]TestHash { + TestHash{"d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f", ""}, + TestHash{"23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7", "abc"}, + TestHash{"75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, + TestHash{"c97ca9a559850ce97a04a96def6d99a9e0e0e2ab14e6b8df265fc0b3", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"}, + TestHash{"20794655980c91d8bbb4c1ea97618a4bf03f42581948b2ee4ee7ad67", data_1_000_000_a}, + } + for v, _ in test_vectors { + computed := sha2.hash_224(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_sha256 :: proc(t: ^testing.T) { - // Test vectors from - // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf - // https://www.di-mgt.com.au/sha_testvectors.html - test_vectors := [?]TestHash { - TestHash{"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", ""}, - TestHash{"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", "abc"}, - TestHash{"248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, - TestHash{"cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"}, - } - for v, _ in test_vectors { - computed := sha2.hash_256(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + // Test vectors from + // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf + // https://www.di-mgt.com.au/sha_testvectors.html + test_vectors := [?]TestHash { + TestHash{"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", ""}, + TestHash{"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", "abc"}, + TestHash{"248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, + TestHash{"cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"}, + } + for v, _ in test_vectors { + computed := sha2.hash_256(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_sha384 :: proc(t: ^testing.T) { - // Test vectors from - // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf - // https://www.di-mgt.com.au/sha_testvectors.html - test_vectors := [?]TestHash { - TestHash{"38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b", ""}, - TestHash{"cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7", "abc"}, - TestHash{"3391fdddfc8dc7393707a65b1b4709397cf8b1d162af05abfe8f450de5f36bc6b0455a8520bc4e6f5fe95b1fe3c8452b", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, - TestHash{"09330c33f71147e83d192fc782cd1b4753111b173b3b05d22fa08086e3b0f712fcc7c71a557e2db966c3e9fa91746039", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"}, - } - for v, _ in test_vectors { - computed := sha2.hash_384(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + // Test vectors from + // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf + // https://www.di-mgt.com.au/sha_testvectors.html + test_vectors := [?]TestHash { + TestHash{"38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b", ""}, + TestHash{"cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7", "abc"}, + TestHash{"3391fdddfc8dc7393707a65b1b4709397cf8b1d162af05abfe8f450de5f36bc6b0455a8520bc4e6f5fe95b1fe3c8452b", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, + TestHash{"09330c33f71147e83d192fc782cd1b4753111b173b3b05d22fa08086e3b0f712fcc7c71a557e2db966c3e9fa91746039", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"}, + } + for v, _ in test_vectors { + computed := sha2.hash_384(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_sha512 :: proc(t: ^testing.T) { - // Test vectors from - // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf - // https://www.di-mgt.com.au/sha_testvectors.html - test_vectors := [?]TestHash { - TestHash{"cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e", ""}, - TestHash{"ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f", "abc"}, - TestHash{"204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, - TestHash{"8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"}, - } - for v, _ in test_vectors { - computed := sha2.hash_512(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + // Test vectors from + // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf + // https://www.di-mgt.com.au/sha_testvectors.html + test_vectors := [?]TestHash { + TestHash{"cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e", ""}, + TestHash{"ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f", "abc"}, + TestHash{"204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, + TestHash{"8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"}, + } + for v, _ in test_vectors { + computed := sha2.hash_512(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_sha3_224 :: proc(t: ^testing.T) { - // Test vectors from - // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf - // https://www.di-mgt.com.au/sha_testvectors.html - test_vectors := [?]TestHash { - TestHash{"6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7", ""}, - TestHash{"e642824c3f8cf24ad09234ee7d3c766fc9a3a5168d0c94ad73b46fdf", "abc"}, - TestHash{"10241ac5187380bd501192e4e56b5280908727dd8fe0d10d4e5ad91e", "abcdbcdecdefdefgefghfghighijhi"}, - TestHash{"fd645fe07d814c397e85e85f92fe58b949f55efa4d3468b2468da45a", "jkijkljklmklmnlmnomnopnopq"}, - TestHash{"9e86ff69557ca95f405f081269685b38e3a819b309ee942f482b6a8b", "a"}, - TestHash{"6961f694b2ff3ed6f0c830d2c66da0c5e7ca9445f7c0dca679171112", "01234567012345670123456701234567"}, - TestHash{"8a24108b154ada21c9fd5574494479ba5c7e7ab76ef264ead0fcce33", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, - TestHash{"543e6868e1666c1a643630df77367ae5a62a85070a51c14cbf665cbc", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"}, - } - for v, _ in test_vectors { - computed := sha3.hash_224(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + // Test vectors from + // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf + // https://www.di-mgt.com.au/sha_testvectors.html + test_vectors := [?]TestHash { + TestHash{"6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7", ""}, + TestHash{"e642824c3f8cf24ad09234ee7d3c766fc9a3a5168d0c94ad73b46fdf", "abc"}, + TestHash{"10241ac5187380bd501192e4e56b5280908727dd8fe0d10d4e5ad91e", "abcdbcdecdefdefgefghfghighijhi"}, + TestHash{"fd645fe07d814c397e85e85f92fe58b949f55efa4d3468b2468da45a", "jkijkljklmklmnlmnomnopnopq"}, + TestHash{"9e86ff69557ca95f405f081269685b38e3a819b309ee942f482b6a8b", "a"}, + TestHash{"6961f694b2ff3ed6f0c830d2c66da0c5e7ca9445f7c0dca679171112", "01234567012345670123456701234567"}, + TestHash{"8a24108b154ada21c9fd5574494479ba5c7e7ab76ef264ead0fcce33", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, + TestHash{"543e6868e1666c1a643630df77367ae5a62a85070a51c14cbf665cbc", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"}, + } + for v, _ in test_vectors { + computed := sha3.hash_224(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_sha3_256 :: proc(t: ^testing.T) { - // Test vectors from - // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf - // https://www.di-mgt.com.au/sha_testvectors.html - test_vectors := [?]TestHash { - TestHash{"a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a", ""}, - TestHash{"3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532", "abc"}, - TestHash{"565ada1ced21278cfaffdde00dea0107964121ac25e4e978abc59412be74550a", "abcdbcdecdefdefgefghfghighijhi"}, - TestHash{"8cc1709d520f495ce972ece48b0d2e1f74ec80d53bc5c47457142158fae15d98", "jkijkljklmklmnlmnomnopnopq"}, - TestHash{"80084bf2fba02475726feb2cab2d8215eab14bc6bdd8bfb2c8151257032ecd8b", "a"}, - TestHash{"e4786de5f88f7d374b7288f225ea9f2f7654da200bab5d417e1fb52d49202767", "01234567012345670123456701234567"}, - TestHash{"41c0dba2a9d6240849100376a8235e2c82e1b9998a999e21db32dd97496d3376", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, - TestHash{"916f6061fe879741ca6469b43971dfdb28b1a32dc36cb3254e812be27aad1d18", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"}, - } - for v, _ in test_vectors { - computed := sha3.hash_256(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + // Test vectors from + // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf + // https://www.di-mgt.com.au/sha_testvectors.html + test_vectors := [?]TestHash { + TestHash{"a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a", ""}, + TestHash{"3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532", "abc"}, + TestHash{"565ada1ced21278cfaffdde00dea0107964121ac25e4e978abc59412be74550a", "abcdbcdecdefdefgefghfghighijhi"}, + TestHash{"8cc1709d520f495ce972ece48b0d2e1f74ec80d53bc5c47457142158fae15d98", "jkijkljklmklmnlmnomnopnopq"}, + TestHash{"80084bf2fba02475726feb2cab2d8215eab14bc6bdd8bfb2c8151257032ecd8b", "a"}, + TestHash{"e4786de5f88f7d374b7288f225ea9f2f7654da200bab5d417e1fb52d49202767", "01234567012345670123456701234567"}, + TestHash{"41c0dba2a9d6240849100376a8235e2c82e1b9998a999e21db32dd97496d3376", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, + TestHash{"916f6061fe879741ca6469b43971dfdb28b1a32dc36cb3254e812be27aad1d18", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"}, + } + for v, _ in test_vectors { + computed := sha3.hash_256(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_sha3_384 :: proc(t: ^testing.T) { - // Test vectors from - // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf - // https://www.di-mgt.com.au/sha_testvectors.html - test_vectors := [?]TestHash { - TestHash{"0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2ac3713831264adb47fb6bd1e058d5f004", ""}, - TestHash{"ec01498288516fc926459f58e2c6ad8df9b473cb0fc08c2596da7cf0e49be4b298d88cea927ac7f539f1edf228376d25", "abc"}, - TestHash{"9aa92dbb716ebb573def0d5e3cdd28d6add38ada310b602b8916e690a3257b7144e5ddd3d0dbbc559c48480d34d57a9a", "abcdbcdecdefdefgefghfghighijhi"}, - TestHash{"77c90323d7392bcdee8a3e7f74f19f47b7d1b1a825ac6a2d8d882a72317879cc26597035f1fc24fe65090b125a691282", "jkijkljklmklmnlmnomnopnopq"}, - TestHash{"1815f774f320491b48569efec794d249eeb59aae46d22bf77dafe25c5edc28d7ea44f93ee1234aa88f61c91912a4ccd9", "a"}, - TestHash{"51072590ad4c51b27ff8265590d74f92de7cc55284168e414ca960087c693285b08a283c6b19d77632994cb9eb93f1be", "01234567012345670123456701234567"}, - TestHash{"991c665755eb3a4b6bbdfb75c78a492e8c56a22c5c4d7e429bfdbc32b9d4ad5aa04a1f076e62fea19eef51acd0657c22", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, - TestHash{"79407d3b5916b59c3e30b09822974791c313fb9ecc849e406f23592d04f625dc8c709b98b43b3852b337216179aa7fc7", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"}, - } - for v, _ in test_vectors { - computed := sha3.hash_384(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + // Test vectors from + // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf + // https://www.di-mgt.com.au/sha_testvectors.html + test_vectors := [?]TestHash { + TestHash{"0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2ac3713831264adb47fb6bd1e058d5f004", ""}, + TestHash{"ec01498288516fc926459f58e2c6ad8df9b473cb0fc08c2596da7cf0e49be4b298d88cea927ac7f539f1edf228376d25", "abc"}, + TestHash{"9aa92dbb716ebb573def0d5e3cdd28d6add38ada310b602b8916e690a3257b7144e5ddd3d0dbbc559c48480d34d57a9a", "abcdbcdecdefdefgefghfghighijhi"}, + TestHash{"77c90323d7392bcdee8a3e7f74f19f47b7d1b1a825ac6a2d8d882a72317879cc26597035f1fc24fe65090b125a691282", "jkijkljklmklmnlmnomnopnopq"}, + TestHash{"1815f774f320491b48569efec794d249eeb59aae46d22bf77dafe25c5edc28d7ea44f93ee1234aa88f61c91912a4ccd9", "a"}, + TestHash{"51072590ad4c51b27ff8265590d74f92de7cc55284168e414ca960087c693285b08a283c6b19d77632994cb9eb93f1be", "01234567012345670123456701234567"}, + TestHash{"991c665755eb3a4b6bbdfb75c78a492e8c56a22c5c4d7e429bfdbc32b9d4ad5aa04a1f076e62fea19eef51acd0657c22", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, + TestHash{"79407d3b5916b59c3e30b09822974791c313fb9ecc849e406f23592d04f625dc8c709b98b43b3852b337216179aa7fc7", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"}, + } + for v, _ in test_vectors { + computed := sha3.hash_384(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_sha3_512 :: proc(t: ^testing.T) { - // Test vectors from - // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf - // https://www.di-mgt.com.au/sha_testvectors.html - test_vectors := [?]TestHash { - TestHash{"a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26", ""}, - TestHash{"b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0", "abc"}, - TestHash{"9f9a327944a35988d67effc4fa748b3c07744f736ac70b479d8e12a3d10d6884d00a7ef593690305462e9e9030a67c51636fd346fd8fa0ee28a5ac2aee103d2e", "abcdbcdecdefdefgefghfghighijhi"}, - TestHash{"dbb124a0deda966eb4d199d0844fa0beb0770ea1ccddabcd335a7939a931ac6fb4fa6aebc6573f462ced2e4e7178277803be0d24d8bc2864626d9603109b7891", "jkijkljklmklmnlmnomnopnopq"}, - TestHash{"697f2d856172cb8309d6b8b97dac4de344b549d4dee61edfb4962d8698b7fa803f4f93ff24393586e28b5b957ac3d1d369420ce53332712f997bd336d09ab02a", "a"}, - TestHash{"5679e353bc8eeea3e801ca60448b249bcfd3ac4a6c3abe429a807bcbd4c9cd12da87a5a9dc74fde64c0d44718632cae966b078397c6f9ec155c6a238f2347cf1", "01234567012345670123456701234567"}, - TestHash{"04a371e84ecfb5b8b77cb48610fca8182dd457ce6f326a0fd3d7ec2f1e91636dee691fbe0c985302ba1b0d8dc78c086346b533b49c030d99a27daf1139d6e75e", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, - TestHash{"afebb2ef542e6579c50cad06d2e578f9f8dd6881d7dc824d26360feebf18a4fa73e3261122948efcfd492e74e82e2189ed0fb440d187f382270cb455f21dd185", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"}, - } - for v, _ in test_vectors { - computed := sha3.hash_512(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + // Test vectors from + // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf + // https://www.di-mgt.com.au/sha_testvectors.html + test_vectors := [?]TestHash { + TestHash{"a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26", ""}, + TestHash{"b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0", "abc"}, + TestHash{"9f9a327944a35988d67effc4fa748b3c07744f736ac70b479d8e12a3d10d6884d00a7ef593690305462e9e9030a67c51636fd346fd8fa0ee28a5ac2aee103d2e", "abcdbcdecdefdefgefghfghighijhi"}, + TestHash{"dbb124a0deda966eb4d199d0844fa0beb0770ea1ccddabcd335a7939a931ac6fb4fa6aebc6573f462ced2e4e7178277803be0d24d8bc2864626d9603109b7891", "jkijkljklmklmnlmnomnopnopq"}, + TestHash{"697f2d856172cb8309d6b8b97dac4de344b549d4dee61edfb4962d8698b7fa803f4f93ff24393586e28b5b957ac3d1d369420ce53332712f997bd336d09ab02a", "a"}, + TestHash{"5679e353bc8eeea3e801ca60448b249bcfd3ac4a6c3abe429a807bcbd4c9cd12da87a5a9dc74fde64c0d44718632cae966b078397c6f9ec155c6a238f2347cf1", "01234567012345670123456701234567"}, + TestHash{"04a371e84ecfb5b8b77cb48610fca8182dd457ce6f326a0fd3d7ec2f1e91636dee691fbe0c985302ba1b0d8dc78c086346b533b49c030d99a27daf1139d6e75e", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, + TestHash{"afebb2ef542e6579c50cad06d2e578f9f8dd6881d7dc824d26360feebf18a4fa73e3261122948efcfd492e74e82e2189ed0fb440d187f382270cb455f21dd185", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"}, + } + for v, _ in test_vectors { + computed := sha3.hash_512(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_shake_128 :: proc(t: ^testing.T) { - test_vectors := [?]TestHash { - TestHash{"7f9c2ba4e88f827d616045507605853e", ""}, - TestHash{"f4202e3c5852f9182a0430fd8144f0a7", "The quick brown fox jumps over the lazy dog"}, - TestHash{"853f4538be0db9621a6cea659a06c110", "The quick brown fox jumps over the lazy dof"}, - } - for v, _ in test_vectors { - computed := shake.hash_128(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + test_vectors := [?]TestHash { + TestHash{"7f9c2ba4e88f827d616045507605853e", ""}, + TestHash{"f4202e3c5852f9182a0430fd8144f0a7", "The quick brown fox jumps over the lazy dog"}, + TestHash{"853f4538be0db9621a6cea659a06c110", "The quick brown fox jumps over the lazy dof"}, + } + for v, _ in test_vectors { + computed := shake.hash_128(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_shake_256 :: proc(t: ^testing.T) { - test_vectors := [?]TestHash { - TestHash{"46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762f", ""}, - TestHash{"2f671343d9b2e1604dc9dcf0753e5fe15c7c64a0d283cbbf722d411a0e36f6ca", "The quick brown fox jumps over the lazy dog"}, - TestHash{"46b1ebb2e142c38b9ac9081bef72877fe4723959640fa57119b366ce6899d401", "The quick brown fox jumps over the lazy dof"}, - } - for v, _ in test_vectors { - computed := shake.hash_256(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + test_vectors := [?]TestHash { + TestHash{"46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762f", ""}, + TestHash{"2f671343d9b2e1604dc9dcf0753e5fe15c7c64a0d283cbbf722d411a0e36f6ca", "The quick brown fox jumps over the lazy dog"}, + TestHash{"46b1ebb2e142c38b9ac9081bef72877fe4723959640fa57119b366ce6899d401", "The quick brown fox jumps over the lazy dof"}, + } + for v, _ in test_vectors { + computed := shake.hash_256(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_keccak_224 :: proc(t: ^testing.T) { - // Test vectors from - // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf - // https://www.di-mgt.com.au/sha_testvectors.html - test_vectors := [?]TestHash { - TestHash{"f71837502ba8e10837bdd8d365adb85591895602fc552b48b7390abd", ""}, - TestHash{"c30411768506ebe1c2871b1ee2e87d38df342317300a9b97a95ec6a8", "abc"}, - } - for v, _ in test_vectors { - computed := keccak.hash_224(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + // Test vectors from + // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf + // https://www.di-mgt.com.au/sha_testvectors.html + test_vectors := [?]TestHash { + TestHash{"f71837502ba8e10837bdd8d365adb85591895602fc552b48b7390abd", ""}, + TestHash{"c30411768506ebe1c2871b1ee2e87d38df342317300a9b97a95ec6a8", "abc"}, + } + for v, _ in test_vectors { + computed := keccak.hash_224(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_keccak_256 :: proc(t: ^testing.T) { - // Test vectors from - // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf - // https://www.di-mgt.com.au/sha_testvectors.html - test_vectors := [?]TestHash { - TestHash{"c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", ""}, - TestHash{"4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45", "abc"}, - } - for v, _ in test_vectors { - computed := keccak.hash_256(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + // Test vectors from + // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf + // https://www.di-mgt.com.au/sha_testvectors.html + test_vectors := [?]TestHash { + TestHash{"c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", ""}, + TestHash{"4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45", "abc"}, + } + for v, _ in test_vectors { + computed := keccak.hash_256(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_keccak_384 :: proc(t: ^testing.T) { - // Test vectors from - // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf - // https://www.di-mgt.com.au/sha_testvectors.html - test_vectors := [?]TestHash { - TestHash{"2c23146a63a29acf99e73b88f8c24eaa7dc60aa771780ccc006afbfa8fe2479b2dd2b21362337441ac12b515911957ff", ""}, - TestHash{"f7df1165f033337be098e7d288ad6a2f74409d7a60b49c36642218de161b1f99f8c681e4afaf31a34db29fb763e3c28e", "abc"}, - } - for v, _ in test_vectors { - computed := keccak.hash_384(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + // Test vectors from + // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf + // https://www.di-mgt.com.au/sha_testvectors.html + test_vectors := [?]TestHash { + TestHash{"2c23146a63a29acf99e73b88f8c24eaa7dc60aa771780ccc006afbfa8fe2479b2dd2b21362337441ac12b515911957ff", ""}, + TestHash{"f7df1165f033337be098e7d288ad6a2f74409d7a60b49c36642218de161b1f99f8c681e4afaf31a34db29fb763e3c28e", "abc"}, + } + for v, _ in test_vectors { + computed := keccak.hash_384(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_keccak_512 :: proc(t: ^testing.T) { - // Test vectors from - // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf - // https://www.di-mgt.com.au/sha_testvectors.html - test_vectors := [?]TestHash { - TestHash{"0eab42de4c3ceb9235fc91acffe746b29c29a8c366b7c60e4e67c466f36a4304c00fa9caf9d87976ba469bcbe06713b435f091ef2769fb160cdab33d3670680e", ""}, - TestHash{"18587dc2ea106b9a1563e32b3312421ca164c7f1f07bc922a9c83d77cea3a1e5d0c69910739025372dc14ac9642629379540c17e2a65b19d77aa511a9d00bb96", "abc"}, - } - for v, _ in test_vectors { - computed := keccak.hash_512(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + // Test vectors from + // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf + // https://www.di-mgt.com.au/sha_testvectors.html + test_vectors := [?]TestHash { + TestHash{"0eab42de4c3ceb9235fc91acffe746b29c29a8c366b7c60e4e67c466f36a4304c00fa9caf9d87976ba469bcbe06713b435f091ef2769fb160cdab33d3670680e", ""}, + TestHash{"18587dc2ea106b9a1563e32b3312421ca164c7f1f07bc922a9c83d77cea3a1e5d0c69910739025372dc14ac9642629379540c17e2a65b19d77aa511a9d00bb96", "abc"}, + } + for v, _ in test_vectors { + computed := keccak.hash_512(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_whirlpool :: proc(t: ^testing.T) { - // Test vectors from - // https://web.archive.org/web/20171129084214/http://www.larc.usp.br/~pbarreto/WhirlpoolPage.html - test_vectors := [?]TestHash { - TestHash{"19fa61d75522a4669b44e39c1d2e1726c530232130d407f89afee0964997f7a73e83be698b288febcf88e3e03c4f0757ea8964e59b63d93708b138cc42a66eb3", ""}, - TestHash{"8aca2602792aec6f11a67206531fb7d7f0dff59413145e6973c45001d0087b42d11bc645413aeff63a42391a39145a591a92200d560195e53b478584fdae231a", "a"}, - TestHash{"33e24e6cbebf168016942df8a7174048f9cebc45cbd829c3b94b401a498acb11c5abcca7f2a1238aaf534371e87a4e4b19758965d5a35a7cad87cf5517043d97", "ab"}, - TestHash{"4e2448a4c6f486bb16b6562c73b4020bf3043e3a731bce721ae1b303d97e6d4c7181eebdb6c57e277d0e34957114cbd6c797fc9d95d8b582d225292076d4eef5", "abc"}, - TestHash{"bda164f0b930c43a1bacb5df880b205d15ac847add35145bf25d991ae74f0b72b1ac794f8aacda5fcb3c47038c954742b1857b5856519de4d1e54bfa2fa4eac5", "abcd"}, - TestHash{"5d745e26ccb20fe655d39c9e7f69455758fbae541cb892b3581e4869244ab35b4fd6078f5d28b1f1a217452a67d9801033d92724a221255a5e377fe9e9e5f0b2", "abcde"}, - TestHash{"a73e425459567308ba5f9eb2ae23570d0d0575eb1357ecf6ac88d4e0358b0ac3ea2371261f5d4c070211784b525911b9eec0ad968429bb7c7891d341cff4e811", "abcdef"}, - TestHash{"08b388f68fd3eb51906ac3d3c699b8e9c3ac65d7ceb49d2e34f8a482cbc3082bc401cead90e85a97b8647c948bf35e448740b79659f3bee42145f0bd653d1f25", "abcdefg"}, - TestHash{"1f1a84d30612820243afe2022712f9dac6d07c4c8bb41b40eacab0184c8d82275da5bcadbb35c7ca1960ff21c90acbae8c14e48d9309e4819027900e882c7ad9", "abcdefgh"}, - TestHash{"11882bc9a31ac1cf1c41dcd9fd6fdd3ccdb9b017fc7f4582680134f314d7bb49af4c71f5a920bc0a6a3c1ff9a00021bf361d9867fe636b0bc1da1552e4237de4", "abcdefghi"}, - TestHash{"717163de24809ffcf7ff6d5aba72b8d67c2129721953c252a4ddfb107614be857cbd76a9d5927de14633d6bdc9ddf335160b919db5c6f12cb2e6549181912eef", "abcdefghij"}, - TestHash{"b97de512e91e3828b40d2b0fdce9ceb3c4a71f9bea8d88e75c4fa854df36725fd2b52eb6544edcacd6f8beddfea403cb55ae31f03ad62a5ef54e42ee82c3fb35", "The quick brown fox jumps over the lazy dog"}, - TestHash{"c27ba124205f72e6847f3e19834f925cc666d0974167af915bb462420ed40cc50900d85a1f923219d832357750492d5c143011a76988344c2635e69d06f2d38c", "The quick brown fox jumps over the lazy eog"}, - } - for v, _ in test_vectors { - computed := whirlpool.hash(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + // Test vectors from + // https://web.archive.org/web/20171129084214/http://www.larc.usp.br/~pbarreto/WhirlpoolPage.html + test_vectors := [?]TestHash { + TestHash{"19fa61d75522a4669b44e39c1d2e1726c530232130d407f89afee0964997f7a73e83be698b288febcf88e3e03c4f0757ea8964e59b63d93708b138cc42a66eb3", ""}, + TestHash{"8aca2602792aec6f11a67206531fb7d7f0dff59413145e6973c45001d0087b42d11bc645413aeff63a42391a39145a591a92200d560195e53b478584fdae231a", "a"}, + TestHash{"33e24e6cbebf168016942df8a7174048f9cebc45cbd829c3b94b401a498acb11c5abcca7f2a1238aaf534371e87a4e4b19758965d5a35a7cad87cf5517043d97", "ab"}, + TestHash{"4e2448a4c6f486bb16b6562c73b4020bf3043e3a731bce721ae1b303d97e6d4c7181eebdb6c57e277d0e34957114cbd6c797fc9d95d8b582d225292076d4eef5", "abc"}, + TestHash{"bda164f0b930c43a1bacb5df880b205d15ac847add35145bf25d991ae74f0b72b1ac794f8aacda5fcb3c47038c954742b1857b5856519de4d1e54bfa2fa4eac5", "abcd"}, + TestHash{"5d745e26ccb20fe655d39c9e7f69455758fbae541cb892b3581e4869244ab35b4fd6078f5d28b1f1a217452a67d9801033d92724a221255a5e377fe9e9e5f0b2", "abcde"}, + TestHash{"a73e425459567308ba5f9eb2ae23570d0d0575eb1357ecf6ac88d4e0358b0ac3ea2371261f5d4c070211784b525911b9eec0ad968429bb7c7891d341cff4e811", "abcdef"}, + TestHash{"08b388f68fd3eb51906ac3d3c699b8e9c3ac65d7ceb49d2e34f8a482cbc3082bc401cead90e85a97b8647c948bf35e448740b79659f3bee42145f0bd653d1f25", "abcdefg"}, + TestHash{"1f1a84d30612820243afe2022712f9dac6d07c4c8bb41b40eacab0184c8d82275da5bcadbb35c7ca1960ff21c90acbae8c14e48d9309e4819027900e882c7ad9", "abcdefgh"}, + TestHash{"11882bc9a31ac1cf1c41dcd9fd6fdd3ccdb9b017fc7f4582680134f314d7bb49af4c71f5a920bc0a6a3c1ff9a00021bf361d9867fe636b0bc1da1552e4237de4", "abcdefghi"}, + TestHash{"717163de24809ffcf7ff6d5aba72b8d67c2129721953c252a4ddfb107614be857cbd76a9d5927de14633d6bdc9ddf335160b919db5c6f12cb2e6549181912eef", "abcdefghij"}, + TestHash{"b97de512e91e3828b40d2b0fdce9ceb3c4a71f9bea8d88e75c4fa854df36725fd2b52eb6544edcacd6f8beddfea403cb55ae31f03ad62a5ef54e42ee82c3fb35", "The quick brown fox jumps over the lazy dog"}, + TestHash{"c27ba124205f72e6847f3e19834f925cc666d0974167af915bb462420ed40cc50900d85a1f923219d832357750492d5c143011a76988344c2635e69d06f2d38c", "The quick brown fox jumps over the lazy eog"}, + } + for v, _ in test_vectors { + computed := whirlpool.hash(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_gost :: proc(t: ^testing.T) { - test_vectors := [?]TestHash { - TestHash{"981e5f3ca30c841487830f84fb433e13ac1101569b9c13584ac483234cd656c0", ""}, - TestHash{"e74c52dd282183bf37af0079c9f78055715a103f17e3133ceff1aacf2f403011", "a"}, - TestHash{"b285056dbf18d7392d7677369524dd14747459ed8143997e163b2986f92fd42c", "abc"}, - TestHash{"bc6041dd2aa401ebfa6e9886734174febdb4729aa972d60f549ac39b29721ba0", "message digest"}, - TestHash{"9004294a361a508c586fe53d1f1b02746765e71b765472786e4770d565830a76", "The quick brown fox jumps over the lazy dog"}, - TestHash{"73b70a39497de53a6e08c67b6d4db853540f03e9389299d9b0156ef7e85d0f61", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, - TestHash{"6bc7b38989b28cf93ae8842bf9d752905910a7528a61e5bce0782de43e610c90", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, - TestHash{"2cefc2f7b7bdc514e18ea57fa74ff357e7fa17d652c75f69cb1be7893ede48eb", "This is message, length=32 bytes"}, - TestHash{"c3730c5cbccacf915ac292676f21e8bd4ef75331d9405e5f1a61dc3130a65011", "Suppose the original message has length = 50 bytes"}, - } - for v, _ in test_vectors { - computed := gost.hash(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + test_vectors := [?]TestHash { + TestHash{"981e5f3ca30c841487830f84fb433e13ac1101569b9c13584ac483234cd656c0", ""}, + TestHash{"e74c52dd282183bf37af0079c9f78055715a103f17e3133ceff1aacf2f403011", "a"}, + TestHash{"b285056dbf18d7392d7677369524dd14747459ed8143997e163b2986f92fd42c", "abc"}, + TestHash{"bc6041dd2aa401ebfa6e9886734174febdb4729aa972d60f549ac39b29721ba0", "message digest"}, + TestHash{"9004294a361a508c586fe53d1f1b02746765e71b765472786e4770d565830a76", "The quick brown fox jumps over the lazy dog"}, + TestHash{"73b70a39497de53a6e08c67b6d4db853540f03e9389299d9b0156ef7e85d0f61", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, + TestHash{"6bc7b38989b28cf93ae8842bf9d752905910a7528a61e5bce0782de43e610c90", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, + TestHash{"2cefc2f7b7bdc514e18ea57fa74ff357e7fa17d652c75f69cb1be7893ede48eb", "This is message, length=32 bytes"}, + TestHash{"c3730c5cbccacf915ac292676f21e8bd4ef75331d9405e5f1a61dc3130a65011", "Suppose the original message has length = 50 bytes"}, + } + for v, _ in test_vectors { + computed := gost.hash(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_streebog_256 :: proc(t: ^testing.T) { - test_vectors := [?]TestHash { - TestHash{"3f539a213e97c802cc229d474c6aa32a825a360b2a933a949fd925208d9ce1bb", ""}, - TestHash{"3e7dea7f2384b6c5a3d0e24aaa29c05e89ddd762145030ec22c71a6db8b2c1f4", "The quick brown fox jumps over the lazy dog"}, - TestHash{"36816a824dcbe7d6171aa58500741f2ea2757ae2e1784ab72c5c3c6c198d71da", "The quick brown fox jumps over the lazy dog."}, - } - for v, _ in test_vectors { - computed := streebog.hash_256(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + test_vectors := [?]TestHash { + TestHash{"3f539a213e97c802cc229d474c6aa32a825a360b2a933a949fd925208d9ce1bb", ""}, + TestHash{"3e7dea7f2384b6c5a3d0e24aaa29c05e89ddd762145030ec22c71a6db8b2c1f4", "The quick brown fox jumps over the lazy dog"}, + TestHash{"36816a824dcbe7d6171aa58500741f2ea2757ae2e1784ab72c5c3c6c198d71da", "The quick brown fox jumps over the lazy dog."}, + } + for v, _ in test_vectors { + computed := streebog.hash_256(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_streebog_512 :: proc(t: ^testing.T) { - test_vectors := [?]TestHash { - TestHash{"8e945da209aa869f0455928529bcae4679e9873ab707b55315f56ceb98bef0a7362f715528356ee83cda5f2aac4c6ad2ba3a715c1bcd81cb8e9f90bf4c1c1a8a", ""}, - TestHash{"d2b793a0bb6cb5904828b5b6dcfb443bb8f33efc06ad09368878ae4cdc8245b97e60802469bed1e7c21a64ff0b179a6a1e0bb74d92965450a0adab69162c00fe", "The quick brown fox jumps over the lazy dog"}, - TestHash{"fe0c42f267d921f940faa72bd9fcf84f9f1bd7e9d055e9816e4c2ace1ec83be82d2957cd59b86e123d8f5adee80b3ca08a017599a9fc1a14d940cf87c77df070", "The quick brown fox jumps over the lazy dog."}, - } - for v, _ in test_vectors { - computed := streebog.hash_512(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + test_vectors := [?]TestHash { + TestHash{"8e945da209aa869f0455928529bcae4679e9873ab707b55315f56ceb98bef0a7362f715528356ee83cda5f2aac4c6ad2ba3a715c1bcd81cb8e9f90bf4c1c1a8a", ""}, + TestHash{"d2b793a0bb6cb5904828b5b6dcfb443bb8f33efc06ad09368878ae4cdc8245b97e60802469bed1e7c21a64ff0b179a6a1e0bb74d92965450a0adab69162c00fe", "The quick brown fox jumps over the lazy dog"}, + TestHash{"fe0c42f267d921f940faa72bd9fcf84f9f1bd7e9d055e9816e4c2ace1ec83be82d2957cd59b86e123d8f5adee80b3ca08a017599a9fc1a14d940cf87c77df070", "The quick brown fox jumps over the lazy dog."}, + } + for v, _ in test_vectors { + computed := streebog.hash_512(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_blake_224 :: proc(t: ^testing.T) { - test_vectors := [?]TestHash { - TestHash{"7dc5313b1c04512a174bd6503b89607aecbee0903d40a8a569c94eed", ""}, - TestHash{"304c27fdbf308aea06955e331adc6814223a21fccd24c09fde9eda7b", "ube"}, - TestHash{"cfb6848add73e1cb47994c4765df33b8f973702705a30a71fe4747a3", "BLAKE"}, - } - for v, _ in test_vectors { - computed := blake.hash_224(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + test_vectors := [?]TestHash { + TestHash{"7dc5313b1c04512a174bd6503b89607aecbee0903d40a8a569c94eed", ""}, + TestHash{"304c27fdbf308aea06955e331adc6814223a21fccd24c09fde9eda7b", "ube"}, + TestHash{"cfb6848add73e1cb47994c4765df33b8f973702705a30a71fe4747a3", "BLAKE"}, + } + for v, _ in test_vectors { + computed := blake.hash_224(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_blake_256 :: proc(t: ^testing.T) { - test_vectors := [?]TestHash { - TestHash{"716f6e863f744b9ac22c97ec7b76ea5f5908bc5b2f67c61510bfc4751384ea7a", ""}, - TestHash{"e802fe2a73fbe5853408f051d040aeb3a76a4d7a0fc5c3415d1af090f76a2c81", "ube"}, - TestHash{"07663e00cf96fbc136cf7b1ee099c95346ba3920893d18cc8851f22ee2e36aa6", "BLAKE"}, - } - for v, _ in test_vectors { - computed := blake.hash_256(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + test_vectors := [?]TestHash { + TestHash{"716f6e863f744b9ac22c97ec7b76ea5f5908bc5b2f67c61510bfc4751384ea7a", ""}, + TestHash{"e802fe2a73fbe5853408f051d040aeb3a76a4d7a0fc5c3415d1af090f76a2c81", "ube"}, + TestHash{"07663e00cf96fbc136cf7b1ee099c95346ba3920893d18cc8851f22ee2e36aa6", "BLAKE"}, + } + for v, _ in test_vectors { + computed := blake.hash_256(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_blake_384 :: proc(t: ^testing.T) { - test_vectors := [?]TestHash { - TestHash{"c6cbd89c926ab525c242e6621f2f5fa73aa4afe3d9e24aed727faaadd6af38b620bdb623dd2b4788b1c8086984af8706", ""}, - TestHash{"8f22f120b2b99dd4fd32b98c8c83bd87abd6413f7317be936b1997511247fc68ae781c6f42113224ccbc1567b0e88593", "ube"}, - TestHash{"f28742f7243990875d07e6afcff962edabdf7e9d19ddea6eae31d094c7fa6d9b00c8213a02ddf1e2d9894f3162345d85", "BLAKE"}, - } - for v, _ in test_vectors { - computed := blake.hash_384(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + test_vectors := [?]TestHash { + TestHash{"c6cbd89c926ab525c242e6621f2f5fa73aa4afe3d9e24aed727faaadd6af38b620bdb623dd2b4788b1c8086984af8706", ""}, + TestHash{"8f22f120b2b99dd4fd32b98c8c83bd87abd6413f7317be936b1997511247fc68ae781c6f42113224ccbc1567b0e88593", "ube"}, + TestHash{"f28742f7243990875d07e6afcff962edabdf7e9d19ddea6eae31d094c7fa6d9b00c8213a02ddf1e2d9894f3162345d85", "BLAKE"}, + } + for v, _ in test_vectors { + computed := blake.hash_384(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_blake_512 :: proc(t: ^testing.T) { - test_vectors := [?]TestHash { - TestHash{"a8cfbbd73726062df0c6864dda65defe58ef0cc52a5625090fa17601e1eecd1b628e94f396ae402a00acc9eab77b4d4c2e852aaaa25a636d80af3fc7913ef5b8", ""}, - TestHash{"49a24ca8f230936f938c19484d46b58f13ea4448ddadafecdf01419b1e1dd922680be2de84069187973ab61b10574da2ee50cbeaade68ea9391c8ec041b76be0", "ube"}, - TestHash{"7bf805d0d8de36802b882e65d0515aa7682a2be97a9d9ec1399f4be2eff7de07684d7099124c8ac81c1c7c200d24ba68c6222e75062e04feb0e9dd589aa6e3b7", "BLAKE"}, - } - for v, _ in test_vectors { - computed := blake.hash_512(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + test_vectors := [?]TestHash { + TestHash{"a8cfbbd73726062df0c6864dda65defe58ef0cc52a5625090fa17601e1eecd1b628e94f396ae402a00acc9eab77b4d4c2e852aaaa25a636d80af3fc7913ef5b8", ""}, + TestHash{"49a24ca8f230936f938c19484d46b58f13ea4448ddadafecdf01419b1e1dd922680be2de84069187973ab61b10574da2ee50cbeaade68ea9391c8ec041b76be0", "ube"}, + TestHash{"7bf805d0d8de36802b882e65d0515aa7682a2be97a9d9ec1399f4be2eff7de07684d7099124c8ac81c1c7c200d24ba68c6222e75062e04feb0e9dd589aa6e3b7", "BLAKE"}, + } + for v, _ in test_vectors { + computed := blake.hash_512(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_blake2b :: proc(t: ^testing.T) { - test_vectors := [?]TestHash { - TestHash{"786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce", ""}, - TestHash{"a8add4bdddfd93e4877d2746e62817b116364a1fa7bc148d95090bc7333b3673f82401cf7aa2e4cb1ecd90296e3f14cb5413f8ed77be73045b13914cdcd6a918", "The quick brown fox jumps over the lazy dog"}, - } - for v, _ in test_vectors { - computed := blake2b.hash(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + test_vectors := [?]TestHash { + TestHash{"786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce", ""}, + TestHash{"a8add4bdddfd93e4877d2746e62817b116364a1fa7bc148d95090bc7333b3673f82401cf7aa2e4cb1ecd90296e3f14cb5413f8ed77be73045b13914cdcd6a918", "The quick brown fox jumps over the lazy dog"}, + } + for v, _ in test_vectors { + computed := blake2b.hash(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_blake2s :: proc(t: ^testing.T) { - test_vectors := [?]TestHash { - TestHash{"69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9", ""}, - TestHash{"606beeec743ccbeff6cbcdf5d5302aa855c256c29b88c8ed331ea1a6bf3c8812", "The quick brown fox jumps over the lazy dog"}, - } - for v, _ in test_vectors { - computed := blake2s.hash(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + test_vectors := [?]TestHash { + TestHash{"69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9", ""}, + TestHash{"606beeec743ccbeff6cbcdf5d5302aa855c256c29b88c8ed331ea1a6bf3c8812", "The quick brown fox jumps over the lazy dog"}, + } + for v, _ in test_vectors { + computed := blake2s.hash(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_ripemd_128 :: proc(t: ^testing.T) { - // Test vectors from - // https://homes.esat.kuleuven.be/~bosselae/ripemd160.html - test_vectors := [?]TestHash { - TestHash{"cdf26213a150dc3ecb610f18f6b38b46", ""}, - TestHash{"86be7afa339d0fc7cfc785e72f578d33", "a"}, - TestHash{"c14a12199c66e4ba84636b0f69144c77", "abc"}, - TestHash{"9e327b3d6e523062afc1132d7df9d1b8", "message digest"}, - TestHash{"fd2aa607f71dc8f510714922b371834e", "abcdefghijklmnopqrstuvwxyz"}, - TestHash{"a1aa0689d0fafa2ddc22e88b49133a06", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, - TestHash{"d1e959eb179c911faea4624c60c5c702", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, - } - for v, _ in test_vectors { - computed := ripemd.hash_128(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + // Test vectors from + // https://homes.esat.kuleuven.be/~bosselae/ripemd160.html + test_vectors := [?]TestHash { + TestHash{"cdf26213a150dc3ecb610f18f6b38b46", ""}, + TestHash{"86be7afa339d0fc7cfc785e72f578d33", "a"}, + TestHash{"c14a12199c66e4ba84636b0f69144c77", "abc"}, + TestHash{"9e327b3d6e523062afc1132d7df9d1b8", "message digest"}, + TestHash{"fd2aa607f71dc8f510714922b371834e", "abcdefghijklmnopqrstuvwxyz"}, + TestHash{"a1aa0689d0fafa2ddc22e88b49133a06", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, + TestHash{"d1e959eb179c911faea4624c60c5c702", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, + } + for v, _ in test_vectors { + computed := ripemd.hash_128(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_ripemd_160 :: proc(t: ^testing.T) { - // Test vectors from - // https://homes.esat.kuleuven.be/~bosselae/ripemd160.html - test_vectors := [?]TestHash { - TestHash{"9c1185a5c5e9fc54612808977ee8f548b2258d31", ""}, - TestHash{"0bdc9d2d256b3ee9daae347be6f4dc835a467ffe", "a"}, - TestHash{"8eb208f7e05d987a9b044a8e98c6b087f15a0bfc", "abc"}, - TestHash{"5d0689ef49d2fae572b881b123a85ffa21595f36", "message digest"}, - TestHash{"f71c27109c692c1b56bbdceb5b9d2865b3708dbc", "abcdefghijklmnopqrstuvwxyz"}, - TestHash{"12a053384a9c0c88e405a06c27dcf49ada62eb2b", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, - TestHash{"b0e20b6e3116640286ed3a87a5713079b21f5189", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, - } - for v, _ in test_vectors { - computed := ripemd.hash_160(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + // Test vectors from + // https://homes.esat.kuleuven.be/~bosselae/ripemd160.html + test_vectors := [?]TestHash { + TestHash{"9c1185a5c5e9fc54612808977ee8f548b2258d31", ""}, + TestHash{"0bdc9d2d256b3ee9daae347be6f4dc835a467ffe", "a"}, + TestHash{"8eb208f7e05d987a9b044a8e98c6b087f15a0bfc", "abc"}, + TestHash{"5d0689ef49d2fae572b881b123a85ffa21595f36", "message digest"}, + TestHash{"f71c27109c692c1b56bbdceb5b9d2865b3708dbc", "abcdefghijklmnopqrstuvwxyz"}, + TestHash{"12a053384a9c0c88e405a06c27dcf49ada62eb2b", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, + TestHash{"b0e20b6e3116640286ed3a87a5713079b21f5189", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, + } + for v, _ in test_vectors { + computed := ripemd.hash_160(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_ripemd_256 :: proc(t: ^testing.T) { - // Test vectors from - // https://homes.esat.kuleuven.be/~bosselae/ripemd160.html - test_vectors := [?]TestHash { - TestHash{"02ba4c4e5f8ecd1877fc52d64d30e37a2d9774fb1e5d026380ae0168e3c5522d", ""}, - TestHash{"f9333e45d857f5d90a91bab70a1eba0cfb1be4b0783c9acfcd883a9134692925", "a"}, - TestHash{"afbd6e228b9d8cbbcef5ca2d03e6dba10ac0bc7dcbe4680e1e42d2e975459b65", "abc"}, - TestHash{"87e971759a1ce47a514d5c914c392c9018c7c46bc14465554afcdf54a5070c0e", "message digest"}, - TestHash{"649d3034751ea216776bf9a18acc81bc7896118a5197968782dd1fd97d8d5133", "abcdefghijklmnopqrstuvwxyz"}, - TestHash{"3843045583aac6c8c8d9128573e7a9809afb2a0f34ccc36ea9e72f16f6368e3f", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, - TestHash{"5740a408ac16b720b84424ae931cbb1fe363d1d0bf4017f1a89f7ea6de77a0b8", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, - } - for v, _ in test_vectors { - computed := ripemd.hash_256(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + // Test vectors from + // https://homes.esat.kuleuven.be/~bosselae/ripemd160.html + test_vectors := [?]TestHash { + TestHash{"02ba4c4e5f8ecd1877fc52d64d30e37a2d9774fb1e5d026380ae0168e3c5522d", ""}, + TestHash{"f9333e45d857f5d90a91bab70a1eba0cfb1be4b0783c9acfcd883a9134692925", "a"}, + TestHash{"afbd6e228b9d8cbbcef5ca2d03e6dba10ac0bc7dcbe4680e1e42d2e975459b65", "abc"}, + TestHash{"87e971759a1ce47a514d5c914c392c9018c7c46bc14465554afcdf54a5070c0e", "message digest"}, + TestHash{"649d3034751ea216776bf9a18acc81bc7896118a5197968782dd1fd97d8d5133", "abcdefghijklmnopqrstuvwxyz"}, + TestHash{"3843045583aac6c8c8d9128573e7a9809afb2a0f34ccc36ea9e72f16f6368e3f", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, + TestHash{"5740a408ac16b720b84424ae931cbb1fe363d1d0bf4017f1a89f7ea6de77a0b8", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, + } + for v, _ in test_vectors { + computed := ripemd.hash_256(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_ripemd_320 :: proc(t: ^testing.T) { - // Test vectors from - // https://homes.esat.kuleuven.be/~bosselae/ripemd160.html - test_vectors := [?]TestHash { - TestHash{"22d65d5661536cdc75c1fdf5c6de7b41b9f27325ebc61e8557177d705a0ec880151c3a32a00899b8", ""}, - TestHash{"ce78850638f92658a5a585097579926dda667a5716562cfcf6fbe77f63542f99b04705d6970dff5d", "a"}, - TestHash{"de4c01b3054f8930a79d09ae738e92301e5a17085beffdc1b8d116713e74f82fa942d64cdbc4682d", "abc"}, - TestHash{"3a8e28502ed45d422f68844f9dd316e7b98533fa3f2a91d29f84d425c88d6b4eff727df66a7c0197", "message digest"}, - TestHash{"cabdb1810b92470a2093aa6bce05952c28348cf43ff60841975166bb40ed234004b8824463e6b009", "abcdefghijklmnopqrstuvwxyz"}, - TestHash{"d034a7950cf722021ba4b84df769a5de2060e259df4c9bb4a4268c0e935bbc7470a969c9d072a1ac", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, - TestHash{"ed544940c86d67f250d232c30b7b3e5770e0c60c8cb9a4cafe3b11388af9920e1b99230b843c86a4", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, - } - for v, _ in test_vectors { - computed := ripemd.hash_320(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + // Test vectors from + // https://homes.esat.kuleuven.be/~bosselae/ripemd160.html + test_vectors := [?]TestHash { + TestHash{"22d65d5661536cdc75c1fdf5c6de7b41b9f27325ebc61e8557177d705a0ec880151c3a32a00899b8", ""}, + TestHash{"ce78850638f92658a5a585097579926dda667a5716562cfcf6fbe77f63542f99b04705d6970dff5d", "a"}, + TestHash{"de4c01b3054f8930a79d09ae738e92301e5a17085beffdc1b8d116713e74f82fa942d64cdbc4682d", "abc"}, + TestHash{"3a8e28502ed45d422f68844f9dd316e7b98533fa3f2a91d29f84d425c88d6b4eff727df66a7c0197", "message digest"}, + TestHash{"cabdb1810b92470a2093aa6bce05952c28348cf43ff60841975166bb40ed234004b8824463e6b009", "abcdefghijklmnopqrstuvwxyz"}, + TestHash{"d034a7950cf722021ba4b84df769a5de2060e259df4c9bb4a4268c0e935bbc7470a969c9d072a1ac", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, + TestHash{"ed544940c86d67f250d232c30b7b3e5770e0c60c8cb9a4cafe3b11388af9920e1b99230b843c86a4", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, + } + for v, _ in test_vectors { + computed := ripemd.hash_320(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_tiger_128 :: proc(t: ^testing.T) { - test_vectors := [?]TestHash { - TestHash{"3293ac630c13f0245f92bbb1766e1616", ""}, - TestHash{"77befbef2e7ef8ab2ec8f93bf587a7fc", "a"}, - TestHash{"2aab1484e8c158f2bfb8c5ff41b57a52", "abc"}, - TestHash{"d981f8cb78201a950dcf3048751e441c", "message digest"}, - TestHash{"1714a472eee57d30040412bfcc55032a", "abcdefghijklmnopqrstuvwxyz"}, - TestHash{"0f7bf9a19b9c58f2b7610df7e84f0ac3", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, - TestHash{"8dcea680a17583ee502ba38a3c368651", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, - TestHash{"1c14795529fd9f207a958f84c52f11e8", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, - TestHash{"6d12a41e72e644f017b6f0e2f7b44c62", "The quick brown fox jumps over the lazy dog"}, - } - for v, _ in test_vectors { - computed := tiger.hash_128(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + test_vectors := [?]TestHash { + TestHash{"3293ac630c13f0245f92bbb1766e1616", ""}, + TestHash{"77befbef2e7ef8ab2ec8f93bf587a7fc", "a"}, + TestHash{"2aab1484e8c158f2bfb8c5ff41b57a52", "abc"}, + TestHash{"d981f8cb78201a950dcf3048751e441c", "message digest"}, + TestHash{"1714a472eee57d30040412bfcc55032a", "abcdefghijklmnopqrstuvwxyz"}, + TestHash{"0f7bf9a19b9c58f2b7610df7e84f0ac3", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, + TestHash{"8dcea680a17583ee502ba38a3c368651", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, + TestHash{"1c14795529fd9f207a958f84c52f11e8", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, + TestHash{"6d12a41e72e644f017b6f0e2f7b44c62", "The quick brown fox jumps over the lazy dog"}, + } + for v, _ in test_vectors { + computed := tiger.hash_128(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_tiger_160 :: proc(t: ^testing.T) { - test_vectors := [?]TestHash { - TestHash{"3293ac630c13f0245f92bbb1766e16167a4e5849", ""}, - TestHash{"77befbef2e7ef8ab2ec8f93bf587a7fc613e247f", "a"}, - TestHash{"2aab1484e8c158f2bfb8c5ff41b57a525129131c", "abc"}, - TestHash{"d981f8cb78201a950dcf3048751e441c517fca1a", "message digest"}, - TestHash{"1714a472eee57d30040412bfcc55032a0b11602f", "abcdefghijklmnopqrstuvwxyz"}, - TestHash{"0f7bf9a19b9c58f2b7610df7e84f0ac3a71c631e", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, - TestHash{"8dcea680a17583ee502ba38a3c368651890ffbcc", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, - TestHash{"1c14795529fd9f207a958f84c52f11e887fa0cab", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, - TestHash{"6d12a41e72e644f017b6f0e2f7b44c6285f06dd5", "The quick brown fox jumps over the lazy dog"}, - } - for v, _ in test_vectors { - computed := tiger.hash_160(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + test_vectors := [?]TestHash { + TestHash{"3293ac630c13f0245f92bbb1766e16167a4e5849", ""}, + TestHash{"77befbef2e7ef8ab2ec8f93bf587a7fc613e247f", "a"}, + TestHash{"2aab1484e8c158f2bfb8c5ff41b57a525129131c", "abc"}, + TestHash{"d981f8cb78201a950dcf3048751e441c517fca1a", "message digest"}, + TestHash{"1714a472eee57d30040412bfcc55032a0b11602f", "abcdefghijklmnopqrstuvwxyz"}, + TestHash{"0f7bf9a19b9c58f2b7610df7e84f0ac3a71c631e", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, + TestHash{"8dcea680a17583ee502ba38a3c368651890ffbcc", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, + TestHash{"1c14795529fd9f207a958f84c52f11e887fa0cab", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, + TestHash{"6d12a41e72e644f017b6f0e2f7b44c6285f06dd5", "The quick brown fox jumps over the lazy dog"}, + } + for v, _ in test_vectors { + computed := tiger.hash_160(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_tiger_192 :: proc(t: ^testing.T) { - test_vectors := [?]TestHash { - TestHash{"3293ac630c13f0245f92bbb1766e16167a4e58492dde73f3", ""}, - TestHash{"77befbef2e7ef8ab2ec8f93bf587a7fc613e247f5f247809", "a"}, - TestHash{"2aab1484e8c158f2bfb8c5ff41b57a525129131c957b5f93", "abc"}, - TestHash{"d981f8cb78201a950dcf3048751e441c517fca1aa55a29f6", "message digest"}, - TestHash{"1714a472eee57d30040412bfcc55032a0b11602ff37beee9", "abcdefghijklmnopqrstuvwxyz"}, - TestHash{"0f7bf9a19b9c58f2b7610df7e84f0ac3a71c631e7b53f78e", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, - TestHash{"8dcea680a17583ee502ba38a3c368651890ffbccdc49a8cc", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, - TestHash{"1c14795529fd9f207a958f84c52f11e887fa0cabdfd91bfd", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, - TestHash{"6d12a41e72e644f017b6f0e2f7b44c6285f06dd5d2c5b075", "The quick brown fox jumps over the lazy dog"}, - } - for v, _ in test_vectors { - computed := tiger.hash_192(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + test_vectors := [?]TestHash { + TestHash{"3293ac630c13f0245f92bbb1766e16167a4e58492dde73f3", ""}, + TestHash{"77befbef2e7ef8ab2ec8f93bf587a7fc613e247f5f247809", "a"}, + TestHash{"2aab1484e8c158f2bfb8c5ff41b57a525129131c957b5f93", "abc"}, + TestHash{"d981f8cb78201a950dcf3048751e441c517fca1aa55a29f6", "message digest"}, + TestHash{"1714a472eee57d30040412bfcc55032a0b11602ff37beee9", "abcdefghijklmnopqrstuvwxyz"}, + TestHash{"0f7bf9a19b9c58f2b7610df7e84f0ac3a71c631e7b53f78e", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, + TestHash{"8dcea680a17583ee502ba38a3c368651890ffbccdc49a8cc", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, + TestHash{"1c14795529fd9f207a958f84c52f11e887fa0cabdfd91bfd", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, + TestHash{"6d12a41e72e644f017b6f0e2f7b44c6285f06dd5d2c5b075", "The quick brown fox jumps over the lazy dog"}, + } + for v, _ in test_vectors { + computed := tiger.hash_192(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_tiger2_128 :: proc(t: ^testing.T) { - test_vectors := [?]TestHash { - TestHash{"4441be75f6018773c206c22745374b92", ""}, - TestHash{"976abff8062a2e9dcea3a1ace966ed9c", "The quick brown fox jumps over the lazy dog"}, - TestHash{"09c11330283a27efb51930aa7dc1ec62", "The quick brown fox jumps over the lazy cog"}, - } - for v, _ in test_vectors { - computed := tiger2.hash_128(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + test_vectors := [?]TestHash { + TestHash{"4441be75f6018773c206c22745374b92", ""}, + TestHash{"976abff8062a2e9dcea3a1ace966ed9c", "The quick brown fox jumps over the lazy dog"}, + TestHash{"09c11330283a27efb51930aa7dc1ec62", "The quick brown fox jumps over the lazy cog"}, + } + for v, _ in test_vectors { + computed := tiger2.hash_128(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_tiger2_160 :: proc(t: ^testing.T) { - test_vectors := [?]TestHash { - TestHash{"4441be75f6018773c206c22745374b924aa8313f", ""}, - TestHash{"976abff8062a2e9dcea3a1ace966ed9c19cb8555", "The quick brown fox jumps over the lazy dog"}, - TestHash{"09c11330283a27efb51930aa7dc1ec624ff738a8", "The quick brown fox jumps over the lazy cog"}, - } - for v, _ in test_vectors { - computed := tiger2.hash_160(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + test_vectors := [?]TestHash { + TestHash{"4441be75f6018773c206c22745374b924aa8313f", ""}, + TestHash{"976abff8062a2e9dcea3a1ace966ed9c19cb8555", "The quick brown fox jumps over the lazy dog"}, + TestHash{"09c11330283a27efb51930aa7dc1ec624ff738a8", "The quick brown fox jumps over the lazy cog"}, + } + for v, _ in test_vectors { + computed := tiger2.hash_160(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_tiger2_192 :: proc(t: ^testing.T) { - test_vectors := [?]TestHash { - TestHash{"4441be75f6018773c206c22745374b924aa8313fef919f41", ""}, - TestHash{"976abff8062a2e9dcea3a1ace966ed9c19cb85558b4976d8", "The quick brown fox jumps over the lazy dog"}, - TestHash{"09c11330283a27efb51930aa7dc1ec624ff738a8d9bdd3df", "The quick brown fox jumps over the lazy cog"}, - } - for v, _ in test_vectors { - computed := tiger2.hash_192(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + test_vectors := [?]TestHash { + TestHash{"4441be75f6018773c206c22745374b924aa8313fef919f41", ""}, + TestHash{"976abff8062a2e9dcea3a1ace966ed9c19cb85558b4976d8", "The quick brown fox jumps over the lazy dog"}, + TestHash{"09c11330283a27efb51930aa7dc1ec624ff738a8d9bdd3df", "The quick brown fox jumps over the lazy cog"}, + } + for v, _ in test_vectors { + computed := tiger2.hash_192(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_sm3 :: proc(t: ^testing.T) { - test_vectors := [?]TestHash { - TestHash{"1ab21d8355cfa17f8e61194831e81a8f22bec8c728fefb747ed035eb5082aa2b", ""}, - TestHash{"66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0", "abc"}, - TestHash{"debe9ff92275b8a138604889c18e5a4d6fdb70e5387e5765293dcba39c0c5732", "abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd"}, - TestHash{"5fdfe814b8573ca021983970fc79b2218c9570369b4859684e2e4c3fc76cb8ea", "The quick brown fox jumps over the lazy dog"}, - TestHash{"ca27d14a42fc04c1e5ecf574a95a8c2d70ecb5805e9b429026ccac8f28b20098", "The quick brown fox jumps over the lazy cog"}, - } - for v, _ in test_vectors { - computed := sm3.hash(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + test_vectors := [?]TestHash { + TestHash{"1ab21d8355cfa17f8e61194831e81a8f22bec8c728fefb747ed035eb5082aa2b", ""}, + TestHash{"66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0", "abc"}, + TestHash{"debe9ff92275b8a138604889c18e5a4d6fdb70e5387e5765293dcba39c0c5732", "abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd"}, + TestHash{"5fdfe814b8573ca021983970fc79b2218c9570369b4859684e2e4c3fc76cb8ea", "The quick brown fox jumps over the lazy dog"}, + TestHash{"ca27d14a42fc04c1e5ecf574a95a8c2d70ecb5805e9b429026ccac8f28b20098", "The quick brown fox jumps over the lazy cog"}, + } + for v, _ in test_vectors { + computed := sm3.hash(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_jh_224 :: proc(t: ^testing.T) { - test_vectors := [?]TestHash { - TestHash{"2c99df889b019309051c60fecc2bd285a774940e43175b76b2626630", ""}, - TestHash{"e715f969fb61b203a97e494aab92d91a9cec52f0933436b0d63bf722", "a"}, - TestHash{"c2b1967e635bd55b6a4d36f863ac4a877be302251d68692873007281", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, - } - for v, _ in test_vectors { - computed := jh.hash_224(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + test_vectors := [?]TestHash { + TestHash{"2c99df889b019309051c60fecc2bd285a774940e43175b76b2626630", ""}, + TestHash{"e715f969fb61b203a97e494aab92d91a9cec52f0933436b0d63bf722", "a"}, + TestHash{"c2b1967e635bd55b6a4d36f863ac4a877be302251d68692873007281", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, + } + for v, _ in test_vectors { + computed := jh.hash_224(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_jh_256 :: proc(t: ^testing.T) { - test_vectors := [?]TestHash { - TestHash{"46e64619c18bb0a92a5e87185a47eef83ca747b8fcc8e1412921357e326df434", ""}, - TestHash{"d52c0c130a1bc0ae5136375637a52773e150c71efe1c968df8956f6745b05386", "a"}, - TestHash{"fc4214867025a8af94c614353b3553b10e561ae749fc18c40e5fd44a7a4ecd1b", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, - } - for v, _ in test_vectors { - computed := jh.hash_256(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + test_vectors := [?]TestHash { + TestHash{"46e64619c18bb0a92a5e87185a47eef83ca747b8fcc8e1412921357e326df434", ""}, + TestHash{"d52c0c130a1bc0ae5136375637a52773e150c71efe1c968df8956f6745b05386", "a"}, + TestHash{"fc4214867025a8af94c614353b3553b10e561ae749fc18c40e5fd44a7a4ecd1b", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, + } + for v, _ in test_vectors { + computed := jh.hash_256(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_jh_384 :: proc(t: ^testing.T) { - test_vectors := [?]TestHash { - TestHash{"2fe5f71b1b3290d3c017fb3c1a4d02a5cbeb03a0476481e25082434a881994b0ff99e078d2c16b105ad069b569315328", ""}, - TestHash{"77de897ca4fd5dadfbcbd1d8d4ea3c3c1426855e38661325853e92b069f3fe156729f6bbb9a5892c7c18a77f1cb9d0bb", "a"}, - TestHash{"6f73d9b9b8ed362f8180fb26020725b40bd6ca75b3b947405f26c4c37a885ce028876dc42e379d2faf6146fed3ea0e42", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, - } - for v, _ in test_vectors { - computed := jh.hash_384(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + test_vectors := [?]TestHash { + TestHash{"2fe5f71b1b3290d3c017fb3c1a4d02a5cbeb03a0476481e25082434a881994b0ff99e078d2c16b105ad069b569315328", ""}, + TestHash{"77de897ca4fd5dadfbcbd1d8d4ea3c3c1426855e38661325853e92b069f3fe156729f6bbb9a5892c7c18a77f1cb9d0bb", "a"}, + TestHash{"6f73d9b9b8ed362f8180fb26020725b40bd6ca75b3b947405f26c4c37a885ce028876dc42e379d2faf6146fed3ea0e42", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, + } + for v, _ in test_vectors { + computed := jh.hash_384(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_jh_512 :: proc(t: ^testing.T) { - test_vectors := [?]TestHash { - TestHash{"90ecf2f76f9d2c8017d979ad5ab96b87d58fc8fc4b83060f3f900774faa2c8fabe69c5f4ff1ec2b61d6b316941cedee117fb04b1f4c5bc1b919ae841c50eec4f", ""}, - TestHash{"f12c87e986daff17c481c81a99a39b603ca6bafcd320c5735523b97cb9a26f7681bad62ffad9aad0e21160a05f773fb0d1434ca4cbcb0483f480a171ada1561b", "a"}, - TestHash{"bafb8e710b35eabeb1a48220c4b0987c2c985b6e73b7b31d164bfb9d67c94d99d7bc43b474a25e647cd6cc36334b6a00a5f2a85fae74907fd2885c6168132fe7", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, - } - for v, _ in test_vectors { - computed := jh.hash_512(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + test_vectors := [?]TestHash { + TestHash{"90ecf2f76f9d2c8017d979ad5ab96b87d58fc8fc4b83060f3f900774faa2c8fabe69c5f4ff1ec2b61d6b316941cedee117fb04b1f4c5bc1b919ae841c50eec4f", ""}, + TestHash{"f12c87e986daff17c481c81a99a39b603ca6bafcd320c5735523b97cb9a26f7681bad62ffad9aad0e21160a05f773fb0d1434ca4cbcb0483f480a171ada1561b", "a"}, + TestHash{"bafb8e710b35eabeb1a48220c4b0987c2c985b6e73b7b31d164bfb9d67c94d99d7bc43b474a25e647cd6cc36334b6a00a5f2a85fae74907fd2885c6168132fe7", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, + } + for v, _ in test_vectors { + computed := jh.hash_512(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_groestl_224 :: proc(t: ^testing.T) { - test_vectors := [?]TestHash { - TestHash{"f2e180fb5947be964cd584e22e496242c6a329c577fc4ce8c36d34c3", ""}, - TestHash{"2dfa5bd326c23c451b1202d99e6cee98a98c45927e1a31077f538712", "a"}, - TestHash{"c8a3e7274d599900ae673419683c3626a2e49ed57308ed2687508bef", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, - } - for v, _ in test_vectors { - computed := groestl.hash_224(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + test_vectors := [?]TestHash { + TestHash{"f2e180fb5947be964cd584e22e496242c6a329c577fc4ce8c36d34c3", ""}, + TestHash{"2dfa5bd326c23c451b1202d99e6cee98a98c45927e1a31077f538712", "a"}, + TestHash{"c8a3e7274d599900ae673419683c3626a2e49ed57308ed2687508bef", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, + } + for v, _ in test_vectors { + computed := groestl.hash_224(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_groestl_256 :: proc(t: ^testing.T) { - test_vectors := [?]TestHash { - TestHash{"1a52d11d550039be16107f9c58db9ebcc417f16f736adb2502567119f0083467", ""}, - TestHash{"3645c245bb31223ad93c80885b719aa40b4bed0a9d9d6e7c11fe99e59ca350b5", "a"}, - TestHash{"2679d98913bee62e57fdbdde97ddb328373548c6b24fc587cc3d08f2a02a529c", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, - } - for v, _ in test_vectors { - computed := groestl.hash_256(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + test_vectors := [?]TestHash { + TestHash{"1a52d11d550039be16107f9c58db9ebcc417f16f736adb2502567119f0083467", ""}, + TestHash{"3645c245bb31223ad93c80885b719aa40b4bed0a9d9d6e7c11fe99e59ca350b5", "a"}, + TestHash{"2679d98913bee62e57fdbdde97ddb328373548c6b24fc587cc3d08f2a02a529c", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, + } + for v, _ in test_vectors { + computed := groestl.hash_256(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_groestl_384 :: proc(t: ^testing.T) { - test_vectors := [?]TestHash { - TestHash{"ac353c1095ace21439251007862d6c62f829ddbe6de4f78e68d310a9205a736d8b11d99bffe448f57a1cfa2934f044a5", ""}, - TestHash{"13fce7bd9fc69b67cc12c77e765a0a97794c585f89df39fbff32408e060d7d9225c7e80fd87da647686888bda896c342", "a"}, - TestHash{"1c446cd70a6de52c9db386f5305aae029fe5a4120bc6230b7cd3a5e1ef1949cc8e6d2548c24cd7347b5ba512628a62f6", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, - } - for v, _ in test_vectors { - computed := groestl.hash_384(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + test_vectors := [?]TestHash { + TestHash{"ac353c1095ace21439251007862d6c62f829ddbe6de4f78e68d310a9205a736d8b11d99bffe448f57a1cfa2934f044a5", ""}, + TestHash{"13fce7bd9fc69b67cc12c77e765a0a97794c585f89df39fbff32408e060d7d9225c7e80fd87da647686888bda896c342", "a"}, + TestHash{"1c446cd70a6de52c9db386f5305aae029fe5a4120bc6230b7cd3a5e1ef1949cc8e6d2548c24cd7347b5ba512628a62f6", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, + } + for v, _ in test_vectors { + computed := groestl.hash_384(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_groestl_512 :: proc(t: ^testing.T) { - test_vectors := [?]TestHash { - TestHash{"6d3ad29d279110eef3adbd66de2a0345a77baede1557f5d099fce0c03d6dc2ba8e6d4a6633dfbd66053c20faa87d1a11f39a7fbe4a6c2f009801370308fc4ad8", ""}, - TestHash{"9ef345a835ee35d6d0d462ce45f722d84b5ca41fde9c81a98a22cfb4f7425720511b03a258cdc055bf8e9179dc9bdb5d88bed906c71125d4cf0cd39d3d7bebc7", "a"}, - TestHash{"862849fd911852cd54beefa88759db4cead0ef8e36aaf15398303c5c4cbc016d9b4c42b32081cbdcba710d2693e7663d244fae116ec29ffb40168baf44f944e7", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, - } - for v, _ in test_vectors { - computed := groestl.hash_512(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + test_vectors := [?]TestHash { + TestHash{"6d3ad29d279110eef3adbd66de2a0345a77baede1557f5d099fce0c03d6dc2ba8e6d4a6633dfbd66053c20faa87d1a11f39a7fbe4a6c2f009801370308fc4ad8", ""}, + TestHash{"9ef345a835ee35d6d0d462ce45f722d84b5ca41fde9c81a98a22cfb4f7425720511b03a258cdc055bf8e9179dc9bdb5d88bed906c71125d4cf0cd39d3d7bebc7", "a"}, + TestHash{"862849fd911852cd54beefa88759db4cead0ef8e36aaf15398303c5c4cbc016d9b4c42b32081cbdcba710d2693e7663d244fae116ec29ffb40168baf44f944e7", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, + } + for v, _ in test_vectors { + computed := groestl.hash_512(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_haval_128 :: proc(t: ^testing.T) { - test_vectors_3 := [?]TestHash { - TestHash{"c68f39913f901f3ddf44c707357a7d70", ""}, - TestHash{"0cd40739683e15f01ca5dbceef4059f1", "a"}, - TestHash{"9e40ed883fb63e985d299b40cda2b8f2", "abc"}, - TestHash{"3caf4a79e81adcd6d1716bcc1cef4573", "message digest"}, - TestHash{"dc502247fb3eb8376109eda32d361d82", "abcdefghijklmnopqrstuvwxyz"}, - TestHash{"44068770868768964d1f2c3bff4aa3d8", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, - TestHash{"de5eb3f7d9eb08fae7a07d68e3047ec6", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, - } - for v, _ in test_vectors_3 { - computed := haval.hash_128_3(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } - test_vectors_4 := [?]TestHash { - TestHash{"ee6bbf4d6a46a679b3a856c88538bb98", ""}, - TestHash{"5cd07f03330c3b5020b29ba75911e17d", "a"}, - } - for v, _ in test_vectors_4 { - computed := haval.hash_128_4(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } - test_vectors_5 := [?]TestHash { - TestHash{"184b8482a0c050dca54b59c7f05bf5dd", ""}, - TestHash{"f23fbe704be8494bfa7a7fb4f8ab09e5", "a"}, - } - for v, _ in test_vectors_5 { - computed := haval.hash_128_5(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + test_vectors_3 := [?]TestHash { + TestHash{"c68f39913f901f3ddf44c707357a7d70", ""}, + TestHash{"0cd40739683e15f01ca5dbceef4059f1", "a"}, + TestHash{"9e40ed883fb63e985d299b40cda2b8f2", "abc"}, + TestHash{"3caf4a79e81adcd6d1716bcc1cef4573", "message digest"}, + TestHash{"dc502247fb3eb8376109eda32d361d82", "abcdefghijklmnopqrstuvwxyz"}, + TestHash{"44068770868768964d1f2c3bff4aa3d8", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, + TestHash{"de5eb3f7d9eb08fae7a07d68e3047ec6", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, + } + for v, _ in test_vectors_3 { + computed := haval.hash_128_3(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + test_vectors_4 := [?]TestHash { + TestHash{"ee6bbf4d6a46a679b3a856c88538bb98", ""}, + TestHash{"5cd07f03330c3b5020b29ba75911e17d", "a"}, + } + for v, _ in test_vectors_4 { + computed := haval.hash_128_4(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + test_vectors_5 := [?]TestHash { + TestHash{"184b8482a0c050dca54b59c7f05bf5dd", ""}, + TestHash{"f23fbe704be8494bfa7a7fb4f8ab09e5", "a"}, + } + for v, _ in test_vectors_5 { + computed := haval.hash_128_5(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_haval_160 :: proc(t: ^testing.T) { - test_vectors_3 := [?]TestHash { - TestHash{"d353c3ae22a25401d257643836d7231a9a95f953", ""}, - TestHash{"4da08f514a7275dbc4cece4a347385983983a830", "a"}, - TestHash{"b21e876c4d391e2a897661149d83576b5530a089", "abc"}, - TestHash{"43a47f6f1c016207f08be8115c0977bf155346da", "message digest"}, - TestHash{"eba9fa6050f24c07c29d1834a60900ea4e32e61b", "abcdefghijklmnopqrstuvwxyz"}, - TestHash{"c30bce448cf8cfe957c141e90c0a063497cdfeeb", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, - TestHash{"97dc988d97caae757be7523c4e8d4ea63007a4b9", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, - } - for v, _ in test_vectors_3 { - computed := haval.hash_160_3(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } - test_vectors_4 := [?]TestHash { - TestHash{"1d33aae1be4146dbaaca0b6e70d7a11f10801525", ""}, - TestHash{"e0a5be29627332034d4dd8a910a1a0e6fe04084d", "a"}, - } - for v, _ in test_vectors_4 { - computed := haval.hash_160_4(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } - test_vectors_5 := [?]TestHash { - TestHash{"255158cfc1eed1a7be7c55ddd64d9790415b933b", ""}, - TestHash{"f5147df7abc5e3c81b031268927c2b5761b5a2b5", "a"}, - } - for v, _ in test_vectors_5 { - computed := haval.hash_160_5(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + test_vectors_3 := [?]TestHash { + TestHash{"d353c3ae22a25401d257643836d7231a9a95f953", ""}, + TestHash{"4da08f514a7275dbc4cece4a347385983983a830", "a"}, + TestHash{"b21e876c4d391e2a897661149d83576b5530a089", "abc"}, + TestHash{"43a47f6f1c016207f08be8115c0977bf155346da", "message digest"}, + TestHash{"eba9fa6050f24c07c29d1834a60900ea4e32e61b", "abcdefghijklmnopqrstuvwxyz"}, + TestHash{"c30bce448cf8cfe957c141e90c0a063497cdfeeb", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, + TestHash{"97dc988d97caae757be7523c4e8d4ea63007a4b9", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, + } + for v, _ in test_vectors_3 { + computed := haval.hash_160_3(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + test_vectors_4 := [?]TestHash { + TestHash{"1d33aae1be4146dbaaca0b6e70d7a11f10801525", ""}, + TestHash{"e0a5be29627332034d4dd8a910a1a0e6fe04084d", "a"}, + } + for v, _ in test_vectors_4 { + computed := haval.hash_160_4(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + test_vectors_5 := [?]TestHash { + TestHash{"255158cfc1eed1a7be7c55ddd64d9790415b933b", ""}, + TestHash{"f5147df7abc5e3c81b031268927c2b5761b5a2b5", "a"}, + } + for v, _ in test_vectors_5 { + computed := haval.hash_160_5(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_haval_192 :: proc(t: ^testing.T) { - test_vectors_3 := [?]TestHash { - TestHash{"e9c48d7903eaf2a91c5b350151efcb175c0fc82de2289a4e", ""}, - TestHash{"b359c8835647f5697472431c142731ff6e2cddcacc4f6e08", "a"}, - } - for v, _ in test_vectors_3 { - computed := haval.hash_192_3(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } - test_vectors_4 := [?]TestHash { - TestHash{"4a8372945afa55c7dead800311272523ca19d42ea47b72da", ""}, - TestHash{"856c19f86214ea9a8a2f0c4b758b973cce72a2d8ff55505c", "a"}, - } - for v, _ in test_vectors_4 { - computed := haval.hash_192_4(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } - test_vectors_5 := [?]TestHash { - TestHash{"4839d0626f95935e17ee2fc4509387bbe2cc46cb382ffe85", ""}, - TestHash{"5ffa3b3548a6e2cfc06b7908ceb5263595df67cf9c4b9341", "a"}, - } - for v, _ in test_vectors_5 { - computed := haval.hash_192_5(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + test_vectors_3 := [?]TestHash { + TestHash{"e9c48d7903eaf2a91c5b350151efcb175c0fc82de2289a4e", ""}, + TestHash{"b359c8835647f5697472431c142731ff6e2cddcacc4f6e08", "a"}, + } + for v, _ in test_vectors_3 { + computed := haval.hash_192_3(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + test_vectors_4 := [?]TestHash { + TestHash{"4a8372945afa55c7dead800311272523ca19d42ea47b72da", ""}, + TestHash{"856c19f86214ea9a8a2f0c4b758b973cce72a2d8ff55505c", "a"}, + } + for v, _ in test_vectors_4 { + computed := haval.hash_192_4(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + test_vectors_5 := [?]TestHash { + TestHash{"4839d0626f95935e17ee2fc4509387bbe2cc46cb382ffe85", ""}, + TestHash{"5ffa3b3548a6e2cfc06b7908ceb5263595df67cf9c4b9341", "a"}, + } + for v, _ in test_vectors_5 { + computed := haval.hash_192_5(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_haval_224 :: proc(t: ^testing.T) { - test_vectors_3 := [?]TestHash { - TestHash{"c5aae9d47bffcaaf84a8c6e7ccacd60a0dd1932be7b1a192b9214b6d", ""}, - TestHash{"731814ba5605c59b673e4caae4ad28eeb515b3abc2b198336794e17b", "a"}, - } - for v, _ in test_vectors_3 { - computed := haval.hash_224_3(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } - test_vectors_4 := [?]TestHash { - TestHash{"3e56243275b3b81561750550e36fcd676ad2f5dd9e15f2e89e6ed78e", ""}, - TestHash{"742f1dbeeaf17f74960558b44f08aa98bdc7d967e6c0ab8f799b3ac1", "a"}, - } - for v, _ in test_vectors_4 { - computed := haval.hash_224_4(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } - test_vectors_5 := [?]TestHash { - TestHash{"4a0513c032754f5582a758d35917ac9adf3854219b39e3ac77d1837e", ""}, - TestHash{"67b3cb8d4068e3641fa4f156e03b52978b421947328bfb9168c7655d", "a"}, - } - for v, _ in test_vectors_5 { - computed := haval.hash_224_5(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + test_vectors_3 := [?]TestHash { + TestHash{"c5aae9d47bffcaaf84a8c6e7ccacd60a0dd1932be7b1a192b9214b6d", ""}, + TestHash{"731814ba5605c59b673e4caae4ad28eeb515b3abc2b198336794e17b", "a"}, + } + for v, _ in test_vectors_3 { + computed := haval.hash_224_3(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + test_vectors_4 := [?]TestHash { + TestHash{"3e56243275b3b81561750550e36fcd676ad2f5dd9e15f2e89e6ed78e", ""}, + TestHash{"742f1dbeeaf17f74960558b44f08aa98bdc7d967e6c0ab8f799b3ac1", "a"}, + } + for v, _ in test_vectors_4 { + computed := haval.hash_224_4(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + test_vectors_5 := [?]TestHash { + TestHash{"4a0513c032754f5582a758d35917ac9adf3854219b39e3ac77d1837e", ""}, + TestHash{"67b3cb8d4068e3641fa4f156e03b52978b421947328bfb9168c7655d", "a"}, + } + for v, _ in test_vectors_5 { + computed := haval.hash_224_5(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } } @(test) test_haval_256 :: proc(t: ^testing.T) { - test_vectors_3 := [?]TestHash { - TestHash{"4f6938531f0bc8991f62da7bbd6f7de3fad44562b8c6f4ebf146d5b4e46f7c17", ""}, - TestHash{"47c838fbb4081d9525a0ff9b1e2c05a98f625714e72db289010374e27db021d8", "a"}, - } - for v, _ in test_vectors_3 { - computed := haval.hash_256_3(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } - test_vectors_4 := [?]TestHash { - TestHash{"c92b2e23091e80e375dadce26982482d197b1a2521be82da819f8ca2c579b99b", ""}, - TestHash{"e686d2394a49b44d306ece295cf9021553221db132b36cc0ff5b593d39295899", "a"}, - } - for v, _ in test_vectors_4 { - computed := haval.hash_256_4(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } - test_vectors_5 := [?]TestHash { - TestHash{"be417bb4dd5cfb76c7126f4f8eeb1553a449039307b1a3cd451dbfdc0fbbe330", ""}, - TestHash{"de8fd5ee72a5e4265af0a756f4e1a1f65c9b2b2f47cf17ecf0d1b88679a3e22f", "a"}, - } - for v, _ in test_vectors_5 { - computed := haval.hash_256_5(v.str) - computed_str := hex_string(computed[:]) - expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) - } + test_vectors_3 := [?]TestHash { + TestHash{"4f6938531f0bc8991f62da7bbd6f7de3fad44562b8c6f4ebf146d5b4e46f7c17", ""}, + TestHash{"47c838fbb4081d9525a0ff9b1e2c05a98f625714e72db289010374e27db021d8", "a"}, + } + for v, _ in test_vectors_3 { + computed := haval.hash_256_3(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + test_vectors_4 := [?]TestHash { + TestHash{"c92b2e23091e80e375dadce26982482d197b1a2521be82da819f8ca2c579b99b", ""}, + TestHash{"e686d2394a49b44d306ece295cf9021553221db132b36cc0ff5b593d39295899", "a"}, + } + for v, _ in test_vectors_4 { + computed := haval.hash_256_4(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } + test_vectors_5 := [?]TestHash { + TestHash{"be417bb4dd5cfb76c7126f4f8eeb1553a449039307b1a3cd451dbfdc0fbbe330", ""}, + TestHash{"de8fd5ee72a5e4265af0a756f4e1a1f65c9b2b2f47cf17ecf0d1b88679a3e22f", "a"}, + } + for v, _ in test_vectors_5 { + computed := haval.hash_256_5(v.str) + computed_str := hex_string(computed[:]) + expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) + } +} + +@(test) +test_siphash_2_4 :: proc(t: ^testing.T) { + // Test vectors from + // https://github.com/veorq/SipHash/blob/master/vectors.h + test_vectors := [?]u64 { + 0x726fdb47dd0e0e31, 0x74f839c593dc67fd, 0x0d6c8009d9a94f5a, 0x85676696d7fb7e2d, + 0xcf2794e0277187b7, 0x18765564cd99a68d, 0xcbc9466e58fee3ce, 0xab0200f58b01d137, + 0x93f5f5799a932462, 0x9e0082df0ba9e4b0, 0x7a5dbbc594ddb9f3, 0xf4b32f46226bada7, + 0x751e8fbc860ee5fb, 0x14ea5627c0843d90, 0xf723ca908e7af2ee, 0xa129ca6149be45e5, + 0x3f2acc7f57c29bdb, 0x699ae9f52cbe4794, 0x4bc1b3f0968dd39c, 0xbb6dc91da77961bd, + 0xbed65cf21aa2ee98, 0xd0f2cbb02e3b67c7, 0x93536795e3a33e88, 0xa80c038ccd5ccec8, + 0xb8ad50c6f649af94, 0xbce192de8a85b8ea, 0x17d835b85bbb15f3, 0x2f2e6163076bcfad, + 0xde4daaaca71dc9a5, 0xa6a2506687956571, 0xad87a3535c49ef28, 0x32d892fad841c342, + 0x7127512f72f27cce, 0xa7f32346f95978e3, 0x12e0b01abb051238, 0x15e034d40fa197ae, + 0x314dffbe0815a3b4, 0x027990f029623981, 0xcadcd4e59ef40c4d, 0x9abfd8766a33735c, + 0x0e3ea96b5304a7d0, 0xad0c42d6fc585992, 0x187306c89bc215a9, 0xd4a60abcf3792b95, + 0xf935451de4f21df2, 0xa9538f0419755787, 0xdb9acddff56ca510, 0xd06c98cd5c0975eb, + 0xe612a3cb9ecba951, 0xc766e62cfcadaf96, 0xee64435a9752fe72, 0xa192d576b245165a, + 0x0a8787bf8ecb74b2, 0x81b3e73d20b49b6f, 0x7fa8220ba3b2ecea, 0x245731c13ca42499, + 0xb78dbfaf3a8d83bd, 0xea1ad565322a1a0b, 0x60e61c23a3795013, 0x6606d7e446282b93, + 0x6ca4ecb15c5f91e1, 0x9f626da15c9625f3, 0xe51b38608ef25f57, 0x958a324ceb064572, + } + + key: [16]byte + for i in 0..<16 { + key[i] = byte(i) + } + + for i in 0.. %v != %v", #procedure, filename, err, e)) + defer file_destroy(file) + + /* Header */ + tc.expect(t, file.magic_number == 0x417848, fmt.tprintf("%v: file.magic_number %v != %v", + #procedure, file.magic_number, 0x417848)) + tc.expect(t, file.version == 1, fmt.tprintf("%v: file.version %v != %v", + #procedure, file.version, 1)) + tc.expect(t, file.internal_node_count == 1, fmt.tprintf("%v: file.internal_node_count %v != %v", + #procedure, file.internal_node_count, 1)) + + /* Nodes (only one) */ + tc.expect(t, len(file.nodes) == 1, fmt.tprintf("%v: len(file.nodes) %v != %v", #procedure, len(file.nodes), 1)) + + m := &file.nodes[0].meta_data + tc.expect(t, len(m^) == 38, fmt.tprintf("%v: len(m^) %v != %v", #procedure, len(m^), 38)) + { + e :: "Texture resolution" + tc.expect(t, m[0].name == e, fmt.tprintf("%v: m[0].name %v != %v", #procedure, m[0].name, e)) + + m_v, m_v_ok := m[0].value.([]i64le) + tc.expect(t, m_v_ok, fmt.tprintf("%v: m_v_ok %v != %v", #procedure, m_v_ok, true)) + tc.expect(t, len(m_v) == 1, fmt.tprintf("%v: len(m_v) %v != %v", #procedure, len(m_v), 1)) + tc.expect(t, m_v[0] == 1024, fmt.tprintf("%v: m_v[0] %v != %v", #procedure, len(m_v), 1024)) + } + { + e :: "Validate" + tc.expect(t, m[37].name == e, fmt.tprintf("%v: m[37].name %v != %v", #procedure, m[37].name, e)) + + m_v, m_v_ok := m[37].value.([]i64le) + tc.expect(t, m_v_ok, fmt.tprintf("%v: m_v_ok %v != %v", #procedure, m_v_ok, true)) + tc.expect(t, len(m_v) == 1, fmt.tprintf("%v: len(m_v) %v != %v", #procedure, len(m_v), 1)) + tc.expect(t, m_v[0] == -2054847231, fmt.tprintf("%v: m_v[0] %v != %v", #procedure, len(m_v), -2054847231)) + } + + /* Node content */ + v, v_ok := file.nodes[0].content.(hxa.Node_Geometry) + tc.expect(t, v_ok, fmt.tprintf("%v: v_ok %v != %v", #procedure, v_ok, true)) + + tc.expect(t, v.vertex_count == 530, fmt.tprintf("%v: v.vertex_count %v != %v", #procedure, v.vertex_count, 530)) + tc.expect(t, v.edge_corner_count == 2026, fmt.tprintf("%v: v.edge_corner_count %v != %v", + #procedure, v.edge_corner_count, 2026)) + tc.expect(t, v.face_count == 517, fmt.tprintf("%v: v.face_count %v != %v", #procedure, v.face_count, 517)) + + /* Vertex stack */ + tc.expect(t, len(v.vertex_stack) == 1, fmt.tprintf("%v: len(v.vertex_stack) %v != %v", + #procedure, len(v.vertex_stack), 1)) + { + e := "vertex" + tc.expect(t, v.vertex_stack[0].name == e, fmt.tprintf("%v: v.vertex_stack[0].name %v != %v", + #procedure, v.vertex_stack[0].name, e)) + } + tc.expect(t, v.vertex_stack[0].components == 3, fmt.tprintf("%v: v.vertex_stack[0].components %v != %v", + #procedure, v.vertex_stack[0].components, 3)) + + /* Vertex stack data */ + vs_d, vs_d_ok := v.vertex_stack[0].data.([]f64le) + tc.expect(t, vs_d_ok, fmt.tprintf("%v: vs_d_ok %v != %v", #procedure, vs_d_ok, true)) + tc.expect(t, len(vs_d) == 1590, fmt.tprintf("%v: len(vs_d) %v != %v", #procedure, len(vs_d), 1590)) + + tc.expect(t, vs_d[0] == 4.06266, fmt.tprintf("%v: vs_d[0] %v (%h) != %v (%h)", + #procedure, vs_d[0], vs_d[0], 4.06266, 4.06266)) + tc.expect(t, vs_d[1] == 2.83457, fmt.tprintf("%v: vs_d[1] %v (%h) != %v (%h)", + #procedure, vs_d[1], vs_d[1], 2.83457, 2.83457)) + tc.expect(t, vs_d[2] == 0hbfbc5da6a4441787, fmt.tprintf("%v: vs_d[2] %v (%h) != %v (%h)", + #procedure, vs_d[2], vs_d[2], + 0hbfbc5da6a4441787, 0hbfbc5da6a4441787)) + tc.expect(t, vs_d[3] == 0h4010074fb549f948, fmt.tprintf("%v: vs_d[3] %v (%h) != %v (%h)", + #procedure, vs_d[3], vs_d[3], + 0h4010074fb549f948, 0h4010074fb549f948)) + tc.expect(t, vs_d[1587] == 0h400befa82e87d2c7, fmt.tprintf("%v: vs_d[1587] %v (%h) != %v (%h)", + #procedure, vs_d[1587], vs_d[1587], + 0h400befa82e87d2c7, 0h400befa82e87d2c7)) + tc.expect(t, vs_d[1588] == 2.83457, fmt.tprintf("%v: vs_d[1588] %v (%h) != %v (%h)", + #procedure, vs_d[1588], vs_d[1588], 2.83457, 2.83457)) + tc.expect(t, vs_d[1589] == -1.56121, fmt.tprintf("%v: vs_d[1589] %v (%h) != %v (%h)", + #procedure, vs_d[1589], vs_d[1589], -1.56121, -1.56121)) + + /* Corner stack */ + tc.expect(t, len(v.corner_stack) == 1, + fmt.tprintf("%v: len(v.corner_stack) %v != %v", #procedure, len(v.corner_stack), 1)) + { + e := "reference" + tc.expect(t, v.corner_stack[0].name == e, fmt.tprintf("%v: v.corner_stack[0].name %v != %v", + #procedure, v.corner_stack[0].name, e)) + } + tc.expect(t, v.corner_stack[0].components == 1, fmt.tprintf("%v: v.corner_stack[0].components %v != %v", + #procedure, v.corner_stack[0].components, 1)) + + /* Corner stack data */ + cs_d, cs_d_ok := v.corner_stack[0].data.([]i32le) + tc.expect(t, cs_d_ok, fmt.tprintf("%v: cs_d_ok %v != %v", #procedure, cs_d_ok, true)) + tc.expect(t, len(cs_d) == 2026, fmt.tprintf("%v: len(cs_d) %v != %v", #procedure, len(cs_d), 2026)) + tc.expect(t, cs_d[0] == 6, fmt.tprintf("%v: cs_d[0] %v != %v", #procedure, cs_d[0], 6)) + tc.expect(t, cs_d[2025] == -32, fmt.tprintf("%v: cs_d[2025] %v != %v", #procedure, cs_d[2025], -32)) + + /* Edge and face stacks (empty) */ + tc.expect(t, len(v.edge_stack) == 0, fmt.tprintf("%v: len(v.edge_stack) %v != %v", + #procedure, len(v.edge_stack), 0)) + tc.expect(t, len(v.face_stack) == 0, fmt.tprintf("%v: len(v.face_stack) %v != %v", + #procedure, len(v.face_stack), 0)) +} + +@test +test_write :: proc(t: ^testing.T) { + + using hxa + + n1 :Node + + n1_m1_value := []f64le{0.4, -1.23, 2341.6, -333.333} + n1_m1 := Meta{"m1", n1_m1_value} + + n1.meta_data = []Meta{n1_m1} + + n1_l1 := Layer{"l1", 2, []f32le{32.1, -41.3}} + n1_l2 := Layer{"l2", 3, []f64le{0.64, 1.64, -2.64}} + + n1_content := Node_Image{Image_Type.Image_1D, [3]u32le{1, 1, 2}, Layer_Stack{n1_l1, n1_l2}} + + n1.content = n1_content + + w_file :File + w_file.nodes = []Node{n1} + + required_size := required_write_size(w_file) + buf := make([]u8, required_size) + + n, write_err := write(buf, w_file) + write_e :: hxa.Write_Error.None + tc.expect(t, write_err == write_e, fmt.tprintf("%v: write_err %v != %v", #procedure, write_err, write_e)) + tc.expect(t, n == required_size, fmt.tprintf("%v: n %v != %v", #procedure, n, required_size)) + + file, read_err := read(buf) + read_e :: hxa.Read_Error.None + tc.expect(t, read_err == read_e, fmt.tprintf("%v: read_err %v != %v", #procedure, read_err, read_e)) + defer file_destroy(file) + + delete(buf) + + tc.expect(t, file.magic_number == 0x417848, fmt.tprintf("%v: file.magic_number %v != %v", + #procedure, file.magic_number, 0x417848)) + tc.expect(t, file.version == 3, fmt.tprintf("%v: file.version %v != %v", #procedure, file.version, 3)) + tc.expect(t, file.internal_node_count == 1, fmt.tprintf("%v: file.internal_node_count %v != %v", + #procedure, file.internal_node_count, 1)) + + tc.expect(t, len(file.nodes) == len(w_file.nodes), fmt.tprintf("%v: len(file.nodes) %v != %v", + #procedure, len(file.nodes), len(w_file.nodes))) + + m := &file.nodes[0].meta_data + w_m := &w_file.nodes[0].meta_data + tc.expect(t, len(m^) == len(w_m^), fmt.tprintf("%v: len(m^) %v != %v", #procedure, len(m^), len(w_m^))) + tc.expect(t, m[0].name == w_m[0].name, fmt.tprintf("%v: m[0].name %v != %v", #procedure, m[0].name, w_m[0].name)) + + m_v, m_v_ok := m[0].value.([]f64le) + tc.expect(t, m_v_ok, fmt.tprintf("%v: m_v_ok %v != %v", #procedure, m_v_ok, true)) + tc.expect(t, len(m_v) == len(n1_m1_value), fmt.tprintf("%v: %v != len(m_v) %v", + #procedure, len(m_v), len(n1_m1_value))) + for i := 0; i < len(m_v); i += 1 { + tc.expect(t, m_v[i] == n1_m1_value[i], fmt.tprintf("%v: m_v[%d] %v != %v", + #procedure, i, m_v[i], n1_m1_value[i])) + } + + v, v_ok := file.nodes[0].content.(hxa.Node_Image) + tc.expect(t, v_ok, fmt.tprintf("%v: v_ok %v != %v", #procedure, v_ok, true)) + tc.expect(t, v.type == n1_content.type, fmt.tprintf("%v: v.type %v != %v", #procedure, v.type, n1_content.type)) + tc.expect(t, len(v.resolution) == 3, fmt.tprintf("%v: len(v.resolution) %v != %v", + #procedure, len(v.resolution), 3)) + tc.expect(t, len(v.image_stack) == len(n1_content.image_stack), fmt.tprintf("%v: len(v.image_stack) %v != %v", + #procedure, len(v.image_stack), len(n1_content.image_stack))) + for i := 0; i < len(v.image_stack); i += 1 { + tc.expect(t, v.image_stack[i].name == n1_content.image_stack[i].name, + fmt.tprintf("%v: v.image_stack[%d].name %v != %v", + #procedure, i, v.image_stack[i].name, n1_content.image_stack[i].name)) + tc.expect(t, v.image_stack[i].components == n1_content.image_stack[i].components, + fmt.tprintf("%v: v.image_stack[%d].components %v != %v", + #procedure, i, v.image_stack[i].components, n1_content.image_stack[i].components)) + + switch n1_t in n1_content.image_stack[i].data { + case []u8: + tc.expect(t, false, fmt.tprintf("%v: n1_content.image_stack[i].data []u8", #procedure)) + case []i32le: + tc.expect(t, false, fmt.tprintf("%v: n1_content.image_stack[i].data []i32le", #procedure)) + case []f32le: + l, l_ok := v.image_stack[i].data.([]f32le) + tc.expect(t, l_ok, fmt.tprintf("%v: l_ok %v != %v", #procedure, l_ok, true)) + tc.expect(t, len(l) == len(n1_t), fmt.tprintf("%v: len(l) %v != %v", #procedure, len(l), len(n1_t))) + for j := 0; j < len(l); j += 1 { + tc.expect(t, l[j] == n1_t[j], fmt.tprintf("%v: l[%d] %v (%h) != %v (%h)", + #procedure, j, l[j], l[j], n1_t[j], n1_t[j])) + } + case []f64le: + l, l_ok := v.image_stack[i].data.([]f64le) + tc.expect(t, l_ok, fmt.tprintf("%v: l_ok %v != %v", #procedure, l_ok, true)) + tc.expect(t, len(l) == len(n1_t), fmt.tprintf("%v: len(l) %v != %v", #procedure, len(l), len(n1_t))) + for j := 0; j < len(l); j += 1 { + tc.expect(t, l[j] == n1_t[j], fmt.tprintf("%v: l[%d] %v != %v", #procedure, j, l[j], n1_t[j])) + } + } + } +} diff --git a/tests/core/encoding/json/test_core_json.odin b/tests/core/encoding/json/test_core_json.odin new file mode 100644 index 000000000..0e6a6412f --- /dev/null +++ b/tests/core/encoding/json/test_core_json.odin @@ -0,0 +1,347 @@ +package test_core_json + +import "core:encoding/json" +import "core:testing" +import "core:fmt" +import "core:os" + +TEST_count := 0 +TEST_fail := 0 + +when ODIN_TEST { + expect :: testing.expect + log :: testing.log +} else { + expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) { + TEST_count += 1 + if !condition { + TEST_fail += 1 + fmt.printf("[%v] %v\n", loc, message) + return + } + } + log :: proc(t: ^testing.T, v: any, loc := #caller_location) { + fmt.printf("[%v] ", loc) + fmt.printf("log: %v\n", v) + } +} + +main :: proc() { + t := testing.T{} + + parse_json(&t) + marshal_json(&t) + unmarshal_json(&t) + + fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count) + if TEST_fail > 0 { + os.exit(1) + } +} + +@test +parse_json :: proc(t: ^testing.T) { + + json_data := ` + { + "firstName": "John", + "lastName": "Smith", + "isAlive": true, + "age": 27, + "address": { + "streetAddress": "21 2nd Street", + "city": "New York", + "state": "NY", + "postalCode": "10021-3100" + }, + "phoneNumbers": [ + { + "type": "home", + "number": "212 555-1234" + }, + { + "type": "office", + "number": "646 555-4567" + } + ], + "children": [], + "spouse": null + } + ` + + _, err := json.parse(transmute([]u8)json_data) + + msg := fmt.tprintf("Expected `json.parse` to return nil, got %v", err) + expect(t, err == nil, msg) +} + +@test +marshal_json :: proc(t: ^testing.T) { + + My_Struct :: struct { + a: int, + b: int, + } + + my_struct := My_Struct { + a = 2, + b = 5, + } + + _, err := json.marshal(my_struct) + msg := fmt.tprintf("Expected `json.marshal` to return nil, got %v", err) + expect(t, err == nil, msg) +} + +PRODUCTS := ` +{ + "cash": "0", + "products": [ + { + "name": "Cog\nCola", + "cost": "3", + "owned": "1", + + "profit": "4", + "seconds": 3, + "multiplier": 1, + "auto_click": false + }, + { + "name": "gingerBeer", + "cost": "9", + "owned": "0", + + "profit": "16", + "seconds": 5, + "multiplier": 1, + "auto_click": false + }, + { + "name": "Coffee", + "cost": "27", + "owned": "0", + + "profit": "64", + "seconds": 7, + "multiplier": 1, + "auto_click": false + }, + { + "name": "Haggis", + "cost": "81", + "owned": "0", + + "profit": "256", + "seconds": 11, + "multiplier": 1, + "auto_click": false + }, + { + "name": "Lasagna", + "cost": "243", + "owned": "0", + + "profit": "1024", + "seconds": 13, + "multiplier": 1, + "auto_click": false + }, + { + "name": "Asparagus", + "cost": "729", + "owned": "0", + + "profit": "4096", + "seconds": 17, + "multiplier": 1, + "auto_click": false + }, + { + "name": "Yorkshire Pudding", + "cost": "2187", + "owned": "0", + + "profit": "16384", + "seconds": 19, + "multiplier": 1, + "auto_click": false + }, + { + "name": "Salmon Wrap", + "cost": "6561", + "owned": "0", + + "profit": "65536", + "seconds": 23, + "multiplier": 1, + "auto_click": false + }, + { + "name": "Poke Bowl", + "cost": "19683", + "owned": "0", + + "profit": "262144", + "seconds": 29, + "multiplier": 1, + "auto_click": false + }, + { + "name": "Chili Con Carne", + "cost": "59049", + "owned": "0", + + "profit": "1048576", + "seconds": 59, + "multiplier": 1, + "auto_click": false + }, + ], +} +` + +original_data := Game_Marshal{ + cash = "0", + products = { + { + name = "Cog\nCola", + cost = "3", + owned = "1", + profit = "4", + seconds = 3, + multiplier = 1, + auto_click = false, + }, + { + name = "gingerBeer", + cost = "9", + owned = "0", + profit = "16", + seconds = 5, + multiplier = 1, + auto_click = false, + }, + { + name = "Coffee", + cost = "27", + owned = "0", + profit = "64", + seconds = 7, + multiplier = 1, + auto_click = false, + }, + { + name = "Haggis", + cost = "81", + owned = "0", + profit = "256", + seconds = 11, + multiplier = 1, + auto_click = false, + }, + { + name = "Lasagna", + cost = "243", + owned = "0", + profit = "1024", + seconds = 13, + multiplier = 1, + auto_click = false, + }, + { + name = "Asparagus", + cost = "729", + owned = "0", + profit = "4096", + seconds = 17, + multiplier = 1, + auto_click = false, + }, + { + name = "Yorkshire Pudding", + cost = "2187", + owned = "0", + profit = "16384", + seconds = 19, + multiplier = 1, + auto_click = false, + }, + { + name = "Salmon Wrap", + cost = "6561", + owned = "0", + profit = "65536", + seconds = 23, + multiplier = 1, + auto_click = false, + }, + { + name = "Poke Bowl", + cost = "19683", + owned = "0", + profit = "262144", + seconds = 29, + multiplier = 1, + auto_click = false, + }, + { + name = "Chili Con Carne", + cost = "59049", + owned = "0", + profit = "1048576", + seconds = 59, + multiplier = 1, + auto_click = false, + }, + }, +} + +Product_Marshal :: struct { + name: cstring, + owned: string, + + cost: string, + + profit: string, + seconds: int, + multiplier: int, + + auto_click: bool, +} + +Game_Marshal :: struct { + cash: string, + products: []Product_Marshal, +} + +cleanup :: proc(g: Game_Marshal) { + for p in g.products { + delete(p.name) + delete(p.owned) + delete(p.cost) + delete(p.profit) + } + delete(g.products) + delete(g.cash) +} + +@test +unmarshal_json :: proc(t: ^testing.T) { + g: Game_Marshal + err := json.unmarshal(transmute([]u8)PRODUCTS, &g, json.DEFAULT_SPECIFICATION) + defer cleanup(g) + + msg := fmt.tprintf("Expected `json.unmarshal` to return nil, got %v", err) + expect(t, err == nil, msg) + + msg = fmt.tprintf("Expected %v products to have been unmarshaled, got %v", len(original_data.products), len(g.products)) + expect(t, len(g.products) == len(original_data.products), msg) + + msg = fmt.tprintf("Expected cash to have been unmarshaled as %v, got %v", original_data.cash, g.cash) + expect(t, original_data.cash == g.cash, msg) + + for p, i in g.products { + expect(t, p == original_data.products[i], "Producted unmarshaled improperly") + } +} \ No newline at end of file diff --git a/tests/core/encoding/test_core_json.odin b/tests/core/encoding/test_core_json.odin deleted file mode 100644 index f536eb4c6..000000000 --- a/tests/core/encoding/test_core_json.odin +++ /dev/null @@ -1,90 +0,0 @@ -package test_core_json - -import "core:encoding/json" -import "core:testing" -import "core:fmt" - -TEST_count := 0 -TEST_fail := 0 - -when ODIN_TEST { - expect :: testing.expect - log :: testing.log -} else { - expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) { - fmt.printf("[%v] ", loc) - TEST_count += 1 - if !condition { - TEST_fail += 1 - fmt.println(message) - return - } - fmt.println(" PASS") - } - log :: proc(t: ^testing.T, v: any, loc := #caller_location) { - fmt.printf("[%v] ", loc) - fmt.printf("log: %v\n", v) - } -} - -main :: proc() { - t := testing.T{} - - parse_json(&t) - marshal_json(&t) - - fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count) -} - -@test -parse_json :: proc(t: ^testing.T) { - - json_data := ` - { - "firstName": "John", - "lastName": "Smith", - "isAlive": true, - "age": 27, - "address": { - "streetAddress": "21 2nd Street", - "city": "New York", - "state": "NY", - "postalCode": "10021-3100" - }, - "phoneNumbers": [ - { - "type": "home", - "number": "212 555-1234" - }, - { - "type": "office", - "number": "646 555-4567" - } - ], - "children": [], - "spouse": null - } - ` - - _, err := json.parse(transmute([]u8)json_data) - - expect(t, err == .None, "expected json error to be none") -} - -@test -marshal_json :: proc(t: ^testing.T) { - - My_Struct :: struct { - a: int, - b: int, - } - - my_struct := My_Struct { - a = 2, - b = 5, - } - - _, err := json.marshal(my_struct) - - expect(t, err == .None, "expected json error to be none") -} diff --git a/tests/core/encoding/varint/test_core_varint.odin b/tests/core/encoding/varint/test_core_varint.odin new file mode 100644 index 000000000..ee1798aa7 --- /dev/null +++ b/tests/core/encoding/varint/test_core_varint.odin @@ -0,0 +1,156 @@ +package test_core_varint + +import "core:encoding/varint" +import "core:testing" +import "core:fmt" +import "core:os" +import "core:slice" +import "core:math/rand" + +TEST_count := 0 +TEST_fail := 0 + +RANDOM_TESTS :: 100 + +when ODIN_TEST { + expect :: testing.expect + log :: testing.log +} else { + expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) { + TEST_count += 1 + if !condition { + TEST_fail += 1 + fmt.printf("[%v] %v\n", loc, message) + return + } + } + log :: proc(t: ^testing.T, v: any, loc := #caller_location) { + fmt.printf("[%v] ", loc) + fmt.printf("log: %v\n", v) + } +} + +main :: proc() { + t := testing.T{} + + test_leb128(&t) + + fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count) + if TEST_fail > 0 { + os.exit(1) + } +} + +@(test) +test_leb128 :: proc(t: ^testing.T) { + buf: [varint.LEB128_MAX_BYTES]u8 + + for vector in ULEB_Vectors { + val, size, err := varint.decode_uleb128(vector.encoded) + + msg := fmt.tprintf("Expected %02x to decode to %v consuming %v bytes, got %v and %v", vector.encoded, vector.value, vector.size, val, size) + expect(t, size == vector.size && val == vector.value, msg) + + msg = fmt.tprintf("Expected decoder to return error %v, got %v for vector %v", vector.error, err, vector) + expect(t, err == vector.error, msg) + + if err == .None { // Try to roundtrip + size, err = varint.encode_uleb128(buf[:], vector.value) + + msg = fmt.tprintf("Expected %v to encode to %02x, got %02x", vector.value, vector.encoded, buf[:size]) + expect(t, size == vector.size && slice.simple_equal(vector.encoded, buf[:size]), msg) + } + } + + for vector in ILEB_Vectors { + val, size, err := varint.decode_ileb128(vector.encoded) + + msg := fmt.tprintf("Expected %02x to decode to %v consuming %v bytes, got %v and %v", vector.encoded, vector.value, vector.size, val, size) + expect(t, size == vector.size && val == vector.value, msg) + + msg = fmt.tprintf("Expected decoder to return error %v, got %v", vector.error, err) + expect(t, err == vector.error, msg) + + if err == .None { // Try to roundtrip + size, err = varint.encode_ileb128(buf[:], vector.value) + + msg = fmt.tprintf("Expected %v to encode to %02x, got %02x", vector.value, vector.encoded, buf[:size]) + expect(t, size == vector.size && slice.simple_equal(vector.encoded, buf[:size]), msg) + } + } + + for num_bytes in 1..=uint(16) { + for _ in 0..=RANDOM_TESTS { + unsigned, signed := get_random(num_bytes) + + { + encode_size, encode_err := varint.encode_uleb128(buf[:], unsigned) + msg := fmt.tprintf("%v failed to encode as an unsigned LEB128 value, got %v", unsigned, encode_err) + expect(t, encode_err == .None, msg) + + decoded, decode_size, decode_err := varint.decode_uleb128(buf[:]) + msg = fmt.tprintf("Expected %02x to decode as %v, got %v", buf[:encode_size], unsigned, decoded) + expect(t, decode_err == .None && decode_size == encode_size && decoded == unsigned, msg) + } + + { + encode_size, encode_err := varint.encode_ileb128(buf[:], signed) + msg := fmt.tprintf("%v failed to encode as a signed LEB128 value, got %v", signed, encode_err) + expect(t, encode_err == .None, msg) + + decoded, decode_size, decode_err := varint.decode_ileb128(buf[:]) + msg = fmt.tprintf("Expected %02x to decode as %v, got %v, err: %v", buf[:encode_size], signed, decoded, decode_err) + expect(t, decode_err == .None && decode_size == encode_size && decoded == signed, msg) + } + } + } +} + +get_random :: proc(byte_count: uint) -> (u: u128, i: i128) { + assert(byte_count >= 0 && byte_count <= size_of(u128)) + + for _ in 1..=byte_count { + u <<= 8 + u |= u128(rand.uint32() & 0xff) + } + + bias := i128(1 << (byte_count * 7)) - 1 + i = i128(u) - bias + + return +} + +ULEB_Test_Vector :: struct { + encoded: []u8, + value: u128, + size: int, + error: varint.Error, +} + +ULEB_Vectors :: []ULEB_Test_Vector{ + { []u8{0x00}, 0, 1, .None }, + { []u8{0x7f}, 127, 1, .None }, + { []u8{0xE5, 0x8E, 0x26}, 624485, 3, .None }, + { []u8{0x80}, 0, 0, .Buffer_Too_Small }, + { []u8{}, 0, 0, .Buffer_Too_Small }, + + { []u8{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03}, max(u128), 19, .None }, +} + +ILEB_Test_Vector :: struct { + encoded: []u8, + value: i128, + size: int, + error: varint.Error, +} + +ILEB_Vectors :: []ILEB_Test_Vector{ + { []u8{0x00}, 0, 1, .None }, + { []u8{0x3f}, 63, 1, .None }, + { []u8{0x40}, -64, 1, .None }, + { []u8{0xC0, 0xBB, 0x78}, -123456, 3, .None }, + { []u8{}, 0, 0, .Buffer_Too_Small }, + + { []u8{0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7e}, min(i128), 19, .None }, + { []u8{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01}, max(i128), 19, .None }, +} \ No newline at end of file diff --git a/tests/core/encoding/xml/test_core_xml.odin b/tests/core/encoding/xml/test_core_xml.odin new file mode 100644 index 000000000..3cfc75a65 --- /dev/null +++ b/tests/core/encoding/xml/test_core_xml.odin @@ -0,0 +1,342 @@ +package test_core_xml + +import "core:encoding/xml" +import "core:testing" +import "core:mem" +import "core:strings" +import "core:io" +import "core:fmt" +import "core:hash" + +Silent :: proc(pos: xml.Pos, format: string, args: ..any) {} + +OPTIONS :: xml.Options{ flags = { .Ignore_Unsupported, .Intern_Comments, }, + expected_doctype = "", +} + +TEST_count := 0 +TEST_fail := 0 + +TEST :: struct { + filename: string, + options: xml.Options, + err: xml.Error, + crc32: u32, +} + +/* + Relative to ODIN_ROOT +*/ +TEST_FILE_PATH_PREFIX :: "tests/core/assets" + +TESTS :: []TEST{ + /* + First we test that certain files parse without error. + */ + + { + /* + Tests UTF-8 idents and values. + Test namespaced ident. + Tests that nested partial CDATA start doesn't trip up parser. + */ + filename = "XML/utf8.xml", + options = { + flags = { + .Ignore_Unsupported, .Intern_Comments, + }, + expected_doctype = "恥ずべきフクロウ", + }, + crc32 = 0x30d82264, + }, + + { + /* + Same as above. + Unbox CDATA in data tag. + */ + filename = "XML/utf8.xml", + options = { + flags = { + .Ignore_Unsupported, .Intern_Comments, .Unbox_CDATA, + }, + expected_doctype = "恥ずべきフクロウ", + }, + crc32 = 0xad31d8e8, + }, + + { + /* + Simple Qt TS translation file. + `core:i18n` requires it to be parsed properly. + */ + filename = "I18N/nl_NL-qt-ts.ts", + options = { + flags = { + .Ignore_Unsupported, .Intern_Comments, .Unbox_CDATA, .Decode_SGML_Entities, + }, + expected_doctype = "TS", + }, + crc32 = 0x7bce2630, + }, + + { + /* + Simple XLiff 1.2 file. + `core:i18n` requires it to be parsed properly. + */ + filename = "I18N/nl_NL-xliff-1.2.xliff", + options = { + flags = { + .Ignore_Unsupported, .Intern_Comments, .Unbox_CDATA, .Decode_SGML_Entities, + }, + expected_doctype = "xliff", + }, + crc32 = 0x43f19d61, + }, + + { + /* + Simple XLiff 2.0 file. + `core:i18n` requires it to be parsed properly. + */ + filename = "I18N/nl_NL-xliff-2.0.xliff", + options = { + flags = { + .Ignore_Unsupported, .Intern_Comments, .Unbox_CDATA, .Decode_SGML_Entities, + }, + expected_doctype = "xliff", + }, + crc32 = 0x961e7635, + }, + + { + filename = "XML/entities.html", + options = { + flags = { + .Ignore_Unsupported, .Intern_Comments, + }, + expected_doctype = "html", + }, + crc32 = 0x573c1033, + }, + + { + filename = "XML/entities.html", + options = { + flags = { + .Ignore_Unsupported, .Intern_Comments, .Unbox_CDATA, + }, + expected_doctype = "html", + }, + crc32 = 0x82588917, + }, + + { + filename = "XML/entities.html", + options = { + flags = { + .Ignore_Unsupported, .Intern_Comments, .Unbox_CDATA, .Decode_SGML_Entities, + }, + expected_doctype = "html", + }, + crc32 = 0x5e74d8a6, + }, + + /* + Then we test that certain errors are returned as expected. + */ + { + filename = "XML/utf8.xml", + options = { + flags = { + .Ignore_Unsupported, .Intern_Comments, + }, + expected_doctype = "Odin", + }, + err = .Invalid_DocType, + crc32 = 0x49b83d0a, + }, + + /* + Parse the 8.2 MiB unicode.xml for good measure. + */ + { + filename = "XML/unicode.xml", + options = { + flags = { + .Ignore_Unsupported, + }, + expected_doctype = "", + }, + err = .None, + crc32 = 0xcaa042b9, + }, +} + +when ODIN_TEST { + expect :: testing.expect + log :: testing.log +} else { + expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) { + TEST_count += 1 + if !condition { + TEST_fail += 1 + fmt.printf("[%v] %v\n", loc, message) + return + } + } + log :: proc(t: ^testing.T, v: any, loc := #caller_location) { + fmt.printf("[%v] LOG:\n\t%v\n", loc, v) + } +} + +test_file_path :: proc(filename: string) -> (path: string) { + + path = fmt.tprintf("%v%v/%v", ODIN_ROOT, TEST_FILE_PATH_PREFIX, filename) + temp := transmute([]u8)path + + for r, i in path { + if r == '\\' { + temp[i] = '/' + } + } + return path +} + +doc_to_string :: proc(doc: ^xml.Document) -> (result: string) { + /* + Effectively a clone of the debug printer in the xml package. + We duplicate it here so that the way it prints an XML document to a string is stable. + + This way we can hash the output. If it changes, it means that the document or how it was parsed changed, + not how it was printed. One less source of variability. + */ + print :: proc(writer: io.Writer, doc: ^xml.Document) -> (written: int, err: io.Error) { + if doc == nil { return } + using fmt + + written += wprintf(writer, "[XML Prolog]\n") + + for attr in doc.prologue { + written += wprintf(writer, "\t%v: %v\n", attr.key, attr.val) + } + + written += wprintf(writer, "[Encoding] %v\n", doc.encoding) + + if len(doc.doctype.ident) > 0 { + written += wprintf(writer, "[DOCTYPE] %v\n", doc.doctype.ident) + + if len(doc.doctype.rest) > 0 { + wprintf(writer, "\t%v\n", doc.doctype.rest) + } + } + + for comment in doc.comments { + written += wprintf(writer, "[Pre-root comment] %v\n", comment) + } + + if doc.element_count > 0 { + wprintln(writer, " --- ") + print_element(writer, doc, 0) + wprintln(writer, " --- ") + } + + return written, .None + } + + print_element :: proc(writer: io.Writer, doc: ^xml.Document, element_id: xml.Element_ID, indent := 0) -> (written: int, err: io.Error) { + using fmt + + tab :: proc(writer: io.Writer, indent: int) { + for _ in 0..=indent { + wprintf(writer, "\t") + } + } + + tab(writer, indent) + + element := doc.elements[element_id] + + if element.kind == .Element { + wprintf(writer, "<%v>\n", element.ident) + if len(element.value) > 0 { + tab(writer, indent + 1) + wprintf(writer, "[Value] %v\n", element.value) + } + + for attr in element.attribs { + tab(writer, indent + 1) + wprintf(writer, "[Attr] %v: %v\n", attr.key, attr.val) + } + + for child in element.children { + print_element(writer, doc, child, indent + 1) + } + } else if element.kind == .Comment { + wprintf(writer, "[COMMENT] %v\n", element.value) + } + + return written, .None + } + + buf: strings.Builder + defer strings.builder_destroy(&buf) + + print(strings.to_writer(&buf), doc) + return strings.clone(strings.to_string(buf)) +} + +@test +run_tests :: proc(t: ^testing.T) { + using fmt + + for test in TESTS { + path := test_file_path(test.filename) + log(t, fmt.tprintf("Trying to parse %v", path)) + + doc, err := xml.load_from_file(path, test.options, Silent) + defer xml.destroy(doc) + + tree_string := doc_to_string(doc) + tree_bytes := transmute([]u8)tree_string + defer delete(tree_bytes) + + crc32 := hash.crc32(tree_bytes) + + failed := err != test.err + err_msg := tprintf("Expected return value %v, got %v", test.err, err) + expect(t, err == test.err, err_msg) + + failed |= crc32 != test.crc32 + err_msg = tprintf("Expected CRC 0x%08x, got 0x%08x, with options %v", test.crc32, crc32, test.options) + expect(t, crc32 == test.crc32, err_msg) + + if failed { + /* + Don't fully print big trees. + */ + tree_string = tree_string[:min(2_048, len(tree_string))] + println(tree_string) + } + } +} + +main :: proc() { + t := testing.T{} + + track: mem.Tracking_Allocator + mem.tracking_allocator_init(&track, context.allocator) + context.allocator = mem.tracking_allocator(&track) + + run_tests(&t) + + if len(track.allocation_map) > 0 { + for _, v in track.allocation_map { + err_msg := fmt.tprintf("%v Leaked %v bytes.", v.location, v.size) + expect(&t, false, err_msg) + } + } + + fmt.printf("\n%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count) +} \ No newline at end of file diff --git a/tests/core/hash/test_core_hash.odin b/tests/core/hash/test_core_hash.odin index 8baa604b6..e69490143 100644 --- a/tests/core/hash/test_core_hash.odin +++ b/tests/core/hash/test_core_hash.odin @@ -5,6 +5,9 @@ import "core:hash" import "core:time" import "core:testing" import "core:fmt" +import "core:os" +import "core:math/rand" +import "core:intrinsics" TEST_count := 0 TEST_fail := 0 @@ -14,14 +17,12 @@ when ODIN_TEST { log :: testing.log } else { expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) { - fmt.printf("[%v] ", loc) TEST_count += 1 if !condition { TEST_fail += 1 - fmt.println(" FAIL:", message) + fmt.printf("[%v] %v\n", loc, message) return } - fmt.println(" PASS") } log :: proc(t: ^testing.T, v: any, loc := #caller_location) { fmt.printf("[%v] ", loc) @@ -32,9 +33,14 @@ when ODIN_TEST { main :: proc() { t := testing.T{} test_benchmark_runner(&t) - test_xxhash_vectors(&t) test_crc64_vectors(&t) + test_xxhash_vectors(&t) + test_xxhash_large(&t) + fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count) + if TEST_fail > 0 { + os.exit(1) + } } /* @@ -189,6 +195,104 @@ test_benchmark_runner :: proc(t: ^testing.T) { benchmark_print(name, options) } +@test +test_xxhash_large :: proc(t: ^testing.T) { + many_zeroes := make([]u8, 16 * 1024 * 1024) + defer delete(many_zeroes) + + // All at once. + for i, v in ZERO_VECTORS { + b := many_zeroes[:i] + + fmt.printf("[test_xxhash_large] All at once. Size: %v\n", i) + + xxh32 := xxhash.XXH32(b) + xxh64 := xxhash.XXH64(b) + xxh3_64 := xxhash.XXH3_64(b) + xxh3_128 := xxhash.XXH3_128(b) + + xxh32_error := fmt.tprintf("[ XXH32(%03d) ] Expected: %08x. Got: %08x.", i, v.xxh_32, xxh32) + xxh64_error := fmt.tprintf("[ XXH64(%03d) ] Expected: %16x. Got: %16x.", i, v.xxh_64, xxh64) + xxh3_64_error := fmt.tprintf("[XXH3_64(%03d) ] Expected: %16x. Got: %16x.", i, v.xxh3_64, xxh3_64) + xxh3_128_error := fmt.tprintf("[XXH3_128(%03d) ] Expected: %32x. Got: %32x.", i, v.xxh3_128, xxh3_128) + + expect(t, xxh32 == v.xxh_32, xxh32_error) + expect(t, xxh64 == v.xxh_64, xxh64_error) + expect(t, xxh3_64 == v.xxh3_64, xxh3_64_error) + expect(t, xxh3_128 == v.xxh3_128, xxh3_128_error) + } + + when #config(RAND_STATE, -1) >= 0 && #config(RAND_INC, -1) >= 0 { + random_seed := rand.Rand{ + state = u64(#config(RAND_STATE, -1)), + inc = u64(#config(RAND_INC, -1)), + } + fmt.printf("Using user-selected seed {{%v,%v}} for update size randomness.\n", random_seed.state, random_seed.inc) + } else { + random_seed := rand.create(u64(intrinsics.read_cycle_counter())) + fmt.printf("Randonly selected seed {{%v,%v}} for update size randomness.\n", random_seed.state, random_seed.inc) + } + + // Streamed + for i, v in ZERO_VECTORS { + b := many_zeroes[:i] + + fmt.printf("[test_xxhash_large] Streamed. Size: %v\n", i) + + // bytes_per_update := []int{1, 42, 13, 7, 16, 5, 23, 74, 1024, 511, 1023, 47} + // update_size_idx: int + + xxh_32_state, xxh_32_err := xxhash.XXH32_create_state() + defer xxhash.XXH32_destroy_state(xxh_32_state) + expect(t, xxh_32_err == nil, "Problem initializing XXH_32 state.") + + xxh_64_state, xxh_64_err := xxhash.XXH64_create_state() + defer xxhash.XXH64_destroy_state(xxh_64_state) + expect(t, xxh_64_err == nil, "Problem initializing XXH_64 state.") + + xxh3_64_state, xxh3_64_err := xxhash.XXH3_create_state() + defer xxhash.XXH3_destroy_state(xxh3_64_state) + expect(t, xxh3_64_err == nil, "Problem initializing XXH3_64 state.") + + xxh3_128_state, xxh3_128_err := xxhash.XXH3_create_state() + defer xxhash.XXH3_destroy_state(xxh3_128_state) + expect(t, xxh3_128_err == nil, "Problem initializing XXH3_128 state.") + + // XXH3_128_update + + for len(b) > 0 { + update_size := min(len(b), rand.int_max(8192, &random_seed)) + if update_size > 4096 { + update_size %= 73 + } + xxhash.XXH32_update (xxh_32_state, b[:update_size]) + xxhash.XXH64_update (xxh_64_state, b[:update_size]) + + xxhash.XXH3_64_update (xxh3_64_state, b[:update_size]) + xxhash.XXH3_128_update(xxh3_128_state, b[:update_size]) + + b = b[update_size:] + } + + // Now finalize + xxh32 := xxhash.XXH32_digest(xxh_32_state) + xxh64 := xxhash.XXH64_digest(xxh_64_state) + + xxh3_64 := xxhash.XXH3_64_digest(xxh3_64_state) + xxh3_128 := xxhash.XXH3_128_digest(xxh3_128_state) + + xxh32_error := fmt.tprintf("[ XXH32(%03d) ] Expected: %08x. Got: %08x.", i, v.xxh_32, xxh32) + xxh64_error := fmt.tprintf("[ XXH64(%03d) ] Expected: %16x. Got: %16x.", i, v.xxh_64, xxh64) + xxh3_64_error := fmt.tprintf("[XXH3_64(%03d) ] Expected: %16x. Got: %16x.", i, v.xxh3_64, xxh3_64) + xxh3_128_error := fmt.tprintf("[XXH3_128(%03d) ] Expected: %32x. Got: %32x.", i, v.xxh3_128, xxh3_128) + + expect(t, xxh32 == v.xxh_32, xxh32_error) + expect(t, xxh64 == v.xxh_64, xxh64_error) + expect(t, xxh3_64 == v.xxh3_64, xxh3_64_error) + expect(t, xxh3_128 == v.xxh3_128, xxh3_128_error) + } +} + @test test_xxhash_vectors :: proc(t: ^testing.T) { fmt.println("Verifying against XXHASH_TEST_VECTOR_SEEDED:") diff --git a/tests/core/hash/test_vectors_xxhash.odin b/tests/core/hash/test_vectors_xxhash.odin index dccda5e53..6a37aef30 100644 --- a/tests/core/hash/test_vectors_xxhash.odin +++ b/tests/core/hash/test_vectors_xxhash.odin @@ -3,7 +3,7 @@ */ package test_core_hash -XXHASH_Test_Vectors_With_Seed :: struct #packed { +XXHASH_Test_Vectors :: struct #packed { /* Old hashes */ @@ -17,7 +17,75 @@ XXHASH_Test_Vectors_With_Seed :: struct #packed { xxh3_128: u128, } -XXHASH_TEST_VECTOR_SEEDED := map[u64][257]XXHASH_Test_Vectors_With_Seed{ +ZERO_VECTORS := map[int]XXHASH_Test_Vectors{ + 1024 * 1024 = { + /* + Old hashes + */ + xxh_32 = 0x9430f97f, // xxhsum -H0 + xxh_64 = 0x87d2a1b6e1163ef1, // xxhsum -H1 + + /* + XXH3 hashes + */ + xxh3_128 = 0xb6ef17a3448492b6918780b90550bf34, // xxhsum -H2 + xxh3_64 = 0x918780b90550bf34, // xxhsum -H3 + }, + 1024 * 2048 = { + /* + Old hashes + */ + xxh_32 = 0xeeb74ca1, // xxhsum -H0 + xxh_64 = 0xeb8a7322f88e23db, // xxhsum -H1 + + /* + XXH3 hashes + */ + xxh3_128 = 0x7b3e6abe1456fd0094e26d8e04364852, // xxhsum -H2 + xxh3_64 = 0x94e26d8e04364852, // xxhsum -H3 + }, + 1024 * 4096 = { + /* + Old hashes + */ + xxh_32 = 0xa59010b8, // xxhsum -H0 + xxh_64 = 0x639f9e1a7cbc9d28, // xxhsum -H1 + + /* + XXH3 hashes + */ + xxh3_128 = 0x34001ae2f947e773165f453a5f35c459, // xxhsum -H2 + xxh3_64 = 0x165f453a5f35c459, // xxhsum -H3 + }, + 1024 * 8192 = { + /* + Old hashes + */ + xxh_32 = 0xfed1d084, // xxhsum -H0 + xxh_64 = 0x86823cbc61f6df0f, // xxhsum -H1 + + /* + XXH3 hashes + */ + xxh3_128 = 0x9d6bf1a4e92df02ce881a25e37e37b19, // xxhsum -H2 + xxh3_64 = 0xe881a25e37e37b19, // xxhsum -H3 + }, + 1024 * 16384 = { + /* + Old hashes + */ + xxh_32 = 0x0ee4ebf9, // xxhsum -H0 + xxh_64 = 0x412f1e415ee2d80b, // xxhsum -H1 + + /* + XXH3 hashes + */ + xxh3_128 = 0x14d914cac1f4c1b1c4979470a1b529a1, // xxhsum -H2 + xxh3_64 = 0xc4979470a1b529a1, // xxhsum -H3 + }, +} + +XXHASH_TEST_VECTOR_SEEDED := map[u64][257]XXHASH_Test_Vectors{ 0 = { { // Length: 000 /* XXH32 with seed */ 0x02cc5d05, diff --git a/tests/core/image/test_core_image.odin b/tests/core/image/test_core_image.odin index 23a7c2561..171e4674d 100644 --- a/tests/core/image/test_core_image.odin +++ b/tests/core/image/test_core_image.odin @@ -5,7 +5,7 @@ List of contributors: Jeroen van Rijn: Initial implementation. - A test suite for PNG. + A test suite for PNG + QOI. */ package test_core_image @@ -13,11 +13,14 @@ import "core:testing" import "core:compress" import "core:image" +import pbm "core:image/netpbm" import "core:image/png" +import "core:image/qoi" import "core:bytes" import "core:hash" import "core:fmt" +import "core:strings" import "core:mem" import "core:os" @@ -25,30 +28,27 @@ import "core:time" import "core:runtime" -WRITE_PPM_ON_FAIL :: #config(WRITE_PPM_ON_FAIL, false) TEST_SUITE_PATH :: "assets/PNG" TEST_count := 0 TEST_fail := 0 when ODIN_TEST { - expect :: testing.expect - log :: testing.log + expect :: testing.expect + log :: testing.log } else { - expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) { - fmt.printf("[%v] ", loc) - TEST_count += 1 - if !condition { - TEST_fail += 1 - fmt.println(message) - return - } - fmt.println(" PASS") - } - log :: proc(t: ^testing.T, v: any, loc := #caller_location) { - fmt.printf("[%v] ", loc) - fmt.printf("log: %v\n", v) - } + expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) { + TEST_count += 1 + if !condition { + TEST_fail += 1 + fmt.printf("[%v] %v\n", loc, message) + return + } + } + log :: proc(t: ^testing.T, v: any, loc := #caller_location) { + fmt.printf("[%v] ", loc) + fmt.printf("log: %v\n", v) + } } I_Error :: image.Error @@ -57,6 +57,9 @@ main :: proc() { png_test(&t) fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count) + if TEST_fail > 0 { + os.exit(1) + } } PNG_Test :: struct { @@ -1197,37 +1200,37 @@ Corrupt_PNG_Tests := []PNG_Test{ { "xs1n0g01", // signature byte 1 MSBit reset to zero { - {Default, .Invalid_PNG_Signature, {}, 0x_0000_0000}, + {Default, .Invalid_Signature, {}, 0x_0000_0000}, }, }, { "xs2n0g01", // signature byte 2 is a 'Q' { - {Default, .Invalid_PNG_Signature, {}, 0x_0000_0000}, + {Default, .Invalid_Signature, {}, 0x_0000_0000}, }, }, { "xs4n0g01", // signature byte 4 lowercase { - {Default, .Invalid_PNG_Signature, {}, 0x_0000_0000}, + {Default, .Invalid_Signature, {}, 0x_0000_0000}, }, }, { "xs7n0g01", // 7th byte a space instead of control-Z { - {Default, .Invalid_PNG_Signature, {}, 0x_0000_0000}, + {Default, .Invalid_Signature, {}, 0x_0000_0000}, }, }, { "xcrn0g04", // added cr bytes { - {Default, .Invalid_PNG_Signature, {}, 0x_0000_0000}, + {Default, .Invalid_Signature, {}, 0x_0000_0000}, }, }, { "xlfn0g04", // added lf bytes { - {Default, .Invalid_PNG_Signature, {}, 0x_0000_0000}, + {Default, .Invalid_Signature, {}, 0x_0000_0000}, }, }, { @@ -1461,7 +1464,7 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) -> (subtotal: int) { context = runtime.default_context() for file in suite { - test_file := fmt.tprintf("%v/%v.png", TEST_SUITE_PATH, file.file) + test_file := strings.concatenate({TEST_SUITE_PATH, "/", file.file, ".png"}, context.temp_allocator) img: ^png.Image err: png.Error @@ -1498,11 +1501,168 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) -> (subtotal: int) { passed &= dims_pass - hash := hash.crc32(pixels) - error = fmt.tprintf("%v test %v hash is %08x, expected %08x.", file.file, count, hash, test.hash) - expect(t, test.hash == hash, error) + png_hash := hash.crc32(pixels) + error = fmt.tprintf("%v test %v hash is %08x, expected %08x with %v.", file.file, count, png_hash, test.hash, test.options) + expect(t, test.hash == png_hash, error) + + passed &= test.hash == png_hash + + if passed { + // Roundtrip through QOI to test the QOI encoder and decoder. + if img.depth == 8 && (img.channels == 3 || img.channels == 4) { + qoi_buffer: bytes.Buffer + defer bytes.buffer_destroy(&qoi_buffer) + qoi_save_err := qoi.save(&qoi_buffer, img) + + error = fmt.tprintf("%v test %v QOI save failed with %v.", file.file, count, qoi_save_err) + expect(t, qoi_save_err == nil, error) + + if qoi_save_err == nil { + qoi_img, qoi_load_err := qoi.load(qoi_buffer.buf[:]) + defer qoi.destroy(qoi_img) + + error = fmt.tprintf("%v test %v QOI load failed with %v.", file.file, count, qoi_load_err) + expect(t, qoi_load_err == nil, error) + + qoi_hash := hash.crc32(qoi_img.pixels.buf[:]) + error = fmt.tprintf("%v test %v QOI load hash is %08x, expected it match PNG's %08x with %v.", file.file, count, qoi_hash, png_hash, test.options) + expect(t, qoi_hash == png_hash, error) + } + } + + { + // Roundtrip through PBM to test the PBM encoders and decoders - prefer binary + pbm_buf, pbm_save_err := pbm.save_to_buffer(img) + defer delete(pbm_buf) + + error = fmt.tprintf("%v test %v PBM save failed with %v.", file.file, count, pbm_save_err) + expect(t, pbm_save_err == nil, error) + + if pbm_save_err == nil { + // Try to load it again. + pbm_img, pbm_load_err := pbm.load(pbm_buf) + defer pbm.destroy(pbm_img) + + error = fmt.tprintf("%v test %v PBM load failed with %v.", file.file, count, pbm_load_err) + expect(t, pbm_load_err == nil, error) + + if pbm_load_err == nil { + pbm_hash := hash.crc32(pbm_img.pixels.buf[:]) + + error = fmt.tprintf("%v test %v PBM load hash is %08x, expected it match PNG's %08x with %v.", file.file, count, pbm_hash, png_hash, test.options) + expect(t, pbm_hash == png_hash, error) + } + } + } + + { + // Roundtrip through PBM to test the PBM encoders and decoders - prefer ASCII + pbm_info, pbm_format_selected := pbm.autoselect_pbm_format_from_image(img, false) + + // We already tested the binary formats above. + if pbm_info.header.format in pbm.ASCII { + pbm_buf, pbm_save_err := pbm.save_to_buffer(img, pbm_info) + defer delete(pbm_buf) + + error = fmt.tprintf("%v test %v PBM save failed with %v.", file.file, count, pbm_save_err) + expect(t, pbm_save_err == nil, error) + + if pbm_save_err == nil { + // Try to load it again. + pbm_img, pbm_load_err := pbm.load(pbm_buf) + defer pbm.destroy(pbm_img) + + error = fmt.tprintf("%v test %v PBM load failed with %v.", file.file, count, pbm_load_err) + expect(t, pbm_load_err == nil, error) + + if pbm_load_err == nil { + pbm_hash := hash.crc32(pbm_img.pixels.buf[:]) + + error = fmt.tprintf("%v test %v PBM load hash is %08x, expected it match PNG's %08x with %v.", file.file, count, pbm_hash, png_hash, test.options) + expect(t, pbm_hash == png_hash, error) + } + } + } + } + + { + // We still need to test Portable Float Maps + if (img.channels == 1 || img.channels == 3) && (img.depth == 8 || img.depth == 16) { + + // Make temporary float image + float_img := new(image.Image) + defer png.destroy(float_img) + + float_img.width = img.width + float_img.height = img.height + float_img.channels = img.channels + float_img.depth = 32 + + buffer_size := image.compute_buffer_size(img.width, img.height, img.channels, 32) + resize(&float_img.pixels.buf, buffer_size) + + pbm_info := pbm.Info { + header = { + width = img.width, + height = img.height, + channels = img.channels, + depth = img.depth, + maxval = 255 if img.depth == 8 else 65535, + little_endian = true if ODIN_ENDIAN == .Little else false, + scale = 1.0, + format = .Pf if img.channels == 1 else .PF, + }, + } + + // Transform data... + orig_float := mem.slice_data_cast([]f32, float_img.pixels.buf[:]) + + switch img.depth { + case 8: + for v, i in img.pixels.buf { + orig_float[i] = f32(v) / f32(256) + } + case 16: + wide := mem.slice_data_cast([]u16, img.pixels.buf[:]) + for v, i in wide { + orig_float[i] = f32(v) / f32(65536) + } + } + + float_pbm_buf, float_pbm_save_err := pbm.save_to_buffer(float_img, pbm_info) + defer delete(float_pbm_buf) + + error = fmt.tprintf("%v test %v save as PFM failed with %v", file.file, count, float_pbm_save_err) + expect(t, float_pbm_save_err == nil, error) + + if float_pbm_save_err == nil { + // Load float image and compare. + float_pbm_img, float_pbm_load_err := pbm.load(float_pbm_buf) + defer pbm.destroy(float_pbm_img) + + error = fmt.tprintf("%v test %v PFM load failed with %v", file.file, count, float_pbm_load_err) + expect(t, float_pbm_load_err == nil, error) + + load_float := mem.slice_data_cast([]f32, float_pbm_img.pixels.buf[:]) + + error = fmt.tprintf("%v test %v PFM load returned %v floats, expected %v", file.file, count, len(load_float), len(orig_float)) + expect(t, len(load_float) == len(orig_float), error) + + // Compare floats + equal := true + for orig, i in orig_float { + if orig != load_float[i] { + equal = false + break + } + } + error = fmt.tprintf("%v test %v PFM loaded floats to match", file.file, count) + expect(t, equal, error) + } + } + } + } - passed &= test.hash == hash if .return_metadata in test.options { if v, ok := img.metadata.(^image.PNG_Info); ok { @@ -1719,12 +1879,6 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) -> (subtotal: int) { } } - if WRITE_PPM_ON_FAIL && !passed && err == nil { // It loaded but had an error in its compares. - testing.logf(t, "Test failed, writing ppm/%v-%v.ppm to help debug.\n", file.file, count) - output := fmt.tprintf("ppm/%v-%v.ppm", file.file, count) - write_image_as_ppm(output, img) - } - png.destroy(img) for _, v in track.allocation_map { @@ -1735,198 +1889,4 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) -> (subtotal: int) { } return -} - -// Crappy PPM writer used during testing. Don't use in production. -write_image_as_ppm :: proc(filename: string, image: ^image.Image) -> (success: bool) { - - _bg :: proc(x, y: int, high := true) -> (res: [3]u16) { - if high { - l := u16(30 * 256 + 30) - - if (x & 4 == 0) ~ (y & 4 == 0) { - res = [3]u16{l, l, l} - } else { - res = [3]u16{l >> 1, l >> 1, l >> 1} - } - } else { - if (x & 4 == 0) ~ (y & 4 == 0) { - res = [3]u16{30, 30, 30} - } else { - res = [3]u16{15, 15, 15} - } - } - return - } - - using image - using os - - flags: int = O_WRONLY|O_CREATE|O_TRUNC - - img := image - - // PBM 16-bit images are big endian - when ODIN_ENDIAN == .Little { - if img.depth == 16 { - // The pixel components are in Big Endian. Let's byteswap back. - input := mem.slice_data_cast([]u16, img.pixels.buf[:]) - output := mem.slice_data_cast([]u16be, img.pixels.buf[:]) - #no_bounds_check for v, i in input { - output[i] = u16be(v) - } - } - } - - pix := bytes.buffer_to_bytes(&img.pixels) - - if len(pix) == 0 || len(pix) < image.width * image.height * int(image.channels) { - return false - } - - mode: int = 0 - when ODIN_OS == "linux" || ODIN_OS == "darwin" { - // NOTE(justasd): 644 (owner read, write; group read; others read) - mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH - } - - fd, err := open(filename, flags, mode) - if err != 0 { - return false - } - defer close(fd) - - write_string(fd, - fmt.tprintf("P6\n%v %v\n%v\n", width, height, (1 << uint(depth) -1)), - ) - - if channels == 3 { - // We don't handle transparency here... - write_ptr(fd, raw_data(pix), len(pix)) - } else { - bpp := depth == 16 ? 2 : 1 - bytes_needed := width * height * 3 * bpp - - op := bytes.Buffer{} - bytes.buffer_init_allocator(&op, bytes_needed, bytes_needed) - defer bytes.buffer_destroy(&op) - - if channels == 1 { - if depth == 16 { - assert(len(pix) == width * height * 2) - p16 := mem.slice_data_cast([]u16, pix) - o16 := mem.slice_data_cast([]u16, op.buf[:]) - #no_bounds_check for len(p16) != 0 { - r := u16(p16[0]) - o16[0] = r - o16[1] = r - o16[2] = r - p16 = p16[1:] - o16 = o16[3:] - } - } else { - o := 0 - for i := 0; i < len(pix); i += 1 { - r := pix[i] - op.buf[o ] = r - op.buf[o+1] = r - op.buf[o+2] = r - o += 3 - } - } - write_ptr(fd, raw_data(op.buf), len(op.buf)) - } else if channels == 2 { - if depth == 16 { - p16 := mem.slice_data_cast([]u16, pix) - o16 := mem.slice_data_cast([]u16, op.buf[:]) - - bgcol := img.background - - #no_bounds_check for len(p16) != 0 { - r := f64(u16(p16[0])) - bg: f64 - if bgcol != nil { - v := bgcol.([3]u16)[0] - bg = f64(v) - } - a := f64(u16(p16[1])) / 65535.0 - l := (a * r) + (1 / a) * bg - - o16[0] = u16(l) - o16[1] = u16(l) - o16[2] = u16(l) - - p16 = p16[2:] - o16 = o16[3:] - } - } else { - o := 0 - for i := 0; i < len(pix); i += 2 { - r := pix[i]; a := pix[i+1]; a1 := f32(a) / 255.0 - c := u8(f32(r) * a1) - op.buf[o ] = c - op.buf[o+1] = c - op.buf[o+2] = c - o += 3 - } - } - write_ptr(fd, raw_data(op.buf), len(op.buf)) - } else if channels == 4 { - if depth == 16 { - p16 := mem.slice_data_cast([]u16be, pix) - o16 := mem.slice_data_cast([]u16be, op.buf[:]) - - i := 0 - for len(p16) > 0 { - i += 1 - x := i % width - y := i / width - bg := _bg(x, y, true) - - r := f32(p16[0]) - g := f32(p16[1]) - b := f32(p16[2]) - a := f32(p16[3]) / 65535.0 - - lr := (a * r) + (1 / a) * f32(bg[0]) - lg := (a * g) + (1 / a) * f32(bg[1]) - lb := (a * b) + (1 / a) * f32(bg[2]) - - o16[0] = u16be(lr) - o16[1] = u16be(lg) - o16[2] = u16be(lb) - - p16 = p16[4:] - o16 = o16[3:] - } - } else { - o := 0 - - for i := 0; i < len(pix); i += 4 { - - x := (i / 4) % width - y := i / width / 4 - _b := _bg(x, y, false) - bgcol := [3]u8{u8(_b[0]), u8(_b[1]), u8(_b[2])} - - r := f32(pix[i]) - g := f32(pix[i+1]) - b := f32(pix[i+2]) - a := f32(pix[i+3]) / 255.0 - - lr := u8(f32(r) * a + (1 / a) * f32(bgcol[0])) - lg := u8(f32(g) * a + (1 / a) * f32(bgcol[1])) - lb := u8(f32(b) * a + (1 / a) * f32(bgcol[2])) - op.buf[o ] = lr - op.buf[o+1] = lg - op.buf[o+2] = lb - o += 3 - } - } - write_ptr(fd, raw_data(op.buf), len(op.buf)) - } else { - return false - } - } - return true } \ No newline at end of file diff --git a/tests/core/math/big/build.bat b/tests/core/math/big/build.bat index e383cdfc1..ad199d775 100644 --- a/tests/core/math/big/build.bat +++ b/tests/core/math/big/build.bat @@ -4,14 +4,11 @@ set PATH_TO_ODIN==..\..\..\..\odin set TEST_ARGS=-fast-tests set TEST_ARGS=-no-random set TEST_ARGS= -set OUT_NAME=math_big_test_library +set OUT_NAME=math_big_test_library.dll set COMMON=-build-mode:shared -show-timings -no-bounds-check -define:MATH_BIG_EXE=false -vet -strict-style echo --- echo Running core:math/big tests echo --- -rem Fails -:%PATH_TO_ODIN% build . %COMMON% -o:speed -out:%OUT_NAME% -rem Passes -%PATH_TO_ODIN% build . %COMMON% -o:size -out:%OUT_NAME% +%PATH_TO_ODIN% build . %COMMON% -o:speed -out:%OUT_NAME% python3 test.py %TEST_ARGS% \ No newline at end of file diff --git a/tests/core/math/big/test.odin b/tests/core/math/big/test.odin index 81f1956dc..a289c89dd 100644 --- a/tests/core/math/big/test.odin +++ b/tests/core/math/big/test.odin @@ -32,9 +32,9 @@ print_to_buffer :: proc(val: ^big.Int) -> cstring { @export test_initialize_constants :: proc "c" () -> (res: u64) { context = runtime.default_context() - res = u64(big.initialize_constants()) - //assert(MUL_KARATSUBA_CUTOFF >= 40); - return res + _ = big.initialize_constants() + + return u64(big._DIGIT_NAILS) } @export test_error_string :: proc "c" (err: big.Error) -> (res: cstring) { diff --git a/tests/core/math/big/test.py b/tests/core/math/big/test.py index d292a3ff4..d4724edb8 100644 --- a/tests/core/math/big/test.py +++ b/tests/core/math/big/test.py @@ -17,7 +17,6 @@ import gc from enum import Enum import argparse - parser = argparse.ArgumentParser( description = "Odin core:math/big test suite", epilog = "By default we run regression and random tests with preset parameters.", @@ -163,6 +162,8 @@ def load(export_name, args, res): export_name.restype = res return export_name + + # # Result values will be passed in a struct { res: cstring, err: Error } # @@ -170,7 +171,11 @@ class Res(Structure): _fields_ = [("res", c_char_p), ("err", c_uint64)] initialize_constants = load(l.test_initialize_constants, [], c_uint64) -print("initialize_constants: ", initialize_constants()) + +NAILS = initialize_constants() +LEG_BITS = 64 - NAILS + +print("LEG BITS: ", LEG_BITS) error_string = load(l.test_error_string, [c_byte], c_char_p) @@ -407,7 +412,7 @@ def test_shl_leg(a = 0, digits = 0, expected_error = Error.Okay): res = int_shl_leg(*args) expected_result = None if expected_error == Error.Okay: - expected_result = a << (digits * 60) + expected_result = a << (digits * LEG_BITS) return test("test_shl_leg", res, [a, digits], expected_error, expected_result) def test_shr_leg(a = 0, digits = 0, expected_error = Error.Okay): @@ -419,7 +424,7 @@ def test_shr_leg(a = 0, digits = 0, expected_error = Error.Okay): # Don't pass negative numbers. We have a shr_signed. return False else: - expected_result = a >> (digits * 60) + expected_result = a >> (digits * LEG_BITS) return test("test_shr_leg", res, [a, digits], expected_error, expected_result) diff --git a/tests/core/math/linalg/glsl/test_linalg_glsl_math.odin b/tests/core/math/linalg/glsl/test_linalg_glsl_math.odin new file mode 100644 index 000000000..e0b4f5145 --- /dev/null +++ b/tests/core/math/linalg/glsl/test_linalg_glsl_math.odin @@ -0,0 +1,85 @@ +// Tests "linalg_glsl_math.odin" in "core:math/linalg/glsl". +// Must be run with `-collection:tests=` flag, e.g. +// ./odin run tests/core/math/linalg/glsl/test_linalg_glsl_math.odin -collection:tests=./tests +package test_core_math_linalg_glsl_math + +import glsl "core:math/linalg/glsl" + +import "core:fmt" +import "core:math" +import "core:testing" +import tc "tests:common" + +main :: proc() { + + t := testing.T{} + + test_fract_f32(&t) + test_fract_f64(&t) + + tc.report(&t) +} + +@test +test_fract_f32 :: proc(t: ^testing.T) { + + using math + + r: f32 + + Datum :: struct { + i: int, + v: f32, + e: f32, + } + @static data := []Datum{ + { 0, 10.5, 0.5 }, // Issue #1574 fract in linalg/glm is broken + { 1, -10.5, -0.5 }, + { 2, F32_MIN, F32_MIN }, // 0x1p-126 + { 3, -F32_MIN, -F32_MIN }, + { 4, 0.0, 0.0 }, + { 5, -0.0, -0.0 }, + { 6, 1, 0.0 }, + { 7, -1, -0.0 }, + { 8, 0h3F80_0001, 0h3400_0000 }, // 0x1.000002p+0, 0x1p-23 + { 9, -0h3F80_0001, -0h3400_0000 }, + } + + for d, i in data { + assert(i == d.i) + r = glsl.fract(d.v) + tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%v (%h)) -> %v (%h) != %v", i, #procedure, d.v, d.v, r, r, d.e)) + } +} + +@test +test_fract_f64 :: proc(t: ^testing.T) { + + using math + + r: f64 + + Datum :: struct { + i: int, + v: f64, + e: f64, + } + @static data := []Datum{ + { 0, 10.5, 0.5 }, // Issue #1574 fract in linalg/glm is broken + { 1, -10.5, -0.5 }, + { 2, F64_MIN, F64_MIN }, // 0x1p-1022 + { 3, -F64_MIN, -F64_MIN }, + { 4, 0.0, 0.0 }, + { 5, -0.0, -0.0 }, + { 6, 1, 0.0 }, + { 7, -1, -0.0 }, + { 8, 0h3FF0_0000_0000_0001, 0h3CB0_0000_0000_0000 }, // 0x1.0000000000001p+0, 0x1p-52 + { 9, -0h3FF0_0000_0000_0001, -0h3CB0_0000_0000_0000 }, + } + + for d, i in data { + assert(i == d.i) + r = glsl.fract(d.v) + tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%v (%h)) -> %v (%h) != %v", i, #procedure, d.v, d.v, r, r, d.e)) + } +} diff --git a/tests/core/math/noise/test_core_math_noise.odin b/tests/core/math/noise/test_core_math_noise.odin new file mode 100644 index 000000000..a0360e695 --- /dev/null +++ b/tests/core/math/noise/test_core_math_noise.odin @@ -0,0 +1,151 @@ +package test_core_math_noise + +import "core:testing" +import "core:math/noise" +import "core:fmt" +import "core:os" + +TEST_count := 0 +TEST_fail := 0 + +V2 :: noise.Vec2 +V3 :: noise.Vec3 +V4 :: noise.Vec4 + +when ODIN_TEST { + expect :: testing.expect + log :: testing.log +} else { + expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) { + TEST_count += 1 + if !condition { + TEST_fail += 1 + fmt.printf("[%v] %v\n", loc, message) + return + } + } + log :: proc(t: ^testing.T, v: any, loc := #caller_location) { + fmt.printf("[%v] ", loc) + fmt.printf("log: %v\n", v) + } +} + +main :: proc() { + t := testing.T{} + noise_test(&t) + fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count) + if TEST_fail > 0 { + os.exit(1) + } +} + +Test_Vector :: struct { + seed: i64, + coord: union {V2, V3, V4}, + expected: f32, + + test_proc: union { + proc(_: i64, _: V2) -> f32, + proc(_: i64, _: V3) -> f32, + proc(_: i64, _: V4) -> f32, + }, +} + +SEED_1 :: 2324223232 +SEED_2 :: 932466901 +SEED_3 :: 9321 + +COORD_1 :: V4{ 242.0, 3433.0, 920.0, 222312.0} +COORD_2 :: V4{ 590.0, 9411.0, 5201.0, 942124256.0} +COORD_3 :: V4{12090.0, 19411.0, 81950901.0, 4224219.0} + +Noise_Tests := []Test_Vector{ + /* + `noise_2d` tests. + */ + {SEED_1, COORD_1.xy, 0.25010583, noise.noise_2d}, + {SEED_2, COORD_2.xy, -0.92513955, noise.noise_2d}, + {SEED_3, COORD_3.xy, 0.67327416, noise.noise_2d}, + + /* + `noise_2d_improve_x` tests. + */ + {SEED_1, COORD_1.xy, 0.17074019, noise.noise_2d_improve_x}, + {SEED_2, COORD_2.xy, 0.72330487, noise.noise_2d_improve_x}, + {SEED_3, COORD_3.xy, -0.032076947, noise.noise_2d_improve_x}, + + /* + `noise_3d_improve_xy` tests. + */ + {SEED_1, COORD_1.xyz, 0.14819577, noise.noise_3d_improve_xy}, + {SEED_2, COORD_2.xyz, -0.065345764, noise.noise_3d_improve_xy}, + {SEED_3, COORD_3.xyz, -0.37761918, noise.noise_3d_improve_xy}, + + /* + `noise_3d_improve_xz` tests. + */ + {SEED_1, COORD_1.xyz, -0.50075006, noise.noise_3d_improve_xz}, + {SEED_2, COORD_2.xyz, -0.36039603, noise.noise_3d_improve_xz}, + {SEED_3, COORD_3.xyz, -0.3479203, noise.noise_3d_improve_xz}, + + /* + `noise_3d_fallback` tests. + */ + {SEED_1, COORD_1.xyz, 0.6557345, noise.noise_3d_fallback}, + {SEED_2, COORD_2.xyz, 0.55452216, noise.noise_3d_fallback}, + {SEED_3, COORD_3.xyz, -0.26408964, noise.noise_3d_fallback}, + + /* + `noise_3d_fallback` tests. + */ + {SEED_1, COORD_1.xyz, 0.6557345, noise.noise_3d_fallback}, + {SEED_2, COORD_2.xyz, 0.55452216, noise.noise_3d_fallback}, + {SEED_3, COORD_3.xyz, -0.26408964, noise.noise_3d_fallback}, + + /* + `noise_4d_improve_xyz_improve_xy` tests. + */ + {SEED_1, COORD_1, 0.44929826, noise.noise_4d_improve_xyz_improve_xy}, + {SEED_2, COORD_2, -0.13270882, noise.noise_4d_improve_xyz_improve_xy}, + {SEED_3, COORD_3, 0.10298563, noise.noise_4d_improve_xyz_improve_xy}, + + /* + `noise_4d_improve_xyz_improve_xz` tests. + */ + {SEED_1, COORD_1, -0.078514606, noise.noise_4d_improve_xyz_improve_xz}, + {SEED_2, COORD_2, -0.032157656, noise.noise_4d_improve_xyz_improve_xz}, + {SEED_3, COORD_3, -0.38607058, noise.noise_4d_improve_xyz_improve_xz}, + + /* + `noise_4d_improve_xyz` tests. + */ + {SEED_1, COORD_1, -0.4442258, noise.noise_4d_improve_xyz}, + {SEED_2, COORD_2, 0.36822623, noise.noise_4d_improve_xyz}, + {SEED_3, COORD_3, 0.22628775, noise.noise_4d_improve_xyz}, + + /* + `noise_4d_fallback` tests. + */ + {SEED_1, COORD_1, -0.14233987, noise.noise_4d_fallback}, + {SEED_2, COORD_2, 0.1354035, noise.noise_4d_fallback}, + {SEED_3, COORD_3, 0.14565045, noise.noise_4d_fallback}, + +} + +noise_test :: proc(t: ^testing.T) { + for test in Noise_Tests { + output: f32 + + switch coord in test.coord { + case V2: + output = test.test_proc.(proc(_: i64, _: V2) -> f32)(test.seed, test.coord.(V2)) + case V3: + output = test.test_proc.(proc(_: i64, _: V3) -> f32)(test.seed, test.coord.(V3)) + case V4: + output = test.test_proc.(proc(_: i64, _: V4) -> f32)(test.seed, test.coord.(V4)) + } + + error := fmt.tprintf("Seed %v, Coord: %v, Expected: %3.8f. Got %3.8f", test.seed, test.coord, test.expected, output) + expect(t, test.expected == output, error) + } +} \ No newline at end of file diff --git a/tests/core/math/test_core_math.odin b/tests/core/math/test_core_math.odin new file mode 100644 index 000000000..57da27002 --- /dev/null +++ b/tests/core/math/test_core_math.odin @@ -0,0 +1,310 @@ +// Tests "math.odin" in "core:math". +// Must be run with `-collection:tests=` flag, e.g. +// ./odin run tests/core/math/test_core_math.odin -collection:tests=./tests +package test_core_math + +import "core:fmt" +import "core:math" +import "core:testing" +import tc "tests:common" + +main :: proc() { + t := testing.T{} + + test_classify_f16(&t) + test_classify_f32(&t) + test_classify_f64(&t) + + test_trunc_f16(&t) + test_trunc_f32(&t) + test_trunc_f64(&t) + + tc.report(&t) +} + +@test +test_classify_f16 :: proc(t: ^testing.T) { + + using math + using Float_Class + + r: Float_Class + + Datum :: struct { + i: int, + v: f16, + e: math.Float_Class, + } + @static data := []Datum{ + { 0, 1.2, Normal }, + { 1, 0h0001, Subnormal }, + { 2, 0.0, Zero }, + { 3, -0.0, Neg_Zero }, + { 4, SNAN_F16, NaN }, + { 5, QNAN_F16, NaN }, + { 6, INF_F16, Inf }, + { 7, NEG_INF_F16, Neg_Inf }, + } + + for d, i in data { + assert(i == d.i) + r = classify_f16(d.v) + tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %v != %v", i, #procedure, d.v, r, d.e)) + } + + /* Check all subnormals (exponent 0, 10-bit significand non-zero) */ + for i :u16 = 1; i < 0x400; i += 1 { + v :f16 = transmute(f16)i + r = classify_f16(v) + e :Float_Class: Subnormal + tc.expect(t, r == e, fmt.tprintf("i:%d %s(%h) -> %v != %v", i, #procedure, v, r, e)) + } +} + +@test +test_classify_f32 :: proc(t: ^testing.T) { + + using math + using Float_Class + + r: Float_Class + + Datum :: struct { + i: int, + v: f32, + e: math.Float_Class, + } + @static data := []Datum{ + { 0, 1.2, Normal }, + { 1, 0h0000_0001, Subnormal }, + { 2, 0.0, Zero }, + { 3, -0.0, Neg_Zero }, + { 4, SNAN_F32, NaN }, + { 5, QNAN_F32, NaN }, + { 6, INF_F32, Inf }, + { 7, NEG_INF_F32, Neg_Inf }, + } + + for d, i in data { + assert(i == d.i) + r = classify_f32(d.v) + tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %v != %v", i, #procedure, d.v, r, d.e)) + } +} + +@test +test_classify_f64 :: proc(t: ^testing.T) { + + using math + using Float_Class + + r: Float_Class + + Datum :: struct { + i: int, + v: f64, + e: math.Float_Class, + } + @static data := []Datum{ + { 0, 1.2, Normal }, + { 1, 0h0000_0000_0000_0001, Subnormal }, + { 2, 0.0, Zero }, + { 3, -0.0, Neg_Zero }, + { 4, SNAN_F64, NaN }, + { 5, QNAN_F64, NaN }, + { 6, INF_F64, Inf }, + { 7, NEG_INF_F64, Neg_Inf }, + } + + for d, i in data { + assert(i == d.i) + r = classify_f64(d.v) + tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %v != %v", i, #procedure, d.v, r, d.e)) + } +} + +@test +test_trunc_f16 :: proc(t: ^testing.T) { + + using math + + r, v: f16 + + Datum :: struct { + i: int, + v: f16, + e: f16, + } + @static data := []Datum{ + { 0, 10.5, 10 }, // Issue #1574 fract in linalg/glm is broken + { 1, -10.5, -10 }, + + { 2, F16_MAX, F16_MAX }, + { 3, -F16_MAX, -F16_MAX }, + { 4, F16_MIN, 0.0 }, + { 5, -F16_MIN, -0.0 }, + { 6, 0.0, 0.0 }, + { 7, -0.0, -0.0 }, + { 8, 1, 1 }, + { 9, -1, -1 }, + { 10, INF_F16, INF_F16 }, + { 11, NEG_INF_F16, NEG_INF_F16 }, + + /* From https://en.wikipedia.org/wiki/Half-precision_floating-point_format */ + { 12, 0h3C01, 1 }, // 0x1.004p+0 (smallest > 1) + { 13, -0h3C01, -1 }, + { 14, 0h3BFF, 0.0 }, // 0x1.ffcp-1 (largest < 1) + { 15, -0h3BFF, -0.0 }, + { 16, 0h0001, 0.0 }, // 0x0.004p-14 (smallest subnormal) + { 17, -0h0001, -0.0 }, + { 18, 0h03FF, 0.0 }, // 0x0.ffcp-14 (largest subnormal) + { 19, -0h03FF, -0.0 }, + + { 20, 0hC809, -8 }, // -0x1.024p+3 + { 21, 0h4458, 4 }, // 0x1.16p+2 + } + + for d, i in data { + assert(i == d.i) + r = trunc_f16(d.v) + tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %h != %h", i, #procedure, d.v, r, d.e)) + } + + v = SNAN_F16 + r = trunc_f16(v) + tc.expect(t, is_nan_f16(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r)) + + v = QNAN_F16 + r = trunc_f16(v) + tc.expect(t, is_nan_f16(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r)) +} + +@test +test_trunc_f32 :: proc(t: ^testing.T) { + + using math + + r, v: f32 + + Datum :: struct { + i: int, + v: f32, + e: f32, + } + @static data := []Datum{ + { 0, 10.5, 10 }, // Issue #1574 fract in linalg/glm is broken + { 1, -10.5, -10 }, + + { 2, F32_MAX, F32_MAX }, + { 3, -F32_MAX, -F32_MAX }, + { 4, F32_MIN, 0.0 }, + { 5, -F32_MIN, -0.0 }, + { 6, 0.0, 0.0 }, + { 7, -0.0, -0.0 }, + { 8, 1, 1 }, + { 9, -1, -1 }, + { 10, INF_F32, INF_F32 }, + { 11, NEG_INF_F32, NEG_INF_F32 }, + + /* From https://en.wikipedia.org/wiki/Single-precision_floating-point_format */ + { 12, 0h3F80_0001, 1 }, // 0x1.000002p+0 (smallest > 1) + { 13, -0h3F80_0001, -1 }, + { 14, 0h3F7F_FFFF, 0.0 }, // 0x1.fffffep-1 (largest < 1) + { 15, -0h3F7F_FFFF, -0.0 }, + { 16, 0h0000_0001, 0.0 }, // 0x0.000002p-126 (smallest subnormal) + { 17, -0h0000_0001, -0.0 }, + { 18, 0h007F_FFFF, 0.0 }, // 0x0.fffffep-126 (largest subnormal) + { 19, -0h007F_FFFF, -0.0 }, + + /* From libc-test src/math/sanity/truncf.h */ + { 20, 0hC101_11D0, -8 }, // -0x1.0223ap+3 + { 21, 0h408B_0C34, 4 }, // 0x1.161868p+2 + { 22, 0hC106_1A5A, -8 }, // -0x1.0c34b4p+3 + { 23, 0hC0D1_0378, -6 }, // -0x1.a206fp+2 + { 24, 0h4114_45DE, 9 }, // 0x1.288bbcp+3 + { 25, 0h3F29_77E8, 0.0 }, // 0x1.52efdp-1 + { 26, 0hBED0_2E64, -0.0 }, // -0x1.a05cc8p-2 + { 27, 0h3F0F_CF7D, 0.0 }, // 0x1.1f9efap-1 + { 28, 0h3F46_2ED8, 0.0 }, // 0x1.8c5dbp-1 + { 29, 0hBF2D_C375, -0.0 }, // -0x1.5b86eap-1 + } + + for d, i in data { + assert(i == d.i) + r = trunc_f32(d.v) + tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %h != %h", i, #procedure, d.v, r, d.e)) + } + + v = SNAN_F32 + r = trunc_f32(v) + tc.expect(t, is_nan_f32(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r)) + + v = QNAN_F32 + r = trunc_f32(v) + tc.expect(t, is_nan_f32(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r)) +} + +@test +test_trunc_f64 :: proc(t: ^testing.T) { + + using math + + r, v: f64 + + Datum :: struct { + i: int, + v: f64, + e: f64, + } + data := []Datum{ + { 0, 10.5, 10 }, // Issue #1574 fract in linalg/glm is broken + { 1, -10.5, -10 }, + + { 2, F64_MAX, F64_MAX }, + { 3, -F64_MAX, -F64_MAX }, + { 4, F64_MIN, 0.0 }, + { 5, -F64_MIN, -0.0 }, + { 6, 0.0, 0.0 }, + { 7, -0.0, -0.0 }, + { 8, 1, 1 }, + { 9, -1, -1 }, + { 10, INF_F64, INF_F64 }, + { 11, NEG_INF_F64, NEG_INF_F64 }, + + /* From https://en.wikipedia.org/wiki/Double-precision_floating-point_format */ + { 12, 0h3FF0_0000_0000_0001, 1 }, // 0x1.0000000000001p+0 (smallest > 1) + { 13, -0h3FF0_0000_0000_0001, -1 }, + { 14, 0h3FEF_FFFF_FFFF_FFFF, 0.0 }, // 0x1.fffffffffffffp-1 (largest < 1) + { 15, -0h3FEF_FFFF_FFFF_FFFF, -0.0 }, + { 16, 0h0000_0000_0000_0001, 0.0 }, // 0x0.0000000000001p-1022 (smallest subnormal) + { 17, -0h0000_0000_0000_0001, -0.0 }, + { 18, 0h000F_FFFF_FFFF_FFFF, 0.0 }, // 0x0.fffffffffffffp-1022 (largest subnormal) + { 19, -0h000F_FFFF_FFFF_FFFF, -0.0 }, + + /* From libc-test src/math/sanity/trunc.h */ + { 20, 0hC020_2239_F3C6_A8F1, -8 }, // -0x1.02239f3c6a8f1p+3 + { 21, 0h4011_6186_8E18_BC67, 4 }, // 0x1.161868e18bc67p+2 + { 22, 0hC020_C34B_3E01_E6E7, -8 }, // -0x1.0c34b3e01e6e7p+3 + { 23, 0hC01A_206F_0A19_DCC4, -6 }, // -0x1.a206f0a19dcc4p+2 + { 24, 0h4022_88BB_B0D6_A1E6, 9 }, // 0x1.288bbb0d6a1e6p+3 + { 25, 0h3FE5_2EFD_0CD8_0497, 0.0 }, // 0x1.52efd0cd80497p-1 + { 26, 0hBFDA_05CC_7544_81D1, -0.0 }, // -0x1.a05cc754481d1p-2 + { 27, 0h3FE1_F9EF_9347_45CB, 0.0 }, // 0x1.1f9ef934745cbp-1 + { 28, 0h3FE8_C5DB_097F_7442, 0.0 }, // 0x1.8c5db097f7442p-1 + { 29, 0hBFE5_B86E_A811_8A0E, -0.0 }, // -0x1.5b86ea8118a0ep-1 + } + + for d, i in data { + assert(i == d.i) + r = trunc_f64(d.v) + tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %h != %h", i, #procedure, d.v, r, d.e)) + } + + v = SNAN_F64 + r = trunc_f64(v) + tc.expect(t, is_nan_f64(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r)) + + v = QNAN_F64 + r = trunc_f64(v) + tc.expect(t, is_nan_f64(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r)) +} diff --git a/tests/core/odin/test_parser.odin b/tests/core/odin/test_parser.odin index 90d913d10..3837436bc 100644 --- a/tests/core/odin/test_parser.odin +++ b/tests/core/odin/test_parser.odin @@ -2,7 +2,7 @@ package test_core_odin_parser import "core:testing" import "core:fmt" - +import "core:os" import "core:odin/parser" @@ -10,31 +10,31 @@ TEST_count := 0 TEST_fail := 0 when ODIN_TEST { - expect :: testing.expect - log :: testing.log + expect :: testing.expect + log :: testing.log } else { - expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) { - fmt.printf("[%v] ", loc) - TEST_count += 1 - if !condition { - TEST_fail += 1 - fmt.println(message) - return - } - fmt.println(" PASS") - } - log :: proc(t: ^testing.T, v: any, loc := #caller_location) { - fmt.printf("[%v] ", loc) - fmt.printf("log: %v\n", v) - } + expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) { + TEST_count += 1 + if !condition { + TEST_fail += 1 + fmt.printf("[%v] %v\n", loc, message) + return + } + } + log :: proc(t: ^testing.T, v: any, loc := #caller_location) { + fmt.printf("[%v] ", loc) + fmt.printf("log: %v\n", v) + } } - main :: proc() { - t := testing.T{} - test_parse_demo(&t) + t := testing.T{} + test_parse_demo(&t) - fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count) + fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count) + if TEST_fail > 0 { + os.exit(1) + } } @@ -47,4 +47,4 @@ test_parse_demo :: proc(t: ^testing.T) { for key, value in pkg.files { expect(t, value.syntax_error_count == 0, fmt.tprintf("%v should contain zero errors", key)) } -} +} \ No newline at end of file diff --git a/tests/core/os/test_core_os_exit.odin b/tests/core/os/test_core_os_exit.odin new file mode 100644 index 000000000..2ab274f5e --- /dev/null +++ b/tests/core/os/test_core_os_exit.odin @@ -0,0 +1,10 @@ +// Tests that Odin run returns exit code of built executable on Unix +// Needs exit status to be inverted to return 0 on success, e.g. +// $(./odin run tests/core/os/test_core_os_exit.odin && exit 1 || exit 0) +package test_core_os_exit + +import "core:os" + +main :: proc() { + os.exit(1) +} diff --git a/tests/core/path/filepath/test_core_filepath.odin b/tests/core/path/filepath/test_core_filepath.odin new file mode 100644 index 000000000..0268fb62c --- /dev/null +++ b/tests/core/path/filepath/test_core_filepath.odin @@ -0,0 +1,123 @@ +// Tests "path.odin" in "core:path/filepath". +// Must be run with `-collection:tests=` flag, e.g. +// ./odin run tests/core/path/filepath/test_core_filepath.odin -collection:tests=tests +package test_core_filepath + +import "core:fmt" +import "core:path/filepath" +import "core:testing" +import tc "tests:common" + +main :: proc() { + t := testing.T{} + + when ODIN_OS == .Windows { + test_split_list_windows(&t) + } else { + test_split_list_unix(&t) + } + + tc.report(&t) +} + +@test +test_split_list_windows :: proc(t: ^testing.T) { + + using filepath + + Datum :: struct { + i: int, + v: string, + e: [3]string, + } + @static data := []Datum{ + { 0, "C:\\Odin;C:\\Visual Studio;\"C:\\Some Other\"", + [3]string{"C:\\Odin", "C:\\Visual Studio", "C:\\Some Other"} }, // Issue #1537 + { 1, "a;;b", [3]string{"a", "", "b"} }, + { 2, "a;b;", [3]string{"a", "b", ""} }, + { 3, ";a;b", [3]string{"", "a", "b"} }, + { 4, ";;", [3]string{"", "", ""} }, + { 5, "\"a;b\"c;d;\"f\"", [3]string{"a;bc", "d", "f"} }, + { 6, "\"a;b;c\";d\";e\";f", [3]string{"a;b;c", "d;e", "f"} }, + } + + for d, i in data { + assert(i == d.i, fmt.tprintf("wrong data index: i %d != d.i %d\n", i, d.i)) + r := split_list(d.v) + defer delete(r) + tc.expect(t, len(r) == len(d.e), fmt.tprintf("i:%d %s(%s) len(r) %d != len(d.e) %d", + i, #procedure, d.v, len(r), len(d.e))) + if len(r) == len(d.e) { + for _, j in r { + tc.expect(t, r[j] == d.e[j], fmt.tprintf("i:%d %s(%v) -> %v[%d] != %v", + i, #procedure, d.v, r[j], j, d.e[j])) + } + } + } + + { + v := "" + r := split_list(v) + tc.expect(t, r == nil, fmt.tprintf("%s(%s) -> %v != nil", #procedure, v, r)) + } + { + v := "a" + r := split_list(v) + defer delete(r) + tc.expect(t, len(r) == 1, fmt.tprintf("%s(%s) len(r) %d != 1", #procedure, v, len(r))) + if len(r) == 1 { + tc.expect(t, r[0] == "a", fmt.tprintf("%s(%v) -> %v[0] != a", #procedure, v, r[0])) + } + } +} + +@test +test_split_list_unix :: proc(t: ^testing.T) { + + using filepath + + Datum :: struct { + i: int, + v: string, + e: [3]string, + } + @static data := []Datum{ + { 0, "/opt/butler:/home/fancykillerpanda/Projects/Odin/Odin:/usr/local/sbin", + [3]string{"/opt/butler", "/home/fancykillerpanda/Projects/Odin/Odin", "/usr/local/sbin"} }, // Issue #1537 + { 1, "a::b", [3]string{"a", "", "b"} }, + { 2, "a:b:", [3]string{"a", "b", ""} }, + { 3, ":a:b", [3]string{"", "a", "b"} }, + { 4, "::", [3]string{"", "", ""} }, + { 5, "\"a:b\"c:d:\"f\"", [3]string{"a:bc", "d", "f"} }, + { 6, "\"a:b:c\":d\":e\":f", [3]string{"a:b:c", "d:e", "f"} }, + } + + for d, i in data { + assert(i == d.i, fmt.tprintf("wrong data index: i %d != d.i %d\n", i, d.i)) + r := split_list(d.v) + defer delete(r) + tc.expect(t, len(r) == len(d.e), fmt.tprintf("i:%d %s(%s) len(r) %d != len(d.e) %d", + i, #procedure, d.v, len(r), len(d.e))) + if len(r) == len(d.e) { + for _, j in r { + tc.expect(t, r[j] == d.e[j], fmt.tprintf("i:%d %s(%v) -> %v[%d] != %v", + i, #procedure, d.v, r[j], j, d.e[j])) + } + } + } + + { + v := "" + r := split_list(v) + tc.expect(t, r == nil, fmt.tprintf("%s(%s) -> %v != nil", #procedure, v, r)) + } + { + v := "a" + r := split_list(v) + defer delete(r) + tc.expect(t, len(r) == 1, fmt.tprintf("%s(%s) len(r) %d != 1", #procedure, v, len(r))) + if len(r) == 1 { + tc.expect(t, r[0] == "a", fmt.tprintf("%s(%v) -> %v[0] != a", #procedure, v, r[0])) + } + } +} diff --git a/tests/core/reflect/test_core_reflect.odin b/tests/core/reflect/test_core_reflect.odin new file mode 100644 index 000000000..039501735 --- /dev/null +++ b/tests/core/reflect/test_core_reflect.odin @@ -0,0 +1,288 @@ +// Tests "core:reflect/reflect". +// Must be run with `-collection:tests=` flag, e.g. +// ./odin run tests/core/reflect/test_core_reflect.odin -out=tests/core/test_core_reflect -collection:tests=./tests +package test_core_reflect + +import "core:fmt" +import "core:reflect" +import "core:testing" +import tc "tests:common" + +main :: proc() { + t := testing.T{} + + test_as_u64(&t) + test_as_f64(&t) + + tc.report(&t) +} + +@test +test_as_u64 :: proc(t: ^testing.T) { + using reflect + + { + /* i8 */ + Datum :: struct { i: int, v: i8, e: u64 } + @static data := []Datum{ + { 0, 0x7F, 0x7F }, + { 1, -1, 0xFFFF_FFFF_FFFF_FFFF }, + { 2, -0x80, 0xFFFF_FFFF_FFFF_FF80 }, + } + + for d, i in data { + assert(i == d.i) + r, valid := as_u64(d.v) + tc.expect(t, valid, fmt.tprintf("i:%d %s(i8 %v) !valid\n", i, #procedure, d.v)) + tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i8 %v) -> %v (0x%X) != %v (0x%X)\n", + i, #procedure, d.v, r, r, d.e, d.e)) + } + } + { + /* i16 */ + Datum :: struct { i: int, v: i16, e: u64 } + @static data := []Datum{ + { 0, 0x7FFF, 0x7FFF }, + { 1, -1, 0xFFFF_FFFF_FFFF_FFFF }, + { 2, -0x8000, 0xFFFF_FFFF_FFFF_8000 }, + } + + for d, i in data { + assert(i == d.i) + r, valid := as_u64(d.v) + tc.expect(t, valid, fmt.tprintf("i:%d %s(i16 %v) !valid\n", i, #procedure, d.v)) + tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i16 %v) -> %v (0x%X) != %v (0x%X)\n", + i, #procedure, d.v, r, r, d.e, d.e)) + } + } + { + /* i32 */ + Datum :: struct { i: int, v: i32, e: u64 } + @static data := []Datum{ + { 0, 0x7FFF_FFFF, 0x7FFF_FFFF }, + { 1, -1, 0xFFFF_FFFF_FFFF_FFFF }, + { 2, -0x8000_0000, 0xFFFF_FFFF_8000_0000 }, + } + + for d, i in data { + assert(i == d.i) + r, valid := as_u64(d.v) + tc.expect(t, valid, fmt.tprintf("i:%d %s(i32 %v) !valid\n", i, #procedure, d.v)) + tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i32 %v) -> %v (0x%X) != %v (0x%X)\n", + i, #procedure, d.v, r, r, d.e, d.e)) + } + } + { + /* i64 */ + Datum :: struct { i: int, v: i64, e: u64 } + @static data := []Datum{ + { 0, 0x7FFF_FFFF_FFFF_FFFF, 0x7FFF_FFFF_FFFF_FFFF }, + { 1, -1, 0xFFFF_FFFF_FFFF_FFFF }, + { 2, -0x8000_0000_0000_0000, 0x8000_0000_0000_0000 }, + } + + for d, i in data { + assert(i == d.i) + r, valid := as_u64(d.v) + tc.expect(t, valid, fmt.tprintf("i:%d %s(i64 %v) !valid\n", i, #procedure, d.v)) + tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i64 %v) -> %v (0x%X) != %v (0x%X)\n", + i, #procedure, d.v, r, r, d.e, d.e)) + } + } + { + /* i128 */ + Datum :: struct { i: int, v: i128, e: u64 } + @static data := []Datum{ + { 0, 0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF }, + { 1, -1, 0xFFFF_FFFF_FFFF_FFFF }, + { 2, 0x8000_0000_0000_0000, 0x8000_0000_0000_0000 }, + { 3, -0x8000_0000_0000_0000, 0x8000_0000_0000_0000 }, + { 4, 0x0001_0000_0000_0000_0000, 0 }, + { 5, -0x8000_0000_0000_0000_0000_0000_0000_0000, 0 }, + } + + for d, i in data { + assert(i == d.i) + r, valid := as_u64(d.v) + tc.expect(t, valid, fmt.tprintf("i:%d %s(i128 %v) !valid\n", i, #procedure, d.v)) + tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i128 %v) -> %v (0x%X) != %v (0x%X)\n", + i, #procedure, d.v, r, r, d.e, d.e)) + } + } + { + /* f16 */ + Datum :: struct { i: int, v: f16, e: u64 } + @static data := []Datum{ + { 0, 1.2, 1 }, + { 1, 123.12, 123 }, + } + + for d, i in data { + assert(i == d.i) + r, valid := as_u64(d.v) + tc.expect(t, valid, fmt.tprintf("i:%d %s(f16 %v) !valid\n", i, #procedure, d.v)) + tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f16 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e)) + } + } + { + /* f32 */ + Datum :: struct { i: int, v: f32, e: u64 } + @static data := []Datum{ + { 0, 123.3415, 123 }, + } + + for d, i in data { + assert(i == d.i) + r, valid := as_u64(d.v) + tc.expect(t, valid, fmt.tprintf("i:%d %s(f32 %v) !valid\n", i, #procedure, d.v)) + tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f32 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e)) + } + } + { + /* f64 */ + Datum :: struct { i: int, v: f64, e: u64 } + @static data := []Datum{ + { 0, 12345345345.3415234234, 12345345345 }, + } + + for d, i in data { + assert(i == d.i) + r, valid := as_u64(d.v) + tc.expect(t, valid, fmt.tprintf("i:%d %s(f64 %v) !valid\n", i, #procedure, d.v)) + tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f64 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e)) + } + } +} + +@test +test_as_f64 :: proc(t: ^testing.T) { + using reflect + + { + /* i8 */ + Datum :: struct { i: int, v: i8, e: f64 } + @static data := []Datum{ + { 0, 0x7F, 0x7F }, + { 1, -1, -1 }, + { 2, -0x80, -0x80 }, + } + + for d, i in data { + assert(i == d.i) + r, valid := as_f64(d.v) + tc.expect(t, valid, fmt.tprintf("i:%d %s(i8 %v) !valid\n", i, #procedure, d.v)) + tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i8 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e)) + } + } + { + /* i16 */ + Datum :: struct { i: int, v: i16, e: f64 } + @static data := []Datum{ + { 0, 0x7FFF, 0x7FFF }, + { 1, -1, -1 }, + { 2, -0x8000, -0x8000 }, + } + + for d, i in data { + assert(i == d.i) + r, valid := as_f64(d.v) + tc.expect(t, valid, fmt.tprintf("i:%d %s(i16 %v) !valid\n", i, #procedure, d.v)) + tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i16 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e)) + } + } + { + /* i32 */ + Datum :: struct { i: int, v: i32, e: f64 } + @static data := []Datum{ + { 0, 0x7FFF_FFFF, 0x7FFF_FFFF }, + { 1, -1, -1 }, + { 2, -0x8000_0000, -0x8000_0000 }, + } + + for d, i in data { + assert(i == d.i) + r, valid := as_f64(d.v) + tc.expect(t, valid, fmt.tprintf("i:%d %s(i32 %v) !valid\n", i, #procedure, d.v)) + tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i32 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e)) + } + } + { + /* i64 */ + Datum :: struct { i: int, v: i64, e: f64 } + @static data := []Datum{ + { 0, 0x7FFF_FFFF_FFFF_FFFF, 0x7FFF_FFFF_FFFF_FFFF }, + { 1, -1, -1 }, + { 2, -0x8000_0000_0000_0000, -0x8000_0000_0000_0000 }, + } + + for d, i in data { + assert(i == d.i) + r, valid := as_f64(d.v) + tc.expect(t, valid, fmt.tprintf("i:%d %s(i64 %v) !valid\n", i, #procedure, d.v)) + tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i64 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e)) + } + } + { + /* i128 */ + Datum :: struct { i: int, v: i128, e: f64 } + @static data := []Datum{ + { 0, 0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF, 0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF }, + { 1, -1, -1 }, + { 2, 0x8000_0000_0000_0000_0000_0000_0000, 0x8000_0000_0000_0000_0000_0000_0000 }, + { 3, -0x8000_0000_0000_0000_0000_0000_0000_0000, -0x8000_0000_0000_0000_0000_0000_0000_0000 }, + } + + for d, i in data { + assert(i == d.i) + r, valid := as_f64(d.v) + tc.expect(t, valid, fmt.tprintf("i:%d %s(i128 %v) !valid\n", i, #procedure, d.v)) + tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i128 %v) -> %v (%H) != %v (%H)\n", + i, #procedure, d.v, r, r, d.e, d.e)) + } + } + { + /* f16 */ + Datum :: struct { i: int, v: f16, e: f64 } + @static data := []Datum{ + { 0, 1.2, 0h3FF3_3400_0000_0000 }, // Precision difference TODO: check + { 1, 123.12, 0h405E_C800_0000_0000 }, // Precision difference TODO: check + } + + for d, i in data { + assert(i == d.i) + r, valid := as_f64(d.v) + tc.expect(t, valid, fmt.tprintf("i:%d %s(f16 %v) !valid\n", i, #procedure, d.v)) + tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f16 %v (%H)) -> %v (%H) != %v (%H)\n", + i, #procedure, d.v, d.v, r, r, d.e, d.e)) + } + } + { + /* f32 */ + Datum :: struct { i: int, v: f32, e: f64 } + @static data := []Datum{ + { 0, 123.3415, 0h405E_D5DB_2000_0000 }, // Precision difference TODO: check + } + + for d, i in data { + assert(i == d.i) + r, valid := as_f64(d.v) + tc.expect(t, valid, fmt.tprintf("i:%d %s(f32 %v) !valid\n", i, #procedure, d.v)) + tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f32 %v (%H)) -> %v (%H) != %v (%H)\n", + i, #procedure, d.v, d.v, r, r, d.e, d.e)) + } + } + { + /* f64 */ + Datum :: struct { i: int, v: f64, e: f64 } + @static data := []Datum{ + { 0, 12345345345.3415234234, 12345345345.3415234234 }, + } + + for d, i in data { + assert(i == d.i) + r, valid := as_f64(d.v) + tc.expect(t, valid, fmt.tprintf("i:%d %s(f64 %v) !valid\n", i, #procedure, d.v)) + tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f64 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e)) + } + } +} diff --git a/tests/core/strings/test_core_strings.odin b/tests/core/strings/test_core_strings.odin index fc1518349..e97734dda 100644 --- a/tests/core/strings/test_core_strings.odin +++ b/tests/core/strings/test_core_strings.odin @@ -1,63 +1,92 @@ -package test_core_image +package test_core_strings import "core:strings" import "core:testing" import "core:fmt" +import "core:os" TEST_count := 0 TEST_fail := 0 when ODIN_TEST { - expect :: testing.expect - log :: testing.log + expect :: testing.expect + log :: testing.log } else { - expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) { - fmt.printf("[%v] ", loc) - TEST_count += 1 - if !condition { - TEST_fail += 1 - fmt.println(message) - return - } - fmt.println(" PASS") - } - log :: proc(t: ^testing.T, v: any, loc := #caller_location) { - fmt.printf("[%v] ", loc) - fmt.printf("log: %v\n", v) - } + expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) { + TEST_count += 1 + if !condition { + TEST_fail += 1 + fmt.printf("[%v] %v\n", loc, message) + return + } + } + log :: proc(t: ^testing.T, v: any, loc := #caller_location) { + fmt.printf("[%v] ", loc) + fmt.printf("log: %v\n", v) + } } main :: proc() { - t := testing.T{} - test_index_any_small_string_not_found(&t) - test_index_any_larger_string_not_found(&t) - test_index_any_small_string_found(&t) - test_index_any_larger_string_found(&t) + t := testing.T{} + test_index_any_small_string_not_found(&t) + test_index_any_larger_string_not_found(&t) + test_index_any_small_string_found(&t) + test_index_any_larger_string_found(&t) + test_cut(&t) - fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count) + fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count) + if TEST_fail > 0 { + os.exit(1) + } } @test test_index_any_small_string_not_found :: proc(t: ^testing.T) { - index := strings.index_any(".", "/:\"") - log(t, index) - expect(t, index == -1, "index_any should be negative") + index := strings.index_any(".", "/:\"") + expect(t, index == -1, "index_any should be negative") } @test test_index_any_larger_string_not_found :: proc(t: ^testing.T) { - index := strings.index_any("aaaaaaaa.aaaaaaaa", "/:\"") - expect(t, index == -1, "index_any should be negative") + index := strings.index_any("aaaaaaaa.aaaaaaaa", "/:\"") + expect(t, index == -1, "index_any should be negative") } @test test_index_any_small_string_found :: proc(t: ^testing.T) { - index := strings.index_any(".", "/:.\"") - expect(t, index == 0, "index_any should be 0") + index := strings.index_any(".", "/:.\"") + expect(t, index == 0, "index_any should be 0") } @test test_index_any_larger_string_found :: proc(t: ^testing.T) { - index := strings.index_any("aaaaaaaa:aaaaaaaa", "/:\"") - expect(t, index == 8, "index_any should be 8") + index := strings.index_any("aaaaaaaa:aaaaaaaa", "/:\"") + expect(t, index == 8, "index_any should be 8") } + +Cut_Test :: struct { + input: string, + offset: int, + length: int, + output: string, +} + +cut_tests :: []Cut_Test{ + {"some example text", 0, 4, "some" }, + {"some example text", 2, 2, "me" }, + {"some example text", 5, 7, "example" }, + {"some example text", 5, 0, "example text"}, + {"恥ずべきフクロウ", 4, 0, "フクロウ" }, +} + +@test +test_cut :: proc(t: ^testing.T) { + for test in cut_tests { + res := strings.cut(test.input, test.offset, test.length) + defer delete(res) + + msg := fmt.tprintf("cut(\"%v\", %v, %v) expected to return \"%v\", got \"%v\"", + test.input, test.offset, test.length, test.output, res) + expect(t, res == test.output, msg) + } +} \ No newline at end of file diff --git a/tests/core/text/i18n/test_core_text_i18n.odin b/tests/core/text/i18n/test_core_text_i18n.odin new file mode 100644 index 000000000..ba668c4fd --- /dev/null +++ b/tests/core/text/i18n/test_core_text_i18n.odin @@ -0,0 +1,165 @@ +package test_core_text_i18n + +import "core:mem" +import "core:fmt" +import "core:os" +import "core:testing" +import "core:text/i18n" + +TEST_count := 0 +TEST_fail := 0 + +when ODIN_TEST { + expect :: testing.expect + log :: testing.log +} else { + expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) { + TEST_count += 1 + if !condition { + TEST_fail += 1 + fmt.printf("[%v] %v\n", loc, message) + return + } + } + log :: proc(t: ^testing.T, v: any, loc := #caller_location) { + fmt.printf("[%v] ", loc) + fmt.printf("log: %v\n", v) + } +} +T :: i18n.get + +Test :: struct { + section: string, + key: string, + val: string, + n: int, +} + +Test_Suite :: struct { + file: string, + loader: proc(string, i18n.Parse_Options, proc(int) -> int, mem.Allocator) -> (^i18n.Translation, i18n.Error), + err: i18n.Error, + options: i18n.Parse_Options, + tests: []Test, +} + +TESTS := []Test_Suite{ + { + file = "assets/I18N/nl_NL.mo", + loader = i18n.parse_mo_file, + tests = { + // These are in the catalog. + { "", "There are 69,105 leaves here.", "Er zijn hier 69.105 bladeren.", 1 }, + { "", "Hellope, World!", "Hallo, Wereld!", 1 }, + { "", "There is %d leaf.\n", "Er is %d blad.\n", 1 }, + { "", "There are %d leaves.\n", "Er is %d blad.\n", 1 }, + { "", "There is %d leaf.\n", "Er zijn %d bladeren.\n", 42 }, + { "", "There are %d leaves.\n", "Er zijn %d bladeren.\n", 42 }, + + // This isn't in the catalog, so should ruturn the key. + { "", "Come visit us on Discord!", "Come visit us on Discord!", 1 }, + }, + }, + + // QT Linguist with default loader options. + { + file = "assets/I18N/nl_NL-qt-ts.ts", + loader = i18n.parse_qt_linguist_file, + tests = { + // These are in the catalog. + { "Page", "Text for translation", "Tekst om te vertalen", 1}, + { "Page", "Also text to translate", "Ook tekst om te vertalen", 1}, + { "installscript", "99 bottles of beer on the wall", "99 flessen bier op de muur", 1}, + { "apple_count", "%d apple(s)", "%d appel", 1}, + { "apple_count", "%d apple(s)", "%d appels", 42}, + + // These aren't in the catalog, so should ruturn the key. + { "", "Come visit us on Discord!", "Come visit us on Discord!", 1 }, + { "Fake_Section", "Come visit us on Discord!", "Come visit us on Discord!", 1 }, + }, + }, + + // QT Linguist, merging sections. + { + file = "assets/I18N/nl_NL-qt-ts.ts", + loader = i18n.parse_qt_linguist_file, + options = {merge_sections = true}, + tests = { + // All of them are now in section "", lookup with original section should return the key. + { "", "Text for translation", "Tekst om te vertalen", 1}, + { "", "Also text to translate", "Ook tekst om te vertalen", 1}, + { "", "99 bottles of beer on the wall", "99 flessen bier op de muur", 1}, + { "", "%d apple(s)", "%d appel", 1}, + { "", "%d apple(s)", "%d appels", 42}, + + // All of them are now in section "", lookup with original section should return the key. + { "Page", "Text for translation", "Text for translation", 1}, + { "Page", "Also text to translate", "Also text to translate", 1}, + { "installscript", "99 bottles of beer on the wall", "99 bottles of beer on the wall", 1}, + { "apple_count", "%d apple(s)", "%d apple(s)", 1}, + { "apple_count", "%d apple(s)", "%d apple(s)", 42}, + }, + }, + + // QT Linguist, merging sections. Expecting .Duplicate_Key error because same key exists in more than 1 section. + { + file = "assets/I18N/duplicate-key.ts", + loader = i18n.parse_qt_linguist_file, + options = {merge_sections = true}, + err = .Duplicate_Key, + }, + + // QT Linguist, not merging sections. Shouldn't return error despite same key existing in more than 1 section. + { + file = "assets/I18N/duplicate-key.ts", + loader = i18n.parse_qt_linguist_file, + }, +} + +@test +tests :: proc(t: ^testing.T) { + using fmt + + cat: ^i18n.Translation + err: i18n.Error + + for suite in TESTS { + cat, err = suite.loader(suite.file, suite.options, nil, context.allocator) + + msg := fmt.tprintf("Expected loading %v to return %v, got %v", suite.file, suite.err, err) + expect(t, err == suite.err, msg) + + if err == .None { + for test in suite.tests { + val := T(test.section, test.key, test.n, cat) + + msg = fmt.tprintf("Expected key `%v` from section `%v`'s form for value `%v` to equal `%v`, got `%v`", test.key, test.section, test.n, test.val, val) + expect(t, val == test.val, msg) + } + } + i18n.destroy(cat) + } +} + +main :: proc() { + using fmt + + track: mem.Tracking_Allocator + mem.tracking_allocator_init(&track, context.allocator) + context.allocator = mem.tracking_allocator(&track) + + t := testing.T{} + tests(&t) + + fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count) + if TEST_fail > 0 { + os.exit(1) + } + + if len(track.allocation_map) > 0 { + println() + for _, v in track.allocation_map { + printf("%v Leaked %v bytes.\n", v.location, v.size) + } + } +} \ No newline at end of file diff --git a/tests/issues/run.bat b/tests/issues/run.bat new file mode 100644 index 000000000..a7078ae0f --- /dev/null +++ b/tests/issues/run.bat @@ -0,0 +1,17 @@ +@echo off + +if not exist "build\" mkdir build + +set COMMON=-collection:tests=.. -out:build\test_issue.exe + +@echo on + +..\..\odin build test_issue_829.odin %COMMON% -file +build\test_issue + +..\..\odin build test_issue_1592.odin %COMMON% -file +build\test_issue + +@echo off + +rmdir /S /Q build diff --git a/tests/issues/run.sh b/tests/issues/run.sh new file mode 100755 index 000000000..ec0804bac --- /dev/null +++ b/tests/issues/run.sh @@ -0,0 +1,18 @@ +#!/bin/bash +set -eu + +mkdir -p build +ODIN=../../odin +COMMON="-collection:tests=.. -out:build/test_issue" + +set -x + +$ODIN build test_issue_829.odin $COMMON -file +./build/test_issue + +$ODIN build test_issue_1592.odin $COMMON -file +./build/test_issue + +set +x + +rm -rf build diff --git a/tests/issues/test_issue_1592.odin b/tests/issues/test_issue_1592.odin new file mode 100644 index 000000000..bb350a30b --- /dev/null +++ b/tests/issues/test_issue_1592.odin @@ -0,0 +1,489 @@ +// Tests issue #1592 https://github.com/odin-lang/Odin/issues/1592 +package test_issues + +import "core:fmt" +import "core:testing" +import tc "tests:common" + +main :: proc() { + t := testing.T{} + + /* This won't short-circuit */ + test_orig() + + /* These will short-circuit */ + test_simple_const_false(&t) + test_simple_const_true(&t) + + /* These won't short-circuit */ + test_simple_proc_false(&t) + test_simple_proc_true(&t) + + /* These won't short-circuit */ + test_const_false_const_false(&t) + test_const_false_const_true(&t) + test_const_true_const_false(&t) + test_const_true_const_true(&t) + + /* These won't short-circuit */ + test_proc_false_const_false(&t) + test_proc_false_const_true(&t) + test_proc_true_const_false(&t) + test_proc_true_const_true(&t) + + tc.report(&t) +} + +/* Original issue #1592 example */ + +// I get a LLVM code gen error when this constant is false, but it works when it is true +CONSTANT_BOOL :: false + +bool_result :: proc() -> bool { + return false +} + +@test +test_orig :: proc() { + if bool_result() || CONSTANT_BOOL { + } +} + +CONSTANT_FALSE :: false +CONSTANT_TRUE :: true + +false_result :: proc() -> bool { + return false +} +true_result :: proc() -> bool { + return true +} + +@test +test_simple_const_false :: proc(t: ^testing.T) { + if CONSTANT_FALSE { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } else { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } + if (CONSTANT_FALSE) { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } else { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } + if !CONSTANT_FALSE { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } else { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } + if (!CONSTANT_FALSE) { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } else { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } + if !(CONSTANT_FALSE) { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } else { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } + if !!CONSTANT_FALSE { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } else { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } + if CONSTANT_FALSE == true { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } else { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } + if CONSTANT_FALSE == false { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } else { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } + if !(CONSTANT_FALSE == true) { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } else { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } + if !(CONSTANT_FALSE == false) { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } else { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } +} + +@test +test_simple_const_true :: proc(t: ^testing.T) { + if CONSTANT_TRUE { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } else { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } + if (CONSTANT_TRUE) { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } else { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } + if !CONSTANT_TRUE { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } else { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } + if (!CONSTANT_TRUE) { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } else { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } + if (!CONSTANT_TRUE) { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } else { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } + if !(CONSTANT_TRUE) { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } else { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } + if !!CONSTANT_TRUE { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } else { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } + if CONSTANT_TRUE == true { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } else { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } + if CONSTANT_TRUE == false { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } else { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } + if !(CONSTANT_TRUE == true) { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } else { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } + if !(CONSTANT_TRUE == false) { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } else { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } +} + +@test +test_simple_proc_false :: proc(t: ^testing.T) { + if false_result() { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } else { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } + if !false_result() { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } else { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } +} + +@test +test_simple_proc_true :: proc(t: ^testing.T) { + if true_result() { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } else { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } + if !true_result() { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } else { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } +} + +@test +test_const_false_const_false :: proc(t: ^testing.T) { + if CONSTANT_FALSE || CONSTANT_FALSE { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } else { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } + if CONSTANT_FALSE && CONSTANT_FALSE { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } else { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } + + if !CONSTANT_FALSE || CONSTANT_FALSE { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } else { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } + if !CONSTANT_FALSE && CONSTANT_FALSE { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } else { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } + + if CONSTANT_FALSE || !CONSTANT_FALSE { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } else { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } + if CONSTANT_FALSE && !CONSTANT_FALSE { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } else { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } + + if !(CONSTANT_FALSE || CONSTANT_FALSE) { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } else { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } + if !(CONSTANT_FALSE && CONSTANT_FALSE) { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } else { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } +} + +@test +test_const_false_const_true :: proc(t: ^testing.T) { + if CONSTANT_FALSE || CONSTANT_TRUE { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } else { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } + if CONSTANT_FALSE && CONSTANT_TRUE { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } else { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } + + if !CONSTANT_FALSE || CONSTANT_TRUE { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } else { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } + if !CONSTANT_FALSE && CONSTANT_TRUE { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } else { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } + + if CONSTANT_FALSE || !CONSTANT_TRUE { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } else { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } + if CONSTANT_FALSE && !CONSTANT_TRUE { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } else { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } + + if !(CONSTANT_FALSE || CONSTANT_TRUE) { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } else { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } + if !(CONSTANT_FALSE && CONSTANT_TRUE) { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } else { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } +} + +@test +test_const_true_const_false :: proc(t: ^testing.T) { + if CONSTANT_TRUE || CONSTANT_FALSE { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } else { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } + if CONSTANT_TRUE && CONSTANT_FALSE { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } else { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } + + if !CONSTANT_TRUE || CONSTANT_FALSE { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } else { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } + if !CONSTANT_TRUE && CONSTANT_FALSE { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } else { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } + + if CONSTANT_TRUE || !CONSTANT_FALSE { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } else { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } + if CONSTANT_TRUE && !CONSTANT_FALSE { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } else { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } + + if !(CONSTANT_TRUE || CONSTANT_FALSE) { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } else { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } + if !(CONSTANT_TRUE && CONSTANT_FALSE) { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } else { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } +} + +@test +test_const_true_const_true :: proc(t: ^testing.T) { + if CONSTANT_TRUE || CONSTANT_TRUE { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } else { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } + if CONSTANT_TRUE && CONSTANT_TRUE { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } else { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } + + if !CONSTANT_TRUE || CONSTANT_TRUE { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } else { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } + if !CONSTANT_TRUE && CONSTANT_TRUE { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } else { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } + + if CONSTANT_TRUE || !CONSTANT_TRUE { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } else { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } + if CONSTANT_TRUE && !CONSTANT_TRUE { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } else { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } + + if !(CONSTANT_TRUE || CONSTANT_TRUE) { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } else { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } + if !(CONSTANT_TRUE && CONSTANT_TRUE) { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } else { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } +} + +@test +test_proc_false_const_false :: proc(t: ^testing.T) { + if false_result() || CONSTANT_FALSE { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } else { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } + if false_result() && CONSTANT_FALSE { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } else { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } + + if !(false_result() || CONSTANT_FALSE) { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } else { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } + if !(false_result() && CONSTANT_FALSE) { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } else { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } +} + +@test +test_proc_false_const_true :: proc(t: ^testing.T) { + if false_result() || CONSTANT_TRUE { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } else { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } + if false_result() && CONSTANT_TRUE { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } else { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } + + if !(false_result() || CONSTANT_TRUE) { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } else { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } + if !(false_result() && CONSTANT_TRUE) { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } else { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } +} + +@test +test_proc_true_const_false :: proc(t: ^testing.T) { + if true_result() || CONSTANT_FALSE { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } else { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } + if true_result() && CONSTANT_FALSE { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } else { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } + + if !(true_result() || CONSTANT_FALSE) { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } else { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } + if !(true_result() && CONSTANT_FALSE) { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } else { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } +} + +@test +test_proc_true_const_true :: proc(t: ^testing.T) { + if true_result() || CONSTANT_TRUE { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } else { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } + if true_result() && CONSTANT_TRUE { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } else { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } + + if !(true_result() || CONSTANT_TRUE) { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } else { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } + if !(true_result() && CONSTANT_TRUE) { + tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure)) + } else { + tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure)) + } +} diff --git a/tests/issues/test_issue_829.odin b/tests/issues/test_issue_829.odin new file mode 100644 index 000000000..4ff3d71f1 --- /dev/null +++ b/tests/issues/test_issue_829.odin @@ -0,0 +1,33 @@ +// Tests issue #829 https://github.com/odin-lang/Odin/issues/829 +package test_issues + +import "core:fmt" +import "core:testing" +import tc "tests:common" + +/* Original issue #829 example */ + +env : map[string]proc(a, b : int) -> int = { + "+" = proc(a, b : int) -> int { + return a + b + }, +} + +test_orig :: proc() { + fmt.println(env["+"](1, 2)) +} + +main :: proc() { + t := testing.T{} + + test_orig() + + test_orig_ret(&t) + + tc.report(&t) +} + +test_orig_ret :: proc(t: ^testing.T) { + r := fmt.tprint(env["+"](1, 2)) + tc.expect(t, r == "3", fmt.tprintf("%s: \"%s\" != \"3\"\n", #procedure, r)) +} diff --git a/tests/vendor/Makefile b/tests/vendor/Makefile index f0a456bae..6c68d7908 100644 --- a/tests/vendor/Makefile +++ b/tests/vendor/Makefile @@ -1,6 +1,13 @@ ODIN=../../odin +ODINFLAGS= + +OS=$(shell uname) + +ifeq ($(OS), OpenBSD) + ODINFLAGS:=$(ODINFLAGS) -extra-linker-flags:-L/usr/local/lib +endif all: botan_test botan_test: - $(ODIN) run botan -out=botan_hash -o:speed -no-bounds-check \ No newline at end of file + $(ODIN) run botan -o:speed -no-bounds-check $(ODINFLAGS) -out=vendor_botan diff --git a/tests/vendor/botan/test_vendor_botan.odin b/tests/vendor/botan/test_vendor_botan.odin index e92410621..0a93723c8 100644 --- a/tests/vendor/botan/test_vendor_botan.odin +++ b/tests/vendor/botan/test_vendor_botan.odin @@ -14,6 +14,8 @@ package test_vendor_botan import "core:testing" import "core:fmt" +import "core:os" +import "core:strings" import "vendor:botan/md4" import "vendor:botan/md5" @@ -30,6 +32,7 @@ import "vendor:botan/gost" import "vendor:botan/streebog" import "vendor:botan/sm3" import "vendor:botan/skein512" +import "vendor:botan/siphash" TEST_count := 0 TEST_fail := 0 @@ -82,8 +85,12 @@ main :: proc() { test_sm3(&t) test_skein512_256(&t) test_skein512_512(&t) + test_siphash_2_4(&t) fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count) + if TEST_fail > 0 { + os.exit(1) + } } TestHash :: struct { @@ -166,11 +173,14 @@ test_sha224 :: proc(t: ^testing.T) { // Test vectors from // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf // https://www.di-mgt.com.au/sha_testvectors.html + // https://datatracker.ietf.org/doc/html/rfc3874#section-3.3 + data_1_000_000_a := strings.repeat("a", 1_000_000) test_vectors := [?]TestHash { TestHash{"d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f", ""}, TestHash{"23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7", "abc"}, TestHash{"75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, TestHash{"c97ca9a559850ce97a04a96def6d99a9e0e0e2ab14e6b8df265fc0b3", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"}, + TestHash{"20794655980c91d8bbb4c1ea97618a4bf03f42581948b2ee4ee7ad67", data_1_000_000_a}, } for v, _ in test_vectors { computed := sha2.hash_224(v.str) @@ -575,3 +585,44 @@ test_skein512_512 :: proc(t: ^testing.T) { expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) } } + +@(test) +test_siphash_2_4 :: proc(t: ^testing.T) { + // Test vectors from + // https://github.com/veorq/SipHash/blob/master/vectors.h + test_vectors := [?]u64 { + 0x726fdb47dd0e0e31, 0x74f839c593dc67fd, 0x0d6c8009d9a94f5a, 0x85676696d7fb7e2d, + 0xcf2794e0277187b7, 0x18765564cd99a68d, 0xcbc9466e58fee3ce, 0xab0200f58b01d137, + 0x93f5f5799a932462, 0x9e0082df0ba9e4b0, 0x7a5dbbc594ddb9f3, 0xf4b32f46226bada7, + 0x751e8fbc860ee5fb, 0x14ea5627c0843d90, 0xf723ca908e7af2ee, 0xa129ca6149be45e5, + 0x3f2acc7f57c29bdb, 0x699ae9f52cbe4794, 0x4bc1b3f0968dd39c, 0xbb6dc91da77961bd, + 0xbed65cf21aa2ee98, 0xd0f2cbb02e3b67c7, 0x93536795e3a33e88, 0xa80c038ccd5ccec8, + 0xb8ad50c6f649af94, 0xbce192de8a85b8ea, 0x17d835b85bbb15f3, 0x2f2e6163076bcfad, + 0xde4daaaca71dc9a5, 0xa6a2506687956571, 0xad87a3535c49ef28, 0x32d892fad841c342, + 0x7127512f72f27cce, 0xa7f32346f95978e3, 0x12e0b01abb051238, 0x15e034d40fa197ae, + 0x314dffbe0815a3b4, 0x027990f029623981, 0xcadcd4e59ef40c4d, 0x9abfd8766a33735c, + 0x0e3ea96b5304a7d0, 0xad0c42d6fc585992, 0x187306c89bc215a9, 0xd4a60abcf3792b95, + 0xf935451de4f21df2, 0xa9538f0419755787, 0xdb9acddff56ca510, 0xd06c98cd5c0975eb, + 0xe612a3cb9ecba951, 0xc766e62cfcadaf96, 0xee64435a9752fe72, 0xa192d576b245165a, + 0x0a8787bf8ecb74b2, 0x81b3e73d20b49b6f, 0x7fa8220ba3b2ecea, 0x245731c13ca42499, + 0xb78dbfaf3a8d83bd, 0xea1ad565322a1a0b, 0x60e61c23a3795013, 0x6606d7e446282b93, + 0x6ca4ecb15c5f91e1, 0x9f626da15c9625f3, 0xe51b38608ef25f57, 0x958a324ceb064572, + } + + key: [16]byte + for i in 0..<16 { + key[i] = byte(i) + } + + for i in 0.. 0 { + os.exit(1) + } } @(test) diff --git a/tools/odinfmt/main.odin b/tools/odinfmt/main.odin index bc1b521ca..cebb20888 100644 --- a/tools/odinfmt/main.odin +++ b/tools/odinfmt/main.odin @@ -114,7 +114,6 @@ main :: proc() { filepath.walk(path, walk_files); for file in files { - fmt.println(file); backup_path := strings.concatenate({file, "_bk"}); defer delete(backup_path); diff --git a/vendor/ENet/enet.odin b/vendor/ENet/enet.odin index 8d068fbfc..37e65b497 100644 --- a/vendor/ENet/enet.odin +++ b/vendor/ENet/enet.odin @@ -1,7 +1,7 @@ package ENet -when ODIN_OS == "windows" { - when ODIN_ARCH == "amd64" { +when ODIN_OS == .Windows { + when ODIN_ARCH == .amd64 { foreign import ENet { "lib/enet64.lib", "system:Ws2_32.lib", diff --git a/vendor/ENet/unix.odin b/vendor/ENet/unix.odin index ea6b84199..05ce41e05 100644 --- a/vendor/ENet/unix.odin +++ b/vendor/ENet/unix.odin @@ -1,4 +1,4 @@ -//+build linux, darwin, freebsd +//+build linux, darwin, freebsd, openbsd package ENet // When we implement the appropriate bindings for Unix, the section separated @@ -14,7 +14,7 @@ import "core:c" @(private="file") FD_ZERO :: #force_inline proc(s: ^fd_set) { for i := size_of(fd_set) / size_of(c.long); i != 0; i -= 1 { - s.fds_bits[i] = 0; + s.fds_bits[i] = 0 } } @@ -56,4 +56,4 @@ SOCKETSET_REMOVE :: #force_inline proc(sockset: ^SocketSet, socket: Socket) { SOCKSET_CHECK :: #force_inline proc(sockset: ^SocketSet, socket: Socket) -> bool { return FD_ISSET(i32(socket), cast(^fd_set)sockset) -} \ No newline at end of file +} diff --git a/vendor/OpenEXRCore/LICENSE.md b/vendor/OpenEXRCore/LICENSE.md new file mode 100644 index 000000000..32e8c226a --- /dev/null +++ b/vendor/OpenEXRCore/LICENSE.md @@ -0,0 +1,11 @@ +Copyright (c) Contributors to the OpenEXR Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/vendor/OpenEXRCore/OpenEXRCore-3_1.lib b/vendor/OpenEXRCore/OpenEXRCore-3_1.lib new file mode 100644 index 000000000..f70938101 Binary files /dev/null and b/vendor/OpenEXRCore/OpenEXRCore-3_1.lib differ diff --git a/vendor/OpenEXRCore/exr_attr.odin b/vendor/OpenEXRCore/exr_attr.odin new file mode 100644 index 000000000..eb07142ec --- /dev/null +++ b/vendor/OpenEXRCore/exr_attr.odin @@ -0,0 +1,397 @@ +package vendor_openexr + +import "core:c" + +// Enum declaring allowed values for \c u8 value stored in built-in compression type. +compression_t :: enum c.int { + NONE = 0, + RLE = 1, + ZIPS = 2, + ZIP = 3, + PIZ = 4, + PXR24 = 5, + B44 = 6, + B44A = 7, + DWAA = 8, + DWAB = 9, +} + +// Enum declaring allowed values for \c u8 value stored in built-in env map type. +envmap_t :: enum c.int { + LATLONG = 0, + CUBE = 1, +} + +// Enum declaring allowed values for \c u8 value stored in \c lineOrder type. +lineorder_t :: enum c.int { + INCREASING_Y = 0, + DECREASING_Y = 1, + RANDOM_Y = 2, +} + +// Enum declaring allowed values for part type. +storage_t :: enum c.int { + SCANLINE = 0, // Corresponds to type of \c scanlineimage. + TILED, // Corresponds to type of \c tiledimage. + DEEP_SCANLINE, // Corresponds to type of \c deepscanline. + DEEP_TILED, // Corresponds to type of \c deeptile. +} + +// @brief Enum representing what type of tile information is contained. +tile_level_mode_t :: enum c.int { + ONE_LEVEL = 0, // Single level of image data. + MIPMAP_LEVELS = 1, // Mipmapped image data. + RIPMAP_LEVELS = 2, // Ripmapped image data. +} + +/** @brief Enum representing how to scale positions between levels. */ +tile_round_mode_t :: enum c.int { + DOWN = 0, + UP = 1, +} + +/** @brief Enum capturing the underlying data type on a channel. */ +pixel_type_t :: enum c.int { + UINT = 0, + HALF = 1, + FLOAT = 2, +} + +/* /////////////////////////////////////// */ +/* First set of structs are data where we can read directly with no allocation needed... */ + +/** @brief Struct to hold color chromaticities to interpret the tristimulus color values in the image data. */ +attr_chromaticities_t :: struct #packed { + red_x: f32, + red_y: f32, + green_x: f32, + green_y: f32, + blue_x: f32, + blue_y: f32, + white_x: f32, + white_y: f32, +} + +/** @brief Struct to hold keycode information. */ +attr_keycode_t :: struct #packed { + film_mfc_code: i32, + film_type: i32, + prefix: i32, + count: i32, + perf_offset: i32, + perfs_per_frame: i32, + perfs_per_count: i32, +} + +/** @brief struct to hold a 32-bit floating-point 3x3 matrix. */ +attr_m33f_t :: struct #packed { + m: [9]f32, +} + +/** @brief struct to hold a 64-bit floating-point 3x3 matrix. */ +attr_m33d_t :: struct #packed { + m: [9]f64, +} + +/** @brief Struct to hold a 32-bit floating-point 4x4 matrix. */ +attr_m44f_t :: struct #packed { + m: [16]f32, +} + +/** @brief Struct to hold a 64-bit floating-point 4x4 matrix. */ +attr_m44d_t :: struct #packed { + m: [16]f64, +} + +/** @brief Struct to hold an integer ratio value. */ +attr_rational_t :: struct #packed { + num: i32, + denom: u32, +} + +/** @brief Struct to hold timecode information. */ +attr_timecode_t :: struct #packed { + time_and_flags: u32, + user_data: u32, +} + +/** @brief Struct to hold a 2-element integer vector. */ +attr_v2i_t :: distinct [2]i32 + +/** @brief Struct to hold a 2-element 32-bit float vector. */ +attr_v2f_t :: distinct [2]f32 + +/** @brief Struct to hold a 2-element 64-bit float vector. */ +attr_v2d_t :: distinct [2]f64 + +/** @brief Struct to hold a 3-element integer vector. */ +attr_v3i_t :: distinct [3]i32 + +/** @brief Struct to hold a 3-element 32-bit float vector. */ +attr_v3f_t :: distinct [3]f32 + +/** @brief Struct to hold a 3-element 64-bit float vector. */ +attr_v3d_t :: distinct [3]f64 + +/** @brief Struct to hold an integer box/region definition. */ +attr_box2i_t :: struct #packed { + min: attr_v2i_t, + max: attr_v2i_t, +} + +/** @brief Struct to hold a floating-point box/region definition. */ +attr_box2f_t:: struct #packed { + min: attr_v2f_t, + max: attr_v2f_t, +} + +/** @brief Struct holding base tiledesc attribute type defined in spec + * + * NB: This is in a tightly packed area so it can be read directly, be + * careful it doesn't become padded to the next \c uint32_t boundary. + */ +attr_tiledesc_t :: struct #packed { + x_size: u32, + y_size: u32, + level_and_round: u8, +} + +/** @brief Macro to access type of tiling from packed structure. */ +GET_TILE_LEVEL_MODE :: #force_inline proc "c" (tiledesc: attr_tiledesc_t) -> tile_level_mode_t { + return tile_level_mode_t(tiledesc.level_and_round & 0xf) +} +/** @brief Macro to access the rounding mode of tiling from packed structure. */ +GET_TILE_ROUND_MODE :: #force_inline proc "c" (tiledesc: attr_tiledesc_t) -> tile_round_mode_t { + return tile_round_mode_t((tiledesc.level_and_round >> 4) & 0xf) +} +/** @brief Macro to pack the tiling type and rounding mode into packed structure. */ +PACK_TILE_LEVEL_ROUND :: #force_inline proc "c" (lvl: tile_level_mode_t, mode: tile_round_mode_t) -> u8 { + return ((u8(mode) & 0xf) << 4) | (u8(lvl) & 0xf) +} + + +/* /////////////////////////////////////// */ +/* Now structs that involve heap allocation to store data. */ + +/** Storage for a string. */ +attr_string_t :: struct { + length: i32, + /** If this is non-zero, the string owns the data, if 0, is a const ref to a static string. */ + alloc_size: i32, + + str: cstring, +} + +/** Storage for a string vector. */ +attr_string_vector_t :: struct { + n_strings: i32, + /** If this is non-zero, the string vector owns the data, if 0, is a const ref. */ + alloc_size: i32, + + strings: [^]attr_string_t, +} + +/** Float vector storage struct. */ +attr_float_vector_t :: struct { + length: i32, + /** If this is non-zero, the float vector owns the data, if 0, is a const ref. */ + alloc_size: i32, + + arr: [^]f32, +} + +/** Hint for lossy compression methods about how to treat values + * (logarithmic or linear), meaning a human sees values like R, G, B, + * luminance difference between 0.1 and 0.2 as about the same as 1.0 + * to 2.0 (logarithmic), where chroma coordinates are closer to linear + * (0.1 and 0.2 is about the same difference as 1.0 and 1.1). + */ +perceptual_treatment_t :: enum c.int { + LOGARITHMIC = 0, + LINEAR = 1, +} + +/** Individual channel information. */ +attr_chlist_entry_t :: struct { + name: attr_string_t, + /** Data representation for these pixels: uint, half, float. */ + pixel_type: pixel_type_t, + /** Possible values are 0 and 1 per docs perceptual_treatment_t. */ + p_linear: u8, + reserved: [3]u8, + x_sampling: i32, + y_sampling: i32, +} + +/** List of channel information (sorted alphabetically). */ +attr_chlist_t :: struct { + num_channels: c.int, + num_alloced: c.int, + + entries: [^]attr_chlist_entry_t, +} + +/** @brief Struct to define attributes of an embedded preview image. */ +attr_preview_t :: struct { + width: u32, + height: u32, + /** If this is non-zero, the preview owns the data, if 0, is a const ref. */ + alloc_size: c.size_t, + + rgba: [^]u8, +} + +/** Custom storage structure for opaque data. + * + * Handlers for opaque types can be registered, then when a + * non-builtin type is encountered with a registered handler, the + * function pointers to unpack/pack it will be set up. + * + * @sa register_attr_type_handler + */ +attr_opaquedata_t :: struct { + size: i32, + unpacked_size: i32, + /** If this is non-zero, the struct owns the data, if 0, is a const ref. */ + packed_alloc_size: i32, + pad: [4]u8, + + packed_data: rawptr, + + /** When an application wants to have custom data, they can store + * an unpacked form here which will be requested to be destroyed + * upon destruction of the attribute. + */ + unpacked_data: rawptr, + + /** An application can register an attribute handler which then + * fills in these function pointers. This allows a user to delay + * the expansion of the custom type until access is desired, and + * similarly, to delay the packing of the data until write time. + */ + unpack_func_ptr: proc "c" ( + ctxt: context_t, + data: rawptr, + attrsize: i32, + outsize: ^i32, + outbuffer: ^rawptr) -> result_t, + pack_func_ptr: proc "c" ( + ctxt: context_t, + data: rawptr, + datasize: i32, + outsize: ^i32, + outbuffer: rawptr) -> result_t, + destroy_unpacked_func_ptr: proc "c" ( + ctxt: context_t, data: rawptr, attrsize: i32), +} + +/* /////////////////////////////////////// */ + +/** @brief Built-in/native attribute type enum. + * + * This will enable us to do a tagged type struct to generically store + * attributes. + */ +attribute_type_t :: enum c.int { + UNKNOWN = 0, // Type indicating an error or uninitialized attribute. + BOX2I, // Integer region definition. @see attr_box2i_t. + BOX2F, // Float region definition. @see attr_box2f_t. + CHLIST, // Definition of channels in file @see chlist_entry. + CHROMATICITIES, // Values to specify color space of colors in file @see attr_chromaticities_t. + COMPRESSION, // ``u8`` declaring compression present. + DOUBLE, // Double precision floating point number. + ENVMAP, // ``u8`` declaring environment map type. + FLOAT, // Normal (4 byte) precision floating point number. + FLOAT_VECTOR, // List of normal (4 byte) precision floating point numbers. + INT, // 32-bit signed integer value. + KEYCODE, // Struct recording keycode @see attr_keycode_t. + LINEORDER, // ``u8`` declaring scanline ordering. + M33F, // 9 32-bit floats representing a 3x3 matrix. + M33D, // 9 64-bit floats representing a 3x3 matrix. + M44F, // 16 32-bit floats representing a 4x4 matrix. + M44D, // 16 64-bit floats representing a 4x4 matrix. + PREVIEW, // 2 ``unsigned ints`` followed by 4 x w x h ``u8`` image. + RATIONAL, // \c int followed by ``unsigned int`` + STRING, // ``int`` (length) followed by char string data. + STRING_VECTOR, // 0 or more text strings (int + string). number is based on attribute size. + TILEDESC, // 2 ``unsigned ints`` ``xSize``, ``ySize`` followed by mode. + TIMECODE, // 2 ``unsigned ints`` time and flags, user data. + V2I, // Pair of 32-bit integers. + V2F, // Pair of 32-bit floats. + V2D, // Pair of 64-bit floats. + V3I, // Set of 3 32-bit integers. + V3F, // Set of 3 32-bit floats. + V3D, // Set of 3 64-bit floats. + OPAQUE, // User/unknown provided type. +} + +/** @brief Storage, name and type information for an attribute. + * + * Attributes (metadata) for the file cause a surprising amount of + * overhead. It is not uncommon for a production-grade EXR to have + * many attributes. As such, the attribute struct is designed in a + * slightly more complicated manner. It is optimized to have the + * storage for that attribute: the struct itself, the name, the type, + * and the data all allocated as one block. Further, the type and + * standard names may use a static string to avoid allocating space + * for those as necessary with the pointers pointing to static strings + * (not to be freed). Finally, small values are optimized for. + */ +attribute_t :: struct { + /** Name of the attribute. */ + name: cstring, + /** String type name of the attribute. */ + type_name: cstring, + /** Length of name string (short flag is 31 max, long allows 255). */ + name_length: u8, + /** Length of type string (short flag is 31 max, long allows 255). */ + type_name_length: u8, + + pad: [2]u8, + + /** Enum of the attribute type. */ + type: attribute_type_t, + + /** Union of pointers of different types that can be used to type + * pun to an appropriate type for builtins. Do note that while + * this looks like a big thing, it is only the size of a single + * pointer. These are all pointers into some other data block + * storing the value you want, with the exception of the pod types + * which are just put in place (i.e. small value optimization). + * + * The attribute type \c type should directly correlate to one + * of these entries. + */ + using _: struct #raw_union { + // NB: not pointers for POD types + uc: u8, + d: f64, + f: f32, + i: i32, + + box2i: ^attr_box2i_t, + box2f: ^attr_box2f_t, + chlist: ^attr_chlist_t, + chromaticities: ^attr_chromaticities_t, + keycode: ^attr_keycode_t, + floatvector: ^attr_float_vector_t, + m33f: ^attr_m33f_t, + m33d: ^attr_m33d_t, + m44f: ^attr_m44f_t, + m44d: ^attr_m44d_t, + preview: ^attr_preview_t, + rational: ^attr_rational_t, + string: ^attr_string_t, + stringvector: ^attr_string_vector_t, + tiledesc: ^attr_tiledesc_t, + timecode: ^attr_timecode_t, + v2i: ^attr_v2i_t, + v2f: ^attr_v2f_t, + v2d: ^attr_v2d_t, + v3i: ^attr_v3i_t, + v3f: ^attr_v3f_t, + v3d: ^attr_v3d_t, + opaque: ^attr_opaquedata_t, + rawptr: ^u8, + }, +} \ No newline at end of file diff --git a/vendor/OpenEXRCore/exr_base.odin b/vendor/OpenEXRCore/exr_base.odin new file mode 100644 index 000000000..3c71f6285 --- /dev/null +++ b/vendor/OpenEXRCore/exr_base.odin @@ -0,0 +1,174 @@ +package vendor_openexr + +when ODIN_OS == .Windows { + foreign import lib "OpenEXRCore-3_1.lib" +} else { + foreign import lib "system:OpenEXRCore-3_1" +} + +import "core:c" + +/** @brief Function pointer used to hold a malloc-like routine. + * + * Providing these to a context will override what memory is used to + * allocate the context itself, as well as any allocations which + * happen during processing of a file or stream. This can be used by + * systems which provide rich malloc tracking routines to override the + * internal allocations performed by the library. + * + * This function is expected to allocate and return a new memory + * handle, or `NULL` if allocation failed (which the library will then + * handle and return an out-of-memory error). + * + * If one is provided, both should be provided. + * @sa exr_memory_free_func_t + */ +memory_allocation_func_t :: proc "c" (bytes: c.size_t) -> rawptr + +/** @brief Function pointer used to hold a free-like routine. + * + * Providing these to a context will override what memory is used to + * allocate the context itself, as well as any allocations which + * happen during processing of a file or stream. This can be used by + * systems which provide rich malloc tracking routines to override the + * internal allocations performed by the library. + * + * This function is expected to return memory to the system, ala free + * from the C library. + * + * If providing one, probably need to provide both routines. + * @sa exr_memory_allocation_func_t + */ +memory_free_func_t :: proc "c" (ptr: rawptr) + +@(link_prefix="exr_", default_calling_convention="c") +foreign lib { + /** @brief Retrieve the current library version. The @p extra string is for + * custom installs, and is a static string, do not free the returned + * pointer. + */ + get_library_version :: proc(maj, min, patch: ^c.int, extra: ^cstring) --- + + /** @brief Limit the size of image allowed to be parsed or created by + * the library. + * + * This is used as a safety check against corrupt files, but can also + * serve to avoid potential issues on machines which have very + * constrained RAM. + * + * These values are among the only globals in the core layer of + * OpenEXR. The intended use is for applications to define a global + * default, which will be combined with the values provided to the + * individual context creation routine. The values are used to check + * against parsed header values. This adds some level of safety from + * memory overruns where a corrupt file given to the system may cause + * a large allocation to happen, enabling buffer overruns or other + * potential security issue. + * + * These global values are combined with the values in + * \ref exr_context_initializer_t using the following rules: + * + * 1. negative values are ignored. + * + * 2. if either value has a positive (non-zero) value, and the other + * has 0, the positive value is preferred. + * + * 3. If both are positive (non-zero), the minimum value is used. + * + * 4. If both values are 0, this disables the constrained size checks. + * + * This function does not fail. + */ + set_default_maximum_image_size :: proc(w, h: c.int) --- + + /** @brief Retrieve the global default maximum image size. + * + * This function does not fail. + */ + get_default_maximum_image_size :: proc(w, h: ^c.int) --- + + /** @brief Limit the size of an image tile allowed to be parsed or + * created by the library. + * + * Similar to image size, this places constraints on the maximum tile + * size as a safety check against bad file data + * + * This is used as a safety check against corrupt files, but can also + * serve to avoid potential issues on machines which have very + * constrained RAM + * + * These values are among the only globals in the core layer of + * OpenEXR. The intended use is for applications to define a global + * default, which will be combined with the values provided to the + * individual context creation routine. The values are used to check + * against parsed header values. This adds some level of safety from + * memory overruns where a corrupt file given to the system may cause + * a large allocation to happen, enabling buffer overruns or other + * potential security issue. + * + * These global values are combined with the values in + * \ref exr_context_initializer_t using the following rules: + * + * 1. negative values are ignored. + * + * 2. if either value has a positive (non-zero) value, and the other + * has 0, the positive value is preferred. + * + * 3. If both are positive (non-zero), the minimum value is used. + * + * 4. If both values are 0, this disables the constrained size checks. + * + * This function does not fail. + */ + set_default_maximum_tile_size :: proc(w, h: c.int) --- + + /** @brief Retrieve the global maximum tile size. + * + * This function does not fail. + */ + get_default_maximum_tile_size :: proc(w, h: ^c.int) --- + + /** @} */ + + /** + * @defgroup CompressionDefaults Provides default compression settings + * @{ + */ + + /** @brief Assigns a default zip compression level. + * + * This value may be controlled separately on each part, but this + * global control determines the initial value. + */ + set_default_zip_compression_level :: proc(l: c.int) --- + + /** @brief Retrieve the global default zip compression value + */ + get_default_zip_compression_level :: proc(l: ^c.int) --- + + /** @brief Assigns a default DWA compression quality level. + * + * This value may be controlled separately on each part, but this + * global control determines the initial value. + */ + set_default_dwa_compression_quality :: proc(q: f32) --- + + /** @brief Retrieve the global default dwa compression quality + */ + get_default_dwa_compression_quality :: proc(q: ^f32) --- + + /** @brief Allow the user to override default allocator used internal + * allocations necessary for files, attributes, and other temporary + * memory. + * + * These routines may be overridden when creating a specific context, + * however this provides global defaults such that the default can be + * applied. + * + * If either pointer is 0, the appropriate malloc/free routine will be + * substituted. + * + * This function does not fail. + */ + set_default_memory_routines :: proc(alloc_func: memory_allocation_func_t, free_func: memory_free_func_t) --- +} \ No newline at end of file diff --git a/vendor/OpenEXRCore/exr_chunkio.odin b/vendor/OpenEXRCore/exr_chunkio.odin new file mode 100644 index 000000000..e5fae15f5 --- /dev/null +++ b/vendor/OpenEXRCore/exr_chunkio.odin @@ -0,0 +1,147 @@ +package vendor_openexr + +when ODIN_OS == .Windows { + foreign import lib "OpenEXRCore-3_1.lib" +} else { + foreign import lib "system:OpenEXRCore-3_1" +} + +import "core:c" + +/** + * Struct describing raw data information about a chunk. + * + * A chunk is the generic term for a pixel data block in an EXR file, + * as described in the OpenEXR File Layout documentation. This is + * common between all different forms of data that can be stored. + */ +chunk_info_t :: struct { + idx: i32, + + /** For tiles, this is the tilex; for scans it is the x. */ + start_x: i32, + /** For tiles, this is the tiley; for scans it is the scanline y. */ + start_y: i32, + height: i32, /**< For this chunk. */ + width: i32, /**< For this chunk. */ + + level_x: u8, /**< For tiled files. */ + level_y: u8, /**< For tiled files. */ + + type: u8, + compression: u8, + + data_offset: u64, + packed_size: u64, + unpacked_size: u64, + + sample_count_data_offset: u64, + sample_count_table_size: u64, +} + +@(link_prefix="exr_", default_calling_convention="c") +foreign lib { + read_scanline_chunk_info :: proc(ctxt: const_context_t, part_index: c.int, y: c.int, cinfo: ^chunk_info_t) -> result_t --- + + read_tile_chunk_info :: proc( + ctxt: const_context_t, + part_index: c.int, + tilex: c.int, + tiley: c.int, + levelx: c.int, + levely: c.int, + cinfo: ^chunk_info_t) -> result_t --- + + /** Read the packed data block for a chunk. + * + * This assumes that the buffer pointed to by @p packed_data is + * large enough to hold the chunk block info packed_size bytes. + */ + read_chunk :: proc( + ctxt: const_context_t, + part_index: c.int, + cinfo: ^chunk_info_t, + packed_data: rawptr) -> result_t --- + + /** + * Read chunk for deep data. + * + * This allows one to read the packed data, the sample count data, or both. + * \c exr_read_chunk also works to read deep data packed data, + * but this is a routine to get the sample count table and the packed + * data in one go, or if you want to pre-read the sample count data, + * you can get just that buffer. + */ + read_deep_chunk :: proc( + ctxt: const_context_t, + part_index: c.int, + cinfo: ^chunk_info_t, + packed_data: rawptr, + sample_data: rawptr) -> result_t --- + + /**************************************/ + + /** Initialize a \c chunk_info_t structure when encoding scanline + * data (similar to read but does not do anything with a chunk + * table). + */ + write_scanline_chunk_info :: proc(ctxt: context_t, part_index: c.int, y: c.int, cinfo: ^chunk_info_t) -> result_t --- + + /** Initialize a \c chunk_info_t structure when encoding tiled data + * (similar to read but does not do anything with a chunk table). + */ + write_tile_chunk_info :: proc( + ctxt: context_t, + part_index: c.int, + tilex: c.int, + tiley: c.int, + levelx: c.int, + levely: c.int, + cinfo: ^chunk_info_t) -> result_t --- + + /** + * @p y must the appropriate starting y for the specified chunk. + */ + write_scanline_chunk :: proc( + ctxt: context_t, + part_index: int, + y: int, + packed_data: rawptr, + packed_size: u64) -> result_t --- + + /** + * @p y must the appropriate starting y for the specified chunk. + */ + write_deep_scanline_chunk :: proc( + ctxt: context_t, + part_index: c.int, + y: c.int, + packed_data: rawptr, + packed_size: u64, + unpacked_size: u64, + sample_data: rawptr, + sample_data_size: u64) -> result_t --- + + write_tile_chunk :: proc( + ctxt: context_t, + part_index: c.int, + tilex: c.int, + tiley: c.int, + levelx: c.int, + levely: c.int, + packed_data: rawptr, + packed_size: u64) -> result_t --- + + write_deep_tile_chunk :: proc( + ctxt: context_t, + part_index: c.int, + tilex: c.int, + tiley: c.int, + levelx: c.int, + levely: c.int, + packed_data: rawptr, + packed_size: u64, + unpacked_size: u64, + sample_data: rawptr, + sample_data_size: u64) -> result_t --- +} \ No newline at end of file diff --git a/vendor/OpenEXRCore/exr_coding.odin b/vendor/OpenEXRCore/exr_coding.odin new file mode 100644 index 000000000..337475edf --- /dev/null +++ b/vendor/OpenEXRCore/exr_coding.odin @@ -0,0 +1,119 @@ +package vendor_openexr + +import "core:c" +/** + * Enum for use in a custom allocator in the encode/decode pipelines + * (that is, so the implementor knows whether to allocate on which + * device based on the buffer disposition). + */ +transcoding_pipeline_buffer_id_t :: enum c.int { + PACKED, + UNPACKED, + COMPRESSED, + SCRATCH1, + SCRATCH2, + PACKED_SAMPLES, + SAMPLES, +} + +/** @brief Struct for negotiating buffers when decoding/encoding + * chunks of data. + * + * This is generic and meant to negotiate exr data bi-directionally, + * in that the same structure is used for both decoding and encoding + * chunks for read and write, respectively. + * + * The first half of the structure will be filled by the library, and + * the caller is expected to fill the second half appropriately. + */ +coding_channel_info_t :: struct { + /************************************************** + * Elements below are populated by the library when + * decoding is initialized/updated and must be left + * untouched when using the default decoder routines. + **************************************************/ + + /** Channel name. + * + * This is provided as a convenient reference. Do not free, this + * refers to the internal data structure in the context. + */ + channel_name: cstring, + + /** Number of lines for this channel in this chunk. + * + * May be 0 or less than overall image height based on sampling + * (i.e. when in 4:2:0 type sampling) + */ + height: i32, + + /** Width in pixel count. + * + * May be 0 or less than overall image width based on sampling + * (i.e. 4:2:2 will have some channels have fewer values). + */ + width: i32, + + /** Horizontal subsampling information. */ + x_samples: i32, + /** Vertical subsampling information. */ + y_samples: i32, + + /** Linear flag from channel definition (used by b44). */ + p_linear: u8, + + /** How many bytes per pixel this channel consumes (2 for float16, + * 4 for float32/uint32). + */ + bytes_per_element: i8, + + /** Small form of exr_pixel_type_t enum (EXR_PIXEL_UINT/HALF/FLOAT). */ + data_type: u16, + + /************************************************** + * Elements below must be edited by the caller + * to control encoding/decoding. + **************************************************/ + + /** How many bytes per pixel the input is or output should be + * (2 for float16, 4 for float32/uint32). Defaults to same + * size as input. + */ + user_bytes_per_element: i16, + + /** Small form of exr_pixel_type_t enum + * (EXR_PIXEL_UINT/HALF/FLOAT). Defaults to same type as input. + */ + user_data_type: u16, + + /** Increment to get to next pixel. + * + * This is in bytes. Must be specified when the decode pointer is + * specified (and always for encode). + * + * This is useful for implementing transcoding generically of + * planar or interleaved data. For planar data, where the layout + * is RRRRRGGGGGBBBBB, you can pass in 1 * bytes per component. + */ + + user_pixel_stride: i32, + + /** When \c lines > 1 for a chunk, this is the increment used to get + * from beginning of line to beginning of next line. + * + * This is in bytes. Must be specified when the decode pointer is + * specified (and always for encode). + */ + user_line_stride: i32, + + /** This data member has different requirements reading vs + * writing. When reading, if this is left as `NULL`, the channel + * will be skipped during read and not filled in. During a write + * operation, this pointer is considered const and not + * modified. To make this more clear, a union is used here. + */ + using _: struct #raw_union { + decode_to_ptr: ^u8, + encode_from_ptr: ^u8, + }, +} diff --git a/vendor/OpenEXRCore/exr_context.odin b/vendor/OpenEXRCore/exr_context.odin new file mode 100644 index 000000000..4b70950b3 --- /dev/null +++ b/vendor/OpenEXRCore/exr_context.odin @@ -0,0 +1,489 @@ +package vendor_openexr + +when ODIN_OS == .Windows { + foreign import lib "OpenEXRCore-3_1.lib" +} else { + foreign import lib "system:OpenEXRCore-3_1" +} + +import "core:c" + +#assert(size_of(c.int) == size_of(b32)) + +context_t :: distinct rawptr +const_context_t :: context_t + +/** + * @defgroup ContextFunctions OpenEXR Context Stream/File Functions + * + * @brief These are a group of function interfaces used to customize + * the error handling, memory allocations, or I/O behavior of an + * OpenEXR context. + * + * @{ + */ + +/** @brief Stream error notifier + * + * This function pointer is provided to the stream functions by the + * library such that they can provide a nice error message to the + * user during stream operations. + */ +stream_error_func_ptr_t :: proc "c" (ctxt: const_context_t, code: result_t, fmt: cstring, #c_vararg args: ..any) -> result_t + +/** @brief Error callback function + * + * Because a file can be read from using many threads at once, it is + * difficult to store an error message for later retrieval. As such, + * when a file is constructed, a callback function can be provided + * which delivers an error message for the calling application to + * handle. This will then be delivered on the same thread causing the + * error. + */ +error_handler_cb_t :: proc "c" (ctxt: const_context_t, code: result_t, msg: cstring) + +/** Destroy custom stream function pointer + * + * Generic callback to clean up user data for custom streams. + * This is called when the file is closed and expected not to + * error. + * + * @param failed Indicates the write operation failed, the + * implementor may wish to cleanup temporary files + */ +destroy_stream_func_ptr_t :: proc "c" (ctxt: const_context_t, userdata: rawptr, failed: c.int) + +/** Query stream size function pointer + * + * Used to query the size of the file, or amount of data representing + * the openexr file in the data stream. + * + * This is used to validate requests against the file. If the size is + * unavailable, return -1, which will disable these validation steps + * for this file, although appropriate memory safeguards must be in + * place in the calling application. + */ +query_size_func_ptr_t :: proc "c" (ctxt: const_context_t, userdata: rawptr) -> i64 + +/** @brief Read custom function pointer + * + * Used to read data from a custom output. Expects similar semantics to + * pread or ReadFile with overlapped data under win32. + * + * It is required that this provides thread-safe concurrent access to + * the same file. If the stream/input layer you are providing does + * not have this guarantee, your are responsible for providing + * appropriate serialization of requests. + * + * A file should be expected to be accessed in the following pattern: + * - upon open, the header and part information attributes will be read + * - upon the first image read request, the offset tables will be read + * multiple threads accessing this concurrently may actually read + * these values at the same time + * - chunks can then be read in any order as preferred by the + * application + * + * While this should mean that the header will be read in 'stream' + * order (no seeks required), no guarantee is made beyond that to + * retrieve image/deep data in order. So if the backing file is + * truly a stream, it is up to the provider to implement appropriate + * caching of data to give the appearance of being able to seek/read + * atomically. + */ +read_func_ptr_t :: proc "c" ( + ctxt: const_context_t, + userdata: rawptr, + buffer: rawptr, + sz: u64, + offset: u64, + error_cb: stream_error_func_ptr_t) -> i64 + +/** Write custom function pointer + * + * Used to write data to a custom output. Expects similar semantics to + * pwrite or WriteFile with overlapped data under win32. + * + * It is required that this provides thread-safe concurrent access to + * the same file. While it is unlikely that multiple threads will + * be used to write data for compressed forms, it is possible. + * + * A file should be expected to be accessed in the following pattern: + * - upon open, the header and part information attributes is constructed. + * + * - when the write_header routine is called, the header becomes immutable + * and is written to the file. This computes the space to store the chunk + * offsets, but does not yet write the values. + * + * - Image chunks are written to the file, and appear in the order + * they are written, not in the ordering that is required by the + * chunk offset table (unless written in that order). This may vary + * slightly if the size of the chunks is not directly known and + * tight packing of data is necessary. + * + * - at file close, the chunk offset tables are written to the file. + */ +write_func_ptr_t :: proc "c" ( + ctxt: const_context_t, + userdata: rawptr, + buffer: rawptr, + sz: u64, + offset: u64, + error_cb: stream_error_func_ptr_t) -> i64 + +/** @brief Struct used to pass function pointers into the context + * initialization routines. + * + * This partly exists to avoid the chicken and egg issue around + * creating the storage needed for the context on systems which want + * to override the malloc/free routines. + * + * However, it also serves to make a tidier/simpler set of functions + * to create and start processing exr files. + * + * The size member is required for version portability. + * + * It can be initialized using \c EXR_DEFAULT_CONTEXT_INITIALIZER. + * + * \code{.c} + * exr_context_initializer_t myctxtinit = DEFAULT_CONTEXT_INITIALIZER; + * myctxtinit.error_cb = &my_super_cool_error_callback_function; + * ... + * \endcode + * + */ +context_initializer_t :: struct { + /** @brief Size member to tag initializer for version stability. + * + * This should be initialized to the size of the current + * structure. This allows EXR to add functions or other + * initializers in the future, and retain version compatibility + */ + size: c.size_t, + + /** @brief Error callback function pointer + * + * The error callback is allowed to be `NULL`, and will use a + * default print which outputs to \c stderr. + * + * @sa exr_error_handler_cb_t + */ + error_handler_fn: error_handler_cb_t, + + /** Custom allocator, if `NULL`, will use malloc. @sa memory_allocation_func_t */ + alloc_fn: memory_allocation_func_t, + + /** Custom deallocator, if `NULL`, will use free. @sa memory_free_func_t */ + free_fn: memory_free_func_t, + + /** Blind data passed to custom read, size, write, destroy + * functions below. Up to user to manage this pointer. + */ + user_data: rawptr, + + /** @brief Custom read routine. + * + * This is only used during read or update contexts. If this is + * provided, it is expected that the caller has previously made + * the stream available, and placed whatever stream/file data + * into \c user_data above. + * + * If this is `NULL`, and the context requested is for reading an + * exr file, an internal implementation is provided for reading + * from normal filesystem files, and the filename provided is + * attempted to be opened as such. + * + * Expected to be `NULL` for a write-only operation, but is ignored + * if it is provided. + * + * For update contexts, both read and write functions must be + * provided if either is. + * + * @sa exr_read_func_ptr_t + */ + read_fn: read_func_ptr_t, + + /** @brief Custom size query routine. + * + * Used to provide validation when reading header values. If this + * is not provided, but a custom read routine is provided, this + * will disable some of the validation checks when parsing the + * image header. + * + * Expected to be `NULL` for a write-only operation, but is ignored + * if it is provided. + * + * @sa exr_query_size_func_ptr_t + */ + size_fn: query_size_func_ptr_t, + + /** @brief Custom write routine. + * + * This is only used during write or update contexts. If this is + * provided, it is expected that the caller has previously made + * the stream available, and placed whatever stream/file data + * into \c user_data above. + * + * If this is `NULL`, and the context requested is for writing an + * exr file, an internal implementation is provided for reading + * from normal filesystem files, and the filename provided is + * attempted to be opened as such. + * + * For update contexts, both read and write functions must be + * provided if either is. + * + * @sa exr_write_func_ptr_t + */ + write_fn: write_func_ptr_t, + + /** @brief Optional function to destroy the user data block of a custom stream. + * + * Allows one to free any user allocated data, and close any handles. + * + * @sa exr_destroy_stream_func_ptr_t + * */ + destroy_fn: destroy_stream_func_ptr_t, + + /** Initialize a field specifying what the maximum image width + * allowed by the context is. See exr_set_default_maximum_image_size() to + * understand how this interacts with global defaults. + */ + max_image_width: c.int, + + /** Initialize a field specifying what the maximum image height + * allowed by the context is. See exr_set_default_maximum_image_size() to + * understand how this interacts with global defaults. + */ + max_image_height: c.int, + + /** Initialize a field specifying what the maximum tile width + * allowed by the context is. See exr_set_default_maximum_tile_size() to + * understand how this interacts with global defaults. + */ + max_tile_width: c.int, + + /** Initialize a field specifying what the maximum tile height + * allowed by the context is. See exr_set_default_maximum_tile_size() to + * understand how this interacts with global defaults. + */ + max_tile_height: c.int, + + /** Initialize a field specifying what the default zip compression level should be + * for this context. See exr_set_default_zip_compresion_level() to + * set it for all contexts. + */ + zip_level: c.int, + + /** Initialize the default dwa compression quality. See + * exr_set_default_dwa_compression_quality() to set the default + * for all contexts. + */ + dwa_quality: f32, + + /** Initialize with a bitwise or of the various context flags + */ + flags: c.int, +} + +/** @brief context flag which will enforce strict header validation + * checks and may prevent reading of files which could otherwise be + * processed. + */ +CONTEXT_FLAG_STRICT_HEADER :: (1 << 0) + +/** @brief Disables error messages while parsing headers + * + * The return values will remain the same, but error reporting will be + * skipped. This is only valid for reading contexts + */ +CONTEXT_FLAG_SILENT_HEADER_PARSE :: (1 << 1) + +/** @brief Disables reconstruction logic upon corrupt / missing data chunks + * + * This will disable the reconstruction logic that searches through an + * incomplete file, and will instead just return errors at read + * time. This is only valid for reading contexts + */ +CONTEXT_FLAG_DISABLE_CHUNK_RECONSTRUCTION :: (1 << 2) + +/** @brief Simple macro to initialize the context initializer with default values. */ +DEFAULT_CONTEXT_INITIALIZER :: context_initializer_t{zip_level = -2, dwa_quality = -1} + +/** @} */ /* context function pointer declarations */ + + +/** @brief Enum describing how default files are handled during write. */ +default_write_mode_t :: enum c.int { + WRITE_FILE_DIRECTLY = 0, /**< Overwrite filename provided directly, deleted upon error. */ + INTERMEDIATE_TEMP_FILE = 1, /**< Create a temporary file, renaming it upon successful write, leaving original upon error */ +} + + +@(link_prefix="exr_", default_calling_convention="c") +foreign lib { + /** @brief Check the magic number of the file and report + * `EXR_ERR_SUCCESS` if the file appears to be a valid file (or at least + * has the correct magic number and can be read). + */ + test_file_header :: proc(filename: cstring, ctxtdata: ^context_initializer_t) -> result_t --- + + /** @brief Close and free any internally allocated memory, + * calling any provided destroy function for custom streams. + * + * If the file was opened for write, first save the chunk offsets + * or any other unwritten data. + */ + finish :: proc(ctxt: ^context_t) -> result_t --- + + /** @brief Create and initialize a read-only exr read context. + * + * If a custom read function is provided, the filename is for + * informational purposes only, the system assumes the user has + * previously opened a stream, file, or whatever and placed relevant + * data in userdata to access that. + * + * One notable attribute of the context is that once it has been + * created and returned a successful code, it has parsed all the + * header data. This is done as one step such that it is easier to + * provide a safe context for multiple threads to request data from + * the same context concurrently. + * + * Once finished reading data, use exr_finish() to clean up + * the context. + * + * If you have custom I/O requirements, see the initializer context + * documentation \ref exr_context_initializer_t. The @p ctxtdata parameter + * is optional, if `NULL`, default values will be used. + */ + start_read :: proc( + ctxt: ^context_t, + filename: cstring, + ctxtdata: ^context_initializer_t) -> result_t --- + + /** @brief Create and initialize a write-only context. + * + * If a custom write function is provided, the filename is for + * informational purposes only, and the @p default_mode parameter will be + * ignored. As such, the system assumes the user has previously opened + * a stream, file, or whatever and placed relevant data in userdata to + * access that. + * + * Multi-Threading: To avoid issues with creating multi-part EXR + * files, the library approaches writing as a multi-step process, so + * the same concurrent guarantees can not be made for writing a + * file. The steps are: + * + * 1. Context creation (this function) + * + * 2. Part definition (required attributes and additional metadata) + * + * 3. Transition to writing data (this "commits" the part definitions, + * any changes requested after will result in an error) + * + * 4. Write part data in sequential order of parts (part0 + * -> partN-1). + * + * 5. Within each part, multiple threads can be encoding and writing + * data concurrently. For some EXR part definitions, this may be able + * to write data concurrently when it can predict the chunk sizes, or + * data is allowed to be padded. For others, it may need to + * temporarily cache chunks until the data is received to flush in + * order. The concurrency around this is handled by the library + * + * 6. Once finished writing data, use exr_finish() to clean + * up the context, which will flush any unwritten data such as the + * final chunk offset tables, and handle the temporary file flags. + * + * If you have custom I/O requirements, see the initializer context + * documentation \ref exr_context_initializer_t. The @p ctxtdata + * parameter is optional, if `NULL`, default values will be used. + */ + start_write :: proc( + ctxt: ^context_t, + filename: cstring, + default_mode: default_write_mode_t, + ctxtdata: ^context_initializer_t) -> result_t --- + + /** @brief Create a new context for updating an exr file in place. + * + * This is a custom mode that allows one to modify the value of a + * metadata entry, although not to change the size of the header, or + * any of the image data. + * + * If you have custom I/O requirements, see the initializer context + * documentation \ref exr_context_initializer_t. The @p ctxtdata parameter + * is optional, if `NULL`, default values will be used. + */ + start_inplace_header_update :: proc( + ctxt: ^context_t, + filename: cstring, + ctxtdata: ^context_initializer_t) -> result_t --- + + /** @brief Retrieve the file name the context is for as provided + * during the start routine. + * + * Do not free the resulting string. + */ + + get_file_name :: proc(ctxt: const_context_t, name: ^cstring) -> result_t --- + + /** @brief Query the user data the context was constructed with. This + * is perhaps useful in the error handler callback to jump back into + * an object the user controls. + */ + + get_user_data :: proc(ctxt: const_context_t, userdata: ^rawptr) -> result_t --- + + /** Any opaque attribute data entry of the specified type is tagged + * with these functions enabling downstream users to unpack (or pack) + * the data. + * + * The library handles the memory packed data internally, but the + * handler is expected to allocate and manage memory for the + * *unpacked* buffer (the library will call the destroy function). + * + * NB: the pack function will be called twice (unless there is a + * memory failure), the first with a `NULL` buffer, requesting the + * maximum size (or exact size if known) for the packed buffer, then + * the second to fill the output packed buffer, at which point the + * size can be re-updated to have the final, precise size to put into + * the file. + */ + register_attr_type_handler :: proc( + ctxt: context_t, + type: cstring, + unpack_func_ptr: proc "c" ( + ctxt: context_t, + data: rawptr, + attrsize: i32, + outsize: ^i32, + outbuffer: ^rawptr) -> result_t, + pack_func_ptr: proc "c" ( + ctxt: context_t, + data: rawptr, + datasize: i32, + outsize: ^i32, + outbuffer: rawptr) -> result_t, + destroy_unpacked_func_ptr: proc "c" ( + ctxt: context_t, data: rawptr, datasize: i32), + ) -> result_t --- + + /** @brief Enable long name support in the output context */ + + set_longname_support :: proc(ctxt: context_t, onoff: b32) -> result_t --- + + /** @brief Write the header data. + * + * Opening a new output file has a small initialization state problem + * compared to opening for read/update: we need to enable the user + * to specify an arbitrary set of metadata across an arbitrary number + * of parts. To avoid having to create the list of parts and entire + * metadata up front, prior to calling the above exr_start_write(), + * allow the data to be set, then once this is called, it switches + * into a mode where the library assumes the data is now valid. + * + * It will recompute the number of chunks that will be written, and + * reset the chunk offsets. If you modify file attributes or part + * information after a call to this, it will error. + */ + write_header :: proc(ctxt: context_t) -> result_t --- +} \ No newline at end of file diff --git a/vendor/OpenEXRCore/exr_debug.odin b/vendor/OpenEXRCore/exr_debug.odin new file mode 100644 index 000000000..e376e9ddd --- /dev/null +++ b/vendor/OpenEXRCore/exr_debug.odin @@ -0,0 +1,12 @@ +package vendor_openexr + +when ODIN_OS == .Windows { + foreign import lib "OpenEXRCore-3_1.lib" +} else { + foreign import lib "system:OpenEXRCore-3_1" +} + +@(link_prefix="exr_", default_calling_convention="c") +foreign lib { + print_context_info :: proc(c: const_context_t, verbose: b32) -> result_t --- +} \ No newline at end of file diff --git a/vendor/OpenEXRCore/exr_decode.odin b/vendor/OpenEXRCore/exr_decode.odin new file mode 100644 index 000000000..2065ee44d --- /dev/null +++ b/vendor/OpenEXRCore/exr_decode.odin @@ -0,0 +1,292 @@ +package vendor_openexr + +when ODIN_OS == .Windows { + foreign import lib "OpenEXRCore-3_1.lib" +} else { + foreign import lib "system:OpenEXRCore-3_1" +} + +import "core:c" + +/** Can be bit-wise or'ed into the decode_flags in the decode pipeline. + * + * Indicates that the sample count table should be decoded to a an + * individual sample count list (n, m, o, ...), with an extra int at + * the end containing the total samples. + * + * Without this (i.e. a value of 0 in that bit), indicates the sample + * count table should be decoded to a cumulative list (n, n+m, n+m+o, + * ...), which is the on-disk representation. + */ +DECODE_SAMPLE_COUNTS_AS_INDIVIDUAL :: u16(1 << 0) + +/** Can be bit-wise or'ed into the decode_flags in the decode pipeline. + * + * Indicates that the data in the channel pointers to decode to is not + * a direct pointer, but instead is a pointer-to-pointers. In this + * mode, the user_pixel_stride and user_line_stride are used to + * advance the pointer offsets for each pixel in the output, but the + * user_bytes_per_element and user_data_type are used to put + * (successive) entries into each destination pointer (if not `NULL`). + * + * So each channel pointer must then point to an array of + * chunk.width * chunk.height pointers. + * + * With this, you can only extract desired pixels (although all the + * pixels must be initially decompressed) to handle such operations + * like proxying where you might want to read every other pixel. + * + * If this is NOT set (0), the default unpacking routine assumes the + * data will be planar and contiguous (each channel is a separate + * memory block), ignoring user_line_stride and user_pixel_stride. + */ +DECODE_NON_IMAGE_DATA_AS_POINTERS :: u16(1 << 1) + +/** + * When reading non-image data (i.e. deep), only read the sample table. + */ +DECODE_SAMPLE_DATA_ONLY :: u16(1 << 2) + +/** + * Struct meant to be used on a per-thread basis for reading exr data + * + * As should be obvious, this structure is NOT thread safe, but rather + * meant to be used by separate threads, which can all be accessing + * the same context concurrently. + */ +decode_pipeline_t :: struct { + /** The output channel information for this chunk. + * + * User is expected to fill the channel pointers for the desired + * output channels (any that are `NULL` will be skipped) if you are + * going to use exr_decoding_choose_default_routines(). If all that is + * desired is to read and decompress the data, this can be left + * uninitialized. + * + * Describes the channel information. This information is + * allocated dynamically during exr_decoding_initialize(). + */ + channels: [^]coding_channel_info_t, + channel_count: i16, + + /** Decode flags to control the behavior. */ + decode_flags: u16, + + /** Copy of the parameters given to the initialize/update for + * convenience. + */ + part_index: c.int, + ctx: const_context_t, + chunk: chunk_info_t, + + /** Can be used by the user to pass custom context data through + * the decode pipeline. + */ + decoding_user_data: rawptr, + + /** The (compressed) buffer. + * + * If `NULL`, will be allocated during the run of the pipeline. + * + * If the caller wishes to take control of the buffer, simple + * adopt the pointer and set it to `NULL` here. Be cognizant of any + * custom allocators. + */ + packed_buffer: rawptr, + + /** Used when re-using the same decode pipeline struct to know if + * chunk is changed size whether current buffer is large enough. + */ + packed_alloc_size: c.size_t, + + /** The decompressed buffer (unpacked_size from the chunk block + * info), but still packed into storage order, only needed for + * compressed files. + * + * If `NULL`, will be allocated during the run of the pipeline when + * needed. + * + * If the caller wishes to take control of the buffer, simple + * adopt the pointer and set it to `NULL` here. Be cognizant of any + * custom allocators. + */ + unpacked_buffer: rawptr, + + /** Used when re-using the same decode pipeline struct to know if + * chunk is changed size whether current buffer is large enough. + */ + unpacked_alloc_size: c.size_t, + + /** For deep or other non-image data: packed sample table + * (compressed, raw on disk representation). + */ + packed_sample_count_table: rawptr, + packed_sample_count_alloc_size: c.size_t, + + /** Usable, native sample count table. Depending on the flag set + * above, will be decoded to either a cumulative list (n, n+m, + * n+m+o, ...), or an individual table (n, m, o, ...). As an + * optimization, if the latter individual count table is chosen, + * an extra int32_t will be allocated at the end of the table to + * contain the total count of samples, so the table will be n+1 + * samples in size. + */ + sample_count_table: [^]i32, + sample_count_alloc_size: c.size_t, + + /** A scratch buffer of unpacked_size for intermediate results. + * + * If `NULL`, will be allocated during the run of the pipeline when + * needed. + * + * If the caller wishes to take control of the buffer, simple + * adopt the pointer and set it to `NULL` here. Be cognizant of any + * custom allocators. + */ + scratch_buffer_1: rawptr, + + /** Used when re-using the same decode pipeline struct to know if + * chunk is changed size whether current buffer is large enough. + */ + scratch_alloc_size_1: c.size_t, + + /** Some decompression routines may need a second scratch buffer (zlib). + * + * If `NULL`, will be allocated during the run of the pipeline when + * needed. + * + * If the caller wishes to take control of the buffer, simple + * adopt the pointer and set it to `NULL` here. Be cognizant of any + * custom allocators. + */ + scratch_buffer_2: rawptr, + + /** Used when re-using the same decode pipeline struct to know if + * chunk is changed size whether current buffer is large enough. + */ + scratch_alloc_size_2: c.size_t, + + /** Enable a custom allocator for the different buffers (if + * decoding on a GPU). If `NULL`, will use the allocator from the + * context. + */ + alloc_fn: proc "c" (transcoding_pipeline_buffer_id_t, c.size_t) -> rawptr, + + /** Enable a custom allocator for the different buffers (if + * decoding on a GPU). If `NULL`, will use the allocator from the + * context. + */ + free_fn: proc "c" (transcoding_pipeline_buffer_id_t, rawptr), + + /** Function chosen to read chunk data from the context. + * + * Initialized to a default generic read routine, may be updated + * based on channel information when + * exr_decoding_choose_default_routines() is called. This is done such that + * if the file is uncompressed and the output channel data is + * planar and the same type, the read function can read straight + * into the output channels, getting closer to a zero-copy + * operation. Otherwise a more traditional read, decompress, then + * unpack pipeline will be used with a default reader. + * + * This is allowed to be overridden, but probably is not necessary + * in most scenarios. + */ + read_fn: proc "c" (pipeline: ^decode_pipeline_t) -> result_t, + + /** Function chosen based on the compression type of the part to + * decompress data. + * + * If the user has a custom decompression method for the + * compression on this part, this can be changed after + * initialization. + * + * If only compressed data is desired, then assign this to `NULL` + * after initialization. + */ + decompress_fn: proc "c" (pipeline: ^decode_pipeline_t) -> result_t, + + /** Function which can be provided if you have bespoke handling for + * non-image data and need to re-allocate the data to handle the + * about-to-be unpacked data. + * + * If left `NULL`, will assume the memory pointed to by the channel + * pointers is sufficient. + */ + realloc_nonimage_data_fn: proc "c" (pipeline: ^decode_pipeline_t) -> result_t, + + /** Function chosen based on the output layout of the channels of the part to + * decompress data. + * + * This will be `NULL` after initialization, until the user + * specifies a custom routine, or initializes the channel data and + * calls exr_decoding_choose_default_routines(). + * + * If only compressed data is desired, then leave or assign this + * to `NULL` after initialization. + */ + unpack_and_convert_fn: proc "c" (pipeline: ^decode_pipeline_t) -> result_t, + + /** Small stash of channel info values. This is faster than calling + * malloc when the channel count in the part is small (RGBAZ), + * which is super common, however if there are a large number of + * channels, it will allocate space for that, so do not rely on + * this being used. + */ + _quick_chan_store: [5]coding_channel_info_t, +} + +DECODE_PIPELINE_INITIALIZER :: decode_pipeline_t{} + + +@(link_prefix="exr_", default_calling_convention="c") +foreign lib { + /** Initialize the decoding pipeline structure with the channel info + * for the specified part, and the first block to be read. + * + * NB: The decode->unpack_and_convert_fn field will be `NULL` after this. If that + * stage is desired, initialize the channel output information and + * call exr_decoding_choose_default_routines(). + */ + decoding_initialize :: proc( + ctxt: const_context_t, + part_index: c.int, + cinfo: ^chunk_info_t, + decode: ^decode_pipeline_t) -> result_t --- + + /** Given an initialized decode pipeline, find appropriate functions + * to read and shuffle/convert data into the defined channel outputs. + * + * Calling this is not required if custom routines will be used, or if + * just the raw compressed data is desired. Although in that scenario, + * it is probably easier to just read the chunk directly using + * exr_read_chunk(). + */ + decoding_choose_default_routines :: proc( + ctxt: const_context_t, part_index: c.int, decode: ^decode_pipeline_t) -> result_t --- + + /** Given a decode pipeline previously initialized, update it for the + * new chunk to be read. + * + * In this manner, memory buffers can be re-used to avoid continual + * malloc/free calls. Further, it allows the previous choices for + * the various functions to be quickly re-used. + */ + decoding_update :: proc( + ctxt: const_context_t, + part_index: c.int, + cinfo: ^chunk_info_t, + decode: ^decode_pipeline_t) -> result_t --- + + /** Execute the decoding pipeline. */ + decoding_run :: proc( + ctxt: const_context_t, part_index: c.int, decode: ^decode_pipeline_t) -> result_t --- + + /** Free any intermediate memory in the decoding pipeline. + * + * This does *not* free any pointers referred to in the channel info + * areas, but rather only the intermediate buffers and memory needed + * for the structure itself. + */ + decoding_destroy :: proc(ctxt: const_context_t, decode: ^decode_pipeline_t) -> result_t --- +} \ No newline at end of file diff --git a/vendor/OpenEXRCore/exr_encode.odin b/vendor/OpenEXRCore/exr_encode.odin new file mode 100644 index 000000000..9d9e80c22 --- /dev/null +++ b/vendor/OpenEXRCore/exr_encode.odin @@ -0,0 +1,323 @@ +package vendor_openexr + +when ODIN_OS == .Windows { + foreign import lib "OpenEXRCore-3_1.lib" +} else { + foreign import lib "system:OpenEXRCore-3_1" +} + +import "core:c" + +/** Can be bit-wise or'ed into the decode_flags in the decode pipeline. + * + * Indicates that the sample count table should be encoded from an + * individual sample count list (n, m, o, ...), meaning it will have + * to compute the cumulative counts on the fly. + * + * Without this (i.e. a value of 0 in that bit), indicates the sample + * count table is already a cumulative list (n, n+m, n+m+o, ...), + * which is the on-disk representation. + */ +ENCODE_DATA_SAMPLE_COUNTS_ARE_INDIVIDUAL :: u16(1 << 0) + +/** Can be bit-wise or'ed into the decode_flags in the decode pipeline. + * + * Indicates that the data in the channel pointers to encode from is not + * a direct pointer, but instead is a pointer-to-pointers. In this + * mode, the user_pixel_stride and user_line_stride are used to + * advance the pointer offsets for each pixel in the output, but the + * user_bytes_per_element and user_data_type are used to put + * (successive) entries into each destination. + * + * So each channel pointer must then point to an array of + * chunk.width * chunk.height pointers. If an entry is + * `NULL`, 0 samples will be placed in the output. + * + * If this is NOT set (0), the default packing routine assumes the + * data will be planar and contiguous (each channel is a separate + * memory block), ignoring user_line_stride and user_pixel_stride and + * advancing only by the sample counts and bytes per element. + */ +ENCODE_NON_IMAGE_DATA_AS_POINTERS :: u16(1 << 1) + +/** Struct meant to be used on a per-thread basis for writing exr data. + * + * As should be obvious, this structure is NOT thread safe, but rather + * meant to be used by separate threads, which can all be accessing + * the same context concurrently. + */ + encode_pipeline_t :: struct { + /** The output channel information for this chunk. + * + * User is expected to fill the channel pointers for the input + * channels. For writing, all channels must be initialized prior + * to using exr_encoding_choose_default_routines(). If a custom pack routine + * is written, that is up to the implementor. + * + * Describes the channel information. This information is + * allocated dynamically during exr_encoding_initialize(). + */ + channels: [^]coding_channel_info_t, + channel_count: i16, + + /** Encode flags to control the behavior. */ + encode_flags: u16, + + /** Copy of the parameters given to the initialize/update for convenience. */ + part_index: c.int, + ctx: const_context_t, + chunk: chunk_info_t, + + /** Can be used by the user to pass custom context data through + * the encode pipeline. + */ + encoding_user_data: rawptr, + + /** The packed buffer where individual channels have been put into here. + * + * If `NULL`, will be allocated during the run of the pipeline. + * + * If the caller wishes to take control of the buffer, simple + * adopt the pointer and set it to `NULL` here. Be cognizant of any + * custom allocators. + */ + packed_buffer: rawptr, + + /** Differing from the allocation size, the number of actual bytes */ + packed_bytes: u64, + + /** Used when re-using the same encode pipeline struct to know if + * chunk is changed size whether current buffer is large enough + * + * If `NULL`, will be allocated during the run of the pipeline. + * + * If the caller wishes to take control of the buffer, simple + * adopt the pointer and set it to `NULL` here. Be cognizant of any + * custom allocators. + */ + packed_alloc_size: c.size_t, + + /** For deep data. NB: the members NOT const because we need to + * temporarily swap it to xdr order and restore it (to avoid a + * duplicate buffer allocation). + * + * Depending on the flag set above, will be treated either as a + * cumulative list (n, n+m, n+m+o, ...), or an individual table + * (n, m, o, ...). */ + sample_count_table: [^]i32, + + /** Allocated table size (to avoid re-allocations). Number of + * samples must always be width * height for the chunk. + */ + sample_count_alloc_size: c.size_t, + + /** Packed sample table (compressed, raw on disk representation) + * for deep or other non-image data. + */ + packed_sample_count_table: rawptr, + + /** Number of bytes to write (actual size) for the + * packed_sample_count_table. + */ + packed_sample_count_bytes: c.size_t, + + /** Allocated size (to avoid re-allocations) for the + * packed_sample_count_table. + */ + packed_sample_count_alloc_size: c.size_t, + + /** The compressed buffer, only needed for compressed files. + * + * If `NULL`, will be allocated during the run of the pipeline when + * needed. + * + * If the caller wishes to take control of the buffer, simple + * adopt the pointer and set it to `NULL` here. Be cognizant of any + * custom allocators. + */ + compressed_buffer: rawptr, + + /** Must be filled in as the pipeline runs to inform the writing + * software about the compressed size of the chunk (if it is an + * uncompressed file or the compression would make the file + * larger, it is expected to be the packed_buffer) + * + * If the caller wishes to take control of the buffer, simple + * adopt the pointer and set it to zero here. Be cognizant of any + * custom allocators. + */ + compressed_bytes: c.size_t, + + /** Used when re-using the same encode pipeline struct to know if + * chunk is changed size whether current buffer is large enough. + * + * If `NULL`, will be allocated during the run of the pipeline when + * needed. + * + * If the caller wishes to take control of the buffer, simple + * adopt the pointer and set it to zero here. Be cognizant of any + * custom allocators. + */ + compressed_alloc_size: c.size_t, + + /** A scratch buffer for intermediate results. + * + * If `NULL`, will be allocated during the run of the pipeline when + * needed. + * + * If the caller wishes to take control of the buffer, simple + * adopt the pointer and set it to `NULL` here. Be cognizant of any + * custom allocators. + */ + scratch_buffer_1: rawptr, + + /** Used when re-using the same encode pipeline struct to know if + * chunk is changed size whether current buffer is large enough. + * + * If `NULL`, will be allocated during the run of the pipeline when + * needed. + * + * If the caller wishes to take control of the buffer, simple + * adopt the pointer and set it to `NULL` here. Be cognizant of any + * custom allocators. + */ + scratch_alloc_size_1: c.size_t, + + /** Some compression routines may need a second scratch buffer. + * + * If `NULL`, will be allocated during the run of the pipeline when + * needed. + * + * If the caller wishes to take control of the buffer, simple + * adopt the pointer and set it to `NULL` here. Be cognizant of any + * custom allocators. + */ + scratch_buffer_2: rawptr, + + /** Used when re-using the same encode pipeline struct to know if + * chunk is changed size whether current buffer is large enough. + */ + scratch_alloc_size_2: c.size_t, + + /** Enable a custom allocator for the different buffers (if + * encoding on a GPU). If `NULL`, will use the allocator from the + * context. + */ + alloc_fn: proc "c" (transcoding_pipeline_buffer_id_t, c.size_t) -> rawptr, + + /** Enable a custom allocator for the different buffers (if + * encoding on a GPU). If `NULL`, will use the allocator from the + * context. + */ + free_fn: proc "c" (transcoding_pipeline_buffer_id_t, rawptr), + + /** Function chosen based on the output layout of the channels of the part to + * decompress data. + * + * If the user has a custom method for the + * compression on this part, this can be changed after + * initialization. + */ + convert_and_pack_fn: proc "c" (pipeline: ^encode_pipeline_t) -> result_t, + + /** Function chosen based on the compression type of the part to + * compress data. + * + * If the user has a custom compression method for the compression + * type on this part, this can be changed after initialization. + */ + compress_fn: proc "c" (pipeline: ^encode_pipeline_t) -> result_t, + + /** This routine is used when waiting for other threads to finish + * writing previous chunks such that this thread can write this + * chunk. This is used for parts which have a specified chunk + * ordering (increasing/decreasing y) and the chunks can not be + * written randomly (as could be true for uncompressed). + * + * This enables the calling application to contribute thread time + * to other computation as needed, or just use something like + * pthread_yield(). + * + * By default, this routine will be assigned to a function which + * returns an error, failing the encode immediately. In this way, + * it assumes that there is only one thread being used for + * writing. + * + * It is up to the user to provide an appropriate routine if + * performing multi-threaded writing. + */ + yield_until_ready_fn: proc "c" (pipeline: ^encode_pipeline_t) -> result_t, + + /** Function chosen to write chunk data to the context. + * + * This is allowed to be overridden, but probably is not necessary + * in most scenarios. + */ + write_fn: proc "c" (pipeline: ^encode_pipeline_t) -> result_t, + + /** Small stash of channel info values. This is faster than calling + * malloc when the channel count in the part is small (RGBAZ), + * which is super common, however if there are a large number of + * channels, it will allocate space for that, so do not rely on + * this being used. + */ + _quick_chan_store: [5]coding_channel_info_t, +} + +ENCODE_PIPELINE_INITIALIZER :: encode_pipeline_t{} + + +@(link_prefix="exr_", default_calling_convention="c") +foreign lib { + /** Initialize the encoding pipeline structure with the channel info + * for the specified part based on the chunk to be written. + * + * NB: The encode_pipe->pack_and_convert_fn field will be `NULL` after this. If that + * stage is desired, initialize the channel output information and + * call exr_encoding_choose_default_routines(). + */ + encoding_initialize :: proc( + ctxt: const_context_t, + part_index: c.int, + cinfo: ^chunk_info_t, + encode_pipe: ^encode_pipeline_t) -> result_t --- + + /** Given an initialized encode pipeline, find an appropriate + * function to shuffle and convert data into the defined channel + * outputs. + * + * Calling this is not required if a custom routine will be used, or + * if just the raw decompressed data is desired. + */ + encoding_choose_default_routines :: proc( + ctxt: const_context_t, + part_index: c.int, + encode_pipe: ^encode_pipeline_t) -> result_t --- + + /** Given a encode pipeline previously initialized, update it for the + * new chunk to be written. + * + * In this manner, memory buffers can be re-used to avoid continual + * malloc/free calls. Further, it allows the previous choices for + * the various functions to be quickly re-used. + */ + encoding_update :: proc( + ctxt: const_context_t, + part_index: c.int, + cinfo: ^chunk_info_t, + encode_pipe: ^encode_pipeline_t) -> result_t --- + + /** Execute the encoding pipeline. */ + encoding_run :: proc( + ctxt: const_context_t, + part_index: c.int, + encode_pipe: ^encode_pipeline_t) -> result_t --- + + /** Free any intermediate memory in the encoding pipeline. + * + * This does NOT free any pointers referred to in the channel info + * areas, but rather only the intermediate buffers and memory needed + * for the structure itself. + */ + encoding_destroy :: proc(ctxt: const_context_t, encode_pipe: ^encode_pipeline_t) -> result_t --- +} \ No newline at end of file diff --git a/vendor/OpenEXRCore/exr_errors.odin b/vendor/OpenEXRCore/exr_errors.odin new file mode 100644 index 000000000..092b888dc --- /dev/null +++ b/vendor/OpenEXRCore/exr_errors.odin @@ -0,0 +1,67 @@ +package vendor_openexr + +when ODIN_OS == .Windows { + foreign import lib "OpenEXRCore-3_1.lib" +} else { + foreign import lib "system:OpenEXRCore-3_1" +} + +import "core:c" + +#assert(size_of(c.int) == size_of(i32)) + +/** Error codes that may be returned by various functions. */ +/** Return type for all functions. */ +result_t :: enum i32 { + SUCCESS = 0, + OUT_OF_MEMORY, + MISSING_CONTEXT_ARG, + INVALID_ARGUMENT, + ARGUMENT_OUT_OF_RANGE, + FILE_ACCESS, + FILE_BAD_HEADER, + NOT_OPEN_READ, + NOT_OPEN_WRITE, + HEADER_NOT_WRITTEN, + READ_IO, + WRITE_IO, + NAME_TOO_LONG, + MISSING_REQ_ATTR, + INVALID_ATTR, + NO_ATTR_BY_NAME, + ATTR_TYPE_MISMATCH, + ATTR_SIZE_MISMATCH, + SCAN_TILE_MIXEDAPI, + TILE_SCAN_MIXEDAPI, + MODIFY_SIZE_CHANGE, + ALREADY_WROTE_ATTRS, + BAD_CHUNK_LEADER, + CORRUPT_CHUNK, + INCORRECT_PART, + INCORRECT_CHUNK, + USE_SCAN_DEEP_WRITE, + USE_TILE_DEEP_WRITE, + USE_SCAN_NONDEEP_WRITE, + USE_TILE_NONDEEP_WRITE, + INVALID_SAMPLE_DATA, + FEATURE_NOT_IMPLEMENTED, + UNKNOWN, +} + +error_code_t :: result_t + + +@(link_prefix="exr_", default_calling_convention="c") +foreign lib { + /** @brief Return a static string corresponding to the specified error code. + * + * The string should not be freed (it is compiled into the binary). + */ + get_default_error_message :: proc(code: result_t) -> cstring --- + + /** @brief Return a static string corresponding to the specified error code. + * + * The string should not be freed (it is compiled into the binary). + */ + get_error_code_as_string :: proc(code: result_t) -> cstring --- +} diff --git a/vendor/OpenEXRCore/exr_part.odin b/vendor/OpenEXRCore/exr_part.odin new file mode 100644 index 000000000..7d7530e50 --- /dev/null +++ b/vendor/OpenEXRCore/exr_part.odin @@ -0,0 +1,737 @@ +package vendor_openexr + +when ODIN_OS == .Windows { + foreign import lib "OpenEXRCore-3_1.lib" +} else { + foreign import lib "system:OpenEXRCore-3_1" +} + +import "core:c" + +attr_list_access_mode_t :: enum c.int { + FILE_ORDER, /**< Order they appear in the file */ + SORTED_ORDER, /**< Alphabetically sorted */ +} + +@(link_prefix="exr_", default_calling_convention="c") +foreign lib { + /** @brief Query how many parts are in the file. */ + get_count :: proc (ctxt: const_context_t, count: ^c.int) -> result_t --- + + /** @brief Query the part name for the specified part. + * + * NB: If this file is a single part file and name has not been set, this + * will return `NULL`. + */ + get_name :: proc(ctxt: const_context_t, part_index: c.int, out: ^cstring) -> result_t --- + + /** @brief Query the storage type for the specified part. */ + get_storage :: proc(ctxt: const_context_t, part_index: c.int, out: ^storage_t) -> result_t --- + + /** @brief Define a new part in the file. */ + add_part :: proc( + ctxt: context_t, + partname: rawptr, + type: storage_t, + new_index: ^c.int) -> result_t --- + + /** @brief Query how many levels are in the specified part. + * + * If the part is a tiled part, fill in how many tile levels are present. + * + * Return `ERR_SUCCESS` on success, an error otherwise (i.e. if the part + * is not tiled). + * + * It is valid to pass `NULL` to either of the @p levelsx or @p levelsy + * arguments, which enables testing if this part is a tiled part, or + * if you don't need both (i.e. in the case of a mip-level tiled + * image) + */ + get_tile_levels :: proc( + ctxt: const_context_t, + part_index: c.int, + levelsx: ^i32, + levelsy: ^i32) -> result_t --- + + /** @brief Query the tile size for a particular level in the specified part. + * + * If the part is a tiled part, fill in the tile size for the + * specified part/level. + * + * Return `ERR_SUCCESS` on success, an error otherwise (i.e. if the + * part is not tiled). + * + * It is valid to pass `NULL` to either of the @p tilew or @p tileh + * arguments, which enables testing if this part is a tiled part, or + * if you don't need both (i.e. in the case of a mip-level tiled + * image) + */ + get_tile_sizes :: proc( + ctxt: const_context_t, + part_index: c.int, + levelx: c.int, + levely: c.int, + tilew: ^i32, + tileh: ^i32) -> result_t --- + + /** @brief Query the data sizes for a particular level in the specified part. + * + * If the part is a tiled part, fill in the width/height for the + * specified levels. + * + * Return `ERR_SUCCESS` on success, an error otherwise (i.e. if the part + * is not tiled). + * + * It is valid to pass `NULL` to either of the @p levw or @p levh + * arguments, which enables testing if this part is a tiled part, or + * if you don't need both for some reason. + */ + get_level_sizes :: proc( + ctxt: const_context_t, + part_index: c.int, + levelx: c.int, + levely: c.int, + levw: ^i32, + levh: ^i32) -> result_t --- + + /** Return the number of chunks contained in this part of the file. + * + * As in the technical documentation for OpenEXR, the chunk is the + * generic term for a pixel data block. This is the atomic unit that + * this library uses to negotiate data to and from a context. + * + * This should be used as a basis for splitting up how a file is + * processed. Depending on the compression, a different number of + * scanlines are encoded in each chunk, and since those need to be + * encoded/decoded as a block, the chunk should be the basis for I/O + * as well. + */ + get_chunk_count :: proc(ctxt: const_context_t, part_index: c.int, out: ^i32) -> result_t --- + + /** Return the number of scanlines chunks for this file part. + * + * When iterating over a scanline file, this may be an easier metric + * for multi-threading or other access than only negotiating chunk + * counts, and so is provided as a utility. + */ + get_scanlines_per_chunk :: proc(ctxt: const_context_t, part_index: c.int, out: ^i32) -> result_t --- + + /** Return the maximum unpacked size of a chunk for the file part. + * + * This may be used ahead of any actual reading of data, so can be + * used to pre-allocate buffers for multiple threads in one block or + * whatever your application may require. + */ + get_chunk_unpacked_size :: proc(ctxt: const_context_t, part_index: c.int, out: ^u64) -> result_t --- + + /** @brief Retrieve the zip compression level used for the specified part. + * + * This only applies when the compression method involves using zip + * compression (zip, zips, some modes of DWAA/DWAB). + * + * This value is NOT persisted in the file, and only exists for the + * lifetime of the context, so will be at the default value when just + * reading a file. + */ + get_zip_compression_level :: proc(ctxt: const_context_t, part_index: c.int, level: ^c.int) -> result_t --- + + /** @brief Set the zip compression method used for the specified part. + * + * This only applies when the compression method involves using zip + * compression (zip, zips, some modes of DWAA/DWAB). + * + * This value is NOT persisted in the file, and only exists for the + * lifetime of the context, so this value will be ignored when + * reading a file. + */ + set_zip_compression_level :: proc(ctxt: context_t, part_index: c.int, level: c.int) -> result_t --- + + /** @brief Retrieve the dwa compression level used for the specified part. + * + * This only applies when the compression method is DWAA/DWAB. + * + * This value is NOT persisted in the file, and only exists for the + * lifetime of the context, so will be at the default value when just + * reading a file. + */ + get_dwa_compression_level :: proc(ctxt: const_context_t, part_index: c.int, level: ^f32) -> result_t --- + + /** @brief Set the dwa compression method used for the specified part. + * + * This only applies when the compression method is DWAA/DWAB. + * + * This value is NOT persisted in the file, and only exists for the + * lifetime of the context, so this value will be ignored when + * reading a file. + */ + set_dwa_compression_level :: proc(ctxt: context_t, part_index: c.int, level: f32) -> result_t --- + + /**************************************/ + + /** @defgroup PartMetadata Functions to get and set metadata for a particular part. + * @{ + * + */ + + /** @brief Query the count of attributes in a part. */ + get_attribute_count :: proc(ctxt: const_context_t, part_index: c.int, count: ^i32) -> result_t --- + + /** @brief Query a particular attribute by index. */ + get_attribute_by_index :: proc( + ctxt: const_context_t, + part_index: c.int, + mode: attr_list_access_mode_t, + idx: i32, + outattr: ^^attribute_t) -> result_t --- + + /** @brief Query a particular attribute by name. */ + get_attribute_by_name :: proc( + ctxt: const_context_t, + part_index: c.int, + name: cstring, + outattr: ^^attribute_t) -> result_t --- + + /** @brief Query the list of attributes in a part. + * + * This retrieves a list of attributes currently defined in a part. + * + * If outlist is `NULL`, this function still succeeds, filling only the + * count. In this manner, the user can allocate memory for the list of + * attributes, then re-call this function to get the full list. + */ + get_attribute_list :: proc( + ctxt: const_context_t, + part_index: c.int, + mode: attr_list_access_mode_t, + count: ^i32, + outlist: ^[^]attribute_t) -> result_t --- + + /** Declare an attribute within the specified part. + * + * Only valid when a file is opened for write. + */ + attr_declare_by_type :: proc( + ctxt: context_t, + part_index: c.int, + name: cstring, + type: cstring, + newattr: ^^attribute_t) -> result_t --- + + /** @brief Declare an attribute within the specified part. + * + * Only valid when a file is opened for write. + */ + attr_declare :: proc( + ctxt: context_t, + part_index: c.int, + name: cstring, + type: attribute_type_t, + newattr: ^^attribute_t) -> result_t --- + + /** + * @defgroup RequiredAttributeHelpers Required Attribute Utililities + * + * @brief These are a group of functions for attributes that are + * required to be in every part of every file. + * + * @{ + */ + + /** @brief Initialize all required attributes for all files. + * + * NB: other file types do require other attributes, such as the tile + * description for a tiled file. + */ + initialize_required_attr :: proc( + ctxt: context_t, + part_index: c.int, + displayWindow: ^attr_box2i_t, + dataWindow: ^attr_box2i_t, + pixelaspectratio: f32, + screenWindowCenter: attr_v2f_t, + screenWindowWidth: f32, + lineorder: lineorder_t, + ctype: compression_t) -> result_t --- + + /** @brief Initialize all required attributes to default values: + * + * - `displayWindow` is set to (0, 0 -> @p width - 1, @p height - 1) + * - `dataWindow` is set to (0, 0 -> @p width - 1, @p height - 1) + * - `pixelAspectRatio` is set to 1.0 + * - `screenWindowCenter` is set to 0.f, 0.f + * - `screenWindowWidth` is set to 1.f + * - `lineorder` is set to `INCREASING_Y` + * - `compression` is set to @p ctype + */ + initialize_required_attr_simple :: proc( + ctxt: context_t, + part_index: c.int, + width: i32, + height: i32, + ctype: compression_t) -> result_t --- + + /** @brief Copy the attributes from one part to another. + * + * This allows one to quickly unassigned attributes from one source to another. + * + * If an attribute in the source part has not been yet set in the + * destination part, the item will be copied over. + * + * For example, when you add a part, the storage type and name + * attributes are required arguments to the definition of a new part, + * but channels has not yet been assigned. So by calling this with an + * input file as the source, you can copy the channel definitions (and + * any other unassigned attributes from the source). + */ + copy_unset_attributes :: proc( + ctxt: context_t, + part_index: c.int, + source: const_context_t, + src_part_index: c.int) -> result_t --- + + /** @brief Retrieve the list of channels. */ + get_channels :: proc(ctxt: const_context_t, part_index: c.int, chlist: ^^attr_chlist_t) -> result_t --- + + /** @brief Define a new channel to the output file part. + * + * The @p percept parameter is used for lossy compression techniques + * to indicate that the value represented is closer to linear (1) or + * closer to logarithmic (0). For r, g, b, luminance, this is normally + * 0. + */ + add_channel :: proc( + ctxt: context_t, + part_index: c.int, + name: cstring, + ptype: pixel_type_t, + percept: perceptual_treatment_t, + xsamp: i32, + ysamp: i32) -> c.int --- + + /** @brief Copy the channels from another source. + * + * Useful if you are manually constructing the list or simply copying + * from an input file. + */ + set_channels :: proc(ctxt: context_t, part_index: c.int, channels: ^attr_chlist_t) -> result_t --- + + /** @brief Retrieve the compression method used for the specified part. */ + get_compression :: proc(ctxt: const_context_t, part_index: c.int, compression: ^compression_t) -> result_t --- + /** @brief Set the compression method used for the specified part. */ + set_compression :: proc(ctxt: context_t, part_index: c.int, ctype: compression_t) -> result_t --- + + /** @brief Retrieve the data window for the specified part. */ + get_data_window :: proc(ctxt: const_context_t, part_index: c.int, out: ^attr_box2i_t) -> result_t --- + /** @brief Set the data window for the specified part. */ + set_data_window :: proc(ctxt: context_t, part_index: c.int, dw: ^attr_box2i_t) -> c.int --- + + /** @brief Retrieve the display window for the specified part. */ + get_display_window :: proc(ctxt: const_context_t, part_index: c.int, out: ^attr_box2i_t) -> result_t --- + /** @brief Set the display window for the specified part. */ + set_display_window :: proc(ctxt: context_t, part_index: c.int, dw: ^attr_box2i_t) -> c.int --- + + /** @brief Retrieve the line order for storing data in the specified part (use 0 for single part images). */ + get_lineorder :: proc(ctxt: const_context_t, part_index: c.int, out: ^lineorder_t) -> result_t --- + /** @brief Set the line order for storing data in the specified part (use 0 for single part images). */ + set_lineorder :: proc(ctxt: context_t, part_index: c.int, lo: lineorder_t) -> result_t --- + + /** @brief Retrieve the pixel aspect ratio for the specified part (use 0 for single part images). */ + get_pixel_aspect_ratio :: proc(ctxt: const_context_t, part_index: c.int, par: ^f32) -> result_t --- + /** @brief Set the pixel aspect ratio for the specified part (use 0 for single part images). */ + set_pixel_aspect_ratio :: proc(ctxt: context_t, part_index: c.int, par: f32) -> result_t --- + + /** @brief Retrieve the screen oriented window center for the specified part (use 0 for single part images). */ + get_screen_window_center :: proc(ctxt: const_context_t, part_index: c.int, wc: ^attr_v2f_t) -> result_t --- + /** @brief Set the screen oriented window center for the specified part (use 0 for single part images). */ + set_screen_window_center :: proc(ctxt: context_t, part_index: c.int, wc: ^attr_v2f_t) -> c.int --- + + /** @brief Retrieve the screen oriented window width for the specified part (use 0 for single part images). */ + get_screen_window_width :: proc(ctxt: const_context_t, part_index: c.int, out: ^f32) -> result_t --- + /** @brief Set the screen oriented window width for the specified part (use 0 for single part images). */ + set_screen_window_width :: proc(ctxt: context_t, part_index: c.int, ssw: f32) -> result_t --- + + /** @brief Retrieve the tiling info for a tiled part (use 0 for single part images). */ + get_tile_descriptor :: proc( + ctxt: const_context_t, + part_index: c.int, + xsize: ^u32, + ysize: ^u32, + level: ^tile_level_mode_t, + round: ^tile_round_mode_t) -> result_t --- + + /** @brief Set the tiling info for a tiled part (use 0 for single part images). */ + set_tile_descriptor :: proc( + ctxt: context_t, + part_index: c.int, + x_size: u32, + y_size: u32, + level_mode: tile_level_mode_t, + round_mode: tile_round_mode_t) -> result_t --- + + set_name :: proc(ctxt: context_t, part_index: c.int, val: cstring) -> result_t --- + + get_version :: proc(ctxt: const_context_t, part_index: c.int, out: ^i32) -> result_t --- + + set_version :: proc(ctxt: context_t, part_index: c.int, val: i32) -> result_t --- + + set_chunk_count :: proc(ctxt: context_t, part_index: c.int, val: i32) -> result_t --- + + /** @} */ /* required attr group. */ + + /** + * @defgroup BuiltinAttributeHelpers Attribute utilities for builtin types + * + * @brief These are a group of functions for attributes that use the builtin types. + * + * @{ + */ + + attr_get_box2i :: proc( + ctxt: const_context_t, + part_index: c.int, + name: cstring, + outval: ^attr_box2i_t) -> result_t --- + + attr_set_box2i :: proc( + ctxt: context_t, + part_index: c.int, + name: cstring, + val: ^attr_box2i_t) -> result_t --- + + attr_get_box2f :: proc( + ctxt: const_context_t, + part_index: c.int, + name: cstring, + outval: ^attr_box2f_t) -> result_t --- + + attr_set_box2f :: proc( + ctxt: context_t, + part_index: c.int, + name: cstring, + val: ^attr_box2f_t) -> result_t --- + + /** @brief Zero-copy query of channel data. + * + * Do not free or manipulate the @p chlist data, or use + * after the lifetime of the context. + */ + attr_get_channels :: proc( + ctxt: const_context_t, + part_index: c.int, + name: cstring, + chlist: ^^attr_chlist_t) -> result_t --- + + /** @brief This allows one to quickly copy the channels from one file + * to another. + */ + attr_set_channels :: proc( + ctxt: context_t, + part_index: c.int, + name: cstring, + channels: ^attr_chlist_t) -> result_t --- + + attr_get_chromaticities :: proc( + ctxt: const_context_t, + part_index: c.int, + name: cstring, + chroma: ^attr_chromaticities_t) -> result_t --- + + attr_set_chromaticities :: proc( + ctxt: context_t, + part_index: c.int, + name: cstring, + chroma: ^attr_chromaticities_t) -> result_t --- + + attr_get_compression :: proc( + ctxt: const_context_t, + part_index: c.int, + name: cstring, + out: ^compression_t) -> result_t --- + + attr_set_compression :: proc( + ctxt: context_t, + part_index: c.int, + name: cstring, + comp: compression_t) -> result_t --- + + attr_get_double :: proc(ctxt: const_context_t, part_index: c.int, name: cstring, out: f64) -> result_t --- + + attr_set_double :: proc(ctxt: context_t, part_index: c.int, name: cstring, val: f64) -> result_t --- + + attr_get_envmap :: proc( + ctxt: const_context_t, + part_index: c.int, + name: cstring, + out: ^envmap_t) -> result_t --- + + attr_set_envmap :: proc(ctxt: context_t, part_index: c.int, name: cstring, emap: envmap_t) -> result_t --- + + attr_get_float :: proc(ctxt: const_context_t, part_index: c.int, name: cstring, out: ^f32) -> result_t --- + + attr_set_float :: proc(ctxt: context_t, part_index: c.int, name: cstring, val: f32) -> result_t --- + + /** @brief Zero-copy query of float data. + * + * Do not free or manipulate the @p out data, or use after the + * lifetime of the context. + */ + attr_get_float_vector :: proc( + ctxt: const_context_t, + part_index: c.int, + name: cstring, + sz: ^i32, + out: ^[^]f32) -> result_t --- + + attr_set_float_vector :: proc( + ctxt: context_t, + part_index: c.int, + name: cstring, + sz: i32, + vals: [^]f32) -> result_t --- + + attr_get_int :: proc(ctxt: const_context_t, part_index: c.int, name: cstring, out: ^i32) -> result_t --- + + attr_set_int :: proc(ctxt: context_t, part_index: c.int, name: cstring, val: i32) -> result_t --- + + attr_get_keycode :: proc( + ctxt: const_context_t, + part_index: c.int, + name: cstring, + out: ^attr_keycode_t) -> result_t --- + + attr_set_keycode :: proc( + ctxt: context_t, + part_index: c.int, + name: cstring, + kc: ^attr_keycode_t) -> result_t --- + + attr_get_lineorder :: proc( + ctxt: const_context_t, + part_index: c.int, + name: cstring, + out: ^lineorder_t) -> result_t --- + + attr_set_lineorder :: proc(ctxt: context_t, part_index: c.int, name: cstring, lo: lineorder_t) -> result_t --- + + attr_get_m33f :: proc( + ctxt: const_context_t, + part_index: c.int, + name: cstring, + out: ^attr_m33f_t) -> result_t --- + + attr_set_m33f :: proc( + ctxt: context_t, + part_index: c.int, + name: cstring, + m: ^attr_m33f_t) -> result_t --- + + attr_get_m33d :: proc( + ctxt: const_context_t, + part_index: c.int, + name: cstring, + out: ^attr_m33d_t) -> result_t --- + + attr_set_m33d :: proc( + ctxt: context_t, + part_index: c.int, + name: cstring, + m: ^attr_m33d_t) -> result_t --- + + attr_get_m44f :: proc( + ctxt: const_context_t, + part_index: c.int, + name: cstring, + out: ^attr_m44f_t) -> result_t --- + + attr_set_m44f :: proc( + ctxt: context_t, + part_index: c.int, + name: cstring, + m: ^attr_m44f_t) -> result_t --- + + attr_get_m44d :: proc( + ctxt: const_context_t, + part_index: c.int, + name: cstring, + out: ^attr_m44d_t) -> result_t --- + + attr_set_m44d :: proc( + ctxt: context_t, + part_index: c.int, + name: cstring, + m: ^attr_m44d_t) -> result_t --- + + attr_get_preview :: proc( + ctxt: const_context_t, + part_index: c.int, + name: cstring, + out: ^attr_preview_t) -> result_t --- + + attr_set_preview :: proc( + ctxt: context_t, + part_index: c.int, + name: cstring, + p: ^attr_preview_t) -> result_t --- + + attr_get_rational :: proc( + ctxt: const_context_t, + part_index: c.int, + name: cstring, + out: ^attr_rational_t) -> result_t --- + + attr_set_rational :: proc( + ctxt: context_t, + part_index: c.int, + name: cstring, + r: ^attr_rational_t) -> result_t --- + + /** @brief Zero-copy query of string value. + * + * Do not modify the string pointed to by @p out, and do not use + * after the lifetime of the context. + */ + attr_get_string :: proc( + ctxt: const_context_t, + part_index: c.int, + name: cstring, + length: ^i32, + out: ^cstring) -> result_t --- + + attr_set_string :: proc(ctxt: context_t, part_index: c.int, name: cstring, s: cstring) -> result_t --- + + /** @brief Zero-copy query of string data. + * + * Do not free the strings pointed to by the array. + * + * Must provide @p size. + * + * \p out must be a ``^cstring`` array large enough to hold + * the string pointers for the string vector when provided. + */ + attr_get_string_vector :: proc( + ctxt: const_context_t, + part_index: c.int, + name: cstring, + size: ^i32, + out: ^cstring) -> result_t --- + + attr_set_string_vector :: proc( + ctxt: context_t, + part_index: c.int, + name: cstring, + size: i32, + sv: ^cstring) -> result_t --- + + attr_get_tiledesc :: proc( + ctxt: const_context_t, + part_index: c.int, + name: cstring, + out: ^attr_tiledesc_t) -> result_t --- + + attr_set_tiledesc :: proc( + ctxt: context_t, + part_index: c.int, + name: cstring, + td: ^attr_tiledesc_t) -> result_t --- + + attr_get_timecode :: proc( + ctxt: const_context_t, + part_index: c.int, + name: cstring, + out: ^attr_timecode_t) -> result_t --- + + attr_set_timecode :: proc( + ctxt: context_t, + part_index: c.int, + name: cstring, + tc: ^attr_timecode_t) -> result_t --- + + attr_get_v2i :: proc( + ctxt: const_context_t, + part_index: c.int, + name: cstring, + out: ^attr_v2i_t) -> result_t --- + + attr_set_v2i :: proc( + ctxt: context_t, + part_index: c.int, + name: cstring, + v: ^attr_v2i_t) -> result_t --- + + attr_get_v2f :: proc( + ctxt: const_context_t, + part_index: c.int, + name: cstring, + out: ^attr_v2f_t) -> result_t --- + + attr_set_v2f :: proc( + ctxt: context_t, + part_index: c.int, + name: cstring, + v: ^attr_v2f_t) -> result_t --- + + attr_get_v2d :: proc( + ctxt: const_context_t, + part_index: c.int, + name: cstring, + out: ^attr_v2d_t) -> result_t --- + + attr_set_v2d :: proc( + ctxt: context_t, + part_index: c.int, + name: cstring, + v: ^attr_v2d_t) -> result_t --- + + attr_get_v3i :: proc( + ctxt: const_context_t, + part_index: c.int, + name: cstring, + out: ^attr_v3i_t) -> result_t --- + + attr_set_v3i :: proc( + ctxt: context_t, + part_index: c.int, + name: cstring, + v: ^attr_v3i_t) -> result_t --- + + attr_get_v3f :: proc( + ctxt: const_context_t, + part_index: c.int, + name: cstring, + out: ^attr_v3f_t) -> result_t --- + + attr_set_v3f :: proc( + ctxt: context_t, + part_index: c.int, + name: cstring, + v: ^attr_v3f_t) -> result_t --- + + attr_get_v3d :: proc( + ctxt: const_context_t, + part_index: c.int, + name: cstring, + out: ^attr_v3d_t) -> result_t --- + + attr_set_v3d :: proc( + ctxt: context_t, + part_index: c.int, + name: cstring, + v: ^attr_v3d_t) -> result_t --- + + attr_get_user :: proc( + ctxt: const_context_t, + part_index: c.int, + name: cstring, + type: ^cstring, + size: ^i32, + out: ^rawptr) -> result_t --- + + attr_set_user :: proc( + ctxt: context_t, + part_index: c.int, + name: cstring, + type: cstring, + size: i32, + out: rawptr) -> result_t --- + +} \ No newline at end of file diff --git a/vendor/OpenGL/helpers.odin b/vendor/OpenGL/helpers.odin index 0a9cffefa..927129130 100644 --- a/vendor/OpenGL/helpers.odin +++ b/vendor/OpenGL/helpers.odin @@ -5,6 +5,7 @@ package odin_gl import "core:os" import "core:fmt" import "core:strings" +_ :: fmt Shader_Type :: enum i32 { NONE = 0x0000, @@ -188,7 +189,7 @@ load_shaders_source :: proc(vs_source, fs_source: string, binary_retrievable := load_shaders :: proc{load_shaders_file} -when ODIN_OS == "windows" { +when ODIN_OS == .Windows { update_shader_if_changed :: proc( vertex_name, fragment_name: string, program: u32, diff --git a/vendor/OpenGL/impl.odin b/vendor/OpenGL/impl.odin index 966f8467d..e9adda4bd 100644 --- a/vendor/OpenGL/impl.odin +++ b/vendor/OpenGL/impl.odin @@ -199,7 +199,7 @@ load_1_1 :: proc(set_proc_address: Set_Proc_Address_Type) { // VERSION_1_2 impl_DrawRangeElements: proc "c" (mode: u32, start: u32, end: u32, count: i32, type: u32, indices: rawptr) -impl_TexImage3D: proc "c" (target: u32, level: i32, internalformat: i32, width: i32, height: i32, depth: i32, border: i32, format: u32, type: u32, pixels: rawptr) +impl_TexImage3D: proc "c" (target: u32, level: i32, internalformat: i32, width: i32, height: i32, depth: i32, border: i32, format: u32, type: u32, data: rawptr) impl_TexSubImage3D: proc "c" (target: u32, level: i32, xoffset: i32, yoffset: i32, zoffset: i32, width: i32, height: i32, depth: i32, format: u32, type: u32, pixels: rawptr) impl_CopyTexSubImage3D: proc "c" (target: u32, level: i32, xoffset: i32, yoffset: i32, zoffset: i32, x: i32, y: i32, width: i32, height: i32) @@ -947,6 +947,13 @@ impl_DrawTransformFeedbackStream: proc "c" (mode: u32, id: u32, stream: u32) impl_BeginQueryIndexed: proc "c" (target: u32, index: u32, id: u32) impl_EndQueryIndexed: proc "c" (target: u32, index: u32) impl_GetQueryIndexediv: proc "c" (target: u32, index: u32, pname: u32, params: [^]i32) +impl_GetTextureHandleARB: proc "c" (texture: u32) -> u64 +impl_GetTextureSamplerHandleARB: proc "c" (texture, sampler: u32) -> u64 +impl_GetImageHandleARB: proc "c" (texture: u32, level: i32, layered: bool, layer: i32, format: u32) -> u64 +impl_MakeTextureHandleResidentARB: proc "c" (handle: u64) +impl_MakeImageHandleResidentARB: proc "c" (handle: u64, access: u32) +impl_MakeTextureHandleNonResidentARB:proc "c" (handle: u64) +impl_MakeImageHandleNonResidentARB: proc "c" (handle: u64) load_4_0 :: proc(set_proc_address: Set_Proc_Address_Type) { set_proc_address(&impl_MinSampleShading, "glMinSampleShading") @@ -995,6 +1002,42 @@ load_4_0 :: proc(set_proc_address: Set_Proc_Address_Type) { set_proc_address(&impl_BeginQueryIndexed, "glBeginQueryIndexed") set_proc_address(&impl_EndQueryIndexed, "glEndQueryIndexed") set_proc_address(&impl_GetQueryIndexediv, "glGetQueryIndexediv") + + // Load ARB (architecture review board, vendor specific) extensions that might be available + set_proc_address(&impl_GetTextureHandleARB, "glGetTextureHandleARB") + if impl_GetTextureHandleARB == nil { + set_proc_address(&impl_GetTextureHandleARB, "glGetTextureHandleNV") + } + + set_proc_address(&impl_GetTextureSamplerHandleARB, "glGetTextureSamplerHandleARB") + if impl_GetTextureSamplerHandleARB == nil { + set_proc_address(&impl_GetTextureSamplerHandleARB, "glGetTextureSamplerHandleNV") + } + + set_proc_address(&impl_GetImageHandleARB, "glGetImageHandleARB") + if impl_GetImageHandleARB == nil { + set_proc_address(&impl_GetImageHandleARB, "glGetImageHandleNV") + } + + set_proc_address(&impl_MakeTextureHandleResidentARB, "glMakeTextureHandleResidentARB") + if impl_MakeTextureHandleResidentARB == nil { + set_proc_address(&impl_MakeTextureHandleResidentARB, "glMakeTextureHandleResidentNV") + } + + set_proc_address(&impl_MakeImageHandleResidentARB, "glMakeImageHandleResidentARB") + if impl_MakeImageHandleResidentARB == nil { + set_proc_address(&impl_MakeImageHandleResidentARB, "glMakeImageHandleResidentNV") + } + + set_proc_address(&impl_MakeTextureHandleNonResidentARB, "glMakeTextureHandleNonResidentARB") + if impl_MakeTextureHandleNonResidentARB == nil { + set_proc_address(&impl_MakeTextureHandleNonResidentARB, "glMakeTextureHandleNonResidentNV") + } + + set_proc_address(&impl_MakeImageHandleNonResidentARB, "glMakeImageHandleNonResidentARB") + if impl_MakeImageHandleNonResidentARB == nil { + set_proc_address(&impl_MakeImageHandleNonResidentARB, "glMakeImageHandleNonResidentNV") + } } @@ -1250,14 +1293,14 @@ impl_VertexAttribLFormat: proc "c" (attribindex: u32, size: i32, typ impl_VertexAttribBinding: proc "c" (attribindex: u32, bindingindex: u32) impl_VertexBindingDivisor: proc "c" (bindingindex: u32, divisor: u32) impl_DebugMessageControl: proc "c" (source: u32, type: u32, severity: u32, count: i32, ids: [^]u32, enabled: bool) -impl_DebugMessageInsert: proc "c" (source: u32, type: u32, id: u32, severity: u32, length: i32, buf: [^]u8) +impl_DebugMessageInsert: proc "c" (source: u32, type: u32, id: u32, severity: u32, length: i32, message: cstring) impl_DebugMessageCallback: proc "c" (callback: debug_proc_t, userParam: rawptr) impl_GetDebugMessageLog: proc "c" (count: u32, bufSize: i32, sources: [^]u32, types: [^]u32, ids: [^]u32, severities: [^]u32, lengths: [^]i32, messageLog: [^]u8) -> u32 impl_PushDebugGroup: proc "c" (source: u32, id: u32, length: i32, message: cstring) impl_PopDebugGroup: proc "c" () -impl_ObjectLabel: proc "c" (identifier: u32, name: u32, length: i32, label: [^]u8) +impl_ObjectLabel: proc "c" (identifier: u32, name: u32, length: i32, label: cstring) impl_GetObjectLabel: proc "c" (identifier: u32, name: u32, bufSize: i32, length: ^i32, label: [^]u8) -impl_ObjectPtrLabel: proc "c" (ptr: rawptr, length: i32, label: [^]u8) +impl_ObjectPtrLabel: proc "c" (ptr: rawptr, length: i32, label: cstring) impl_GetObjectPtrLabel: proc "c" (ptr: rawptr, bufSize: i32, length: ^i32, label: [^]u8) load_4_3 :: proc(set_proc_address: Set_Proc_Address_Type) { @@ -1594,3 +1637,4 @@ load_4_6 :: proc(set_proc_address: Set_Proc_Address_Type) { set_proc_address(&impl_MultiDrawElementsIndirectCount, "glMultiDrawElementsIndirectCount") set_proc_address(&impl_PolygonOffsetClamp, "glPolygonOffsetClamp") } + diff --git a/vendor/OpenGL/wrappers.odin b/vendor/OpenGL/wrappers.odin index 10a601eec..b62ed216b 100644 --- a/vendor/OpenGL/wrappers.odin +++ b/vendor/OpenGL/wrappers.odin @@ -70,7 +70,7 @@ when !ODIN_DEBUG { // VERSION_1_2 DrawRangeElements :: proc "c" (mode, start, end: u32, count: i32, type: u32, indices: rawptr) { impl_DrawRangeElements(mode, start, end, count, type, indices) } - TexImage3D :: proc "c" (target: u32, level, internalformat, width, height, depth, border: i32, format, type: u32, pixels: rawptr) { impl_TexImage3D(target, level, internalformat, width, height, depth, border, format, type, pixels) } + TexImage3D :: proc "c" (target: u32, level, internalformat, width, height, depth, border: i32, format, type: u32, data: rawptr) { impl_TexImage3D(target, level, internalformat, width, height, depth, border, format, type, data) } TexSubImage3D :: proc "c" (target: u32, level, xoffset, yoffset, zoffset, width, height, depth: i32, format, type: u32, pixels: rawptr) { impl_TexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels) } CopyTexSubImage3D :: proc "c" (target: u32, level, xoffset, yoffset, zoffset, x, y, width, height: i32) { impl_CopyTexSubImage3D(target, level, xoffset, yoffset, zoffset, x, y, width, height) } @@ -449,6 +449,20 @@ when !ODIN_DEBUG { BeginQueryIndexed :: proc "c" (target: u32, index: u32, id: u32) { impl_BeginQueryIndexed(target, index, id) } EndQueryIndexed :: proc "c" (target: u32, index: u32) { impl_EndQueryIndexed(target, index) } GetQueryIndexediv :: proc "c" (target: u32, index: u32, pname: u32, params: [^]i32) { impl_GetQueryIndexediv(target, index, pname, params) } + GetTextureHandleARB :: proc "c" (texture: u32) -> u64 + { return impl_GetTextureHandleARB(texture) } + GetTextureSamplerHandleARB :: proc "c" (texture, sampler: u32) -> u64 + { return impl_GetTextureSamplerHandleARB(texture, sampler) } + GetImageHandleARB :: proc "c" (texture: u32, level: i32, layered: bool, layer: i32, format: u32) -> u64 + { return impl_GetImageHandleARB(texture, level, layered, layer, format) } + MakeTextureHandleResidentARB :: proc "c" (handle: u64) + { impl_MakeTextureHandleResidentARB(handle) } + MakeImageHandleResidentARB :: proc "c" (handle: u64, access: u32) + { impl_MakeImageHandleResidentARB(handle, access) } + MakeTextureHandleNonResidentARB:: proc "c" (handle: u64) + { impl_MakeTextureHandleNonResidentARB(handle) } + MakeImageHandleNonResidentARB :: proc "c" (handle: u64) + { impl_MakeImageHandleNonResidentARB(handle) } // VERSION_4_1 ReleaseShaderCompiler :: proc "c" () { impl_ReleaseShaderCompiler() } @@ -589,14 +603,14 @@ when !ODIN_DEBUG { VertexAttribBinding :: proc "c" (attribindex: u32, bindingindex: u32) { impl_VertexAttribBinding(attribindex, bindingindex) } VertexBindingDivisor :: proc "c" (bindingindex: u32, divisor: u32) { impl_VertexBindingDivisor(bindingindex, divisor) } DebugMessageControl :: proc "c" (source: u32, type: u32, severity: u32, count: i32, ids: [^]u32, enabled: bool) { impl_DebugMessageControl(source, type, severity, count, ids, enabled) } - DebugMessageInsert :: proc "c" (source: u32, type: u32, id: u32, severity: u32, length: i32, buf: ^u8) { impl_DebugMessageInsert(source, type, id, severity, length, buf) } + DebugMessageInsert :: proc "c" (source: u32, type: u32, id: u32, severity: u32, length: i32, message: cstring) { impl_DebugMessageInsert(source, type, id, severity, length, message) } DebugMessageCallback :: proc "c" (callback: debug_proc_t, userParam: rawptr) { impl_DebugMessageCallback(callback, userParam) } GetDebugMessageLog :: proc "c" (count: u32, bufSize: i32, sources: [^]u32, types: [^]u32, ids: [^]u32, severities: [^]u32, lengths: [^]i32, messageLog: [^]u8) -> u32 { ret := impl_GetDebugMessageLog(count, bufSize, sources, types, ids, severities, lengths, messageLog); return ret } PushDebugGroup :: proc "c" (source: u32, id: u32, length: i32, message: cstring) { impl_PushDebugGroup(source, id, length, message) } PopDebugGroup :: proc "c" () { impl_PopDebugGroup() } - ObjectLabel :: proc "c" (identifier: u32, name: u32, length: i32, label: [^]u8) { impl_ObjectLabel(identifier, name, length, label) } + ObjectLabel :: proc "c" (identifier: u32, name: u32, length: i32, label: cstring) { impl_ObjectLabel(identifier, name, length, label) } GetObjectLabel :: proc "c" (identifier: u32, name: u32, bufSize: i32, length: ^i32, label: [^]u8) { impl_GetObjectLabel(identifier, name, bufSize, length, label) } - ObjectPtrLabel :: proc "c" (ptr: rawptr, length: i32, label: [^]u8) { impl_ObjectPtrLabel(ptr, length, label) } + ObjectPtrLabel :: proc "c" (ptr: rawptr, length: i32, label: cstring) { impl_ObjectPtrLabel(ptr, length, label) } GetObjectPtrLabel :: proc "c" (ptr: rawptr, bufSize: i32, length: ^i32, label: [^]u8) { impl_GetObjectPtrLabel(ptr, bufSize, length, label) } // VERSION_4_4 @@ -1249,6 +1263,22 @@ when !ODIN_DEBUG { BeginQueryIndexed :: proc "c" (target: u32, index: u32, id: u32, loc := #caller_location) { impl_BeginQueryIndexed(target, index, id); debug_helper(loc, 0, target, index, id) } EndQueryIndexed :: proc "c" (target: u32, index: u32, loc := #caller_location) { impl_EndQueryIndexed(target, index); debug_helper(loc, 0, target, index) } GetQueryIndexediv :: proc "c" (target: u32, index: u32, pname: u32, params: [^]i32, loc := #caller_location) { impl_GetQueryIndexediv(target, index, pname, params); debug_helper(loc, 0, target, index, pname, params) } + GetTextureHandleARB :: proc "c" (target: u32, loc := #caller_location) -> u64 + { ret := impl_GetTextureHandleARB(target); debug_helper(loc, 0, target); return ret } + GetTextureSamplerHandleARB :: proc "c" (texture, sampler: u32, loc := #caller_location) -> u64 + { ret := impl_GetTextureSamplerHandleARB(texture, sampler); debug_helper(loc, 0, texture, sampler); return ret } + GetImageHandleARB :: proc "c" (texture: u32, level: i32, layered: bool, layer: i32, format: u32, loc := #caller_location) -> u64 + { ret := impl_GetImageHandleARB(texture, level, layered, layer, format); debug_helper(loc, 0, texture, level, layered, layer, format); return ret } + MakeTextureHandleResidentARB :: proc "c" (handle: u64, loc := #caller_location) + { impl_MakeTextureHandleResidentARB(handle); debug_helper(loc, 0, handle) } + MakeImageHandleResidentARB :: proc "c" (handle: u64, access: u32, loc := #caller_location) + { impl_MakeImageHandleResidentARB(handle, access); debug_helper(loc, 0, handle, access) } + MakeTextureHandleNonResidentARB:: proc "c" (handle: u64, loc := #caller_location) + { impl_MakeTextureHandleNonResidentARB(handle); debug_helper(loc, 0, handle) } + MakeImageHandleNonResidentARB :: proc "c" (handle: u64, loc := #caller_location) + { impl_MakeImageHandleNonResidentARB(handle); debug_helper(loc, 0, handle) } + + // VERSION_4_1 ReleaseShaderCompiler :: proc "c" (loc := #caller_location) { impl_ReleaseShaderCompiler(); debug_helper(loc, 0) } @@ -1389,14 +1419,14 @@ when !ODIN_DEBUG { VertexAttribBinding :: proc "c" (attribindex: u32, bindingindex: u32, loc := #caller_location) { impl_VertexAttribBinding(attribindex, bindingindex); debug_helper(loc, 0, attribindex, bindingindex) } VertexBindingDivisor :: proc "c" (bindingindex: u32, divisor: u32, loc := #caller_location) { impl_VertexBindingDivisor(bindingindex, divisor); debug_helper(loc, 0, bindingindex, divisor) } DebugMessageControl :: proc "c" (source: u32, type: u32, severity: u32, count: i32, ids: [^]u32, enabled: bool, loc := #caller_location) { impl_DebugMessageControl(source, type, severity, count, ids, enabled); debug_helper(loc, 0, source, type, severity, count, ids, enabled) } - DebugMessageInsert :: proc "c" (source: u32, type: u32, id: u32, severity: u32, length: i32, buf: ^u8, loc := #caller_location) { impl_DebugMessageInsert(source, type, id, severity, length, buf); debug_helper(loc, 0, source, type, id, severity, length, buf) } + DebugMessageInsert :: proc "c" (source: u32, type: u32, id: u32, severity: u32, length: i32, message: cstring, loc := #caller_location) { impl_DebugMessageInsert(source, type, id, severity, length, message); debug_helper(loc, 0, source, type, id, severity, length, message) } DebugMessageCallback :: proc "c" (callback: debug_proc_t, userParam: rawptr, loc := #caller_location) { impl_DebugMessageCallback(callback, userParam); debug_helper(loc, 0, callback, userParam) } GetDebugMessageLog :: proc "c" (count: u32, bufSize: i32, sources: [^]u32, types: [^]u32, ids: [^]u32, severities: [^]u32, lengths: [^]i32, messageLog: [^]u8, loc := #caller_location) -> u32 { ret := impl_GetDebugMessageLog(count, bufSize, sources, types, ids, severities, lengths, messageLog); debug_helper(loc, 1, ret, count, bufSize, sources, types, ids, severities, lengths, messageLog); return ret } PushDebugGroup :: proc "c" (source: u32, id: u32, length: i32, message: cstring, loc := #caller_location) { impl_PushDebugGroup(source, id, length, message); debug_helper(loc, 0, source, id, length, message) } PopDebugGroup :: proc "c" (loc := #caller_location) { impl_PopDebugGroup(); debug_helper(loc, 0) } - ObjectLabel :: proc "c" (identifier: u32, name: u32, length: i32, label: [^]u8, loc := #caller_location) { impl_ObjectLabel(identifier, name, length, label); debug_helper(loc, 0, identifier, name, length, label) } + ObjectLabel :: proc "c" (identifier: u32, name: u32, length: i32, label: cstring, loc := #caller_location) { impl_ObjectLabel(identifier, name, length, label); debug_helper(loc, 0, identifier, name, length, label) } GetObjectLabel :: proc "c" (identifier: u32, name: u32, bufSize: i32, length: ^i32, label: [^]u8, loc := #caller_location) { impl_GetObjectLabel(identifier, name, bufSize, length, label); debug_helper(loc, 0, identifier, name, bufSize, length, label) } - ObjectPtrLabel :: proc "c" (ptr: rawptr, length: i32, label: [^]u8, loc := #caller_location) { impl_ObjectPtrLabel(ptr, length, label); debug_helper(loc, 0, ptr, length, label) } + ObjectPtrLabel :: proc "c" (ptr: rawptr, length: i32, label: cstring, loc := #caller_location) { impl_ObjectPtrLabel(ptr, length, label); debug_helper(loc, 0, ptr, length, label) } GetObjectPtrLabel :: proc "c" (ptr: rawptr, bufSize: i32, length: ^i32, label: [^]u8, loc := #caller_location) { impl_GetObjectPtrLabel(ptr, bufSize, length, label); debug_helper(loc, 0, ptr, bufSize, length, label) } // VERSION_4_4 diff --git a/vendor/README.md b/vendor/README.md index 14e91ca89..628ea2727 100644 --- a/vendor/README.md +++ b/vendor/README.md @@ -119,6 +119,14 @@ See also LICENSE.txt in the `portmidi` directory itself. See also LICENSE in the `ENet` directory itself. +## GGPO + +[GGPO](https://www.ggpo.net/) GGPO Rollback Networking SDK. + +Zero-input latency networking library for peer-to-peer games. + +See also LICENSE in the `GGPO` directory itself. + ## Botan [Botan](https://botan.randombit.net/) Crypto and TLS library. diff --git a/vendor/botan/bindings/botan.odin b/vendor/botan/bindings/botan.odin index d1d88cda0..a12706e95 100644 --- a/vendor/botan/bindings/botan.odin +++ b/vendor/botan/bindings/botan.odin @@ -99,6 +99,10 @@ MAC_HMAC_SHA_384 :: "HMAC(SHA-384)" MAC_HMAC_SHA_512 :: "HMAC(SHA-512)" MAC_HMAC_MD5 :: "HMAC(MD5)" +MAC_SIPHASH_1_3 :: "SipHash(1,3)" +MAC_SIPHASH_2_4 :: "SipHash(2,4)" +MAC_SIPHASH_4_8 :: "SipHash(4,8)" + hash_struct :: struct{} hash_t :: ^hash_struct rng_struct :: struct{} @@ -136,11 +140,9 @@ totp_t :: ^totp_struct fpe_struct :: struct{} fpe_t :: ^fpe_struct -when ODIN_OS == "windows" { +when ODIN_OS == .Windows { foreign import botan_lib "botan.lib" -} else when ODIN_OS == "linux" { - foreign import botan_lib "system:botan-2" -} else when ODIN_OS == "darwin" { +} else { foreign import botan_lib "system:botan-2" } @@ -467,4 +469,4 @@ foreign botan_lib { fpe_destroy :: proc(fpe: fpe_t) -> c.int --- fpe_encrypt :: proc(fpe: fpe_t, x: mp_t, tweak: ^c.char, tweak_len: c.size_t) -> c.int --- fpe_decrypt :: proc(fpe: fpe_t, x: mp_t, tweak: ^c.char, tweak_len: c.size_t) -> c.int --- -} \ No newline at end of file +} diff --git a/vendor/botan/blake2b/blake2b.odin b/vendor/botan/blake2b/blake2b.odin index 226502e83..67238ec74 100644 --- a/vendor/botan/blake2b/blake2b.odin +++ b/vendor/botan/blake2b/blake2b.odin @@ -44,7 +44,7 @@ hash_bytes :: proc "contextless" (data: []byte) -> [DIGEST_SIZE]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer(transmute([]byte)(data), hash); + hash_bytes_to_buffer(transmute([]byte)(data), hash) } // hash_bytes_to_buffer will hash the given input and write the diff --git a/vendor/botan/gost/gost.odin b/vendor/botan/gost/gost.odin index 9f081f9cb..6bf1c5b97 100644 --- a/vendor/botan/gost/gost.odin +++ b/vendor/botan/gost/gost.odin @@ -44,7 +44,7 @@ hash_bytes :: proc "contextless" (data: []byte) -> [DIGEST_SIZE]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer(transmute([]byte)(data), hash); + hash_bytes_to_buffer(transmute([]byte)(data), hash) } // hash_bytes_to_buffer will hash the given input and write the diff --git a/vendor/botan/keccak/keccak.odin b/vendor/botan/keccak/keccak.odin index 3316de017..28e7374ba 100644 --- a/vendor/botan/keccak/keccak.odin +++ b/vendor/botan/keccak/keccak.odin @@ -44,7 +44,7 @@ hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_512 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_512(transmute([]byte)(data), hash); + hash_bytes_to_buffer_512(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_512 will hash the given input and write the diff --git a/vendor/botan/md4/md4.odin b/vendor/botan/md4/md4.odin index c8a1ad903..174676a82 100644 --- a/vendor/botan/md4/md4.odin +++ b/vendor/botan/md4/md4.odin @@ -44,7 +44,7 @@ hash_bytes :: proc "contextless" (data: []byte) -> [DIGEST_SIZE]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer(transmute([]byte)(data), hash); + hash_bytes_to_buffer(transmute([]byte)(data), hash) } // hash_bytes_to_buffer will hash the given input and write the diff --git a/vendor/botan/md5/md5.odin b/vendor/botan/md5/md5.odin index 203f2d092..01e099062 100644 --- a/vendor/botan/md5/md5.odin +++ b/vendor/botan/md5/md5.odin @@ -44,7 +44,7 @@ hash_bytes :: proc "contextless" (data: []byte) -> [DIGEST_SIZE]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer(transmute([]byte)(data), hash); + hash_bytes_to_buffer(transmute([]byte)(data), hash) } // hash_bytes_to_buffer will hash the given input and write the diff --git a/vendor/botan/ripemd/ripemd.odin b/vendor/botan/ripemd/ripemd.odin index 0a8195a96..230e4c0d9 100644 --- a/vendor/botan/ripemd/ripemd.odin +++ b/vendor/botan/ripemd/ripemd.odin @@ -44,7 +44,7 @@ hash_bytes_160 :: proc(data: []byte) -> [DIGEST_SIZE_160]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_160 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_160(transmute([]byte)(data), hash); + hash_bytes_to_buffer_160(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_160 will hash the given input and write the diff --git a/vendor/botan/sha1/sha1.odin b/vendor/botan/sha1/sha1.odin index 005b01821..1f74c8fc2 100644 --- a/vendor/botan/sha1/sha1.odin +++ b/vendor/botan/sha1/sha1.odin @@ -44,7 +44,7 @@ hash_bytes :: proc "contextless" (data: []byte) -> [DIGEST_SIZE]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer(transmute([]byte)(data), hash); + hash_bytes_to_buffer(transmute([]byte)(data), hash) } // hash_bytes_to_buffer will hash the given input and write the diff --git a/vendor/botan/sha2/sha2.odin b/vendor/botan/sha2/sha2.odin index f5d6921a8..4c201cc26 100644 --- a/vendor/botan/sha2/sha2.odin +++ b/vendor/botan/sha2/sha2.odin @@ -47,7 +47,7 @@ hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_224 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_224(transmute([]byte)(data), hash); + hash_bytes_to_buffer_224(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_224 will hash the given input and write the @@ -126,7 +126,7 @@ hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_256 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_256(transmute([]byte)(data), hash); + hash_bytes_to_buffer_256(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_256 will hash the given input and write the @@ -205,7 +205,7 @@ hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_384 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_384(transmute([]byte)(data), hash); + hash_bytes_to_buffer_384(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_384 will hash the given input and write the @@ -284,7 +284,7 @@ hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_512 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_512(transmute([]byte)(data), hash); + hash_bytes_to_buffer_512(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_512 will hash the given input and write the diff --git a/vendor/botan/sha3/sha3.odin b/vendor/botan/sha3/sha3.odin index cf9fa5b2b..4c1b87dda 100644 --- a/vendor/botan/sha3/sha3.odin +++ b/vendor/botan/sha3/sha3.odin @@ -47,7 +47,7 @@ hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_224 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_224(transmute([]byte)(data), hash); + hash_bytes_to_buffer_224(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_224 will hash the given input and write the @@ -126,7 +126,7 @@ hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_256 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_256(transmute([]byte)(data), hash); + hash_bytes_to_buffer_256(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_256 will hash the given input and write the @@ -205,7 +205,7 @@ hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_384 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_384(transmute([]byte)(data), hash); + hash_bytes_to_buffer_384(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_384 will hash the given input and write the @@ -284,7 +284,7 @@ hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_512 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_512(transmute([]byte)(data), hash); + hash_bytes_to_buffer_512(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_512 will hash the given input and write the diff --git a/vendor/botan/shake/shake.odin b/vendor/botan/shake/shake.odin index ac8432f64..f1023b90e 100644 --- a/vendor/botan/shake/shake.odin +++ b/vendor/botan/shake/shake.odin @@ -45,7 +45,7 @@ hash_bytes_128 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_128 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_128(transmute([]byte)(data), hash); + hash_bytes_to_buffer_128(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_128 will hash the given input and write the @@ -124,7 +124,7 @@ hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_256 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_256(transmute([]byte)(data), hash); + hash_bytes_to_buffer_256(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_256 will hash the given input and write the diff --git a/vendor/botan/siphash/siphash.odin b/vendor/botan/siphash/siphash.odin new file mode 100644 index 000000000..c2b7b64f4 --- /dev/null +++ b/vendor/botan/siphash/siphash.odin @@ -0,0 +1,253 @@ +package siphash + +/* + Copyright 2022 zhibog + Made available under the BSD-3 license. + + List of contributors: + zhibog: Initial implementation. + + Interface for the SipHash hashing algorithm. + The hash will be computed via bindings to the Botan crypto library + + Use the specific procedures for a certain setup. The generic procdedures will default to Siphash 2-4 +*/ + +import "core:crypto" +import "core:crypto/util" + +import botan "../bindings" + +KEY_SIZE :: 16 +DIGEST_SIZE :: 8 + +// sum_string_1_3 will hash the given message with the key and return +// the computed hash as a u64 +sum_string_1_3 :: proc(msg, key: string) -> u64 { + return sum_bytes_1_3(transmute([]byte)(msg), transmute([]byte)(key)) +} + +// sum_bytes_1_3 will hash the given message with the key and return +// the computed hash as a u64 +sum_bytes_1_3 :: proc (msg, key: []byte) -> u64 { + dst: [8]byte + ctx: botan.mac_t + init(&ctx, key[:], 1, 3) + update(&ctx, msg[:]) + final(&ctx, dst[:]) + return util.U64_LE(dst[:]) +} + +// sum_string_to_buffer_1_3 will hash the given message with the key and write +// the computed hash into the provided destination buffer +sum_string_to_buffer_1_3 :: proc(msg, key: string, dst: []byte) { + sum_bytes_to_buffer_1_3(transmute([]byte)(msg), transmute([]byte)(key), dst) +} + +// sum_bytes_to_buffer_1_3 will hash the given message with the key and write +// the computed hash into the provided destination buffer +sum_bytes_to_buffer_1_3 :: proc(msg, key, dst: []byte) { + assert(len(dst) >= DIGEST_SIZE, "vendor/botan: Destination buffer needs to be at least of size 8") + ctx: botan.mac_t + init(&ctx, key[:], 1, 3) + update(&ctx, msg[:]) + final(&ctx, dst[:]) +} + +sum_1_3 :: proc { + sum_string_1_3, + sum_bytes_1_3, + sum_string_to_buffer_1_3, + sum_bytes_to_buffer_1_3, +} + +// verify_u64_1_3 will check if the supplied tag matches with the output you +// will get from the provided message and key +verify_u64_1_3 :: proc (tag: u64 msg, key: []byte) -> bool { + return sum_bytes_1_3(msg, key) == tag +} + +// verify_bytes_1_3 will check if the supplied tag matches with the output you +// will get from the provided message and key +verify_bytes_1_3 :: proc (tag, msg, key: []byte) -> bool { + derived_tag: [8]byte + sum_bytes_to_buffer_1_3(msg, key, derived_tag[:]) + return crypto.compare_constant_time(derived_tag[:], tag) == 1 +} + +verify_1_3 :: proc { + verify_bytes_1_3, + verify_u64_1_3, +} + +// sum_string_2_4 will hash the given message with the key and return +// the computed hash as a u64 +sum_string_2_4 :: proc(msg, key: string) -> u64 { + return sum_bytes_2_4(transmute([]byte)(msg), transmute([]byte)(key)) +} + +// sum_bytes_2_4 will hash the given message with the key and return +// the computed hash as a u64 +sum_bytes_2_4 :: proc (msg, key: []byte) -> u64 { + dst: [8]byte + ctx: botan.mac_t + init(&ctx, key[:]) + update(&ctx, msg[:]) + final(&ctx, dst[:]) + return util.U64_LE(dst[:]) +} + +// sum_string_to_buffer_2_4 will hash the given message with the key and write +// the computed hash into the provided destination buffer +sum_string_to_buffer_2_4 :: proc(msg, key: string, dst: []byte) { + sum_bytes_to_buffer_2_4(transmute([]byte)(msg), transmute([]byte)(key), dst) +} + +// sum_bytes_to_buffer_2_4 will hash the given message with the key and write +// the computed hash into the provided destination buffer +sum_bytes_to_buffer_2_4 :: proc(msg, key, dst: []byte) { + assert(len(dst) >= DIGEST_SIZE, "vendor/botan: Destination buffer needs to be at least of size 8") + ctx: botan.mac_t + init(&ctx, key[:]) + update(&ctx, msg[:]) + final(&ctx, dst[:]) +} + +sum_2_4 :: proc { + sum_string_2_4, + sum_bytes_2_4, + sum_string_to_buffer_2_4, + sum_bytes_to_buffer_2_4, +} + +sum_string :: sum_string_2_4 +sum_bytes :: sum_bytes_2_4 +sum_string_to_buffer :: sum_string_to_buffer_2_4 +sum_bytes_to_buffer :: sum_bytes_to_buffer_2_4 +sum :: proc { + sum_string, + sum_bytes, + sum_string_to_buffer, + sum_bytes_to_buffer, +} + + +// verify_u64_2_4 will check if the supplied tag matches with the output you +// will get from the provided message and key +verify_u64_2_4 :: proc (tag: u64 msg, key: []byte) -> bool { + return sum_bytes_2_4(msg, key) == tag +} + +// verify_bytes_2_4 will check if the supplied tag matches with the output you +// will get from the provided message and key +verify_bytes_2_4 :: proc (tag, msg, key: []byte) -> bool { + derived_tag: [8]byte + sum_bytes_to_buffer_2_4(msg, key, derived_tag[:]) + return crypto.compare_constant_time(derived_tag[:], tag) == 1 +} + +verify_2_4 :: proc { + verify_bytes_2_4, + verify_u64_2_4, +} + +verify_bytes :: verify_bytes_2_4 +verify_u64 :: verify_u64_2_4 +verify :: proc { + verify_bytes, + verify_u64, +} + +// sum_string_4_8 will hash the given message with the key and return +// the computed hash as a u64 +sum_string_4_8 :: proc(msg, key: string) -> u64 { + return sum_bytes_4_8(transmute([]byte)(msg), transmute([]byte)(key)) +} + +// sum_bytes_4_8 will hash the given message with the key and return +// the computed hash as a u64 +sum_bytes_4_8 :: proc (msg, key: []byte) -> u64 { + dst: [8]byte + ctx: botan.mac_t + init(&ctx, key[:], 4, 8) + update(&ctx, msg[:]) + final(&ctx, dst[:]) + return util.U64_LE(dst[:]) +} + +// sum_string_to_buffer_4_8 will hash the given message with the key and write +// the computed hash into the provided destination buffer +sum_string_to_buffer_4_8 :: proc(msg, key: string, dst: []byte) { + sum_bytes_to_buffer_2_4(transmute([]byte)(msg), transmute([]byte)(key), dst) +} + +// sum_bytes_to_buffer_4_8 will hash the given message with the key and write +// the computed hash into the provided destination buffer +sum_bytes_to_buffer_4_8 :: proc(msg, key, dst: []byte) { + assert(len(dst) >= DIGEST_SIZE, "vendor/botan: Destination buffer needs to be at least of size 8") + ctx: botan.mac_t + init(&ctx, key[:], 4, 8) + update(&ctx, msg[:]) + final(&ctx, dst[:]) +} + +sum_4_8 :: proc { + sum_string_4_8, + sum_bytes_4_8, + sum_string_to_buffer_4_8, + sum_bytes_to_buffer_4_8, +} + +// verify_u64_4_8 will check if the supplied tag matches with the output you +// will get from the provided message and key +verify_u64_4_8 :: proc (tag: u64 msg, key: []byte) -> bool { + return sum_bytes_4_8(msg, key) == tag +} + +// verify_bytes_4_8 will check if the supplied tag matches with the output you +// will get from the provided message and key +verify_bytes_4_8 :: proc (tag, msg, key: []byte) -> bool { + derived_tag: [8]byte + sum_bytes_to_buffer_4_8(msg, key, derived_tag[:]) + return crypto.compare_constant_time(derived_tag[:], tag) == 1 +} + +verify_4_8 :: proc { + verify_bytes_4_8, + verify_u64_4_8, +} + +/* + Low level API +*/ + +Context :: botan.mac_t + +init :: proc(ctx: ^botan.mac_t, key: []byte, c_rounds := 2, d_rounds := 4) { + assert(len(key) == KEY_SIZE, "vendor/botan: Invalid key size, want 16") + is_valid_setting := (c_rounds == 1 && d_rounds == 3) || + (c_rounds == 2 && d_rounds == 4) || + (c_rounds == 4 && d_rounds == 8) + assert(is_valid_setting, "vendor/botan: Incorrect rounds set up. Valid pairs are (1,3), (2,4) and (4,8)") + if c_rounds == 1 && d_rounds == 3 { + botan.mac_init(ctx, botan.MAC_SIPHASH_1_3, 0) + } else if c_rounds == 2 && d_rounds == 4 { + botan.mac_init(ctx, botan.MAC_SIPHASH_2_4, 0) + } else if c_rounds == 4 && d_rounds == 8 { + botan.mac_init(ctx, botan.MAC_SIPHASH_4_8, 0) + } + botan.mac_set_key(ctx^, len(key) == 0 ? nil : &key[0], uint(len(key))) +} + +update :: proc "contextless" (ctx: ^botan.mac_t, data: []byte) { + botan.mac_update(ctx^, len(data) == 0 ? nil : &data[0], uint(len(data))) +} + +final :: proc(ctx: ^botan.mac_t, dst: []byte) { + botan.mac_final(ctx^, &dst[0]) + reset(ctx) +} + +reset :: proc(ctx: ^botan.mac_t) { + botan.mac_destroy(ctx^) +} \ No newline at end of file diff --git a/vendor/botan/skein512/skein512.odin b/vendor/botan/skein512/skein512.odin index 490eeba03..4fed07853 100644 --- a/vendor/botan/skein512/skein512.odin +++ b/vendor/botan/skein512/skein512.odin @@ -47,7 +47,7 @@ hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_256 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_256(transmute([]byte)(data), hash); + hash_bytes_to_buffer_256(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_256 will hash the given input and write the @@ -126,7 +126,7 @@ hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_512 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_512(transmute([]byte)(data), hash); + hash_bytes_to_buffer_512(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_512 will hash the given input and write the @@ -205,7 +205,7 @@ hash_bytes_slice :: proc(data: []byte, bit_size: int, allocator := context.alloc // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_slice :: proc(data: string, hash: []byte, bit_size: int, allocator := context.allocator) { - hash_bytes_to_buffer_slice(transmute([]byte)(data), hash, bit_size, allocator); + hash_bytes_to_buffer_slice(transmute([]byte)(data), hash, bit_size, allocator) } // hash_bytes_to_buffer_slice will hash the given input and write the diff --git a/vendor/botan/sm3/sm3.odin b/vendor/botan/sm3/sm3.odin index 7eb3f1f8d..75cf40679 100644 --- a/vendor/botan/sm3/sm3.odin +++ b/vendor/botan/sm3/sm3.odin @@ -44,7 +44,7 @@ hash_bytes :: proc "contextless" (data: []byte) -> [DIGEST_SIZE]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer(transmute([]byte)(data), hash); + hash_bytes_to_buffer(transmute([]byte)(data), hash) } // hash_bytes_to_buffer will hash the given input and write the diff --git a/vendor/botan/streebog/streebog.odin b/vendor/botan/streebog/streebog.odin index cbf2047ed..20b4e6adb 100644 --- a/vendor/botan/streebog/streebog.odin +++ b/vendor/botan/streebog/streebog.odin @@ -45,7 +45,7 @@ hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_256 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_256(transmute([]byte)(data), hash); + hash_bytes_to_buffer_256(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_256 will hash the given input and write the @@ -124,7 +124,7 @@ hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_512 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_512(transmute([]byte)(data), hash); + hash_bytes_to_buffer_512(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_512 will hash the given input and write the diff --git a/vendor/botan/tiger/tiger.odin b/vendor/botan/tiger/tiger.odin index b29602b26..f24dc7019 100644 --- a/vendor/botan/tiger/tiger.odin +++ b/vendor/botan/tiger/tiger.odin @@ -46,7 +46,7 @@ hash_bytes_128 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_128 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_128(transmute([]byte)(data), hash); + hash_bytes_to_buffer_128(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_128 will hash the given input and write the @@ -125,7 +125,7 @@ hash_bytes_160 :: proc(data: []byte) -> [DIGEST_SIZE_160]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_160 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_160(transmute([]byte)(data), hash); + hash_bytes_to_buffer_160(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_160 will hash the given input and write the @@ -204,7 +204,7 @@ hash_bytes_192 :: proc(data: []byte) -> [DIGEST_SIZE_192]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_192 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_192(transmute([]byte)(data), hash); + hash_bytes_to_buffer_192(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_192 will hash the given input and write the diff --git a/vendor/botan/whirlpool/whirlpool.odin b/vendor/botan/whirlpool/whirlpool.odin index 2aff3c8ed..a7c1abbe8 100644 --- a/vendor/botan/whirlpool/whirlpool.odin +++ b/vendor/botan/whirlpool/whirlpool.odin @@ -44,7 +44,7 @@ hash_bytes :: proc "contextless" (data: []byte) -> [DIGEST_SIZE]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer(transmute([]byte)(data), hash); + hash_bytes_to_buffer(transmute([]byte)(data), hash) } // hash_bytes_to_buffer will hash the given input and write the diff --git a/vendor/darwin/Foundation/NSApplication.odin b/vendor/darwin/Foundation/NSApplication.odin new file mode 100644 index 000000000..2fc4e6356 --- /dev/null +++ b/vendor/darwin/Foundation/NSApplication.odin @@ -0,0 +1,95 @@ +package objc_Foundation + +import "core:intrinsics" + +ActivationPolicy :: enum UInteger { + Regular = 0, + Accessory = 1, + Prohibited = 2, +} + +ApplicationDelegate :: struct { + willFinishLaunching: proc "c" (self: ^ApplicationDelegate, notification: ^Notification), + didFinishLaunching: proc "c" (self: ^ApplicationDelegate, notification: ^Notification), + shouldTerminateAfterLastWindowClosed: proc "c" (self: ^ApplicationDelegate, sender: ^Application), + + user_data: rawptr, +} + +@(objc_class="NSApplication") +Application :: struct {using _: Object} + +@(objc_type=Application, objc_name="sharedApplication", objc_is_class_method=true) +Application_sharedApplication :: proc() -> ^Application { + return msgSend(^Application, Application, "sharedApplication") +} + +@(objc_type=Application, objc_name="setDelegate") +Application_setDelegate :: proc(self: ^Application, delegate: ^ApplicationDelegate) { + willFinishLaunching :: proc "c" (self: ^Value, _: SEL, notification: ^Notification) { + del := (^ApplicationDelegate)(self->pointerValue()) + del->willFinishLaunching(notification) + } + didFinishLaunching :: proc "c" (self: ^Value, _: SEL, notification: ^Notification) { + del := (^ApplicationDelegate)(self->pointerValue()) + del->didFinishLaunching(notification) + } + shouldTerminateAfterLastWindowClosed :: proc "c" (self: ^Value, _: SEL, application: ^Application) { + del := (^ApplicationDelegate)(self->pointerValue()) + del->shouldTerminateAfterLastWindowClosed(application) + } + + wrapper := Value.valueWithPointer(delegate) + + class_addMethod(intrinsics.objc_find_class("NSValue"), intrinsics.objc_find_selector("applicationWillFinishLaunching:"), auto_cast willFinishLaunching, "v@:@") + class_addMethod(intrinsics.objc_find_class("NSValue"), intrinsics.objc_find_selector("applicationDidFinishLaunching:"), auto_cast didFinishLaunching, "v@:@") + class_addMethod(intrinsics.objc_find_class("NSValue"), intrinsics.objc_find_selector("applicationShouldTerminateAfterLastWindowClosed:"), auto_cast shouldTerminateAfterLastWindowClosed, "B@:@") + + msgSend(nil, self, "setDelegate:", wrapper) +} + +@(objc_type=Application, objc_name="setActivationPolicy") +Application_setActivationPolicy :: proc(self: ^Application, activationPolicy: ActivationPolicy) -> BOOL { + return msgSend(BOOL, self, "setActivationPolicy:", activationPolicy) +} + +@(objc_type=Application, objc_name="activateIgnoringOtherApps") +Application_activateIgnoringOtherApps :: proc(self: ^Application, ignoreOtherApps: BOOL) { + msgSend(nil, self, "activateIgnoringOtherApps:", ignoreOtherApps) +} + +@(objc_type=Application, objc_name="setMainMenu") +Application_setMainMenu :: proc(self: ^Application, menu: ^Menu) { + msgSend(nil, self, "setMainMenu:", menu) +} + +@(objc_type=Application, objc_name="windows") +Application_windows :: proc(self: ^Application) -> ^Array { + return msgSend(^Array, self, "windows") +} + +@(objc_type=Application, objc_name="run") +Application_run :: proc(self: ^Application) { + msgSend(nil, self, "run") +} + + +@(objc_type=Application, objc_name="terminate") +Application_terminate :: proc(self: ^Application, sender: ^Object) { + msgSend(nil, self, "terminate:", sender) +} + + + +@(objc_class="NSRunningApplication") +RunningApplication :: struct {using _: Object} + +@(objc_type=RunningApplication, objc_name="currentApplication", objc_is_class_method=true) +RunningApplication_currentApplication :: proc() -> ^RunningApplication { + return msgSend(^RunningApplication, RunningApplication, "currentApplication") +} + +@(objc_type=RunningApplication, objc_name="localizedName") +RunningApplication_localizedName :: proc(self: ^RunningApplication) -> ^String { + return msgSend(^String, self, "localizedName") +} \ No newline at end of file diff --git a/vendor/darwin/Foundation/NSArray.odin b/vendor/darwin/Foundation/NSArray.odin new file mode 100644 index 000000000..d6021838b --- /dev/null +++ b/vendor/darwin/Foundation/NSArray.odin @@ -0,0 +1,42 @@ +package objc_Foundation + +import "core:intrinsics" + +@(objc_class="NSArray") +Array :: struct { + using _: Copying(Array), +} + +@(objc_type=Array, objc_name="alloc", objc_is_class_method=true) +Array_alloc :: proc() -> ^Array { + return msgSend(^Array, Array, "alloc") +} + +@(objc_type=Array, objc_name="init") +Array_init :: proc(self: ^Array) -> ^Array { + return msgSend(^Array, self, "init") +} + +@(objc_type=Array, objc_name="initWithObjects") +Array_initWithObjects :: proc(self: ^Array, objects: [^]^Object, count: UInteger) -> ^Array { + return msgSend(^Array, self, "initWithObjects:count:", objects, count) +} + +@(objc_type=Array, objc_name="initWithCoder") +Array_initWithCoder :: proc(self: ^Array, coder: ^Coder) -> ^Array { + return msgSend(^Array, self, "initWithCoder:", coder) +} + +@(objc_type=Array, objc_name="object") +Array_object :: proc(self: ^Array, index: UInteger) -> ^Object { + return msgSend(^Object, self, "objectAtIndex:", index) +} +@(objc_type=Array, objc_name="objectAs") +Array_objectAs :: proc(self: ^Array, index: UInteger, $T: typeid) -> T where intrinsics.type_is_pointer(T), intrinsics.type_is_subtype_of(T, ^Object) { + return (T)(Array_object(self, index)) +} + +@(objc_type=Array, objc_name="count") +Array_count :: proc(self: ^Array) -> UInteger { + return msgSend(UInteger, self, "count") +} diff --git a/vendor/darwin/Foundation/NSAutoreleasePool.odin b/vendor/darwin/Foundation/NSAutoreleasePool.odin new file mode 100644 index 000000000..a388a7146 --- /dev/null +++ b/vendor/darwin/Foundation/NSAutoreleasePool.odin @@ -0,0 +1,33 @@ +package objc_Foundation + +@(objc_class="NSAutoreleasePool") +AutoreleasePool :: struct {using _: Object} + +@(objc_type=AutoreleasePool, objc_name="alloc", objc_is_class_method=true) +AutoreleasePool_alloc :: proc() -> ^AutoreleasePool { + return msgSend(^AutoreleasePool, AutoreleasePool, "alloc") +} + +@(objc_type=AutoreleasePool, objc_name="init") +AutoreleasePool_init :: proc(self: ^AutoreleasePool) -> ^AutoreleasePool { + return msgSend(^AutoreleasePool, self, "init") +} + +@(objc_type=AutoreleasePool, objc_name="drain") +AutoreleasePool_drain :: proc(self: ^AutoreleasePool) { + msgSend(nil, self, "drain") +} +@(objc_type=AutoreleasePool, objc_name="addObject") +AutoreleasePool_addObject :: proc(self: ^AutoreleasePool, obj: ^Object) { + msgSend(nil, self, "addObject:", obj) +} +@(objc_type=AutoreleasePool, objc_name="showPools") +AutoreleasePool_showPools :: proc(self: ^AutoreleasePool, obj: ^Object) { + msgSend(nil, self, "showPools") +} + + +@(deferred_out=AutoreleasePool_drain) +scoped_autoreleasepool :: proc() -> ^AutoreleasePool { + return AutoreleasePool.alloc()->init() +} \ No newline at end of file diff --git a/vendor/darwin/Foundation/NSBlock.odin b/vendor/darwin/Foundation/NSBlock.odin new file mode 100644 index 000000000..7fa061484 --- /dev/null +++ b/vendor/darwin/Foundation/NSBlock.odin @@ -0,0 +1,81 @@ +package objc_Foundation + +import "core:intrinsics" + +@(objc_class="NSConcreteGlobalBlock") +Block :: struct {using _: Object} + +@(objc_type=Block, objc_name="createGlobal", objc_is_class_method=true) +Block_createGlobal :: proc "c" (user_data: rawptr, user_proc: proc "c" (user_data: rawptr)) -> ^Block { + return Block_createInternal(true, user_data, user_proc) +} + +@(objc_type=Block, objc_name="createLocal", objc_is_class_method=true) +Block_createLocal :: proc "c" (user_data: rawptr, user_proc: proc "c" (user_data: rawptr)) -> ^Block { + return Block_createInternal(false, user_data, user_proc) +} + + +@(private) +Internal_Block_Literal_Base :: struct { + isa: ^intrinsics.objc_class, + flags: u32, + reserved: u32, + invoke: proc "c" (^Internal_Block_Literal), + descriptor: ^Block_Descriptor, +} + +@(private) +Internal_Block_Literal :: struct { + using base: Internal_Block_Literal_Base, + // Imported Variables + user_proc: proc "c" (user_data: rawptr), + user_data: rawptr, +} + +@(private) +Block_Descriptor :: struct { + reserved: uint, + size: uint, + copy_helper: proc "c" (dst, src: rawptr), + dispose_helper: proc "c" (src: rawptr), + signature: cstring, +} + +@(private) +global_block_descriptor := Block_Descriptor{ + reserved = 0, + size = size_of(Internal_Block_Literal), +} + +@(private="file") +Block_createInternal :: proc "c" (is_global: bool, user_data: rawptr, user_proc: proc "c" (user_data: rawptr)) -> ^Block { + // Set to true on blocks that have captures (and thus are not true + // global blocks) but are known not to escape for various other + // reasons. For backward compatibility with old runtimes, whenever + // BLOCK_IS_NOESCAPE is set, BLOCK_IS_GLOBAL is set too. Copying a + // non-escaping block returns the original block and releasing such a + // block is a no-op, which is exactly how global blocks are handled. + BLOCK_IS_NOESCAPE :: (1 << 23)|BLOCK_IS_GLOBAL + + BLOCK_HAS_COPY_DISPOSE :: 1 << 25 + BLOCK_HAS_CTOR :: 1 << 26 // helpers have C++ code + BLOCK_IS_GLOBAL :: 1 << 28 + BLOCK_HAS_STRET :: 1 << 29 // IFF BLOCK_HAS_SIGNATURE + BLOCK_HAS_SIGNATURE :: 1 << 30 + + extraBytes :: size_of(Internal_Block_Literal) - size_of(Internal_Block_Literal_Base) + + cls := intrinsics.objc_find_class("NSConcreteGlobalBlock") + bl := (^Internal_Block_Literal)(AllocateObject(cls, extraBytes, nil)) + bl.isa = cls + bl.flags = BLOCK_IS_GLOBAL if is_global else 0 + bl.invoke = proc "c" (bl: ^Internal_Block_Literal) { + bl.user_proc(bl.user_data) + } + bl.descriptor = &global_block_descriptor + bl.user_proc = user_proc + bl.user_data = user_data + + return auto_cast bl +} diff --git a/vendor/darwin/Foundation/NSBundle.odin b/vendor/darwin/Foundation/NSBundle.odin new file mode 100644 index 000000000..5e136378b --- /dev/null +++ b/vendor/darwin/Foundation/NSBundle.odin @@ -0,0 +1,191 @@ +package objc_Foundation + +@(objc_class="NSBundle") +Bundle :: struct { using _: Object } + +@(objc_type=Bundle, objc_name="mainBundle", objc_is_class_method=true) +Bundle_mainBundle :: proc() -> ^Bundle { + return msgSend(^Bundle, Bundle, "mainBundle") +} + +@(objc_type=Bundle, objc_name="bundleWithPath", objc_is_class_method=true) +Bundle_bundleWithPath :: proc(path: ^String) -> ^Bundle { + return msgSend(^Bundle, Bundle, "bundleWithPath:", path) +} + +@(objc_type=Bundle, objc_name="bundleWithURL", objc_is_class_method=true) +Bundle_bundleWithURL :: proc(url: ^URL) -> ^Bundle { + return msgSend(^Bundle, Bundle, "bundleWithUrl:", url) +} + + +@(objc_type=Bundle, objc_name="alloc", objc_is_class_method=true) +Bundle_alloc :: proc() -> ^Bundle { + return msgSend(^Bundle, Bundle, "alloc") +} + +@(objc_type=Bundle, objc_name="init") +Bundle_init :: proc(self: ^Bundle) -> ^Bundle { + return msgSend(^Bundle, self, "init") +} + +@(objc_type=Bundle, objc_name="initWithPath") +Bundle_initWithPath :: proc(self: ^Bundle, path: ^String) -> ^Bundle { + return msgSend(^Bundle, self, "initWithPath:", path) +} + +@(objc_type=Bundle, objc_name="initWithURL") +Bundle_initWithURL :: proc(self: ^Bundle, url: ^URL) -> ^Bundle { + return msgSend(^Bundle, self, "initWithUrl:", url) +} + +@(objc_type=Bundle, objc_name="allBundles") +Bundle_allBundles :: proc() -> (all: ^Array) { + return msgSend(type_of(all), Bundle, "allBundles") +} + +@(objc_type=Bundle, objc_name="allFrameworks") +Bundle_allFrameworks :: proc() -> (all: ^Array) { + return msgSend(type_of(all), Bundle, "allFrameworks") +} + +@(objc_type=Bundle, objc_name="load") +Bundle_load :: proc(self: ^Bundle) -> BOOL { + return msgSend(BOOL, self, "load") +} +@(objc_type=Bundle, objc_name="unload") +Bundle_unload :: proc(self: ^Bundle) -> BOOL { + return msgSend(BOOL, self, "unload") +} + +@(objc_type=Bundle, objc_name="isLoaded") +Bundle_isLoaded :: proc(self: ^Bundle) -> BOOL { + return msgSend(BOOL, self, "isLoaded") +} + +@(objc_type=Bundle, objc_name="preflightAndReturnError") +Bundle_preflightAndReturnError :: proc(self: ^Bundle) -> (ok: BOOL, error: ^Error) { + ok = msgSend(BOOL, self, "preflightAndReturnError:", &error) + return +} + +@(objc_type=Bundle, objc_name="loadAndReturnError") +Bundle_loadAndReturnError :: proc(self: ^Bundle) -> (ok: BOOL, error: ^Error) { + ok = msgSend(BOOL, self, "loadAndReturnError:", &error) + return +} + +@(objc_type=Bundle, objc_name="bundleURL") +Bundle_bundleURL :: proc(self: ^Bundle) -> ^URL { + return msgSend(^URL, self, "bundleURL") +} + +@(objc_type=Bundle, objc_name="resourceURL") +Bundle_resourceURL :: proc(self: ^Bundle) -> ^URL { + return msgSend(^URL, self, "resourceURL") +} + +@(objc_type=Bundle, objc_name="executableURL") +Bundle_executableURL :: proc(self: ^Bundle) -> ^URL { + return msgSend(^URL, self, "executableURL") +} + +@(objc_type=Bundle, objc_name="URLForAuxiliaryExecutable") +Bundle_URLForAuxiliaryExecutable :: proc(self: ^Bundle, executableName: ^String) -> ^URL { + return msgSend(^URL, self, "URLForAuxiliaryExecutable:", executableName) +} + +@(objc_type=Bundle, objc_name="privateFrameworksURL") +Bundle_privateFrameworksURL :: proc(self: ^Bundle) -> ^URL { + return msgSend(^URL, self, "privateFrameworksURL") +} + +@(objc_type=Bundle, objc_name="sharedFrameworksURL") +Bundle_sharedFrameworksURL :: proc(self: ^Bundle) -> ^URL { + return msgSend(^URL, self, "sharedFrameworksURL") +} + +@(objc_type=Bundle, objc_name="sharedSupportURL") +Bundle_sharedSupportURL :: proc(self: ^Bundle) -> ^URL { + return msgSend(^URL, self, "sharedSupportURL") +} + +@(objc_type=Bundle, objc_name="builtInPlugInsURL") +Bundle_builtInPlugInsURL :: proc(self: ^Bundle) -> ^URL { + return msgSend(^URL, self, "builtInPlugInsURL") +} + +@(objc_type=Bundle, objc_name="appStoreReceiptURL") +Bundle_appStoreReceiptURL :: proc(self: ^Bundle) -> ^URL { + return msgSend(^URL, self, "appStoreReceiptURL") +} + +@(objc_type=Bundle, objc_name="bundlePath") +Bundle_bundlePath :: proc(self: ^Bundle) -> ^String { + return msgSend(^String, self, "bundlePath") +} + +@(objc_type=Bundle, objc_name="resourcePath") +Bundle_resourcePath :: proc(self: ^Bundle) -> ^String { + return msgSend(^String, self, "resourcePath") +} + +@(objc_type=Bundle, objc_name="executablePath") +Bundle_executablePath :: proc(self: ^Bundle) -> ^String { + return msgSend(^String, self, "executablePath") +} + +@(objc_type=Bundle, objc_name="PathForAuxiliaryExecutable") +Bundle_PathForAuxiliaryExecutable :: proc(self: ^Bundle, executableName: ^String) -> ^String { + return msgSend(^String, self, "PathForAuxiliaryExecutable:", executableName) +} + +@(objc_type=Bundle, objc_name="privateFrameworksPath") +Bundle_privateFrameworksPath :: proc(self: ^Bundle) -> ^String { + return msgSend(^String, self, "privateFrameworksPath") +} + +@(objc_type=Bundle, objc_name="sharedFrameworksPath") +Bundle_sharedFrameworksPath :: proc(self: ^Bundle) -> ^String { + return msgSend(^String, self, "sharedFrameworksPath") +} + +@(objc_type=Bundle, objc_name="sharedSupportPath") +Bundle_sharedSupportPath :: proc(self: ^Bundle) -> ^String { + return msgSend(^String, self, "sharedSupportPath") +} + +@(objc_type=Bundle, objc_name="builtInPlugInsPath") +Bundle_builtInPlugInsPath :: proc(self: ^Bundle) -> ^String { + return msgSend(^String, self, "builtInPlugInsPath") +} + +@(objc_type=Bundle, objc_name="appStoreReceiptPath") +Bundle_appStoreReceiptPath :: proc(self: ^Bundle) -> ^String { + return msgSend(^String, self, "appStoreReceiptPath") +} + +@(objc_type=Bundle, objc_name="bundleIdentifier") +Bundle_bundleIdentifier :: proc(self: ^Bundle) -> ^String { + return msgSend(^String, self, "bundleIdentifier") +} + +@(objc_type=Bundle, objc_name="infoDictionary") +Bundle_infoDictionary :: proc(self: ^Bundle) -> ^Dictionary { + return msgSend(^Dictionary, self, "infoDictionary") +} + +@(objc_type=Bundle, objc_name="localizedInfoDictionary") +Bundle_localizedInfoDictionary :: proc(self: ^Bundle) -> ^Dictionary { + return msgSend(^Dictionary, self, "localizedInfoDictionary") +} + +@(objc_type=Bundle, objc_name="objectForInfoDictionaryKey") +Bundle_objectForInfoDictionaryKey :: proc(self: ^Bundle, key: ^String) -> ^Object { + return msgSend(^Object, self, "objectForInfoDictionaryKey:", key) +} + +@(objc_type=Bundle, objc_name="localizedStringForKey") +Bundle_localizedStringForKey :: proc(self: ^Bundle, key: ^String, value: ^String = nil, tableName: ^String = nil) -> ^String { + return msgSend(^String, self, "localizedStringForKey:value:table:", key, value, tableName) +} diff --git a/vendor/darwin/Foundation/NSData.odin b/vendor/darwin/Foundation/NSData.odin new file mode 100644 index 000000000..3c6369e86 --- /dev/null +++ b/vendor/darwin/Foundation/NSData.odin @@ -0,0 +1,24 @@ +package objc_Foundation + +@(objc_class="NSData") +Data :: struct {using _: Copying(Data)} + +@(objc_type=Data, objc_name="alloc", objc_is_class_method=true) +Data_alloc :: proc() -> ^Data { + return msgSend(^Data, Data, "alloc") +} + +@(objc_type=Data, objc_name="init") +Data_init :: proc(self: ^Data) -> ^Data { + return msgSend(^Data, self, "init") +} + +@(objc_type=Data, objc_name="mutableBytes") +Data_mutableBytes :: proc(self: ^Data) -> rawptr { + return msgSend(rawptr, self, "mutableBytes") +} + +@(objc_type=Data, objc_name="length") +Data_length :: proc(self: ^Data) -> UInteger { + return msgSend(UInteger, self, "length") +} \ No newline at end of file diff --git a/vendor/darwin/Foundation/NSDate.odin b/vendor/darwin/Foundation/NSDate.odin new file mode 100644 index 000000000..e30cb07d0 --- /dev/null +++ b/vendor/darwin/Foundation/NSDate.odin @@ -0,0 +1,19 @@ +package objc_Foundation + +@(objc_class="NSDate") +Date :: struct {using _: Copying(Date)} + +@(objc_type=Date, objc_name="alloc", objc_is_class_method=true) +Date_alloc :: proc() -> ^Date { + return msgSend(^Date, Date, "alloc") +} + +@(objc_type=Date, objc_name="init") +Date_init :: proc(self: ^Date) -> ^Date { + return msgSend(^Date, self, "init") +} + +@(objc_type=Date, objc_name="dateWithTimeIntervalSinceNow") +Date_dateWithTimeIntervalSinceNow :: proc(secs: TimeInterval) -> ^Date { + return msgSend(^Date, Date, "dateWithTimeIntervalSinceNow:", secs) +} \ No newline at end of file diff --git a/vendor/darwin/Foundation/NSDictionary.odin b/vendor/darwin/Foundation/NSDictionary.odin new file mode 100644 index 000000000..3832c05f1 --- /dev/null +++ b/vendor/darwin/Foundation/NSDictionary.odin @@ -0,0 +1,50 @@ +package objc_Foundation + +@(objc_class="NSDictionary") +Dictionary :: struct {using _: Copying(Dictionary)} + +@(objc_type=Dictionary, objc_name="dictionary", objc_is_class_method=true) +Dictionary_dictionary :: proc() -> ^Dictionary { + return msgSend(^Dictionary, Dictionary, "dictionary") +} + +@(objc_type=Dictionary, objc_name="dictionaryWithObject", objc_is_class_method=true) +Dictionary_dictionaryWithObject :: proc(object: ^Object, forKey: ^Object) -> ^Dictionary { + return msgSend(^Dictionary, Dictionary, "dictionaryWithObject:forKey:", object, forKey) +} + +@(objc_type=Dictionary, objc_name="dictionaryWithObjects", objc_is_class_method=true) +Dictionary_dictionaryWithObjects :: proc(objects: [^]^Object, forKeys: [^]^Object, count: UInteger) -> ^Dictionary { + return msgSend(^Dictionary, Dictionary, "dictionaryWithObjects:forKeys:count", objects, forKeys, count) +} + + +@(objc_type=Dictionary, objc_name="alloc", objc_is_class_method=true) +Dictionary_alloc :: proc() -> ^Dictionary { + return msgSend(^Dictionary, Dictionary, "alloc") +} + +@(objc_type=Dictionary, objc_name="init") +Dictionary_init :: proc(self: ^Dictionary) -> ^Dictionary { + return msgSend(^Dictionary, self, "init") +} + +@(objc_type=Dictionary, objc_name="initWithObjects") +Dictionary_initWithObjects :: proc(self: ^Dictionary, objects: [^]^Object, forKeys: [^]^Object, count: UInteger) -> ^Dictionary { + return msgSend(^Dictionary, self, "initWithObjects:forKeys:count", objects, forKeys, count) +} + +@(objc_type=Dictionary, objc_name="objectForKey") +Dictionary_objectForKey :: proc(self: ^Dictionary, key: ^Object) -> ^Object { + return msgSend(^Dictionary, self, "objectForKey:", key) +} + +@(objc_type=Dictionary, objc_name="count") +Dictionary_count :: proc(self: ^Dictionary) -> UInteger { + return msgSend(UInteger, self, "count") +} + +@(objc_type=Dictionary, objc_name="keyEnumerator") +Dictionary_keyEnumerator :: proc(self: ^Dictionary, $KeyType: typeid) -> (enumerator: ^Enumerator(KeyType)) { + return msgSend(type_of(enumerator), self, "keyEnumerator") +} diff --git a/vendor/darwin/Foundation/NSEnumerator.odin b/vendor/darwin/Foundation/NSEnumerator.odin new file mode 100644 index 000000000..1c7ddeed2 --- /dev/null +++ b/vendor/darwin/Foundation/NSEnumerator.odin @@ -0,0 +1,50 @@ +package objc_Foundation + +import "core:c" +import "core:intrinsics" + +FastEnumerationState :: struct #packed { + state: c.ulong, + itemsPtr: [^]^Object, + mutationsPtr: [^]c.ulong, + extra: [5]c.ulong, +} + +@(objc_class="NSFastEnumeration") +FastEnumeration :: struct {using _: Object} + +@(objc_class="NSEnumerator") +Enumerator :: struct($T: typeid) where intrinsics.type_is_pointer(T), intrinsics.type_is_subtype_of(T, ^Object) { + using _: FastEnumeration, +} + + +@(objc_type=FastEnumeration, objc_name="alloc", objc_is_class_method=true) +FastEnumeration_alloc :: proc() -> ^FastEnumeration { + return msgSend(^FastEnumeration, FastEnumeration, "alloc") +} + +@(objc_type=FastEnumeration, objc_name="init") +FastEnumeration_init :: proc(self: ^FastEnumeration) -> ^FastEnumeration { + return msgSend(^FastEnumeration, self, "init") +} + + +@(objc_type=FastEnumeration, objc_name="countByEnumerating") +FastEnumeration_countByEnumerating :: proc(self: ^FastEnumeration, state: ^FastEnumerationState, buffer: [^]^Object, len: UInteger) -> UInteger { + return msgSend(UInteger, self, "countByEnumeratingWithState:objects:count:", state, buffer, len) +} + +Enumerator_nextObject :: proc(self: ^$E/Enumerator($T)) -> T { + return msgSend(T, self, "nextObject") +} + +Enumerator_allObjects :: proc(self: ^$E/Enumerator($T)) -> (all: ^Array) { + return msgSend(type_of(all), self, "allObjects") +} + +Enumerator_iterator :: proc(self: ^$E/Enumerator($T)) -> (obj: T, ok: bool) { + obj = msgSend(T, self, "nextObject") + ok = obj != nil + return +} diff --git a/vendor/darwin/Foundation/NSError.odin b/vendor/darwin/Foundation/NSError.odin new file mode 100644 index 000000000..23e6eaba7 --- /dev/null +++ b/vendor/darwin/Foundation/NSError.odin @@ -0,0 +1,88 @@ +package objc_Foundation + +foreign import "system:Foundation.framework" + +ErrorDomain :: ^String + +foreign Foundation { + @(linkage="weak") CocoaErrorDomain: ErrorDomain + @(linkage="weak") POSIXErrorDomain: ErrorDomain + @(linkage="weak") OSStatusErrorDomain: ErrorDomain + @(linkage="weak") MachErrorDomain: ErrorDomain +} + +ErrorUserInfoKey :: ^String + +foreign Foundation { + @(linkage="weak") UnderlyingErrorKey: ErrorUserInfoKey + @(linkage="weak") LocalizedDescriptionKey: ErrorUserInfoKey + @(linkage="weak") LocalizedFailureReasonErrorKey: ErrorUserInfoKey + @(linkage="weak") LocalizedRecoverySuggestionErrorKey: ErrorUserInfoKey + @(linkage="weak") LocalizedRecoveryOptionsErrorKey: ErrorUserInfoKey + @(linkage="weak") RecoveryAttempterErrorKey: ErrorUserInfoKey + @(linkage="weak") HelpAnchorErrorKey: ErrorUserInfoKey + @(linkage="weak") DebugDescriptionErrorKey: ErrorUserInfoKey + @(linkage="weak") LocalizedFailureErrorKey: ErrorUserInfoKey + @(linkage="weak") StringEncodingErrorKey: ErrorUserInfoKey + @(linkage="weak") URLErrorKey: ErrorUserInfoKey + @(linkage="weak") FilePathErrorKey: ErrorUserInfoKey +} + +@(objc_class="NSError") +Error :: struct { using _: Copying(Error) } + + +@(objc_type=Error, objc_name="alloc", objc_is_class_method=true) +Error_alloc :: proc() -> ^Error { + return msgSend(^Error, Error, "alloc") +} + +@(objc_type=Error, objc_name="init") +Error_init :: proc(self: ^Error) -> ^Error { + return msgSend(^Error, self, "init") +} + +@(objc_type=Error, objc_name="errorWithDomain", objc_is_class_method=true) +Error_errorWithDomain :: proc(domain: ErrorDomain, code: Integer, userInfo: ^Dictionary) -> ^Error { + return msgSend(^Error, Error, "errorWithDomain:code:userInfo:", domain, code, userInfo) +} + +@(objc_type=Error, objc_name="initWithDomain") +Error_initWithDomain :: proc(self: ^Error, domain: ErrorDomain, code: Integer, userInfo: ^Dictionary) -> ^Error { + return msgSend(^Error, self, "initWithDomain:code:userInfo:", domain, code, userInfo) +} + +@(objc_type=Error, objc_name="code") +Error_code :: proc(self: ^Error) -> Integer { + return msgSend(Integer, self, "code") +} + +@(objc_type=Error, objc_name="domain") +Error_domain :: proc(self: ^Error) -> ErrorDomain { + return msgSend(ErrorDomain, self, "domain") +} + +@(objc_type=Error, objc_name="userInfo") +Error_userInfo :: proc(self: ^Error) -> ^Dictionary { + return msgSend(^Dictionary, self, "userInfo") +} + +@(objc_type=Error, objc_name="localizedDescription") +Error_localizedDescription :: proc(self: ^Error) -> ^String { + return msgSend(^String, self, "localizedDescription") +} + +@(objc_type=Error, objc_name="localizedRecoveryOptions") +Error_localizedRecoveryOptions :: proc(self: ^Error) -> (options: ^Array) { + return msgSend(type_of(options), self, "localizedRecoveryOptions") +} + +@(objc_type=Error, objc_name="localizedRecoverySuggestion") +Error_localizedRecoverySuggestion :: proc(self: ^Error) -> ^String { + return msgSend(^String, self, "localizedRecoverySuggestion") +} + +@(objc_type=Error, objc_name="localizedFailureReason") +Error_localizedFailureReason :: proc(self: ^Error) -> ^String { + return msgSend(^String, self, "localizedFailureReason") +} \ No newline at end of file diff --git a/vendor/darwin/Foundation/NSLock.odin b/vendor/darwin/Foundation/NSLock.odin new file mode 100644 index 000000000..c48b5dbad --- /dev/null +++ b/vendor/darwin/Foundation/NSLock.odin @@ -0,0 +1,53 @@ +package objc_Foundation + +Locking :: struct($T: typeid) {using _: Object} + +Locking_lock :: proc(self: ^Locking($T)) { + msgSend(nil, self, "lock") +} +Locking_unlock :: proc(self: ^Locking($T)) { + msgSend(nil, self, "unlock") +} + +@(objc_class="NSCondition") +Condition :: struct {using _: Locking(Condition) } + + +@(objc_type=Condition, objc_name="alloc", objc_is_class_method=true) +Condition_alloc :: proc() -> ^Condition { + return msgSend(^Condition, Condition, "alloc") +} + +@(objc_type=Condition, objc_name="init") +Condition_init :: proc(self: ^Condition) -> ^Condition { + return msgSend(^Condition, self, "init") +} + +@(objc_type=Condition, objc_name="wait") +Condition_wait :: proc(self: ^Condition) { + msgSend(nil, self, "wait") +} + +@(objc_type=Condition, objc_name="waitUntilDate") +Condition_waitUntilDate :: proc(self: ^Condition, limit: ^Date) -> BOOL { + return msgSend(BOOL, self, "waitUntilDate:", limit) +} + +@(objc_type=Condition, objc_name="signal") +Condition_signal :: proc(self: ^Condition) { + msgSend(nil, self, "signal") +} + +@(objc_type=Condition, objc_name="broadcast") +Condition_broadcast :: proc(self: ^Condition) { + msgSend(nil, self, "broadcast") +} + +@(objc_type=Condition, objc_name="lock") +Condition_lock :: proc(self: ^Condition) { + msgSend(nil, self, "lock") +} +@(objc_type=Condition, objc_name="unlock") +Condition_unlock :: proc(self: ^Condition) { + msgSend(nil, self, "unlock") +} \ No newline at end of file diff --git a/vendor/darwin/Foundation/NSMenu.odin b/vendor/darwin/Foundation/NSMenu.odin new file mode 100644 index 000000000..964e3061d --- /dev/null +++ b/vendor/darwin/Foundation/NSMenu.odin @@ -0,0 +1,103 @@ +package objc_Foundation + +import "core:builtin" +import "core:intrinsics" + +KeyEquivalentModifierFlag :: enum UInteger { + CapsLock = 16, // Set if Caps Lock key is pressed. + Shift = 17, // Set if Shift key is pressed. + Control = 18, // Set if Control key is pressed. + Option = 19, // Set if Option or Alternate key is pressed. + Command = 20, // Set if Command key is pressed. + NumericPad = 21, // Set if any key in the numeric keypad is pressed. + Help = 22, // Set if the Help key is pressed. + Function = 23, // Set if any function key is pressed. +} +KeyEquivalentModifierMask :: distinct bit_set[KeyEquivalentModifierFlag; UInteger] + +// Used to retrieve only the device-independent modifier flags, allowing applications to mask off the device-dependent modifier flags, including event coalescing information. +KeyEventModifierFlagDeviceIndependentFlagsMask := transmute(KeyEquivalentModifierMask)_KeyEventModifierFlagDeviceIndependentFlagsMask +@(private) _KeyEventModifierFlagDeviceIndependentFlagsMask := UInteger(0xffff0000) + + +MenuItemCallback :: proc "c" (unused: rawptr, name: SEL, sender: ^Object) + + +@(objc_class="NSMenuItem") +MenuItem :: struct {using _: Object} + +@(objc_type=MenuItem, objc_name="alloc", objc_is_class_method=true) +MenuItem_alloc :: proc() -> ^MenuItem { + return msgSend(^MenuItem, MenuItem, "alloc") +} +@(objc_type=MenuItem, objc_name="registerActionCallback", objc_is_class_method=true) +MenuItem_registerActionCallback :: proc(name: cstring, callback: MenuItemCallback) -> SEL { + s := string(name) + n := len(s) + sel: SEL + if n > 0 && s[n-1] != ':' { + col_name := intrinsics.alloca(n+2, 1) + builtin.copy(col_name[:n], s) + col_name[n] = ':' + col_name[n+1] = 0 + sel = sel_registerName(cstring(col_name)) + } else { + sel = sel_registerName(name) + } + if callback != nil { + class_addMethod(intrinsics.objc_find_class("NSObject"), sel, auto_cast callback, "v@:@") + } + return sel +} + +@(objc_type=MenuItem, objc_name="init") +MenuItem_init :: proc(self: ^MenuItem) -> ^MenuItem { + return msgSend(^MenuItem, self, "init") +} + +@(objc_type=MenuItem, objc_name="setKeyEquivalentModifierMask") +MenuItem_setKeyEquivalentModifierMask :: proc(self: ^MenuItem, modifierMask: KeyEquivalentModifierMask) { + msgSend(nil, self, "setKeyEquivalentModifierMask:", modifierMask) +} + +@(objc_type=MenuItem, objc_name="keyEquivalentModifierMask") +MenuItem_keyEquivalentModifierMask :: proc(self: ^MenuItem) -> KeyEquivalentModifierMask { + return msgSend(KeyEquivalentModifierMask, self, "keyEquivalentModifierMask") +} + +@(objc_type=MenuItem, objc_name="setSubmenu") +MenuItem_setSubmenu :: proc(self: ^MenuItem, submenu: ^Menu) { + msgSend(nil, self, "setSubmenu:", submenu) +} + + + + +@(objc_class="NSMenu") +Menu :: struct {using _: Object} + +@(objc_type=Menu, objc_name="alloc", objc_is_class_method=true) +Menu_alloc :: proc() -> ^Menu { + return msgSend(^Menu, Menu, "alloc") +} + +@(objc_type=Menu, objc_name="init") +Menu_init :: proc(self: ^Menu) -> ^Menu { + return msgSend(^Menu, self, "init") +} + +@(objc_type=Menu, objc_name="initWithTitle") +Menu_initWithTitle :: proc(self: ^Menu, title: ^String) -> ^Menu { + return msgSend(^Menu, self, "initWithTitle:", title) +} + + +@(objc_type=Menu, objc_name="addItem") +Menu_addItem :: proc(self: ^Menu, item: ^MenuItem) { + msgSend(nil, self, "addItem:", item) +} + +@(objc_type=Menu, objc_name="addItemWithTitle") +Menu_addItemWithTitle :: proc(self: ^Menu, title: ^String, selector: SEL, keyEquivalent: ^String) -> ^MenuItem { + return msgSend(^MenuItem, self, "addItemWithTitle:action:keyEquivalent:", title, selector, keyEquivalent) +} \ No newline at end of file diff --git a/vendor/darwin/Foundation/NSNotification.odin b/vendor/darwin/Foundation/NSNotification.odin new file mode 100644 index 000000000..ec8dddab7 --- /dev/null +++ b/vendor/darwin/Foundation/NSNotification.odin @@ -0,0 +1,30 @@ +package objc_Foundation + +@(objc_class="NSNotification") +Notification :: struct{using _: Object} + + +@(objc_type=Notification, objc_name="alloc", objc_is_class_method=true) +Notification_alloc :: proc() -> ^Notification { + return msgSend(^Notification, Notification, "alloc") +} + +@(objc_type=Notification, objc_name="init") +Notification_init :: proc(self: ^Notification) -> ^Notification { + return msgSend(^Notification, self, "init") +} + +@(objc_type=Notification, objc_name="name") +Notification_name :: proc(self: ^Notification) -> ^String { + return msgSend(^String, self, "name") +} + +@(objc_type=Notification, objc_name="object") +Notification_object :: proc(self: ^Notification) -> ^Object { + return msgSend(^Object, self, "object") +} + +@(objc_type=Notification, objc_name="userInfo") +Notification_userInfo :: proc(self: ^Notification) -> ^Dictionary { + return msgSend(^Dictionary, self, "userInfo") +} \ No newline at end of file diff --git a/vendor/darwin/Foundation/NSNumber.odin b/vendor/darwin/Foundation/NSNumber.odin new file mode 100644 index 000000000..d459e673f --- /dev/null +++ b/vendor/darwin/Foundation/NSNumber.odin @@ -0,0 +1,154 @@ +package objc_Foundation + +when ODIN_OS == .Darwin { + import "core:c" + _ :: c + + #assert(size_of(c.long) == size_of(int)) + #assert(size_of(c.ulong) == size_of(uint)) +} + +@(objc_class="NSValue") +Value :: struct{using _: Copying(Value)} + +@(objc_type=Value, objc_name="alloc", objc_is_class_method=true) +Value_alloc :: proc() -> ^Value { + return msgSend(^Value, Value, "alloc") +} + +@(objc_type=Value, objc_name="init") +Value_init :: proc(self: ^Value) -> ^Value { + return msgSend(^Value, self, "init") +} + +@(objc_type=Value, objc_name="valueWithBytes", objc_is_class_method=true) +Value_valueWithBytes :: proc(value: rawptr, type: cstring) -> ^Value { + return msgSend(^Value, Value, "valueWithBytes:objCType:", value, type) +} + +@(objc_type=Value, objc_name="valueWithPointer", objc_is_class_method=true) +Value_valueWithPointer :: proc(pointer: rawptr) -> ^Value { + return msgSend(^Value, Value, "valueWithPointer:", pointer) +} + +@(objc_type=Value, objc_name="initWithBytes") +Value_initWithBytes :: proc(self: ^Value, value: rawptr, type: cstring) -> ^Value { + return msgSend(^Value, self, "initWithBytes:objCType:", value, type) +} + +@(objc_type=Value, objc_name="initWithCoder") +Value_initWithCoder :: proc(self: ^Value, coder: ^Coder) -> ^Value { + return msgSend(^Value, self, "initWithCoder:", coder) +} + +@(objc_type=Value, objc_name="getValue") +Value_getValue :: proc(self: ^Value, value: rawptr, size: UInteger) { + msgSend(nil, self, "getValue:size:", value, size) +} + + +@(objc_type=Value, objc_name="objCType") +Value_objCType :: proc "c" (self: ^Value) -> cstring { + return msgSend(cstring, self, "objCType") +} + +@(objc_type=Value, objc_name="isEqualToValue") +Value_isEqualToValue :: proc "c" (self, other: ^Value) -> BOOL { + return msgSend(BOOL, self, "isEqualToValue:", other) +} + +@(objc_type=Value, objc_name="pointerValue") +Value_pointerValue :: proc "c" (self: ^Value) -> rawptr { + return msgSend(rawptr, self, "pointerValue") +} + + +@(objc_class="NSNumber") +Number :: struct{using _: Copying(Number), using _: Value} + +@(objc_type=Number, objc_name="alloc", objc_is_class_method=true) +Number_alloc :: proc() -> ^Number { + return msgSend(^Number, Number, "alloc") +} + +@(objc_type=Number, objc_name="init") +Number_init :: proc(self: ^Number) -> ^Number { + return msgSend(^Number, self, "init") +} + +@(objc_type=Number, objc_name="numberWithI8", objc_is_class_method=true) Number_numberWithI8 :: proc(value: i8) -> ^Number { return msgSend(^Number, Number, "numberWithChar:", value) } +@(objc_type=Number, objc_name="numberWithU8", objc_is_class_method=true) Number_numberWithU8 :: proc(value: u8) -> ^Number { return msgSend(^Number, Number, "numberWithUnsignedChar:", value) } +@(objc_type=Number, objc_name="numberWithI16", objc_is_class_method=true) Number_numberWithI16 :: proc(value: i16) -> ^Number { return msgSend(^Number, Number, "numberWithShort:", value) } +@(objc_type=Number, objc_name="numberWithU16", objc_is_class_method=true) Number_numberWithU16 :: proc(value: u16) -> ^Number { return msgSend(^Number, Number, "numberWithUnsignedShort:", value) } +@(objc_type=Number, objc_name="numberWithI32", objc_is_class_method=true) Number_numberWithI32 :: proc(value: i32) -> ^Number { return msgSend(^Number, Number, "numberWithInt:", value) } +@(objc_type=Number, objc_name="numberWithU32", objc_is_class_method=true) Number_numberWithU32 :: proc(value: u32) -> ^Number { return msgSend(^Number, Number, "numberWithUnsignedInt:", value) } +@(objc_type=Number, objc_name="numberWithInt", objc_is_class_method=true) Number_numberWithInt :: proc(value: int) -> ^Number { return msgSend(^Number, Number, "numberWithLong:", value) } +@(objc_type=Number, objc_name="numberWithUint", objc_is_class_method=true) Number_numberWithUint :: proc(value: uint) -> ^Number { return msgSend(^Number, Number, "numberWithUnsignedLong:", value) } +@(objc_type=Number, objc_name="numberWithU64", objc_is_class_method=true) Number_numberWithU64 :: proc(value: u64) -> ^Number { return msgSend(^Number, Number, "numberWithLongLong:", value) } +@(objc_type=Number, objc_name="numberWithI64", objc_is_class_method=true) Number_numberWithI64 :: proc(value: i64) -> ^Number { return msgSend(^Number, Number, "numberWithUnsignedLongLong:", value) } +@(objc_type=Number, objc_name="numberWithF32", objc_is_class_method=true) Number_numberWithF32 :: proc(value: f32) -> ^Number { return msgSend(^Number, Number, "numberWithFloat:", value) } +@(objc_type=Number, objc_name="numberWithF64", objc_is_class_method=true) Number_numberWithF64 :: proc(value: f64) -> ^Number { return msgSend(^Number, Number, "numberWithDouble:", value) } +@(objc_type=Number, objc_name="numberWithBool", objc_is_class_method=true) Number_numberWithBool :: proc(value: BOOL) -> ^Number { return msgSend(^Number, Number, "numberWithBool:", value) } + +Number_number :: proc{ + Number_numberWithI8, + Number_numberWithU8, + Number_numberWithI16, + Number_numberWithU16, + Number_numberWithI32, + Number_numberWithU32, + Number_numberWithInt, + Number_numberWithUint, + Number_numberWithU64, + Number_numberWithI64, + Number_numberWithF32, + Number_numberWithF64, + Number_numberWithBool, +} + +@(objc_type=Number, objc_name="initWithI8") Number_initWithI8 :: proc(self: ^Number, value: i8) -> ^Number { return msgSend(^Number, self, "initWithChar:", value) } +@(objc_type=Number, objc_name="initWithU8") Number_initWithU8 :: proc(self: ^Number, value: u8) -> ^Number { return msgSend(^Number, self, "initWithUnsignedChar:", value) } +@(objc_type=Number, objc_name="initWithI16") Number_initWithI16 :: proc(self: ^Number, value: i16) -> ^Number { return msgSend(^Number, self, "initWithShort:", value) } +@(objc_type=Number, objc_name="initWithU16") Number_initWithU16 :: proc(self: ^Number, value: u16) -> ^Number { return msgSend(^Number, self, "initWithUnsignedShort:", value) } +@(objc_type=Number, objc_name="initWithI32") Number_initWithI32 :: proc(self: ^Number, value: i32) -> ^Number { return msgSend(^Number, self, "initWithInt:", value) } +@(objc_type=Number, objc_name="initWithU32") Number_initWithU32 :: proc(self: ^Number, value: u32) -> ^Number { return msgSend(^Number, self, "initWithUnsignedInt:", value) } +@(objc_type=Number, objc_name="initWithInt") Number_initWithInt :: proc(self: ^Number, value: int) -> ^Number { return msgSend(^Number, self, "initWithLong:", value) } +@(objc_type=Number, objc_name="initWithUint") Number_initWithUint :: proc(self: ^Number, value: uint) -> ^Number { return msgSend(^Number, self, "initWithUnsignedLong:", value) } +@(objc_type=Number, objc_name="initWithU64") Number_initWithU64 :: proc(self: ^Number, value: u64) -> ^Number { return msgSend(^Number, self, "initWithLongLong:", value) } +@(objc_type=Number, objc_name="initWithI64") Number_initWithI64 :: proc(self: ^Number, value: i64) -> ^Number { return msgSend(^Number, self, "initWithUnsignedLongLong:", value) } +@(objc_type=Number, objc_name="initWithF32") Number_initWithF32 :: proc(self: ^Number, value: f32) -> ^Number { return msgSend(^Number, self, "initWithFloat:", value) } +@(objc_type=Number, objc_name="initWithF64") Number_initWithF64 :: proc(self: ^Number, value: f64) -> ^Number { return msgSend(^Number, self, "initWithDouble:", value) } +@(objc_type=Number, objc_name="initWithBool") Number_initWithBool :: proc(self: ^Number, value: BOOL) -> ^Number { return msgSend(^Number, self, "initWithBool:", value) } + + +@(objc_type=Number, objc_name="i8Value") Number_i8Value :: proc(self: ^Number) -> i8 { return msgSend(i8, self, "charValue") } +@(objc_type=Number, objc_name="u8Value") Number_u8Value :: proc(self: ^Number) -> u8 { return msgSend(u8, self, "unsignedCharValue") } +@(objc_type=Number, objc_name="i16Value") Number_i16Value :: proc(self: ^Number) -> i16 { return msgSend(i16, self, "shortValue") } +@(objc_type=Number, objc_name="u16Value") Number_u16Value :: proc(self: ^Number) -> u16 { return msgSend(u16, self, "unsignedShortValue") } +@(objc_type=Number, objc_name="i32Value") Number_i32Value :: proc(self: ^Number) -> i32 { return msgSend(i32, self, "intValue") } +@(objc_type=Number, objc_name="u32Value") Number_u32Value :: proc(self: ^Number) -> u32 { return msgSend(u32, self, "unsignedIntValue") } +@(objc_type=Number, objc_name="intValue") Number_intValue :: proc(self: ^Number) -> int { return msgSend(int, self, "longValue") } +@(objc_type=Number, objc_name="uintValue") Number_uintValue :: proc(self: ^Number) -> uint { return msgSend(uint, self, "unsignedLongValue") } +@(objc_type=Number, objc_name="u64Value") Number_u64Value :: proc(self: ^Number) -> u64 { return msgSend(u64, self, "longLongValue") } +@(objc_type=Number, objc_name="i64Value") Number_i64Value :: proc(self: ^Number) -> i64 { return msgSend(i64, self, "unsignedLongLongValue") } +@(objc_type=Number, objc_name="f32Value") Number_f32Value :: proc(self: ^Number) -> f32 { return msgSend(f32, self, "floatValue") } +@(objc_type=Number, objc_name="f64Value") Number_f64Value :: proc(self: ^Number) -> f64 { return msgSend(f64, self, "doubleValue") } +@(objc_type=Number, objc_name="boolValue") Number_boolValue :: proc(self: ^Number) -> BOOL { return msgSend(BOOL, self, "boolValue") } +@(objc_type=Number, objc_name="integerValue") Number_integerValue :: proc(self: ^Number) -> Integer { return msgSend(Integer, self, "integerValue") } +@(objc_type=Number, objc_name="uintegerValue") Number_uintegerValue :: proc(self: ^Number) -> UInteger { return msgSend(UInteger, self, "unsignedIntegerValue") } +@(objc_type=Number, objc_name="stringValue") Number_stringValue :: proc(self: ^Number) -> ^String { return msgSend(^String, self, "stringValue") } + +@(objc_type=Number, objc_name="compare") +Number_compare :: proc(self, other: ^Number) -> ComparisonResult { + return msgSend(ComparisonResult, self, "compare:", other) +} + +@(objc_type=Number, objc_name="isEqualToNumber") +Number_isEqualToNumber :: proc(self, other: ^Number) -> BOOL { + return msgSend(BOOL, self, "isEqualToNumber:", other) +} + +@(objc_type=Number, objc_name="descriptionWithLocale") +Number_descriptionWithLocale :: proc(self: ^Number, locale: ^Object) -> ^String { + return msgSend(^String, self, "descriptionWithLocale:", locale) +} \ No newline at end of file diff --git a/vendor/darwin/Foundation/NSObject.odin b/vendor/darwin/Foundation/NSObject.odin new file mode 100644 index 000000000..6ec5939ee --- /dev/null +++ b/vendor/darwin/Foundation/NSObject.odin @@ -0,0 +1,91 @@ +package objc_Foundation + +import "core:intrinsics" + +methodSignatureForSelector :: proc "c" (obj: ^Object, selector: SEL) -> rawptr { + return msgSend(rawptr, obj, "methodSignatureForSelector:", selector) +} + +respondsToSelector :: proc "c" (obj: ^Object, selector: SEL) -> BOOL { + return msgSend(BOOL, obj, "respondsToSelector:", selector) +} + +msgSendSafeCheck :: proc "c" (obj: ^Object, selector: SEL) -> BOOL { + return respondsToSelector(obj, selector) || methodSignatureForSelector(obj, selector) != nil +} + + +@(objc_class="NSObject") +Object :: struct {using _: intrinsics.objc_object} + +@(objc_class="NSObject") +Copying :: struct($T: typeid) {using _: Object} + +alloc :: proc($T: typeid) -> ^T where intrinsics.type_is_subtype_of(T, Object) { + return msgSend(^T, T, "alloc") +} +@(objc_type=Object, objc_name="init") +init :: proc(self: ^$T) -> ^T where intrinsics.type_is_subtype_of(T, Object) { + return msgSend(^T, self, "init") +} +@(objc_type=Object, objc_name="copy") +copy :: proc(self: ^Copying($T)) -> ^T where intrinsics.type_is_subtype_of(T, Object) { + return msgSend(^T, self, "copy") +} + +new :: proc($T: typeid) -> ^T where intrinsics.type_is_subtype_of(T, Object) { + return init(alloc(T)) +} + +@(objc_type=Object, objc_name="retain") +retain :: proc(self: ^Object) { + _ = msgSend(^Object, self, "retain") +} +@(objc_type=Object, objc_name="release") +release :: proc(self: ^Object) { + msgSend(nil, self, "release") +} +@(objc_type=Object, objc_name="autorelease") +autorelease :: proc(self: ^Object) { + msgSend(nil, self, "autorelease") +} +@(objc_type=Object, objc_name="retainCount") +retainCount :: proc(self: ^Object) -> UInteger { + return msgSend(UInteger, self, "retainCount") +} +@(objc_type=Object, objc_name="class") +class :: proc(self: ^Object) -> Class { + return msgSend(Class, self, "class") +} + +@(objc_type=Object, objc_name="hash") +hash :: proc(self: ^Object) -> UInteger { + return msgSend(UInteger, self, "hash") +} + +@(objc_type=Object, objc_name="isEqual") +isEqual :: proc(self, pObject: ^Object) -> BOOL { + return msgSend(BOOL, self, "isEqual:", pObject) +} + +@(objc_type=Object, objc_name="description") +description :: proc(self: ^Object) -> ^String { + return msgSend(^String, self, "description") +} + +@(objc_type=Object, objc_name="debugDescription") +debugDescription :: proc(self: ^Object) -> ^String { + if msgSendSafeCheck(self, intrinsics.objc_find_selector("debugDescription")) { + return msgSend(^String, self, "debugDescription") + } + return nil +} + +bridgingCast :: proc($T: typeid, obj: ^Object) where intrinsics.type_is_pointer(T), intrinsics.type_is_subtype_of(T, ^Object) { + return (T)(obj) +} + + +@(objc_class="NSCoder") +Coder :: struct {using _: Object} +// TODO(bill): Implement all the methods for this massive type \ No newline at end of file diff --git a/vendor/darwin/Foundation/NSRange.odin b/vendor/darwin/Foundation/NSRange.odin new file mode 100644 index 000000000..74ce595a3 --- /dev/null +++ b/vendor/darwin/Foundation/NSRange.odin @@ -0,0 +1,22 @@ +package objc_Foundation + +Range :: struct { + location: UInteger, + length: UInteger, +} + +Range_Make :: proc(loc, len: UInteger) -> Range { + return Range{loc, len} +} + +Range_Equal :: proc(a, b: Range) -> BOOL { + return a == b +} + +Range_LocationInRange :: proc(self: Range, loc: UInteger) -> BOOL { + return !((loc < self.location) && ((loc - self.location) < self.length)) +} + +Range_Max :: proc(self: Range) -> UInteger { + return self.location + self.length +} \ No newline at end of file diff --git a/vendor/darwin/Foundation/NSString.odin b/vendor/darwin/Foundation/NSString.odin new file mode 100644 index 000000000..18e392415 --- /dev/null +++ b/vendor/darwin/Foundation/NSString.odin @@ -0,0 +1,140 @@ +package objc_Foundation + +foreign import "system:Foundation.framework" + +@(objc_class="NSString") +String :: struct {using _: Copying(String)} + +StringEncoding :: enum UInteger { + ASCII = 1, + NEXTSTEP = 2, + JapaneseEUC = 3, + UTF8 = 4, + ISOLatin1 = 5, + Symbol = 6, + NonLossyASCII = 7, + ShiftJIS = 8, + ISOLatin2 = 9, + Unicode = 10, + WindowsCP1251 = 11, + WindowsCP1252 = 12, + WindowsCP1253 = 13, + WindowsCP1254 = 14, + WindowsCP1250 = 15, + ISO2022JP = 21, + MacOSRoman = 30, + + UTF16 = Unicode, + + UTF16BigEndian = 0x90000100, + UTF16LittleEndian = 0x94000100, + + UTF32 = 0x8c000100, + UTF32BigEndian = 0x98000100, + UTF32LittleEndian = 0x9c000100, +} + +StringCompareOptions :: distinct bit_set[StringCompareOption; UInteger] +StringCompareOption :: enum UInteger { + CaseInsensitive = 0, + LiteralSearch = 1, + BackwardsSearch = 2, + AnchoredSearch = 3, + NumericSearch = 6, + DiacriticInsensitive = 7, + WidthInsensitive = 8, + ForcedOrdering = 9, + RegularExpression = 10, +} + +unichar :: distinct u16 + +@(link_prefix="NS", default_calling_convention="c") +foreign Foundation { + StringFromClass :: proc(cls: Class) -> ^String --- +} + +AT :: MakeConstantString +MakeConstantString :: proc "c" (#const c: cstring) -> ^String { + foreign Foundation { + __CFStringMakeConstantString :: proc "c" (c: cstring) -> ^String --- + } + return __CFStringMakeConstantString(c) +} + + +@(objc_type=String, objc_name="alloc", objc_is_class_method=true) +String_alloc :: proc() -> ^String { + return msgSend(^String, String, "alloc") +} + +@(objc_type=String, objc_name="init") +String_init :: proc(self: ^String) -> ^String { + return msgSend(^String, self, "init") +} + + +@(objc_type=String, objc_name="initWithString") +String_initWithString :: proc(self: ^String, other: ^String) -> ^String { + return msgSend(^String, self, "initWithString:", other) +} + +@(objc_type=String, objc_name="initWithCString") +String_initWithCString :: proc(self: ^String, pString: cstring, encoding: StringEncoding) -> ^String { + return msgSend(^String, self, "initWithCstring:encoding:", pString, encoding) +} + +@(objc_type=String, objc_name="initWithBytesNoCopy") +String_initWithBytesNoCopy :: proc(self: ^String, pBytes: rawptr, length: UInteger, encoding: StringEncoding, freeWhenDone: bool) -> ^String { + return msgSend(^String, self, "initWithBytesNoCopy:length:encoding:freeWhenDone:", pBytes, length, encoding, freeWhenDone) +} + +@(objc_type=String, objc_name="initWithOdinString") +String_initWithOdinString :: proc(self: ^String, str: string) -> ^String { + return String_initWithBytesNoCopy(self, raw_data(str), UInteger(len(str)), .UTF8, false) +} + +@(objc_type=String, objc_name="characterAtIndex") +String_characterAtIndex :: proc(self: ^String, index: UInteger) -> unichar { + return msgSend(unichar, self, "characterAtIndex:", index) +} + +@(objc_type=String, objc_name="length") +String_length :: proc(self: ^String) -> UInteger { + return msgSend(UInteger, self, "length") +} + +@(objc_type=String, objc_name="cstringUsingEncoding") +String_cstringUsingEncoding :: proc(self: ^String, encoding: StringEncoding) -> cstring { + return msgSend(cstring, self, "cStringUsingEncoding:", encoding) +} + +@(objc_type=String, objc_name="UTF8String") +String_UTF8String :: proc(self: ^String) -> cstring { + return msgSend(cstring, self, "UTF8String") +} + +@(objc_type=String, objc_name="odinString") +String_odinString :: proc(self: ^String) -> string { + return string(String_UTF8String(self)) +} + +@(objc_type=String, objc_name="maximumLengthOfBytesUsingEncoding") +String_maximumLengthOfBytesUsingEncoding :: proc(self: ^String, encoding: StringEncoding) -> UInteger { + return msgSend(UInteger, self, "maximumLengthOfBytesUsingEncoding:", encoding) +} + +@(objc_type=String, objc_name="lengthOfBytesUsingEncoding") +String_lengthOfBytesUsingEncoding :: proc(self: ^String, encoding: StringEncoding) -> UInteger { + return msgSend(UInteger, self, "lengthOfBytesUsingEncoding:", encoding) +} + +@(objc_type=String, objc_name="isEqualToString") +String_isEqualToString :: proc(self, other: ^String) -> BOOL { + return msgSend(BOOL, self, "isEqualToString:", other) +} + +@(objc_type=String, objc_name="rangeOfString") +String_rangeOfString :: proc(self, other: ^String, options: StringCompareOptions) -> Range { + return msgSend(Range, self, "rangeOfString:options:", other, options) +} \ No newline at end of file diff --git a/vendor/darwin/Foundation/NSTypes.odin b/vendor/darwin/Foundation/NSTypes.odin new file mode 100644 index 000000000..0c1239710 --- /dev/null +++ b/vendor/darwin/Foundation/NSTypes.odin @@ -0,0 +1,47 @@ +package objc_Foundation + +import "core:intrinsics" + +@(private) msgSend :: intrinsics.objc_send + +id :: ^intrinsics.objc_object +SEL :: ^intrinsics.objc_selector +Class :: ^intrinsics.objc_class + +TimeInterval :: distinct f64 +Integer :: distinct int +UInteger :: distinct uint + +IntegerMax :: max(Integer) +Integermin :: min(Integer) +UIntegerMax :: max(UInteger) + +BOOL :: bool // TODO(bill): should this be `distinct`? +YES :: true +NO :: false + +OperatingSystemVersion :: struct #packed { + majorVersion: Integer, + minorVersion: Integer, + patchVersion: Integer, +} + +ComparisonResult :: enum Integer { + OrderedAscending = -1, + OrderedSame = 0, + OrderedDescending = 1, +} + +NotFound :: IntegerMax + +Float :: distinct (f32 when size_of(uint) == 4 else f64) + +Point :: struct { + x: Float, + y: Float, +} + +Size :: struct { + width: Float, + height: Float, +} diff --git a/vendor/darwin/Foundation/NSURL.odin b/vendor/darwin/Foundation/NSURL.odin new file mode 100644 index 000000000..72e5fc906 --- /dev/null +++ b/vendor/darwin/Foundation/NSURL.odin @@ -0,0 +1,30 @@ +package objc_Foundation + +@(objc_class="NSURL") +URL :: struct{using _: Copying(URL)} + + +@(objc_type=URL, objc_name="alloc", objc_is_class_method=true) +URL_alloc :: proc() -> ^URL { + return msgSend(^URL, URL, "alloc") +} + +@(objc_type=URL, objc_name="init") +URL_init :: proc(self: ^URL) -> ^URL { + return msgSend(^URL, self, "init") +} + +@(objc_type=URL, objc_name="initWithString") +URL_initWithString :: proc(self: ^URL, value: ^String) -> ^URL { + return msgSend(^URL, self, "initWithString:", value) +} + +@(objc_type=URL, objc_name="initFileURLWithPath") +URL_initFileURLWithPath :: proc(self: ^URL, path: ^String) -> ^URL { + return msgSend(^URL, self, "initFileURLWithPath:", path) +} + +@(objc_type=URL, objc_name="fileSystemRepresentation") +URL_fileSystemRepresentation :: proc(self: ^URL) -> ^String { + return msgSend(^String, self, "fileSystemRepresentation") +} \ No newline at end of file diff --git a/vendor/darwin/Foundation/NSWindow.odin b/vendor/darwin/Foundation/NSWindow.odin new file mode 100644 index 000000000..330af6012 --- /dev/null +++ b/vendor/darwin/Foundation/NSWindow.odin @@ -0,0 +1,162 @@ +package objc_Foundation + +import NS "vendor:darwin/Foundation" + +Rect :: struct { + using origin: Point, + using size: Size, +} + +WindowStyleFlag :: enum NS.UInteger { + Titled = 0, + Closable = 1, + Miniaturizable = 2, + Resizable = 3, + TexturedBackground = 8, + UnifiedTitleAndToolbar = 12, + FullScreen = 14, + FullSizeContentView = 15, + UtilityWindow = 4, + DocModalWindow = 6, + NonactivatingPanel = 7, + HUDWindow = 13, +} +WindowStyleMask :: distinct bit_set[WindowStyleFlag; NS.UInteger] +WindowStyleMaskBorderless :: WindowStyleMask{} +WindowStyleMaskTitled :: WindowStyleMask{.Titled} +WindowStyleMaskClosable :: WindowStyleMask{.Closable} +WindowStyleMaskMiniaturizable :: WindowStyleMask{.Miniaturizable} +WindowStyleMaskResizable :: WindowStyleMask{.Resizable} +WindowStyleMaskTexturedBackground :: WindowStyleMask{.TexturedBackground} +WindowStyleMaskUnifiedTitleAndToolbar :: WindowStyleMask{.UnifiedTitleAndToolbar} +WindowStyleMaskFullScreen :: WindowStyleMask{.FullScreen} +WindowStyleMaskFullSizeContentView :: WindowStyleMask{.FullSizeContentView} +WindowStyleMaskUtilityWindow :: WindowStyleMask{.UtilityWindow} +WindowStyleMaskDocModalWindow :: WindowStyleMask{.DocModalWindow} +WindowStyleMaskNonactivatingPanel :: WindowStyleMask{.NonactivatingPanel} +WindowStyleMaskHUDWindow :: WindowStyleMask{.HUDWindow} + +BackingStoreType :: enum NS.UInteger { + Retained = 0, + Nonretained = 1, + Buffered = 2, +} + +@(objc_class="NSColor") +Color :: struct {using _: Object} + +@(objc_class="CALayer") +Layer :: struct { using _: NS.Object } + +@(objc_type=Layer, objc_name="contentsScale") +Layer_contentsScale :: proc(self: ^Layer) -> Float { + return msgSend(Float, self, "contentsScale") +} +@(objc_type=Layer, objc_name="setContentsScale") +Layer_setContentsScale :: proc(self: ^Layer, scale: Float) { + msgSend(nil, self, "setContentsScale:", scale) +} +@(objc_type=Layer, objc_name="frame") +Layer_frame :: proc(self: ^Layer) -> Rect { + return msgSend(Rect, self, "frame") +} +@(objc_type=Layer, objc_name="addSublayer") +Layer_addSublayer :: proc(self: ^Layer, layer: ^Layer) { + msgSend(nil, self, "addSublayer:", layer) +} + +@(objc_class="NSResponder") +Responder :: struct {using _: Object} + +@(objc_class="NSView") +View :: struct {using _: Responder} + + +@(objc_type=View, objc_name="initWithFrame") +View_initWithFrame :: proc(self: ^View, frame: Rect) -> ^View { + return msgSend(^View, self, "initWithFrame:", frame) +} +@(objc_type=View, objc_name="layer") +View_layer :: proc(self: ^View) -> ^Layer { + return msgSend(^Layer, self, "layer") +} +@(objc_type=View, objc_name="setLayer") +View_setLayer :: proc(self: ^View, layer: ^Layer) { + msgSend(nil, self, "setLayer:", layer) +} +@(objc_type=View, objc_name="wantsLayer") +View_wantsLayer :: proc(self: ^View) -> BOOL { + return msgSend(BOOL, self, "wantsLayer") +} +@(objc_type=View, objc_name="setWantsLayer") +View_setWantsLayer :: proc(self: ^View, wantsLayer: BOOL) { + msgSend(nil, self, "setWantsLayer:", wantsLayer) +} + +@(objc_class="NSWindow") +Window :: struct {using _: Responder} + +@(objc_type=Window, objc_name="alloc", objc_is_class_method=true) +Window_alloc :: proc() -> ^Window { + return msgSend(^Window, Window, "alloc") +} + +@(objc_type=Window, objc_name="initWithContentRect") +Window_initWithContentRect :: proc (self: ^Window, contentRect: Rect, styleMask: WindowStyleMask, backing: BackingStoreType, doDefer: bool) -> ^Window { + self := self + // HACK: due to a compiler bug, the generated calling code does not + // currently work for this message. Has to do with passing a struct along + // with other parameters, so we don't send the rect here. + // Omiting the rect argument here actually works, because of how the C + // calling conventions are defined. + self = msgSend(^Window, self, "initWithContentRect:styleMask:backing:defer:", styleMask, backing, doDefer) + + // apply the contentRect now, since we did not pass it to the init call + msgSend(nil, self, "setContentSize:", contentRect.size) + msgSend(nil, self, "setFrameOrigin:", contentRect.origin) + return self +} +@(objc_type=Window, objc_name="contentView") +Window_contentView :: proc(self: ^Window) -> ^View { + return msgSend(^View, self, "contentView") +} +@(objc_type=Window, objc_name="setContentView") +Window_setContentView :: proc(self: ^Window, content_view: ^View) { + msgSend(nil, self, "setContentView:", content_view) +} +@(objc_type=Window, objc_name="frame") +Window_frame :: proc(self: ^Window) -> Rect { + return msgSend(Rect, self, "frame") +} +@(objc_type=Window, objc_name="setFrame") +Window_setFrame :: proc(self: ^Window, frame: Rect) { + msgSend(nil, self, "setFrame:", frame) +} +@(objc_type=Window, objc_name="opaque") +Window_opaque :: proc(self: ^Window) -> NS.BOOL { + return msgSend(NS.BOOL, self, "opaque") +} +@(objc_type=Window, objc_name="setOpaque") +Window_setOpaque :: proc(self: ^Window, ok: NS.BOOL) { + msgSend(nil, self, "setOpaque:", ok) +} +@(objc_type=Window, objc_name="backgroundColor") +Window_backgroundColor :: proc(self: ^Window) -> ^NS.Color { + return msgSend(^NS.Color, self, "backgroundColor") +} +@(objc_type=Window, objc_name="setBackgroundColor") +Window_setBackgroundColor :: proc(self: ^Window, color: ^NS.Color) { + msgSend(nil, self, "setBackgroundColor:", color) +} +@(objc_type=Window, objc_name="makeKeyAndOrderFront") +Window_makeKeyAndOrderFront :: proc(self: ^Window, key: ^NS.Object) { + msgSend(nil, self, "makeKeyAndOrderFront:", key) +} +@(objc_type=Window, objc_name="setTitle") +Window_setTitle :: proc(self: ^Window, title: ^NS.String) { + msgSend(nil, self, "setTitle:", title) +} +@(objc_type=Window, objc_name="close") +Window_close :: proc(self: ^Window) { + msgSend(nil, self, "close") +} diff --git a/vendor/darwin/Foundation/objc.odin b/vendor/darwin/Foundation/objc.odin new file mode 100644 index 000000000..7db82df73 --- /dev/null +++ b/vendor/darwin/Foundation/objc.odin @@ -0,0 +1,73 @@ +package objc_Foundation + +foreign import "system:Foundation.framework" + +import "core:intrinsics" +import "core:c" + +IMP :: proc "c" (object: id, sel: SEL, #c_vararg args: ..any) -> id + +foreign Foundation { + objc_lookUpClass :: proc "c" (name: cstring) -> Class --- + sel_registerName :: proc "c" (name: cstring) -> SEL --- + objc_allocateClassPair :: proc "c" (superclass: Class, name: cstring, extraBytes: uint) --- + + class_addMethod :: proc "c" (cls: Class, name: SEL, imp: IMP, types: cstring) -> BOOL --- +} + + +@(objc_class="NSZone") +Zone :: struct {using _: Object} + +@(link_prefix="NS") +foreign Foundation { + AllocateObject :: proc "c" (aClass: Class, extraBytes: UInteger, zone: ^Zone) -> id --- + DeallocateObject :: proc "c" (object: id) --- +} + +Method :: ^objc_method +objc_method :: struct { + method_name: SEL, + method_types: cstring, + method_imp: IMP, +} +objc_method_list :: struct {} + +objc_ivar :: struct {} +objc_ivar_list :: struct {} + +objc_cache :: struct { + mask: u32, + occupied: u32, + buckets: [1]Method, +} + +objc_protocol_list :: struct { + next: ^objc_protocol_list, + count: c.int, + list: [1]^Protocol, +} + +@(objc_class="Protocol") +Protocol :: struct{using _: intrinsics.objc_object} + +objc_object_internals :: struct { + isa: ^objc_class_internals, +} + + +objc_class_internals :: struct { + isa: Class, + super_class: Class, + name: cstring, + version: c.long, + info: c.long, + instance_size: c.long, + ivars: ^objc_ivar_list, + + methodLists: ^^objc_method_list, + + cache: rawptr, + protocols: rawptr, + +} \ No newline at end of file diff --git a/vendor/darwin/Metal/MetalClasses.odin b/vendor/darwin/Metal/MetalClasses.odin new file mode 100644 index 000000000..075aae545 --- /dev/null +++ b/vendor/darwin/Metal/MetalClasses.odin @@ -0,0 +1,8561 @@ +package objc_Metal + +import NS "vendor:darwin/Foundation" +import "core:mem" +_ :: mem + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + AccelerationStructureBoundingBoxGeometryDescriptor +Class Methods: + alloc + descriptor +Methods: + init + boundingBoxBuffer + boundingBoxBufferOffset + boundingBoxCount + boundingBoxStride + setBoundingBoxBuffer + setBoundingBoxBufferOffset + setBoundingBoxCount + setBoundingBoxStride +*/ +@(objc_class="MTLAccelerationStructureBoundingBoxGeometryDescriptor") +AccelerationStructureBoundingBoxGeometryDescriptor :: struct { using _: NS.Copying(AccelerationStructureBoundingBoxGeometryDescriptor), using _: AccelerationStructureDescriptor } + +@(objc_type=AccelerationStructureBoundingBoxGeometryDescriptor, objc_name="alloc", objc_is_class_method=true) +AccelerationStructureBoundingBoxGeometryDescriptor_alloc :: #force_inline proc() -> ^AccelerationStructureBoundingBoxGeometryDescriptor { + return msgSend(^AccelerationStructureBoundingBoxGeometryDescriptor, AccelerationStructureBoundingBoxGeometryDescriptor, "alloc") +} +@(objc_type=AccelerationStructureBoundingBoxGeometryDescriptor, objc_name="init") +AccelerationStructureBoundingBoxGeometryDescriptor_init :: #force_inline proc(self: ^AccelerationStructureBoundingBoxGeometryDescriptor) -> ^AccelerationStructureBoundingBoxGeometryDescriptor { + return msgSend(^AccelerationStructureBoundingBoxGeometryDescriptor, self, "init") +} +@(objc_type=AccelerationStructureBoundingBoxGeometryDescriptor, objc_name="boundingBoxBuffer") +AccelerationStructureBoundingBoxGeometryDescriptor_boundingBoxBuffer :: #force_inline proc(self: ^AccelerationStructureBoundingBoxGeometryDescriptor) -> ^Buffer { + return msgSend(^Buffer, self, "boundingBoxBuffer") +} +@(objc_type=AccelerationStructureBoundingBoxGeometryDescriptor, objc_name="boundingBoxBufferOffset") +AccelerationStructureBoundingBoxGeometryDescriptor_boundingBoxBufferOffset :: #force_inline proc(self: ^AccelerationStructureBoundingBoxGeometryDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "boundingBoxBufferOffset") +} +@(objc_type=AccelerationStructureBoundingBoxGeometryDescriptor, objc_name="boundingBoxCount") +AccelerationStructureBoundingBoxGeometryDescriptor_boundingBoxCount :: #force_inline proc(self: ^AccelerationStructureBoundingBoxGeometryDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "boundingBoxCount") +} +@(objc_type=AccelerationStructureBoundingBoxGeometryDescriptor, objc_name="boundingBoxStride") +AccelerationStructureBoundingBoxGeometryDescriptor_boundingBoxStride :: #force_inline proc(self: ^AccelerationStructureBoundingBoxGeometryDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "boundingBoxStride") +} +@(objc_type=AccelerationStructureBoundingBoxGeometryDescriptor, objc_name="descriptor", objc_is_class_method=true) +AccelerationStructureBoundingBoxGeometryDescriptor_descriptor :: #force_inline proc() -> ^AccelerationStructureBoundingBoxGeometryDescriptor { + return msgSend(^AccelerationStructureBoundingBoxGeometryDescriptor, AccelerationStructureBoundingBoxGeometryDescriptor, "descriptor") +} +@(objc_type=AccelerationStructureBoundingBoxGeometryDescriptor, objc_name="setBoundingBoxBuffer") +AccelerationStructureBoundingBoxGeometryDescriptor_setBoundingBoxBuffer :: #force_inline proc(self: ^AccelerationStructureBoundingBoxGeometryDescriptor, boundingBoxBuffer: ^Buffer) { + msgSend(nil, self, "setBoundingBoxBuffer:", boundingBoxBuffer) +} +@(objc_type=AccelerationStructureBoundingBoxGeometryDescriptor, objc_name="setBoundingBoxBufferOffset") +AccelerationStructureBoundingBoxGeometryDescriptor_setBoundingBoxBufferOffset :: #force_inline proc(self: ^AccelerationStructureBoundingBoxGeometryDescriptor, boundingBoxBufferOffset: NS.UInteger) { + msgSend(nil, self, "setBoundingBoxBufferOffset:", boundingBoxBufferOffset) +} +@(objc_type=AccelerationStructureBoundingBoxGeometryDescriptor, objc_name="setBoundingBoxCount") +AccelerationStructureBoundingBoxGeometryDescriptor_setBoundingBoxCount :: #force_inline proc(self: ^AccelerationStructureBoundingBoxGeometryDescriptor, boundingBoxCount: NS.UInteger) { + msgSend(nil, self, "setBoundingBoxCount:", boundingBoxCount) +} +@(objc_type=AccelerationStructureBoundingBoxGeometryDescriptor, objc_name="setBoundingBoxStride") +AccelerationStructureBoundingBoxGeometryDescriptor_setBoundingBoxStride :: #force_inline proc(self: ^AccelerationStructureBoundingBoxGeometryDescriptor, boundingBoxStride: NS.UInteger) { + msgSend(nil, self, "setBoundingBoxStride:", boundingBoxStride) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + MotionKeyframeData +Class Methods: + alloc + data +Methods: + init + buffer + setBuffer + offset + setOffset +*/ + +@(objc_class="MTLMotionKeyframeData") +MotionKeyframeData :: struct { using _: NS.Object } + +@(objc_type=MotionKeyframeData, objc_name="alloc", objc_is_class_method=true) +MotionKeyframeData_alloc :: #force_inline proc() -> ^MotionKeyframeData { + return msgSend(^MotionKeyframeData, MotionKeyframeData, "alloc") +} +@(objc_type=MotionKeyframeData, objc_name="data", objc_is_class_method=true) +MotionKeyframeData_data :: #force_inline proc() -> ^MotionKeyframeData { + return msgSend(^MotionKeyframeData, MotionKeyframeData, "data") +} +@(objc_type=MotionKeyframeData, objc_name="init", objc_is_class_method=true) +MotionKeyframeData_init :: #force_inline proc(self: ^MotionKeyframeData) -> ^MotionKeyframeData { + return msgSend(^MotionKeyframeData, self, "init") +} +@(objc_type=MotionKeyframeData, objc_name="buffer", objc_is_class_method=true) +MotionKeyframeData_buffer :: #force_inline proc(self: ^MotionKeyframeData) -> ^Buffer { + return msgSend(^Buffer, self, "buffer") +} +@(objc_type=MotionKeyframeData, objc_name="setBuffer", objc_is_class_method=true) +MotionKeyframeData_setBuffer :: #force_inline proc(self: ^MotionKeyframeData, buffer: ^Buffer) { + msgSend(nil, self, "setBuffer:", buffer) +} +@(objc_type=MotionKeyframeData, objc_name="offset", objc_is_class_method=true) +MotionKeyframeData_offset :: #force_inline proc(self: ^MotionKeyframeData) -> NS.UInteger { + return msgSend(NS.UInteger, self, "offset") +} +@(objc_type=MotionKeyframeData, objc_name="setOffset", objc_is_class_method=true) +MotionKeyframeData_setOffset :: #force_inline proc(self: ^MotionKeyframeData, offset: NS.UInteger) { + msgSend(nil, self, "setOffset:", offset) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + AccelerationStructureMotionTriangleGeometryDescriptor +*/ + +@(objc_class="MTLAccelerationStructureMotionTriangleGeometryDescriptor") +AccelerationStructureMotionTriangleGeometryDescriptor :: struct { using _: NS.Copying(AccelerationStructureMotionTriangleGeometryDescriptor), using _: AccelerationStructureGeometryDescriptor } + +@(objc_type=AccelerationStructureMotionTriangleGeometryDescriptor, objc_name="alloc", objc_is_class_method=true) +AccelerationStructureMotionTriangleGeometryDescriptor_alloc :: #force_inline proc() -> ^AccelerationStructureMotionTriangleGeometryDescriptor { + return msgSend(^AccelerationStructureMotionTriangleGeometryDescriptor, AccelerationStructureMotionTriangleGeometryDescriptor, "alloc") +} +@(objc_type=AccelerationStructureMotionTriangleGeometryDescriptor, objc_name="init") +AccelerationStructureMotionTriangleGeometryDescriptor_init :: #force_inline proc(self: ^AccelerationStructureMotionTriangleGeometryDescriptor) -> ^AccelerationStructureMotionTriangleGeometryDescriptor { + return msgSend(^AccelerationStructureMotionTriangleGeometryDescriptor, self, "init") +} + +@(objc_type=AccelerationStructureMotionTriangleGeometryDescriptor, objc_name="vertexBuffers") +AccelerationStructureMotionTriangleGeometryDescriptor_vertexBuffers :: #force_inline proc(self: ^AccelerationStructureMotionTriangleGeometryDescriptor) -> ^NS.Array { + return msgSend(^NS.Array, self, "vertexBuffers") +} +@(objc_type=AccelerationStructureMotionTriangleGeometryDescriptor, objc_name="setVertexBuffers") +AccelerationStructureMotionTriangleGeometryDescriptor_setVertexBuffers :: #force_inline proc(self: ^AccelerationStructureMotionTriangleGeometryDescriptor, buffers: ^NS.Array) { + msgSend(nil, self, "setVertexBuffers:", buffers) +} + +@(objc_type=AccelerationStructureMotionTriangleGeometryDescriptor, objc_name="vertexStride") +AccelerationStructureMotionTriangleGeometryDescriptor_vertexStride :: #force_inline proc(self: ^AccelerationStructureMotionTriangleGeometryDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "vertexStride") +} +@(objc_type=AccelerationStructureMotionTriangleGeometryDescriptor, objc_name="setVertexStride") +AccelerationStructureMotionTriangleGeometryDescriptor_setVertexStride :: #force_inline proc(self: ^AccelerationStructureMotionTriangleGeometryDescriptor, stride: NS.UInteger) { + msgSend(nil, self, "setVertexStride:", stride) +} + + +@(objc_type=AccelerationStructureMotionTriangleGeometryDescriptor, objc_name="indexBuffer") +AccelerationStructureMotionTriangleGeometryDescriptor_indexBuffer :: #force_inline proc(self: ^AccelerationStructureMotionTriangleGeometryDescriptor) -> ^Buffer { + return msgSend(^Buffer, self, "indexBuffer") +} +@(objc_type=AccelerationStructureMotionTriangleGeometryDescriptor, objc_name="setIndexBuffer") +AccelerationStructureMotionTriangleGeometryDescriptor_setIndexBuffer :: #force_inline proc(self: ^AccelerationStructureMotionTriangleGeometryDescriptor, buffer: ^Buffer) { + msgSend(nil, self, "setIndexBuffer:", buffer) +} + + +@(objc_type=AccelerationStructureMotionTriangleGeometryDescriptor, objc_name="indexBufferOffset") +AccelerationStructureMotionTriangleGeometryDescriptor_indexBufferOffset :: #force_inline proc(self: ^AccelerationStructureMotionTriangleGeometryDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "indexBufferOffset") +} +@(objc_type=AccelerationStructureMotionTriangleGeometryDescriptor, objc_name="setIndexBufferOffset") +AccelerationStructureMotionTriangleGeometryDescriptor_setIndexBufferOffset :: #force_inline proc(self: ^AccelerationStructureMotionTriangleGeometryDescriptor, offset: NS.UInteger) { + msgSend(nil, self, "setIndexBufferOffset:", offset) +} + +@(objc_type=AccelerationStructureMotionTriangleGeometryDescriptor, objc_name="indexType") +AccelerationStructureMotionTriangleGeometryDescriptor_indexType :: #force_inline proc(self: ^AccelerationStructureMotionTriangleGeometryDescriptor) -> IndexType { + return msgSend(IndexType, self, "indexType") +} +@(objc_type=AccelerationStructureMotionTriangleGeometryDescriptor, objc_name="setIndexType") +AccelerationStructureMotionTriangleGeometryDescriptor_setIndexType :: #force_inline proc(self: ^AccelerationStructureMotionTriangleGeometryDescriptor, indexType: IndexType) { + msgSend(nil, self, "setIndexType:", indexType) +} + +@(objc_type=AccelerationStructureMotionTriangleGeometryDescriptor, objc_name="triangleCount") +AccelerationStructureMotionTriangleGeometryDescriptor_triangleCount :: #force_inline proc(self: ^AccelerationStructureMotionTriangleGeometryDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "triangleCount") +} +@(objc_type=AccelerationStructureMotionTriangleGeometryDescriptor, objc_name="setTriangleCount") +AccelerationStructureMotionTriangleGeometryDescriptor_setTriangleCount :: #force_inline proc(self: ^AccelerationStructureMotionTriangleGeometryDescriptor, count: NS.UInteger) { + msgSend(nil, self, "setTriangleCount:", count) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + AccelerationStructureMotionBoundingBoxGeometryDescriptor +*/ + +@(objc_class="MTLAccelerationStructureMotionBoundingBoxGeometryDescriptor") +AccelerationStructureMotionBoundingBoxGeometryDescriptor :: struct { using _: NS.Copying(AccelerationStructureMotionBoundingBoxGeometryDescriptor), using _: AccelerationStructureGeometryDescriptor } + +@(objc_type=AccelerationStructureMotionBoundingBoxGeometryDescriptor, objc_name="alloc", objc_is_class_method=true) +AccelerationStructureMotionBoundingBoxGeometryDescriptor_alloc :: #force_inline proc() -> ^AccelerationStructureMotionBoundingBoxGeometryDescriptor { + return msgSend(^AccelerationStructureMotionBoundingBoxGeometryDescriptor, AccelerationStructureMotionBoundingBoxGeometryDescriptor, "alloc") +} +@(objc_type=AccelerationStructureMotionBoundingBoxGeometryDescriptor, objc_name="init") +AccelerationStructureMotionBoundingBoxGeometryDescriptor_init :: #force_inline proc(self: ^AccelerationStructureMotionBoundingBoxGeometryDescriptor) -> ^AccelerationStructureMotionBoundingBoxGeometryDescriptor { + return msgSend(^AccelerationStructureMotionBoundingBoxGeometryDescriptor, self, "init") +} + +@(objc_type=AccelerationStructureMotionBoundingBoxGeometryDescriptor, objc_name="descriptor", objc_is_class_method=true) +AccelerationStructureMotionBoundingBoxGeometryDescriptor_descriptor :: #force_inline proc() -> ^AccelerationStructureMotionBoundingBoxGeometryDescriptor { + return msgSend(^AccelerationStructureMotionBoundingBoxGeometryDescriptor, AccelerationStructureMotionBoundingBoxGeometryDescriptor, "descriptor") +} + +@(objc_type=AccelerationStructureMotionBoundingBoxGeometryDescriptor, objc_name="boundingBoxBuffers") +AccelerationStructureMotionBoundingBoxGeometryDescriptor_boundingBoxBuffers :: #force_inline proc(self: ^AccelerationStructureMotionBoundingBoxGeometryDescriptor) -> ^NS.Array { + return msgSend(^NS.Array, self, "boundingBoxBuffers") +} +@(objc_type=AccelerationStructureMotionBoundingBoxGeometryDescriptor, objc_name="setBoundBoxBuffers") +AccelerationStructureMotionBoundingBoxGeometryDescriptor_setBoundBoxBuffers :: #force_inline proc(self: ^AccelerationStructureMotionBoundingBoxGeometryDescriptor, buffers: ^NS.Array) { + msgSend(nil, self, "setBoundBoxBuffers:", buffers) +} + +@(objc_type=AccelerationStructureMotionBoundingBoxGeometryDescriptor, objc_name="boundingBoxStride") +AccelerationStructureMotionBoundingBoxGeometryDescriptor_boundingBoxStride :: #force_inline proc(self: ^AccelerationStructureMotionBoundingBoxGeometryDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "boundingBoxStride") +} +@(objc_type=AccelerationStructureMotionBoundingBoxGeometryDescriptor, objc_name="setBoundingBoxStride") +AccelerationStructureMotionBoundingBoxGeometryDescriptor_setBoundingBoxStride :: #force_inline proc(self: ^AccelerationStructureMotionBoundingBoxGeometryDescriptor, stride: NS.UInteger) { + msgSend(nil, self, "setBoundingBoxStride:", stride) +} + +@(objc_type=AccelerationStructureMotionBoundingBoxGeometryDescriptor, objc_name="boundingBoxCount") +AccelerationStructureMotionBoundingBoxGeometryDescriptor_boundingBoxCount :: #force_inline proc(self: ^AccelerationStructureMotionBoundingBoxGeometryDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "boundingBoxCount") +} +@(objc_type=AccelerationStructureMotionBoundingBoxGeometryDescriptor, objc_name="setBoundingBoxCount") +AccelerationStructureMotionBoundingBoxGeometryDescriptor_setBoundingBoxCount :: #force_inline proc(self: ^AccelerationStructureMotionBoundingBoxGeometryDescriptor, offset: NS.UInteger) { + msgSend(nil, self, "setBoundingBoxCount:", offset) +} + + + + + + + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + AccelerationStructureDescriptor +Class Methods: + alloc +Methods: + init + setUsage + usage +*/ +@(objc_class="MTLAccelerationStructureDescriptor") +AccelerationStructureDescriptor :: struct { using _: NS.Copying(AccelerationStructureDescriptor) } + +@(objc_type=AccelerationStructureDescriptor, objc_name="alloc", objc_is_class_method=true) +AccelerationStructureDescriptor_alloc :: #force_inline proc() -> ^AccelerationStructureDescriptor { + return msgSend(^AccelerationStructureDescriptor, AccelerationStructureDescriptor, "alloc") +} +@(objc_type=AccelerationStructureDescriptor, objc_name="init") +AccelerationStructureDescriptor_init :: #force_inline proc(self: ^AccelerationStructureDescriptor) -> ^AccelerationStructureDescriptor { + return msgSend(^AccelerationStructureDescriptor, self, "init") +} +@(objc_type=AccelerationStructureDescriptor, objc_name="setUsage") +AccelerationStructureDescriptor_setUsage :: #force_inline proc(self: ^AccelerationStructureDescriptor, usage: AccelerationStructureUsage) { + msgSend(nil, self, "setUsage:", usage) +} +@(objc_type=AccelerationStructureDescriptor, objc_name="usage") +AccelerationStructureDescriptor_usage :: #force_inline proc(self: ^AccelerationStructureDescriptor) -> AccelerationStructureUsage { + return msgSend(AccelerationStructureUsage, self, "usage") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + AccelerationStructureGeometryDescriptor +Class Methods: + alloc +Methods: + init + allowDuplicateIntersectionFunctionInvocation + intersectionFunctionTableOffset + opaque + setAllowDuplicateIntersectionFunctionInvocation + setIntersectionFunctionTableOffset + setOpaque +*/ +@(objc_class="MTLAccelerationStructureGeometryDescriptor") +AccelerationStructureGeometryDescriptor :: struct { using _: NS.Copying(AccelerationStructureGeometryDescriptor) } + +@(objc_type=AccelerationStructureGeometryDescriptor, objc_name="alloc", objc_is_class_method=true) +AccelerationStructureGeometryDescriptor_alloc :: #force_inline proc() -> ^AccelerationStructureGeometryDescriptor { + return msgSend(^AccelerationStructureGeometryDescriptor, AccelerationStructureGeometryDescriptor, "alloc") +} +@(objc_type=AccelerationStructureGeometryDescriptor, objc_name="init") +AccelerationStructureGeometryDescriptor_init :: #force_inline proc(self: ^AccelerationStructureGeometryDescriptor) -> ^AccelerationStructureGeometryDescriptor { + return msgSend(^AccelerationStructureGeometryDescriptor, self, "init") +} +@(objc_type=AccelerationStructureGeometryDescriptor, objc_name="allowDuplicateIntersectionFunctionInvocation") +AccelerationStructureGeometryDescriptor_allowDuplicateIntersectionFunctionInvocation :: #force_inline proc(self: ^AccelerationStructureGeometryDescriptor) -> BOOL { + return msgSend(BOOL, self, "allowDuplicateIntersectionFunctionInvocation") +} +@(objc_type=AccelerationStructureGeometryDescriptor, objc_name="intersectionFunctionTableOffset") +AccelerationStructureGeometryDescriptor_intersectionFunctionTableOffset :: #force_inline proc(self: ^AccelerationStructureGeometryDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "intersectionFunctionTableOffset") +} +@(objc_type=AccelerationStructureGeometryDescriptor, objc_name="opaque") +AccelerationStructureGeometryDescriptor_opaque :: #force_inline proc(self: ^AccelerationStructureGeometryDescriptor) -> BOOL { + return msgSend(BOOL, self, "opaque") +} +@(objc_type=AccelerationStructureGeometryDescriptor, objc_name="setAllowDuplicateIntersectionFunctionInvocation") +AccelerationStructureGeometryDescriptor_setAllowDuplicateIntersectionFunctionInvocation :: #force_inline proc(self: ^AccelerationStructureGeometryDescriptor, allowDuplicateIntersectionFunctionInvocation: BOOL) { + msgSend(nil, self, "setAllowDuplicateIntersectionFunctionInvocation:", allowDuplicateIntersectionFunctionInvocation) +} +@(objc_type=AccelerationStructureGeometryDescriptor, objc_name="setIntersectionFunctionTableOffset") +AccelerationStructureGeometryDescriptor_setIntersectionFunctionTableOffset :: #force_inline proc(self: ^AccelerationStructureGeometryDescriptor, intersectionFunctionTableOffset: NS.UInteger) { + msgSend(nil, self, "setIntersectionFunctionTableOffset:", intersectionFunctionTableOffset) +} +@(objc_type=AccelerationStructureGeometryDescriptor, objc_name="setOpaque") +AccelerationStructureGeometryDescriptor_setOpaque :: #force_inline proc(self: ^AccelerationStructureGeometryDescriptor, opaque: BOOL) { + msgSend(nil, self, "setOpaque:", opaque) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + AccelerationStructureTriangleGeometryDescriptor +Class Methods: + alloc + descriptor +Methods: + init + indexBuffer + indexBufferOffset + indexType + setIndexBuffer + setIndexBufferOffset + setIndexType + setTriangleCount + setVertexBuffer + setVertexBufferOffset + setVertexStride + triangleCount + vertexBuffer + vertexBufferOffset + vertexStride +*/ +@(objc_class="MTLAccelerationStructureTriangleGeometryDescriptor") +AccelerationStructureTriangleGeometryDescriptor :: struct { using _: NS.Copying(AccelerationStructureTriangleGeometryDescriptor), using _: AccelerationStructureDescriptor } + +@(objc_type=AccelerationStructureTriangleGeometryDescriptor, objc_name="alloc", objc_is_class_method=true) +AccelerationStructureTriangleGeometryDescriptor_alloc :: #force_inline proc() -> ^AccelerationStructureTriangleGeometryDescriptor { + return msgSend(^AccelerationStructureTriangleGeometryDescriptor, AccelerationStructureTriangleGeometryDescriptor, "alloc") +} +@(objc_type=AccelerationStructureTriangleGeometryDescriptor, objc_name="init") +AccelerationStructureTriangleGeometryDescriptor_init :: #force_inline proc(self: ^AccelerationStructureTriangleGeometryDescriptor) -> ^AccelerationStructureTriangleGeometryDescriptor { + return msgSend(^AccelerationStructureTriangleGeometryDescriptor, self, "init") +} +@(objc_type=AccelerationStructureTriangleGeometryDescriptor, objc_name="descriptor", objc_is_class_method=true) +AccelerationStructureTriangleGeometryDescriptor_descriptor :: #force_inline proc() -> ^AccelerationStructureTriangleGeometryDescriptor { + return msgSend(^AccelerationStructureTriangleGeometryDescriptor, AccelerationStructureTriangleGeometryDescriptor, "descriptor") +} +@(objc_type=AccelerationStructureTriangleGeometryDescriptor, objc_name="indexBuffer") +AccelerationStructureTriangleGeometryDescriptor_indexBuffer :: #force_inline proc(self: ^AccelerationStructureTriangleGeometryDescriptor) -> ^Buffer { + return msgSend(^Buffer, self, "indexBuffer") +} +@(objc_type=AccelerationStructureTriangleGeometryDescriptor, objc_name="indexBufferOffset") +AccelerationStructureTriangleGeometryDescriptor_indexBufferOffset :: #force_inline proc(self: ^AccelerationStructureTriangleGeometryDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "indexBufferOffset") +} +@(objc_type=AccelerationStructureTriangleGeometryDescriptor, objc_name="indexType") +AccelerationStructureTriangleGeometryDescriptor_indexType :: #force_inline proc(self: ^AccelerationStructureTriangleGeometryDescriptor) -> IndexType { + return msgSend(IndexType, self, "indexType") +} +@(objc_type=AccelerationStructureTriangleGeometryDescriptor, objc_name="setIndexBuffer") +AccelerationStructureTriangleGeometryDescriptor_setIndexBuffer :: #force_inline proc(self: ^AccelerationStructureTriangleGeometryDescriptor, indexBuffer: ^Buffer) { + msgSend(nil, self, "setIndexBuffer:", indexBuffer) +} +@(objc_type=AccelerationStructureTriangleGeometryDescriptor, objc_name="setIndexBufferOffset") +AccelerationStructureTriangleGeometryDescriptor_setIndexBufferOffset :: #force_inline proc(self: ^AccelerationStructureTriangleGeometryDescriptor, indexBufferOffset: NS.UInteger) { + msgSend(nil, self, "setIndexBufferOffset:", indexBufferOffset) +} +@(objc_type=AccelerationStructureTriangleGeometryDescriptor, objc_name="setIndexType") +AccelerationStructureTriangleGeometryDescriptor_setIndexType :: #force_inline proc(self: ^AccelerationStructureTriangleGeometryDescriptor, indexType: IndexType) { + msgSend(nil, self, "setIndexType:", indexType) +} +@(objc_type=AccelerationStructureTriangleGeometryDescriptor, objc_name="setTriangleCount") +AccelerationStructureTriangleGeometryDescriptor_setTriangleCount :: #force_inline proc(self: ^AccelerationStructureTriangleGeometryDescriptor, triangleCount: NS.UInteger) { + msgSend(nil, self, "setTriangleCount:", triangleCount) +} +@(objc_type=AccelerationStructureTriangleGeometryDescriptor, objc_name="setVertexBuffer") +AccelerationStructureTriangleGeometryDescriptor_setVertexBuffer :: #force_inline proc(self: ^AccelerationStructureTriangleGeometryDescriptor, vertexBuffer: ^Buffer) { + msgSend(nil, self, "setVertexBuffer:", vertexBuffer) +} +@(objc_type=AccelerationStructureTriangleGeometryDescriptor, objc_name="setVertexBufferOffset") +AccelerationStructureTriangleGeometryDescriptor_setVertexBufferOffset :: #force_inline proc(self: ^AccelerationStructureTriangleGeometryDescriptor, vertexBufferOffset: NS.UInteger) { + msgSend(nil, self, "setVertexBufferOffset:", vertexBufferOffset) +} +@(objc_type=AccelerationStructureTriangleGeometryDescriptor, objc_name="setVertexStride") +AccelerationStructureTriangleGeometryDescriptor_setVertexStride :: #force_inline proc(self: ^AccelerationStructureTriangleGeometryDescriptor, vertexStride: NS.UInteger) { + msgSend(nil, self, "setVertexStride:", vertexStride) +} +@(objc_type=AccelerationStructureTriangleGeometryDescriptor, objc_name="triangleCount") +AccelerationStructureTriangleGeometryDescriptor_triangleCount :: #force_inline proc(self: ^AccelerationStructureTriangleGeometryDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "triangleCount") +} +@(objc_type=AccelerationStructureTriangleGeometryDescriptor, objc_name="vertexBuffer") +AccelerationStructureTriangleGeometryDescriptor_vertexBuffer :: #force_inline proc(self: ^AccelerationStructureTriangleGeometryDescriptor) -> ^Buffer { + return msgSend(^Buffer, self, "vertexBuffer") +} +@(objc_type=AccelerationStructureTriangleGeometryDescriptor, objc_name="vertexBufferOffset") +AccelerationStructureTriangleGeometryDescriptor_vertexBufferOffset :: #force_inline proc(self: ^AccelerationStructureTriangleGeometryDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "vertexBufferOffset") +} +@(objc_type=AccelerationStructureTriangleGeometryDescriptor, objc_name="vertexStride") +AccelerationStructureTriangleGeometryDescriptor_vertexStride :: #force_inline proc(self: ^AccelerationStructureTriangleGeometryDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "vertexStride") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + Argument +Class Methods: + alloc +Methods: + init + access + arrayLength + bufferAlignment + bufferDataSize + bufferDataType + bufferPointerType + bufferStructType + index + isActive + isDepthTexture + name + textureDataType + textureType + threadgroupMemoryAlignment + threadgroupMemoryDataSize + type +*/ +@(objc_class="MTLArgument") +Argument :: struct { using _: NS.Object } + +@(objc_type=Argument, objc_name="alloc", objc_is_class_method=true) +Argument_alloc :: #force_inline proc() -> ^Argument { + return msgSend(^Argument, Argument, "alloc") +} +@(objc_type=Argument, objc_name="init") +Argument_init :: #force_inline proc(self: ^Argument) -> ^Argument { + return msgSend(^Argument, self, "init") +} +@(objc_type=Argument, objc_name="access") +Argument_access :: #force_inline proc(self: ^Argument) -> ArgumentAccess { + return msgSend(ArgumentAccess, self, "access") +} +@(objc_type=Argument, objc_name="arrayLength") +Argument_arrayLength :: #force_inline proc(self: ^Argument) -> NS.UInteger { + return msgSend(NS.UInteger, self, "arrayLength") +} +@(objc_type=Argument, objc_name="bufferAlignment") +Argument_bufferAlignment :: #force_inline proc(self: ^Argument) -> NS.UInteger { + return msgSend(NS.UInteger, self, "bufferAlignment") +} +@(objc_type=Argument, objc_name="bufferDataSize") +Argument_bufferDataSize :: #force_inline proc(self: ^Argument) -> NS.UInteger { + return msgSend(NS.UInteger, self, "bufferDataSize") +} +@(objc_type=Argument, objc_name="bufferDataType") +Argument_bufferDataType :: #force_inline proc(self: ^Argument) -> DataType { + return msgSend(DataType, self, "bufferDataType") +} +@(objc_type=Argument, objc_name="bufferPointerType") +Argument_bufferPointerType :: #force_inline proc(self: ^Argument) -> ^PointerType { + return msgSend(^PointerType, self, "bufferPointerType") +} +@(objc_type=Argument, objc_name="bufferStructType") +Argument_bufferStructType :: #force_inline proc(self: ^Argument) -> ^StructType { + return msgSend(^StructType, self, "bufferStructType") +} +@(objc_type=Argument, objc_name="index") +Argument_index :: #force_inline proc(self: ^Argument) -> NS.UInteger { + return msgSend(NS.UInteger, self, "index") +} +@(objc_type=Argument, objc_name="isActive") +Argument_isActive :: #force_inline proc(self: ^Argument) -> BOOL { + return msgSend(BOOL, self, "isActive") +} +@(objc_type=Argument, objc_name="isDepthTexture") +Argument_isDepthTexture :: #force_inline proc(self: ^Argument) -> BOOL { + return msgSend(BOOL, self, "isDepthTexture") +} +@(objc_type=Argument, objc_name="name") +Argument_name :: #force_inline proc(self: ^Argument) -> ^NS.String { + return msgSend(^NS.String, self, "name") +} +@(objc_type=Argument, objc_name="textureDataType") +Argument_textureDataType :: #force_inline proc(self: ^Argument) -> DataType { + return msgSend(DataType, self, "textureDataType") +} +@(objc_type=Argument, objc_name="textureType") +Argument_textureType :: #force_inline proc(self: ^Argument) -> TextureType { + return msgSend(TextureType, self, "textureType") +} +@(objc_type=Argument, objc_name="threadgroupMemoryAlignment") +Argument_threadgroupMemoryAlignment :: #force_inline proc(self: ^Argument) -> NS.UInteger { + return msgSend(NS.UInteger, self, "threadgroupMemoryAlignment") +} +@(objc_type=Argument, objc_name="threadgroupMemoryDataSize") +Argument_threadgroupMemoryDataSize :: #force_inline proc(self: ^Argument) -> NS.UInteger { + return msgSend(NS.UInteger, self, "threadgroupMemoryDataSize") +} +@(objc_type=Argument, objc_name="type") +Argument_type :: #force_inline proc(self: ^Argument) -> ArgumentType { + return msgSend(ArgumentType, self, "type") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + ArgumentDescriptor +Class Methods: + alloc + argumentDescriptor +Methods: + init + access + arrayLength + constantBlockAlignment + dataType + index + setAccess + setArrayLength + setConstantBlockAlignment + setDataType + setIndex + setTextureType + textureType +*/ +@(objc_class="MTLArgumentDescriptor") +ArgumentDescriptor :: struct { using _: NS.Copying(ArgumentDescriptor) } + +@(objc_type=ArgumentDescriptor, objc_name="alloc", objc_is_class_method=true) +ArgumentDescriptor_alloc :: #force_inline proc() -> ^ArgumentDescriptor { + return msgSend(^ArgumentDescriptor, ArgumentDescriptor, "alloc") +} +@(objc_type=ArgumentDescriptor, objc_name="init") +ArgumentDescriptor_init :: #force_inline proc(self: ^ArgumentDescriptor) -> ^ArgumentDescriptor { + return msgSend(^ArgumentDescriptor, self, "init") +} +@(objc_type=ArgumentDescriptor, objc_name="access") +ArgumentDescriptor_access :: #force_inline proc(self: ^ArgumentDescriptor) -> ArgumentAccess { + return msgSend(ArgumentAccess, self, "access") +} +@(objc_type=ArgumentDescriptor, objc_name="argumentDescriptor", objc_is_class_method=true) +ArgumentDescriptor_argumentDescriptor :: #force_inline proc() -> ^ArgumentDescriptor { + return msgSend(^ArgumentDescriptor, ArgumentDescriptor, "argumentDescriptor") +} +@(objc_type=ArgumentDescriptor, objc_name="arrayLength") +ArgumentDescriptor_arrayLength :: #force_inline proc(self: ^ArgumentDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "arrayLength") +} +@(objc_type=ArgumentDescriptor, objc_name="constantBlockAlignment") +ArgumentDescriptor_constantBlockAlignment :: #force_inline proc(self: ^ArgumentDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "constantBlockAlignment") +} +@(objc_type=ArgumentDescriptor, objc_name="dataType") +ArgumentDescriptor_dataType :: #force_inline proc(self: ^ArgumentDescriptor) -> DataType { + return msgSend(DataType, self, "dataType") +} +@(objc_type=ArgumentDescriptor, objc_name="index") +ArgumentDescriptor_index :: #force_inline proc(self: ^ArgumentDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "index") +} +@(objc_type=ArgumentDescriptor, objc_name="setAccess") +ArgumentDescriptor_setAccess :: #force_inline proc(self: ^ArgumentDescriptor, access: ArgumentAccess) { + msgSend(nil, self, "setAccess:", access) +} +@(objc_type=ArgumentDescriptor, objc_name="setArrayLength") +ArgumentDescriptor_setArrayLength :: #force_inline proc(self: ^ArgumentDescriptor, arrayLength: NS.UInteger) { + msgSend(nil, self, "setArrayLength:", arrayLength) +} +@(objc_type=ArgumentDescriptor, objc_name="setConstantBlockAlignment") +ArgumentDescriptor_setConstantBlockAlignment :: #force_inline proc(self: ^ArgumentDescriptor, constantBlockAlignment: NS.UInteger) { + msgSend(nil, self, "setConstantBlockAlignment:", constantBlockAlignment) +} +@(objc_type=ArgumentDescriptor, objc_name="setDataType") +ArgumentDescriptor_setDataType :: #force_inline proc(self: ^ArgumentDescriptor, dataType: DataType) { + msgSend(nil, self, "setDataType:", dataType) +} +@(objc_type=ArgumentDescriptor, objc_name="setIndex") +ArgumentDescriptor_setIndex :: #force_inline proc(self: ^ArgumentDescriptor, index: NS.UInteger) { + msgSend(nil, self, "setIndex:", index) +} +@(objc_type=ArgumentDescriptor, objc_name="setTextureType") +ArgumentDescriptor_setTextureType :: #force_inline proc(self: ^ArgumentDescriptor, textureType: TextureType) { + msgSend(nil, self, "setTextureType:", textureType) +} +@(objc_type=ArgumentDescriptor, objc_name="textureType") +ArgumentDescriptor_textureType :: #force_inline proc(self: ^ArgumentDescriptor) -> TextureType { + return msgSend(TextureType, self, "textureType") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + ArrayType +Class Methods: + alloc +Methods: + init + argumentIndexStride + arrayLength + elementArrayType + elementPointerType + elementStructType + elementTextureReferenceType + elementType + stride +*/ +@(objc_class="MTLArrayType") +ArrayType :: struct { using _: Type } + +@(objc_type=ArrayType, objc_name="alloc", objc_is_class_method=true) +ArrayType_alloc :: #force_inline proc() -> ^ArrayType { + return msgSend(^ArrayType, ArrayType, "alloc") +} +@(objc_type=ArrayType, objc_name="init") +ArrayType_init :: #force_inline proc(self: ^ArrayType) -> ^ArrayType { + return msgSend(^ArrayType, self, "init") +} +@(objc_type=ArrayType, objc_name="argumentIndexStride") +ArrayType_argumentIndexStride :: #force_inline proc(self: ^ArrayType) -> NS.UInteger { + return msgSend(NS.UInteger, self, "argumentIndexStride") +} +@(objc_type=ArrayType, objc_name="arrayLength") +ArrayType_arrayLength :: #force_inline proc(self: ^ArrayType) -> NS.UInteger { + return msgSend(NS.UInteger, self, "arrayLength") +} +@(objc_type=ArrayType, objc_name="elementArrayType") +ArrayType_elementArrayType :: #force_inline proc(self: ^ArrayType) -> ^ArrayType { + return msgSend(^ArrayType, self, "elementArrayType") +} +@(objc_type=ArrayType, objc_name="elementPointerType") +ArrayType_elementPointerType :: #force_inline proc(self: ^ArrayType) -> ^PointerType { + return msgSend(^PointerType, self, "elementPointerType") +} +@(objc_type=ArrayType, objc_name="elementStructType") +ArrayType_elementStructType :: #force_inline proc(self: ^ArrayType) -> ^StructType { + return msgSend(^StructType, self, "elementStructType") +} +@(objc_type=ArrayType, objc_name="elementTextureReferenceType") +ArrayType_elementTextureReferenceType :: #force_inline proc(self: ^ArrayType) -> ^TextureReferenceType { + return msgSend(^TextureReferenceType, self, "elementTextureReferenceType") +} +@(objc_type=ArrayType, objc_name="elementType") +ArrayType_elementType :: #force_inline proc(self: ^ArrayType) -> DataType { + return msgSend(DataType, self, "elementType") +} +@(objc_type=ArrayType, objc_name="stride") +ArrayType_stride :: #force_inline proc(self: ^ArrayType) -> NS.UInteger { + return msgSend(NS.UInteger, self, "stride") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + Attribute +Class Methods: + alloc +Methods: + init + attributeIndex + attributeType + isActive + isPatchControlPointData + isPatchData + name +*/ +@(objc_class="MTLAttribute") +Attribute :: struct { using _: NS.Object } + +@(objc_type=Attribute, objc_name="alloc", objc_is_class_method=true) +Attribute_alloc :: #force_inline proc() -> ^Attribute { + return msgSend(^Attribute, Attribute, "alloc") +} +@(objc_type=Attribute, objc_name="init") +Attribute_init :: #force_inline proc(self: ^Attribute) -> ^Attribute { + return msgSend(^Attribute, self, "init") +} +@(objc_type=Attribute, objc_name="attributeIndex") +Attribute_attributeIndex :: #force_inline proc(self: ^Attribute) -> NS.UInteger { + return msgSend(NS.UInteger, self, "attributeIndex") +} +@(objc_type=Attribute, objc_name="attributeType") +Attribute_attributeType :: #force_inline proc(self: ^Attribute) -> DataType { + return msgSend(DataType, self, "attributeType") +} +@(objc_type=Attribute, objc_name="isActive") +Attribute_isActive :: #force_inline proc(self: ^Attribute) -> BOOL { + return msgSend(BOOL, self, "isActive") +} +@(objc_type=Attribute, objc_name="isPatchControlPointData") +Attribute_isPatchControlPointData :: #force_inline proc(self: ^Attribute) -> BOOL { + return msgSend(BOOL, self, "isPatchControlPointData") +} +@(objc_type=Attribute, objc_name="isPatchData") +Attribute_isPatchData :: #force_inline proc(self: ^Attribute) -> BOOL { + return msgSend(BOOL, self, "isPatchData") +} +@(objc_type=Attribute, objc_name="name") +Attribute_name :: #force_inline proc(self: ^Attribute) -> ^NS.String { + return msgSend(^NS.String, self, "name") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + AttributeDescriptor +Class Methods: + alloc +Methods: + init + bufferIndex + format + offset + setBufferIndex + setFormat + setOffset +*/ +@(objc_class="MTLAttributeDescriptor") +AttributeDescriptor :: struct { using _: NS.Copying(AttributeDescriptor) } + +@(objc_type=AttributeDescriptor, objc_name="alloc", objc_is_class_method=true) +AttributeDescriptor_alloc :: #force_inline proc() -> ^AttributeDescriptor { + return msgSend(^AttributeDescriptor, AttributeDescriptor, "alloc") +} +@(objc_type=AttributeDescriptor, objc_name="init") +AttributeDescriptor_init :: #force_inline proc(self: ^AttributeDescriptor) -> ^AttributeDescriptor { + return msgSend(^AttributeDescriptor, self, "init") +} +@(objc_type=AttributeDescriptor, objc_name="bufferIndex") +AttributeDescriptor_bufferIndex :: #force_inline proc(self: ^AttributeDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "bufferIndex") +} +@(objc_type=AttributeDescriptor, objc_name="format") +AttributeDescriptor_format :: #force_inline proc(self: ^AttributeDescriptor) -> AttributeFormat { + return msgSend(AttributeFormat, self, "format") +} +@(objc_type=AttributeDescriptor, objc_name="offset") +AttributeDescriptor_offset :: #force_inline proc(self: ^AttributeDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "offset") +} +@(objc_type=AttributeDescriptor, objc_name="setBufferIndex") +AttributeDescriptor_setBufferIndex :: #force_inline proc(self: ^AttributeDescriptor, bufferIndex: NS.UInteger) { + msgSend(nil, self, "setBufferIndex:", bufferIndex) +} +@(objc_type=AttributeDescriptor, objc_name="setFormat") +AttributeDescriptor_setFormat :: #force_inline proc(self: ^AttributeDescriptor, format: AttributeFormat) { + msgSend(nil, self, "setFormat:", format) +} +@(objc_type=AttributeDescriptor, objc_name="setOffset") +AttributeDescriptor_setOffset :: #force_inline proc(self: ^AttributeDescriptor, offset: NS.UInteger) { + msgSend(nil, self, "setOffset:", offset) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + AttributeDescriptorArray +Class Methods: + alloc +Methods: + init + objectAtIndexedSubscript + setObject +*/ +@(objc_class="MTLAttributeDescriptorArray") +AttributeDescriptorArray :: struct { using _: NS.Object } + +@(objc_type=AttributeDescriptorArray, objc_name="alloc", objc_is_class_method=true) +AttributeDescriptorArray_alloc :: #force_inline proc() -> ^AttributeDescriptorArray { + return msgSend(^AttributeDescriptorArray, AttributeDescriptorArray, "alloc") +} +@(objc_type=AttributeDescriptorArray, objc_name="init") +AttributeDescriptorArray_init :: #force_inline proc(self: ^AttributeDescriptorArray) -> ^AttributeDescriptorArray { + return msgSend(^AttributeDescriptorArray, self, "init") +} +@(objc_type=AttributeDescriptorArray, objc_name="object") +AttributeDescriptorArray_object :: #force_inline proc(self: ^AttributeDescriptorArray, index: NS.UInteger) -> ^AttributeDescriptor { + return msgSend(^AttributeDescriptor, self, "objectAtIndexedSubscript:", index) +} +@(objc_type=AttributeDescriptorArray, objc_name="setObject") +AttributeDescriptorArray_setObject :: #force_inline proc(self: ^AttributeDescriptorArray, attributeDesc: ^AttributeDescriptor, index: NS.UInteger) { + msgSend(nil, self, "setObject:atIndexedSubscript:", attributeDesc, index) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + BinaryArchiveDescriptor +Class Methods: + alloc +Methods: + init + setUrl + url +*/ +@(objc_class="MTLBinaryArchiveDescriptor") +BinaryArchiveDescriptor :: struct { using _: NS.Copying(BinaryArchiveDescriptor) } + +@(objc_type=BinaryArchiveDescriptor, objc_name="alloc", objc_is_class_method=true) +BinaryArchiveDescriptor_alloc :: #force_inline proc() -> ^BinaryArchiveDescriptor { + return msgSend(^BinaryArchiveDescriptor, BinaryArchiveDescriptor, "alloc") +} +@(objc_type=BinaryArchiveDescriptor, objc_name="init") +BinaryArchiveDescriptor_init :: #force_inline proc(self: ^BinaryArchiveDescriptor) -> ^BinaryArchiveDescriptor { + return msgSend(^BinaryArchiveDescriptor, self, "init") +} +@(objc_type=BinaryArchiveDescriptor, objc_name="setUrl") +BinaryArchiveDescriptor_setUrl :: #force_inline proc(self: ^BinaryArchiveDescriptor, url: ^NS.URL) { + msgSend(nil, self, "setUrl:", url) +} +@(objc_type=BinaryArchiveDescriptor, objc_name="url") +BinaryArchiveDescriptor_url :: #force_inline proc(self: ^BinaryArchiveDescriptor) -> ^NS.URL { + return msgSend(^NS.URL, self, "url") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + BlitPassDescriptor +Class Methods: + alloc + blitPassDescriptor +Methods: + init + sampleBufferAttachments +*/ +@(objc_class="MTLBlitPassDescriptor") +BlitPassDescriptor :: struct { using _: NS.Copying(BlitPassDescriptor) } + +@(objc_type=BlitPassDescriptor, objc_name="alloc", objc_is_class_method=true) +BlitPassDescriptor_alloc :: #force_inline proc() -> ^BlitPassDescriptor { + return msgSend(^BlitPassDescriptor, BlitPassDescriptor, "alloc") +} +@(objc_type=BlitPassDescriptor, objc_name="init") +BlitPassDescriptor_init :: #force_inline proc(self: ^BlitPassDescriptor) -> ^BlitPassDescriptor { + return msgSend(^BlitPassDescriptor, self, "init") +} +@(objc_type=BlitPassDescriptor, objc_name="blitPassDescriptor", objc_is_class_method=true) +BlitPassDescriptor_blitPassDescriptor :: #force_inline proc() -> ^BlitPassDescriptor { + return msgSend(^BlitPassDescriptor, BlitPassDescriptor, "blitPassDescriptor") +} +@(objc_type=BlitPassDescriptor, objc_name="sampleBufferAttachments") +BlitPassDescriptor_sampleBufferAttachments :: #force_inline proc(self: ^BlitPassDescriptor) -> ^BlitPassSampleBufferAttachmentDescriptorArray { + return msgSend(^BlitPassSampleBufferAttachmentDescriptorArray, self, "sampleBufferAttachments") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + BlitPassSampleBufferAttachmentDescriptor +Class Methods: + alloc +Methods: + init + endOfEncoderSampleIndex + sampleBuffer + setEndOfEncoderSampleIndex + setSampleBuffer + setStartOfEncoderSampleIndex + startOfEncoderSampleIndex +*/ +@(objc_class="MTLBlitPassSampleBufferAttachmentDescriptor") +BlitPassSampleBufferAttachmentDescriptor :: struct { using _: NS.Copying(BlitPassSampleBufferAttachmentDescriptor) } + +@(objc_type=BlitPassSampleBufferAttachmentDescriptor, objc_name="alloc", objc_is_class_method=true) +BlitPassSampleBufferAttachmentDescriptor_alloc :: #force_inline proc() -> ^BlitPassSampleBufferAttachmentDescriptor { + return msgSend(^BlitPassSampleBufferAttachmentDescriptor, BlitPassSampleBufferAttachmentDescriptor, "alloc") +} +@(objc_type=BlitPassSampleBufferAttachmentDescriptor, objc_name="init") +BlitPassSampleBufferAttachmentDescriptor_init :: #force_inline proc(self: ^BlitPassSampleBufferAttachmentDescriptor) -> ^BlitPassSampleBufferAttachmentDescriptor { + return msgSend(^BlitPassSampleBufferAttachmentDescriptor, self, "init") +} +@(objc_type=BlitPassSampleBufferAttachmentDescriptor, objc_name="endOfEncoderSampleIndex") +BlitPassSampleBufferAttachmentDescriptor_endOfEncoderSampleIndex :: #force_inline proc(self: ^BlitPassSampleBufferAttachmentDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "endOfEncoderSampleIndex") +} +@(objc_type=BlitPassSampleBufferAttachmentDescriptor, objc_name="sampleBuffer") +BlitPassSampleBufferAttachmentDescriptor_sampleBuffer :: #force_inline proc(self: ^BlitPassSampleBufferAttachmentDescriptor) -> ^CounterSampleBuffer { + return msgSend(^CounterSampleBuffer, self, "sampleBuffer") +} +@(objc_type=BlitPassSampleBufferAttachmentDescriptor, objc_name="setEndOfEncoderSampleIndex") +BlitPassSampleBufferAttachmentDescriptor_setEndOfEncoderSampleIndex :: #force_inline proc(self: ^BlitPassSampleBufferAttachmentDescriptor, endOfEncoderSampleIndex: NS.UInteger) { + msgSend(nil, self, "setEndOfEncoderSampleIndex:", endOfEncoderSampleIndex) +} +@(objc_type=BlitPassSampleBufferAttachmentDescriptor, objc_name="setSampleBuffer") +BlitPassSampleBufferAttachmentDescriptor_setSampleBuffer :: #force_inline proc(self: ^BlitPassSampleBufferAttachmentDescriptor, sampleBuffer: ^CounterSampleBuffer) { + msgSend(nil, self, "setSampleBuffer:", sampleBuffer) +} +@(objc_type=BlitPassSampleBufferAttachmentDescriptor, objc_name="setStartOfEncoderSampleIndex") +BlitPassSampleBufferAttachmentDescriptor_setStartOfEncoderSampleIndex :: #force_inline proc(self: ^BlitPassSampleBufferAttachmentDescriptor, startOfEncoderSampleIndex: NS.UInteger) { + msgSend(nil, self, "setStartOfEncoderSampleIndex:", startOfEncoderSampleIndex) +} +@(objc_type=BlitPassSampleBufferAttachmentDescriptor, objc_name="startOfEncoderSampleIndex") +BlitPassSampleBufferAttachmentDescriptor_startOfEncoderSampleIndex :: #force_inline proc(self: ^BlitPassSampleBufferAttachmentDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "startOfEncoderSampleIndex") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + BlitPassSampleBufferAttachmentDescriptorArray +Class Methods: + alloc +Methods: + init + objectAtIndexedSubscript + setObject +*/ +@(objc_class="MTLBlitPassSampleBufferAttachmentDescriptorArray") +BlitPassSampleBufferAttachmentDescriptorArray :: struct { using _: NS.Object } + +@(objc_type=BlitPassSampleBufferAttachmentDescriptorArray, objc_name="alloc", objc_is_class_method=true) +BlitPassSampleBufferAttachmentDescriptorArray_alloc :: #force_inline proc() -> ^BlitPassSampleBufferAttachmentDescriptorArray { + return msgSend(^BlitPassSampleBufferAttachmentDescriptorArray, BlitPassSampleBufferAttachmentDescriptorArray, "alloc") +} +@(objc_type=BlitPassSampleBufferAttachmentDescriptorArray, objc_name="init") +BlitPassSampleBufferAttachmentDescriptorArray_init :: #force_inline proc(self: ^BlitPassSampleBufferAttachmentDescriptorArray) -> ^BlitPassSampleBufferAttachmentDescriptorArray { + return msgSend(^BlitPassSampleBufferAttachmentDescriptorArray, self, "init") +} +@(objc_type=BlitPassSampleBufferAttachmentDescriptorArray, objc_name="object") +BlitPassSampleBufferAttachmentDescriptorArray_object :: #force_inline proc(self: ^BlitPassSampleBufferAttachmentDescriptorArray, attachmentIndex: NS.UInteger) -> ^BlitPassSampleBufferAttachmentDescriptor { + return msgSend(^BlitPassSampleBufferAttachmentDescriptor, self, "objectAtIndexedSubscript:", attachmentIndex) +} +@(objc_type=BlitPassSampleBufferAttachmentDescriptorArray, objc_name="setObject") +BlitPassSampleBufferAttachmentDescriptorArray_setObject :: #force_inline proc(self: ^BlitPassSampleBufferAttachmentDescriptorArray, attachment: ^BlitPassSampleBufferAttachmentDescriptor, attachmentIndex: NS.UInteger) { + msgSend(nil, self, "setObject:atIndexedSubscript:", attachment, attachmentIndex) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + BufferLayoutDescriptor +Class Methods: + alloc +Methods: + init + setStepFunction + setStepRate + setStride + stepFunction + stepRate + stride +*/ +@(objc_class="MTLBufferLayoutDescriptor") +BufferLayoutDescriptor :: struct { using _: NS.Copying(BufferLayoutDescriptor) } + +@(objc_type=BufferLayoutDescriptor, objc_name="alloc", objc_is_class_method=true) +BufferLayoutDescriptor_alloc :: #force_inline proc() -> ^BufferLayoutDescriptor { + return msgSend(^BufferLayoutDescriptor, BufferLayoutDescriptor, "alloc") +} +@(objc_type=BufferLayoutDescriptor, objc_name="init") +BufferLayoutDescriptor_init :: #force_inline proc(self: ^BufferLayoutDescriptor) -> ^BufferLayoutDescriptor { + return msgSend(^BufferLayoutDescriptor, self, "init") +} +@(objc_type=BufferLayoutDescriptor, objc_name="setStepFunction") +BufferLayoutDescriptor_setStepFunction :: #force_inline proc(self: ^BufferLayoutDescriptor, stepFunction: StepFunction) { + msgSend(nil, self, "setStepFunction:", stepFunction) +} +@(objc_type=BufferLayoutDescriptor, objc_name="setStepRate") +BufferLayoutDescriptor_setStepRate :: #force_inline proc(self: ^BufferLayoutDescriptor, stepRate: NS.UInteger) { + msgSend(nil, self, "setStepRate:", stepRate) +} +@(objc_type=BufferLayoutDescriptor, objc_name="setStride") +BufferLayoutDescriptor_setStride :: #force_inline proc(self: ^BufferLayoutDescriptor, stride: NS.UInteger) { + msgSend(nil, self, "setStride:", stride) +} +@(objc_type=BufferLayoutDescriptor, objc_name="stepFunction") +BufferLayoutDescriptor_stepFunction :: #force_inline proc(self: ^BufferLayoutDescriptor) -> StepFunction { + return msgSend(StepFunction, self, "stepFunction") +} +@(objc_type=BufferLayoutDescriptor, objc_name="stepRate") +BufferLayoutDescriptor_stepRate :: #force_inline proc(self: ^BufferLayoutDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "stepRate") +} +@(objc_type=BufferLayoutDescriptor, objc_name="stride") +BufferLayoutDescriptor_stride :: #force_inline proc(self: ^BufferLayoutDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "stride") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + BufferLayoutDescriptorArray +Class Methods: + alloc +Methods: + init + objectAtIndexedSubscript + setObject +*/ +@(objc_class="MTLBufferLayoutDescriptorArray") +BufferLayoutDescriptorArray :: struct { using _: NS.Object } + +@(objc_type=BufferLayoutDescriptorArray, objc_name="alloc", objc_is_class_method=true) +BufferLayoutDescriptorArray_alloc :: #force_inline proc() -> ^BufferLayoutDescriptorArray { + return msgSend(^BufferLayoutDescriptorArray, BufferLayoutDescriptorArray, "alloc") +} +@(objc_type=BufferLayoutDescriptorArray, objc_name="init") +BufferLayoutDescriptorArray_init :: #force_inline proc(self: ^BufferLayoutDescriptorArray) -> ^BufferLayoutDescriptorArray { + return msgSend(^BufferLayoutDescriptorArray, self, "init") +} +@(objc_type=BufferLayoutDescriptorArray, objc_name="object") +BufferLayoutDescriptorArray_object :: #force_inline proc(self: ^BufferLayoutDescriptorArray, index: NS.UInteger) -> ^BufferLayoutDescriptor { + return msgSend(^BufferLayoutDescriptor, self, "objectAtIndexedSubscript:", index) +} +@(objc_type=BufferLayoutDescriptorArray, objc_name="setObject") +BufferLayoutDescriptorArray_setObject :: #force_inline proc(self: ^BufferLayoutDescriptorArray, bufferDesc: ^BufferLayoutDescriptor, index: NS.UInteger) { + msgSend(nil, self, "setObject:atIndexedSubscript:", bufferDesc, index) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + CaptureDescriptor +Class Methods: + alloc +Methods: + init + captureObject + destination + outputURL + setCaptureObject + setDestination + setOutputURL +*/ +@(objc_class="MTLCaptureDescriptor") +CaptureDescriptor :: struct { using _: NS.Copying(CaptureDescriptor) } + +@(objc_type=CaptureDescriptor, objc_name="alloc", objc_is_class_method=true) +CaptureDescriptor_alloc :: #force_inline proc() -> ^CaptureDescriptor { + return msgSend(^CaptureDescriptor, CaptureDescriptor, "alloc") +} +@(objc_type=CaptureDescriptor, objc_name="init") +CaptureDescriptor_init :: #force_inline proc(self: ^CaptureDescriptor) -> ^CaptureDescriptor { + return msgSend(^CaptureDescriptor, self, "init") +} +@(objc_type=CaptureDescriptor, objc_name="captureObject") +CaptureDescriptor_captureObject :: #force_inline proc(self: ^CaptureDescriptor) -> id { + return msgSend(id, self, "captureObject") +} +@(objc_type=CaptureDescriptor, objc_name="destination") +CaptureDescriptor_destination :: #force_inline proc(self: ^CaptureDescriptor) -> CaptureDestination { + return msgSend(CaptureDestination, self, "destination") +} +@(objc_type=CaptureDescriptor, objc_name="outputURL") +CaptureDescriptor_outputURL :: #force_inline proc(self: ^CaptureDescriptor) -> ^NS.URL { + return msgSend(^NS.URL, self, "outputURL") +} +@(objc_type=CaptureDescriptor, objc_name="setCaptureObject") +CaptureDescriptor_setCaptureObject :: #force_inline proc(self: ^CaptureDescriptor, captureObject: id) { + msgSend(nil, self, "setCaptureObject:", captureObject) +} +@(objc_type=CaptureDescriptor, objc_name="setDestination") +CaptureDescriptor_setDestination :: #force_inline proc(self: ^CaptureDescriptor, destination: CaptureDestination) { + msgSend(nil, self, "setDestination:", destination) +} +@(objc_type=CaptureDescriptor, objc_name="setOutputURL") +CaptureDescriptor_setOutputURL :: #force_inline proc(self: ^CaptureDescriptor, outputURL: ^NS.URL) { + msgSend(nil, self, "setOutputURL:", outputURL) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + CaptureManager +Class Methods: + alloc + sharedCaptureManager +Methods: + defaultCaptureScope + init + isCapturing + newCaptureScopeWithCommandQueue + newCaptureScopeWithDevice + setDefaultCaptureScope + startCaptureWithCommandQueue + startCaptureWithDescriptor + startCaptureWithDevice + startCaptureWithScope + stopCapture + supportsDestination +*/ +@(objc_class="MTLCaptureManager") +CaptureManager :: struct { using _: NS.Object } + +@(objc_type=CaptureManager, objc_name="alloc", objc_is_class_method=true) +CaptureManager_alloc :: #force_inline proc() -> ^CaptureManager { + return msgSend(^CaptureManager, CaptureManager, "alloc") +} +@(objc_type=CaptureManager, objc_name="defaultCaptureScope") +CaptureManager_defaultCaptureScope :: #force_inline proc(self: ^CaptureManager) -> ^CaptureManager { + return msgSend(^CaptureManager, self, "defaultCaptureScope") +} +@(objc_type=CaptureManager, objc_name="init") +CaptureManager_init :: #force_inline proc(self: ^CaptureManager) -> ^CaptureManager { + return msgSend(^CaptureManager, self, "init") +} +@(objc_type=CaptureManager, objc_name="isCapturing") +CaptureManager_isCapturing :: #force_inline proc(self: ^CaptureManager) -> BOOL { + return msgSend(BOOL, self, "isCapturing") +} +@(objc_type=CaptureManager, objc_name="newCaptureScopeWithCommandQueue") +CaptureManager_newCaptureScopeWithCommandQueue :: #force_inline proc(self: ^CaptureManager, commandQueue: ^CommandQueue) -> ^CaptureScope { + return msgSend(^CaptureScope, self, "newCaptureScopeWithCommandQueue:", commandQueue) +} +@(objc_type=CaptureManager, objc_name="newCaptureScopeWithDevice") +CaptureManager_newCaptureScopeWithDevice :: #force_inline proc(self: ^CaptureManager, device: ^Device) -> ^CaptureScope { + return msgSend(^CaptureScope, self, "newCaptureScopeWithDevice:", device) +} +@(objc_type=CaptureManager, objc_name="newCaptureScope") +CaptureManager_newCaptureScope :: proc{ + CaptureManager_newCaptureScopeWithCommandQueue, + CaptureManager_newCaptureScopeWithDevice, +} + +@(objc_type=CaptureManager, objc_name="setDefaultCaptureScope") +CaptureManager_setDefaultCaptureScope :: #force_inline proc(self: ^CaptureManager, defaultCaptureScope: ^CaptureScope) { + msgSend(nil, self, "setDefaultCaptureScope:", defaultCaptureScope) +} +@(objc_type=CaptureManager, objc_name="sharedCaptureManager", objc_is_class_method=true) +CaptureManager_sharedCaptureManager :: #force_inline proc() -> ^CaptureManager { + return msgSend(^CaptureManager, CaptureManager, "sharedCaptureManager") +} +@(objc_type=CaptureManager, objc_name="startCaptureWithCommandQueue") +CaptureManager_startCaptureWithCommandQueue :: #force_inline proc(self: ^CaptureManager, commandQueue: ^CommandQueue) { + msgSend(nil, self, "startCaptureWithCommandQueue:", commandQueue) +} +@(objc_type=CaptureManager, objc_name="startCaptureWithDescriptor") +CaptureManager_startCaptureWithDescriptor :: #force_inline proc(self: ^CaptureManager, descriptor: ^CaptureDescriptor) -> (ok: BOOL, error: ^NS.Error) { + ok = msgSend(BOOL, self, "startCaptureWithDescriptor:error:", descriptor, &error) + return +} +@(objc_type=CaptureManager, objc_name="startCaptureWithDevice") +CaptureManager_startCaptureWithDevice :: #force_inline proc(self: ^CaptureManager, device: ^Device) { + msgSend(nil, self, "startCaptureWithDevice:", device) +} +@(objc_type=CaptureManager, objc_name="startCaptureWithScope") +CaptureManager_startCaptureWithScope :: #force_inline proc(self: ^CaptureManager, captureScope: ^CaptureScope) { + msgSend(nil, self, "startCaptureWithScope:", captureScope) +} +@(objc_type=CaptureManager, objc_name="stopCapture") +CaptureManager_stopCapture :: #force_inline proc(self: ^CaptureManager) { + msgSend(nil, self, "stopCapture") +} +@(objc_type=CaptureManager, objc_name="supportsDestination") +CaptureManager_supportsDestination :: #force_inline proc(self: ^CaptureManager, destination: CaptureDestination) -> BOOL { + return msgSend(BOOL, self, "supportsDestination:", destination) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + CommandBufferDescriptor +Class Methods: + alloc +Methods: + init + errorOptions + retainedReferences + setErrorOptions + setRetainedReferences +*/ +@(objc_class="MTLCommandBufferDescriptor") +CommandBufferDescriptor :: struct { using _: NS.Copying(CommandBufferDescriptor) } + +@(objc_type=CommandBufferDescriptor, objc_name="alloc", objc_is_class_method=true) +CommandBufferDescriptor_alloc :: #force_inline proc() -> ^CommandBufferDescriptor { + return msgSend(^CommandBufferDescriptor, CommandBufferDescriptor, "alloc") +} +@(objc_type=CommandBufferDescriptor, objc_name="init") +CommandBufferDescriptor_init :: #force_inline proc(self: ^CommandBufferDescriptor) -> ^CommandBufferDescriptor { + return msgSend(^CommandBufferDescriptor, self, "init") +} +@(objc_type=CommandBufferDescriptor, objc_name="errorOptions") +CommandBufferDescriptor_errorOptions :: #force_inline proc(self: ^CommandBufferDescriptor) -> CommandBufferErrorOption { + return msgSend(CommandBufferErrorOption, self, "errorOptions") +} +@(objc_type=CommandBufferDescriptor, objc_name="retainedReferences") +CommandBufferDescriptor_retainedReferences :: #force_inline proc(self: ^CommandBufferDescriptor) -> BOOL { + return msgSend(BOOL, self, "retainedReferences") +} +@(objc_type=CommandBufferDescriptor, objc_name="setErrorOptions") +CommandBufferDescriptor_setErrorOptions :: #force_inline proc(self: ^CommandBufferDescriptor, errorOptions: CommandBufferErrorOption) { + msgSend(nil, self, "setErrorOptions:", errorOptions) +} +@(objc_type=CommandBufferDescriptor, objc_name="setRetainedReferences") +CommandBufferDescriptor_setRetainedReferences :: #force_inline proc(self: ^CommandBufferDescriptor, retainedReferences: BOOL) { + msgSend(nil, self, "setRetainedReferences:", retainedReferences) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + CompileOptions +Class Methods: + alloc +Methods: + init + fastMathEnabled + installName + languageVersion + libraries + libraryType + preprocessorMacros + preserveInvariance + setFastMathEnabled + setInstallName + setLanguageVersion + setLibraries + setLibraryType + setPreprocessorMacros + setPreserveInvariance +*/ +@(objc_class="MTLCompileOptions") +CompileOptions :: struct { using _: NS.Copying(CompileOptions) } + +@(objc_type=CompileOptions, objc_name="alloc", objc_is_class_method=true) +CompileOptions_alloc :: #force_inline proc() -> ^CompileOptions { + return msgSend(^CompileOptions, CompileOptions, "alloc") +} +@(objc_type=CompileOptions, objc_name="init") +CompileOptions_init :: #force_inline proc(self: ^CompileOptions) -> ^CompileOptions { + return msgSend(^CompileOptions, self, "init") +} +@(objc_type=CompileOptions, objc_name="fastMathEnabled") +CompileOptions_fastMathEnabled :: #force_inline proc(self: ^CompileOptions) -> BOOL { + return msgSend(BOOL, self, "fastMathEnabled") +} +@(objc_type=CompileOptions, objc_name="installName") +CompileOptions_installName :: #force_inline proc(self: ^CompileOptions) -> ^NS.String { + return msgSend(^NS.String, self, "installName") +} +@(objc_type=CompileOptions, objc_name="languageVersion") +CompileOptions_languageVersion :: #force_inline proc(self: ^CompileOptions) -> LanguageVersion { + return msgSend(LanguageVersion, self, "languageVersion") +} +@(objc_type=CompileOptions, objc_name="libraries") +CompileOptions_libraries :: #force_inline proc(self: ^CompileOptions) -> ^NS.Array { + return msgSend(^NS.Array, self, "libraries") +} +@(objc_type=CompileOptions, objc_name="libraryType") +CompileOptions_libraryType :: #force_inline proc(self: ^CompileOptions) -> LibraryType { + return msgSend(LibraryType, self, "libraryType") +} +@(objc_type=CompileOptions, objc_name="preprocessorMacros") +CompileOptions_preprocessorMacros :: #force_inline proc(self: ^CompileOptions) -> ^NS.Dictionary { + return msgSend(^NS.Dictionary, self, "preprocessorMacros") +} +@(objc_type=CompileOptions, objc_name="preserveInvariance") +CompileOptions_preserveInvariance :: #force_inline proc(self: ^CompileOptions) -> BOOL { + return msgSend(BOOL, self, "preserveInvariance") +} +@(objc_type=CompileOptions, objc_name="setFastMathEnabled") +CompileOptions_setFastMathEnabled :: #force_inline proc(self: ^CompileOptions, fastMathEnabled: BOOL) { + msgSend(nil, self, "setFastMathEnabled:", fastMathEnabled) +} +@(objc_type=CompileOptions, objc_name="setInstallName") +CompileOptions_setInstallName :: #force_inline proc(self: ^CompileOptions, installName: ^NS.String) { + msgSend(nil, self, "setInstallName:", installName) +} +@(objc_type=CompileOptions, objc_name="setLanguageVersion") +CompileOptions_setLanguageVersion :: #force_inline proc(self: ^CompileOptions, languageVersion: LanguageVersion) { + msgSend(nil, self, "setLanguageVersion:", languageVersion) +} +@(objc_type=CompileOptions, objc_name="setLibraries") +CompileOptions_setLibraries :: #force_inline proc(self: ^CompileOptions, libraries: ^NS.Array) { + msgSend(nil, self, "setLibraries:", libraries) +} +@(objc_type=CompileOptions, objc_name="setLibraryType") +CompileOptions_setLibraryType :: #force_inline proc(self: ^CompileOptions, libraryType: LibraryType) { + msgSend(nil, self, "setLibraryType:", libraryType) +} +@(objc_type=CompileOptions, objc_name="setPreprocessorMacros") +CompileOptions_setPreprocessorMacros :: #force_inline proc(self: ^CompileOptions, preprocessorMacros: ^NS.Dictionary) { + msgSend(nil, self, "setPreprocessorMacros:", preprocessorMacros) +} +@(objc_type=CompileOptions, objc_name="setPreserveInvariance") +CompileOptions_setPreserveInvariance :: #force_inline proc(self: ^CompileOptions, preserveInvariance: BOOL) { + msgSend(nil, self, "setPreserveInvariance:", preserveInvariance) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + ComputePassDescriptor +Class Methods: + alloc + computePassDescriptor +Methods: + init + dispatchType + sampleBufferAttachments + setDispatchType +*/ +@(objc_class="MTLComputePassDescriptor") +ComputePassDescriptor :: struct { using _: NS.Copying(ComputePassDescriptor) } + +@(objc_type=ComputePassDescriptor, objc_name="alloc", objc_is_class_method=true) +ComputePassDescriptor_alloc :: #force_inline proc() -> ^ComputePassDescriptor { + return msgSend(^ComputePassDescriptor, ComputePassDescriptor, "alloc") +} +@(objc_type=ComputePassDescriptor, objc_name="init") +ComputePassDescriptor_init :: #force_inline proc(self: ^ComputePassDescriptor) -> ^ComputePassDescriptor { + return msgSend(^ComputePassDescriptor, self, "init") +} +@(objc_type=ComputePassDescriptor, objc_name="computePassDescriptor", objc_is_class_method=true) +ComputePassDescriptor_computePassDescriptor :: #force_inline proc() -> ^ComputePassDescriptor { + return msgSend(^ComputePassDescriptor, ComputePassDescriptor, "computePassDescriptor") +} +@(objc_type=ComputePassDescriptor, objc_name="dispatchType") +ComputePassDescriptor_dispatchType :: #force_inline proc(self: ^ComputePassDescriptor) -> DispatchType { + return msgSend(DispatchType, self, "dispatchType") +} +@(objc_type=ComputePassDescriptor, objc_name="sampleBufferAttachments") +ComputePassDescriptor_sampleBufferAttachments :: #force_inline proc(self: ^ComputePassDescriptor) -> ^ComputePassSampleBufferAttachmentDescriptorArray { + return msgSend(^ComputePassSampleBufferAttachmentDescriptorArray, self, "sampleBufferAttachments") +} +@(objc_type=ComputePassDescriptor, objc_name="setDispatchType") +ComputePassDescriptor_setDispatchType :: #force_inline proc(self: ^ComputePassDescriptor, dispatchType: DispatchType) { + msgSend(nil, self, "setDispatchType:", dispatchType) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + ComputePassSampleBufferAttachmentDescriptor +Class Methods: + alloc +Methods: + init + endOfEncoderSampleIndex + sampleBuffer + setEndOfEncoderSampleIndex + setSampleBuffer + setStartOfEncoderSampleIndex + startOfEncoderSampleIndex +*/ +@(objc_class="MTLComputePassSampleBufferAttachmentDescriptor") +ComputePassSampleBufferAttachmentDescriptor :: struct { using _: NS.Copying(ComputePassSampleBufferAttachmentDescriptor) } + +@(objc_type=ComputePassSampleBufferAttachmentDescriptor, objc_name="alloc", objc_is_class_method=true) +ComputePassSampleBufferAttachmentDescriptor_alloc :: #force_inline proc() -> ^ComputePassSampleBufferAttachmentDescriptor { + return msgSend(^ComputePassSampleBufferAttachmentDescriptor, ComputePassSampleBufferAttachmentDescriptor, "alloc") +} +@(objc_type=ComputePassSampleBufferAttachmentDescriptor, objc_name="init") +ComputePassSampleBufferAttachmentDescriptor_init :: #force_inline proc(self: ^ComputePassSampleBufferAttachmentDescriptor) -> ^ComputePassSampleBufferAttachmentDescriptor { + return msgSend(^ComputePassSampleBufferAttachmentDescriptor, self, "init") +} +@(objc_type=ComputePassSampleBufferAttachmentDescriptor, objc_name="endOfEncoderSampleIndex") +ComputePassSampleBufferAttachmentDescriptor_endOfEncoderSampleIndex :: #force_inline proc(self: ^ComputePassSampleBufferAttachmentDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "endOfEncoderSampleIndex") +} +@(objc_type=ComputePassSampleBufferAttachmentDescriptor, objc_name="sampleBuffer") +ComputePassSampleBufferAttachmentDescriptor_sampleBuffer :: #force_inline proc(self: ^ComputePassSampleBufferAttachmentDescriptor) -> ^CounterSampleBuffer { + return msgSend(^CounterSampleBuffer, self, "sampleBuffer") +} +@(objc_type=ComputePassSampleBufferAttachmentDescriptor, objc_name="setEndOfEncoderSampleIndex") +ComputePassSampleBufferAttachmentDescriptor_setEndOfEncoderSampleIndex :: #force_inline proc(self: ^ComputePassSampleBufferAttachmentDescriptor, endOfEncoderSampleIndex: NS.UInteger) { + msgSend(nil, self, "setEndOfEncoderSampleIndex:", endOfEncoderSampleIndex) +} +@(objc_type=ComputePassSampleBufferAttachmentDescriptor, objc_name="setSampleBuffer") +ComputePassSampleBufferAttachmentDescriptor_setSampleBuffer :: #force_inline proc(self: ^ComputePassSampleBufferAttachmentDescriptor, sampleBuffer: ^Buffer) { + msgSend(nil, self, "setSampleBuffer:", sampleBuffer) +} +@(objc_type=ComputePassSampleBufferAttachmentDescriptor, objc_name="setStartOfEncoderSampleIndex") +ComputePassSampleBufferAttachmentDescriptor_setStartOfEncoderSampleIndex :: #force_inline proc(self: ^ComputePassSampleBufferAttachmentDescriptor, startOfEncoderSampleIndex: NS.UInteger) { + msgSend(nil, self, "setStartOfEncoderSampleIndex:", startOfEncoderSampleIndex) +} +@(objc_type=ComputePassSampleBufferAttachmentDescriptor, objc_name="startOfEncoderSampleIndex") +ComputePassSampleBufferAttachmentDescriptor_startOfEncoderSampleIndex :: #force_inline proc(self: ^ComputePassSampleBufferAttachmentDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "startOfEncoderSampleIndex") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + ComputePassSampleBufferAttachmentDescriptorArray +Class Methods: + alloc +Methods: + init + objectAtIndexedSubscript + setObject +*/ +@(objc_class="MTLComputePassSampleBufferAttachmentDescriptorArray") +ComputePassSampleBufferAttachmentDescriptorArray :: struct { using _: NS.Object } + +@(objc_type=ComputePassSampleBufferAttachmentDescriptorArray, objc_name="alloc", objc_is_class_method=true) +ComputePassSampleBufferAttachmentDescriptorArray_alloc :: #force_inline proc() -> ^ComputePassSampleBufferAttachmentDescriptorArray { + return msgSend(^ComputePassSampleBufferAttachmentDescriptorArray, ComputePassSampleBufferAttachmentDescriptorArray, "alloc") +} +@(objc_type=ComputePassSampleBufferAttachmentDescriptorArray, objc_name="init") +ComputePassSampleBufferAttachmentDescriptorArray_init :: #force_inline proc(self: ^ComputePassSampleBufferAttachmentDescriptorArray) -> ^ComputePassSampleBufferAttachmentDescriptorArray { + return msgSend(^ComputePassSampleBufferAttachmentDescriptorArray, self, "init") +} +@(objc_type=ComputePassSampleBufferAttachmentDescriptorArray, objc_name="object") +ComputePassSampleBufferAttachmentDescriptorArray_object :: #force_inline proc(self: ^ComputePassSampleBufferAttachmentDescriptorArray, attachmentIndex: NS.UInteger) -> ^ComputePassSampleBufferAttachmentDescriptor { + return msgSend(^ComputePassSampleBufferAttachmentDescriptor, self, "objectAtIndexedSubscript:", attachmentIndex) +} +@(objc_type=ComputePassSampleBufferAttachmentDescriptorArray, objc_name="setObject") +ComputePassSampleBufferAttachmentDescriptorArray_setObject :: #force_inline proc(self: ^ComputePassSampleBufferAttachmentDescriptorArray, attachment: ^ComputePassSampleBufferAttachmentDescriptor, attachmentIndex: NS.UInteger) { + msgSend(nil, self, "setObject:atIndexedSubscript:", attachment, attachmentIndex) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + ComputePipelineDescriptor +Class Methods: + alloc +Methods: + init + binaryArchives + buffers + computeFunction + insertLibraries + label + linkedFunctions + maxCallStackDepth + maxTotalThreadsPerThreadgroup + reset + setBinaryArchives + setComputeFunction + setInsertLibraries + setLabel + setLinkedFunctions + setMaxCallStackDepth + setMaxTotalThreadsPerThreadgroup + setStageInputDescriptor + setSupportAddingBinaryFunctions + setSupportIndirectCommandBuffers + setThreadGroupSizeIsMultipleOfThreadExecutionWidth + stageInputDescriptor + supportAddingBinaryFunctions + supportIndirectCommandBuffers + threadGroupSizeIsMultipleOfThreadExecutionWidth +*/ +@(objc_class="MTLComputePipelineDescriptor") +ComputePipelineDescriptor :: struct { using _: NS.Copying(ComputePipelineDescriptor) } + +@(objc_type=ComputePipelineDescriptor, objc_name="alloc", objc_is_class_method=true) +ComputePipelineDescriptor_alloc :: #force_inline proc() -> ^ComputePipelineDescriptor { + return msgSend(^ComputePipelineDescriptor, ComputePipelineDescriptor, "alloc") +} +@(objc_type=ComputePipelineDescriptor, objc_name="init") +ComputePipelineDescriptor_init :: #force_inline proc(self: ^ComputePipelineDescriptor) -> ^ComputePipelineDescriptor { + return msgSend(^ComputePipelineDescriptor, self, "init") +} +@(objc_type=ComputePipelineDescriptor, objc_name="binaryArchives") +ComputePipelineDescriptor_binaryArchives :: #force_inline proc(self: ^ComputePipelineDescriptor) -> ^NS.Array { + return msgSend(^NS.Array, self, "binaryArchives") +} +@(objc_type=ComputePipelineDescriptor, objc_name="buffers") +ComputePipelineDescriptor_buffers :: #force_inline proc(self: ^ComputePipelineDescriptor) -> ^PipelineBufferDescriptorArray { + return msgSend(^PipelineBufferDescriptorArray, self, "buffers") +} +@(objc_type=ComputePipelineDescriptor, objc_name="computeFunction") +ComputePipelineDescriptor_computeFunction :: #force_inline proc(self: ^ComputePipelineDescriptor) -> ^Function { + return msgSend(^Function, self, "computeFunction") +} +@(objc_type=ComputePipelineDescriptor, objc_name="insertLibraries") +ComputePipelineDescriptor_insertLibraries :: #force_inline proc(self: ^ComputePipelineDescriptor) -> ^NS.Array { + return msgSend(^NS.Array, self, "insertLibraries") +} +@(objc_type=ComputePipelineDescriptor, objc_name="label") +ComputePipelineDescriptor_label :: #force_inline proc(self: ^ComputePipelineDescriptor) -> ^NS.String { + return msgSend(^NS.String, self, "label") +} +@(objc_type=ComputePipelineDescriptor, objc_name="linkedFunctions") +ComputePipelineDescriptor_linkedFunctions :: #force_inline proc(self: ^ComputePipelineDescriptor) -> ^LinkedFunctions { + return msgSend(^LinkedFunctions, self, "linkedFunctions") +} +@(objc_type=ComputePipelineDescriptor, objc_name="maxCallStackDepth") +ComputePipelineDescriptor_maxCallStackDepth :: #force_inline proc(self: ^ComputePipelineDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "maxCallStackDepth") +} +@(objc_type=ComputePipelineDescriptor, objc_name="maxTotalThreadsPerThreadgroup") +ComputePipelineDescriptor_maxTotalThreadsPerThreadgroup :: #force_inline proc(self: ^ComputePipelineDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "maxTotalThreadsPerThreadgroup") +} +@(objc_type=ComputePipelineDescriptor, objc_name="reset") +ComputePipelineDescriptor_reset :: #force_inline proc(self: ^ComputePipelineDescriptor) { + msgSend(nil, self, "reset") +} +@(objc_type=ComputePipelineDescriptor, objc_name="setBinaryArchives") +ComputePipelineDescriptor_setBinaryArchives :: #force_inline proc(self: ^ComputePipelineDescriptor, binaryArchives: ^NS.Array) { + msgSend(nil, self, "setBinaryArchives:", binaryArchives) +} +@(objc_type=ComputePipelineDescriptor, objc_name="setComputeFunction") +ComputePipelineDescriptor_setComputeFunction :: #force_inline proc(self: ^ComputePipelineDescriptor, computeFunction: ^Function) { + msgSend(nil, self, "setComputeFunction:", computeFunction) +} +@(objc_type=ComputePipelineDescriptor, objc_name="setInsertLibraries") +ComputePipelineDescriptor_setInsertLibraries :: #force_inline proc(self: ^ComputePipelineDescriptor, insertLibraries: ^NS.Array) { + msgSend(nil, self, "setInsertLibraries:", insertLibraries) +} +@(objc_type=ComputePipelineDescriptor, objc_name="setLabel") +ComputePipelineDescriptor_setLabel :: #force_inline proc(self: ^ComputePipelineDescriptor, label: ^NS.String) { + msgSend(nil, self, "setLabel:", label) +} +@(objc_type=ComputePipelineDescriptor, objc_name="setLinkedFunctions") +ComputePipelineDescriptor_setLinkedFunctions :: #force_inline proc(self: ^ComputePipelineDescriptor, linkedFunctions: ^LinkedFunctions) { + msgSend(nil, self, "setLinkedFunctions:", linkedFunctions) +} +@(objc_type=ComputePipelineDescriptor, objc_name="setMaxCallStackDepth") +ComputePipelineDescriptor_setMaxCallStackDepth :: #force_inline proc(self: ^ComputePipelineDescriptor, maxCallStackDepth: NS.UInteger) { + msgSend(nil, self, "setMaxCallStackDepth:", maxCallStackDepth) +} +@(objc_type=ComputePipelineDescriptor, objc_name="setMaxTotalThreadsPerThreadgroup") +ComputePipelineDescriptor_setMaxTotalThreadsPerThreadgroup :: #force_inline proc(self: ^ComputePipelineDescriptor, maxTotalThreadsPerThreadgroup: NS.UInteger) { + msgSend(nil, self, "setMaxTotalThreadsPerThreadgroup:", maxTotalThreadsPerThreadgroup) +} +@(objc_type=ComputePipelineDescriptor, objc_name="setStageInputDescriptor") +ComputePipelineDescriptor_setStageInputDescriptor :: #force_inline proc(self: ^ComputePipelineDescriptor, stageInputDescriptor: ^StageInputOutputDescriptor) { + msgSend(nil, self, "setStageInputDescriptor:", stageInputDescriptor) +} +@(objc_type=ComputePipelineDescriptor, objc_name="setSupportAddingBinaryFunctions") +ComputePipelineDescriptor_setSupportAddingBinaryFunctions :: #force_inline proc(self: ^ComputePipelineDescriptor, supportAddingBinaryFunctions: BOOL) { + msgSend(nil, self, "setSupportAddingBinaryFunctions:", supportAddingBinaryFunctions) +} +@(objc_type=ComputePipelineDescriptor, objc_name="setSupportIndirectCommandBuffers") +ComputePipelineDescriptor_setSupportIndirectCommandBuffers :: #force_inline proc(self: ^ComputePipelineDescriptor, supportIndirectCommandBuffers: BOOL) { + msgSend(nil, self, "setSupportIndirectCommandBuffers:", supportIndirectCommandBuffers) +} +@(objc_type=ComputePipelineDescriptor, objc_name="setThreadGroupSizeIsMultipleOfThreadExecutionWidth") +ComputePipelineDescriptor_setThreadGroupSizeIsMultipleOfThreadExecutionWidth :: #force_inline proc(self: ^ComputePipelineDescriptor, threadGroupSizeIsMultipleOfThreadExecutionWidth: BOOL) { + msgSend(nil, self, "setThreadGroupSizeIsMultipleOfThreadExecutionWidth:", threadGroupSizeIsMultipleOfThreadExecutionWidth) +} +@(objc_type=ComputePipelineDescriptor, objc_name="stageInputDescriptor") +ComputePipelineDescriptor_stageInputDescriptor :: #force_inline proc(self: ^ComputePipelineDescriptor) -> ^StageInputOutputDescriptor { + return msgSend(^StageInputOutputDescriptor, self, "stageInputDescriptor") +} +@(objc_type=ComputePipelineDescriptor, objc_name="supportAddingBinaryFunctions") +ComputePipelineDescriptor_supportAddingBinaryFunctions :: #force_inline proc(self: ^ComputePipelineDescriptor) -> BOOL { + return msgSend(BOOL, self, "supportAddingBinaryFunctions") +} +@(objc_type=ComputePipelineDescriptor, objc_name="supportIndirectCommandBuffers") +ComputePipelineDescriptor_supportIndirectCommandBuffers :: #force_inline proc(self: ^ComputePipelineDescriptor) -> BOOL { + return msgSend(BOOL, self, "supportIndirectCommandBuffers") +} +@(objc_type=ComputePipelineDescriptor, objc_name="threadGroupSizeIsMultipleOfThreadExecutionWidth") +ComputePipelineDescriptor_threadGroupSizeIsMultipleOfThreadExecutionWidth :: #force_inline proc(self: ^ComputePipelineDescriptor) -> BOOL { + return msgSend(BOOL, self, "threadGroupSizeIsMultipleOfThreadExecutionWidth") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + ComputePipelineReflection +Class Methods: + alloc +Methods: + init + arguments +*/ +@(objc_class="MTLComputePipelineReflection") +ComputePipelineReflection :: struct { using _: NS.Object } + +@(objc_type=ComputePipelineReflection, objc_name="alloc", objc_is_class_method=true) +ComputePipelineReflection_alloc :: #force_inline proc() -> ^ComputePipelineReflection { + return msgSend(^ComputePipelineReflection, ComputePipelineReflection, "alloc") +} +@(objc_type=ComputePipelineReflection, objc_name="init") +ComputePipelineReflection_init :: #force_inline proc(self: ^ComputePipelineReflection) -> ^ComputePipelineReflection { + return msgSend(^ComputePipelineReflection, self, "init") +} +@(objc_type=ComputePipelineReflection, objc_name="arguments") +ComputePipelineReflection_arguments :: #force_inline proc(self: ^ComputePipelineReflection) -> ^NS.Array { + return msgSend(^NS.Array, self, "arguments") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + CounterSampleBufferDescriptor +Class Methods: + alloc +Methods: + init + counterSet + label + sampleCount + setCounterSet + setLabel + setSampleCount + setStorageMode + storageMode +*/ +@(objc_class="MTLCounterSampleBufferDescriptor") +CounterSampleBufferDescriptor :: struct { using _: NS.Copying(CounterSampleBufferDescriptor) } + +@(objc_type=CounterSampleBufferDescriptor, objc_name="alloc", objc_is_class_method=true) +CounterSampleBufferDescriptor_alloc :: #force_inline proc() -> ^CounterSampleBufferDescriptor { + return msgSend(^CounterSampleBufferDescriptor, CounterSampleBufferDescriptor, "alloc") +} +@(objc_type=CounterSampleBufferDescriptor, objc_name="init") +CounterSampleBufferDescriptor_init :: #force_inline proc(self: ^CounterSampleBufferDescriptor) -> ^CounterSampleBufferDescriptor { + return msgSend(^CounterSampleBufferDescriptor, self, "init") +} +@(objc_type=CounterSampleBufferDescriptor, objc_name="counterSet") +CounterSampleBufferDescriptor_counterSet :: #force_inline proc(self: ^CounterSampleBufferDescriptor) -> ^CounterSet { + return msgSend(^CounterSet, self, "counterSet") +} +@(objc_type=CounterSampleBufferDescriptor, objc_name="label") +CounterSampleBufferDescriptor_label :: #force_inline proc(self: ^CounterSampleBufferDescriptor) -> ^NS.String { + return msgSend(^NS.String, self, "label") +} +@(objc_type=CounterSampleBufferDescriptor, objc_name="sampleCount") +CounterSampleBufferDescriptor_sampleCount :: #force_inline proc(self: ^CounterSampleBufferDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "sampleCount") +} +@(objc_type=CounterSampleBufferDescriptor, objc_name="setCounterSet") +CounterSampleBufferDescriptor_setCounterSet :: #force_inline proc(self: ^CounterSampleBufferDescriptor, counterSet: ^CounterSet) { + msgSend(nil, self, "setCounterSet:", counterSet) +} +@(objc_type=CounterSampleBufferDescriptor, objc_name="setLabel") +CounterSampleBufferDescriptor_setLabel :: #force_inline proc(self: ^CounterSampleBufferDescriptor, label: ^NS.String) { + msgSend(nil, self, "setLabel:", label) +} +@(objc_type=CounterSampleBufferDescriptor, objc_name="setSampleCount") +CounterSampleBufferDescriptor_setSampleCount :: #force_inline proc(self: ^CounterSampleBufferDescriptor, sampleCount: NS.UInteger) { + msgSend(nil, self, "setSampleCount:", sampleCount) +} +@(objc_type=CounterSampleBufferDescriptor, objc_name="setStorageMode") +CounterSampleBufferDescriptor_setStorageMode :: #force_inline proc(self: ^CounterSampleBufferDescriptor, storageMode: StorageMode) { + msgSend(nil, self, "setStorageMode:", storageMode) +} +@(objc_type=CounterSampleBufferDescriptor, objc_name="storageMode") +CounterSampleBufferDescriptor_storageMode :: #force_inline proc(self: ^CounterSampleBufferDescriptor) -> StorageMode { + return msgSend(StorageMode, self, "storageMode") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + DepthStencilDescriptor +Class Methods: + alloc +Methods: + init + backFaceStencil + depthCompareFunction + frontFaceStencil + isDepthWriteEnabled + label + setBackFaceStencil + setDepthCompareFunction + setDepthWriteEnabled + setFrontFaceStencil + setLabel +*/ +@(objc_class="MTLDepthStencilDescriptor") +DepthStencilDescriptor :: struct { using _: NS.Copying(DepthStencilDescriptor) } + +@(objc_type=DepthStencilDescriptor, objc_name="alloc", objc_is_class_method=true) +DepthStencilDescriptor_alloc :: #force_inline proc() -> ^DepthStencilDescriptor { + return msgSend(^DepthStencilDescriptor, DepthStencilDescriptor, "alloc") +} +@(objc_type=DepthStencilDescriptor, objc_name="init") +DepthStencilDescriptor_init :: #force_inline proc(self: ^DepthStencilDescriptor) -> ^DepthStencilDescriptor { + return msgSend(^DepthStencilDescriptor, self, "init") +} +@(objc_type=DepthStencilDescriptor, objc_name="backFaceStencil") +DepthStencilDescriptor_backFaceStencil :: #force_inline proc(self: ^DepthStencilDescriptor) -> ^StencilDescriptor { + return msgSend(^StencilDescriptor, self, "backFaceStencil") +} +@(objc_type=DepthStencilDescriptor, objc_name="depthCompareFunction") +DepthStencilDescriptor_depthCompareFunction :: #force_inline proc(self: ^DepthStencilDescriptor) -> CompareFunction { + return msgSend(CompareFunction, self, "depthCompareFunction") +} +@(objc_type=DepthStencilDescriptor, objc_name="frontFaceStencil") +DepthStencilDescriptor_frontFaceStencil :: #force_inline proc(self: ^DepthStencilDescriptor) -> ^StencilDescriptor { + return msgSend(^StencilDescriptor, self, "frontFaceStencil") +} +@(objc_type=DepthStencilDescriptor, objc_name="isDepthWriteEnabled") +DepthStencilDescriptor_isDepthWriteEnabled :: #force_inline proc(self: ^DepthStencilDescriptor) -> BOOL { + return msgSend(BOOL, self, "isDepthWriteEnabled") +} +@(objc_type=DepthStencilDescriptor, objc_name="label") +DepthStencilDescriptor_label :: #force_inline proc(self: ^DepthStencilDescriptor) -> ^NS.String { + return msgSend(^NS.String, self, "label") +} +@(objc_type=DepthStencilDescriptor, objc_name="setBackFaceStencil") +DepthStencilDescriptor_setBackFaceStencil :: #force_inline proc(self: ^DepthStencilDescriptor, backFaceStencil: ^StencilDescriptor) { + msgSend(nil, self, "setBackFaceStencil:", backFaceStencil) +} +@(objc_type=DepthStencilDescriptor, objc_name="setDepthCompareFunction") +DepthStencilDescriptor_setDepthCompareFunction :: #force_inline proc(self: ^DepthStencilDescriptor, depthCompareFunction: CompareFunction) { + msgSend(nil, self, "setDepthCompareFunction:", depthCompareFunction) +} +@(objc_type=DepthStencilDescriptor, objc_name="setDepthWriteEnabled") +DepthStencilDescriptor_setDepthWriteEnabled :: #force_inline proc(self: ^DepthStencilDescriptor, depthWriteEnabled: BOOL) { + msgSend(nil, self, "setDepthWriteEnabled:", depthWriteEnabled) +} +@(objc_type=DepthStencilDescriptor, objc_name="setFrontFaceStencil") +DepthStencilDescriptor_setFrontFaceStencil :: #force_inline proc(self: ^DepthStencilDescriptor, frontFaceStencil: ^StencilDescriptor) { + msgSend(nil, self, "setFrontFaceStencil:", frontFaceStencil) +} +@(objc_type=DepthStencilDescriptor, objc_name="setLabel") +DepthStencilDescriptor_setLabel :: #force_inline proc(self: ^DepthStencilDescriptor, label: ^NS.String) { + msgSend(nil, self, "setLabel:", label) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + FunctionConstant +Class Methods: + alloc +Methods: + init + index + name + required + type +*/ +@(objc_class="MTLFunctionConstant") +FunctionConstant :: struct { using _: NS.Copying(FunctionConstant) } + +@(objc_type=FunctionConstant, objc_name="alloc", objc_is_class_method=true) +FunctionConstant_alloc :: #force_inline proc() -> ^FunctionConstant { + return msgSend(^FunctionConstant, FunctionConstant, "alloc") +} +@(objc_type=FunctionConstant, objc_name="init") +FunctionConstant_init :: #force_inline proc(self: ^FunctionConstant) -> ^FunctionConstant { + return msgSend(^FunctionConstant, self, "init") +} +@(objc_type=FunctionConstant, objc_name="index") +FunctionConstant_index :: #force_inline proc(self: ^FunctionConstant) -> NS.UInteger { + return msgSend(NS.UInteger, self, "index") +} +@(objc_type=FunctionConstant, objc_name="name") +FunctionConstant_name :: #force_inline proc(self: ^FunctionConstant) -> ^NS.String { + return msgSend(^NS.String, self, "name") +} +@(objc_type=FunctionConstant, objc_name="required") +FunctionConstant_required :: #force_inline proc(self: ^FunctionConstant) -> BOOL { + return msgSend(BOOL, self, "required") +} +@(objc_type=FunctionConstant, objc_name="type") +FunctionConstant_type :: #force_inline proc(self: ^FunctionConstant) -> DataType { + return msgSend(DataType, self, "type") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + FunctionConstantValues +Class Methods: + alloc +Methods: + init + reset + setConstantValue + setConstantValue + setConstantValues +*/ +@(objc_class="MTLFunctionConstantValues") +FunctionConstantValues :: struct { using _: NS.Copying(FunctionConstantValues) } + +@(objc_type=FunctionConstantValues, objc_name="alloc", objc_is_class_method=true) +FunctionConstantValues_alloc :: #force_inline proc() -> ^FunctionConstantValues { + return msgSend(^FunctionConstantValues, FunctionConstantValues, "alloc") +} +@(objc_type=FunctionConstantValues, objc_name="init") +FunctionConstantValues_init :: #force_inline proc(self: ^FunctionConstantValues) -> ^FunctionConstantValues { + return msgSend(^FunctionConstantValues, self, "init") +} +@(objc_type=FunctionConstantValues, objc_name="reset") +FunctionConstantValues_reset :: #force_inline proc(self: ^FunctionConstantValues) { + msgSend(nil, self, "reset") +} +@(objc_type=FunctionConstantValues, objc_name="setConstantValue") +FunctionConstantValues_setConstantValue :: #force_inline proc(self: ^FunctionConstantValues, value: rawptr, type: DataType, index: NS.UInteger) { + msgSend(nil, self, "setConstantValue:type:atIndex:", value, type, index) +} +@(objc_type=FunctionConstantValues, objc_name="setConstantValueWithName") +FunctionConstantValues_setConstantValueWithName :: #force_inline proc(self: ^FunctionConstantValues, value: rawptr, type: DataType, name: ^NS.String) { + msgSend(nil, self, "setConstantValue:type:withName:", value, type, name) +} +@(objc_type=FunctionConstantValues, objc_name="setConstantValues") +FunctionConstantValues_setConstantValues :: #force_inline proc(self: ^FunctionConstantValues, values: rawptr, type: DataType, range: NS.Range) { + msgSend(nil, self, "setConstantValues:type:withRange:", values, type, range) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + FunctionDescriptor +Class Methods: + alloc + functionDescriptor +Methods: + init + constantValues + name + options + setConstantValues + setName + setOptions + setSpecializedName + specializedName +*/ +@(objc_class="MTLFunctionDescriptor") +FunctionDescriptor :: struct { using _: NS.Copying(FunctionDescriptor) } + +@(objc_type=FunctionDescriptor, objc_name="alloc", objc_is_class_method=true) +FunctionDescriptor_alloc :: #force_inline proc() -> ^FunctionDescriptor { + return msgSend(^FunctionDescriptor, FunctionDescriptor, "alloc") +} +@(objc_type=FunctionDescriptor, objc_name="init") +FunctionDescriptor_init :: #force_inline proc(self: ^FunctionDescriptor) -> ^FunctionDescriptor { + return msgSend(^FunctionDescriptor, self, "init") +} +@(objc_type=FunctionDescriptor, objc_name="constantValues") +FunctionDescriptor_constantValues :: #force_inline proc(self: ^FunctionDescriptor) -> ^FunctionConstantValues { + return msgSend(^FunctionConstantValues, self, "constantValues") +} +@(objc_type=FunctionDescriptor, objc_name="functionDescriptor", objc_is_class_method=true) +FunctionDescriptor_functionDescriptor :: #force_inline proc() -> ^FunctionDescriptor { + return msgSend(^FunctionDescriptor, FunctionDescriptor, "functionDescriptor") +} +@(objc_type=FunctionDescriptor, objc_name="name") +FunctionDescriptor_name :: #force_inline proc(self: ^FunctionDescriptor) -> ^NS.String { + return msgSend(^NS.String, self, "name") +} +@(objc_type=FunctionDescriptor, objc_name="options") +FunctionDescriptor_options :: #force_inline proc(self: ^FunctionDescriptor) -> FunctionOptions { + return msgSend(FunctionOptions, self, "options") +} +@(objc_type=FunctionDescriptor, objc_name="setConstantValues") +FunctionDescriptor_setConstantValues :: #force_inline proc(self: ^FunctionDescriptor, constantValues: ^FunctionConstantValues) { + msgSend(nil, self, "setConstantValues:", constantValues) +} +@(objc_type=FunctionDescriptor, objc_name="setName") +FunctionDescriptor_setName :: #force_inline proc(self: ^FunctionDescriptor, name: ^NS.String) { + msgSend(nil, self, "setName:", name) +} +@(objc_type=FunctionDescriptor, objc_name="setOptions") +FunctionDescriptor_setOptions :: #force_inline proc(self: ^FunctionDescriptor, options: FunctionOptions) { + msgSend(nil, self, "setOptions:", options) +} +@(objc_type=FunctionDescriptor, objc_name="setSpecializedName") +FunctionDescriptor_setSpecializedName :: #force_inline proc(self: ^FunctionDescriptor, specializedName: ^NS.String) { + msgSend(nil, self, "setSpecializedName:", specializedName) +} +@(objc_type=FunctionDescriptor, objc_name="specializedName") +FunctionDescriptor_specializedName :: #force_inline proc(self: ^FunctionDescriptor) -> ^NS.String { + return msgSend(^NS.String, self, "specializedName") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + IntersectionFunctionDescriptor +Class Methods: + alloc +Methods: + init +*/ +@(objc_class="MTLIntersectionFunctionDescriptor") +IntersectionFunctionDescriptor :: struct { using _: NS.Copying(IntersectionFunctionDescriptor) } + +@(objc_type=IntersectionFunctionDescriptor, objc_name="alloc", objc_is_class_method=true) +IntersectionFunctionDescriptor_alloc :: #force_inline proc() -> ^IntersectionFunctionDescriptor { + return msgSend(^IntersectionFunctionDescriptor, IntersectionFunctionDescriptor, "alloc") +} +@(objc_type=IntersectionFunctionDescriptor, objc_name="init") +IntersectionFunctionDescriptor_init :: #force_inline proc(self: ^IntersectionFunctionDescriptor) -> ^IntersectionFunctionDescriptor { + return msgSend(^IntersectionFunctionDescriptor, self, "init") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + HeapDescriptor +Class Methods: + alloc +Methods: + init + cpuCacheMode + hazardTrackingMode + resourceOptions + setCpuCacheMode + setHazardTrackingMode + setResourceOptions + setSize + setStorageMode + setType + size + storageMode + type +*/ +@(objc_class="MTLHeapDescriptor") +HeapDescriptor :: struct { using _: NS.Copying(HeapDescriptor) } + +@(objc_type=HeapDescriptor, objc_name="alloc", objc_is_class_method=true) +HeapDescriptor_alloc :: #force_inline proc() -> ^HeapDescriptor { + return msgSend(^HeapDescriptor, HeapDescriptor, "alloc") +} +@(objc_type=HeapDescriptor, objc_name="init") +HeapDescriptor_init :: #force_inline proc(self: ^HeapDescriptor) -> ^HeapDescriptor { + return msgSend(^HeapDescriptor, self, "init") +} +@(objc_type=HeapDescriptor, objc_name="cpuCacheMode") +HeapDescriptor_cpuCacheMode :: #force_inline proc(self: ^HeapDescriptor) -> CPUCacheMode { + return msgSend(CPUCacheMode, self, "cpuCacheMode") +} +@(objc_type=HeapDescriptor, objc_name="hazardTrackingMode") +HeapDescriptor_hazardTrackingMode :: #force_inline proc(self: ^HeapDescriptor) -> HazardTrackingMode { + return msgSend(HazardTrackingMode, self, "hazardTrackingMode") +} +@(objc_type=HeapDescriptor, objc_name="resourceOptions") +HeapDescriptor_resourceOptions :: #force_inline proc(self: ^HeapDescriptor) -> ResourceOptions { + return msgSend(ResourceOptions, self, "resourceOptions") +} +@(objc_type=HeapDescriptor, objc_name="setCpuCacheMode") +HeapDescriptor_setCpuCacheMode :: #force_inline proc(self: ^HeapDescriptor, cpuCacheMode: CPUCacheMode) { + msgSend(nil, self, "setCpuCacheMode:", cpuCacheMode) +} +@(objc_type=HeapDescriptor, objc_name="setHazardTrackingMode") +HeapDescriptor_setHazardTrackingMode :: #force_inline proc(self: ^HeapDescriptor, hazardTrackingMode: HazardTrackingMode) { + msgSend(nil, self, "setHazardTrackingMode:", hazardTrackingMode) +} +@(objc_type=HeapDescriptor, objc_name="setResourceOptions") +HeapDescriptor_setResourceOptions :: #force_inline proc(self: ^HeapDescriptor, resourceOptions: ResourceOptions) { + msgSend(nil, self, "setResourceOptions:", resourceOptions) +} +@(objc_type=HeapDescriptor, objc_name="setSize") +HeapDescriptor_setSize :: #force_inline proc(self: ^HeapDescriptor, size: NS.UInteger) { + msgSend(nil, self, "setSize:", size) +} +@(objc_type=HeapDescriptor, objc_name="setStorageMode") +HeapDescriptor_setStorageMode :: #force_inline proc(self: ^HeapDescriptor, storageMode: StorageMode) { + msgSend(nil, self, "setStorageMode:", storageMode) +} +@(objc_type=HeapDescriptor, objc_name="setType") +HeapDescriptor_setType :: #force_inline proc(self: ^HeapDescriptor, type: HeapType) { + msgSend(nil, self, "setType:", type) +} +@(objc_type=HeapDescriptor, objc_name="size") +HeapDescriptor_size :: #force_inline proc(self: ^HeapDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "size") +} +@(objc_type=HeapDescriptor, objc_name="storageMode") +HeapDescriptor_storageMode :: #force_inline proc(self: ^HeapDescriptor) -> StorageMode { + return msgSend(StorageMode, self, "storageMode") +} +@(objc_type=HeapDescriptor, objc_name="type") +HeapDescriptor_type :: #force_inline proc(self: ^HeapDescriptor) -> HeapType { + return msgSend(HeapType, self, "type") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + IndirectCommandBufferDescriptor +Class Methods: + alloc +Methods: + init + commandTypes + inheritBuffers + inheritPipelineState + maxFragmentBufferBindCount + maxKernelBufferBindCount + maxVertexBufferBindCount + setCommandTypes + setInheritBuffers + setInheritPipelineState + setMaxFragmentBufferBindCount + setMaxKernelBufferBindCount + setMaxVertexBufferBindCount +*/ +@(objc_class="MTLIndirectCommandBufferDescriptor") +IndirectCommandBufferDescriptor :: struct { using _: NS.Copying(IndirectCommandBufferDescriptor) } + +@(objc_type=IndirectCommandBufferDescriptor, objc_name="alloc", objc_is_class_method=true) +IndirectCommandBufferDescriptor_alloc :: #force_inline proc() -> ^IndirectCommandBufferDescriptor { + return msgSend(^IndirectCommandBufferDescriptor, IndirectCommandBufferDescriptor, "alloc") +} +@(objc_type=IndirectCommandBufferDescriptor, objc_name="init") +IndirectCommandBufferDescriptor_init :: #force_inline proc(self: ^IndirectCommandBufferDescriptor) -> ^IndirectCommandBufferDescriptor { + return msgSend(^IndirectCommandBufferDescriptor, self, "init") +} +@(objc_type=IndirectCommandBufferDescriptor, objc_name="commandTypes") +IndirectCommandBufferDescriptor_commandTypes :: #force_inline proc(self: ^IndirectCommandBufferDescriptor) -> IndirectCommandType { + return msgSend(IndirectCommandType, self, "commandTypes") +} +@(objc_type=IndirectCommandBufferDescriptor, objc_name="inheritBuffers") +IndirectCommandBufferDescriptor_inheritBuffers :: #force_inline proc(self: ^IndirectCommandBufferDescriptor) -> BOOL { + return msgSend(BOOL, self, "inheritBuffers") +} +@(objc_type=IndirectCommandBufferDescriptor, objc_name="inheritPipelineState") +IndirectCommandBufferDescriptor_inheritPipelineState :: #force_inline proc(self: ^IndirectCommandBufferDescriptor) -> BOOL { + return msgSend(BOOL, self, "inheritPipelineState") +} +@(objc_type=IndirectCommandBufferDescriptor, objc_name="maxFragmentBufferBindCount") +IndirectCommandBufferDescriptor_maxFragmentBufferBindCount :: #force_inline proc(self: ^IndirectCommandBufferDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "maxFragmentBufferBindCount") +} +@(objc_type=IndirectCommandBufferDescriptor, objc_name="maxKernelBufferBindCount") +IndirectCommandBufferDescriptor_maxKernelBufferBindCount :: #force_inline proc(self: ^IndirectCommandBufferDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "maxKernelBufferBindCount") +} +@(objc_type=IndirectCommandBufferDescriptor, objc_name="maxVertexBufferBindCount") +IndirectCommandBufferDescriptor_maxVertexBufferBindCount :: #force_inline proc(self: ^IndirectCommandBufferDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "maxVertexBufferBindCount") +} +@(objc_type=IndirectCommandBufferDescriptor, objc_name="setCommandTypes") +IndirectCommandBufferDescriptor_setCommandTypes :: #force_inline proc(self: ^IndirectCommandBufferDescriptor, commandTypes: IndirectCommandType) { + msgSend(nil, self, "setCommandTypes:", commandTypes) +} +@(objc_type=IndirectCommandBufferDescriptor, objc_name="setInheritBuffers") +IndirectCommandBufferDescriptor_setInheritBuffers :: #force_inline proc(self: ^IndirectCommandBufferDescriptor, inheritBuffers: BOOL) { + msgSend(nil, self, "setInheritBuffers:", inheritBuffers) +} +@(objc_type=IndirectCommandBufferDescriptor, objc_name="setInheritPipelineState") +IndirectCommandBufferDescriptor_setInheritPipelineState :: #force_inline proc(self: ^IndirectCommandBufferDescriptor, inheritPipelineState: BOOL) { + msgSend(nil, self, "setInheritPipelineState:", inheritPipelineState) +} +@(objc_type=IndirectCommandBufferDescriptor, objc_name="setMaxFragmentBufferBindCount") +IndirectCommandBufferDescriptor_setMaxFragmentBufferBindCount :: #force_inline proc(self: ^IndirectCommandBufferDescriptor, maxFragmentBufferBindCount: NS.UInteger) { + msgSend(nil, self, "setMaxFragmentBufferBindCount:", maxFragmentBufferBindCount) +} +@(objc_type=IndirectCommandBufferDescriptor, objc_name="setMaxKernelBufferBindCount") +IndirectCommandBufferDescriptor_setMaxKernelBufferBindCount :: #force_inline proc(self: ^IndirectCommandBufferDescriptor, maxKernelBufferBindCount: NS.UInteger) { + msgSend(nil, self, "setMaxKernelBufferBindCount:", maxKernelBufferBindCount) +} +@(objc_type=IndirectCommandBufferDescriptor, objc_name="setMaxVertexBufferBindCount") +IndirectCommandBufferDescriptor_setMaxVertexBufferBindCount :: #force_inline proc(self: ^IndirectCommandBufferDescriptor, maxVertexBufferBindCount: NS.UInteger) { + msgSend(nil, self, "setMaxVertexBufferBindCount:", maxVertexBufferBindCount) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + InstanceAccelerationStructureDescriptor +Class Methods: + alloc + descriptor +Methods: + init + instanceCount + instanceDescriptorBuffer + instanceDescriptorBufferOffset + instanceDescriptorStride + instancedAccelerationStructures + setInstanceCount + setInstanceDescriptorBuffer + setInstanceDescriptorBufferOffset + setInstanceDescriptorStride + setInstancedAccelerationStructures +*/ +@(objc_class="MTLInstanceAccelerationStructureDescriptor") +InstanceAccelerationStructureDescriptor :: struct { using _: NS.Copying(InstanceAccelerationStructureDescriptor), using _: AccelerationStructureDescriptor } + +@(objc_type=InstanceAccelerationStructureDescriptor, objc_name="alloc", objc_is_class_method=true) +InstanceAccelerationStructureDescriptor_alloc :: #force_inline proc() -> ^InstanceAccelerationStructureDescriptor { + return msgSend(^InstanceAccelerationStructureDescriptor, InstanceAccelerationStructureDescriptor, "alloc") +} +@(objc_type=InstanceAccelerationStructureDescriptor, objc_name="init") +InstanceAccelerationStructureDescriptor_init :: #force_inline proc(self: ^InstanceAccelerationStructureDescriptor) -> ^InstanceAccelerationStructureDescriptor { + return msgSend(^InstanceAccelerationStructureDescriptor, self, "init") +} +@(objc_type=InstanceAccelerationStructureDescriptor, objc_name="descriptor", objc_is_class_method=true) +InstanceAccelerationStructureDescriptor_descriptor :: #force_inline proc() -> ^InstanceAccelerationStructureDescriptor { + return msgSend(^InstanceAccelerationStructureDescriptor, InstanceAccelerationStructureDescriptor, "descriptor") +} +@(objc_type=InstanceAccelerationStructureDescriptor, objc_name="instanceCount") +InstanceAccelerationStructureDescriptor_instanceCount :: #force_inline proc(self: ^InstanceAccelerationStructureDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "instanceCount") +} +@(objc_type=InstanceAccelerationStructureDescriptor, objc_name="instanceDescriptorBuffer") +InstanceAccelerationStructureDescriptor_instanceDescriptorBuffer :: #force_inline proc(self: ^InstanceAccelerationStructureDescriptor) -> ^Buffer { + return msgSend(^Buffer, self, "instanceDescriptorBuffer") +} +@(objc_type=InstanceAccelerationStructureDescriptor, objc_name="instanceDescriptorBufferOffset") +InstanceAccelerationStructureDescriptor_instanceDescriptorBufferOffset :: #force_inline proc(self: ^InstanceAccelerationStructureDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "instanceDescriptorBufferOffset") +} +@(objc_type=InstanceAccelerationStructureDescriptor, objc_name="instanceDescriptorStride") +InstanceAccelerationStructureDescriptor_instanceDescriptorStride :: #force_inline proc(self: ^InstanceAccelerationStructureDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "instanceDescriptorStride") +} +@(objc_type=InstanceAccelerationStructureDescriptor, objc_name="instancedAccelerationStructures") +InstanceAccelerationStructureDescriptor_instancedAccelerationStructures :: #force_inline proc(self: ^InstanceAccelerationStructureDescriptor) -> ^NS.Array { + return msgSend(^NS.Array, self, "instancedAccelerationStructures") +} +@(objc_type=InstanceAccelerationStructureDescriptor, objc_name="setInstanceCount") +InstanceAccelerationStructureDescriptor_setInstanceCount :: #force_inline proc(self: ^InstanceAccelerationStructureDescriptor, instanceCount: NS.UInteger) { + msgSend(nil, self, "setInstanceCount:", instanceCount) +} +@(objc_type=InstanceAccelerationStructureDescriptor, objc_name="setInstanceDescriptorBuffer") +InstanceAccelerationStructureDescriptor_setInstanceDescriptorBuffer :: #force_inline proc(self: ^InstanceAccelerationStructureDescriptor, instanceDescriptorBuffer: ^Buffer) { + msgSend(nil, self, "setInstanceDescriptorBuffer:", instanceDescriptorBuffer) +} +@(objc_type=InstanceAccelerationStructureDescriptor, objc_name="setInstanceDescriptorBufferOffset") +InstanceAccelerationStructureDescriptor_setInstanceDescriptorBufferOffset :: #force_inline proc(self: ^InstanceAccelerationStructureDescriptor, instanceDescriptorBufferOffset: NS.UInteger) { + msgSend(nil, self, "setInstanceDescriptorBufferOffset:", instanceDescriptorBufferOffset) +} +@(objc_type=InstanceAccelerationStructureDescriptor, objc_name="setInstanceDescriptorStride") +InstanceAccelerationStructureDescriptor_setInstanceDescriptorStride :: #force_inline proc(self: ^InstanceAccelerationStructureDescriptor, instanceDescriptorStride: NS.UInteger) { + msgSend(nil, self, "setInstanceDescriptorStride:", instanceDescriptorStride) +} +@(objc_type=InstanceAccelerationStructureDescriptor, objc_name="setInstancedAccelerationStructures") +InstanceAccelerationStructureDescriptor_setInstancedAccelerationStructures :: #force_inline proc(self: ^InstanceAccelerationStructureDescriptor, instancedAccelerationStructures: ^NS.Array) { + msgSend(nil, self, "setInstancedAccelerationStructures:", instancedAccelerationStructures) +} + +@(objc_type=InstanceAccelerationStructureDescriptor, objc_name="instanceDescriptorType") +InstanceAccelerationStructureDescriptor_instanceDescriptorType :: #force_inline proc(self: ^InstanceAccelerationStructureDescriptor) -> AccelerationStructureInstanceDescriptorType { + return msgSend(AccelerationStructureInstanceDescriptorType, self, "instanceDescriptorType") +} +@(objc_type=InstanceAccelerationStructureDescriptor, objc_name="setInstanceDescriptorType") +InstanceAccelerationStructureDescriptor_setInstanceDescriptorType :: #force_inline proc(self: ^InstanceAccelerationStructureDescriptor, buffer: AccelerationStructureInstanceDescriptorType) { + msgSend(nil, self, "setInstanceDescriptorType:", buffer) +} + + +@(objc_type=InstanceAccelerationStructureDescriptor, objc_name="motionTransformBuffer") +InstanceAccelerationStructureDescriptor_motionTransformBuffer :: #force_inline proc(self: ^InstanceAccelerationStructureDescriptor) -> ^Buffer { + return msgSend(^Buffer, self, "motionTransformBuffer") +} +@(objc_type=InstanceAccelerationStructureDescriptor, objc_name="setMotionTransformBuffer") +InstanceAccelerationStructureDescriptor_setMotionTransformBuffer :: #force_inline proc(self: ^InstanceAccelerationStructureDescriptor, buffer: ^Buffer) { + msgSend(nil, self, "setMotionTransformBuffer:", buffer) +} + + +@(objc_type=InstanceAccelerationStructureDescriptor, objc_name="motionTransformBufferOffset") +InstanceAccelerationStructureDescriptor_motionTransformBufferOffset :: #force_inline proc(self: ^InstanceAccelerationStructureDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "motionTransformBufferOffset") +} +@(objc_type=InstanceAccelerationStructureDescriptor, objc_name="setMotionTransformBufferOffset") +InstanceAccelerationStructureDescriptor_setMotionTransformBufferOffset :: #force_inline proc(self: ^InstanceAccelerationStructureDescriptor, offset: NS.UInteger) { + msgSend(nil, self, "setMotionTransformBufferOffset:", offset) +} + +@(objc_type=InstanceAccelerationStructureDescriptor, objc_name="motionTransformCount") +InstanceAccelerationStructureDescriptor_motionTransformCount :: #force_inline proc(self: ^InstanceAccelerationStructureDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "motionTransformCount") +} +@(objc_type=InstanceAccelerationStructureDescriptor, objc_name="setMotionTransformCount") +InstanceAccelerationStructureDescriptor_setMotionTransformCount :: #force_inline proc(self: ^InstanceAccelerationStructureDescriptor, offset: NS.UInteger) { + msgSend(nil, self, "setMotionTransformCount:", offset) +} + + + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + IntersectionFunctionTableDescriptor +Class Methods: + alloc + intersectionFunctionTableDescriptor +Methods: + init + functionCount + setFunctionCount +*/ +@(objc_class="MTLIntersectionFunctionTableDescriptor") +IntersectionFunctionTableDescriptor :: struct { using _: NS.Copying(IntersectionFunctionTableDescriptor) } + +@(objc_type=IntersectionFunctionTableDescriptor, objc_name="alloc", objc_is_class_method=true) +IntersectionFunctionTableDescriptor_alloc :: #force_inline proc() -> ^IntersectionFunctionTableDescriptor { + return msgSend(^IntersectionFunctionTableDescriptor, IntersectionFunctionTableDescriptor, "alloc") +} +@(objc_type=IntersectionFunctionTableDescriptor, objc_name="init") +IntersectionFunctionTableDescriptor_init :: #force_inline proc(self: ^IntersectionFunctionTableDescriptor) -> ^IntersectionFunctionTableDescriptor { + return msgSend(^IntersectionFunctionTableDescriptor, self, "init") +} +@(objc_type=IntersectionFunctionTableDescriptor, objc_name="functionCount") +IntersectionFunctionTableDescriptor_functionCount :: #force_inline proc(self: ^IntersectionFunctionTableDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "functionCount") +} +@(objc_type=IntersectionFunctionTableDescriptor, objc_name="intersectionFunctionTableDescriptor", objc_is_class_method=true) +IntersectionFunctionTableDescriptor_intersectionFunctionTableDescriptor :: #force_inline proc() -> ^IntersectionFunctionTableDescriptor { + return msgSend(^IntersectionFunctionTableDescriptor, IntersectionFunctionTableDescriptor, "intersectionFunctionTableDescriptor") +} +@(objc_type=IntersectionFunctionTableDescriptor, objc_name="setFunctionCount") +IntersectionFunctionTableDescriptor_setFunctionCount :: #force_inline proc(self: ^IntersectionFunctionTableDescriptor, functionCount: NS.UInteger) { + msgSend(nil, self, "setFunctionCount:", functionCount) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + LinkedFunctions +Class Methods: + alloc + linkedFunctions +Methods: + init + binaryFunctions + functions + groups + setBinaryFunctions + setFunctions + setGroups +*/ +@(objc_class="MTLLinkedFunctions") +LinkedFunctions :: struct { using _: NS.Copying(LinkedFunctions) } + +@(objc_type=LinkedFunctions, objc_name="alloc", objc_is_class_method=true) +LinkedFunctions_alloc :: #force_inline proc() -> ^LinkedFunctions { + return msgSend(^LinkedFunctions, LinkedFunctions, "alloc") +} +@(objc_type=LinkedFunctions, objc_name="init") +LinkedFunctions_init :: #force_inline proc(self: ^LinkedFunctions) -> ^LinkedFunctions { + return msgSend(^LinkedFunctions, self, "init") +} +@(objc_type=LinkedFunctions, objc_name="binaryFunctions") +LinkedFunctions_binaryFunctions :: #force_inline proc(self: ^LinkedFunctions) -> ^NS.Array { + return msgSend(^NS.Array, self, "binaryFunctions") +} +@(objc_type=LinkedFunctions, objc_name="functions") +LinkedFunctions_functions :: #force_inline proc(self: ^LinkedFunctions) -> ^NS.Array { + return msgSend(^NS.Array, self, "functions") +} +@(objc_type=LinkedFunctions, objc_name="groups") +LinkedFunctions_groups :: #force_inline proc(self: ^LinkedFunctions) -> ^NS.Dictionary { + return msgSend(^NS.Dictionary, self, "groups") +} +@(objc_type=LinkedFunctions, objc_name="linkedFunctions", objc_is_class_method=true) +LinkedFunctions_linkedFunctions :: #force_inline proc() -> ^LinkedFunctions { + return msgSend(^LinkedFunctions, LinkedFunctions, "linkedFunctions") +} +@(objc_type=LinkedFunctions, objc_name="setBinaryFunctions") +LinkedFunctions_setBinaryFunctions :: #force_inline proc(self: ^LinkedFunctions, binaryFunctions: ^NS.Array) { + msgSend(nil, self, "setBinaryFunctions:", binaryFunctions) +} +@(objc_type=LinkedFunctions, objc_name="setFunctions") +LinkedFunctions_setFunctions :: #force_inline proc(self: ^LinkedFunctions, functions: ^NS.Array) { + msgSend(nil, self, "setFunctions:", functions) +} +@(objc_type=LinkedFunctions, objc_name="setGroups") +LinkedFunctions_setGroups :: #force_inline proc(self: ^LinkedFunctions, groups: ^NS.Dictionary) { + msgSend(nil, self, "setGroups:", groups) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + PipelineBufferDescriptor +Class Methods: + alloc +Methods: + init + mutability + setMutability +*/ +@(objc_class="MTLPipelineBufferDescriptor") +PipelineBufferDescriptor :: struct { using _: NS.Copying(PipelineBufferDescriptor) } + +@(objc_type=PipelineBufferDescriptor, objc_name="alloc", objc_is_class_method=true) +PipelineBufferDescriptor_alloc :: #force_inline proc() -> ^PipelineBufferDescriptor { + return msgSend(^PipelineBufferDescriptor, PipelineBufferDescriptor, "alloc") +} +@(objc_type=PipelineBufferDescriptor, objc_name="init") +PipelineBufferDescriptor_init :: #force_inline proc(self: ^PipelineBufferDescriptor) -> ^PipelineBufferDescriptor { + return msgSend(^PipelineBufferDescriptor, self, "init") +} +@(objc_type=PipelineBufferDescriptor, objc_name="mutability") +PipelineBufferDescriptor_mutability :: #force_inline proc(self: ^PipelineBufferDescriptor) -> Mutability { + return msgSend(Mutability, self, "mutability") +} +@(objc_type=PipelineBufferDescriptor, objc_name="setMutability") +PipelineBufferDescriptor_setMutability :: #force_inline proc(self: ^PipelineBufferDescriptor, mutability: Mutability) { + msgSend(nil, self, "setMutability:", mutability) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + PipelineBufferDescriptorArray +Class Methods: + alloc +Methods: + init + objectAtIndexedSubscript + setObject +*/ +@(objc_class="MTLPipelineBufferDescriptorArray") +PipelineBufferDescriptorArray :: struct { using _: NS.Object } + +@(objc_type=PipelineBufferDescriptorArray, objc_name="alloc", objc_is_class_method=true) +PipelineBufferDescriptorArray_alloc :: #force_inline proc() -> ^PipelineBufferDescriptorArray { + return msgSend(^PipelineBufferDescriptorArray, PipelineBufferDescriptorArray, "alloc") +} +@(objc_type=PipelineBufferDescriptorArray, objc_name="init") +PipelineBufferDescriptorArray_init :: #force_inline proc(self: ^PipelineBufferDescriptorArray) -> ^PipelineBufferDescriptorArray { + return msgSend(^PipelineBufferDescriptorArray, self, "init") +} +@(objc_type=PipelineBufferDescriptorArray, objc_name="object") +PipelineBufferDescriptorArray_object :: #force_inline proc(self: ^PipelineBufferDescriptorArray, bufferIndex: NS.UInteger) -> ^PipelineBufferDescriptor { + return msgSend(^PipelineBufferDescriptor, self, "objectAtIndexedSubscript:", bufferIndex) +} +@(objc_type=PipelineBufferDescriptorArray, objc_name="setObject") +PipelineBufferDescriptorArray_setObject :: #force_inline proc(self: ^PipelineBufferDescriptorArray, buffer: ^PipelineBufferDescriptor, bufferIndex: NS.UInteger) { + msgSend(nil, self, "setObject:atIndexedSubscript:", buffer, bufferIndex) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + PointerType +Class Methods: + alloc +Methods: + init + access + alignment + dataSize + elementArrayType + elementIsArgumentBuffer + elementStructType + elementType +*/ +@(objc_class="MTLPointerType") +PointerType :: struct { using _: Type } + +@(objc_type=PointerType, objc_name="alloc", objc_is_class_method=true) +PointerType_alloc :: #force_inline proc() -> ^PointerType { + return msgSend(^PointerType, PointerType, "alloc") +} +@(objc_type=PointerType, objc_name="init") +PointerType_init :: #force_inline proc(self: ^PointerType) -> ^PointerType { + return msgSend(^PointerType, self, "init") +} +@(objc_type=PointerType, objc_name="access") +PointerType_access :: #force_inline proc(self: ^PointerType) -> ArgumentAccess { + return msgSend(ArgumentAccess, self, "access") +} +@(objc_type=PointerType, objc_name="alignment") +PointerType_alignment :: #force_inline proc(self: ^PointerType) -> NS.UInteger { + return msgSend(NS.UInteger, self, "alignment") +} +@(objc_type=PointerType, objc_name="dataSize") +PointerType_dataSize :: #force_inline proc(self: ^PointerType) -> NS.UInteger { + return msgSend(NS.UInteger, self, "dataSize") +} +@(objc_type=PointerType, objc_name="elementArrayType") +PointerType_elementArrayType :: #force_inline proc(self: ^PointerType) -> ^ArrayType { + return msgSend(^ArrayType, self, "elementArrayType") +} +@(objc_type=PointerType, objc_name="elementIsArgumentBuffer") +PointerType_elementIsArgumentBuffer :: #force_inline proc(self: ^PointerType) -> BOOL { + return msgSend(BOOL, self, "elementIsArgumentBuffer") +} +@(objc_type=PointerType, objc_name="elementStructType") +PointerType_elementStructType :: #force_inline proc(self: ^PointerType) -> ^StructType { + return msgSend(^StructType, self, "elementStructType") +} +@(objc_type=PointerType, objc_name="elementType") +PointerType_elementType :: #force_inline proc(self: ^PointerType) -> DataType { + return msgSend(DataType, self, "elementType") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + PrimitiveAccelerationStructureDescriptor +Class Methods: + alloc + descriptor +Methods: + init + geometryDescriptors + setGeometryDescriptors +*/ +@(objc_class="MTLPrimitiveAccelerationStructureDescriptor") +PrimitiveAccelerationStructureDescriptor :: struct { using _: NS.Copying(PrimitiveAccelerationStructureDescriptor), using _: AccelerationStructureDescriptor } + +@(objc_type=PrimitiveAccelerationStructureDescriptor, objc_name="alloc", objc_is_class_method=true) +PrimitiveAccelerationStructureDescriptor_alloc :: #force_inline proc() -> ^PrimitiveAccelerationStructureDescriptor { + return msgSend(^PrimitiveAccelerationStructureDescriptor, PrimitiveAccelerationStructureDescriptor, "alloc") +} +@(objc_type=PrimitiveAccelerationStructureDescriptor, objc_name="init") +PrimitiveAccelerationStructureDescriptor_init :: #force_inline proc(self: ^PrimitiveAccelerationStructureDescriptor) -> ^PrimitiveAccelerationStructureDescriptor { + return msgSend(^PrimitiveAccelerationStructureDescriptor, self, "init") +} +@(objc_type=PrimitiveAccelerationStructureDescriptor, objc_name="descriptor", objc_is_class_method=true) +PrimitiveAccelerationStructureDescriptor_descriptor :: #force_inline proc() -> ^PrimitiveAccelerationStructureDescriptor { + return msgSend(^PrimitiveAccelerationStructureDescriptor, PrimitiveAccelerationStructureDescriptor, "descriptor") +} +@(objc_type=PrimitiveAccelerationStructureDescriptor, objc_name="geometryDescriptors") +PrimitiveAccelerationStructureDescriptor_geometryDescriptors :: #force_inline proc(self: ^PrimitiveAccelerationStructureDescriptor) -> ^NS.Array { + return msgSend(^NS.Array, self, "geometryDescriptors") +} +@(objc_type=PrimitiveAccelerationStructureDescriptor, objc_name="setGeometryDescriptors") +PrimitiveAccelerationStructureDescriptor_setGeometryDescriptors :: #force_inline proc(self: ^PrimitiveAccelerationStructureDescriptor, geometryDescriptors: ^NS.Array) { + msgSend(nil, self, "setGeometryDescriptors:", geometryDescriptors) +} + +@(objc_type=PrimitiveAccelerationStructureDescriptor, objc_name="motionStartBorderMode") +PrimitiveAccelerationStructureDescriptor_motionStartBorderMode :: #force_inline proc(self: ^PrimitiveAccelerationStructureDescriptor) -> MotionBorderMode { + return msgSend(MotionBorderMode, self, "motionStartBorderMode") +} +@(objc_type=PrimitiveAccelerationStructureDescriptor, objc_name="setMotionStartBorderMode") +PrimitiveAccelerationStructureDescriptor_setMotionStartBorderMode :: #force_inline proc(self: ^PrimitiveAccelerationStructureDescriptor, motionStartBorderMode: MotionBorderMode) { + msgSend(nil, self, "setMotionStartBorderMode:", motionStartBorderMode) +} + +@(objc_type=PrimitiveAccelerationStructureDescriptor, objc_name="motionEndBorderMode") +PrimitiveAccelerationStructureDescriptor_motionEndBorderMode :: #force_inline proc(self: ^PrimitiveAccelerationStructureDescriptor) -> MotionBorderMode { + return msgSend(MotionBorderMode, self, "motionEndBorderMode") +} +@(objc_type=PrimitiveAccelerationStructureDescriptor, objc_name="setMotionEndBorderMode") +PrimitiveAccelerationStructureDescriptor_setMotionEndBorderMode :: #force_inline proc(self: ^PrimitiveAccelerationStructureDescriptor, motionEndBorderMode: MotionBorderMode) { + msgSend(nil, self, "setMotionEndBorderMode:", motionEndBorderMode) +} + +@(objc_type=PrimitiveAccelerationStructureDescriptor, objc_name="motionStartTime") +PrimitiveAccelerationStructureDescriptor_motionStartTime :: #force_inline proc(self: ^PrimitiveAccelerationStructureDescriptor) -> f32 { + return msgSend(f32, self, "motionStartTime") +} +@(objc_type=PrimitiveAccelerationStructureDescriptor, objc_name="setMotionStartTime") +PrimitiveAccelerationStructureDescriptor_setMotionStartTime :: #force_inline proc(self: ^PrimitiveAccelerationStructureDescriptor, motionStartTime: f32) { + msgSend(nil, self, "setMotionStartTime:", motionStartTime) +} + + +@(objc_type=PrimitiveAccelerationStructureDescriptor, objc_name="motionEndTime") +PrimitiveAccelerationStructureDescriptor_motionEndTime :: #force_inline proc(self: ^PrimitiveAccelerationStructureDescriptor) -> f32 { + return msgSend(f32, self, "motionEndTime") +} +@(objc_type=PrimitiveAccelerationStructureDescriptor, objc_name="setMotionEndTime") +PrimitiveAccelerationStructureDescriptor_setMotionEndTime :: #force_inline proc(self: ^PrimitiveAccelerationStructureDescriptor, motionEndTime: f32) { + msgSend(nil, self, "setMotionEndTime:", motionEndTime) +} + +@(objc_type=PrimitiveAccelerationStructureDescriptor, objc_name="motionKeyframeCount") +PrimitiveAccelerationStructureDescriptor_motionKeyframeCount :: #force_inline proc(self: ^PrimitiveAccelerationStructureDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "motionKeyframeCount") +} +@(objc_type=PrimitiveAccelerationStructureDescriptor, objc_name="setMotionKeyframeCount") +PrimitiveAccelerationStructureDescriptor_setMotionKeyframeCount :: #force_inline proc(self: ^PrimitiveAccelerationStructureDescriptor, motionKeyframeCount: NS.UInteger) { + msgSend(nil, self, "setMotionKeyframeCount:", motionKeyframeCount) +} + + + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + RasterizationRateLayerArray +Class Methods: + alloc +Methods: + init + objectAtIndexedSubscript + setObject +*/ +@(objc_class="MTLRasterizationRateLayerArray") +RasterizationRateLayerArray :: struct { using _: NS.Object } + +@(objc_type=RasterizationRateLayerArray, objc_name="alloc", objc_is_class_method=true) +RasterizationRateLayerArray_alloc :: #force_inline proc() -> ^RasterizationRateLayerArray { + return msgSend(^RasterizationRateLayerArray, RasterizationRateLayerArray, "alloc") +} +@(objc_type=RasterizationRateLayerArray, objc_name="init") +RasterizationRateLayerArray_init :: #force_inline proc(self: ^RasterizationRateLayerArray) -> ^RasterizationRateLayerArray { + return msgSend(^RasterizationRateLayerArray, self, "init") +} +@(objc_type=RasterizationRateLayerArray, objc_name="object") +RasterizationRateLayerArray_object :: #force_inline proc(self: ^RasterizationRateLayerArray, layerIndex: NS.UInteger) -> ^RasterizationRateLayerDescriptor { + return msgSend(^RasterizationRateLayerDescriptor, self, "objectAtIndexedSubscript:", layerIndex) +} +@(objc_type=RasterizationRateLayerArray, objc_name="setObject") +RasterizationRateLayerArray_setObject :: #force_inline proc(self: ^RasterizationRateLayerArray, layer: ^RasterizationRateLayerDescriptor, layerIndex: NS.UInteger) { + msgSend(nil, self, "setObject:atIndexedSubscript:", layer, layerIndex) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + RasterizationRateLayerDescriptor +Class Methods: + alloc +Methods: + horizontal + horizontalSampleStorage + init + initWithSampleCount + initWithSampleCount + sampleCount + vertical + verticalSampleStorage +*/ +@(objc_class="MTLRasterizationRateLayerDescriptor") +RasterizationRateLayerDescriptor :: struct { using _: NS.Copying(RasterizationRateLayerDescriptor) } + +@(objc_type=RasterizationRateLayerDescriptor, objc_name="alloc", objc_is_class_method=true) +RasterizationRateLayerDescriptor_alloc :: #force_inline proc() -> ^RasterizationRateLayerDescriptor { + return msgSend(^RasterizationRateLayerDescriptor, RasterizationRateLayerDescriptor, "alloc") +} +@(objc_type=RasterizationRateLayerDescriptor, objc_name="horizontal") +RasterizationRateLayerDescriptor_horizontal :: #force_inline proc(self: ^RasterizationRateLayerDescriptor) -> ^RasterizationRateSampleArray { + return msgSend(^RasterizationRateSampleArray, self, "horizontal") +} +@(objc_type=RasterizationRateLayerDescriptor, objc_name="horizontalSampleStorage") +RasterizationRateLayerDescriptor_horizontalSampleStorage :: #force_inline proc(self: ^RasterizationRateLayerDescriptor) -> [^]f32 { // TODO: how could this be made into a slice? + return msgSend([^]f32, self, "horizontalSampleStorage") +} +@(objc_type=RasterizationRateLayerDescriptor, objc_name="init") +RasterizationRateLayerDescriptor_init :: #force_inline proc(self: ^RasterizationRateLayerDescriptor) -> ^RasterizationRateLayerDescriptor { + return msgSend(^RasterizationRateLayerDescriptor, self, "init") +} +@(objc_type=RasterizationRateLayerDescriptor, objc_name="initWithSampleCount") +RasterizationRateLayerDescriptor_initWithSampleCount :: #force_inline proc(self: ^RasterizationRateLayerDescriptor, sampleCount: Size) -> ^RasterizationRateLayerDescriptor { + return msgSend(^RasterizationRateLayerDescriptor, self, "initWithSampleCount:", sampleCount) +} +@(objc_type=RasterizationRateLayerDescriptor, objc_name="initWithSampleCountWithDimensions") +RasterizationRateLayerDescriptor_initWithSampleCountWithDimensions :: #force_inline proc(self: ^RasterizationRateLayerDescriptor, sampleCount: Size, horizontal: []f32, vertical: []f32) -> ^RasterizationRateLayerDescriptor { + return msgSend(^RasterizationRateLayerDescriptor, self, "initWithSampleCount:horizontal:vertical:", sampleCount, raw_data(horizontal), raw_data(vertical)) +} +@(objc_type=RasterizationRateLayerDescriptor, objc_name="sampleCount") +RasterizationRateLayerDescriptor_sampleCount :: #force_inline proc(self: ^RasterizationRateLayerDescriptor) -> Size { + return msgSend(Size, self, "sampleCount") +} +@(objc_type=RasterizationRateLayerDescriptor, objc_name="vertical") +RasterizationRateLayerDescriptor_vertical :: #force_inline proc(self: ^RasterizationRateLayerDescriptor) -> ^RasterizationRateSampleArray { + return msgSend(^RasterizationRateSampleArray, self, "vertical") +} +@(objc_type=RasterizationRateLayerDescriptor, objc_name="verticalSampleStorage") +RasterizationRateLayerDescriptor_verticalSampleStorage :: #force_inline proc(self: ^RasterizationRateLayerDescriptor) -> [^]f32 { // TODO: how could this be made into a slice? + return msgSend([^]f32, self, "verticalSampleStorage") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + RasterizationRateMapDescriptor +Class Methods: + alloc + rasterizationRateMapDescriptorWithScreenSize + rasterizationRateMapDescriptorWithScreenSize + rasterizationRateMapDescriptorWithScreenSize +Methods: + init + label + layerAtIndex + layerCount + layers + screenSize + setLabel + setLayer + setScreenSize +*/ +@(objc_class="MTLRasterizationRateMapDescriptor") +RasterizationRateMapDescriptor :: struct { using _: NS.Copying(RasterizationRateMapDescriptor) } + +@(objc_type=RasterizationRateMapDescriptor, objc_name="alloc", objc_is_class_method=true) +RasterizationRateMapDescriptor_alloc :: #force_inline proc() -> ^RasterizationRateMapDescriptor { + return msgSend(^RasterizationRateMapDescriptor, RasterizationRateMapDescriptor, "alloc") +} +@(objc_type=RasterizationRateMapDescriptor, objc_name="init") +RasterizationRateMapDescriptor_init :: #force_inline proc(self: ^RasterizationRateMapDescriptor) -> ^RasterizationRateMapDescriptor { + return msgSend(^RasterizationRateMapDescriptor, self, "init") +} +@(objc_type=RasterizationRateMapDescriptor, objc_name="label") +RasterizationRateMapDescriptor_label :: #force_inline proc(self: ^RasterizationRateMapDescriptor) -> ^NS.String { + return msgSend(^NS.String, self, "label") +} +@(objc_type=RasterizationRateMapDescriptor, objc_name="layer") +RasterizationRateMapDescriptor_layer :: #force_inline proc(self: ^RasterizationRateMapDescriptor, layerIndex: NS.UInteger) -> ^RasterizationRateLayerDescriptor { + return msgSend(^RasterizationRateLayerDescriptor, self, "layerAtIndex:", layerIndex) +} +@(objc_type=RasterizationRateMapDescriptor, objc_name="layerCount") +RasterizationRateMapDescriptor_layerCount :: #force_inline proc(self: ^RasterizationRateMapDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "layerCount") +} +@(objc_type=RasterizationRateMapDescriptor, objc_name="layers") +RasterizationRateMapDescriptor_layers :: #force_inline proc(self: ^RasterizationRateMapDescriptor) -> ^RasterizationRateLayerArray { + return msgSend(^RasterizationRateLayerArray, self, "layers") +} +@(objc_type=RasterizationRateMapDescriptor, objc_name="rasterizationRateMapDescriptorWithScreenSize", objc_is_class_method=true) +RasterizationRateMapDescriptor_rasterizationRateMapDescriptorWithScreenSize :: #force_inline proc(screenSize: Size) -> ^RasterizationRateMapDescriptor { + return msgSend(^RasterizationRateMapDescriptor, RasterizationRateMapDescriptor, "rasterizationRateMapDescriptorWithScreenSize:", screenSize) +} +@(objc_type=RasterizationRateMapDescriptor, objc_name="rasterizationRateMapDescriptorWithScreenSizeWithLayer", objc_is_class_method=true) +RasterizationRateMapDescriptor_rasterizationRateMapDescriptorWithScreenSizeWithLayer :: #force_inline proc(screenSize: Size, layer: ^RasterizationRateLayerDescriptor) -> ^RasterizationRateMapDescriptor { + return msgSend(^RasterizationRateMapDescriptor, RasterizationRateMapDescriptor, "rasterizationRateMapDescriptorWithScreenSize:layer:", screenSize, layer) +} +@(objc_type=RasterizationRateMapDescriptor, objc_name="rasterizationRateMapDescriptorWithScreenSizeWithLayers", objc_is_class_method=true) +RasterizationRateMapDescriptor_rasterizationRateMapDescriptorWithScreenSizeWithLayers :: #force_inline proc(screenSize: Size, layers: []^RasterizationRateLayerDescriptor) -> ^RasterizationRateMapDescriptor { + return msgSend(^RasterizationRateMapDescriptor, RasterizationRateMapDescriptor, "rasterizationRateMapDescriptorWithScreenSize:layerCount:layers:", screenSize, NS.UInteger(len(layers)), layers) +} +@(objc_type=RasterizationRateMapDescriptor, objc_name="screenSize") +RasterizationRateMapDescriptor_screenSize :: #force_inline proc(self: ^RasterizationRateMapDescriptor) -> Size { + return msgSend(Size, self, "screenSize") +} +@(objc_type=RasterizationRateMapDescriptor, objc_name="setLabel") +RasterizationRateMapDescriptor_setLabel :: #force_inline proc(self: ^RasterizationRateMapDescriptor, label: ^NS.String) { + msgSend(nil, self, "setLabel:", label) +} +@(objc_type=RasterizationRateMapDescriptor, objc_name="setLayer") +RasterizationRateMapDescriptor_setLayer :: #force_inline proc(self: ^RasterizationRateMapDescriptor, layer: ^RasterizationRateLayerDescriptor, layerIndex: NS.UInteger) { + msgSend(nil, self, "setLayer:atIndex:", layer, layerIndex) +} +@(objc_type=RasterizationRateMapDescriptor, objc_name="setScreenSize") +RasterizationRateMapDescriptor_setScreenSize :: #force_inline proc(self: ^RasterizationRateMapDescriptor, screenSize: Size) { + msgSend(nil, self, "setScreenSize:", screenSize) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + RasterizationRateSampleArray +Class Methods: + alloc +Methods: + init + objectAtIndexedSubscript + setObject +*/ +@(objc_class="MTLRasterizationRateSampleArray") +RasterizationRateSampleArray :: struct { using _: NS.Object } + +@(objc_type=RasterizationRateSampleArray, objc_name="alloc", objc_is_class_method=true) +RasterizationRateSampleArray_alloc :: #force_inline proc() -> ^RasterizationRateSampleArray { + return msgSend(^RasterizationRateSampleArray, RasterizationRateSampleArray, "alloc") +} +@(objc_type=RasterizationRateSampleArray, objc_name="init") +RasterizationRateSampleArray_init :: #force_inline proc(self: ^RasterizationRateSampleArray) -> ^RasterizationRateSampleArray { + return msgSend(^RasterizationRateSampleArray, self, "init") +} +@(objc_type=RasterizationRateSampleArray, objc_name="object") +RasterizationRateSampleArray_object :: #force_inline proc(self: ^RasterizationRateSampleArray, index: NS.UInteger) -> ^NS.Number { + return msgSend(^NS.Number, self, "objectAtIndexedSubscript:", index) +} +@(objc_type=RasterizationRateSampleArray, objc_name="setObject") +RasterizationRateSampleArray_setObject :: #force_inline proc(self: ^RasterizationRateSampleArray, value: ^NS.Number, index: NS.UInteger) { + msgSend(nil, self, "setObject:atIndexedSubscript:", value, index) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + RenderPassAttachmentDescriptor +Class Methods: + alloc +Methods: + init + depthPlane + level + loadAction + resolveDepthPlane + resolveLevel + resolveSlice + resolveTexture + setDepthPlane + setLevel + setLoadAction + setResolveDepthPlane + setResolveLevel + setResolveSlice + setResolveTexture + setSlice + setStoreAction + setStoreActionOptions + setTexture + slice + storeAction + storeActionOptions + texture +*/ +@(objc_class="MTLRenderPassAttachmentDescriptor") +RenderPassAttachmentDescriptor :: struct { using _: NS.Copying(RenderPassAttachmentDescriptor) } + +@(objc_type=RenderPassAttachmentDescriptor, objc_name="alloc", objc_is_class_method=true) +RenderPassAttachmentDescriptor_alloc :: #force_inline proc() -> ^RenderPassAttachmentDescriptor { + return msgSend(^RenderPassAttachmentDescriptor, RenderPassAttachmentDescriptor, "alloc") +} +@(objc_type=RenderPassAttachmentDescriptor, objc_name="init") +RenderPassAttachmentDescriptor_init :: #force_inline proc(self: ^RenderPassAttachmentDescriptor) -> ^RenderPassAttachmentDescriptor { + return msgSend(^RenderPassAttachmentDescriptor, self, "init") +} +@(objc_type=RenderPassAttachmentDescriptor, objc_name="depthPlane") +RenderPassAttachmentDescriptor_depthPlane :: #force_inline proc(self: ^RenderPassAttachmentDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "depthPlane") +} +@(objc_type=RenderPassAttachmentDescriptor, objc_name="level") +RenderPassAttachmentDescriptor_level :: #force_inline proc(self: ^RenderPassAttachmentDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "level") +} +@(objc_type=RenderPassAttachmentDescriptor, objc_name="loadAction") +RenderPassAttachmentDescriptor_loadAction :: #force_inline proc(self: ^RenderPassAttachmentDescriptor) -> LoadAction { + return msgSend(LoadAction, self, "loadAction") +} +@(objc_type=RenderPassAttachmentDescriptor, objc_name="resolveDepthPlane") +RenderPassAttachmentDescriptor_resolveDepthPlane :: #force_inline proc(self: ^RenderPassAttachmentDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "resolveDepthPlane") +} +@(objc_type=RenderPassAttachmentDescriptor, objc_name="resolveLevel") +RenderPassAttachmentDescriptor_resolveLevel :: #force_inline proc(self: ^RenderPassAttachmentDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "resolveLevel") +} +@(objc_type=RenderPassAttachmentDescriptor, objc_name="resolveSlice") +RenderPassAttachmentDescriptor_resolveSlice :: #force_inline proc(self: ^RenderPassAttachmentDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "resolveSlice") +} +@(objc_type=RenderPassAttachmentDescriptor, objc_name="resolveTexture") +RenderPassAttachmentDescriptor_resolveTexture :: #force_inline proc(self: ^RenderPassAttachmentDescriptor) -> ^Texture { + return msgSend(^Texture, self, "resolveTexture") +} +@(objc_type=RenderPassAttachmentDescriptor, objc_name="setDepthPlane") +RenderPassAttachmentDescriptor_setDepthPlane :: #force_inline proc(self: ^RenderPassAttachmentDescriptor, depthPlane: NS.UInteger) { + msgSend(nil, self, "setDepthPlane:", depthPlane) +} +@(objc_type=RenderPassAttachmentDescriptor, objc_name="setLevel") +RenderPassAttachmentDescriptor_setLevel :: #force_inline proc(self: ^RenderPassAttachmentDescriptor, level: NS.UInteger) { + msgSend(nil, self, "setLevel:", level) +} +@(objc_type=RenderPassAttachmentDescriptor, objc_name="setLoadAction") +RenderPassAttachmentDescriptor_setLoadAction :: #force_inline proc(self: ^RenderPassAttachmentDescriptor, loadAction: LoadAction) { + msgSend(nil, self, "setLoadAction:", loadAction) +} +@(objc_type=RenderPassAttachmentDescriptor, objc_name="setResolveDepthPlane") +RenderPassAttachmentDescriptor_setResolveDepthPlane :: #force_inline proc(self: ^RenderPassAttachmentDescriptor, resolveDepthPlane: NS.UInteger) { + msgSend(nil, self, "setResolveDepthPlane:", resolveDepthPlane) +} +@(objc_type=RenderPassAttachmentDescriptor, objc_name="setResolveLevel") +RenderPassAttachmentDescriptor_setResolveLevel :: #force_inline proc(self: ^RenderPassAttachmentDescriptor, resolveLevel: NS.UInteger) { + msgSend(nil, self, "setResolveLevel:", resolveLevel) +} +@(objc_type=RenderPassAttachmentDescriptor, objc_name="setResolveSlice") +RenderPassAttachmentDescriptor_setResolveSlice :: #force_inline proc(self: ^RenderPassAttachmentDescriptor, resolveSlice: NS.UInteger) { + msgSend(nil, self, "setResolveSlice:", resolveSlice) +} +@(objc_type=RenderPassAttachmentDescriptor, objc_name="setResolveTexture") +RenderPassAttachmentDescriptor_setResolveTexture :: #force_inline proc(self: ^RenderPassAttachmentDescriptor, resolveTexture: ^Texture) { + msgSend(nil, self, "setResolveTexture:", resolveTexture) +} +@(objc_type=RenderPassAttachmentDescriptor, objc_name="setSlice") +RenderPassAttachmentDescriptor_setSlice :: #force_inline proc(self: ^RenderPassAttachmentDescriptor, slice: NS.UInteger) { + msgSend(nil, self, "setSlice:", slice) +} +@(objc_type=RenderPassAttachmentDescriptor, objc_name="setStoreAction") +RenderPassAttachmentDescriptor_setStoreAction :: #force_inline proc(self: ^RenderPassAttachmentDescriptor, storeAction: StoreAction) { + msgSend(nil, self, "setStoreAction:", storeAction) +} +@(objc_type=RenderPassAttachmentDescriptor, objc_name="setStoreActionOptions") +RenderPassAttachmentDescriptor_setStoreActionOptions :: #force_inline proc(self: ^RenderPassAttachmentDescriptor, storeActionOptions: StoreActionOptions) { + msgSend(nil, self, "setStoreActionOptions:", storeActionOptions) +} +@(objc_type=RenderPassAttachmentDescriptor, objc_name="setTexture") +RenderPassAttachmentDescriptor_setTexture :: #force_inline proc(self: ^RenderPassAttachmentDescriptor, texture: ^Texture) { + msgSend(nil, self, "setTexture:", texture) +} +@(objc_type=RenderPassAttachmentDescriptor, objc_name="slice") +RenderPassAttachmentDescriptor_slice :: #force_inline proc(self: ^RenderPassAttachmentDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "slice") +} +@(objc_type=RenderPassAttachmentDescriptor, objc_name="storeAction") +RenderPassAttachmentDescriptor_storeAction :: #force_inline proc(self: ^RenderPassAttachmentDescriptor) -> StoreAction { + return msgSend(StoreAction, self, "storeAction") +} +@(objc_type=RenderPassAttachmentDescriptor, objc_name="storeActionOptions") +RenderPassAttachmentDescriptor_storeActionOptions :: #force_inline proc(self: ^RenderPassAttachmentDescriptor) -> StoreActionOptions { + return msgSend(StoreActionOptions, self, "storeActionOptions") +} +@(objc_type=RenderPassAttachmentDescriptor, objc_name="texture") +RenderPassAttachmentDescriptor_texture :: #force_inline proc(self: ^RenderPassAttachmentDescriptor) -> ^Texture { + return msgSend(^Texture, self, "texture") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + RenderPassColorAttachmentDescriptor +Class Methods: + alloc +Methods: + init + clearColor + setClearColor +*/ +@(objc_class="MTLRenderPassColorAttachmentDescriptor") +RenderPassColorAttachmentDescriptor :: struct { using _: NS.Copying(RenderPassColorAttachmentDescriptor), using _: RenderPassAttachmentDescriptor } + +@(objc_type=RenderPassColorAttachmentDescriptor, objc_name="alloc", objc_is_class_method=true) +RenderPassColorAttachmentDescriptor_alloc :: #force_inline proc() -> ^RenderPassColorAttachmentDescriptor { + return msgSend(^RenderPassColorAttachmentDescriptor, RenderPassColorAttachmentDescriptor, "alloc") +} +@(objc_type=RenderPassColorAttachmentDescriptor, objc_name="init") +RenderPassColorAttachmentDescriptor_init :: #force_inline proc(self: ^RenderPassColorAttachmentDescriptor) -> ^RenderPassColorAttachmentDescriptor { + return msgSend(^RenderPassColorAttachmentDescriptor, self, "init") +} +@(objc_type=RenderPassColorAttachmentDescriptor, objc_name="clearColor") +RenderPassColorAttachmentDescriptor_clearColor :: #force_inline proc(self: ^RenderPassColorAttachmentDescriptor) -> ClearColor { + return msgSend(ClearColor, self, "clearColor") +} +@(objc_type=RenderPassColorAttachmentDescriptor, objc_name="setClearColor") +RenderPassColorAttachmentDescriptor_setClearColor :: #force_inline proc(self: ^RenderPassColorAttachmentDescriptor, clearColor: ClearColor) { + msgSend(nil, self, "setClearColor:", clearColor) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + RenderPassColorAttachmentDescriptorArray +Class Methods: + alloc +Methods: + init + objectAtIndexedSubscript + setObject +*/ +@(objc_class="MTLRenderPassColorAttachmentDescriptorArray") +RenderPassColorAttachmentDescriptorArray :: struct { using _: NS.Object } + +@(objc_type=RenderPassColorAttachmentDescriptorArray, objc_name="alloc", objc_is_class_method=true) +RenderPassColorAttachmentDescriptorArray_alloc :: #force_inline proc() -> ^RenderPassColorAttachmentDescriptorArray { + return msgSend(^RenderPassColorAttachmentDescriptorArray, RenderPassColorAttachmentDescriptorArray, "alloc") +} +@(objc_type=RenderPassColorAttachmentDescriptorArray, objc_name="init") +RenderPassColorAttachmentDescriptorArray_init :: #force_inline proc(self: ^RenderPassColorAttachmentDescriptorArray) -> ^RenderPassColorAttachmentDescriptorArray { + return msgSend(^RenderPassColorAttachmentDescriptorArray, self, "init") +} +@(objc_type=RenderPassColorAttachmentDescriptorArray, objc_name="object") +RenderPassColorAttachmentDescriptorArray_object :: #force_inline proc(self: ^RenderPassColorAttachmentDescriptorArray, attachmentIndex: NS.UInteger) -> ^RenderPassColorAttachmentDescriptor { + return msgSend(^RenderPassColorAttachmentDescriptor, self, "objectAtIndexedSubscript:", attachmentIndex) +} +@(objc_type=RenderPassColorAttachmentDescriptorArray, objc_name="setObject") +RenderPassColorAttachmentDescriptorArray_setObject :: #force_inline proc(self: ^RenderPassColorAttachmentDescriptorArray, attachment: ^RenderPassColorAttachmentDescriptor, attachmentIndex: NS.UInteger) { + msgSend(nil, self, "setObject:atIndexedSubscript:", attachment, attachmentIndex) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + RenderPassDepthAttachmentDescriptor +Class Methods: + alloc +Methods: + init + clearDepth + depthResolveFilter + setClearDepth + setDepthResolveFilter +*/ +@(objc_class="MTLRenderPassDepthAttachmentDescriptor") +RenderPassDepthAttachmentDescriptor :: struct { using _: NS.Copying(RenderPassDepthAttachmentDescriptor), using _: RenderPassAttachmentDescriptor } + +@(objc_type=RenderPassDepthAttachmentDescriptor, objc_name="alloc", objc_is_class_method=true) +RenderPassDepthAttachmentDescriptor_alloc :: #force_inline proc() -> ^RenderPassDepthAttachmentDescriptor { + return msgSend(^RenderPassDepthAttachmentDescriptor, RenderPassDepthAttachmentDescriptor, "alloc") +} +@(objc_type=RenderPassDepthAttachmentDescriptor, objc_name="init") +RenderPassDepthAttachmentDescriptor_init :: #force_inline proc(self: ^RenderPassDepthAttachmentDescriptor) -> ^RenderPassDepthAttachmentDescriptor { + return msgSend(^RenderPassDepthAttachmentDescriptor, self, "init") +} +@(objc_type=RenderPassDepthAttachmentDescriptor, objc_name="clearDepth") +RenderPassDepthAttachmentDescriptor_clearDepth :: #force_inline proc(self: ^RenderPassDepthAttachmentDescriptor) -> f64 { + return msgSend(f64, self, "clearDepth") +} +@(objc_type=RenderPassDepthAttachmentDescriptor, objc_name="depthResolveFilter") +RenderPassDepthAttachmentDescriptor_depthResolveFilter :: #force_inline proc(self: ^RenderPassDepthAttachmentDescriptor) -> MultisampleDepthResolveFilter { + return msgSend(MultisampleDepthResolveFilter, self, "depthResolveFilter") +} +@(objc_type=RenderPassDepthAttachmentDescriptor, objc_name="setClearDepth") +RenderPassDepthAttachmentDescriptor_setClearDepth :: #force_inline proc(self: ^RenderPassDepthAttachmentDescriptor, clearDepth: f64) { + msgSend(nil, self, "setClearDepth:", clearDepth) +} +@(objc_type=RenderPassDepthAttachmentDescriptor, objc_name="setDepthResolveFilter") +RenderPassDepthAttachmentDescriptor_setDepthResolveFilter :: #force_inline proc(self: ^RenderPassDepthAttachmentDescriptor, depthResolveFilter: MultisampleDepthResolveFilter) { + msgSend(nil, self, "setDepthResolveFilter:", depthResolveFilter) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + RenderPassDescriptor +Class Methods: + alloc + renderPassDescriptor +Methods: + init + colorAttachments + defaultRasterSampleCount + depthAttachment + getSamplePositions + imageblockSampleLength + rasterizationRateMap + renderTargetArrayLength + renderTargetHeight + renderTargetWidth + sampleBufferAttachments + setDefaultRasterSampleCount + setDepthAttachment + setImageblockSampleLength + setRasterizationRateMap + setRenderTargetArrayLength + setRenderTargetHeight + setRenderTargetWidth + setSamplePositions + setStencilAttachment + setThreadgroupMemoryLength + setTileHeight + setTileWidth + setVisibilityResultBuffer + stencilAttachment + threadgroupMemoryLength + tileHeight + tileWidth + visibilityResultBuffer +*/ +@(objc_class="MTLRenderPassDescriptor") +RenderPassDescriptor :: struct { using _: NS.Copying(RenderPassDescriptor), using _: AccelerationStructureDescriptor } + +@(objc_type=RenderPassDescriptor, objc_name="alloc", objc_is_class_method=true) +RenderPassDescriptor_alloc :: #force_inline proc() -> ^RenderPassDescriptor { + return msgSend(^RenderPassDescriptor, RenderPassDescriptor, "alloc") +} +@(objc_type=RenderPassDescriptor, objc_name="init") +RenderPassDescriptor_init :: #force_inline proc(self: ^RenderPassDescriptor) -> ^RenderPassDescriptor { + return msgSend(^RenderPassDescriptor, self, "init") +} +@(objc_type=RenderPassDescriptor, objc_name="colorAttachments") +RenderPassDescriptor_colorAttachments :: #force_inline proc(self: ^RenderPassDescriptor) -> ^RenderPassColorAttachmentDescriptorArray { + return msgSend(^RenderPassColorAttachmentDescriptorArray, self, "colorAttachments") +} +@(objc_type=RenderPassDescriptor, objc_name="defaultRasterSampleCount") +RenderPassDescriptor_defaultRasterSampleCount :: #force_inline proc(self: ^RenderPassDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "defaultRasterSampleCount") +} +@(objc_type=RenderPassDescriptor, objc_name="depthAttachment") +RenderPassDescriptor_depthAttachment :: #force_inline proc(self: ^RenderPassDescriptor) -> ^RenderPassDepthAttachmentDescriptor { + return msgSend(^RenderPassDepthAttachmentDescriptor, self, "depthAttachment") +} +@(objc_type=RenderPassDescriptor, objc_name="getSamplePositions") +RenderPassDescriptor_getSamplePositions :: #force_inline proc(self: ^RenderPassDescriptor, positions: []SamplePosition) -> NS.UInteger { + return msgSend(NS.UInteger, self, "getSamplePositions:count:", raw_data(positions), NS.UInteger(len(positions))) +} +@(objc_type=RenderPassDescriptor, objc_name="imageblockSampleLength") +RenderPassDescriptor_imageblockSampleLength :: #force_inline proc(self: ^RenderPassDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "imageblockSampleLength") +} +@(objc_type=RenderPassDescriptor, objc_name="rasterizationRateMap") +RenderPassDescriptor_rasterizationRateMap :: #force_inline proc(self: ^RenderPassDescriptor) -> ^RasterizationRateMap { + return msgSend(^RasterizationRateMap, self, "rasterizationRateMap") +} +@(objc_type=RenderPassDescriptor, objc_name="renderPassDescriptor", objc_is_class_method=true) +RenderPassDescriptor_renderPassDescriptor :: #force_inline proc() -> ^RenderPassDescriptor { + return msgSend(^RenderPassDescriptor, RenderPassDescriptor, "renderPassDescriptor") +} +@(objc_type=RenderPassDescriptor, objc_name="renderTargetArrayLength") +RenderPassDescriptor_renderTargetArrayLength :: #force_inline proc(self: ^RenderPassDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "renderTargetArrayLength") +} +@(objc_type=RenderPassDescriptor, objc_name="renderTargetHeight") +RenderPassDescriptor_renderTargetHeight :: #force_inline proc(self: ^RenderPassDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "renderTargetHeight") +} +@(objc_type=RenderPassDescriptor, objc_name="renderTargetWidth") +RenderPassDescriptor_renderTargetWidth :: #force_inline proc(self: ^RenderPassDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "renderTargetWidth") +} +@(objc_type=RenderPassDescriptor, objc_name="sampleBufferAttachments") +RenderPassDescriptor_sampleBufferAttachments :: #force_inline proc(self: ^RenderPassDescriptor) -> ^RenderPassSampleBufferAttachmentDescriptorArray { + return msgSend(^RenderPassSampleBufferAttachmentDescriptorArray, self, "sampleBufferAttachments") +} +@(objc_type=RenderPassDescriptor, objc_name="setDefaultRasterSampleCount") +RenderPassDescriptor_setDefaultRasterSampleCount :: #force_inline proc(self: ^RenderPassDescriptor, defaultRasterSampleCount: NS.UInteger) { + msgSend(nil, self, "setDefaultRasterSampleCount:", defaultRasterSampleCount) +} +@(objc_type=RenderPassDescriptor, objc_name="setDepthAttachment") +RenderPassDescriptor_setDepthAttachment :: #force_inline proc(self: ^RenderPassDescriptor, depthAttachment: ^RenderPassDepthAttachmentDescriptor) { + msgSend(nil, self, "setDepthAttachment:", depthAttachment) +} +@(objc_type=RenderPassDescriptor, objc_name="setImageblockSampleLength") +RenderPassDescriptor_setImageblockSampleLength :: #force_inline proc(self: ^RenderPassDescriptor, imageblockSampleLength: NS.UInteger) { + msgSend(nil, self, "setImageblockSampleLength:", imageblockSampleLength) +} +@(objc_type=RenderPassDescriptor, objc_name="setRasterizationRateMap") +RenderPassDescriptor_setRasterizationRateMap :: #force_inline proc(self: ^RenderPassDescriptor, rasterizationRateMap: ^RasterizationRateMap) { + msgSend(nil, self, "setRasterizationRateMap:", rasterizationRateMap) +} +@(objc_type=RenderPassDescriptor, objc_name="setRenderTargetArrayLength") +RenderPassDescriptor_setRenderTargetArrayLength :: #force_inline proc(self: ^RenderPassDescriptor, renderTargetArrayLength: NS.UInteger) { + msgSend(nil, self, "setRenderTargetArrayLength:", renderTargetArrayLength) +} +@(objc_type=RenderPassDescriptor, objc_name="setRenderTargetHeight") +RenderPassDescriptor_setRenderTargetHeight :: #force_inline proc(self: ^RenderPassDescriptor, renderTargetHeight: NS.UInteger) { + msgSend(nil, self, "setRenderTargetHeight:", renderTargetHeight) +} +@(objc_type=RenderPassDescriptor, objc_name="setRenderTargetWidth") +RenderPassDescriptor_setRenderTargetWidth :: #force_inline proc(self: ^RenderPassDescriptor, renderTargetWidth: NS.UInteger) { + msgSend(nil, self, "setRenderTargetWidth:", renderTargetWidth) +} +@(objc_type=RenderPassDescriptor, objc_name="setSamplePositions") +RenderPassDescriptor_setSamplePositions :: #force_inline proc(self: ^RenderPassDescriptor, positions: []SamplePosition) { + msgSend(nil, self, "setSamplePositions:count:", raw_data(positions), NS.UInteger(len(positions))) +} +@(objc_type=RenderPassDescriptor, objc_name="setStencilAttachment") +RenderPassDescriptor_setStencilAttachment :: #force_inline proc(self: ^RenderPassDescriptor, stencilAttachment: ^RenderPassStencilAttachmentDescriptor) { + msgSend(nil, self, "setStencilAttachment:", stencilAttachment) +} +@(objc_type=RenderPassDescriptor, objc_name="setThreadgroupMemoryLength") +RenderPassDescriptor_setThreadgroupMemoryLength :: #force_inline proc(self: ^RenderPassDescriptor, threadgroupMemoryLength: NS.UInteger) { + msgSend(nil, self, "setThreadgroupMemoryLength:", threadgroupMemoryLength) +} +@(objc_type=RenderPassDescriptor, objc_name="setTileHeight") +RenderPassDescriptor_setTileHeight :: #force_inline proc(self: ^RenderPassDescriptor, tileHeight: NS.UInteger) { + msgSend(nil, self, "setTileHeight:", tileHeight) +} +@(objc_type=RenderPassDescriptor, objc_name="setTileWidth") +RenderPassDescriptor_setTileWidth :: #force_inline proc(self: ^RenderPassDescriptor, tileWidth: NS.UInteger) { + msgSend(nil, self, "setTileWidth:", tileWidth) +} +@(objc_type=RenderPassDescriptor, objc_name="setVisibilityResultBuffer") +RenderPassDescriptor_setVisibilityResultBuffer :: #force_inline proc(self: ^RenderPassDescriptor, visibilityResultBuffer: ^Buffer) { + msgSend(nil, self, "setVisibilityResultBuffer:", visibilityResultBuffer) +} +@(objc_type=RenderPassDescriptor, objc_name="stencilAttachment") +RenderPassDescriptor_stencilAttachment :: #force_inline proc(self: ^RenderPassDescriptor) -> ^RenderPassStencilAttachmentDescriptor { + return msgSend(^RenderPassStencilAttachmentDescriptor, self, "stencilAttachment") +} +@(objc_type=RenderPassDescriptor, objc_name="threadgroupMemoryLength") +RenderPassDescriptor_threadgroupMemoryLength :: #force_inline proc(self: ^RenderPassDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "threadgroupMemoryLength") +} +@(objc_type=RenderPassDescriptor, objc_name="tileHeight") +RenderPassDescriptor_tileHeight :: #force_inline proc(self: ^RenderPassDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "tileHeight") +} +@(objc_type=RenderPassDescriptor, objc_name="tileWidth") +RenderPassDescriptor_tileWidth :: #force_inline proc(self: ^RenderPassDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "tileWidth") +} +@(objc_type=RenderPassDescriptor, objc_name="visibilityResultBuffer") +RenderPassDescriptor_visibilityResultBuffer :: #force_inline proc(self: ^RenderPassDescriptor) -> ^Buffer { + return msgSend(^Buffer, self, "visibilityResultBuffer") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + RenderPassSampleBufferAttachmentDescriptor +Class Methods: + alloc +Methods: + init + endOfFragmentSampleIndex + endOfVertexSampleIndex + sampleBuffer + setEndOfFragmentSampleIndex + setEndOfVertexSampleIndex + setSampleBuffer + setStartOfFragmentSampleIndex + setStartOfVertexSampleIndex + startOfFragmentSampleIndex + startOfVertexSampleIndex +*/ +@(objc_class="MTLRenderPassSampleBufferAttachmentDescriptor") +RenderPassSampleBufferAttachmentDescriptor :: struct { using _: NS.Copying(RenderPassSampleBufferAttachmentDescriptor) } + +@(objc_type=RenderPassSampleBufferAttachmentDescriptor, objc_name="alloc", objc_is_class_method=true) +RenderPassSampleBufferAttachmentDescriptor_alloc :: #force_inline proc() -> ^RenderPassSampleBufferAttachmentDescriptor { + return msgSend(^RenderPassSampleBufferAttachmentDescriptor, RenderPassSampleBufferAttachmentDescriptor, "alloc") +} +@(objc_type=RenderPassSampleBufferAttachmentDescriptor, objc_name="init") +RenderPassSampleBufferAttachmentDescriptor_init :: #force_inline proc(self: ^RenderPassSampleBufferAttachmentDescriptor) -> ^RenderPassSampleBufferAttachmentDescriptor { + return msgSend(^RenderPassSampleBufferAttachmentDescriptor, self, "init") +} +@(objc_type=RenderPassSampleBufferAttachmentDescriptor, objc_name="endOfFragmentSampleIndex") +RenderPassSampleBufferAttachmentDescriptor_endOfFragmentSampleIndex :: #force_inline proc(self: ^RenderPassSampleBufferAttachmentDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "endOfFragmentSampleIndex") +} +@(objc_type=RenderPassSampleBufferAttachmentDescriptor, objc_name="endOfVertexSampleIndex") +RenderPassSampleBufferAttachmentDescriptor_endOfVertexSampleIndex :: #force_inline proc(self: ^RenderPassSampleBufferAttachmentDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "endOfVertexSampleIndex") +} +@(objc_type=RenderPassSampleBufferAttachmentDescriptor, objc_name="sampleBuffer") +RenderPassSampleBufferAttachmentDescriptor_sampleBuffer :: #force_inline proc(self: ^RenderPassSampleBufferAttachmentDescriptor) -> ^CounterSampleBuffer { + return msgSend(^CounterSampleBuffer, self, "sampleBuffer") +} +@(objc_type=RenderPassSampleBufferAttachmentDescriptor, objc_name="setEndOfFragmentSampleIndex") +RenderPassSampleBufferAttachmentDescriptor_setEndOfFragmentSampleIndex :: #force_inline proc(self: ^RenderPassSampleBufferAttachmentDescriptor, endOfFragmentSampleIndex: NS.UInteger) { + msgSend(nil, self, "setEndOfFragmentSampleIndex:", endOfFragmentSampleIndex) +} +@(objc_type=RenderPassSampleBufferAttachmentDescriptor, objc_name="setEndOfVertexSampleIndex") +RenderPassSampleBufferAttachmentDescriptor_setEndOfVertexSampleIndex :: #force_inline proc(self: ^RenderPassSampleBufferAttachmentDescriptor, endOfVertexSampleIndex: NS.UInteger) { + msgSend(nil, self, "setEndOfVertexSampleIndex:", endOfVertexSampleIndex) +} +@(objc_type=RenderPassSampleBufferAttachmentDescriptor, objc_name="setSampleBuffer") +RenderPassSampleBufferAttachmentDescriptor_setSampleBuffer :: #force_inline proc(self: ^RenderPassSampleBufferAttachmentDescriptor, sampleBuffer: ^CounterSampleBuffer) { + msgSend(nil, self, "setSampleBuffer:", sampleBuffer) +} +@(objc_type=RenderPassSampleBufferAttachmentDescriptor, objc_name="setStartOfFragmentSampleIndex") +RenderPassSampleBufferAttachmentDescriptor_setStartOfFragmentSampleIndex :: #force_inline proc(self: ^RenderPassSampleBufferAttachmentDescriptor, startOfFragmentSampleIndex: NS.UInteger) { + msgSend(nil, self, "setStartOfFragmentSampleIndex:", startOfFragmentSampleIndex) +} +@(objc_type=RenderPassSampleBufferAttachmentDescriptor, objc_name="setStartOfVertexSampleIndex") +RenderPassSampleBufferAttachmentDescriptor_setStartOfVertexSampleIndex :: #force_inline proc(self: ^RenderPassSampleBufferAttachmentDescriptor, startOfVertexSampleIndex: NS.UInteger) { + msgSend(nil, self, "setStartOfVertexSampleIndex:", startOfVertexSampleIndex) +} +@(objc_type=RenderPassSampleBufferAttachmentDescriptor, objc_name="startOfFragmentSampleIndex") +RenderPassSampleBufferAttachmentDescriptor_startOfFragmentSampleIndex :: #force_inline proc(self: ^RenderPassSampleBufferAttachmentDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "startOfFragmentSampleIndex") +} +@(objc_type=RenderPassSampleBufferAttachmentDescriptor, objc_name="startOfVertexSampleIndex") +RenderPassSampleBufferAttachmentDescriptor_startOfVertexSampleIndex :: #force_inline proc(self: ^RenderPassSampleBufferAttachmentDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "startOfVertexSampleIndex") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + RenderPassSampleBufferAttachmentDescriptorArray +Class Methods: + alloc +Methods: + init + objectAtIndexedSubscript + setObject +*/ +@(objc_class="MTLRenderPassSampleBufferAttachmentDescriptorArray") +RenderPassSampleBufferAttachmentDescriptorArray :: struct { using _: NS.Object } + +@(objc_type=RenderPassSampleBufferAttachmentDescriptorArray, objc_name="alloc", objc_is_class_method=true) +RenderPassSampleBufferAttachmentDescriptorArray_alloc :: #force_inline proc() -> ^RenderPassSampleBufferAttachmentDescriptorArray { + return msgSend(^RenderPassSampleBufferAttachmentDescriptorArray, RenderPassSampleBufferAttachmentDescriptorArray, "alloc") +} +@(objc_type=RenderPassSampleBufferAttachmentDescriptorArray, objc_name="init") +RenderPassSampleBufferAttachmentDescriptorArray_init :: #force_inline proc(self: ^RenderPassSampleBufferAttachmentDescriptorArray) -> ^RenderPassSampleBufferAttachmentDescriptorArray { + return msgSend(^RenderPassSampleBufferAttachmentDescriptorArray, self, "init") +} +@(objc_type=RenderPassSampleBufferAttachmentDescriptorArray, objc_name="object") +RenderPassSampleBufferAttachmentDescriptorArray_object :: #force_inline proc(self: ^RenderPassSampleBufferAttachmentDescriptorArray, attachmentIndex: NS.UInteger) -> ^RenderPassSampleBufferAttachmentDescriptor { + return msgSend(^RenderPassSampleBufferAttachmentDescriptor, self, "objectAtIndexedSubscript:", attachmentIndex) +} +@(objc_type=RenderPassSampleBufferAttachmentDescriptorArray, objc_name="setObject") +RenderPassSampleBufferAttachmentDescriptorArray_setObject :: #force_inline proc(self: ^RenderPassSampleBufferAttachmentDescriptorArray, attachment: ^RenderPassSampleBufferAttachmentDescriptor, attachmentIndex: NS.UInteger) { + msgSend(nil, self, "setObject:atIndexedSubscript:", attachment, attachmentIndex) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + RenderPassStencilAttachmentDescriptor +Class Methods: + alloc +Methods: + init + clearStencil + setClearStencil + setStencilResolveFilter + stencilResolveFilter +*/ +@(objc_class="MTLRenderPassStencilAttachmentDescriptor") +RenderPassStencilAttachmentDescriptor :: struct { using _: NS.Copying(RenderPassStencilAttachmentDescriptor) } + +@(objc_type=RenderPassStencilAttachmentDescriptor, objc_name="alloc", objc_is_class_method=true) +RenderPassStencilAttachmentDescriptor_alloc :: #force_inline proc() -> ^RenderPassStencilAttachmentDescriptor { + return msgSend(^RenderPassStencilAttachmentDescriptor, RenderPassStencilAttachmentDescriptor, "alloc") +} +@(objc_type=RenderPassStencilAttachmentDescriptor, objc_name="init") +RenderPassStencilAttachmentDescriptor_init :: #force_inline proc(self: ^RenderPassStencilAttachmentDescriptor) -> ^RenderPassStencilAttachmentDescriptor { + return msgSend(^RenderPassStencilAttachmentDescriptor, self, "init") +} +@(objc_type=RenderPassStencilAttachmentDescriptor, objc_name="clearStencil") +RenderPassStencilAttachmentDescriptor_clearStencil :: #force_inline proc(self: ^RenderPassStencilAttachmentDescriptor) -> u32 { + return msgSend(u32, self, "clearStencil") +} +@(objc_type=RenderPassStencilAttachmentDescriptor, objc_name="setClearStencil") +RenderPassStencilAttachmentDescriptor_setClearStencil :: #force_inline proc(self: ^RenderPassStencilAttachmentDescriptor, clearStencil: u32) { + msgSend(nil, self, "setClearStencil:", clearStencil) +} +@(objc_type=RenderPassStencilAttachmentDescriptor, objc_name="setStencilResolveFilter") +RenderPassStencilAttachmentDescriptor_setStencilResolveFilter :: #force_inline proc(self: ^RenderPassStencilAttachmentDescriptor, stencilResolveFilter: MultisampleStencilResolveFilter) { + msgSend(nil, self, "setStencilResolveFilter:", stencilResolveFilter) +} +@(objc_type=RenderPassStencilAttachmentDescriptor, objc_name="stencilResolveFilter") +RenderPassStencilAttachmentDescriptor_stencilResolveFilter :: #force_inline proc(self: ^RenderPassStencilAttachmentDescriptor) -> MultisampleStencilResolveFilter { + return msgSend(MultisampleStencilResolveFilter, self, "stencilResolveFilter") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + RenderPipelineColorAttachmentDescriptor +Class Methods: + alloc +Methods: + init + alphaBlendOperation + destinationAlphaBlendFactor + destinationRGBBlendFactor + isBlendingEnabled + pixelFormat + rgbBlendOperation + setAlphaBlendOperation + setBlendingEnabled + setDestinationAlphaBlendFactor + setDestinationRGBBlendFactor + setPixelFormat + setRgbBlendOperation + setSourceAlphaBlendFactor + setSourceRGBBlendFactor + setWriteMask + sourceAlphaBlendFactor + sourceRGBBlendFactor + writeMask +*/ +@(objc_class="MTLRenderPipelineColorAttachmentDescriptor") +RenderPipelineColorAttachmentDescriptor :: struct { using _: NS.Copying(RenderPipelineColorAttachmentDescriptor), using _: RenderPassAttachmentDescriptor } + +@(objc_type=RenderPipelineColorAttachmentDescriptor, objc_name="alloc", objc_is_class_method=true) +RenderPipelineColorAttachmentDescriptor_alloc :: #force_inline proc() -> ^RenderPipelineColorAttachmentDescriptor { + return msgSend(^RenderPipelineColorAttachmentDescriptor, RenderPipelineColorAttachmentDescriptor, "alloc") +} +@(objc_type=RenderPipelineColorAttachmentDescriptor, objc_name="init") +RenderPipelineColorAttachmentDescriptor_init :: #force_inline proc(self: ^RenderPipelineColorAttachmentDescriptor) -> ^RenderPipelineColorAttachmentDescriptor { + return msgSend(^RenderPipelineColorAttachmentDescriptor, self, "init") +} +@(objc_type=RenderPipelineColorAttachmentDescriptor, objc_name="alphaBlendOperation") +RenderPipelineColorAttachmentDescriptor_alphaBlendOperation :: #force_inline proc(self: ^RenderPipelineColorAttachmentDescriptor) -> BlendOperation { + return msgSend(BlendOperation, self, "alphaBlendOperation") +} +@(objc_type=RenderPipelineColorAttachmentDescriptor, objc_name="destinationAlphaBlendFactor") +RenderPipelineColorAttachmentDescriptor_destinationAlphaBlendFactor :: #force_inline proc(self: ^RenderPipelineColorAttachmentDescriptor) -> BlendFactor { + return msgSend(BlendFactor, self, "destinationAlphaBlendFactor") +} +@(objc_type=RenderPipelineColorAttachmentDescriptor, objc_name="destinationRGBBlendFactor") +RenderPipelineColorAttachmentDescriptor_destinationRGBBlendFactor :: #force_inline proc(self: ^RenderPipelineColorAttachmentDescriptor) -> BlendFactor { + return msgSend(BlendFactor, self, "destinationRGBBlendFactor") +} +@(objc_type=RenderPipelineColorAttachmentDescriptor, objc_name="isBlendingEnabled") +RenderPipelineColorAttachmentDescriptor_isBlendingEnabled :: #force_inline proc(self: ^RenderPipelineColorAttachmentDescriptor) -> BOOL { + return msgSend(BOOL, self, "isBlendingEnabled") +} +@(objc_type=RenderPipelineColorAttachmentDescriptor, objc_name="pixelFormat") +RenderPipelineColorAttachmentDescriptor_pixelFormat :: #force_inline proc(self: ^RenderPipelineColorAttachmentDescriptor) -> PixelFormat { + return msgSend(PixelFormat, self, "pixelFormat") +} +@(objc_type=RenderPipelineColorAttachmentDescriptor, objc_name="rgbBlendOperation") +RenderPipelineColorAttachmentDescriptor_rgbBlendOperation :: #force_inline proc(self: ^RenderPipelineColorAttachmentDescriptor) -> BlendOperation { + return msgSend(BlendOperation, self, "rgbBlendOperation") +} +@(objc_type=RenderPipelineColorAttachmentDescriptor, objc_name="setAlphaBlendOperation") +RenderPipelineColorAttachmentDescriptor_setAlphaBlendOperation :: #force_inline proc(self: ^RenderPipelineColorAttachmentDescriptor, alphaBlendOperation: BlendOperation) { + msgSend(nil, self, "setAlphaBlendOperation:", alphaBlendOperation) +} +@(objc_type=RenderPipelineColorAttachmentDescriptor, objc_name="setBlendingEnabled") +RenderPipelineColorAttachmentDescriptor_setBlendingEnabled :: #force_inline proc(self: ^RenderPipelineColorAttachmentDescriptor, blendingEnabled: BOOL) { + msgSend(nil, self, "setBlendingEnabled:", blendingEnabled) +} +@(objc_type=RenderPipelineColorAttachmentDescriptor, objc_name="setDestinationAlphaBlendFactor") +RenderPipelineColorAttachmentDescriptor_setDestinationAlphaBlendFactor :: #force_inline proc(self: ^RenderPipelineColorAttachmentDescriptor, destinationAlphaBlendFactor: BlendFactor) { + msgSend(nil, self, "setDestinationAlphaBlendFactor:", destinationAlphaBlendFactor) +} +@(objc_type=RenderPipelineColorAttachmentDescriptor, objc_name="setDestinationRGBBlendFactor") +RenderPipelineColorAttachmentDescriptor_setDestinationRGBBlendFactor :: #force_inline proc(self: ^RenderPipelineColorAttachmentDescriptor, destinationRGBBlendFactor: BlendFactor) { + msgSend(nil, self, "setDestinationRGBBlendFactor:", destinationRGBBlendFactor) +} +@(objc_type=RenderPipelineColorAttachmentDescriptor, objc_name="setPixelFormat") +RenderPipelineColorAttachmentDescriptor_setPixelFormat :: #force_inline proc(self: ^RenderPipelineColorAttachmentDescriptor, pixelFormat: PixelFormat) { + msgSend(nil, self, "setPixelFormat:", pixelFormat) +} +@(objc_type=RenderPipelineColorAttachmentDescriptor, objc_name="setRgbBlendOperation") +RenderPipelineColorAttachmentDescriptor_setRgbBlendOperation :: #force_inline proc(self: ^RenderPipelineColorAttachmentDescriptor, rgbBlendOperation: BlendOperation) { + msgSend(nil, self, "setRgbBlendOperation:", rgbBlendOperation) +} +@(objc_type=RenderPipelineColorAttachmentDescriptor, objc_name="setSourceAlphaBlendFactor") +RenderPipelineColorAttachmentDescriptor_setSourceAlphaBlendFactor :: #force_inline proc(self: ^RenderPipelineColorAttachmentDescriptor, sourceAlphaBlendFactor: BlendFactor) { + msgSend(nil, self, "setSourceAlphaBlendFactor:", sourceAlphaBlendFactor) +} +@(objc_type=RenderPipelineColorAttachmentDescriptor, objc_name="setSourceRGBBlendFactor") +RenderPipelineColorAttachmentDescriptor_setSourceRGBBlendFactor :: #force_inline proc(self: ^RenderPipelineColorAttachmentDescriptor, sourceRGBBlendFactor: BlendFactor) { + msgSend(nil, self, "setSourceRGBBlendFactor:", sourceRGBBlendFactor) +} +@(objc_type=RenderPipelineColorAttachmentDescriptor, objc_name="setWriteMask") +RenderPipelineColorAttachmentDescriptor_setWriteMask :: #force_inline proc(self: ^RenderPipelineColorAttachmentDescriptor, writeMask: ColorWriteMask) { + msgSend(nil, self, "setWriteMask:", writeMask) +} +@(objc_type=RenderPipelineColorAttachmentDescriptor, objc_name="sourceAlphaBlendFactor") +RenderPipelineColorAttachmentDescriptor_sourceAlphaBlendFactor :: #force_inline proc(self: ^RenderPipelineColorAttachmentDescriptor) -> BlendFactor { + return msgSend(BlendFactor, self, "sourceAlphaBlendFactor") +} +@(objc_type=RenderPipelineColorAttachmentDescriptor, objc_name="sourceRGBBlendFactor") +RenderPipelineColorAttachmentDescriptor_sourceRGBBlendFactor :: #force_inline proc(self: ^RenderPipelineColorAttachmentDescriptor) -> BlendFactor { + return msgSend(BlendFactor, self, "sourceRGBBlendFactor") +} +@(objc_type=RenderPipelineColorAttachmentDescriptor, objc_name="writeMask") +RenderPipelineColorAttachmentDescriptor_writeMask :: #force_inline proc(self: ^RenderPipelineColorAttachmentDescriptor) -> ColorWriteMask { + return msgSend(ColorWriteMask, self, "writeMask") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + RenderPipelineColorAttachmentDescriptorArray +Class Methods: + alloc +Methods: + init + objectAtIndexedSubscript + setObject +*/ +@(objc_class="MTLRenderPipelineColorAttachmentDescriptorArray") +RenderPipelineColorAttachmentDescriptorArray :: struct { using _: NS.Object } + +@(objc_type=RenderPipelineColorAttachmentDescriptorArray, objc_name="alloc", objc_is_class_method=true) +RenderPipelineColorAttachmentDescriptorArray_alloc :: #force_inline proc() -> ^RenderPipelineColorAttachmentDescriptorArray { + return msgSend(^RenderPipelineColorAttachmentDescriptorArray, RenderPipelineColorAttachmentDescriptorArray, "alloc") +} +@(objc_type=RenderPipelineColorAttachmentDescriptorArray, objc_name="init") +RenderPipelineColorAttachmentDescriptorArray_init :: #force_inline proc(self: ^RenderPipelineColorAttachmentDescriptorArray) -> ^RenderPipelineColorAttachmentDescriptorArray { + return msgSend(^RenderPipelineColorAttachmentDescriptorArray, self, "init") +} +@(objc_type=RenderPipelineColorAttachmentDescriptorArray, objc_name="object") +RenderPipelineColorAttachmentDescriptorArray_object :: #force_inline proc(self: ^RenderPipelineColorAttachmentDescriptorArray, attachmentIndex: NS.UInteger) -> ^RenderPipelineColorAttachmentDescriptor { + return msgSend(^RenderPipelineColorAttachmentDescriptor, self, "objectAtIndexedSubscript:", attachmentIndex) +} +@(objc_type=RenderPipelineColorAttachmentDescriptorArray, objc_name="setObject") +RenderPipelineColorAttachmentDescriptorArray_setObject :: #force_inline proc(self: ^RenderPipelineColorAttachmentDescriptorArray, attachment: ^RenderPipelineColorAttachmentDescriptor, attachmentIndex: NS.UInteger) { + msgSend(nil, self, "setObject:atIndexedSubscript:", attachment, attachmentIndex) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + RenderPipelineDescriptor +Class Methods: + alloc +Methods: + init + binaryArchives + colorAttachments + depthAttachmentPixelFormat + fragmentBuffers + fragmentFunction + inputPrimitiveTopology + isAlphaToCoverageEnabled + isAlphaToOneEnabled + isRasterizationEnabled + isTessellationFactorScaleEnabled + label + maxTessellationFactor + maxVertexAmplificationCount + rasterSampleCount + reset + sampleCount + setAlphaToCoverageEnabled + setAlphaToOneEnabled + setBinaryArchives + setDepthAttachmentPixelFormat + setFragmentFunction + setInputPrimitiveTopology + setLabel + setMaxTessellationFactor + setMaxVertexAmplificationCount + setRasterSampleCount + setRasterizationEnabled + setSampleCount + setStencilAttachmentPixelFormat + setSupportIndirectCommandBuffers + setTessellationControlPointIndexType + setTessellationFactorFormat + setTessellationFactorScaleEnabled + setTessellationFactorStepFunction + setTessellationOutputWindingOrder + setTessellationPartitionMode + setVertexDescriptor + setVertexFunction + stencilAttachmentPixelFormat + supportIndirectCommandBuffers + tessellationControlPointIndexType + tessellationFactorFormat + tessellationFactorStepFunction + tessellationOutputWindingOrder + tessellationPartitionMode + vertexBuffers + vertexDescriptor + vertexFunction +*/ +@(objc_class="MTLRenderPipelineDescriptor") +RenderPipelineDescriptor :: struct { using _: NS.Copying(RenderPipelineDescriptor) } + +@(objc_type=RenderPipelineDescriptor, objc_name="alloc", objc_is_class_method=true) +RenderPipelineDescriptor_alloc :: #force_inline proc() -> ^RenderPipelineDescriptor { + return msgSend(^RenderPipelineDescriptor, RenderPipelineDescriptor, "alloc") +} +@(objc_type=RenderPipelineDescriptor, objc_name="init") +RenderPipelineDescriptor_init :: #force_inline proc(self: ^RenderPipelineDescriptor) -> ^RenderPipelineDescriptor { + return msgSend(^RenderPipelineDescriptor, self, "init") +} +@(objc_type=RenderPipelineDescriptor, objc_name="binaryArchives") +RenderPipelineDescriptor_binaryArchives :: #force_inline proc(self: ^RenderPipelineDescriptor) -> ^NS.Array { + return msgSend(^NS.Array, self, "binaryArchives") +} +@(objc_type=RenderPipelineDescriptor, objc_name="colorAttachments") +RenderPipelineDescriptor_colorAttachments :: #force_inline proc(self: ^RenderPipelineDescriptor) -> ^RenderPipelineColorAttachmentDescriptorArray { + return msgSend(^RenderPipelineColorAttachmentDescriptorArray, self, "colorAttachments") +} +@(objc_type=RenderPipelineDescriptor, objc_name="depthAttachmentPixelFormat") +RenderPipelineDescriptor_depthAttachmentPixelFormat :: #force_inline proc(self: ^RenderPipelineDescriptor) -> PixelFormat { + return msgSend(PixelFormat, self, "depthAttachmentPixelFormat") +} +@(objc_type=RenderPipelineDescriptor, objc_name="fragmentBuffers") +RenderPipelineDescriptor_fragmentBuffers :: #force_inline proc(self: ^RenderPipelineDescriptor) -> ^PipelineBufferDescriptorArray { + return msgSend(^PipelineBufferDescriptorArray, self, "fragmentBuffers") +} +@(objc_type=RenderPipelineDescriptor, objc_name="fragmentFunction") +RenderPipelineDescriptor_fragmentFunction :: #force_inline proc(self: ^RenderPipelineDescriptor) -> ^Function { + return msgSend(^Function, self, "fragmentFunction") +} +@(objc_type=RenderPipelineDescriptor, objc_name="inputPrimitiveTopology") +RenderPipelineDescriptor_inputPrimitiveTopology :: #force_inline proc(self: ^RenderPipelineDescriptor) -> PrimitiveTopologyClass { + return msgSend(PrimitiveTopologyClass, self, "inputPrimitiveTopology") +} +@(objc_type=RenderPipelineDescriptor, objc_name="isAlphaToCoverageEnabled") +RenderPipelineDescriptor_isAlphaToCoverageEnabled :: #force_inline proc(self: ^RenderPipelineDescriptor) -> BOOL { + return msgSend(BOOL, self, "isAlphaToCoverageEnabled") +} +@(objc_type=RenderPipelineDescriptor, objc_name="isAlphaToOneEnabled") +RenderPipelineDescriptor_isAlphaToOneEnabled :: #force_inline proc(self: ^RenderPipelineDescriptor) -> BOOL { + return msgSend(BOOL, self, "isAlphaToOneEnabled") +} +@(objc_type=RenderPipelineDescriptor, objc_name="isRasterizationEnabled") +RenderPipelineDescriptor_isRasterizationEnabled :: #force_inline proc(self: ^RenderPipelineDescriptor) -> BOOL { + return msgSend(BOOL, self, "isRasterizationEnabled") +} +@(objc_type=RenderPipelineDescriptor, objc_name="isTessellationFactorScaleEnabled") +RenderPipelineDescriptor_isTessellationFactorScaleEnabled :: #force_inline proc(self: ^RenderPipelineDescriptor) -> BOOL { + return msgSend(BOOL, self, "isTessellationFactorScaleEnabled") +} +@(objc_type=RenderPipelineDescriptor, objc_name="label") +RenderPipelineDescriptor_label :: #force_inline proc(self: ^RenderPipelineDescriptor) -> ^NS.String { + return msgSend(^NS.String, self, "label") +} +@(objc_type=RenderPipelineDescriptor, objc_name="maxTessellationFactor") +RenderPipelineDescriptor_maxTessellationFactor :: #force_inline proc(self: ^RenderPipelineDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "maxTessellationFactor") +} +@(objc_type=RenderPipelineDescriptor, objc_name="maxVertexAmplificationCount") +RenderPipelineDescriptor_maxVertexAmplificationCount :: #force_inline proc(self: ^RenderPipelineDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "maxVertexAmplificationCount") +} +@(objc_type=RenderPipelineDescriptor, objc_name="rasterSampleCount") +RenderPipelineDescriptor_rasterSampleCount :: #force_inline proc(self: ^RenderPipelineDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "rasterSampleCount") +} +@(objc_type=RenderPipelineDescriptor, objc_name="reset") +RenderPipelineDescriptor_reset :: #force_inline proc(self: ^RenderPipelineDescriptor) { + msgSend(nil, self, "reset") +} +@(objc_type=RenderPipelineDescriptor, objc_name="sampleCount") +RenderPipelineDescriptor_sampleCount :: #force_inline proc(self: ^RenderPipelineDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "sampleCount") +} +@(objc_type=RenderPipelineDescriptor, objc_name="setAlphaToCoverageEnabled") +RenderPipelineDescriptor_setAlphaToCoverageEnabled :: #force_inline proc(self: ^RenderPipelineDescriptor, alphaToCoverageEnabled: BOOL) { + msgSend(nil, self, "setAlphaToCoverageEnabled:", alphaToCoverageEnabled) +} +@(objc_type=RenderPipelineDescriptor, objc_name="setAlphaToOneEnabled") +RenderPipelineDescriptor_setAlphaToOneEnabled :: #force_inline proc(self: ^RenderPipelineDescriptor, alphaToOneEnabled: BOOL) { + msgSend(nil, self, "setAlphaToOneEnabled:", alphaToOneEnabled) +} +@(objc_type=RenderPipelineDescriptor, objc_name="setBinaryArchives") +RenderPipelineDescriptor_setBinaryArchives :: #force_inline proc(self: ^RenderPipelineDescriptor, binaryArchives: ^NS.Array) { + msgSend(nil, self, "setBinaryArchives:", binaryArchives) +} +@(objc_type=RenderPipelineDescriptor, objc_name="setDepthAttachmentPixelFormat") +RenderPipelineDescriptor_setDepthAttachmentPixelFormat :: #force_inline proc(self: ^RenderPipelineDescriptor, depthAttachmentPixelFormat: PixelFormat) { + msgSend(nil, self, "setDepthAttachmentPixelFormat:", depthAttachmentPixelFormat) +} +@(objc_type=RenderPipelineDescriptor, objc_name="setFragmentFunction") +RenderPipelineDescriptor_setFragmentFunction :: #force_inline proc(self: ^RenderPipelineDescriptor, fragmentFunction: ^Function) { + msgSend(nil, self, "setFragmentFunction:", fragmentFunction) +} +@(objc_type=RenderPipelineDescriptor, objc_name="setInputPrimitiveTopology") +RenderPipelineDescriptor_setInputPrimitiveTopology :: #force_inline proc(self: ^RenderPipelineDescriptor, inputPrimitiveTopology: PrimitiveTopologyClass) { + msgSend(nil, self, "setInputPrimitiveTopology:", inputPrimitiveTopology) +} +@(objc_type=RenderPipelineDescriptor, objc_name="setLabel") +RenderPipelineDescriptor_setLabel :: #force_inline proc(self: ^RenderPipelineDescriptor, label: ^NS.String) { + msgSend(nil, self, "setLabel:", label) +} +@(objc_type=RenderPipelineDescriptor, objc_name="setMaxTessellationFactor") +RenderPipelineDescriptor_setMaxTessellationFactor :: #force_inline proc(self: ^RenderPipelineDescriptor, maxTessellationFactor: NS.UInteger) { + msgSend(nil, self, "setMaxTessellationFactor:", maxTessellationFactor) +} +@(objc_type=RenderPipelineDescriptor, objc_name="setMaxVertexAmplificationCount") +RenderPipelineDescriptor_setMaxVertexAmplificationCount :: #force_inline proc(self: ^RenderPipelineDescriptor, maxVertexAmplificationCount: NS.UInteger) { + msgSend(nil, self, "setMaxVertexAmplificationCount:", maxVertexAmplificationCount) +} +@(objc_type=RenderPipelineDescriptor, objc_name="setRasterSampleCount") +RenderPipelineDescriptor_setRasterSampleCount :: #force_inline proc(self: ^RenderPipelineDescriptor, rasterSampleCount: NS.UInteger) { + msgSend(nil, self, "setRasterSampleCount:", rasterSampleCount) +} +@(objc_type=RenderPipelineDescriptor, objc_name="setRasterizationEnabled") +RenderPipelineDescriptor_setRasterizationEnabled :: #force_inline proc(self: ^RenderPipelineDescriptor, rasterizationEnabled: BOOL) { + msgSend(nil, self, "setRasterizationEnabled:", rasterizationEnabled) +} +@(objc_type=RenderPipelineDescriptor, objc_name="setSampleCount") +RenderPipelineDescriptor_setSampleCount :: #force_inline proc(self: ^RenderPipelineDescriptor, sampleCount: NS.UInteger) { + msgSend(nil, self, "setSampleCount:", sampleCount) +} +@(objc_type=RenderPipelineDescriptor, objc_name="setStencilAttachmentPixelFormat") +RenderPipelineDescriptor_setStencilAttachmentPixelFormat :: #force_inline proc(self: ^RenderPipelineDescriptor, stencilAttachmentPixelFormat: PixelFormat) { + msgSend(nil, self, "setStencilAttachmentPixelFormat:", stencilAttachmentPixelFormat) +} +@(objc_type=RenderPipelineDescriptor, objc_name="setSupportIndirectCommandBuffers") +RenderPipelineDescriptor_setSupportIndirectCommandBuffers :: #force_inline proc(self: ^RenderPipelineDescriptor, supportIndirectCommandBuffers: BOOL) { + msgSend(nil, self, "setSupportIndirectCommandBuffers:", supportIndirectCommandBuffers) +} +@(objc_type=RenderPipelineDescriptor, objc_name="setTessellationControlPointIndexType") +RenderPipelineDescriptor_setTessellationControlPointIndexType :: #force_inline proc(self: ^RenderPipelineDescriptor, tessellationControlPointIndexType: TessellationControlPointIndexType) { + msgSend(nil, self, "setTessellationControlPointIndexType:", tessellationControlPointIndexType) +} +@(objc_type=RenderPipelineDescriptor, objc_name="setTessellationFactorFormat") +RenderPipelineDescriptor_setTessellationFactorFormat :: #force_inline proc(self: ^RenderPipelineDescriptor, tessellationFactorFormat: TessellationFactorFormat) { + msgSend(nil, self, "setTessellationFactorFormat:", tessellationFactorFormat) +} +@(objc_type=RenderPipelineDescriptor, objc_name="setTessellationFactorScaleEnabled") +RenderPipelineDescriptor_setTessellationFactorScaleEnabled :: #force_inline proc(self: ^RenderPipelineDescriptor, tessellationFactorScaleEnabled: BOOL) { + msgSend(nil, self, "setTessellationFactorScaleEnabled:", tessellationFactorScaleEnabled) +} +@(objc_type=RenderPipelineDescriptor, objc_name="setTessellationFactorStepFunction") +RenderPipelineDescriptor_setTessellationFactorStepFunction :: #force_inline proc(self: ^RenderPipelineDescriptor, tessellationFactorStepFunction: TessellationFactorStepFunction) { + msgSend(nil, self, "setTessellationFactorStepFunction:", tessellationFactorStepFunction) +} +@(objc_type=RenderPipelineDescriptor, objc_name="setTessellationOutputWindingOrder") +RenderPipelineDescriptor_setTessellationOutputWindingOrder :: #force_inline proc(self: ^RenderPipelineDescriptor, tessellationOutputWindingOrder: Winding) { + msgSend(nil, self, "setTessellationOutputWindingOrder:", tessellationOutputWindingOrder) +} +@(objc_type=RenderPipelineDescriptor, objc_name="setTessellationPartitionMode") +RenderPipelineDescriptor_setTessellationPartitionMode :: #force_inline proc(self: ^RenderPipelineDescriptor, tessellationPartitionMode: TessellationPartitionMode) { + msgSend(nil, self, "setTessellationPartitionMode:", tessellationPartitionMode) +} +@(objc_type=RenderPipelineDescriptor, objc_name="setVertexDescriptor") +RenderPipelineDescriptor_setVertexDescriptor :: #force_inline proc(self: ^RenderPipelineDescriptor, vertexDescriptor: ^VertexDescriptor) { + msgSend(nil, self, "setVertexDescriptor:", vertexDescriptor) +} +@(objc_type=RenderPipelineDescriptor, objc_name="setVertexFunction") +RenderPipelineDescriptor_setVertexFunction :: #force_inline proc(self: ^RenderPipelineDescriptor, vertexFunction: ^Function) { + msgSend(nil, self, "setVertexFunction:", vertexFunction) +} +@(objc_type=RenderPipelineDescriptor, objc_name="stencilAttachmentPixelFormat") +RenderPipelineDescriptor_stencilAttachmentPixelFormat :: #force_inline proc(self: ^RenderPipelineDescriptor) -> PixelFormat { + return msgSend(PixelFormat, self, "stencilAttachmentPixelFormat") +} +@(objc_type=RenderPipelineDescriptor, objc_name="supportIndirectCommandBuffers") +RenderPipelineDescriptor_supportIndirectCommandBuffers :: #force_inline proc(self: ^RenderPipelineDescriptor) -> BOOL { + return msgSend(BOOL, self, "supportIndirectCommandBuffers") +} +@(objc_type=RenderPipelineDescriptor, objc_name="tessellationControlPointIndexType") +RenderPipelineDescriptor_tessellationControlPointIndexType :: #force_inline proc(self: ^RenderPipelineDescriptor) -> TessellationControlPointIndexType { + return msgSend(TessellationControlPointIndexType, self, "tessellationControlPointIndexType") +} +@(objc_type=RenderPipelineDescriptor, objc_name="tessellationFactorFormat") +RenderPipelineDescriptor_tessellationFactorFormat :: #force_inline proc(self: ^RenderPipelineDescriptor) -> TessellationFactorFormat { + return msgSend(TessellationFactorFormat, self, "tessellationFactorFormat") +} +@(objc_type=RenderPipelineDescriptor, objc_name="tessellationFactorStepFunction") +RenderPipelineDescriptor_tessellationFactorStepFunction :: #force_inline proc(self: ^RenderPipelineDescriptor) -> TessellationFactorStepFunction { + return msgSend(TessellationFactorStepFunction, self, "tessellationFactorStepFunction") +} +@(objc_type=RenderPipelineDescriptor, objc_name="tessellationOutputWindingOrder") +RenderPipelineDescriptor_tessellationOutputWindingOrder :: #force_inline proc(self: ^RenderPipelineDescriptor) -> Winding { + return msgSend(Winding, self, "tessellationOutputWindingOrder") +} +@(objc_type=RenderPipelineDescriptor, objc_name="tessellationPartitionMode") +RenderPipelineDescriptor_tessellationPartitionMode :: #force_inline proc(self: ^RenderPipelineDescriptor) -> TessellationPartitionMode { + return msgSend(TessellationPartitionMode, self, "tessellationPartitionMode") +} +@(objc_type=RenderPipelineDescriptor, objc_name="vertexBuffers") +RenderPipelineDescriptor_vertexBuffers :: #force_inline proc(self: ^RenderPipelineDescriptor) -> ^PipelineBufferDescriptorArray { + return msgSend(^PipelineBufferDescriptorArray, self, "vertexBuffers") +} +@(objc_type=RenderPipelineDescriptor, objc_name="vertexDescriptor") +RenderPipelineDescriptor_vertexDescriptor :: #force_inline proc(self: ^RenderPipelineDescriptor) -> ^VertexDescriptor { + return msgSend(^VertexDescriptor, self, "vertexDescriptor") +} +@(objc_type=RenderPipelineDescriptor, objc_name="vertexFunction") +RenderPipelineDescriptor_vertexFunction :: #force_inline proc(self: ^RenderPipelineDescriptor) -> ^Function { + return msgSend(^Function, self, "vertexFunction") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + RenderPipelineReflection +Class Methods: + alloc +Methods: + init + fragmentArguments + tileArguments + vertexArguments +*/ +@(objc_class="MTLRenderPipelineReflection") +RenderPipelineReflection :: struct { using _: NS.Object } + +@(objc_type=RenderPipelineReflection, objc_name="alloc", objc_is_class_method=true) +RenderPipelineReflection_alloc :: #force_inline proc() -> ^RenderPipelineReflection { + return msgSend(^RenderPipelineReflection, RenderPipelineReflection, "alloc") +} +@(objc_type=RenderPipelineReflection, objc_name="init") +RenderPipelineReflection_init :: #force_inline proc(self: ^RenderPipelineReflection) -> ^RenderPipelineReflection { + return msgSend(^RenderPipelineReflection, self, "init") +} +@(objc_type=RenderPipelineReflection, objc_name="fragmentArguments") +RenderPipelineReflection_fragmentArguments :: #force_inline proc(self: ^RenderPipelineReflection) -> ^NS.Array { + return msgSend(^NS.Array, self, "fragmentArguments") +} +@(objc_type=RenderPipelineReflection, objc_name="tileArguments") +RenderPipelineReflection_tileArguments :: #force_inline proc(self: ^RenderPipelineReflection) -> ^NS.Array { + return msgSend(^NS.Array, self, "tileArguments") +} +@(objc_type=RenderPipelineReflection, objc_name="vertexArguments") +RenderPipelineReflection_vertexArguments :: #force_inline proc(self: ^RenderPipelineReflection) -> ^NS.Array { + return msgSend(^NS.Array, self, "vertexArguments") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + ResourceStatePassDescriptor +Class Methods: + alloc + resourceStatePassDescriptor +Methods: + init + sampleBufferAttachments +*/ +@(objc_class="MTLResourceStatePassDescriptor") +ResourceStatePassDescriptor :: struct { using _: NS.Copying(ResourceStatePassDescriptor) } + +@(objc_type=ResourceStatePassDescriptor, objc_name="alloc", objc_is_class_method=true) +ResourceStatePassDescriptor_alloc :: #force_inline proc() -> ^ResourceStatePassDescriptor { + return msgSend(^ResourceStatePassDescriptor, ResourceStatePassDescriptor, "alloc") +} +@(objc_type=ResourceStatePassDescriptor, objc_name="init") +ResourceStatePassDescriptor_init :: #force_inline proc(self: ^ResourceStatePassDescriptor) -> ^ResourceStatePassDescriptor { + return msgSend(^ResourceStatePassDescriptor, self, "init") +} +@(objc_type=ResourceStatePassDescriptor, objc_name="resourceStatePassDescriptor", objc_is_class_method=true) +ResourceStatePassDescriptor_resourceStatePassDescriptor :: #force_inline proc() -> ^ResourceStatePassDescriptor { + return msgSend(^ResourceStatePassDescriptor, ResourceStatePassDescriptor, "resourceStatePassDescriptor") +} +@(objc_type=ResourceStatePassDescriptor, objc_name="sampleBufferAttachments") +ResourceStatePassDescriptor_sampleBufferAttachments :: #force_inline proc(self: ^ResourceStatePassDescriptor) -> ^ResourceStatePassSampleBufferAttachmentDescriptorArray { + return msgSend(^ResourceStatePassSampleBufferAttachmentDescriptorArray, self, "sampleBufferAttachments") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + ResourceStatePassSampleBufferAttachmentDescriptor +Class Methods: + alloc +Methods: + init + endOfEncoderSampleIndex + sampleBuffer + setEndOfEncoderSampleIndex + setSampleBuffer + setStartOfEncoderSampleIndex + startOfEncoderSampleIndex +*/ +@(objc_class="MTLResourceStatePassSampleBufferAttachmentDescriptor") +ResourceStatePassSampleBufferAttachmentDescriptor :: struct { using _: NS.Copying(ResourceStatePassSampleBufferAttachmentDescriptor) } + +@(objc_type=ResourceStatePassSampleBufferAttachmentDescriptor, objc_name="alloc", objc_is_class_method=true) +ResourceStatePassSampleBufferAttachmentDescriptor_alloc :: #force_inline proc() -> ^ResourceStatePassSampleBufferAttachmentDescriptor { + return msgSend(^ResourceStatePassSampleBufferAttachmentDescriptor, ResourceStatePassSampleBufferAttachmentDescriptor, "alloc") +} +@(objc_type=ResourceStatePassSampleBufferAttachmentDescriptor, objc_name="init") +ResourceStatePassSampleBufferAttachmentDescriptor_init :: #force_inline proc(self: ^ResourceStatePassSampleBufferAttachmentDescriptor) -> ^ResourceStatePassSampleBufferAttachmentDescriptor { + return msgSend(^ResourceStatePassSampleBufferAttachmentDescriptor, self, "init") +} +@(objc_type=ResourceStatePassSampleBufferAttachmentDescriptor, objc_name="endOfEncoderSampleIndex") +ResourceStatePassSampleBufferAttachmentDescriptor_endOfEncoderSampleIndex :: #force_inline proc(self: ^ResourceStatePassSampleBufferAttachmentDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "endOfEncoderSampleIndex") +} +@(objc_type=ResourceStatePassSampleBufferAttachmentDescriptor, objc_name="sampleBuffer") +ResourceStatePassSampleBufferAttachmentDescriptor_sampleBuffer :: #force_inline proc(self: ^ResourceStatePassSampleBufferAttachmentDescriptor) -> ^CounterSampleBuffer { + return msgSend(^CounterSampleBuffer, self, "sampleBuffer") +} +@(objc_type=ResourceStatePassSampleBufferAttachmentDescriptor, objc_name="setEndOfEncoderSampleIndex") +ResourceStatePassSampleBufferAttachmentDescriptor_setEndOfEncoderSampleIndex :: #force_inline proc(self: ^ResourceStatePassSampleBufferAttachmentDescriptor, endOfEncoderSampleIndex: NS.UInteger) { + msgSend(nil, self, "setEndOfEncoderSampleIndex:", endOfEncoderSampleIndex) +} +@(objc_type=ResourceStatePassSampleBufferAttachmentDescriptor, objc_name="setSampleBuffer") +ResourceStatePassSampleBufferAttachmentDescriptor_setSampleBuffer :: #force_inline proc(self: ^ResourceStatePassSampleBufferAttachmentDescriptor, sampleBuffer: ^CounterSampleBuffer) { + msgSend(nil, self, "setSampleBuffer:", sampleBuffer) +} +@(objc_type=ResourceStatePassSampleBufferAttachmentDescriptor, objc_name="setStartOfEncoderSampleIndex") +ResourceStatePassSampleBufferAttachmentDescriptor_setStartOfEncoderSampleIndex :: #force_inline proc(self: ^ResourceStatePassSampleBufferAttachmentDescriptor, startOfEncoderSampleIndex: NS.UInteger) { + msgSend(nil, self, "setStartOfEncoderSampleIndex:", startOfEncoderSampleIndex) +} +@(objc_type=ResourceStatePassSampleBufferAttachmentDescriptor, objc_name="startOfEncoderSampleIndex") +ResourceStatePassSampleBufferAttachmentDescriptor_startOfEncoderSampleIndex :: #force_inline proc(self: ^ResourceStatePassSampleBufferAttachmentDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "startOfEncoderSampleIndex") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + ResourceStatePassSampleBufferAttachmentDescriptorArray +Class Methods: + alloc +Methods: + init + objectAtIndexedSubscript + setObject +*/ +@(objc_class="MTLResourceStatePassSampleBufferAttachmentDescriptorArray") +ResourceStatePassSampleBufferAttachmentDescriptorArray :: struct { using _: NS.Object } + +@(objc_type=ResourceStatePassSampleBufferAttachmentDescriptorArray, objc_name="alloc", objc_is_class_method=true) +ResourceStatePassSampleBufferAttachmentDescriptorArray_alloc :: #force_inline proc() -> ^ResourceStatePassSampleBufferAttachmentDescriptorArray { + return msgSend(^ResourceStatePassSampleBufferAttachmentDescriptorArray, ResourceStatePassSampleBufferAttachmentDescriptorArray, "alloc") +} +@(objc_type=ResourceStatePassSampleBufferAttachmentDescriptorArray, objc_name="init") +ResourceStatePassSampleBufferAttachmentDescriptorArray_init :: #force_inline proc(self: ^ResourceStatePassSampleBufferAttachmentDescriptorArray) -> ^ResourceStatePassSampleBufferAttachmentDescriptorArray { + return msgSend(^ResourceStatePassSampleBufferAttachmentDescriptorArray, self, "init") +} +@(objc_type=ResourceStatePassSampleBufferAttachmentDescriptorArray, objc_name="object") +ResourceStatePassSampleBufferAttachmentDescriptorArray_object :: #force_inline proc(self: ^ResourceStatePassSampleBufferAttachmentDescriptorArray, attachmentIndex: NS.UInteger) -> ^ResourceStatePassSampleBufferAttachmentDescriptor { + return msgSend(^ResourceStatePassSampleBufferAttachmentDescriptor, self, "objectAtIndexedSubscript:", attachmentIndex) +} +@(objc_type=ResourceStatePassSampleBufferAttachmentDescriptorArray, objc_name="setObject") +ResourceStatePassSampleBufferAttachmentDescriptorArray_setObject :: #force_inline proc(self: ^ResourceStatePassSampleBufferAttachmentDescriptorArray, attachment: ^ResourceStatePassSampleBufferAttachmentDescriptor, attachmentIndex: NS.UInteger) { + msgSend(nil, self, "setObject:atIndexedSubscript:", attachment, attachmentIndex) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + SamplerDescriptor +Class Methods: + alloc +Methods: + init + borderColor + compareFunction + label + lodAverage + lodMaxClamp + lodMinClamp + magFilter + maxAnisotropy + minFilter + mipFilter + normalizedCoordinates + rAddressMode + sAddressMode + setBorderColor + setCompareFunction + setLabel + setLodAverage + setLodMaxClamp + setLodMinClamp + setMagFilter + setMaxAnisotropy + setMinFilter + setMipFilter + setNormalizedCoordinates + setRAddressMode + setSAddressMode + setSupportArgumentBuffers + setTAddressMode + supportArgumentBuffers + tAddressMode +*/ +@(objc_class="MTLSamplerDescriptor") +SamplerDescriptor :: struct { using _: NS.Copying(SamplerDescriptor) } + +@(objc_type=SamplerDescriptor, objc_name="alloc", objc_is_class_method=true) +SamplerDescriptor_alloc :: #force_inline proc() -> ^SamplerDescriptor { + return msgSend(^SamplerDescriptor, SamplerDescriptor, "alloc") +} +@(objc_type=SamplerDescriptor, objc_name="init") +SamplerDescriptor_init :: #force_inline proc(self: ^SamplerDescriptor) -> ^SamplerDescriptor { + return msgSend(^SamplerDescriptor, self, "init") +} +@(objc_type=SamplerDescriptor, objc_name="borderColor") +SamplerDescriptor_borderColor :: #force_inline proc(self: ^SamplerDescriptor) -> SamplerBorderColor { + return msgSend(SamplerBorderColor, self, "borderColor") +} +@(objc_type=SamplerDescriptor, objc_name="compareFunction") +SamplerDescriptor_compareFunction :: #force_inline proc(self: ^SamplerDescriptor) -> CompareFunction { + return msgSend(CompareFunction, self, "compareFunction") +} +@(objc_type=SamplerDescriptor, objc_name="label") +SamplerDescriptor_label :: #force_inline proc(self: ^SamplerDescriptor) -> ^NS.String { + return msgSend(^NS.String, self, "label") +} +@(objc_type=SamplerDescriptor, objc_name="lodAverage") +SamplerDescriptor_lodAverage :: #force_inline proc(self: ^SamplerDescriptor) -> BOOL { + return msgSend(BOOL, self, "lodAverage") +} +@(objc_type=SamplerDescriptor, objc_name="lodMaxClamp") +SamplerDescriptor_lodMaxClamp :: #force_inline proc(self: ^SamplerDescriptor) -> f32 { + return msgSend(f32, self, "lodMaxClamp") +} +@(objc_type=SamplerDescriptor, objc_name="lodMinClamp") +SamplerDescriptor_lodMinClamp :: #force_inline proc(self: ^SamplerDescriptor) -> f32 { + return msgSend(f32, self, "lodMinClamp") +} +@(objc_type=SamplerDescriptor, objc_name="magFilter") +SamplerDescriptor_magFilter :: #force_inline proc(self: ^SamplerDescriptor) -> SamplerMinMagFilter { + return msgSend(SamplerMinMagFilter, self, "magFilter") +} +@(objc_type=SamplerDescriptor, objc_name="maxAnisotropy") +SamplerDescriptor_maxAnisotropy :: #force_inline proc(self: ^SamplerDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "maxAnisotropy") +} +@(objc_type=SamplerDescriptor, objc_name="minFilter") +SamplerDescriptor_minFilter :: #force_inline proc(self: ^SamplerDescriptor) -> SamplerMinMagFilter { + return msgSend(SamplerMinMagFilter, self, "minFilter") +} +@(objc_type=SamplerDescriptor, objc_name="mipFilter") +SamplerDescriptor_mipFilter :: #force_inline proc(self: ^SamplerDescriptor) -> SamplerMipFilter { + return msgSend(SamplerMipFilter, self, "mipFilter") +} +@(objc_type=SamplerDescriptor, objc_name="normalizedCoordinates") +SamplerDescriptor_normalizedCoordinates :: #force_inline proc(self: ^SamplerDescriptor) -> BOOL { + return msgSend(BOOL, self, "normalizedCoordinates") +} +@(objc_type=SamplerDescriptor, objc_name="rAddressMode") +SamplerDescriptor_rAddressMode :: #force_inline proc(self: ^SamplerDescriptor) -> SamplerAddressMode { + return msgSend(SamplerAddressMode, self, "rAddressMode") +} +@(objc_type=SamplerDescriptor, objc_name="sAddressMode") +SamplerDescriptor_sAddressMode :: #force_inline proc(self: ^SamplerDescriptor) -> SamplerAddressMode { + return msgSend(SamplerAddressMode, self, "sAddressMode") +} +@(objc_type=SamplerDescriptor, objc_name="setBorderColor") +SamplerDescriptor_setBorderColor :: #force_inline proc(self: ^SamplerDescriptor, borderColor: SamplerBorderColor) { + msgSend(nil, self, "setBorderColor:", borderColor) +} +@(objc_type=SamplerDescriptor, objc_name="setCompareFunction") +SamplerDescriptor_setCompareFunction :: #force_inline proc(self: ^SamplerDescriptor, compareFunction: CompareFunction) { + msgSend(nil, self, "setCompareFunction:", compareFunction) +} +@(objc_type=SamplerDescriptor, objc_name="setLabel") +SamplerDescriptor_setLabel :: #force_inline proc(self: ^SamplerDescriptor, label: ^NS.String) { + msgSend(nil, self, "setLabel:", label) +} +@(objc_type=SamplerDescriptor, objc_name="setLodAverage") +SamplerDescriptor_setLodAverage :: #force_inline proc(self: ^SamplerDescriptor, lodAverage: BOOL) { + msgSend(nil, self, "setLodAverage:", lodAverage) +} +@(objc_type=SamplerDescriptor, objc_name="setLodMaxClamp") +SamplerDescriptor_setLodMaxClamp :: #force_inline proc(self: ^SamplerDescriptor, lodMaxClamp: f32) { + msgSend(nil, self, "setLodMaxClamp:", lodMaxClamp) +} +@(objc_type=SamplerDescriptor, objc_name="setLodMinClamp") +SamplerDescriptor_setLodMinClamp :: #force_inline proc(self: ^SamplerDescriptor, lodMinClamp: f32) { + msgSend(nil, self, "setLodMinClamp:", lodMinClamp) +} +@(objc_type=SamplerDescriptor, objc_name="setMagFilter") +SamplerDescriptor_setMagFilter :: #force_inline proc(self: ^SamplerDescriptor, magFilter: SamplerMinMagFilter) { + msgSend(nil, self, "setMagFilter:", magFilter) +} +@(objc_type=SamplerDescriptor, objc_name="setMaxAnisotropy") +SamplerDescriptor_setMaxAnisotropy :: #force_inline proc(self: ^SamplerDescriptor, maxAnisotropy: NS.UInteger) { + msgSend(nil, self, "setMaxAnisotropy:", maxAnisotropy) +} +@(objc_type=SamplerDescriptor, objc_name="setMinFilter") +SamplerDescriptor_setMinFilter :: #force_inline proc(self: ^SamplerDescriptor, minFilter: SamplerMinMagFilter) { + msgSend(nil, self, "setMinFilter:", minFilter) +} +@(objc_type=SamplerDescriptor, objc_name="setMipFilter") +SamplerDescriptor_setMipFilter :: #force_inline proc(self: ^SamplerDescriptor, mipFilter: SamplerMipFilter) { + msgSend(nil, self, "setMipFilter:", mipFilter) +} +@(objc_type=SamplerDescriptor, objc_name="setNormalizedCoordinates") +SamplerDescriptor_setNormalizedCoordinates :: #force_inline proc(self: ^SamplerDescriptor, normalizedCoordinates: BOOL) { + msgSend(nil, self, "setNormalizedCoordinates:", normalizedCoordinates) +} +@(objc_type=SamplerDescriptor, objc_name="setRAddressMode") +SamplerDescriptor_setRAddressMode :: #force_inline proc(self: ^SamplerDescriptor, rAddressMode: SamplerAddressMode) { + msgSend(nil, self, "setRAddressMode:", rAddressMode) +} +@(objc_type=SamplerDescriptor, objc_name="setSAddressMode") +SamplerDescriptor_setSAddressMode :: #force_inline proc(self: ^SamplerDescriptor, sAddressMode: SamplerAddressMode) { + msgSend(nil, self, "setSAddressMode:", sAddressMode) +} +@(objc_type=SamplerDescriptor, objc_name="setSupportArgumentBuffers") +SamplerDescriptor_setSupportArgumentBuffers :: #force_inline proc(self: ^SamplerDescriptor, supportArgumentBuffers: BOOL) { + msgSend(nil, self, "setSupportArgumentBuffers:", supportArgumentBuffers) +} +@(objc_type=SamplerDescriptor, objc_name="setTAddressMode") +SamplerDescriptor_setTAddressMode :: #force_inline proc(self: ^SamplerDescriptor, tAddressMode: SamplerAddressMode) { + msgSend(nil, self, "setTAddressMode:", tAddressMode) +} +@(objc_type=SamplerDescriptor, objc_name="supportArgumentBuffers") +SamplerDescriptor_supportArgumentBuffers :: #force_inline proc(self: ^SamplerDescriptor) -> BOOL { + return msgSend(BOOL, self, "supportArgumentBuffers") +} +@(objc_type=SamplerDescriptor, objc_name="tAddressMode") +SamplerDescriptor_tAddressMode :: #force_inline proc(self: ^SamplerDescriptor) -> SamplerAddressMode { + return msgSend(SamplerAddressMode, self, "tAddressMode") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + SharedEventHandle +Class Methods: + alloc +Methods: + init + label +*/ +@(objc_class="MTLSharedEventHandle") +SharedEventHandle :: struct { using _: NS.Object } + +@(objc_type=SharedEventHandle, objc_name="alloc", objc_is_class_method=true) +SharedEventHandle_alloc :: #force_inline proc() -> ^SharedEventHandle { + return msgSend(^SharedEventHandle, SharedEventHandle, "alloc") +} +@(objc_type=SharedEventHandle, objc_name="init") +SharedEventHandle_init :: #force_inline proc(self: ^SharedEventHandle) -> ^SharedEventHandle { + return msgSend(^SharedEventHandle, self, "init") +} +@(objc_type=SharedEventHandle, objc_name="label") +SharedEventHandle_label :: #force_inline proc(self: ^SharedEventHandle) -> ^NS.String { + return msgSend(^NS.String, self, "label") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + SharedEventListener +Class Methods: + alloc +Methods: + dispatchQueue + init + initWithDispatchQueue +*/ +@(objc_class="MTLSharedEventListener") +SharedEventListener :: struct { using _: NS.Object } + +@(objc_type=SharedEventListener, objc_name="alloc", objc_is_class_method=true) +SharedEventListener_alloc :: #force_inline proc() -> ^SharedEventListener { + return msgSend(^SharedEventListener, SharedEventListener, "alloc") +} +@(objc_type=SharedEventListener, objc_name="dispatchQueue") +SharedEventListener_dispatchQueue :: #force_inline proc(self: ^SharedEventListener) -> dispatch_queue_t { + return msgSend(dispatch_queue_t, self, "dispatchQueue") +} +@(objc_type=SharedEventListener, objc_name="init") +SharedEventListener_init :: #force_inline proc(self: ^SharedEventListener) -> ^SharedEventListener { + return msgSend(^SharedEventListener, self, "init") +} +@(objc_type=SharedEventListener, objc_name="initWithDispatchQueue") +SharedEventListener_initWithDispatchQueue :: #force_inline proc(self: ^SharedEventListener, dispatchQueue: dispatch_queue_t) -> ^SharedEventListener { + return msgSend(^SharedEventListener, self, "initWithDispatchQueue:", dispatchQueue) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + SharedTextureHandle +Class Methods: + alloc +Methods: + init + device + label +*/ +@(objc_class="MTLSharedTextureHandle") +SharedTextureHandle :: struct { using _: NS.Object } + +@(objc_type=SharedTextureHandle, objc_name="alloc", objc_is_class_method=true) +SharedTextureHandle_alloc :: #force_inline proc() -> ^SharedTextureHandle { + return msgSend(^SharedTextureHandle, SharedTextureHandle, "alloc") +} +@(objc_type=SharedTextureHandle, objc_name="init") +SharedTextureHandle_init :: #force_inline proc(self: ^SharedTextureHandle) -> ^SharedTextureHandle { + return msgSend(^SharedTextureHandle, self, "init") +} +@(objc_type=SharedTextureHandle, objc_name="device") +SharedTextureHandle_device :: #force_inline proc(self: ^SharedTextureHandle) -> ^Device { + return msgSend(^Device, self, "device") +} +@(objc_type=SharedTextureHandle, objc_name="label") +SharedTextureHandle_label :: #force_inline proc(self: ^SharedTextureHandle) -> ^NS.String { + return msgSend(^NS.String, self, "label") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + StageInputOutputDescriptor +Class Methods: + alloc + stageInputOutputDescriptor +Methods: + init + attributes + indexBufferIndex + indexType + layouts + reset + setIndexBufferIndex + setIndexType +*/ +@(objc_class="MTLStageInputOutputDescriptor") +StageInputOutputDescriptor :: struct { using _: NS.Copying(StageInputOutputDescriptor) } + +@(objc_type=StageInputOutputDescriptor, objc_name="alloc", objc_is_class_method=true) +StageInputOutputDescriptor_alloc :: #force_inline proc() -> ^StageInputOutputDescriptor { + return msgSend(^StageInputOutputDescriptor, StageInputOutputDescriptor, "alloc") +} +@(objc_type=StageInputOutputDescriptor, objc_name="init") +StageInputOutputDescriptor_init :: #force_inline proc(self: ^StageInputOutputDescriptor) -> ^StageInputOutputDescriptor { + return msgSend(^StageInputOutputDescriptor, self, "init") +} +@(objc_type=StageInputOutputDescriptor, objc_name="attributes") +StageInputOutputDescriptor_attributes :: #force_inline proc(self: ^StageInputOutputDescriptor) -> ^AttributeDescriptorArray { + return msgSend(^AttributeDescriptorArray, self, "attributes") +} +@(objc_type=StageInputOutputDescriptor, objc_name="indexBufferIndex") +StageInputOutputDescriptor_indexBufferIndex :: #force_inline proc(self: ^StageInputOutputDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "indexBufferIndex") +} +@(objc_type=StageInputOutputDescriptor, objc_name="indexType") +StageInputOutputDescriptor_indexType :: #force_inline proc(self: ^StageInputOutputDescriptor) -> IndexType { + return msgSend(IndexType, self, "indexType") +} +@(objc_type=StageInputOutputDescriptor, objc_name="layouts") +StageInputOutputDescriptor_layouts :: #force_inline proc(self: ^StageInputOutputDescriptor) -> ^BufferLayoutDescriptorArray { + return msgSend(^BufferLayoutDescriptorArray, self, "layouts") +} +@(objc_type=StageInputOutputDescriptor, objc_name="reset") +StageInputOutputDescriptor_reset :: #force_inline proc(self: ^StageInputOutputDescriptor) { + msgSend(nil, self, "reset") +} +@(objc_type=StageInputOutputDescriptor, objc_name="setIndexBufferIndex") +StageInputOutputDescriptor_setIndexBufferIndex :: #force_inline proc(self: ^StageInputOutputDescriptor, indexBufferIndex: NS.UInteger) { + msgSend(nil, self, "setIndexBufferIndex:", indexBufferIndex) +} +@(objc_type=StageInputOutputDescriptor, objc_name="setIndexType") +StageInputOutputDescriptor_setIndexType :: #force_inline proc(self: ^StageInputOutputDescriptor, indexType: IndexType) { + msgSend(nil, self, "setIndexType:", indexType) +} +@(objc_type=StageInputOutputDescriptor, objc_name="stageInputOutputDescriptor", objc_is_class_method=true) +StageInputOutputDescriptor_stageInputOutputDescriptor :: #force_inline proc() -> ^StageInputOutputDescriptor { + return msgSend(^StageInputOutputDescriptor, StageInputOutputDescriptor, "stageInputOutputDescriptor") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + StencilDescriptor +Class Methods: + alloc +Methods: + init + depthFailureOperation + depthStencilPassOperation + readMask + setDepthFailureOperation + setDepthStencilPassOperation + setReadMask + setStencilCompareFunction + setStencilFailureOperation + setWriteMask + stencilCompareFunction + stencilFailureOperation + writeMask +*/ +@(objc_class="MTLStencilDescriptor") +StencilDescriptor :: struct { using _: NS.Copying(StencilDescriptor) } + +@(objc_type=StencilDescriptor, objc_name="alloc", objc_is_class_method=true) +StencilDescriptor_alloc :: #force_inline proc() -> ^StencilDescriptor { + return msgSend(^StencilDescriptor, StencilDescriptor, "alloc") +} +@(objc_type=StencilDescriptor, objc_name="init") +StencilDescriptor_init :: #force_inline proc(self: ^StencilDescriptor) -> ^StencilDescriptor { + return msgSend(^StencilDescriptor, self, "init") +} +@(objc_type=StencilDescriptor, objc_name="depthFailureOperation") +StencilDescriptor_depthFailureOperation :: #force_inline proc(self: ^StencilDescriptor) -> StencilOperation { + return msgSend(StencilOperation, self, "depthFailureOperation") +} +@(objc_type=StencilDescriptor, objc_name="depthStencilPassOperation") +StencilDescriptor_depthStencilPassOperation :: #force_inline proc(self: ^StencilDescriptor) -> StencilOperation { + return msgSend(StencilOperation, self, "depthStencilPassOperation") +} +@(objc_type=StencilDescriptor, objc_name="readMask") +StencilDescriptor_readMask :: #force_inline proc(self: ^StencilDescriptor) -> u32 { + return msgSend(u32, self, "readMask") +} +@(objc_type=StencilDescriptor, objc_name="setDepthFailureOperation") +StencilDescriptor_setDepthFailureOperation :: #force_inline proc(self: ^StencilDescriptor, depthFailureOperation: StencilOperation) { + msgSend(nil, self, "setDepthFailureOperation:", depthFailureOperation) +} +@(objc_type=StencilDescriptor, objc_name="setDepthStencilPassOperation") +StencilDescriptor_setDepthStencilPassOperation :: #force_inline proc(self: ^StencilDescriptor, depthStencilPassOperation: StencilOperation) { + msgSend(nil, self, "setDepthStencilPassOperation:", depthStencilPassOperation) +} +@(objc_type=StencilDescriptor, objc_name="setReadMask") +StencilDescriptor_setReadMask :: #force_inline proc(self: ^StencilDescriptor, readMask: u32) { + msgSend(nil, self, "setReadMask:", readMask) +} +@(objc_type=StencilDescriptor, objc_name="setStencilCompareFunction") +StencilDescriptor_setStencilCompareFunction :: #force_inline proc(self: ^StencilDescriptor, stencilCompareFunction: CompareFunction) { + msgSend(nil, self, "setStencilCompareFunction:", stencilCompareFunction) +} +@(objc_type=StencilDescriptor, objc_name="setStencilFailureOperation") +StencilDescriptor_setStencilFailureOperation :: #force_inline proc(self: ^StencilDescriptor, stencilFailureOperation: StencilOperation) { + msgSend(nil, self, "setStencilFailureOperation:", stencilFailureOperation) +} +@(objc_type=StencilDescriptor, objc_name="setWriteMask") +StencilDescriptor_setWriteMask :: #force_inline proc(self: ^StencilDescriptor, writeMask: u32) { + msgSend(nil, self, "setWriteMask:", writeMask) +} +@(objc_type=StencilDescriptor, objc_name="stencilCompareFunction") +StencilDescriptor_stencilCompareFunction :: #force_inline proc(self: ^StencilDescriptor) -> CompareFunction { + return msgSend(CompareFunction, self, "stencilCompareFunction") +} +@(objc_type=StencilDescriptor, objc_name="stencilFailureOperation") +StencilDescriptor_stencilFailureOperation :: #force_inline proc(self: ^StencilDescriptor) -> StencilOperation { + return msgSend(StencilOperation, self, "stencilFailureOperation") +} +@(objc_type=StencilDescriptor, objc_name="writeMask") +StencilDescriptor_writeMask :: #force_inline proc(self: ^StencilDescriptor) -> u32 { + return msgSend(u32, self, "writeMask") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + StructMember +Class Methods: + alloc +Methods: + init + argumentIndex + arrayType + dataType + name + offset + pointerType + structType + textureReferenceType +*/ +@(objc_class="MTLStructMember") +StructMember :: struct { using _: NS.Object } + +@(objc_type=StructMember, objc_name="alloc", objc_is_class_method=true) +StructMember_alloc :: #force_inline proc() -> ^StructMember { + return msgSend(^StructMember, StructMember, "alloc") +} +@(objc_type=StructMember, objc_name="init") +StructMember_init :: #force_inline proc(self: ^StructMember) -> ^StructMember { + return msgSend(^StructMember, self, "init") +} +@(objc_type=StructMember, objc_name="argumentIndex") +StructMember_argumentIndex :: #force_inline proc(self: ^StructMember) -> NS.UInteger { + return msgSend(NS.UInteger, self, "argumentIndex") +} +@(objc_type=StructMember, objc_name="arrayType") +StructMember_arrayType :: #force_inline proc(self: ^StructMember) -> ^ArrayType { + return msgSend(^ArrayType, self, "arrayType") +} +@(objc_type=StructMember, objc_name="dataType") +StructMember_dataType :: #force_inline proc(self: ^StructMember) -> DataType { + return msgSend(DataType, self, "dataType") +} +@(objc_type=StructMember, objc_name="name") +StructMember_name :: #force_inline proc(self: ^StructMember) -> ^NS.String { + return msgSend(^NS.String, self, "name") +} +@(objc_type=StructMember, objc_name="offset") +StructMember_offset :: #force_inline proc(self: ^StructMember) -> NS.UInteger { + return msgSend(NS.UInteger, self, "offset") +} +@(objc_type=StructMember, objc_name="pointerType") +StructMember_pointerType :: #force_inline proc(self: ^StructMember) -> ^PointerType { + return msgSend(^PointerType, self, "pointerType") +} +@(objc_type=StructMember, objc_name="structType") +StructMember_structType :: #force_inline proc(self: ^StructMember) -> ^StructType { + return msgSend(^StructType, self, "structType") +} +@(objc_type=StructMember, objc_name="textureReferenceType") +StructMember_textureReferenceType :: #force_inline proc(self: ^StructMember) -> ^TextureReferenceType { + return msgSend(^TextureReferenceType, self, "textureReferenceType") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + StructType +Class Methods: + alloc +Methods: + init + memberByName + members +*/ +@(objc_class="MTLStructType") +StructType :: struct { using _: Type } + +@(objc_type=StructType, objc_name="alloc", objc_is_class_method=true) +StructType_alloc :: #force_inline proc() -> ^StructType { + return msgSend(^StructType, StructType, "alloc") +} +@(objc_type=StructType, objc_name="init") +StructType_init :: #force_inline proc(self: ^StructType) -> ^StructType { + return msgSend(^StructType, self, "init") +} +@(objc_type=StructType, objc_name="memberByName") +StructType_memberByName :: #force_inline proc(self: ^StructType, name: ^NS.String) -> ^StructMember { + return msgSend(^StructMember, self, "memberByName:", name) +} +@(objc_type=StructType, objc_name="members") +StructType_members :: #force_inline proc(self: ^StructType) -> ^NS.Array { + return msgSend(^NS.Array, self, "members") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + TextureDescriptor +Class Methods: + alloc + texture2DDescriptorWithPixelFormat + textureBufferDescriptorWithPixelFormat + textureCubeDescriptorWithPixelFormat +Methods: + init + allowGPUOptimizedContents + arrayLength + cpuCacheMode + depth + hazardTrackingMode + height + mipmapLevelCount + pixelFormat + resourceOptions + sampleCount + setAllowGPUOptimizedContents + setArrayLength + setCpuCacheMode + setDepth + setHazardTrackingMode + setHeight + setMipmapLevelCount + setPixelFormat + setResourceOptions + setSampleCount + setStorageMode + setSwizzle + setTextureType + setUsage + setWidth + storageMode + swizzle + textureType + usage + width +*/ +@(objc_class="MTLTextureDescriptor") +TextureDescriptor :: struct { using _: NS.Copying(TextureDescriptor) } + +@(objc_type=TextureDescriptor, objc_name="alloc", objc_is_class_method=true) +TextureDescriptor_alloc :: #force_inline proc() -> ^TextureDescriptor { + return msgSend(^TextureDescriptor, TextureDescriptor, "alloc") +} +@(objc_type=TextureDescriptor, objc_name="init") +TextureDescriptor_init :: #force_inline proc(self: ^TextureDescriptor) -> ^TextureDescriptor { + return msgSend(^TextureDescriptor, self, "init") +} +@(objc_type=TextureDescriptor, objc_name="allowGPUOptimizedContents") +TextureDescriptor_allowGPUOptimizedContents :: #force_inline proc(self: ^TextureDescriptor) -> BOOL { + return msgSend(BOOL, self, "allowGPUOptimizedContents") +} +@(objc_type=TextureDescriptor, objc_name="arrayLength") +TextureDescriptor_arrayLength :: #force_inline proc(self: ^TextureDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "arrayLength") +} +@(objc_type=TextureDescriptor, objc_name="cpuCacheMode") +TextureDescriptor_cpuCacheMode :: #force_inline proc(self: ^TextureDescriptor) -> CPUCacheMode { + return msgSend(CPUCacheMode, self, "cpuCacheMode") +} +@(objc_type=TextureDescriptor, objc_name="depth") +TextureDescriptor_depth :: #force_inline proc(self: ^TextureDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "depth") +} +@(objc_type=TextureDescriptor, objc_name="hazardTrackingMode") +TextureDescriptor_hazardTrackingMode :: #force_inline proc(self: ^TextureDescriptor) -> HazardTrackingMode { + return msgSend(HazardTrackingMode, self, "hazardTrackingMode") +} +@(objc_type=TextureDescriptor, objc_name="height") +TextureDescriptor_height :: #force_inline proc(self: ^TextureDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "height") +} +@(objc_type=TextureDescriptor, objc_name="mipmapLevelCount") +TextureDescriptor_mipmapLevelCount :: #force_inline proc(self: ^TextureDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "mipmapLevelCount") +} +@(objc_type=TextureDescriptor, objc_name="pixelFormat") +TextureDescriptor_pixelFormat :: #force_inline proc(self: ^TextureDescriptor) -> PixelFormat { + return msgSend(PixelFormat, self, "pixelFormat") +} +@(objc_type=TextureDescriptor, objc_name="resourceOptions") +TextureDescriptor_resourceOptions :: #force_inline proc(self: ^TextureDescriptor) -> ResourceOptions { + return msgSend(ResourceOptions, self, "resourceOptions") +} +@(objc_type=TextureDescriptor, objc_name="sampleCount") +TextureDescriptor_sampleCount :: #force_inline proc(self: ^TextureDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "sampleCount") +} +@(objc_type=TextureDescriptor, objc_name="setAllowGPUOptimizedContents") +TextureDescriptor_setAllowGPUOptimizedContents :: #force_inline proc(self: ^TextureDescriptor, allowGPUOptimizedContents: BOOL) { + msgSend(nil, self, "setAllowGPUOptimizedContents:", allowGPUOptimizedContents) +} +@(objc_type=TextureDescriptor, objc_name="setArrayLength") +TextureDescriptor_setArrayLength :: #force_inline proc(self: ^TextureDescriptor, arrayLength: NS.UInteger) { + msgSend(nil, self, "setArrayLength:", arrayLength) +} +@(objc_type=TextureDescriptor, objc_name="setCpuCacheMode") +TextureDescriptor_setCpuCacheMode :: #force_inline proc(self: ^TextureDescriptor, cpuCacheMode: CPUCacheMode) { + msgSend(nil, self, "setCpuCacheMode:", cpuCacheMode) +} +@(objc_type=TextureDescriptor, objc_name="setDepth") +TextureDescriptor_setDepth :: #force_inline proc(self: ^TextureDescriptor, depth: NS.UInteger) { + msgSend(nil, self, "setDepth:", depth) +} +@(objc_type=TextureDescriptor, objc_name="setHazardTrackingMode") +TextureDescriptor_setHazardTrackingMode :: #force_inline proc(self: ^TextureDescriptor, hazardTrackingMode: HazardTrackingMode) { + msgSend(nil, self, "setHazardTrackingMode:", hazardTrackingMode) +} +@(objc_type=TextureDescriptor, objc_name="setHeight") +TextureDescriptor_setHeight :: #force_inline proc(self: ^TextureDescriptor, height: NS.UInteger) { + msgSend(nil, self, "setHeight:", height) +} +@(objc_type=TextureDescriptor, objc_name="setMipmapLevelCount") +TextureDescriptor_setMipmapLevelCount :: #force_inline proc(self: ^TextureDescriptor, mipmapLevelCount: NS.UInteger) { + msgSend(nil, self, "setMipmapLevelCount:", mipmapLevelCount) +} +@(objc_type=TextureDescriptor, objc_name="setPixelFormat") +TextureDescriptor_setPixelFormat :: #force_inline proc(self: ^TextureDescriptor, pixelFormat: PixelFormat) { + msgSend(nil, self, "setPixelFormat:", pixelFormat) +} +@(objc_type=TextureDescriptor, objc_name="setResourceOptions") +TextureDescriptor_setResourceOptions :: #force_inline proc(self: ^TextureDescriptor, resourceOptions: ResourceOptions) { + msgSend(nil, self, "setResourceOptions:", resourceOptions) +} +@(objc_type=TextureDescriptor, objc_name="setSampleCount") +TextureDescriptor_setSampleCount :: #force_inline proc(self: ^TextureDescriptor, sampleCount: NS.UInteger) { + msgSend(nil, self, "setSampleCount:", sampleCount) +} +@(objc_type=TextureDescriptor, objc_name="setStorageMode") +TextureDescriptor_setStorageMode :: #force_inline proc(self: ^TextureDescriptor, storageMode: StorageMode) { + msgSend(nil, self, "setStorageMode:", storageMode) +} +@(objc_type=TextureDescriptor, objc_name="setSwizzle") +TextureDescriptor_setSwizzle :: #force_inline proc(self: ^TextureDescriptor, swizzle: TextureSwizzleChannels) { + msgSend(nil, self, "setSwizzle:", swizzle) +} +@(objc_type=TextureDescriptor, objc_name="setTextureType") +TextureDescriptor_setTextureType :: #force_inline proc(self: ^TextureDescriptor, textureType: TextureType) { + msgSend(nil, self, "setTextureType:", textureType) +} +@(objc_type=TextureDescriptor, objc_name="setUsage") +TextureDescriptor_setUsage :: #force_inline proc(self: ^TextureDescriptor, usage: TextureUsage) { + msgSend(nil, self, "setUsage:", usage) +} +@(objc_type=TextureDescriptor, objc_name="setWidth") +TextureDescriptor_setWidth :: #force_inline proc(self: ^TextureDescriptor, width: NS.UInteger) { + msgSend(nil, self, "setWidth:", width) +} +@(objc_type=TextureDescriptor, objc_name="storageMode") +TextureDescriptor_storageMode :: #force_inline proc(self: ^TextureDescriptor) -> StorageMode { + return msgSend(StorageMode, self, "storageMode") +} +@(objc_type=TextureDescriptor, objc_name="swizzle") +TextureDescriptor_swizzle :: #force_inline proc(self: ^TextureDescriptor) -> TextureSwizzleChannels { + return msgSend(TextureSwizzleChannels, self, "swizzle") +} +@(objc_type=TextureDescriptor, objc_name="texture2DDescriptorWithPixelFormat", objc_is_class_method=true) +TextureDescriptor_texture2DDescriptorWithPixelFormat :: #force_inline proc(pixelFormat: PixelFormat, width: NS.UInteger, height: NS.UInteger, mipmapped: BOOL) -> ^TextureDescriptor { + return msgSend(^TextureDescriptor, TextureDescriptor, "texture2DDescriptorWithPixelFormat:width:height:mipmapped:", pixelFormat, width, height, mipmapped) +} +@(objc_type=TextureDescriptor, objc_name="textureBufferDescriptorWithPixelFormat", objc_is_class_method=true) +TextureDescriptor_textureBufferDescriptorWithPixelFormat :: #force_inline proc(pixelFormat: PixelFormat, width: NS.UInteger, resourceOptions: ResourceOptions, usage: TextureUsage) -> ^TextureDescriptor { + return msgSend(^TextureDescriptor, TextureDescriptor, "textureBufferDescriptorWithPixelFormat:width:resourceOptions:usage:", pixelFormat, width, resourceOptions, usage) +} +@(objc_type=TextureDescriptor, objc_name="textureCubeDescriptorWithPixelFormat", objc_is_class_method=true) +TextureDescriptor_textureCubeDescriptorWithPixelFormat :: #force_inline proc(pixelFormat: PixelFormat, size: NS.UInteger, mipmapped: BOOL) -> ^TextureDescriptor { + return msgSend(^TextureDescriptor, TextureDescriptor, "textureCubeDescriptorWithPixelFormat:size:mipmapped:", pixelFormat, size, mipmapped) +} +@(objc_type=TextureDescriptor, objc_name="textureType") +TextureDescriptor_textureType :: #force_inline proc(self: ^TextureDescriptor) -> TextureType { + return msgSend(TextureType, self, "textureType") +} +@(objc_type=TextureDescriptor, objc_name="usage") +TextureDescriptor_usage :: #force_inline proc(self: ^TextureDescriptor) -> TextureUsage { + return msgSend(TextureUsage, self, "usage") +} +@(objc_type=TextureDescriptor, objc_name="width") +TextureDescriptor_width :: #force_inline proc(self: ^TextureDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "width") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + TextureReferenceType +Class Methods: + alloc +Methods: + init + access + isDepthTexture + textureDataType + textureType +*/ +@(objc_class="MTLTextureReferenceType") +TextureReferenceType :: struct { using _: Type } + +@(objc_type=TextureReferenceType, objc_name="alloc", objc_is_class_method=true) +TextureReferenceType_alloc :: #force_inline proc() -> ^TextureReferenceType { + return msgSend(^TextureReferenceType, TextureReferenceType, "alloc") +} +@(objc_type=TextureReferenceType, objc_name="init") +TextureReferenceType_init :: #force_inline proc(self: ^TextureReferenceType) -> ^TextureReferenceType { + return msgSend(^TextureReferenceType, self, "init") +} +@(objc_type=TextureReferenceType, objc_name="access") +TextureReferenceType_access :: #force_inline proc(self: ^TextureReferenceType) -> ArgumentAccess { + return msgSend(ArgumentAccess, self, "access") +} +@(objc_type=TextureReferenceType, objc_name="isDepthTexture") +TextureReferenceType_isDepthTexture :: #force_inline proc(self: ^TextureReferenceType) -> BOOL { + return msgSend(BOOL, self, "isDepthTexture") +} +@(objc_type=TextureReferenceType, objc_name="textureDataType") +TextureReferenceType_textureDataType :: #force_inline proc(self: ^TextureReferenceType) -> DataType { + return msgSend(DataType, self, "textureDataType") +} +@(objc_type=TextureReferenceType, objc_name="textureType") +TextureReferenceType_textureType :: #force_inline proc(self: ^TextureReferenceType) -> TextureType { + return msgSend(TextureType, self, "textureType") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + TileRenderPipelineColorAttachmentDescriptor +Class Methods: + alloc +Methods: + init + pixelFormat + setPixelFormat +*/ +@(objc_class="MTLTileRenderPipelineColorAttachmentDescriptor") +TileRenderPipelineColorAttachmentDescriptor :: struct { using _: NS.Copying(TileRenderPipelineColorAttachmentDescriptor) } + +@(objc_type=TileRenderPipelineColorAttachmentDescriptor, objc_name="alloc", objc_is_class_method=true) +TileRenderPipelineColorAttachmentDescriptor_alloc :: #force_inline proc() -> ^TileRenderPipelineColorAttachmentDescriptor { + return msgSend(^TileRenderPipelineColorAttachmentDescriptor, TileRenderPipelineColorAttachmentDescriptor, "alloc") +} +@(objc_type=TileRenderPipelineColorAttachmentDescriptor, objc_name="init") +TileRenderPipelineColorAttachmentDescriptor_init :: #force_inline proc(self: ^TileRenderPipelineColorAttachmentDescriptor) -> ^TileRenderPipelineColorAttachmentDescriptor { + return msgSend(^TileRenderPipelineColorAttachmentDescriptor, self, "init") +} +@(objc_type=TileRenderPipelineColorAttachmentDescriptor, objc_name="pixelFormat") +TileRenderPipelineColorAttachmentDescriptor_pixelFormat :: #force_inline proc(self: ^TileRenderPipelineColorAttachmentDescriptor) -> PixelFormat { + return msgSend(PixelFormat, self, "pixelFormat") +} +@(objc_type=TileRenderPipelineColorAttachmentDescriptor, objc_name="setPixelFormat") +TileRenderPipelineColorAttachmentDescriptor_setPixelFormat :: #force_inline proc(self: ^TileRenderPipelineColorAttachmentDescriptor, pixelFormat: PixelFormat) { + msgSend(nil, self, "setPixelFormat:", pixelFormat) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + TileRenderPipelineColorAttachmentDescriptorArray +Class Methods: + alloc +Methods: + init + objectAtIndexedSubscript + setObject +*/ +@(objc_class="MTLTileRenderPipelineColorAttachmentDescriptorArray") +TileRenderPipelineColorAttachmentDescriptorArray :: struct { using _: NS.Object } + +@(objc_type=TileRenderPipelineColorAttachmentDescriptorArray, objc_name="alloc", objc_is_class_method=true) +TileRenderPipelineColorAttachmentDescriptorArray_alloc :: #force_inline proc() -> ^TileRenderPipelineColorAttachmentDescriptorArray { + return msgSend(^TileRenderPipelineColorAttachmentDescriptorArray, TileRenderPipelineColorAttachmentDescriptorArray, "alloc") +} +@(objc_type=TileRenderPipelineColorAttachmentDescriptorArray, objc_name="init") +TileRenderPipelineColorAttachmentDescriptorArray_init :: #force_inline proc(self: ^TileRenderPipelineColorAttachmentDescriptorArray) -> ^TileRenderPipelineColorAttachmentDescriptorArray { + return msgSend(^TileRenderPipelineColorAttachmentDescriptorArray, self, "init") +} +@(objc_type=TileRenderPipelineColorAttachmentDescriptorArray, objc_name="object") +TileRenderPipelineColorAttachmentDescriptorArray_object :: #force_inline proc(self: ^TileRenderPipelineColorAttachmentDescriptorArray, attachmentIndex: NS.UInteger) -> ^TileRenderPipelineColorAttachmentDescriptor { + return msgSend(^TileRenderPipelineColorAttachmentDescriptor, self, "objectAtIndexedSubscript:", attachmentIndex) +} +@(objc_type=TileRenderPipelineColorAttachmentDescriptorArray, objc_name="setObject") +TileRenderPipelineColorAttachmentDescriptorArray_setObject :: #force_inline proc(self: ^TileRenderPipelineColorAttachmentDescriptorArray, attachment: ^TileRenderPipelineColorAttachmentDescriptor, attachmentIndex: NS.UInteger) { + msgSend(nil, self, "setObject:atIndexedSubscript:", attachment, attachmentIndex) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + TileRenderPipelineDescriptor +Class Methods: + alloc +Methods: + init + binaryArchives + colorAttachments + label + maxTotalThreadsPerThreadgroup + rasterSampleCount + reset + setBinaryArchives + setLabel + setMaxTotalThreadsPerThreadgroup + setRasterSampleCount + setThreadgroupSizeMatchesTileSize + setTileFunction + threadgroupSizeMatchesTileSize + tileBuffers + tileFunction +*/ +@(objc_class="MTLTileRenderPipelineDescriptor") +TileRenderPipelineDescriptor :: struct { using _: NS.Copying(TileRenderPipelineDescriptor) } + +@(objc_type=TileRenderPipelineDescriptor, objc_name="alloc", objc_is_class_method=true) +TileRenderPipelineDescriptor_alloc :: #force_inline proc() -> ^TileRenderPipelineDescriptor { + return msgSend(^TileRenderPipelineDescriptor, TileRenderPipelineDescriptor, "alloc") +} +@(objc_type=TileRenderPipelineDescriptor, objc_name="init") +TileRenderPipelineDescriptor_init :: #force_inline proc(self: ^TileRenderPipelineDescriptor) -> ^TileRenderPipelineDescriptor { + return msgSend(^TileRenderPipelineDescriptor, self, "init") +} +@(objc_type=TileRenderPipelineDescriptor, objc_name="binaryArchives") +TileRenderPipelineDescriptor_binaryArchives :: #force_inline proc(self: ^TileRenderPipelineDescriptor) -> ^NS.Array { + return msgSend(^NS.Array, self, "binaryArchives") +} +@(objc_type=TileRenderPipelineDescriptor, objc_name="colorAttachments") +TileRenderPipelineDescriptor_colorAttachments :: #force_inline proc(self: ^TileRenderPipelineDescriptor) -> ^TileRenderPipelineColorAttachmentDescriptorArray { + return msgSend(^TileRenderPipelineColorAttachmentDescriptorArray, self, "colorAttachments") +} +@(objc_type=TileRenderPipelineDescriptor, objc_name="label") +TileRenderPipelineDescriptor_label :: #force_inline proc(self: ^TileRenderPipelineDescriptor) -> ^NS.String { + return msgSend(^NS.String, self, "label") +} +@(objc_type=TileRenderPipelineDescriptor, objc_name="maxTotalThreadsPerThreadgroup") +TileRenderPipelineDescriptor_maxTotalThreadsPerThreadgroup :: #force_inline proc(self: ^TileRenderPipelineDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "maxTotalThreadsPerThreadgroup") +} +@(objc_type=TileRenderPipelineDescriptor, objc_name="rasterSampleCount") +TileRenderPipelineDescriptor_rasterSampleCount :: #force_inline proc(self: ^TileRenderPipelineDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "rasterSampleCount") +} +@(objc_type=TileRenderPipelineDescriptor, objc_name="reset") +TileRenderPipelineDescriptor_reset :: #force_inline proc(self: ^TileRenderPipelineDescriptor) { + msgSend(nil, self, "reset") +} +@(objc_type=TileRenderPipelineDescriptor, objc_name="setBinaryArchives") +TileRenderPipelineDescriptor_setBinaryArchives :: #force_inline proc(self: ^TileRenderPipelineDescriptor, binaryArchives: ^NS.Array) { + msgSend(nil, self, "setBinaryArchives:", binaryArchives) +} +@(objc_type=TileRenderPipelineDescriptor, objc_name="setLabel") +TileRenderPipelineDescriptor_setLabel :: #force_inline proc(self: ^TileRenderPipelineDescriptor, label: ^NS.String) { + msgSend(nil, self, "setLabel:", label) +} +@(objc_type=TileRenderPipelineDescriptor, objc_name="setMaxTotalThreadsPerThreadgroup") +TileRenderPipelineDescriptor_setMaxTotalThreadsPerThreadgroup :: #force_inline proc(self: ^TileRenderPipelineDescriptor, maxTotalThreadsPerThreadgroup: NS.UInteger) { + msgSend(nil, self, "setMaxTotalThreadsPerThreadgroup:", maxTotalThreadsPerThreadgroup) +} +@(objc_type=TileRenderPipelineDescriptor, objc_name="setRasterSampleCount") +TileRenderPipelineDescriptor_setRasterSampleCount :: #force_inline proc(self: ^TileRenderPipelineDescriptor, rasterSampleCount: NS.UInteger) { + msgSend(nil, self, "setRasterSampleCount:", rasterSampleCount) +} +@(objc_type=TileRenderPipelineDescriptor, objc_name="setThreadgroupSizeMatchesTileSize") +TileRenderPipelineDescriptor_setThreadgroupSizeMatchesTileSize :: #force_inline proc(self: ^TileRenderPipelineDescriptor, threadgroupSizeMatchesTileSize: BOOL) { + msgSend(nil, self, "setThreadgroupSizeMatchesTileSize:", threadgroupSizeMatchesTileSize) +} +@(objc_type=TileRenderPipelineDescriptor, objc_name="setTileFunction") +TileRenderPipelineDescriptor_setTileFunction :: #force_inline proc(self: ^TileRenderPipelineDescriptor, tileFunction: ^Function) { + msgSend(nil, self, "setTileFunction:", tileFunction) +} +@(objc_type=TileRenderPipelineDescriptor, objc_name="threadgroupSizeMatchesTileSize") +TileRenderPipelineDescriptor_threadgroupSizeMatchesTileSize :: #force_inline proc(self: ^TileRenderPipelineDescriptor) -> BOOL { + return msgSend(BOOL, self, "threadgroupSizeMatchesTileSize") +} +@(objc_type=TileRenderPipelineDescriptor, objc_name="tileBuffers") +TileRenderPipelineDescriptor_tileBuffers :: #force_inline proc(self: ^TileRenderPipelineDescriptor) -> ^PipelineBufferDescriptorArray { + return msgSend(^PipelineBufferDescriptorArray, self, "tileBuffers") +} +@(objc_type=TileRenderPipelineDescriptor, objc_name="tileFunction") +TileRenderPipelineDescriptor_tileFunction :: #force_inline proc(self: ^TileRenderPipelineDescriptor) -> ^Function { + return msgSend(^Function, self, "tileFunction") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + Type +Class Methods: + alloc +Methods: + init + dataType +*/ +@(objc_class="MTLType") +Type :: struct { using _: NS.Object } + +@(objc_type=Type, objc_name="alloc", objc_is_class_method=true) +Type_alloc :: #force_inline proc() -> ^Type { + return msgSend(^Type, Type, "alloc") +} +@(objc_type=Type, objc_name="init") +Type_init :: #force_inline proc(self: ^Type) -> ^Type { + return msgSend(^Type, self, "init") +} +@(objc_type=Type, objc_name="dataType") +Type_dataType :: #force_inline proc(self: ^Type) -> DataType { + return msgSend(DataType, self, "dataType") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + VertexAttribute +Class Methods: + alloc +Methods: + init + attributeIndex + attributeType + isActive + isPatchControlPointData + isPatchData + name +*/ +@(objc_class="MTLVertexAttribute") +VertexAttribute :: struct { using _: NS.Object } + +@(objc_type=VertexAttribute, objc_name="alloc", objc_is_class_method=true) +VertexAttribute_alloc :: #force_inline proc() -> ^VertexAttribute { + return msgSend(^VertexAttribute, VertexAttribute, "alloc") +} +@(objc_type=VertexAttribute, objc_name="init") +VertexAttribute_init :: #force_inline proc(self: ^VertexAttribute) -> ^VertexAttribute { + return msgSend(^VertexAttribute, self, "init") +} +@(objc_type=VertexAttribute, objc_name="attributeIndex") +VertexAttribute_attributeIndex :: #force_inline proc(self: ^VertexAttribute) -> NS.UInteger { + return msgSend(NS.UInteger, self, "attributeIndex") +} +@(objc_type=VertexAttribute, objc_name="attributeType") +VertexAttribute_attributeType :: #force_inline proc(self: ^VertexAttribute) -> DataType { + return msgSend(DataType, self, "attributeType") +} +@(objc_type=VertexAttribute, objc_name="isActive") +VertexAttribute_isActive :: #force_inline proc(self: ^VertexAttribute) -> BOOL { + return msgSend(BOOL, self, "isActive") +} +@(objc_type=VertexAttribute, objc_name="isPatchControlPointData") +VertexAttribute_isPatchControlPointData :: #force_inline proc(self: ^VertexAttribute) -> BOOL { + return msgSend(BOOL, self, "isPatchControlPointData") +} +@(objc_type=VertexAttribute, objc_name="isPatchData") +VertexAttribute_isPatchData :: #force_inline proc(self: ^VertexAttribute) -> BOOL { + return msgSend(BOOL, self, "isPatchData") +} +@(objc_type=VertexAttribute, objc_name="name") +VertexAttribute_name :: #force_inline proc(self: ^VertexAttribute) -> ^NS.String { + return msgSend(^NS.String, self, "name") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + VertexAttributeDescriptor +Class Methods: + alloc +Methods: + init + bufferIndex + format + offset + setBufferIndex + setFormat + setOffset +*/ +@(objc_class="MTLVertexAttributeDescriptor") +VertexAttributeDescriptor :: struct { using _: NS.Copying(VertexAttributeDescriptor) } + +@(objc_type=VertexAttributeDescriptor, objc_name="alloc", objc_is_class_method=true) +VertexAttributeDescriptor_alloc :: #force_inline proc() -> ^VertexAttributeDescriptor { + return msgSend(^VertexAttributeDescriptor, VertexAttributeDescriptor, "alloc") +} +@(objc_type=VertexAttributeDescriptor, objc_name="init") +VertexAttributeDescriptor_init :: #force_inline proc(self: ^VertexAttributeDescriptor) -> ^VertexAttributeDescriptor { + return msgSend(^VertexAttributeDescriptor, self, "init") +} +@(objc_type=VertexAttributeDescriptor, objc_name="bufferIndex") +VertexAttributeDescriptor_bufferIndex :: #force_inline proc(self: ^VertexAttributeDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "bufferIndex") +} +@(objc_type=VertexAttributeDescriptor, objc_name="format") +VertexAttributeDescriptor_format :: #force_inline proc(self: ^VertexAttributeDescriptor) -> VertexFormat { + return msgSend(VertexFormat, self, "format") +} +@(objc_type=VertexAttributeDescriptor, objc_name="offset") +VertexAttributeDescriptor_offset :: #force_inline proc(self: ^VertexAttributeDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "offset") +} +@(objc_type=VertexAttributeDescriptor, objc_name="setBufferIndex") +VertexAttributeDescriptor_setBufferIndex :: #force_inline proc(self: ^VertexAttributeDescriptor, bufferIndex: NS.UInteger) { + msgSend(nil, self, "setBufferIndex:", bufferIndex) +} +@(objc_type=VertexAttributeDescriptor, objc_name="setFormat") +VertexAttributeDescriptor_setFormat :: #force_inline proc(self: ^VertexAttributeDescriptor, format: VertexFormat) { + msgSend(nil, self, "setFormat:", format) +} +@(objc_type=VertexAttributeDescriptor, objc_name="setOffset") +VertexAttributeDescriptor_setOffset :: #force_inline proc(self: ^VertexAttributeDescriptor, offset: NS.UInteger) { + msgSend(nil, self, "setOffset:", offset) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + VertexAttributeDescriptorArray +Class Methods: + alloc +Methods: + init + objectAtIndexedSubscript + setObject +*/ +@(objc_class="MTLVertexAttributeDescriptorArray") +VertexAttributeDescriptorArray :: struct { using _: NS.Object } + +@(objc_type=VertexAttributeDescriptorArray, objc_name="alloc", objc_is_class_method=true) +VertexAttributeDescriptorArray_alloc :: #force_inline proc() -> ^VertexAttributeDescriptorArray { + return msgSend(^VertexAttributeDescriptorArray, VertexAttributeDescriptorArray, "alloc") +} +@(objc_type=VertexAttributeDescriptorArray, objc_name="init") +VertexAttributeDescriptorArray_init :: #force_inline proc(self: ^VertexAttributeDescriptorArray) -> ^VertexAttributeDescriptorArray { + return msgSend(^VertexAttributeDescriptorArray, self, "init") +} +@(objc_type=VertexAttributeDescriptorArray, objc_name="object") +VertexAttributeDescriptorArray_object :: #force_inline proc(self: ^VertexAttributeDescriptorArray, index: NS.UInteger) -> ^VertexAttributeDescriptor { + return msgSend(^VertexAttributeDescriptor, self, "objectAtIndexedSubscript:", index) +} +@(objc_type=VertexAttributeDescriptorArray, objc_name="setObject") +VertexAttributeDescriptorArray_setObject :: #force_inline proc(self: ^VertexAttributeDescriptorArray, attributeDesc: ^VertexAttributeDescriptor, index: NS.UInteger) { + msgSend(nil, self, "setObject:atIndexedSubscript:", attributeDesc, index) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + VertexBufferLayoutDescriptor +Class Methods: + alloc +Methods: + init + setStepFunction + setStepRate + setStride + stepFunction + stepRate + stride +*/ +@(objc_class="MTLVertexBufferLayoutDescriptor") +VertexBufferLayoutDescriptor :: struct { using _: NS.Copying(VertexBufferLayoutDescriptor) } + +@(objc_type=VertexBufferLayoutDescriptor, objc_name="alloc", objc_is_class_method=true) +VertexBufferLayoutDescriptor_alloc :: #force_inline proc() -> ^VertexBufferLayoutDescriptor { + return msgSend(^VertexBufferLayoutDescriptor, VertexBufferLayoutDescriptor, "alloc") +} +@(objc_type=VertexBufferLayoutDescriptor, objc_name="init") +VertexBufferLayoutDescriptor_init :: #force_inline proc(self: ^VertexBufferLayoutDescriptor) -> ^VertexBufferLayoutDescriptor { + return msgSend(^VertexBufferLayoutDescriptor, self, "init") +} +@(objc_type=VertexBufferLayoutDescriptor, objc_name="setStepFunction") +VertexBufferLayoutDescriptor_setStepFunction :: #force_inline proc(self: ^VertexBufferLayoutDescriptor, stepFunction: VertexStepFunction) { + msgSend(nil, self, "setStepFunction:", stepFunction) +} +@(objc_type=VertexBufferLayoutDescriptor, objc_name="setStepRate") +VertexBufferLayoutDescriptor_setStepRate :: #force_inline proc(self: ^VertexBufferLayoutDescriptor, stepRate: NS.UInteger) { + msgSend(nil, self, "setStepRate:", stepRate) +} +@(objc_type=VertexBufferLayoutDescriptor, objc_name="setStride") +VertexBufferLayoutDescriptor_setStride :: #force_inline proc(self: ^VertexBufferLayoutDescriptor, stride: NS.UInteger) { + msgSend(nil, self, "setStride:", stride) +} +@(objc_type=VertexBufferLayoutDescriptor, objc_name="stepFunction") +VertexBufferLayoutDescriptor_stepFunction :: #force_inline proc(self: ^VertexBufferLayoutDescriptor) -> VertexStepFunction { + return msgSend(VertexStepFunction, self, "stepFunction") +} +@(objc_type=VertexBufferLayoutDescriptor, objc_name="stepRate") +VertexBufferLayoutDescriptor_stepRate :: #force_inline proc(self: ^VertexBufferLayoutDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "stepRate") +} +@(objc_type=VertexBufferLayoutDescriptor, objc_name="stride") +VertexBufferLayoutDescriptor_stride :: #force_inline proc(self: ^VertexBufferLayoutDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "stride") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + VertexBufferLayoutDescriptorArray +Class Methods: + alloc +Methods: + init + objectAtIndexedSubscript + setObject +*/ +@(objc_class="MTLVertexBufferLayoutDescriptorArray") +VertexBufferLayoutDescriptorArray :: struct { using _: NS.Object } + +@(objc_type=VertexBufferLayoutDescriptorArray, objc_name="alloc", objc_is_class_method=true) +VertexBufferLayoutDescriptorArray_alloc :: #force_inline proc() -> ^VertexBufferLayoutDescriptorArray { + return msgSend(^VertexBufferLayoutDescriptorArray, VertexBufferLayoutDescriptorArray, "alloc") +} +@(objc_type=VertexBufferLayoutDescriptorArray, objc_name="init") +VertexBufferLayoutDescriptorArray_init :: #force_inline proc(self: ^VertexBufferLayoutDescriptorArray) -> ^VertexBufferLayoutDescriptorArray { + return msgSend(^VertexBufferLayoutDescriptorArray, self, "init") +} +@(objc_type=VertexBufferLayoutDescriptorArray, objc_name="object") +VertexBufferLayoutDescriptorArray_object :: #force_inline proc(self: ^VertexBufferLayoutDescriptorArray, index: NS.UInteger) -> ^VertexBufferLayoutDescriptor { + return msgSend(^VertexBufferLayoutDescriptor, self, "objectAtIndexedSubscript:", index) +} +@(objc_type=VertexBufferLayoutDescriptorArray, objc_name="setObject") +VertexBufferLayoutDescriptorArray_setObject :: #force_inline proc(self: ^VertexBufferLayoutDescriptorArray, bufferDesc: ^VertexBufferLayoutDescriptor, index: NS.UInteger) { + msgSend(nil, self, "setObject:atIndexedSubscript:", bufferDesc, index) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + VertexDescriptor +Class Methods: + alloc + vertexDescriptor +Methods: + init + attributes + layouts + reset +*/ +@(objc_class="MTLVertexDescriptor") +VertexDescriptor :: struct { using _: NS.Copying(VertexDescriptor) } + +@(objc_type=VertexDescriptor, objc_name="alloc", objc_is_class_method=true) +VertexDescriptor_alloc :: #force_inline proc() -> ^VertexDescriptor { + return msgSend(^VertexDescriptor, VertexDescriptor, "alloc") +} +@(objc_type=VertexDescriptor, objc_name="init") +VertexDescriptor_init :: #force_inline proc(self: ^VertexDescriptor) -> ^VertexDescriptor { + return msgSend(^VertexDescriptor, self, "init") +} +@(objc_type=VertexDescriptor, objc_name="attributes") +VertexDescriptor_attributes :: #force_inline proc(self: ^VertexDescriptor) -> ^VertexAttributeDescriptorArray { + return msgSend(^VertexAttributeDescriptorArray, self, "attributes") +} +@(objc_type=VertexDescriptor, objc_name="layouts") +VertexDescriptor_layouts :: #force_inline proc(self: ^VertexDescriptor) -> ^VertexBufferLayoutDescriptorArray { + return msgSend(^VertexBufferLayoutDescriptorArray, self, "layouts") +} +@(objc_type=VertexDescriptor, objc_name="reset") +VertexDescriptor_reset :: #force_inline proc(self: ^VertexDescriptor) { + msgSend(nil, self, "reset") +} +@(objc_type=VertexDescriptor, objc_name="vertexDescriptor", objc_is_class_method=true) +VertexDescriptor_vertexDescriptor :: #force_inline proc() -> ^VertexDescriptor { + return msgSend(^VertexDescriptor, VertexDescriptor, "vertexDescriptor") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + VisibleFunctionTableDescriptor +Class Methods: + alloc + visibleFunctionTableDescriptor +Methods: + init + functionCount + setFunctionCount +*/ +@(objc_class="MTLVisibleFunctionTableDescriptor") +VisibleFunctionTableDescriptor :: struct { using _: NS.Copying(VisibleFunctionTableDescriptor) } + +@(objc_type=VisibleFunctionTableDescriptor, objc_name="alloc", objc_is_class_method=true) +VisibleFunctionTableDescriptor_alloc :: #force_inline proc() -> ^VisibleFunctionTableDescriptor { + return msgSend(^VisibleFunctionTableDescriptor, VisibleFunctionTableDescriptor, "alloc") +} +@(objc_type=VisibleFunctionTableDescriptor, objc_name="init") +VisibleFunctionTableDescriptor_init :: #force_inline proc(self: ^VisibleFunctionTableDescriptor) -> ^VisibleFunctionTableDescriptor { + return msgSend(^VisibleFunctionTableDescriptor, self, "init") +} +@(objc_type=VisibleFunctionTableDescriptor, objc_name="functionCount") +VisibleFunctionTableDescriptor_functionCount :: #force_inline proc(self: ^VisibleFunctionTableDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "functionCount") +} +@(objc_type=VisibleFunctionTableDescriptor, objc_name="setFunctionCount") +VisibleFunctionTableDescriptor_setFunctionCount :: #force_inline proc(self: ^VisibleFunctionTableDescriptor, functionCount: NS.UInteger) { + msgSend(nil, self, "setFunctionCount:", functionCount) +} +@(objc_type=VisibleFunctionTableDescriptor, objc_name="visibleFunctionTableDescriptor", objc_is_class_method=true) +VisibleFunctionTableDescriptor_visibleFunctionTableDescriptor :: #force_inline proc() -> ^VisibleFunctionTableDescriptor { + return msgSend(^VisibleFunctionTableDescriptor, VisibleFunctionTableDescriptor, "visibleFunctionTableDescriptor") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + AccelerationStructure +Class Methods: +Methods: + size +*/ +@(objc_class="MTLAccelerationStructure") +AccelerationStructure :: struct { using _: Resource } + +@(objc_type=AccelerationStructure, objc_name="size") +AccelerationStructure_size :: #force_inline proc(self: ^AccelerationStructure) -> NS.UInteger { + return msgSend(NS.UInteger, self, "size") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + AccelerationStructureCommandEncoder +Class Methods: +Methods: + buildAccelerationStructure + copyAccelerationStructure + copyAndCompactAccelerationStructure + refitAccelerationStructure + sampleCountersInBuffer + updateFence + useHeap + useHeaps + useResource + useResources + waitForFence + writeCompactedAccelerationStructureSize +*/ +@(objc_class="MTLAccelerationStructureCommandEncoder") +AccelerationStructureCommandEncoder :: struct { using _: CommandEncoder } + +@(objc_type=AccelerationStructureCommandEncoder, objc_name="buildAccelerationStructure") +AccelerationStructureCommandEncoder_buildAccelerationStructure :: #force_inline proc(self: ^AccelerationStructureCommandEncoder, accelerationStructure: ^AccelerationStructure, descriptor: ^AccelerationStructureDescriptor, scratchBuffer: ^Buffer, scratchBufferOffset: NS.UInteger) { + msgSend(nil, self, "buildAccelerationStructure:descriptor:scratchBuffer:scratchBufferOffset:", accelerationStructure, descriptor, scratchBuffer, scratchBufferOffset) +} +@(objc_type=AccelerationStructureCommandEncoder, objc_name="copyAccelerationStructure") +AccelerationStructureCommandEncoder_copyAccelerationStructure :: #force_inline proc(self: ^AccelerationStructureCommandEncoder, sourceAccelerationStructure, destinationAccelerationStructure: ^AccelerationStructure) { + msgSend(nil, self, "copyAccelerationStructure:toAccelerationStructure:", sourceAccelerationStructure, destinationAccelerationStructure) +} +@(objc_type=AccelerationStructureCommandEncoder, objc_name="copyAndCompactAccelerationStructure") +AccelerationStructureCommandEncoder_copyAndCompactAccelerationStructure :: #force_inline proc(self: ^AccelerationStructureCommandEncoder, sourceAccelerationStructure, destinationAccelerationStructure: ^AccelerationStructure) { + msgSend(nil, self, "copyAndCompactAccelerationStructure:toAccelerationStructure:", sourceAccelerationStructure, destinationAccelerationStructure) +} +@(objc_type=AccelerationStructureCommandEncoder, objc_name="refitAccelerationStructure") +AccelerationStructureCommandEncoder_refitAccelerationStructure :: #force_inline proc(self: ^AccelerationStructureCommandEncoder, sourceAccelerationStructure: ^AccelerationStructure, descriptor: ^AccelerationStructureDescriptor, destinationAccelerationStructure: ^AccelerationStructure, scratchBuffer: ^Buffer, scratchBufferOffset: NS.UInteger) { + msgSend(nil, self, "refitAccelerationStructure:descriptor:destination:scratchBuffer:scratchBufferOffset:", sourceAccelerationStructure, descriptor, destinationAccelerationStructure, scratchBuffer, scratchBufferOffset) +} +@(objc_type=AccelerationStructureCommandEncoder, objc_name="sampleCountersInBuffer") +AccelerationStructureCommandEncoder_sampleCountersInBuffer :: #force_inline proc(self: ^AccelerationStructureCommandEncoder, sampleBuffer: ^Buffer, sampleIndex: NS.UInteger, barrier: BOOL) { + msgSend(nil, self, "sampleCountersInBuffer:atSampleIndex:withBarrier:", sampleBuffer, sampleIndex, barrier) +} +@(objc_type=AccelerationStructureCommandEncoder, objc_name="updateFence") +AccelerationStructureCommandEncoder_updateFence :: #force_inline proc(self: ^AccelerationStructureCommandEncoder, fence: ^Fence) { + msgSend(nil, self, "updateFence:", fence) +} +@(objc_type=AccelerationStructureCommandEncoder, objc_name="useHeap") +AccelerationStructureCommandEncoder_useHeap :: #force_inline proc(self: ^AccelerationStructureCommandEncoder, heap: ^Heap) { + msgSend(nil, self, "useHeap:", heap) +} +@(objc_type=AccelerationStructureCommandEncoder, objc_name="useHeaps") +AccelerationStructureCommandEncoder_useHeaps :: #force_inline proc(self: ^AccelerationStructureCommandEncoder, heaps: []^Heap) { + msgSend(nil, self, "useHeaps:count:", raw_data(heaps), NS.UInteger(len(heaps))) +} +@(objc_type=AccelerationStructureCommandEncoder, objc_name="useResource") +AccelerationStructureCommandEncoder_useResource :: #force_inline proc(self: ^AccelerationStructureCommandEncoder, resource: ^Resource, usage: ResourceUsage) { + msgSend(nil, self, "useResource:usage:", resource, usage) +} +@(objc_type=AccelerationStructureCommandEncoder, objc_name="useResources") +AccelerationStructureCommandEncoder_useResources :: #force_inline proc(self: ^AccelerationStructureCommandEncoder, resources: []^Resource, usage: ResourceUsage) { + msgSend(nil, self, "useResources:count:usage:", resources, NS.UInteger(len(resources)), usage) +} +@(objc_type=AccelerationStructureCommandEncoder, objc_name="waitForFence") +AccelerationStructureCommandEncoder_waitForFence :: #force_inline proc(self: ^AccelerationStructureCommandEncoder, fence: ^Fence) { + msgSend(nil, self, "waitForFence:", fence) +} +@(objc_type=AccelerationStructureCommandEncoder, objc_name="writeCompactedAccelerationStructureSize") +AccelerationStructureCommandEncoder_writeCompactedAccelerationStructureSize :: #force_inline proc(self: ^AccelerationStructureCommandEncoder, accelerationStructure: ^AccelerationStructure, buffer: ^Buffer, offset: NS.UInteger) { + msgSend(nil, self, "writeCompactedAccelerationStructureSize:toBuffer:offset:", accelerationStructure, buffer, offset) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + ArgumentEncoder +Class Methods: +Methods: + alignment + constantDataAtIndex + device + encodedLength + label + newArgumentEncoderForBufferAtIndex + setAccelerationStructure + setArgumentBuffer + setArgumentBuffer + setBuffer + setBuffers + setComputePipelineState + setComputePipelineStates + setIndirectCommandBuffer + setIndirectCommandBuffers + setIntersectionFunctionTable + setIntersectionFunctionTables + setLabel + setRenderPipelineState + setRenderPipelineStates + setSamplerState + setSamplerStates + setTexture + setTextures + setVisibleFunctionTable + setVisibleFunctionTables +*/ +@(objc_class="MTLArgumentEncoder") +ArgumentEncoder :: struct { using _: NS.Object } + +@(objc_type=ArgumentEncoder, objc_name="alignment") +ArgumentEncoder_alignment :: #force_inline proc(self: ^ArgumentEncoder) -> NS.UInteger { + return msgSend(NS.UInteger, self, "alignment") +} +@(objc_type=ArgumentEncoder, objc_name="constantData") +ArgumentEncoder_constantData :: #force_inline proc(self: ^ArgumentEncoder, index: NS.UInteger) -> rawptr { + return msgSend(rawptr, self, "constantDataAtIndex:", index) +} +@(objc_type=ArgumentEncoder, objc_name="device") +ArgumentEncoder_device :: #force_inline proc(self: ^ArgumentEncoder) -> ^Device { + return msgSend(^Device, self, "device") +} +@(objc_type=ArgumentEncoder, objc_name="encodedLength") +ArgumentEncoder_encodedLength :: #force_inline proc(self: ^ArgumentEncoder) -> NS.UInteger { + return msgSend(NS.UInteger, self, "encodedLength") +} +@(objc_type=ArgumentEncoder, objc_name="label") +ArgumentEncoder_label :: #force_inline proc(self: ^ArgumentEncoder) -> ^NS.String { + return msgSend(^NS.String, self, "label") +} +@(objc_type=ArgumentEncoder, objc_name="newArgumentEncoderForBuffer") +ArgumentEncoder_newArgumentEncoderForBuffer :: #force_inline proc(self: ^ArgumentEncoder, index: NS.UInteger) -> ^ArgumentEncoder { + return msgSend(^ArgumentEncoder, self, "newArgumentEncoderForBufferAtIndex:", index) +} +@(objc_type=ArgumentEncoder, objc_name="setAccelerationStructure") +ArgumentEncoder_setAccelerationStructure :: #force_inline proc(self: ^ArgumentEncoder, accelerationStructure: ^AccelerationStructure, index: NS.UInteger) { + msgSend(nil, self, "setAccelerationStructure:atIndex:", accelerationStructure, index) +} +@(objc_type=ArgumentEncoder, objc_name="setArgumentBufferWithOffset") +ArgumentEncoder_setArgumentBufferWithOffset :: #force_inline proc(self: ^ArgumentEncoder, argumentBuffer: ^Buffer, offset: NS.UInteger) { + msgSend(nil, self, "setArgumentBuffer:offset:", argumentBuffer, offset) +} +@(objc_type=ArgumentEncoder, objc_name="setArgumentBufferWithStartOffset") +ArgumentEncoder_setArgumentBuffer_startOffsetWithStartOffset :: #force_inline proc(self: ^ArgumentEncoder, argumentBuffer: ^Buffer, startOffset: NS.UInteger, arrayElement: NS.UInteger) { + msgSend(nil, self, "setArgumentBuffer:startOffset:arrayElement:", argumentBuffer, startOffset, arrayElement) +} +@(objc_type=ArgumentEncoder, objc_name="setBuffer") +ArgumentEncoder_setBuffer :: #force_inline proc(self: ^ArgumentEncoder, buffer: ^Buffer, offset: NS.UInteger, index: NS.UInteger) { + msgSend(nil, self, "setBuffer:offset:atIndex:", buffer, offset, index) +} +@(objc_type=ArgumentEncoder, objc_name="setBuffers") +ArgumentEncoder_setBuffers :: #force_inline proc(self: ^ArgumentEncoder, buffers: []^Buffer, offsets: []NS.UInteger, range: NS.Range) { + assert(len(buffers) == len(offsets)) + msgSend(nil, self, "setBuffers:offsets:withRange:", raw_data(buffers), raw_data(offsets), range) +} +@(objc_type=ArgumentEncoder, objc_name="setComputePipelineState") +ArgumentEncoder_setComputePipelineState :: #force_inline proc(self: ^ArgumentEncoder, pipeline: ^ComputePipelineState, index: NS.UInteger) { + msgSend(nil, self, "setComputePipelineState:atIndex:", pipeline, index) +} +@(objc_type=ArgumentEncoder, objc_name="setComputePipelineStates") +ArgumentEncoder_setComputePipelineStates :: #force_inline proc(self: ^ArgumentEncoder, pipelines: []^ComputePipelineState, range: NS.Range) { + assert(range.length <= NS.UInteger(len(pipelines))) + msgSend(nil, self, "setComputePipelineStates:withRange:", raw_data(pipelines), range) +} +@(objc_type=ArgumentEncoder, objc_name="setIndirectCommandBuffer") +ArgumentEncoder_setIndirectCommandBuffer :: #force_inline proc(self: ^ArgumentEncoder, indirectCommandBuffer: ^IndirectCommandBuffer, index: NS.UInteger) { + msgSend(nil, self, "setIndirectCommandBuffer:atIndex:", indirectCommandBuffer, index) +} +@(objc_type=ArgumentEncoder, objc_name="setIndirectCommandBuffers") +ArgumentEncoder_setIndirectCommandBuffers :: #force_inline proc(self: ^ArgumentEncoder, buffers: []^IndirectCommandBuffer, range: NS.Range) { + assert(range.length <= NS.UInteger(len(buffers))) + msgSend(nil, self, "setIndirectCommandBuffers:withRange:", raw_data(buffers), range) +} +@(objc_type=ArgumentEncoder, objc_name="setIntersectionFunctionTable") +ArgumentEncoder_setIntersectionFunctionTable :: #force_inline proc(self: ^ArgumentEncoder, intersectionFunctionTable: ^IntersectionFunctionTable, index: NS.UInteger) { + msgSend(nil, self, "setIntersectionFunctionTable:atIndex:", intersectionFunctionTable, index) +} +@(objc_type=ArgumentEncoder, objc_name="setIntersectionFunctionTables") +ArgumentEncoder_setIntersectionFunctionTables :: #force_inline proc(self: ^ArgumentEncoder, intersectionFunctionTables: []^IntersectionFunctionTable, range: NS.Range) { + assert(range.length <= NS.UInteger(len(intersectionFunctionTables))) + msgSend(nil, self, "setIntersectionFunctionTables:withRange:", raw_data(intersectionFunctionTables), range) +} +@(objc_type=ArgumentEncoder, objc_name="setLabel") +ArgumentEncoder_setLabel :: #force_inline proc(self: ^ArgumentEncoder, label: ^NS.String) { + msgSend(nil, self, "setLabel:", label) +} +@(objc_type=ArgumentEncoder, objc_name="setRenderPipelineState") +ArgumentEncoder_setRenderPipelineState :: #force_inline proc(self: ^ArgumentEncoder, pipeline: ^RenderPipelineState, index: NS.UInteger) { + msgSend(nil, self, "setRenderPipelineState:atIndex:", pipeline, index) +} +@(objc_type=ArgumentEncoder, objc_name="setRenderPipelineStates") +ArgumentEncoder_setRenderPipelineStates :: #force_inline proc(self: ^ArgumentEncoder, pipelines: []^RenderPipelineState, range: NS.Range) { + assert(range.length <= NS.UInteger(len(pipelines))) + msgSend(nil, self, "setRenderPipelineStates:withRange:", raw_data(pipelines), range) +} +@(objc_type=ArgumentEncoder, objc_name="setSamplerState") +ArgumentEncoder_setSamplerState :: #force_inline proc(self: ^ArgumentEncoder, sampler: ^SamplerState, index: NS.UInteger) { + msgSend(nil, self, "setSamplerState:atIndex:", sampler, index) +} +@(objc_type=ArgumentEncoder, objc_name="setSamplerStates") +ArgumentEncoder_setSamplerStates :: #force_inline proc(self: ^ArgumentEncoder, samplers: []^SamplerState, range: NS.Range) { + assert(range.length <= NS.UInteger(len(samplers))) + msgSend(nil, self, "setSamplerStates:withRange:", raw_data(samplers), range) +} +@(objc_type=ArgumentEncoder, objc_name="setTexture") +ArgumentEncoder_setTexture :: #force_inline proc(self: ^ArgumentEncoder, texture: ^Texture, index: NS.UInteger) { + msgSend(nil, self, "setTexture:atIndex:", texture, index) +} +@(objc_type=ArgumentEncoder, objc_name="setTextures") +ArgumentEncoder_setTextures :: #force_inline proc(self: ^ArgumentEncoder, textures: []^Texture, range: NS.Range) { + assert(range.length <= NS.UInteger(len(textures))) + msgSend(nil, self, "setTextures:withRange:", raw_data(textures), range) +} +@(objc_type=ArgumentEncoder, objc_name="setVisibleFunctionTable") +ArgumentEncoder_setVisibleFunctionTable :: #force_inline proc(self: ^ArgumentEncoder, visibleFunctionTable: ^VisibleFunctionTable, index: NS.UInteger) { + msgSend(nil, self, "setVisibleFunctionTable:atIndex:", visibleFunctionTable, index) +} +@(objc_type=ArgumentEncoder, objc_name="setVisibleFunctionTables") +ArgumentEncoder_setVisibleFunctionTables :: #force_inline proc(self: ^ArgumentEncoder, visibleFunctionTables: []^VisibleFunctionTable, range: NS.Range) { + assert(range.length <= NS.UInteger(len(visibleFunctionTables))) + msgSend(nil, self, "setVisibleFunctionTables:withRange:", raw_data(visibleFunctionTables), range) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + BinaryArchive +Class Methods: +Methods: + addComputePipelineFunctionsWithDescriptor + addRenderPipelineFunctionsWithDescriptor + addTileRenderPipelineFunctionsWithDescriptor + device + label + serializeToURL + setLabel +*/ +@(objc_class="MTLBinaryArchive") +BinaryArchive :: struct { using _: NS.Copying(BinaryArchive) } + +@(objc_type=BinaryArchive, objc_name="addComputePipelineFunctions") +BinaryArchive_addComputePipelineFunctions :: #force_inline proc(self: ^BinaryArchive, descriptor: ^ComputePipelineDescriptor) -> (ok: BOOL, error: ^NS.Error) { + ok = msgSend(BOOL, self, "addComputePipelineFunctionsWithDescriptor:error:", descriptor, &error) + return +} +@(objc_type=BinaryArchive, objc_name="addRenderPipelineFunctions") +BinaryArchive_addRenderPipelineFunctions :: #force_inline proc(self: ^BinaryArchive, descriptor: ^RenderPipelineDescriptor) -> (ok: BOOL, error: ^NS.Error) { + ok = msgSend(BOOL, self, "addRenderPipelineFunctionsWithDescriptor:error:", descriptor, &error) + return +} +@(objc_type=BinaryArchive, objc_name="addTileRenderPipelineFunctions") +BinaryArchive_addTileRenderPipelineFunctions :: #force_inline proc(self: ^BinaryArchive, descriptor: ^TileRenderPipelineDescriptor) -> (ok: BOOL, error: ^NS.Error) { + ok = msgSend(BOOL, self, "addTileRenderPipelineFunctionsWithDescriptor:error:", descriptor, &error) + return +} +@(objc_type=BinaryArchive, objc_name="device") +BinaryArchive_device :: #force_inline proc(self: ^BinaryArchive) -> ^Device { + return msgSend(^Device, self, "device") +} +@(objc_type=BinaryArchive, objc_name="label") +BinaryArchive_label :: #force_inline proc(self: ^BinaryArchive) -> ^NS.String { + return msgSend(^NS.String, self, "label") +} +@(objc_type=BinaryArchive, objc_name="serializeToURL") +BinaryArchive_serializeToURL :: #force_inline proc(self: ^BinaryArchive, url: ^NS.URL) -> (ok: BOOL, error: ^NS.Error) { + ok = msgSend(BOOL, self, "serializeToURL:error:", url, &error) + return +} +@(objc_type=BinaryArchive, objc_name="setLabel") +BinaryArchive_setLabel :: #force_inline proc(self: ^BinaryArchive, label: ^NS.String) { + msgSend(nil, self, "setLabel:", label) +} + + +@(objc_type=BinaryArchive, objc_name="addFunction") +BinaryArchive_addFunction :: #force_inline proc(self: ^BinaryArchive, descriptor: ^FunctionDescriptor, library: ^Library) -> (ok: BOOL, error: ^NS.Error) { + ok = msgSend(BOOL, self, "addFunction:", descriptor, library, &error) + return +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + BlitCommandEncoder +Class Methods: +Methods: + copyFromBuffer + copyFromBuffer + copyFromBuffer + copyFromTexture + copyFromTexture + copyFromTexture + copyFromTexture + copyFromTexture + copyIndirectCommandBuffer + fillBuffer + generateMipmapsForTexture + getTextureAccessCounters + optimizeContentsForCPUAccess + optimizeContentsForCPUAccess + optimizeContentsForGPUAccess + optimizeContentsForGPUAccess + optimizeIndirectCommandBuffer + resetCommandsInBuffer + resetTextureAccessCounters + resolveCounters + sampleCountersInBuffer + synchronizeResource + synchronizeTexture + updateFence + waitForFence +*/ +@(objc_class="MTLBlitCommandEncoder") +BlitCommandEncoder :: struct { using _: CommandEncoder } + +@(objc_type=BlitCommandEncoder, objc_name="copyFromBufferEx") +BlitCommandEncoder_copyFromBufferEx :: #force_inline proc(self: ^BlitCommandEncoder, sourceBuffer: ^Buffer, sourceOffset: NS.UInteger, sourceBytesPerRow: NS.UInteger, sourceBytesPerImage: NS.UInteger, sourceSize: Size, destinationTexture: ^Texture, destinationSlice: NS.UInteger, destinationLevel: NS.UInteger, destinationOrigin: Origin) { + msgSend(nil, self, "copyFromBuffer:sourceOffset:sourceBytesPerRow:sourceBytesPerImage:sourceSize:toTexture:destinationSlice:destinationLevel:destinationOrigin:", sourceBuffer, sourceOffset, sourceBytesPerRow, sourceBytesPerImage, sourceSize, destinationTexture, destinationSlice, destinationLevel, destinationOrigin) +} +@(objc_type=BlitCommandEncoder, objc_name="copyFromBufferExWithOptions") +BlitCommandEncoder_copyFromBufferExWithOptions :: #force_inline proc(self: ^BlitCommandEncoder, sourceBuffer: ^Buffer, sourceOffset: NS.UInteger, sourceBytesPerRow: NS.UInteger, sourceBytesPerImage: NS.UInteger, sourceSize: Size, destinationTexture: ^Texture, destinationSlice: NS.UInteger, destinationLevel: NS.UInteger, destinationOrigin: Origin, options: BlitOption) { + msgSend(nil, self, "copyFromBuffer:sourceOffset:sourceBytesPerRow:sourceBytesPerImage:sourceSize:toTexture:destinationSlice:destinationLevel:destinationOrigin:options:", sourceBuffer, sourceOffset, sourceBytesPerRow, sourceBytesPerImage, sourceSize, destinationTexture, destinationSlice, destinationLevel, destinationOrigin, options) +} +@(objc_type=BlitCommandEncoder, objc_name="copyFromBuffer") +BlitCommandEncoder_copyFromBuffer :: #force_inline proc(self: ^BlitCommandEncoder, sourceBuffer: ^Buffer, sourceOffset: NS.UInteger, destinationBuffer: ^Buffer, destinationOffset: NS.UInteger, size: NS.UInteger) { + msgSend(nil, self, "copyFromBuffer:sourceOffset:toBuffer:destinationOffset:size:", sourceBuffer, sourceOffset, destinationBuffer, destinationOffset, size) +} +@(objc_type=BlitCommandEncoder, objc_name="copyFromTextureEx") +BlitCommandEncoder_copyFromTextureEx :: #force_inline proc(self: ^BlitCommandEncoder, sourceTexture: ^Texture, sourceSlice: NS.UInteger, sourceLevel: NS.UInteger, sourceOrigin: Origin, sourceSize: Size, destinationBuffer: ^Buffer, destinationOffset: NS.UInteger, destinationBytesPerRow: NS.UInteger, destinationBytesPerImage: NS.UInteger) { + msgSend(nil, self, "copyFromTexture:sourceSlice:sourceLevel:sourceOrigin:sourceSize:toBuffer:destinationOffset:destinationBytesPerRow:destinationBytesPerImage:", sourceTexture, sourceSlice, sourceLevel, sourceOrigin, sourceSize, destinationBuffer, destinationOffset, destinationBytesPerRow, destinationBytesPerImage) +} +@(objc_type=BlitCommandEncoder, objc_name="copyFromTextureExWithOptions") +BlitCommandEncoder_copyFromTextureExWithOptions :: #force_inline proc(self: ^BlitCommandEncoder, sourceTexture: ^Texture, sourceSlice: NS.UInteger, sourceLevel: NS.UInteger, sourceOrigin: Origin, sourceSize: Size, destinationBuffer: ^Buffer, destinationOffset: NS.UInteger, destinationBytesPerRow: NS.UInteger, destinationBytesPerImage: NS.UInteger, options: BlitOption) { + msgSend(nil, self, "copyFromTexture:sourceSlice:sourceLevel:sourceOrigin:sourceSize:toBuffer:destinationOffset:destinationBytesPerRow:destinationBytesPerImage:options:", sourceTexture, sourceSlice, sourceLevel, sourceOrigin, sourceSize, destinationBuffer, destinationOffset, destinationBytesPerRow, destinationBytesPerImage, options) +} +@(objc_type=BlitCommandEncoder, objc_name="copyFromTextureWithDestinationOrigin") +BlitCommandEncoder_copyFromTextureWithDestinationOrigin :: #force_inline proc(self: ^BlitCommandEncoder, sourceTexture: ^Texture, sourceSlice: NS.UInteger, sourceLevel: NS.UInteger, sourceOrigin: Origin, sourceSize: Size, destinationTexture: ^Texture, destinationSlice: NS.UInteger, destinationLevel: NS.UInteger, destinationOrigin: Origin) { + msgSend(nil, self, "copyFromTexture:sourceSlice:sourceLevel:sourceOrigin:sourceSize:toTexture:destinationSlice:destinationLevel:destinationOrigin:", sourceTexture, sourceSlice, sourceLevel, sourceOrigin, sourceSize, destinationTexture, destinationSlice, destinationLevel, destinationOrigin) +} +@(objc_type=BlitCommandEncoder, objc_name="copyFromTextureWithCounts") +BlitCommandEncoder_copyFromTextureWithCounts :: #force_inline proc(self: ^BlitCommandEncoder, sourceTexture: ^Texture, sourceSlice: NS.UInteger, sourceLevel: NS.UInteger, destinationTexture: ^Texture, destinationSlice: NS.UInteger, destinationLevel: NS.UInteger, sliceCount: NS.UInteger, levelCount: NS.UInteger) { + msgSend(nil, self, "copyFromTexture:sourceSlice:sourceLevel:toTexture:destinationSlice:destinationLevel:sliceCount:levelCount:", sourceTexture, sourceSlice, sourceLevel, destinationTexture, destinationSlice, destinationLevel, sliceCount, levelCount) +} +@(objc_type=BlitCommandEncoder, objc_name="copyFromTexture") +BlitCommandEncoder_copyFromTexture :: #force_inline proc(self: ^BlitCommandEncoder, sourceTexture: ^Texture, destinationTexture: ^Texture) { + msgSend(nil, self, "copyFromTexture:toTexture:", sourceTexture, destinationTexture) +} +@(objc_type=BlitCommandEncoder, objc_name="copyIndirectCommandBuffer") +BlitCommandEncoder_copyIndirectCommandBuffer :: #force_inline proc(self: ^BlitCommandEncoder, source: ^IndirectCommandBuffer, sourceRange: NS.Range, destination: ^IndirectCommandBuffer, destinationIndex: NS.UInteger) { + msgSend(nil, self, "copyIndirectCommandBuffer:sourceRange:destination:destinationIndex:", source, sourceRange, destination, destinationIndex) +} +@(objc_type=BlitCommandEncoder, objc_name="fillBuffer") +BlitCommandEncoder_fillBuffer :: #force_inline proc(self: ^BlitCommandEncoder, buffer: ^Buffer, range: NS.Range, value: u8) { + msgSend(nil, self, "fillBuffer:range:value:", buffer, range, value) +} +@(objc_type=BlitCommandEncoder, objc_name="generateMipmapsForTexture") +BlitCommandEncoder_generateMipmapsForTexture :: #force_inline proc(self: ^BlitCommandEncoder, texture: ^Texture) { + msgSend(nil, self, "generateMipmapsForTexture:", texture) +} +@(objc_type=BlitCommandEncoder, objc_name="getTextureAccessCounters") +BlitCommandEncoder_getTextureAccessCounters :: #force_inline proc(self: ^BlitCommandEncoder, texture: ^Texture, region: Region, mipLevel: NS.UInteger, slice: NS.UInteger, resetCounters: BOOL, countersBuffer: ^Buffer, countersBufferOffset: NS.UInteger) { + msgSend(nil, self, "getTextureAccessCounters:region:mipLevel:slice:resetCounters:countersBuffer:countersBufferOffset:", texture, region, mipLevel, slice, resetCounters, countersBuffer, countersBufferOffset) +} +@(objc_type=BlitCommandEncoder, objc_name="optimizeContentsForCPUAccess") +BlitCommandEncoder_optimizeContentsForCPUAccess :: #force_inline proc(self: ^BlitCommandEncoder, texture: ^Texture) { + msgSend(nil, self, "optimizeContentsForCPUAccess:", texture) +} +@(objc_type=BlitCommandEncoder, objc_name="optimizeContentsForCPUAccessWithSliceAndLevel") +BlitCommandEncoder_optimizeContentsForCPUAccessWithSliceAndLevel :: #force_inline proc(self: ^BlitCommandEncoder, texture: ^Texture, slice: NS.UInteger, level: NS.UInteger) { + msgSend(nil, self, "optimizeContentsForCPUAccess:slice:level:", texture, slice, level) +} +@(objc_type=BlitCommandEncoder, objc_name="optimizeContentsForGPUAccess") +BlitCommandEncoder_optimizeContentsForGPUAccess :: #force_inline proc(self: ^BlitCommandEncoder, texture: ^Texture) { + msgSend(nil, self, "optimizeContentsForGPUAccess:", texture) +} +@(objc_type=BlitCommandEncoder, objc_name="optimizeContentsForGPUAccessWithSliceAndLevel") +BlitCommandEncoder_optimizeContentsForGPUAccessWithSliceAndLevel :: #force_inline proc(self: ^BlitCommandEncoder, texture: ^Texture, slice: NS.UInteger, level: NS.UInteger) { + msgSend(nil, self, "optimizeContentsForGPUAccess:slice:level:", texture, slice, level) +} +@(objc_type=BlitCommandEncoder, objc_name="optimizeIndirectCommandBuffer") +BlitCommandEncoder_optimizeIndirectCommandBuffer :: #force_inline proc(self: ^BlitCommandEncoder, indirectCommandBuffer: ^Buffer, range: NS.Range) { + msgSend(nil, self, "optimizeIndirectCommandBuffer:withRange:", indirectCommandBuffer, range) +} +@(objc_type=BlitCommandEncoder, objc_name="resetCommandsInBuffer") +BlitCommandEncoder_resetCommandsInBuffer :: #force_inline proc(self: ^BlitCommandEncoder, buffer: ^Buffer, range: NS.Range) { + msgSend(nil, self, "resetCommandsInBuffer:withRange:", buffer, range) +} +@(objc_type=BlitCommandEncoder, objc_name="resetTextureAccessCounters") +BlitCommandEncoder_resetTextureAccessCounters :: #force_inline proc(self: ^BlitCommandEncoder, texture: ^Texture, region: Region, mipLevel: NS.UInteger, slice: NS.UInteger) { + msgSend(nil, self, "resetTextureAccessCounters:region:mipLevel:slice:", texture, region, mipLevel, slice) +} +@(objc_type=BlitCommandEncoder, objc_name="resolveCounters") +BlitCommandEncoder_resolveCounters :: #force_inline proc(self: ^BlitCommandEncoder, sampleBuffer: ^Buffer, range: NS.Range, destinationBuffer: ^Buffer, destinationOffset: NS.UInteger) { + msgSend(nil, self, "resolveCounters:inRange:destinationBuffer:destinationOffset:", sampleBuffer, range, destinationBuffer, destinationOffset) +} +@(objc_type=BlitCommandEncoder, objc_name="sampleCountersInBuffer") +BlitCommandEncoder_sampleCountersInBuffer :: #force_inline proc(self: ^BlitCommandEncoder, sampleBuffer: ^Buffer, sampleIndex: NS.UInteger, barrier: BOOL) { + msgSend(nil, self, "sampleCountersInBuffer:atSampleIndex:withBarrier:", sampleBuffer, sampleIndex, barrier) +} +@(objc_type=BlitCommandEncoder, objc_name="synchronizeResource") +BlitCommandEncoder_synchronizeResource :: #force_inline proc(self: ^BlitCommandEncoder, resource: ^Resource) { + msgSend(nil, self, "synchronizeResource:", resource) +} +@(objc_type=BlitCommandEncoder, objc_name="synchronizeTexture") +BlitCommandEncoder_synchronizeTexture :: #force_inline proc(self: ^BlitCommandEncoder, texture: ^Texture, slice: NS.UInteger, level: NS.UInteger) { + msgSend(nil, self, "synchronizeTexture:slice:level:", texture, slice, level) +} +@(objc_type=BlitCommandEncoder, objc_name="updateFence") +BlitCommandEncoder_updateFence :: #force_inline proc(self: ^BlitCommandEncoder, fence: ^Fence) { + msgSend(nil, self, "updateFence:", fence) +} +@(objc_type=BlitCommandEncoder, objc_name="waitForFence") +BlitCommandEncoder_waitForFence :: #force_inline proc(self: ^BlitCommandEncoder, fence: ^Fence) { + msgSend(nil, self, "waitForFence:", fence) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + Buffer +Class Methods: +Methods: + addDebugMarker + contents + didModifyRange + length + newRemoteBufferViewForDevice + newTextureWithDescriptor + remoteStorageBuffer + removeAllDebugMarkers +*/ +@(objc_class="MTLBuffer") +Buffer :: struct { using _: Resource } + +@(objc_type=Buffer, objc_name="addDebugMarker") +Buffer_addDebugMarker :: #force_inline proc(self: ^Buffer, marker: ^NS.String, range: NS.Range) { + msgSend(nil, self, "addDebugMarker:range:", marker, range) +} +@(objc_type=Buffer, objc_name="contents") +Buffer_contents :: #force_inline proc(self: ^Buffer) -> []byte { + contents := msgSend([^]byte, self, "contents") + length := Buffer_length(self) + return contents[:length] +} +@(objc_type=Buffer, objc_name="contentsPointer") +Buffer_contentsPointer :: #force_inline proc(self: ^Buffer) -> rawptr { + return msgSend(rawptr, self, "contents") +} +@(objc_type=Buffer, objc_name="contentsAsSlice") +Buffer_contentsAsSlice :: #force_inline proc(self: ^Buffer, $T: typeid/[]$E) -> T { + contents := msgSend([^]byte, self, "contents") + length := Buffer_length(self) + return mem.slice_data_cast(T, contents[:length]) +} +@(objc_type=Buffer, objc_name="contentsAsType") +Buffer_contentsAsType :: #force_inline proc(self: ^Buffer, $T: typeid, offset: uintptr = 0) -> ^T { + ptr := msgSend(rawptr, self, "contents") + return (^T)(uintptr(ptr) + offset) +} +@(objc_type=Buffer, objc_name="didModifyRange") +Buffer_didModifyRange :: #force_inline proc(self: ^Buffer, range: NS.Range) { + msgSend(nil, self, "didModifyRange:", range) +} +@(objc_type=Buffer, objc_name="length") +Buffer_length :: #force_inline proc(self: ^Buffer) -> NS.UInteger { + return msgSend(NS.UInteger, self, "length") +} +@(objc_type=Buffer, objc_name="newRemoteBufferViewForDevice") +Buffer_newRemoteBufferViewForDevice :: #force_inline proc(self: ^Buffer, device: ^Device) -> ^Buffer { + return msgSend(^Buffer, self, "newRemoteBufferViewForDevice:", device) +} +@(objc_type=Buffer, objc_name="newTexture") +Buffer_newTexture :: #force_inline proc(self: ^Buffer, descriptor: ^TextureDescriptor, offset: NS.UInteger, bytesPerRow: NS.UInteger) -> ^Texture { + return msgSend(^Texture, self, "newTextureWithDescriptor:offset:bytesPerRow:", descriptor, offset, bytesPerRow) +} +@(objc_type=Buffer, objc_name="remoteStorageBuffer") +Buffer_remoteStorageBuffer :: #force_inline proc(self: ^Buffer) -> ^Buffer { + return msgSend(^Buffer, self, "remoteStorageBuffer") +} +@(objc_type=Buffer, objc_name="removeAllDebugMarkers") +Buffer_removeAllDebugMarkers :: #force_inline proc(self: ^Buffer) { + msgSend(nil, self, "removeAllDebugMarkers") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + CaptureScope +Class Methods: +Methods: + beginScope + commandQueue + device + endScope + label + setLabel +*/ +@(objc_class="MTLCaptureScope") +CaptureScope :: struct { using _: NS.Object } + +@(objc_type=CaptureScope, objc_name="beginScope") +CaptureScope_beginScope :: #force_inline proc(self: ^CaptureScope) { + msgSend(nil, self, "beginScope") +} +@(objc_type=CaptureScope, objc_name="commandQueue") +CaptureScope_commandQueue :: #force_inline proc(self: ^CaptureScope) -> ^CommandQueue { + return msgSend(^CommandQueue, self, "commandQueue") +} +@(objc_type=CaptureScope, objc_name="device") +CaptureScope_device :: #force_inline proc(self: ^CaptureScope) -> ^Device { + return msgSend(^Device, self, "device") +} +@(objc_type=CaptureScope, objc_name="endScope") +CaptureScope_endScope :: #force_inline proc(self: ^CaptureScope) { + msgSend(nil, self, "endScope") +} +@(objc_type=CaptureScope, objc_name="label") +CaptureScope_label :: #force_inline proc(self: ^CaptureScope) -> ^NS.String { + return msgSend(^NS.String, self, "label") +} +@(objc_type=CaptureScope, objc_name="setLabel") +CaptureScope_setLabel :: #force_inline proc(self: ^CaptureScope, label: ^NS.String) { + msgSend(nil, self, "setLabel:", label) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + CommandBuffer +Class Methods: +Methods: + GPUEndTime + GPUStartTime + accelerationStructureCommandEncoder + addCompletedHandler + addScheduledHandler + blitCommandEncoder + blitCommandEncoderWithDescriptor + commandQueue + commit + computeCommandEncoder + computeCommandEncoderWithDescriptor + computeCommandEncoderWithDispatchType + device + encodeSignalEvent + encodeWaitForEvent + enqueue + error + errorOptions + kernelEndTime + kernelStartTime + label + logs + parallelRenderCommandEncoderWithDescriptor + popDebugGroup + presentDrawable + presentDrawable + presentDrawable + pushDebugGroup + renderCommandEncoderWithDescriptor + resourceStateCommandEncoder + resourceStateCommandEncoderWithDescriptor + retainedReferences + setLabel + status + waitUntilCompleted + waitUntilScheduled +*/ +@(objc_class="MTLCommandBuffer") +CommandBuffer :: struct { using _: NS.Object } + +@(objc_type=CommandBuffer, objc_name="GPUEndTime") +CommandBuffer_GPUEndTime :: #force_inline proc(self: ^CommandBuffer) -> CFTimeInterval { + return msgSend(CFTimeInterval, self, "GPUEndTime") +} +@(objc_type=CommandBuffer, objc_name="GPUStartTime") +CommandBuffer_GPUStartTime :: #force_inline proc(self: ^CommandBuffer) -> CFTimeInterval { + return msgSend(CFTimeInterval, self, "GPUStartTime") +} +@(objc_type=CommandBuffer, objc_name="accelerationStructureCommandEncoder") +CommandBuffer_accelerationStructureCommandEncoder :: #force_inline proc(self: ^CommandBuffer) -> ^AccelerationStructureCommandEncoder { + return msgSend(^AccelerationStructureCommandEncoder, self, "accelerationStructureCommandEncoder") +} +@(objc_type=CommandBuffer, objc_name="addCompletedHandler") +CommandBuffer_addCompletedHandler :: #force_inline proc(self: ^CommandBuffer, block: CommandBufferHandler) { + msgSend(nil, self, "addCompletedHandler:", block) +} +@(objc_type=CommandBuffer, objc_name="addScheduledHandler") +CommandBuffer_addScheduledHandler :: #force_inline proc(self: ^CommandBuffer, block: CommandBufferHandler) { + msgSend(nil, self, "addScheduledHandler:", block) +} +@(objc_type=CommandBuffer, objc_name="blitCommandEncoder") +CommandBuffer_blitCommandEncoder :: #force_inline proc(self: ^CommandBuffer) -> ^BlitCommandEncoder { + return msgSend(^BlitCommandEncoder, self, "blitCommandEncoder") +} +@(objc_type=CommandBuffer, objc_name="blitCommandEncoderWithDescriptor") +CommandBuffer_blitCommandEncoderWithDescriptor :: #force_inline proc(self: ^CommandBuffer, blitPassDescriptor: ^BlitPassDescriptor) -> ^BlitCommandEncoder { + return msgSend(^BlitCommandEncoder, self, "blitCommandEncoderWithDescriptor:", blitPassDescriptor) +} +@(objc_type=CommandBuffer, objc_name="commandQueue") +CommandBuffer_commandQueue :: #force_inline proc(self: ^CommandBuffer) -> ^CommandQueue { + return msgSend(^CommandQueue, self, "commandQueue") +} +@(objc_type=CommandBuffer, objc_name="commit") +CommandBuffer_commit :: #force_inline proc(self: ^CommandBuffer) { + msgSend(nil, self, "commit") +} +@(objc_type=CommandBuffer, objc_name="computeCommandEncoder") +CommandBuffer_computeCommandEncoder :: #force_inline proc(self: ^CommandBuffer) -> ^ComputeCommandEncoder { + return msgSend(^ComputeCommandEncoder, self, "computeCommandEncoder") +} +@(objc_type=CommandBuffer, objc_name="computeCommandEncoderWithDescriptor") +CommandBuffer_computeCommandEncoderWithDescriptor :: #force_inline proc(self: ^CommandBuffer, computePassDescriptor: ^ComputePassDescriptor) -> ^ComputeCommandEncoder { + return msgSend(^ComputeCommandEncoder, self, "computeCommandEncoderWithDescriptor:", computePassDescriptor) +} +@(objc_type=CommandBuffer, objc_name="computeCommandEncoderWithDispatchType") +CommandBuffer_computeCommandEncoderWithDispatchType :: #force_inline proc(self: ^CommandBuffer, dispatchType: DispatchType) -> ^ComputeCommandEncoder { + return msgSend(^ComputeCommandEncoder, self, "computeCommandEncoderWithDispatchType:", dispatchType) +} +@(objc_type=CommandBuffer, objc_name="device") +CommandBuffer_device :: #force_inline proc(self: ^CommandBuffer) -> ^Device { + return msgSend(^Device, self, "device") +} +@(objc_type=CommandBuffer, objc_name="encodeSignalEvent") +CommandBuffer_encodeSignalEvent :: #force_inline proc(self: ^CommandBuffer, event: ^Event, value: u64) { + msgSend(nil, self, "encodeSignalEvent:value:", event, value) +} +@(objc_type=CommandBuffer, objc_name="encodeWaitForEvent") +CommandBuffer_encodeWaitForEvent :: #force_inline proc(self: ^CommandBuffer, event: ^Event, value: u64) { + msgSend(nil, self, "encodeWaitForEvent:value:", event, value) +} +@(objc_type=CommandBuffer, objc_name="enqueue") +CommandBuffer_enqueue :: #force_inline proc(self: ^CommandBuffer) { + msgSend(nil, self, "enqueue") +} +@(objc_type=CommandBuffer, objc_name="error") +CommandBuffer_error :: #force_inline proc(self: ^CommandBuffer) -> ^NS.Error { + return msgSend(^NS.Error, self, "error") +} +@(objc_type=CommandBuffer, objc_name="errorOptions") +CommandBuffer_errorOptions :: #force_inline proc(self: ^CommandBuffer) -> CommandBufferErrorOption { + return msgSend(CommandBufferErrorOption, self, "errorOptions") +} +@(objc_type=CommandBuffer, objc_name="kernelEndTime") +CommandBuffer_kernelEndTime :: #force_inline proc(self: ^CommandBuffer) -> CFTimeInterval { + return msgSend(CFTimeInterval, self, "kernelEndTime") +} +@(objc_type=CommandBuffer, objc_name="kernelStartTime") +CommandBuffer_kernelStartTime :: #force_inline proc(self: ^CommandBuffer) -> CFTimeInterval { + return msgSend(CFTimeInterval, self, "kernelStartTime") +} +@(objc_type=CommandBuffer, objc_name="label") +CommandBuffer_label :: #force_inline proc(self: ^CommandBuffer) -> ^NS.String { + return msgSend(^NS.String, self, "label") +} +@(objc_type=CommandBuffer, objc_name="logs") +CommandBuffer_logs :: #force_inline proc(self: ^CommandBuffer) -> ^LogContainer { + return msgSend(^LogContainer, self, "logs") +} +@(objc_type=CommandBuffer, objc_name="parallelRenderCommandEncoder") +CommandBuffer_parallelRenderCommandEncoder :: #force_inline proc(self: ^CommandBuffer, renderPassDescriptor: ^RenderPassDescriptor) -> ^ParallelRenderCommandEncoder { + return msgSend(^ParallelRenderCommandEncoder, self, "parallelRenderCommandEncoderWithDescriptor:", renderPassDescriptor) +} +@(objc_type=CommandBuffer, objc_name="popDebugGroup") +CommandBuffer_popDebugGroup :: #force_inline proc(self: ^CommandBuffer) { + msgSend(nil, self, "popDebugGroup") +} +@(objc_type=CommandBuffer, objc_name="presentDrawable") +CommandBuffer_presentDrawable :: #force_inline proc(self: ^CommandBuffer, drawable: ^Drawable) { + msgSend(nil, self, "presentDrawable:", drawable) +} +@(objc_type=CommandBuffer, objc_name="presentDrawableAfterMinimumDuration") +CommandBuffer_presentDrawableAfterMinimumDuration :: #force_inline proc(self: ^CommandBuffer, drawable: ^Drawable, duration: CFTimeInterval) { + msgSend(nil, self, "presentDrawable:afterMinimumDuration:", drawable, duration) +} +@(objc_type=CommandBuffer, objc_name="presentDrawableAtTime") +CommandBuffer_presentDrawableAtTime :: #force_inline proc(self: ^CommandBuffer, drawable: ^Drawable, presentationTime: CFTimeInterval) { + msgSend(nil, self, "presentDrawable:atTime:", drawable, presentationTime) +} +@(objc_type=CommandBuffer, objc_name="pushDebugGroup") +CommandBuffer_pushDebugGroup :: #force_inline proc(self: ^CommandBuffer, string: ^NS.String) { + msgSend(nil, self, "pushDebugGroup:", string) +} +@(objc_type=CommandBuffer, objc_name="renderCommandEncoderWithDescriptor") +CommandBuffer_renderCommandEncoderWithDescriptor :: #force_inline proc(self: ^CommandBuffer, renderPassDescriptor: ^RenderPassDescriptor) -> ^RenderCommandEncoder { + return msgSend(^RenderCommandEncoder, self, "renderCommandEncoderWithDescriptor:", renderPassDescriptor) +} +@(objc_type=CommandBuffer, objc_name="resourceStateCommandEncoder") +CommandBuffer_resourceStateCommandEncoder :: #force_inline proc(self: ^CommandBuffer) -> ^CommandBuffer { + return msgSend(^CommandBuffer, self, "resourceStateCommandEncoder") +} +@(objc_type=CommandBuffer, objc_name="resourceStateCommandEncoderWithDescriptor") +CommandBuffer_resourceStateCommandEncoderWithDescriptor :: #force_inline proc(self: ^CommandBuffer, resourceStatePassDescriptor: ^ResourceStatePassDescriptor) -> ^ResourceStateCommandEncoder { + return msgSend(^ResourceStateCommandEncoder, self, "resourceStateCommandEncoderWithDescriptor:", resourceStatePassDescriptor) +} +@(objc_type=CommandBuffer, objc_name="retainedReferences") +CommandBuffer_retainedReferences :: #force_inline proc(self: ^CommandBuffer) -> BOOL { + return msgSend(BOOL, self, "retainedReferences") +} +@(objc_type=CommandBuffer, objc_name="setLabel") +CommandBuffer_setLabel :: #force_inline proc(self: ^CommandBuffer, label: ^NS.String) { + msgSend(nil, self, "setLabel:", label) +} +@(objc_type=CommandBuffer, objc_name="status") +CommandBuffer_status :: #force_inline proc(self: ^CommandBuffer) -> CommandBufferStatus { + return msgSend(CommandBufferStatus, self, "status") +} +@(objc_type=CommandBuffer, objc_name="waitUntilCompleted") +CommandBuffer_waitUntilCompleted :: #force_inline proc(self: ^CommandBuffer) { + msgSend(nil, self, "waitUntilCompleted") +} +@(objc_type=CommandBuffer, objc_name="waitUntilScheduled") +CommandBuffer_waitUntilScheduled :: #force_inline proc(self: ^CommandBuffer) { + msgSend(nil, self, "waitUntilScheduled") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + CommandBufferEncoderInfo +Class Methods: +Methods: + debugSignposts + errorState + label +*/ +@(objc_class="MTLCommandBufferEncoderInfo") +CommandBufferEncoderInfo :: struct { using _: NS.Object } + +@(objc_type=CommandBufferEncoderInfo, objc_name="debugSignposts") +CommandBufferEncoderInfo_debugSignposts :: #force_inline proc(self: ^CommandBufferEncoderInfo) -> ^NS.Array { + return msgSend(^NS.Array, self, "debugSignposts") +} +@(objc_type=CommandBufferEncoderInfo, objc_name="errorState") +CommandBufferEncoderInfo_errorState :: #force_inline proc(self: ^CommandBufferEncoderInfo) -> CommandEncoderErrorState { + return msgSend(CommandEncoderErrorState, self, "errorState") +} +@(objc_type=CommandBufferEncoderInfo, objc_name="label") +CommandBufferEncoderInfo_label :: #force_inline proc(self: ^CommandBufferEncoderInfo) -> ^NS.String { + return msgSend(^NS.String, self, "label") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + CommandEncoder +Class Methods: +Methods: + device + endEncoding + insertDebugSignpost + label + popDebugGroup + pushDebugGroup + setLabel +*/ +@(objc_class="MTLCommandEncoder") +CommandEncoder :: struct { using _: NS.Object } + +@(objc_type=CommandEncoder, objc_name="device") +CommandEncoder_device :: #force_inline proc(self: ^CommandEncoder) -> ^Device { + return msgSend(^Device, self, "device") +} +@(objc_type=CommandEncoder, objc_name="endEncoding") +CommandEncoder_endEncoding :: #force_inline proc(self: ^CommandEncoder) { + msgSend(nil, self, "endEncoding") +} +@(objc_type=CommandEncoder, objc_name="insertDebugSignpost") +CommandEncoder_insertDebugSignpost :: #force_inline proc(self: ^CommandEncoder, string: ^NS.String) { + msgSend(nil, self, "insertDebugSignpost:", string) +} +@(objc_type=CommandEncoder, objc_name="label") +CommandEncoder_label :: #force_inline proc(self: ^CommandEncoder) -> ^NS.String { + return msgSend(^NS.String, self, "label") +} +@(objc_type=CommandEncoder, objc_name="popDebugGroup") +CommandEncoder_popDebugGroup :: #force_inline proc(self: ^CommandEncoder) { + msgSend(nil, self, "popDebugGroup") +} +@(objc_type=CommandEncoder, objc_name="pushDebugGroup") +CommandEncoder_pushDebugGroup :: #force_inline proc(self: ^CommandEncoder, string: ^NS.String) { + msgSend(nil, self, "pushDebugGroup:", string) +} +@(objc_type=CommandEncoder, objc_name="setLabel") +CommandEncoder_setLabel :: #force_inline proc(self: ^CommandEncoder, label: ^NS.String) { + msgSend(nil, self, "setLabel:", label) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + CommandQueue +Class Methods: +Methods: + commandBuffer + commandBufferWithDescriptor + commandBufferWithUnretainedReferences + device + insertDebugCaptureBoundary + label + setLabel +*/ +@(objc_class="MTLCommandQueue") +CommandQueue :: struct { using _: NS.Object } + +@(objc_type=CommandQueue, objc_name="commandBuffer") +CommandQueue_commandBuffer :: #force_inline proc(self: ^CommandQueue) -> ^CommandBuffer { + return msgSend(^CommandBuffer, self, "commandBuffer") +} +@(objc_type=CommandQueue, objc_name="commandBufferWithDescriptor") +CommandQueue_commandBufferWithDescriptor :: #force_inline proc(self: ^CommandQueue, descriptor: ^CommandBufferDescriptor) -> ^CommandQueue { + return msgSend(^CommandQueue, self, "commandBufferWithDescriptor:", descriptor) +} +@(objc_type=CommandQueue, objc_name="commandBufferWithUnretainedReferences") +CommandQueue_commandBufferWithUnretainedReferences :: #force_inline proc(self: ^CommandQueue) -> ^CommandQueue { + return msgSend(^CommandQueue, self, "commandBufferWithUnretainedReferences") +} +@(objc_type=CommandQueue, objc_name="device") +CommandQueue_device :: #force_inline proc(self: ^CommandQueue) -> ^Device { + return msgSend(^Device, self, "device") +} +@(objc_type=CommandQueue, objc_name="insertDebugCaptureBoundary") +CommandQueue_insertDebugCaptureBoundary :: #force_inline proc(self: ^CommandQueue) { + msgSend(nil, self, "insertDebugCaptureBoundary") +} +@(objc_type=CommandQueue, objc_name="label") +CommandQueue_label :: #force_inline proc(self: ^CommandQueue) -> ^NS.String { + return msgSend(^NS.String, self, "label") +} +@(objc_type=CommandQueue, objc_name="setLabel") +CommandQueue_setLabel :: #force_inline proc(self: ^CommandQueue, label: ^NS.String) { + msgSend(nil, self, "setLabel:", label) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + ComputeCommandEncoder +Class Methods: +Methods: + dispatchThreadgroups + dispatchThreadgroupsWithIndirectBuffer + dispatchThreads + dispatchType + executeCommandsInBuffer + executeCommandsInBuffer + memoryBarrierWithResources + memoryBarrierWithScope + sampleCountersInBuffer + setAccelerationStructure + setBuffer + setBufferOffset + setBuffers + setBytes + setComputePipelineState + setImageblockWidth + setIntersectionFunctionTable + setIntersectionFunctionTables + setSamplerState + setSamplerState + setSamplerStates + setSamplerStates + setStageInRegion + setStageInRegionWithIndirectBuffer + setTexture + setTextures + setThreadgroupMemoryLength + setVisibleFunctionTable + setVisibleFunctionTables + updateFence + useHeap + useHeaps + useResource + useResources + waitForFence +*/ +@(objc_class="MTLComputeCommandEncoder") +ComputeCommandEncoder :: struct { using _: CommandEncoder } + +@(objc_type=ComputeCommandEncoder, objc_name="dispatchThreadgroups") +ComputeCommandEncoder_dispatchThreadgroups :: #force_inline proc(self: ^ComputeCommandEncoder, threadgroupsPerGrid: Size, threadsPerThreadgroup: Size) { + msgSend(nil, self, "dispatchThreadgroups:threadsPerThreadgroup:", threadgroupsPerGrid, threadsPerThreadgroup) +} +@(objc_type=ComputeCommandEncoder, objc_name="dispatchThreadgroupsWithIndirectBuffer") +ComputeCommandEncoder_dispatchThreadgroupsWithIndirectBuffer :: #force_inline proc(self: ^ComputeCommandEncoder, indirectBuffer: ^Buffer, indirectBufferOffset: NS.UInteger, threadsPerThreadgroup: Size) { + msgSend(nil, self, "dispatchThreadgroupsWithIndirectBuffer:indirectBufferOffset:threadsPerThreadgroup:", indirectBuffer, indirectBufferOffset, threadsPerThreadgroup) +} +@(objc_type=ComputeCommandEncoder, objc_name="dispatchThreads") +ComputeCommandEncoder_dispatchThreads :: #force_inline proc(self: ^ComputeCommandEncoder, threadsPerGrid: Size, threadsPerThreadgroup: Size) { + msgSend(nil, self, "dispatchThreads:threadsPerThreadgroup:", threadsPerGrid, threadsPerThreadgroup) +} +@(objc_type=ComputeCommandEncoder, objc_name="dispatchType") +ComputeCommandEncoder_dispatchType :: #force_inline proc(self: ^ComputeCommandEncoder) -> DispatchType { + return msgSend(DispatchType, self, "dispatchType") +} +@(objc_type=ComputeCommandEncoder, objc_name="executeCommandsInBuffer") +ComputeCommandEncoder_executeCommandsInBuffer :: #force_inline proc(self: ^ComputeCommandEncoder, indirectCommandbuffer: ^Buffer, indirectRangeBuffer: ^Buffer, indirectBufferOffset: NS.UInteger) { + msgSend(nil, self, "executeCommandsInBuffer:indirectBuffer:indirectBufferOffset:", indirectCommandbuffer, indirectRangeBuffer, indirectBufferOffset) +} +@(objc_type=ComputeCommandEncoder, objc_name="executeCommandsInBufferWithRange") +ComputeCommandEncoder_executeCommandsInBufferWithRange :: #force_inline proc(self: ^ComputeCommandEncoder, indirectCommandBuffer: ^Buffer, executionRange: NS.Range) { + msgSend(nil, self, "executeCommandsInBuffer:withRange:", indirectCommandBuffer, executionRange) +} +@(objc_type=ComputeCommandEncoder, objc_name="memoryBarrierWithResources") +ComputeCommandEncoder_memoryBarrierWithResources :: #force_inline proc(self: ^ComputeCommandEncoder, resources: []^Resource) { + msgSend(nil, self, "memoryBarrierWithResources:count:", raw_data(resources), NS.UInteger(len(resources))) +} +@(objc_type=ComputeCommandEncoder, objc_name="memoryBarrierWithScope") +ComputeCommandEncoder_memoryBarrierWithScope :: #force_inline proc(self: ^ComputeCommandEncoder, scope: BarrierScope) { + msgSend(nil, self, "memoryBarrierWithScope:", scope) +} +@(objc_type=ComputeCommandEncoder, objc_name="sampleCountersInBuffer") +ComputeCommandEncoder_sampleCountersInBuffer :: #force_inline proc(self: ^ComputeCommandEncoder, sampleBuffer: ^Buffer, sampleIndex: NS.UInteger, barrier: BOOL) { + msgSend(nil, self, "sampleCountersInBuffer:atSampleIndex:withBarrier:", sampleBuffer, sampleIndex, barrier) +} +@(objc_type=ComputeCommandEncoder, objc_name="setAccelerationStructure") +ComputeCommandEncoder_setAccelerationStructure :: #force_inline proc(self: ^ComputeCommandEncoder, accelerationStructure: ^AccelerationStructure, bufferIndex: NS.UInteger) { + msgSend(nil, self, "setAccelerationStructure:atBufferIndex:", accelerationStructure, bufferIndex) +} +@(objc_type=ComputeCommandEncoder, objc_name="setBuffer") +ComputeCommandEncoder_setBuffer :: #force_inline proc(self: ^ComputeCommandEncoder, buffer: ^Buffer, offset: NS.UInteger, index: NS.UInteger) { + msgSend(nil, self, "setBuffer:offset:atIndex:", buffer, offset, index) +} +@(objc_type=ComputeCommandEncoder, objc_name="setBufferOffset") +ComputeCommandEncoder_setBufferOffset :: #force_inline proc(self: ^ComputeCommandEncoder, offset: NS.UInteger, index: NS.UInteger) { + msgSend(nil, self, "setBufferOffset:atIndex:", offset, index) +} +@(objc_type=ComputeCommandEncoder, objc_name="setBuffers") +ComputeCommandEncoder_setBuffers :: #force_inline proc(self: ^ComputeCommandEncoder, buffers: []^Buffer, offsets: []NS.UInteger, range: NS.Range) { + assert(len(buffers) == len(offsets)) + assert(range.length <= NS.UInteger(len(buffers))) + msgSend(nil, self, "setBuffers:offsets:withRange:", raw_data(buffers), raw_data(offsets), range) +} +@(objc_type=ComputeCommandEncoder, objc_name="setBytes") +ComputeCommandEncoder_setBytes :: #force_inline proc(self: ^ComputeCommandEncoder, bytes: []byte, index: NS.UInteger) { + msgSend(nil, self, "setBytes:length:atIndex:", raw_data(bytes), NS.UInteger(len(bytes)), index) +} +@(objc_type=ComputeCommandEncoder, objc_name="setComputePipelineState") +ComputeCommandEncoder_setComputePipelineState :: #force_inline proc(self: ^ComputeCommandEncoder, pipelineState: ^ComputePipelineState) { + msgSend(nil, self, "setComputePipelineState:", pipelineState) +} +@(objc_type=ComputeCommandEncoder, objc_name="setImageblockWidth") +ComputeCommandEncoder_setImageblockWidth :: #force_inline proc(self: ^ComputeCommandEncoder, width: NS.UInteger, height: NS.UInteger) { + msgSend(nil, self, "setImageblockWidth:height:", width, height) +} +@(objc_type=ComputeCommandEncoder, objc_name="setIntersectionFunctionTable") +ComputeCommandEncoder_setIntersectionFunctionTable :: #force_inline proc(self: ^ComputeCommandEncoder, intersectionFunctionTable: ^IntersectionFunctionTable, bufferIndex: NS.UInteger) { + msgSend(nil, self, "setIntersectionFunctionTable:atBufferIndex:", intersectionFunctionTable, bufferIndex) +} +@(objc_type=ComputeCommandEncoder, objc_name="setIntersectionFunctionTables") +ComputeCommandEncoder_setIntersectionFunctionTables :: #force_inline proc(self: ^ComputeCommandEncoder, intersectionFunctionTables: []^IntersectionFunctionTable, range: NS.Range) { + assert(range.length <= NS.UInteger(len(intersectionFunctionTables))) + msgSend(nil, self, "setIntersectionFunctionTables:withBufferRange:", raw_data(intersectionFunctionTables), range) +} +@(objc_type=ComputeCommandEncoder, objc_name="setSamplerState") +ComputeCommandEncoder_setSamplerState :: #force_inline proc(self: ^ComputeCommandEncoder, sampler: ^SamplerState, index: NS.UInteger) { + msgSend(nil, self, "setSamplerState:atIndex:", sampler, index) +} +@(objc_type=ComputeCommandEncoder, objc_name="setSamplerStateWithLod") +ComputeCommandEncoder_setSamplerState_lodMinClamp_lodMaxClamp :: #force_inline proc(self: ^ComputeCommandEncoder, sampler: ^SamplerState, lodMinClamp: f32, lodMaxClamp: f32, index: NS.UInteger) { + msgSend(nil, self, "setSamplerState:lodMinClamp:lodMaxClamp:atIndex:", sampler, lodMinClamp, lodMaxClamp, index) +} +@(objc_type=ComputeCommandEncoder, objc_name="setSamplerStatesWithLod") +ComputeCommandEncoder_setSamplerStatesWithLod :: #force_inline proc(self: ^ComputeCommandEncoder, samplers: []^SamplerState, lodMinClamps, lodMaxClamps: []f32, range: NS.Range) { + msgSend(nil, self, "setSamplerStates:lodMinClamps:lodMaxClamps:withRange:", raw_data(samplers), raw_data(lodMinClamps), raw_data(lodMaxClamps), range) +} +@(objc_type=ComputeCommandEncoder, objc_name="setSamplerStatesWithRange") +ComputeCommandEncoder_setSamplerStatesWithRange :: #force_inline proc(self: ^ComputeCommandEncoder, samplers: []^SamplerState, range: NS.Range) { + msgSend(nil, self, "setSamplerStates:withRange:", raw_data(samplers), range) +} +@(objc_type=ComputeCommandEncoder, objc_name="setStageInRegion") +ComputeCommandEncoder_setStageInRegion :: #force_inline proc(self: ^ComputeCommandEncoder, region: Region) { + msgSend(nil, self, "setStageInRegion:", region) +} +@(objc_type=ComputeCommandEncoder, objc_name="setStageInRegionWithIndirectBuffer") +ComputeCommandEncoder_setStageInRegionWithIndirectBuffer :: #force_inline proc(self: ^ComputeCommandEncoder, indirectBuffer: ^Buffer, indirectBufferOffset: NS.UInteger) { + msgSend(nil, self, "setStageInRegionWithIndirectBuffer:indirectBufferOffset:", indirectBuffer, indirectBufferOffset) +} +@(objc_type=ComputeCommandEncoder, objc_name="setTexture") +ComputeCommandEncoder_setTexture :: #force_inline proc(self: ^ComputeCommandEncoder, texture: ^Texture, index: NS.UInteger) { + msgSend(nil, self, "setTexture:atIndex:", texture, index) +} +@(objc_type=ComputeCommandEncoder, objc_name="setTextures") +ComputeCommandEncoder_setTextures :: #force_inline proc(self: ^ComputeCommandEncoder, textures: []^Texture, range: NS.Range) { + msgSend(nil, self, "setTextures:withRange:", raw_data(textures), range) +} +@(objc_type=ComputeCommandEncoder, objc_name="setThreadgroupMemoryLength") +ComputeCommandEncoder_setThreadgroupMemoryLength :: #force_inline proc(self: ^ComputeCommandEncoder, length: NS.UInteger, index: NS.UInteger) { + msgSend(nil, self, "setThreadgroupMemoryLength:atIndex:", length, index) +} +@(objc_type=ComputeCommandEncoder, objc_name="setVisibleFunctionTable") +ComputeCommandEncoder_setVisibleFunctionTable :: #force_inline proc(self: ^ComputeCommandEncoder, visibleFunctionTable: ^VisibleFunctionTable, bufferIndex: NS.UInteger) { + msgSend(nil, self, "setVisibleFunctionTable:atBufferIndex:", visibleFunctionTable, bufferIndex) +} +@(objc_type=ComputeCommandEncoder, objc_name="setVisibleFunctionTables") +ComputeCommandEncoder_setVisibleFunctionTables :: #force_inline proc(self: ^ComputeCommandEncoder, visibleFunctionTables: []^VisibleFunctionTable, range: NS.Range) { + msgSend(nil, self, "setVisibleFunctionTables:withBufferRange:", raw_data(visibleFunctionTables), range) +} +@(objc_type=ComputeCommandEncoder, objc_name="updateFence") +ComputeCommandEncoder_updateFence :: #force_inline proc(self: ^ComputeCommandEncoder, fence: ^Fence) { + msgSend(nil, self, "updateFence:", fence) +} +@(objc_type=ComputeCommandEncoder, objc_name="useHeap") +ComputeCommandEncoder_useHeap :: #force_inline proc(self: ^ComputeCommandEncoder, heap: ^Heap) { + msgSend(nil, self, "useHeap:", heap) +} +@(objc_type=ComputeCommandEncoder, objc_name="useHeaps") +ComputeCommandEncoder_useHeaps :: #force_inline proc(self: ^ComputeCommandEncoder, heaps: []^Heap) { + msgSend(nil, self, "useHeaps:count:", raw_data(heaps), NS.UInteger(len(heaps))) +} +@(objc_type=ComputeCommandEncoder, objc_name="useResource") +ComputeCommandEncoder_useResource :: #force_inline proc(self: ^ComputeCommandEncoder, resource: ^Resource, usage: ResourceUsage) { + msgSend(nil, self, "useResource:usage:", resource, usage) +} +@(objc_type=ComputeCommandEncoder, objc_name="useResources") +ComputeCommandEncoder_useResources :: #force_inline proc(self: ^ComputeCommandEncoder, resources: []^Resource, usage: ResourceUsage) { + msgSend(nil, self, "useResources:count:usage:", raw_data(resources), NS.UInteger(len(resources)), usage) +} +@(objc_type=ComputeCommandEncoder, objc_name="waitForFence") +ComputeCommandEncoder_waitForFence :: #force_inline proc(self: ^ComputeCommandEncoder, fence: ^Fence) { + msgSend(nil, self, "waitForFence:", fence) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + ComputePipelineState +Class Methods: +Methods: + device + functionHandleWithFunction + imageblockMemoryLengthForDimensions + label + maxTotalThreadsPerThreadgroup + newComputePipelineStateWithAdditionalBinaryFunctions + newIntersectionFunctionTableWithDescriptor + newVisibleFunctionTableWithDescriptor + staticThreadgroupMemoryLength + supportIndirectCommandBuffers + threadExecutionWidth +*/ +@(objc_class="MTLComputePipelineState") +ComputePipelineState :: struct { using _: NS.Object } + +@(objc_type=ComputePipelineState, objc_name="device") +ComputePipelineState_device :: #force_inline proc(self: ^ComputePipelineState) -> ^Device { + return msgSend(^Device, self, "device") +} +@(objc_type=ComputePipelineState, objc_name="functionHandleWithFunction") +ComputePipelineState_functionHandleWithFunction :: #force_inline proc(self: ^ComputePipelineState, function: ^Function) -> ^FunctionHandle { + return msgSend(^FunctionHandle, self, "functionHandleWithFunction:", function) +} +@(objc_type=ComputePipelineState, objc_name="imageblockMemoryLengthForDimensions") +ComputePipelineState_imageblockMemoryLengthForDimensions :: #force_inline proc(self: ^ComputePipelineState, imageblockDimensions: Size) -> ^ComputePipelineState { + return msgSend(^ComputePipelineState, self, "imageblockMemoryLengthForDimensions:", imageblockDimensions) +} +@(objc_type=ComputePipelineState, objc_name="label") +ComputePipelineState_label :: #force_inline proc(self: ^ComputePipelineState) -> ^NS.String { + return msgSend(^NS.String, self, "label") +} +@(objc_type=ComputePipelineState, objc_name="maxTotalThreadsPerThreadgroup") +ComputePipelineState_maxTotalThreadsPerThreadgroup :: #force_inline proc(self: ^ComputePipelineState) -> NS.UInteger { + return msgSend(NS.UInteger, self, "maxTotalThreadsPerThreadgroup") +} +@(objc_type=ComputePipelineState, objc_name="newComputePipelineState") +ComputePipelineState_newComputePipelineState :: #force_inline proc(self: ^ComputePipelineState, functions: ^NS.Array) -> (state: ^ComputePipelineState, error: ^NS.Error) { + state = msgSend(^ComputePipelineState, self, "newComputePipelineStateWithAdditionalBinaryFunctions:error:", functions, &error) + return +} +@(objc_type=ComputePipelineState, objc_name="newIntersectionFunctionTable") +ComputePipelineState_newIntersectionFunctionTable :: #force_inline proc(self: ^ComputePipelineState, descriptor: ^IntersectionFunctionTableDescriptor) -> ^IntersectionFunctionTable { + return msgSend(^IntersectionFunctionTable, self, "newIntersectionFunctionTableWithDescriptor:", descriptor) +} +@(objc_type=ComputePipelineState, objc_name="newVisibleFunctionTable") +ComputePipelineState_newVisibleFunctionTable :: #force_inline proc(self: ^ComputePipelineState, descriptor: ^VisibleFunctionTableDescriptor) -> ^VisibleFunctionTable { + return msgSend(^VisibleFunctionTable, self, "newVisibleFunctionTableWithDescriptor:", descriptor) +} +@(objc_type=ComputePipelineState, objc_name="staticThreadgroupMemoryLength") +ComputePipelineState_staticThreadgroupMemoryLength :: #force_inline proc(self: ^ComputePipelineState) -> NS.UInteger { + return msgSend(NS.UInteger, self, "staticThreadgroupMemoryLength") +} +@(objc_type=ComputePipelineState, objc_name="supportIndirectCommandBuffers") +ComputePipelineState_supportIndirectCommandBuffers :: #force_inline proc(self: ^ComputePipelineState) -> BOOL { + return msgSend(BOOL, self, "supportIndirectCommandBuffers") +} +@(objc_type=ComputePipelineState, objc_name="threadExecutionWidth") +ComputePipelineState_threadExecutionWidth :: #force_inline proc(self: ^ComputePipelineState) -> NS.UInteger { + return msgSend(NS.UInteger, self, "threadExecutionWidth") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + Counter +Class Methods: +Methods: + name +*/ +@(objc_class="MTLCounter") +Counter :: struct { using _: NS.Object } + +@(objc_type=Counter, objc_name="name") +Counter_name :: #force_inline proc(self: ^Counter) -> ^NS.String { + return msgSend(^NS.String, self, "name") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + CounterSampleBuffer +Class Methods: +Methods: + device + label + resolveCounterRange + sampleCount +*/ +@(objc_class="MTLCounterSampleBuffer") +CounterSampleBuffer :: struct { using _: NS.Object } + +@(objc_type=CounterSampleBuffer, objc_name="device") +CounterSampleBuffer_device :: #force_inline proc(self: ^CounterSampleBuffer) -> ^Device { + return msgSend(^Device, self, "device") +} +@(objc_type=CounterSampleBuffer, objc_name="label") +CounterSampleBuffer_label :: #force_inline proc(self: ^CounterSampleBuffer) -> ^NS.String { + return msgSend(^NS.String, self, "label") +} +@(objc_type=CounterSampleBuffer, objc_name="resolveCounterRange") +CounterSampleBuffer_resolveCounterRange :: #force_inline proc(self: ^CounterSampleBuffer, range: NS.Range) -> ^NS.Data { + return msgSend(^NS.Data, self, "resolveCounterRange:", range) +} +@(objc_type=CounterSampleBuffer, objc_name="sampleCount") +CounterSampleBuffer_sampleCount :: #force_inline proc(self: ^CounterSampleBuffer) -> NS.UInteger { + return msgSend(NS.UInteger, self, "sampleCount") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + CounterSet +Class Methods: +Methods: + counters + name +*/ +@(objc_class="MTLCounterSet") +CounterSet :: struct { using _: NS.Object } + +@(objc_type=CounterSet, objc_name="counters") +CounterSet_counters :: #force_inline proc(self: ^CounterSet) -> ^NS.Array { + return msgSend(^NS.Array, self, "counters") +} +@(objc_type=CounterSet, objc_name="name") +CounterSet_name :: #force_inline proc(self: ^CounterSet) -> ^NS.String { + return msgSend(^NS.String, self, "name") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + DepthStencilState +Class Methods: +Methods: + device + label +*/ +@(objc_class="MTLDepthStencilState") +DepthStencilState :: struct { using _: NS.Object } + +@(objc_type=DepthStencilState, objc_name="device") +DepthStencilState_device :: #force_inline proc(self: ^DepthStencilState) -> ^Device { + return msgSend(^Device, self, "device") +} +@(objc_type=DepthStencilState, objc_name="label") +DepthStencilState_label :: #force_inline proc(self: ^DepthStencilState) -> ^NS.String { + return msgSend(^NS.String, self, "label") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + Device +Class Methods: +Methods: + accelerationStructureSizesWithDescriptor + areBarycentricCoordsSupported + areProgrammableSamplePositionsSupported + areRasterOrderGroupsSupported + argumentBuffersSupport + convertSparsePixelRegions + convertSparseTileRegions + counterSets + currentAllocatedSize + getDefaultSamplePositions + hasUnifiedMemory + heapBufferSizeAndAlignWithLength + heapTextureSizeAndAlignWithDescriptor + isDepth24Stencil8PixelFormatSupported + isHeadless + isLowPower + isRemovable + location + locationNumber + maxArgumentBufferSamplerCount + maxBufferLength + maxThreadgroupMemoryLength + maxThreadsPerThreadgroup + maxTransferRate + minimumLinearTextureAlignmentForPixelFormat + minimumTextureBufferAlignmentForPixelFormat + name + newAccelerationStructureWithDescriptor + newAccelerationStructureWithSize + newArgumentEncoderWithArguments + newBinaryArchiveWithDescriptor + newBufferWithBytes + newBufferWithBytesNoCopy + newBufferWithLength + newCommandQueue + newCommandQueueWithMaxCommandBufferCount + newComputePipelineStateWithDescriptor + newComputePipelineStateWithDescriptor + newComputePipelineStateWithFunction + newComputePipelineStateWithFunction + newComputePipelineStateWithFunction + newComputePipelineStateWithFunction + newCounterSampleBufferWithDescriptor + newDefaultLibrary + newDefaultLibraryWithBundle + newDepthStencilStateWithDescriptor + newDynamicLibrary + newDynamicLibraryWithURL + newEvent + newFence + newHeapWithDescriptor + newIndirectCommandBufferWithDescriptor + newLibraryWithData + newLibraryWithFile + newLibraryWithSource + newLibraryWithSource + newLibraryWithURL + newRasterizationRateMapWithDescriptor + newRenderPipelineStateWithDescriptor + newRenderPipelineStateWithDescriptor + newRenderPipelineStateWithDescriptor + newRenderPipelineStateWithDescriptor + newRenderPipelineStateWithTileDescriptor + newRenderPipelineStateWithTileDescriptor + newSamplerState + newSharedEvent + newSharedEventWithHandle + newSharedTextureWithDescriptor + newSharedTextureWithHandle + newTextureWithDescriptor + newTextureWithDescriptor + peerCount + peerGroupID + peerIndex + readWriteTextureSupport + recommendedMaxWorkingSetSize + registryID + sampleTimestamps + sparseTileSizeInBytes + sparseTileSizeWithTextureType + supports32BitFloatFiltering + supports32BitMSAA + supportsBCTextureCompression + supportsCounterSampling + supportsDynamicLibraries + supportsFamily + supportsFeatureSet + supportsFunctionPointers + supportsPullModelInterpolation + supportsQueryTextureLOD + supportsRasterizationRateMapWithLayerCount + supportsRaytracing + supportsShaderBarycentricCoordinates + supportsTextureSampleCount + supportsVertexAmplificationCount +*/ +@(objc_class="MTLDevice") +Device :: struct { using _: NS.Object } + +@(objc_type=Device, objc_name="accelerationStructureSizesWithDescriptor") +Device_accelerationStructureSizesWithDescriptor :: #force_inline proc(self: ^Device, descriptor: ^AccelerationStructureDescriptor) -> AccelerationStructureSizes { + return msgSend(AccelerationStructureSizes, self, "accelerationStructureSizesWithDescriptor:", descriptor) +} +@(objc_type=Device, objc_name="areBarycentricCoordsSupported") +Device_areBarycentricCoordsSupported :: #force_inline proc(self: ^Device) -> BOOL { + return msgSend(BOOL, self, "areBarycentricCoordsSupported") +} +@(objc_type=Device, objc_name="areProgrammableSamplePositionsSupported") +Device_areProgrammableSamplePositionsSupported :: #force_inline proc(self: ^Device) -> BOOL { + return msgSend(BOOL, self, "areProgrammableSamplePositionsSupported") +} +@(objc_type=Device, objc_name="areRasterOrderGroupsSupported") +Device_areRasterOrderGroupsSupported :: #force_inline proc(self: ^Device) -> BOOL { + return msgSend(BOOL, self, "areRasterOrderGroupsSupported") +} +@(objc_type=Device, objc_name="argumentBuffersSupport") +Device_argumentBuffersSupport :: #force_inline proc(self: ^Device) -> ArgumentBuffersTier { + return msgSend(ArgumentBuffersTier, self, "argumentBuffersSupport") +} +@(objc_type=Device, objc_name="convertSparsePixelRegions") +Device_convertSparsePixelRegions :: #force_inline proc(self: ^Device, pixelRegions, tileRegions: ^Region, tileSize: Size, mode: SparseTextureRegionAlignmentMode, numRegions: NS.UInteger) { + msgSend(nil, self, "convertSparsePixelRegions:toTileRegions:withTileSize:alignmentMode:numRegions:", pixelRegions, tileRegions, tileSize, mode, numRegions) +} +@(objc_type=Device, objc_name="convertSparseTileRegions") +Device_convertSparseTileRegions :: #force_inline proc(self: ^Device, tileRegions, pixelRegions: ^Region, tileSize: Size, numRegions: NS.UInteger) { + msgSend(nil, self, "convertSparseTileRegions:toPixelRegions:withTileSize:numRegions:", tileRegions, pixelRegions, tileSize, numRegions) +} +@(objc_type=Device, objc_name="counterSets") +Device_counterSets :: #force_inline proc(self: ^Device) -> ^NS.Array { + return msgSend(^NS.Array, self, "counterSets") +} +@(objc_type=Device, objc_name="currentAllocatedSize") +Device_currentAllocatedSize :: #force_inline proc(self: ^Device) -> NS.UInteger { + return msgSend(NS.UInteger, self, "currentAllocatedSize") +} +@(objc_type=Device, objc_name="getDefaultSamplePositions") +Device_getDefaultSamplePositions :: #force_inline proc(self: ^Device, positions: []SamplePosition) { + msgSend(nil, self, "getDefaultSamplePositions:count:", raw_data(positions), NS.UInteger(len(positions))) +} +@(objc_type=Device, objc_name="hasUnifiedMemory") +Device_hasUnifiedMemory :: #force_inline proc(self: ^Device) -> BOOL { + return msgSend(BOOL, self, "hasUnifiedMemory") +} +@(objc_type=Device, objc_name="heapBufferSizeAndAlignWithLength") +Device_heapBufferSizeAndAlignWithLength :: #force_inline proc(self: ^Device, length: NS.UInteger, options: ResourceOptions) -> (size, align: NS.UInteger) { + res := msgSend(SizeAndAlign, self, "heapBufferSizeAndAlignWithLength:options:", length, options) + return res.size, res.align +} +@(objc_type=Device, objc_name="heapTextureSizeAndAlignWithDescriptor") +Device_heapTextureSizeAndAlignWithDescriptor :: #force_inline proc(self: ^Device, desc: ^TextureDescriptor) -> (size, align: NS.UInteger) { + res := msgSend(SizeAndAlign, self, "heapTextureSizeAndAlignWithDescriptor:", desc) + return res.size, res.align +} +@(objc_type=Device, objc_name="isDepth24Stencil8PixelFormatSupported") +Device_isDepth24Stencil8PixelFormatSupported :: #force_inline proc(self: ^Device) -> BOOL { + return msgSend(BOOL, self, "isDepth24Stencil8PixelFormatSupported") +} +@(objc_type=Device, objc_name="isHeadless") +Device_isHeadless :: #force_inline proc(self: ^Device) -> BOOL { + return msgSend(BOOL, self, "isHeadless") +} +@(objc_type=Device, objc_name="isLowPower") +Device_isLowPower :: #force_inline proc(self: ^Device) -> BOOL { + return msgSend(BOOL, self, "isLowPower") +} +@(objc_type=Device, objc_name="isRemovable") +Device_isRemovable :: #force_inline proc(self: ^Device) -> BOOL { + return msgSend(BOOL, self, "isRemovable") +} +@(objc_type=Device, objc_name="location") +Device_location :: #force_inline proc(self: ^Device) -> DeviceLocation { + return msgSend(DeviceLocation, self, "location") +} +@(objc_type=Device, objc_name="locationNumber") +Device_locationNumber :: #force_inline proc(self: ^Device) -> NS.UInteger { + return msgSend(NS.UInteger, self, "locationNumber") +} +@(objc_type=Device, objc_name="maxArgumentBufferSamplerCount") +Device_maxArgumentBufferSamplerCount :: #force_inline proc(self: ^Device) -> NS.UInteger { + return msgSend(NS.UInteger, self, "maxArgumentBufferSamplerCount") +} +@(objc_type=Device, objc_name="maxBufferLength") +Device_maxBufferLength :: #force_inline proc(self: ^Device) -> NS.UInteger { + return msgSend(NS.UInteger, self, "maxBufferLength") +} +@(objc_type=Device, objc_name="maxThreadgroupMemoryLength") +Device_maxThreadgroupMemoryLength :: #force_inline proc(self: ^Device) -> NS.UInteger { + return msgSend(NS.UInteger, self, "maxThreadgroupMemoryLength") +} +@(objc_type=Device, objc_name="maxThreadsPerThreadgroup") +Device_maxThreadsPerThreadgroup :: #force_inline proc(self: ^Device) -> Size { + return msgSend(Size, self, "maxThreadsPerThreadgroup") +} +@(objc_type=Device, objc_name="maxTransferRate") +Device_maxTransferRate :: #force_inline proc(self: ^Device) -> u64 { + return msgSend(u64, self, "maxTransferRate") +} +@(objc_type=Device, objc_name="minimumLinearTextureAlignmentForPixelFormat") +Device_minimumLinearTextureAlignmentForPixelFormat :: #force_inline proc(self: ^Device, format: PixelFormat) -> NS.UInteger { + return msgSend(NS.UInteger, self, "minimumLinearTextureAlignmentForPixelFormat:", format) +} +@(objc_type=Device, objc_name="minimumTextureBufferAlignmentForPixelFormat") +Device_minimumTextureBufferAlignmentForPixelFormat :: #force_inline proc(self: ^Device, format: PixelFormat) -> NS.UInteger { + return msgSend(NS.UInteger, self, "minimumTextureBufferAlignmentForPixelFormat:", format) +} +@(objc_type=Device, objc_name="name") +Device_name :: #force_inline proc(self: ^Device) -> ^NS.String { + return msgSend(^NS.String, self, "name") +} +@(objc_type=Device, objc_name="newAccelerationStructureWithDescriptor") +Device_newAccelerationStructureWithDescriptor :: #force_inline proc(self: ^Device, descriptor: ^AccelerationStructureDescriptor) -> ^AccelerationStructure { + return msgSend(^AccelerationStructure, self, "newAccelerationStructureWithDescriptor:", descriptor) +} +@(objc_type=Device, objc_name="newAccelerationStructureWithSize") +Device_newAccelerationStructureWithSize :: #force_inline proc(self: ^Device, size: NS.UInteger) -> ^AccelerationStructure { + return msgSend(^AccelerationStructure, self, "newAccelerationStructureWithSize:", size) +} +@(objc_type=Device, objc_name="newArgumentEncoderWithArguments") +Device_newArgumentEncoderWithArguments :: #force_inline proc(self: ^Device, arguments: ^NS.Array) -> ^ArgumentEncoder { + return msgSend(^ArgumentEncoder, self, "newArgumentEncoderWithArguments:", arguments) +} +@(objc_type=Device, objc_name="newBinaryArchive") +Device_newBinaryArchive :: #force_inline proc(self: ^Device, descriptor: ^BinaryArchiveDescriptor) -> (res: ^BinaryArchive, error: ^NS.Error) { + res = msgSend(^BinaryArchive, self, "newBinaryArchiveWithDescriptor:error:", descriptor, &error) + return +} +@(objc_type=Device, objc_name="newBufferWithBytes") +Device_newBufferWithBytes :: #force_inline proc(self: ^Device, bytes: []byte, options: ResourceOptions) -> ^Buffer { + return msgSend(^Buffer, self, "newBufferWithBytes:length:options:", raw_data(bytes), NS.UInteger(len(bytes)), options) +} +@(objc_type=Device, objc_name="newBufferWithBytesNoCopy") +Device_newBufferWithBytesNoCopy :: #force_inline proc(self: ^Device, bytes: []byte, options: ResourceOptions, deallocator: rawptr) -> ^Buffer { + return msgSend(^Buffer, self, "newBufferWithBytesNoCopy:length:options:deallocator:", raw_data(bytes), NS.UInteger(len(bytes)), options, deallocator) +} + +@(objc_type=Device, objc_name="newBufferWithSlice") +Device_newBufferWithSlice :: #force_inline proc(self: ^Device, slice: $S/[]$E, options: ResourceOptions) -> ^Buffer { + return Device_newBufferWithBytes(self, mem.slice_to_bytes(slice), options) +} +@(objc_type=Device, objc_name="newBufferWithSliceNoCopy") +Device_newBufferWithSliceNoCopy :: #force_inline proc(self: ^Device, slice: $S/[]$E, options: ResourceOptions, deallocator: rawptr) -> ^Buffer { + return Device_newBufferWithBytesNotCopy(self, mem.slice_to_bytes(slice), options, deallocator) +} + +@(objc_type=Device, objc_name="newBuffer") +Device_newBuffer :: #force_inline proc(self: ^Device, length: NS.UInteger, options: ResourceOptions) -> ^Buffer { + return msgSend(^Buffer, self, "newBufferWithLength:options:", length, options) +} +@(objc_type=Device, objc_name="newCommandQueue") +Device_newCommandQueue :: #force_inline proc(self: ^Device) -> ^CommandQueue { + return msgSend(^CommandQueue, self, "newCommandQueue") +} +@(objc_type=Device, objc_name="newCommandQueueWithMaxCommandBufferCount") +Device_newCommandQueueWithMaxCommandBufferCount :: #force_inline proc(self: ^Device, maxCommandBufferCount: NS.UInteger) -> ^CommandQueue { + return msgSend(^CommandQueue, self, "newCommandQueueWithMaxCommandBufferCount:", maxCommandBufferCount) +} +@(objc_type=Device, objc_name="newComputePipelineStateWithDescriptorWithCompletionHandler") +Device_newComputePipelineStateWithDescriptorWithCompletionHandler :: #force_inline proc(self: ^Device, descriptor: ^ComputePipelineDescriptor, options: PipelineOption, completionHandler: NewComputePipelineStateWithReflectionCompletionHandler) -> ^ComputePipelineState { + return msgSend(^ComputePipelineState, self, "newComputePipelineStateWithDescriptor:options:completionHandler:", descriptor, options, completionHandler) +} +@(objc_type=Device, objc_name="newComputePipelineStateWithDescriptorWithReflection") +Device_newComputePipelineStateWithDescriptorWithReflection :: #force_inline proc(self: ^Device, descriptor: ^ComputePipelineDescriptor, options: PipelineOption, reflection: ^AutoreleasedComputePipelineReflection) -> (res: ^ComputePipelineState, error: ^NS.Error) { + res = msgSend(^ComputePipelineState, self, "newComputePipelineStateWithDescriptor:options:reflection:error:", descriptor, options, reflection, &error) + return +} +@(objc_type=Device, objc_name="newComputePipelineStateWithFunctionWithCompletionHandler") +Device_newComputePipelineStateWithFunctionWithCompletionHandler :: #force_inline proc(self: ^Device, computeFunction: ^Function, completionHandler: NewComputePipelineStateCompletionHandler) -> ^ComputePipelineState { + return msgSend(^ComputePipelineState, self, "newComputePipelineStateWithFunction:completionHandler:", computeFunction, completionHandler) +} +@(objc_type=Device, objc_name="newComputePipelineStateWithFunction") +Device_newComputePipelineStateWithFunction :: #force_inline proc(self: ^Device, computeFunction: ^Function) -> (res: ^ComputePipelineState, error: ^NS.Error) { + res = msgSend(^ComputePipelineState, self, "newComputePipelineStateWithFunction:error:", computeFunction, &error) + return +} +@(objc_type=Device, objc_name="newComputePipelineStateWithFunctionWithOptionsAndCompletionHandler") +Device_newComputePipelineStateWithFunctionWithOptionsAndCompletionHandler :: #force_inline proc(self: ^Device, computeFunction: ^Function, options: PipelineOption, completionHandler: NewComputePipelineStateWithReflectionCompletionHandler) -> (res: ^ComputePipelineState) { + return msgSend(^ComputePipelineState, self, "newComputePipelineStateWithFunction:options:completionHandler:", computeFunction, options, completionHandler) +} +@(objc_type=Device, objc_name="newComputePipelineStateWithFunctionWithReflection") +Device_newComputePipelineStateWithFunctionWithReflection :: #force_inline proc(self: ^Device, computeFunction: ^Function, options: PipelineOption, reflection: ^AutoreleasedComputePipelineReflection) -> (res: ^ComputePipelineState, error: ^NS.Error) { + res = msgSend(^ComputePipelineState, self, "newComputePipelineStateWithFunction:options:reflection:error:", computeFunction, options, reflection, &error) + return +} + +@(objc_type=Device, objc_name="newComputePipelineState") +Device_newComputePipelineState :: proc{ + Device_newComputePipelineStateWithDescriptorWithCompletionHandler, + Device_newComputePipelineStateWithDescriptorWithReflection, + Device_newComputePipelineStateWithFunctionWithCompletionHandler, + Device_newComputePipelineStateWithFunction, + Device_newComputePipelineStateWithFunctionWithOptionsAndCompletionHandler, + Device_newComputePipelineStateWithFunctionWithReflection, +} + +@(objc_type=Device, objc_name="newCounterSampleBuffer") +Device_newCounterSampleBuffer :: #force_inline proc(self: ^Device, descriptor: ^CounterSampleBufferDescriptor) -> (counter: ^Counter, error: ^NS.Error) { + counter = msgSend(^Counter, self, "newCounterSampleBufferWithDescriptor:error:", descriptor, &error) + return +} +@(objc_type=Device, objc_name="newDefaultLibrary") +Device_newDefaultLibrary :: #force_inline proc(self: ^Device) -> ^Library { + return msgSend(^Library, self, "newDefaultLibrary") +} +@(objc_type=Device, objc_name="newDefaultLibraryWithBundle") +Device_newDefaultLibraryWithBundle :: #force_inline proc(self: ^Device, bundle: ^NS.Bundle) -> (library: ^Library, error: ^NS.Error) { + library = msgSend(^Library, self, "newDefaultLibraryWithBundle:error:", bundle, &error) + return +} +@(objc_type=Device, objc_name="newDepthStencilState") +Device_newDepthStencilState :: #force_inline proc(self: ^Device, descriptor: ^DepthStencilDescriptor) -> ^DepthStencilState { + return msgSend(^DepthStencilState, self, "newDepthStencilStateWithDescriptor:", descriptor) +} +@(objc_type=Device, objc_name="newDynamicLibrary") +Device_newDynamicLibrary :: #force_inline proc(self: ^Device, library: ^Library) -> (dyn_library: ^DynamicLibrary, error: ^NS.Error) { + dyn_library = msgSend(^DynamicLibrary, self, "newDynamicLibrary:error:", library, &error) + return +} +@(objc_type=Device, objc_name="newDynamicLibraryWithURL") +Device_newDynamicLibraryWithURL :: #force_inline proc(self: ^Device, url: ^NS.URL) -> (dyn_library: ^DynamicLibrary, error: ^NS.Error) { + dyn_library = msgSend(^DynamicLibrary, self, "newDynamicLibraryWithURL:error:", url, &error) + return +} +@(objc_type=Device, objc_name="newEvent") +Device_newEvent :: #force_inline proc(self: ^Device) -> ^Event { + return msgSend(^Event, self, "newEvent") +} +@(objc_type=Device, objc_name="newFence") +Device_newFence :: #force_inline proc(self: ^Device) -> ^Fence { + return msgSend(^Fence, self, "newFence") +} +@(objc_type=Device, objc_name="newHeap") +Device_newHeap :: #force_inline proc(self: ^Device, descriptor: ^HeapDescriptor) -> ^Heap { + return msgSend(^Heap, self, "newHeapWithDescriptor:", descriptor) +} +@(objc_type=Device, objc_name="newIndirectCommandBuffer") +Device_newIndirectCommandBuffer :: #force_inline proc(self: ^Device, descriptor: ^IndirectCommandBufferDescriptor, maxCount: NS.UInteger, options: ResourceOptions) -> ^IndirectCommandBuffer { + return msgSend(^IndirectCommandBuffer, self, "newIndirectCommandBufferWithDescriptor:maxCommandCount:options:", descriptor, maxCount, options) +} + +@(objc_type=Device, objc_name="newLibraryWithData") +Device_newLibraryWithData :: #force_inline proc(self: ^Device, data: dispatch_data_t) -> (library: ^Library, error: ^NS.Error) { + library = msgSend(^Library, self, "newLibraryWithData:error:", data, &error) + return +} +@(objc_type=Device, objc_name="newLibraryWithFile") +Device_newLibraryWithFile :: #force_inline proc(self: ^Device, filepath: ^NS.String) -> (library: ^Library, error: ^NS.Error) { + library = msgSend(^Library, self, "newLibraryWithFile:error:", filepath, &error) + return +} +@(objc_type=Device, objc_name="newLibraryWithSourceWithCompletionHandler") +Device_newLibraryWithSourceWithCompletionHandler :: #force_inline proc(self: ^Device, source: ^NS.String, options: ^CompileOptions, completionHandler: NewLibraryCompletionHandler) -> ^Library { + return msgSend(^Library, self, "newLibraryWithSource:options:completionHandler:", source, options, completionHandler) +} +@(objc_type=Device, objc_name="newLibraryWithSource") +Device_newLibraryWithSource :: #force_inline proc(self: ^Device, source: ^NS.String, options: ^CompileOptions) -> (library: ^Library, error: ^NS.Error) { + library = msgSend(^Library, self, "newLibraryWithSource:options:error:", source, options, &error) + return +} +@(objc_type=Device, objc_name="newLibraryWithURL") +Device_newLibraryWithURL :: #force_inline proc(self: ^Device, url: ^NS.URL) -> (library: ^Library, error: ^NS.Error) { + library = msgSend(^Library, self, "newLibraryWithURL:error:", url, &error) + return +} +@(objc_type=Device, objc_name="newLibrary") +Device_newLibrary :: proc{ + Device_newLibraryWithData, + Device_newLibraryWithFile, + Device_newLibraryWithSourceWithCompletionHandler, + Device_newLibraryWithSource, + Device_newLibraryWithURL, +} + + +@(objc_type=Device, objc_name="newRasterizationRateMap") +Device_newRasterizationRateMap :: #force_inline proc(self: ^Device, descriptor: ^RasterizationRateMapDescriptor) -> ^RasterizationRateMap { + return msgSend(^RasterizationRateMap, self, "newRasterizationRateMapWithDescriptor:", descriptor) +} + +@(objc_type=Device, objc_name="newRenderPipelineStateWithDescriptorWithCompletionHandler") +Device_newRenderPipelineStateWithDescriptorWithCompletionHandler :: #force_inline proc(self: ^Device, descriptor: ^RenderPipelineDescriptor, completionHandler: NewRenderPipelineStateCompletionHandler) -> ^RenderPipelineState { + return msgSend(^RenderPipelineState, self, "newRenderPipelineStateWithDescriptor:completionHandler:", descriptor, completionHandler) +} +@(objc_type=Device, objc_name="newRenderPipelineStateWithDescriptor") +Device_newRenderPipelineStateWithDescriptor :: #force_inline proc(self: ^Device, descriptor: ^RenderPipelineDescriptor) -> (pipeline: ^RenderPipelineState, error: ^NS.Error) { + pipeline = msgSend(^RenderPipelineState, self, "newRenderPipelineStateWithDescriptor:error:", descriptor, &error) + return +} +@(objc_type=Device, objc_name="newRenderPipelineStateWithDescriptorWithOptionsAndCompletionHandler") +Device_newRenderPipelineStateWithDescriptorWithOptionsAndCompletionHandler :: #force_inline proc(self: ^Device, descriptor: ^RenderPipelineDescriptor, options: PipelineOption, completionHandler: NewRenderPipelineStateWithReflectionCompletionHandler) -> ^RenderPipelineState { + return msgSend(^RenderPipelineState, self, "newRenderPipelineStateWithDescriptor:options:completionHandler:", descriptor, options, completionHandler) +} +@(objc_type=Device, objc_name="newRenderPipelineStateWithDescriptorWithReflection") +Device_newRenderPipelineStateWithDescriptorWithReflection :: #force_inline proc(self: ^Device, descriptor: ^RenderPipelineDescriptor, options: PipelineOption, reflection: ^AutoreleasedRenderPipelineReflection) -> (pipeline: ^RenderPipelineState, error: ^NS.Error) { + pipeline = msgSend(^RenderPipelineState, self, "newRenderPipelineStateWithDescriptor:options:reflection:error:", descriptor, options, reflection, &error) + return +} +@(objc_type=Device, objc_name="newRenderPipelineStateWithTileDescriptorWithCompletionHandler") +Device_newRenderPipelineStateWithTileDescriptorWithCompletionHandler :: #force_inline proc(self: ^Device, descriptor: ^TileRenderPipelineDescriptor, options: PipelineOption, completionHandler: NewRenderPipelineStateWithReflectionCompletionHandler) -> ^RenderPipelineState { + return msgSend(^RenderPipelineState, self, "newRenderPipelineStateWithTileDescriptor:options:completionHandler:", descriptor, options, completionHandler) +} +@(objc_type=Device, objc_name="newRenderPipelineStateWithTileDescriptorWithReflection") +Device_newRenderPipelineStateWithTileDescriptorWithReflection :: #force_inline proc(self: ^Device, descriptor: ^TileRenderPipelineDescriptor, options: PipelineOption, reflection: ^AutoreleasedRenderPipelineReflection) -> (pipeline: ^RenderPipelineState, error: ^NS.Error) { + pipeline = msgSend(^RenderPipelineState, self, "newRenderPipelineStateWithTileDescriptor:options:reflection:error:", descriptor, options, reflection, &error) + return +} +@(objc_type=Device, objc_name="newRenderPipelineState") +Device_newRenderPipelineState :: proc{ + Device_newRenderPipelineStateWithDescriptorWithCompletionHandler, + Device_newRenderPipelineStateWithDescriptor, + Device_newRenderPipelineStateWithDescriptorWithOptionsAndCompletionHandler, + Device_newRenderPipelineStateWithDescriptorWithReflection, + Device_newRenderPipelineStateWithTileDescriptorWithCompletionHandler, + Device_newRenderPipelineStateWithTileDescriptorWithReflection, +} + + +@(objc_type=Device, objc_name="newSamplerState") +Device_newSamplerState :: #force_inline proc(self: ^Device, descriptor: ^SamplerDescriptor) -> ^SamplerState { + return msgSend(^SamplerState, self, "newSamplerStateWithDescriptor:", descriptor) +} +@(objc_type=Device, objc_name="newSharedEvent") +Device_newSharedEvent :: #force_inline proc(self: ^Device) -> ^SharedEvent { + return msgSend(^SharedEvent, self, "newSharedEvent") +} +@(objc_type=Device, objc_name="newSharedEventWithHandle") +Device_newSharedEventWithHandle :: #force_inline proc(self: ^Device, sharedEventHandle: ^SharedEventHandle) -> ^SharedEvent { + return msgSend(^SharedEvent, self, "newSharedEventWithHandle:", sharedEventHandle) +} +@(objc_type=Device, objc_name="newSharedTextureWithDescriptor") +Device_newSharedTextureWithDescriptor :: #force_inline proc(self: ^Device, descriptor: ^TextureDescriptor) -> ^SharedEvent { + return msgSend(^SharedEvent, self, "newSharedTextureWithDescriptor:", descriptor) +} +@(objc_type=Device, objc_name="newSharedTextureWithHandle") +Device_newSharedTextureWithHandle :: #force_inline proc(self: ^Device, sharedHandle: ^SharedTextureHandle) -> ^SharedEvent { + return msgSend(^SharedEvent, self, "newSharedTextureWithHandle:", sharedHandle) +} +@(objc_type=Device, objc_name="newSharedTexture") +Device_newSharedTexture :: proc{ + Device_newSharedTextureWithDescriptor, + Device_newSharedTextureWithHandle, +} + +@(objc_type=Device, objc_name="newTextureWithDescriptor") +Device_newTextureWithDescriptor :: #force_inline proc(self: ^Device, desc: ^TextureDescriptor) -> ^Texture { + return msgSend(^Texture, self, "newTextureWithDescriptor:", desc) +} +@(objc_type=Device, objc_name="newTextureWithIOSurface") +Device_newTextureWithIOSurface :: #force_inline proc(self: ^Device, descriptor: ^TextureDescriptor, iosurface: IOSurfaceRef, plane: NS.UInteger) -> ^Texture { + return msgSend(^Texture, self, "newTextureWithDescriptor:iosurface:plane:", descriptor, iosurface, plane) +} +@(objc_type=Device, objc_name="newTexture") +Device_newTexture :: proc{ + Device_newTextureWithDescriptor, + Device_newTextureWithIOSurface, +} + +@(objc_type=Device, objc_name="peerCount") +Device_peerCount :: #force_inline proc(self: ^Device) -> u32 { + return msgSend(u32, self, "peerCount") +} +@(objc_type=Device, objc_name="peerGroupID") +Device_peerGroupID :: #force_inline proc(self: ^Device) -> u64 { + return msgSend(u64, self, "peerGroupID") +} +@(objc_type=Device, objc_name="peerIndex") +Device_peerIndex :: #force_inline proc(self: ^Device) -> u32 { + return msgSend(u32, self, "peerIndex") +} +@(objc_type=Device, objc_name="readWriteTextureSupport") +Device_readWriteTextureSupport :: #force_inline proc(self: ^Device) -> ReadWriteTextureTier { + return msgSend(ReadWriteTextureTier, self, "readWriteTextureSupport") +} +@(objc_type=Device, objc_name="recommendedMaxWorkingSetSize") +Device_recommendedMaxWorkingSetSize :: #force_inline proc(self: ^Device) -> u64 { + return msgSend(u64, self, "recommendedMaxWorkingSetSize") +} +@(objc_type=Device, objc_name="registryID") +Device_registryID :: #force_inline proc(self: ^Device) -> u64 { + return msgSend(u64, self, "registryID") +} +@(objc_type=Device, objc_name="sampleTimestamps") +Device_sampleTimestamps :: #force_inline proc(self: ^Device, cpuTimestamp: ^Timestamp, gpuTimestamp: ^Timestamp) { + msgSend(nil, self, "sampleTimestamps:gpuTimestamp:", cpuTimestamp, gpuTimestamp) +} +@(objc_type=Device, objc_name="sparseTileSizeInBytes") +Device_sparseTileSizeInBytes :: #force_inline proc(self: ^Device) -> NS.UInteger { + return msgSend(NS.UInteger, self, "sparseTileSizeInBytes") +} +@(objc_type=Device, objc_name="sparseTileSizeWithTextureType") +Device_sparseTileSizeWithTextureType :: #force_inline proc(self: ^Device, textureType: TextureType, pixelFormat: PixelFormat, sampleCount: NS.UInteger) -> Size { + return msgSend(Size, self, "sparseTileSizeWithTextureType:pixelFormat:sampleCount:", textureType, pixelFormat, sampleCount) +} +@(objc_type=Device, objc_name="supports32BitFloatFiltering") +Device_supports32BitFloatFiltering :: #force_inline proc(self: ^Device) -> BOOL { + return msgSend(BOOL, self, "supports32BitFloatFiltering") +} +@(objc_type=Device, objc_name="supports32BitMSAA") +Device_supports32BitMSAA :: #force_inline proc(self: ^Device) -> BOOL { + return msgSend(BOOL, self, "supports32BitMSAA") +} +@(objc_type=Device, objc_name="supportsBCTextureCompression") +Device_supportsBCTextureCompression :: #force_inline proc(self: ^Device) -> BOOL { + return msgSend(BOOL, self, "supportsBCTextureCompression") +} +@(objc_type=Device, objc_name="supportsCounterSampling") +Device_supportsCounterSampling :: #force_inline proc(self: ^Device, samplingPoint: CounterSamplingPoint) -> BOOL { + return msgSend(BOOL, self, "supportsCounterSampling:", samplingPoint) +} +@(objc_type=Device, objc_name="supportsDynamicLibraries") +Device_supportsDynamicLibraries :: #force_inline proc(self: ^Device) -> BOOL { + return msgSend(BOOL, self, "supportsDynamicLibraries") +} +@(objc_type=Device, objc_name="supportsFamily") +Device_supportsFamily :: #force_inline proc(self: ^Device, gpuFamily: GPUFamily) -> BOOL { + return msgSend(BOOL, self, "supportsFamily:", gpuFamily) +} +@(objc_type=Device, objc_name="supportsFeatureSet") +Device_supportsFeatureSet :: #force_inline proc(self: ^Device, featureSet: FeatureSet) -> BOOL { + return msgSend(BOOL, self, "supportsFeatureSet:", featureSet) +} +@(objc_type=Device, objc_name="supportsFunctionPointers") +Device_supportsFunctionPointers :: #force_inline proc(self: ^Device) -> BOOL { + return msgSend(BOOL, self, "supportsFunctionPointers") +} +@(objc_type=Device, objc_name="supportsPullModelInterpolation") +Device_supportsPullModelInterpolation :: #force_inline proc(self: ^Device) -> BOOL { + return msgSend(BOOL, self, "supportsPullModelInterpolation") +} +@(objc_type=Device, objc_name="supportsQueryTextureLOD") +Device_supportsQueryTextureLOD :: #force_inline proc(self: ^Device) -> BOOL { + return msgSend(BOOL, self, "supportsQueryTextureLOD") +} +@(objc_type=Device, objc_name="supportsRasterizationRateMapWithLayerCount") +Device_supportsRasterizationRateMapWithLayerCount :: #force_inline proc(self: ^Device, layerCount: NS.UInteger) -> BOOL { + return msgSend(BOOL, self, "supportsRasterizationRateMapWithLayerCount:", layerCount) +} +@(objc_type=Device, objc_name="supportsRaytracing") +Device_supportsRaytracing :: #force_inline proc(self: ^Device) -> BOOL { + return msgSend(BOOL, self, "supportsRaytracing") +} +@(objc_type=Device, objc_name="supportsShaderBarycentricCoordinates") +Device_supportsShaderBarycentricCoordinates :: #force_inline proc(self: ^Device) -> BOOL { + return msgSend(BOOL, self, "supportsShaderBarycentricCoordinates") +} +@(objc_type=Device, objc_name="supportsTextureSampleCount") +Device_supportsTextureSampleCount :: #force_inline proc(self: ^Device, sampleCount: NS.UInteger) -> BOOL { + return msgSend(BOOL, self, "supportsTextureSampleCount:", sampleCount) +} +@(objc_type=Device, objc_name="supportsVertexAmplificationCount") +Device_supportsVertexAmplificationCount :: #force_inline proc(self: ^Device, count: NS.UInteger) -> BOOL { + return msgSend(BOOL, self, "supportsVertexAmplificationCount:", count) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + Drawable +Class Methods: +Methods: + addPresentedHandler + drawableID + present + presentAfterMinimumDuration + presentAtTime + presentedTime +*/ +@(objc_class="MTLDrawable") +Drawable :: struct { using _: NS.Object } + +@(objc_type=Drawable, objc_name="addPresentedHandler") +Drawable_addPresentedHandler :: #force_inline proc(self: ^Drawable, block: DrawablePresentedHandler) { + msgSend(nil, self, "addPresentedHandler:", block) +} +@(objc_type=Drawable, objc_name="drawableID") +Drawable_drawableID :: #force_inline proc(self: ^Drawable) -> NS.UInteger { + return msgSend(NS.UInteger, self, "drawableID") +} +@(objc_type=Drawable, objc_name="present") +Drawable_present :: #force_inline proc(self: ^Drawable) { + msgSend(nil, self, "present") +} +@(objc_type=Drawable, objc_name="presentAfterMinimumDuration") +Drawable_presentAfterMinimumDuration :: #force_inline proc(self: ^Drawable, duration: CFTimeInterval) { + msgSend(nil, self, "presentAfterMinimumDuration:", duration) +} +@(objc_type=Drawable, objc_name="presentAtTime") +Drawable_presentAtTime :: #force_inline proc(self: ^Drawable, presentationTime: CFTimeInterval) { + msgSend(nil, self, "presentAtTime:", presentationTime) +} +@(objc_type=Drawable, objc_name="presentedTime") +Drawable_presentedTime :: #force_inline proc(self: ^Drawable) -> CFTimeInterval { + return msgSend(CFTimeInterval, self, "presentedTime") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + DynamicLibrary +Class Methods: +Methods: + device + installName + label + serializeToURL + setLabel +*/ +@(objc_class="MTLDynamicLibrary") +DynamicLibrary :: struct { using _: NS.Object } + +@(objc_type=DynamicLibrary, objc_name="device") +DynamicLibrary_device :: #force_inline proc(self: ^DynamicLibrary) -> ^Device { + return msgSend(^Device, self, "device") +} +@(objc_type=DynamicLibrary, objc_name="installName") +DynamicLibrary_installName :: #force_inline proc(self: ^DynamicLibrary) -> ^NS.String { + return msgSend(^NS.String, self, "installName") +} +@(objc_type=DynamicLibrary, objc_name="label") +DynamicLibrary_label :: #force_inline proc(self: ^DynamicLibrary) -> ^NS.String { + return msgSend(^NS.String, self, "label") +} +@(objc_type=DynamicLibrary, objc_name="serializeToURL") +DynamicLibrary_serializeToURL :: #force_inline proc(self: ^DynamicLibrary, url: ^NS.URL) -> (ok: BOOL, error: ^NS.Error) { + ok = msgSend(BOOL, self, "serializeToURL:error:", url, &error) + return +} +@(objc_type=DynamicLibrary, objc_name="setLabel") +DynamicLibrary_setLabel :: #force_inline proc(self: ^DynamicLibrary, label: ^NS.String) { + msgSend(nil, self, "setLabel:", label) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + Event +Class Methods: +Methods: + device + label + setLabel +*/ +@(objc_class="MTLEvent") +Event :: struct { using _: NS.Object } + +@(objc_type=Event, objc_name="device") +Event_device :: #force_inline proc(self: ^Event) -> ^Device { + return msgSend(^Device, self, "device") +} +@(objc_type=Event, objc_name="label") +Event_label :: #force_inline proc(self: ^Event) -> ^NS.String { + return msgSend(^NS.String, self, "label") +} +@(objc_type=Event, objc_name="setLabel") +Event_setLabel :: #force_inline proc(self: ^Event, label: ^NS.String) { + msgSend(nil, self, "setLabel:", label) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + Fence +Class Methods: +Methods: + device + label + setLabel +*/ +@(objc_class="MTLFence") +Fence :: struct { using _: NS.Object } + +@(objc_type=Fence, objc_name="device") +Fence_device :: #force_inline proc(self: ^Fence) -> ^Device { + return msgSend(^Device, self, "device") +} +@(objc_type=Fence, objc_name="label") +Fence_label :: #force_inline proc(self: ^Fence) -> ^NS.String { + return msgSend(^NS.String, self, "label") +} +@(objc_type=Fence, objc_name="setLabel") +Fence_setLabel :: #force_inline proc(self: ^Fence, label: ^NS.String) { + msgSend(nil, self, "setLabel:", label) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + Function +Class Methods: +Methods: + device + functionConstantsDictionary + functionType + label + name + newArgumentEncoderWithBufferIndex + newArgumentEncoderWithBufferIndex + options + patchControlPointCount + patchType + setLabel + stageInputAttributes + vertexAttributes +*/ +@(objc_class="MTLFunction") +Function :: struct { using _: NS.Object } + +@(objc_type=Function, objc_name="device") +Function_device :: #force_inline proc(self: ^Function) -> ^Device { + return msgSend(^Device, self, "device") +} +@(objc_type=Function, objc_name="functionConstantsDictionary") +Function_functionConstantsDictionary :: #force_inline proc(self: ^Function) -> ^NS.Dictionary { + return msgSend(^NS.Dictionary, self, "functionConstantsDictionary") +} +@(objc_type=Function, objc_name="functionType") +Function_functionType :: #force_inline proc(self: ^Function) -> FunctionType { + return msgSend(FunctionType, self, "functionType") +} +@(objc_type=Function, objc_name="label") +Function_label :: #force_inline proc(self: ^Function) -> ^NS.String { + return msgSend(^NS.String, self, "label") +} +@(objc_type=Function, objc_name="name") +Function_name :: #force_inline proc(self: ^Function) -> ^NS.String { + return msgSend(^NS.String, self, "name") +} +@(objc_type=Function, objc_name="newArgumentEncoder") +Function_newArgumentEncoder :: #force_inline proc(self: ^Function, bufferIndex: NS.UInteger) -> ^ArgumentEncoder { + return msgSend(^ArgumentEncoder, self, "newArgumentEncoderWithBufferIndex:", bufferIndex) +} +@(objc_type=Function, objc_name="newArgumentEncoderWithReflection") +Function_newArgumentEncoderWithReflection :: #force_inline proc(self: ^Function, bufferIndex: NS.UInteger, reflection: ^AutoreleasedArgument) -> ^ArgumentEncoder { + return msgSend(^ArgumentEncoder, self, "newArgumentEncoderWithBufferIndex:reflection:", bufferIndex, reflection) +} +@(objc_type=Function, objc_name="options") +Function_options :: #force_inline proc(self: ^Function) -> FunctionOptions { + return msgSend(FunctionOptions, self, "options") +} +@(objc_type=Function, objc_name="patchControlPointCount") +Function_patchControlPointCount :: #force_inline proc(self: ^Function) -> NS.UInteger { + return msgSend(NS.UInteger, self, "patchControlPointCount") +} +@(objc_type=Function, objc_name="patchType") +Function_patchType :: #force_inline proc(self: ^Function) -> PatchType { + return msgSend(PatchType, self, "patchType") +} +@(objc_type=Function, objc_name="setLabel") +Function_setLabel :: #force_inline proc(self: ^Function, label: ^NS.String) { + msgSend(nil, self, "setLabel:", label) +} +@(objc_type=Function, objc_name="stageInputAttributes") +Function_stageInputAttributes :: #force_inline proc(self: ^Function) -> ^NS.Array { + return msgSend(^NS.Array, self, "stageInputAttributes") +} +@(objc_type=Function, objc_name="vertexAttributes") +Function_vertexAttributes :: #force_inline proc(self: ^Function) -> ^NS.Array { + return msgSend(^NS.Array, self, "vertexAttributes") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + FunctionHandle +Class Methods: +Methods: + device + functionType + name +*/ +@(objc_class="MTLFunctionHandle") +FunctionHandle :: struct { using _: NS.Object } + +@(objc_type=FunctionHandle, objc_name="device") +FunctionHandle_device :: #force_inline proc(self: ^FunctionHandle) -> ^Device { + return msgSend(^Device, self, "device") +} +@(objc_type=FunctionHandle, objc_name="functionType") +FunctionHandle_functionType :: #force_inline proc(self: ^FunctionHandle) -> FunctionType { + return msgSend(FunctionType, self, "functionType") +} +@(objc_type=FunctionHandle, objc_name="name") +FunctionHandle_name :: #force_inline proc(self: ^FunctionHandle) -> ^NS.String { + return msgSend(^NS.String, self, "name") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + LogContainer +*/ + +@(objc_class="MTLLogContainer") +LogContainer :: struct { using _: NS.FastEnumeration } + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + FunctionLog +Class Methods: +Methods: + debugLocation + encoderLabel + function + type +*/ +@(objc_class="MTLFunctionLog") +FunctionLog :: struct { using _: NS.Object } + +@(objc_type=FunctionLog, objc_name="debugLocation") +FunctionLog_debugLocation :: #force_inline proc(self: ^FunctionLog) -> ^FunctionLog { + return msgSend(^FunctionLog, self, "debugLocation") +} +@(objc_type=FunctionLog, objc_name="encoderLabel") +FunctionLog_encoderLabel :: #force_inline proc(self: ^FunctionLog) -> ^NS.String { + return msgSend(^NS.String, self, "encoderLabel") +} +@(objc_type=FunctionLog, objc_name="function") +FunctionLog_function :: #force_inline proc(self: ^FunctionLog) -> ^FunctionLog { + return msgSend(^FunctionLog, self, "function") +} +@(objc_type=FunctionLog, objc_name="type") +FunctionLog_type :: #force_inline proc(self: ^FunctionLog) -> FunctionLogType { + return msgSend(FunctionLogType, self, "type") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + FunctionLogDebugLocation +Class Methods: +Methods: + URL + column + functionName + line +*/ +@(objc_class="MTLFunctionLogDebugLocation") +FunctionLogDebugLocation :: struct { using _: NS.Object } + +@(objc_type=FunctionLogDebugLocation, objc_name="URL") +FunctionLogDebugLocation_URL :: #force_inline proc(self: ^FunctionLogDebugLocation) -> ^NS.URL { + return msgSend(^NS.URL, self, "URL") +} +@(objc_type=FunctionLogDebugLocation, objc_name="column") +FunctionLogDebugLocation_column :: #force_inline proc(self: ^FunctionLogDebugLocation) -> NS.UInteger { + return msgSend(NS.UInteger, self, "column") +} +@(objc_type=FunctionLogDebugLocation, objc_name="functionName") +FunctionLogDebugLocation_functionName :: #force_inline proc(self: ^FunctionLogDebugLocation) -> ^NS.String { + return msgSend(^NS.String, self, "functionName") +} +@(objc_type=FunctionLogDebugLocation, objc_name="line") +FunctionLogDebugLocation_line :: #force_inline proc(self: ^FunctionLogDebugLocation) -> NS.UInteger { + return msgSend(NS.UInteger, self, "line") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + Heap +Class Methods: +Methods: + cpuCacheMode + currentAllocatedSize + device + hazardTrackingMode + label + maxAvailableSizeWithAlignment + newBufferWithLength + newBufferWithLength + newTextureWithDescriptor + newTextureWithDescriptor + resourceOptions + setLabel + setPurgeableState + size + storageMode + type + usedSize +*/ +@(objc_class="MTLHeap") +Heap :: struct { using _: NS.Object } + +@(objc_type=Heap, objc_name="cpuCacheMode") +Heap_cpuCacheMode :: #force_inline proc(self: ^Heap) -> CPUCacheMode { + return msgSend(CPUCacheMode, self, "cpuCacheMode") +} +@(objc_type=Heap, objc_name="currentAllocatedSize") +Heap_currentAllocatedSize :: #force_inline proc(self: ^Heap) -> NS.UInteger { + return msgSend(NS.UInteger, self, "currentAllocatedSize") +} +@(objc_type=Heap, objc_name="device") +Heap_device :: #force_inline proc(self: ^Heap) -> ^Device { + return msgSend(^Device, self, "device") +} +@(objc_type=Heap, objc_name="hazardTrackingMode") +Heap_hazardTrackingMode :: #force_inline proc(self: ^Heap) -> HazardTrackingMode { + return msgSend(HazardTrackingMode, self, "hazardTrackingMode") +} +@(objc_type=Heap, objc_name="label") +Heap_label :: #force_inline proc(self: ^Heap) -> ^NS.String { + return msgSend(^NS.String, self, "label") +} +@(objc_type=Heap, objc_name="maxAvailableSizeWithAlignment") +Heap_maxAvailableSizeWithAlignment :: #force_inline proc(self: ^Heap, alignment: NS.UInteger) -> NS.UInteger { + return msgSend(NS.UInteger, self, "maxAvailableSizeWithAlignment:", alignment) +} +@(objc_type=Heap, objc_name="newBufferWithLength") +Heap_newBufferWithLength :: #force_inline proc(self: ^Heap, length: NS.UInteger, options: ResourceOptions) -> ^Buffer { + return msgSend(^Buffer, self, "newBufferWithLength:options:", length, options) +} +@(objc_type=Heap, objc_name="newBufferWithOptions") +Heap_newBufferWithOptions :: #force_inline proc(self: ^Heap, length: NS.UInteger, options: ResourceOptions, offset: NS.UInteger) -> ^Buffer { + return msgSend(^Buffer, self, "newBufferWithLength:options:offset:", length, options, offset) +} +@(objc_type=Heap, objc_name="newBuffer") +Heap_newBuffer :: proc{ + Heap_newBufferWithLength, + Heap_newBufferWithOptions, +} + +@(objc_type=Heap, objc_name="newTextureWithDescriptor") +Heap_newTextureWithDescriptor :: #force_inline proc(self: ^Heap, desc: ^TextureDescriptor) -> ^Texture { + return msgSend(^Texture, self, "newTextureWithDescriptor:", desc) +} +@(objc_type=Heap, objc_name="newTextureWithDescriptorAndOffset") +Heap_newTextureWithDescriptorAndOffset :: #force_inline proc(self: ^Heap, descriptor: ^TextureDescriptor, offset: NS.UInteger) -> ^Texture { + return msgSend(^Texture, self, "newTextureWithDescriptor:offset:", descriptor, offset) +} +@(objc_type=Heap, objc_name="newTexture") +Heap_newTexture :: proc{ + Heap_newTextureWithDescriptor, + Heap_newTextureWithDescriptorAndOffset, +} + +@(objc_type=Heap, objc_name="resourceOptions") +Heap_resourceOptions :: #force_inline proc(self: ^Heap) -> ResourceOptions { + return msgSend(ResourceOptions, self, "resourceOptions") +} +@(objc_type=Heap, objc_name="setLabel") +Heap_setLabel :: #force_inline proc(self: ^Heap, label: ^NS.String) { + msgSend(nil, self, "setLabel:", label) +} +@(objc_type=Heap, objc_name="setPurgeableState") +Heap_setPurgeableState :: #force_inline proc(self: ^Heap, state: PurgeableState) -> PurgeableState { + return msgSend(PurgeableState, self, "setPurgeableState:", state) +} +@(objc_type=Heap, objc_name="size") +Heap_size :: #force_inline proc(self: ^Heap) -> NS.UInteger { + return msgSend(NS.UInteger, self, "size") +} +@(objc_type=Heap, objc_name="storageMode") +Heap_storageMode :: #force_inline proc(self: ^Heap) -> StorageMode { + return msgSend(StorageMode, self, "storageMode") +} +@(objc_type=Heap, objc_name="type") +Heap_type :: #force_inline proc(self: ^Heap) -> FunctionLogType { + return msgSend(FunctionLogType, self, "type") +} +@(objc_type=Heap, objc_name="usedSize") +Heap_usedSize :: #force_inline proc(self: ^Heap) -> NS.UInteger { + return msgSend(NS.UInteger, self, "usedSize") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + IndirectCommandBuffer +Class Methods: +Methods: + indirectComputeCommandAtIndex + indirectRenderCommandAtIndex + resetWithRange + size +*/ +@(objc_class="MTLIndirectCommandBuffer") +IndirectCommandBuffer :: struct { using _: Resource } + +@(objc_type=IndirectCommandBuffer, objc_name="indirectComputeCommand") +IndirectCommandBuffer_indirectComputeCommand :: #force_inline proc(self: ^IndirectCommandBuffer, commandIndex: NS.UInteger) -> ^IndirectComputeCommand { + return msgSend(^IndirectComputeCommand, self, "indirectComputeCommandAtIndex:", commandIndex) +} +@(objc_type=IndirectCommandBuffer, objc_name="indirectRenderCommand") +IndirectCommandBuffer_indirectRenderCommand :: #force_inline proc(self: ^IndirectCommandBuffer, commandIndex: NS.UInteger) -> ^IndirectRenderCommand { + return msgSend(^IndirectRenderCommand, self, "indirectRenderCommandAtIndex:", commandIndex) +} +@(objc_type=IndirectCommandBuffer, objc_name="resetWithRange") +IndirectCommandBuffer_resetWithRange :: #force_inline proc(self: ^IndirectCommandBuffer, range: NS.Range) { + msgSend(nil, self, "resetWithRange:", range) +} +@(objc_type=IndirectCommandBuffer, objc_name="size") +IndirectCommandBuffer_size :: #force_inline proc(self: ^IndirectCommandBuffer) -> NS.UInteger { + return msgSend(NS.UInteger, self, "size") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + IndirectComputeCommand +Class Methods: +Methods: + clearBarrier + concurrentDispatchThreadgroups + concurrentDispatchThreads + reset + setBarrier + setComputePipelineState + setImageblockWidth + setKernelBuffer + setStageInRegion + setThreadgroupMemoryLength +*/ +@(objc_class="MTLIndirectComputeCommand") +IndirectComputeCommand :: struct { using _: NS.Object } + +@(objc_type=IndirectComputeCommand, objc_name="clearBarrier") +IndirectComputeCommand_clearBarrier :: #force_inline proc(self: ^IndirectComputeCommand) { + msgSend(nil, self, "clearBarrier") +} +@(objc_type=IndirectComputeCommand, objc_name="concurrentDispatchThreadgroups") +IndirectComputeCommand_concurrentDispatchThreadgroups :: #force_inline proc(self: ^IndirectComputeCommand, threadgroupsPerGrid: Size, threadsPerThreadgroup: Size) { + msgSend(nil, self, "concurrentDispatchThreadgroups:threadsPerThreadgroup:", threadgroupsPerGrid, threadsPerThreadgroup) +} +@(objc_type=IndirectComputeCommand, objc_name="concurrentDispatchThreads") +IndirectComputeCommand_concurrentDispatchThreads :: #force_inline proc(self: ^IndirectComputeCommand, threadsPerGrid: Size, threadsPerThreadgroup: Size) { + msgSend(nil, self, "concurrentDispatchThreads:threadsPerThreadgroup:", threadsPerGrid, threadsPerThreadgroup) +} +@(objc_type=IndirectComputeCommand, objc_name="reset") +IndirectComputeCommand_reset :: #force_inline proc(self: ^IndirectComputeCommand) { + msgSend(nil, self, "reset") +} +@(objc_type=IndirectComputeCommand, objc_name="setBarrier") +IndirectComputeCommand_setBarrier :: #force_inline proc(self: ^IndirectComputeCommand) { + msgSend(nil, self, "setBarrier") +} +@(objc_type=IndirectComputeCommand, objc_name="setComputePipelineState") +IndirectComputeCommand_setComputePipelineState :: #force_inline proc(self: ^IndirectComputeCommand, pipelineState: ^ComputePipelineState) { + msgSend(nil, self, "setComputePipelineState:", pipelineState) +} +@(objc_type=IndirectComputeCommand, objc_name="setImageblockWidth") +IndirectComputeCommand_setImageblockWidth :: #force_inline proc(self: ^IndirectComputeCommand, width: NS.UInteger, height: NS.UInteger) { + msgSend(nil, self, "setImageblockWidth:height:", width, height) +} +@(objc_type=IndirectComputeCommand, objc_name="setKernelBuffer") +IndirectComputeCommand_setKernelBuffer :: #force_inline proc(self: ^IndirectComputeCommand, buffer: ^Buffer, offset: NS.UInteger, index: NS.UInteger) { + msgSend(nil, self, "setKernelBuffer:offset:atIndex:", buffer, offset, index) +} +@(objc_type=IndirectComputeCommand, objc_name="setStageInRegion") +IndirectComputeCommand_setStageInRegion :: #force_inline proc(self: ^IndirectComputeCommand, region: Region) { + msgSend(nil, self, "setStageInRegion:", region) +} +@(objc_type=IndirectComputeCommand, objc_name="setThreadgroupMemoryLength") +IndirectComputeCommand_setThreadgroupMemoryLength :: #force_inline proc(self: ^IndirectComputeCommand, length: NS.UInteger, index: NS.UInteger) { + msgSend(nil, self, "setThreadgroupMemoryLength:atIndex:", length, index) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + IndirectRenderCommand +Class Methods: +Methods: + drawIndexedPatches + drawIndexedPrimitives + drawPatches + drawPrimitives + reset + setFragmentBuffer + setRenderPipelineState + setVertexBuffer +*/ +@(objc_class="MTLIndirectRenderCommand") +IndirectRenderCommand :: struct { using _: NS.Object } + +@(objc_type=IndirectRenderCommand, objc_name="drawIndexedPatches") +IndirectRenderCommand_drawIndexedPatches :: #force_inline proc(self: ^IndirectRenderCommand, numberOfPatchControlPoints: NS.UInteger, patchStart, patchCount: NS.UInteger, patchIndexBuffer: ^Buffer, patchIndexBufferOffset: NS.UInteger, controlPointIndexBuffer: ^Buffer, controlPointIndexBufferOffset: NS.UInteger, instanceCount: NS.UInteger, baseInstance: NS.UInteger, buffer: ^Buffer, offset: NS.UInteger, instanceStride: NS.UInteger) { + msgSend(nil, self, "drawIndexedPatches:patchStart:patchCount:patchIndexBuffer:patchIndexBufferOffset:controlPointIndexBuffer:controlPointIndexBufferOffset:instanceCount:baseInstance:tessellationFactorBuffer:tessellationFactorBufferOffset:tessellationFactorBufferInstanceStride:", numberOfPatchControlPoints, patchStart, patchCount, patchIndexBuffer, patchIndexBufferOffset, controlPointIndexBuffer, controlPointIndexBufferOffset, instanceCount, baseInstance, buffer, offset, instanceStride) +} +@(objc_type=IndirectRenderCommand, objc_name="drawIndexedPrimitives") +IndirectRenderCommand_drawIndexedPrimitives :: #force_inline proc(self: ^IndirectRenderCommand, primitiveType: PrimitiveType, indexCount: NS.UInteger, indexType: IndexType, indexBuffer: ^Buffer, indexBufferOffset: NS.UInteger, instanceCount: NS.UInteger, baseVertex: NS.Integer, baseInstance: NS.UInteger) { + msgSend(nil, self, "drawIndexedPrimitives:indexCount:indexType:indexBuffer:indexBufferOffset:instanceCount:baseVertex:baseInstance:", primitiveType, indexCount, indexType, indexBuffer, indexBufferOffset, instanceCount, baseVertex, baseInstance) +} +@(objc_type=IndirectRenderCommand, objc_name="drawPatches") +IndirectRenderCommand_drawPatches :: #force_inline proc(self: ^IndirectRenderCommand, numberOfPatchControlPoints: NS.UInteger, patchStart, patchCount: NS.UInteger, patchIndexBuffer: ^Buffer, patchIndexBufferOffset: NS.UInteger, instanceCount: NS.UInteger, baseInstance: NS.UInteger, buffer: ^Buffer, offset: NS.UInteger, instanceStride: NS.UInteger) { + msgSend(nil, self, "drawPatches:patchStart:patchCount:patchIndexBuffer:patchIndexBufferOffset:instanceCount:baseInstance:tessellationFactorBuffer:tessellationFactorBufferOffset:tessellationFactorBufferInstanceStride:", numberOfPatchControlPoints, patchStart, patchCount, patchIndexBuffer, patchIndexBufferOffset, instanceCount, baseInstance, buffer, offset, instanceStride) +} +@(objc_type=IndirectRenderCommand, objc_name="drawPrimitives") +IndirectRenderCommand_drawPrimitives :: #force_inline proc(self: ^IndirectRenderCommand, primitiveType: PrimitiveType, vertexStart: NS.UInteger, vertexCount: NS.UInteger, instanceCount: NS.UInteger, baseInstance: NS.UInteger = 0) { + msgSend(nil, self, "drawPrimitives:vertexStart:vertexCount:instanceCount:baseInstance:", primitiveType, vertexStart, vertexCount, instanceCount, baseInstance) +} +@(objc_type=IndirectRenderCommand, objc_name="reset") +IndirectRenderCommand_reset :: #force_inline proc(self: ^IndirectRenderCommand) { + msgSend(nil, self, "reset") +} +@(objc_type=IndirectRenderCommand, objc_name="setFragmentBuffer") +IndirectRenderCommand_setFragmentBuffer :: #force_inline proc(self: ^IndirectRenderCommand, buffer: ^Buffer, offset: NS.UInteger, index: NS.UInteger) { + msgSend(nil, self, "setFragmentBuffer:offset:atIndex:", buffer, offset, index) +} +@(objc_type=IndirectRenderCommand, objc_name="setRenderPipelineState") +IndirectRenderCommand_setRenderPipelineState :: #force_inline proc(self: ^IndirectRenderCommand, pipelineState: ^RenderPipelineState) { + msgSend(nil, self, "setRenderPipelineState:", pipelineState) +} +@(objc_type=IndirectRenderCommand, objc_name="setVertexBuffer") +IndirectRenderCommand_setVertexBuffer :: #force_inline proc(self: ^IndirectRenderCommand, buffer: ^Buffer, offset: NS.UInteger, index: NS.UInteger) { + msgSend(nil, self, "setVertexBuffer:offset:atIndex:", buffer, offset, index) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + IntersectionFunctionTable +Class Methods: +Methods: + setBuffer + setBuffers + setFunction + setFunctions + setOpaqueTriangleIntersectionFunctionWithSignature + setOpaqueTriangleIntersectionFunctionWithSignature + setVisibleFunctionTable + setVisibleFunctionTables +*/ +@(objc_class="MTLIntersectionFunctionTable") +IntersectionFunctionTable :: struct { using _: Resource } + +@(objc_type=IntersectionFunctionTable, objc_name="setBuffer") +IntersectionFunctionTable_setBuffer :: #force_inline proc(self: ^IntersectionFunctionTable, buffer: ^Buffer, offset: NS.UInteger, index: NS.UInteger) { + msgSend(nil, self, "setBuffer:offset:atIndex:", buffer, offset, index) +} +@(objc_type=IntersectionFunctionTable, objc_name="setBuffers") +IntersectionFunctionTable_setBuffers :: #force_inline proc(self: ^IntersectionFunctionTable, buffers: []^Buffer, offsets: []NS.UInteger, range: NS.Range) { + msgSend(nil, self, "setBuffers:offsets:withRange:", raw_data(buffers), raw_data(offsets), range) +} +@(objc_type=IntersectionFunctionTable, objc_name="setFunction") +IntersectionFunctionTable_setFunction :: #force_inline proc(self: ^IntersectionFunctionTable, function: ^FunctionHandle, index: NS.UInteger) { + msgSend(nil, self, "setFunction:atIndex:", function, index) +} +@(objc_type=IntersectionFunctionTable, objc_name="setFunctions") +IntersectionFunctionTable_setFunctions :: #force_inline proc(self: ^IntersectionFunctionTable, functions: []^FunctionHandle, range: NS.Range) { + msgSend(nil, self, "setFunctions:withRange:", raw_data(functions), range) +} +@(objc_type=IntersectionFunctionTable, objc_name="setOpaqueTriangleIntersectionFunctionWithSignature") +IntersectionFunctionTable_setOpaqueTriangleIntersectionFunctionWithSignature :: #force_inline proc(self: ^IntersectionFunctionTable, signature: IntersectionFunctionSignature, index: NS.UInteger) { + msgSend(nil, self, "setOpaqueTriangleIntersectionFunctionWithSignature:atIndex:", signature, index) +} +@(objc_type=IntersectionFunctionTable, objc_name="setOpaqueTriangleIntersectionFunctionWithSignatureWithRange") +IntersectionFunctionTable_setOpaqueTriangleIntersectionFunctionWithSignatureWithRange :: #force_inline proc(self: ^IntersectionFunctionTable, signature: IntersectionFunctionSignature, range: NS.Range) { + msgSend(nil, self, "setOpaqueTriangleIntersectionFunctionWithSignature:withRange:", signature, range) +} +@(objc_type=IntersectionFunctionTable, objc_name="setVisibleFunctionTable") +IntersectionFunctionTable_setVisibleFunctionTable :: #force_inline proc(self: ^IntersectionFunctionTable, visibleFunctionTable: ^VisibleFunctionTable, bufferIndex: NS.UInteger) { + msgSend(nil, self, "setVisibleFunctionTable:atBufferIndex:", visibleFunctionTable, bufferIndex) +} +@(objc_type=IntersectionFunctionTable, objc_name="setVisibleFunctionTables") +IntersectionFunctionTable_setVisibleFunctionTables :: #force_inline proc(self: ^IntersectionFunctionTable, visibleFunctionTables: []^VisibleFunctionTable, range: NS.Range) { + msgSend(nil, self, "setVisibleFunctionTables:withBufferRange:", raw_data(visibleFunctionTables), range) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + Library +Class Methods: +Methods: + device + functionNames + installName + label + newFunctionWithDescriptor + newFunctionWithDescriptor + newFunctionWithName + newFunctionWithName + newFunctionWithName + newIntersectionFunctionWithDescriptor + newIntersectionFunctionWithDescriptor + setLabel + type +*/ +@(objc_class="MTLLibrary") +Library :: struct { using _: NS.Object } + +@(objc_type=Library, objc_name="device") +Library_device :: #force_inline proc(self: ^Library) -> ^Device { + return msgSend(^Device, self, "device") +} +@(objc_type=Library, objc_name="functionNames") +Library_functionNames :: #force_inline proc(self: ^Library) -> ^NS.Array { + return msgSend(^NS.Array, self, "functionNames") +} +@(objc_type=Library, objc_name="installName") +Library_installName :: #force_inline proc(self: ^Library) -> ^NS.String { + return msgSend(^NS.String, self, "installName") +} +@(objc_type=Library, objc_name="label") +Library_label :: #force_inline proc(self: ^Library) -> ^NS.String { + return msgSend(^NS.String, self, "label") +} +@(objc_type=Library, objc_name="newFunctionWithCompletionHandler") +Library_newFunctionWithCompletionHandler :: #force_inline proc(self: ^Library, descriptor: ^FunctionDescriptor, completionHandler: ^NS.Block) -> ^Function { + return msgSend(^Function, self, "newFunctionWithDescriptor:completionHandler:", descriptor, completionHandler) +} +@(objc_type=Library, objc_name="newFunctionWithDescriptor") +Library_newFunctionWithDescriptor :: #force_inline proc(self: ^Library, descriptor: ^FunctionDescriptor) -> (function: ^Function, error: ^NS.Error) { + function = msgSend(^Function, self, "newFunctionWithDescriptor:error:", descriptor, &error) + return +} +@(objc_type=Library, objc_name="newFunctionWithName") +Library_newFunctionWithName :: #force_inline proc(self: ^Library, functionName: ^NS.String) -> ^Function { + return msgSend(^Function, self, "newFunctionWithName:", functionName) +} +@(objc_type=Library, objc_name="newFunctionWithConstantValuesAndCompletionHandler") +Library_newFunctionWithConstantValuesAndCompletionHandler :: #force_inline proc(self: ^Library, name: ^NS.String, constantValues: ^FunctionConstantValues, completionHandler: ^NS.Block) -> ^Function { + return msgSend(^Function, self, "newFunctionWithName:constantValues:completionHandler:", name, constantValues, completionHandler) +} +@(objc_type=Library, objc_name="newFunctionWithConstantValues") +Library_newFunctionWithConstantValues :: #force_inline proc(self: ^Library, name: ^NS.String, constantValues: ^FunctionConstantValues) -> (function: ^Function, error: ^NS.Error) { + function = msgSend(^Function, self, "newFunctionWithName:constantValues:error:", name, constantValues, &error) + return +} +@(objc_type=Library, objc_name="newFunction") +Library_newFunction :: proc{ + Library_newFunctionWithCompletionHandler, + Library_newFunctionWithDescriptor, + Library_newFunctionWithName, + Library_newFunctionWithConstantValuesAndCompletionHandler, + Library_newFunctionWithConstantValues, +} + +@(objc_type=Library, objc_name="newIntersectionFunctionWithCompletionHandler") +Library_newIntersectionFunctionWithCompletionHandler :: #force_inline proc(self: ^Library, descriptor: ^IntersectionFunctionDescriptor, completionHandler: ^NS.Block) -> ^Function { + return msgSend(^Function, self, "newIntersectionFunctionWithDescriptor:completionHandler:", descriptor, completionHandler) +} +@(objc_type=Library, objc_name="newIntersectionFunction") +Library_newIntersectionFunction :: #force_inline proc(self: ^Library, descriptor: ^IntersectionFunctionDescriptor) -> (function: ^Function, error: ^NS.Error) { + function = msgSend(^Function, self, "newIntersectionFunctionWithDescriptor:error:", descriptor, &error) + return +} +@(objc_type=Library, objc_name="setLabel") +Library_setLabel :: #force_inline proc(self: ^Library, label: ^NS.String) { + msgSend(nil, self, "setLabel:", label) +} +@(objc_type=Library, objc_name="type") +Library_type :: #force_inline proc(self: ^Library) -> LibraryType { + return msgSend(LibraryType, self, "type") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + ParallelRenderCommandEncoder +Class Methods: +Methods: + renderCommandEncoder + setColorStoreAction + setColorStoreActionOptions + setDepthStoreAction + setDepthStoreActionOptions + setStencilStoreAction + setStencilStoreActionOptions +*/ +@(objc_class="MTLParallelRenderCommandEncoder") +ParallelRenderCommandEncoder :: struct { using _: CommandEncoder } + +@(objc_type=ParallelRenderCommandEncoder, objc_name="renderCommandEncoder") +ParallelRenderCommandEncoder_renderCommandEncoder :: #force_inline proc(self: ^ParallelRenderCommandEncoder) -> ^RenderCommandEncoder { + return msgSend(^RenderCommandEncoder, self, "renderCommandEncoder") +} +@(objc_type=ParallelRenderCommandEncoder, objc_name="setColorStoreAction") +ParallelRenderCommandEncoder_setColorStoreAction :: #force_inline proc(self: ^ParallelRenderCommandEncoder, storeAction: StoreAction, colorAttachmentIndex: NS.UInteger) { + msgSend(nil, self, "setColorStoreAction:atIndex:", storeAction, colorAttachmentIndex) +} +@(objc_type=ParallelRenderCommandEncoder, objc_name="setColorStoreActionOptions") +ParallelRenderCommandEncoder_setColorStoreActionOptions :: #force_inline proc(self: ^ParallelRenderCommandEncoder, storeActionOptions: StoreActionOptions, colorAttachmentIndex: NS.UInteger) { + msgSend(nil, self, "setColorStoreActionOptions:atIndex:", storeActionOptions, colorAttachmentIndex) +} +@(objc_type=ParallelRenderCommandEncoder, objc_name="setDepthStoreAction") +ParallelRenderCommandEncoder_setDepthStoreAction :: #force_inline proc(self: ^ParallelRenderCommandEncoder, storeAction: StoreAction) { + msgSend(nil, self, "setDepthStoreAction:", storeAction) +} +@(objc_type=ParallelRenderCommandEncoder, objc_name="setDepthStoreActionOptions") +ParallelRenderCommandEncoder_setDepthStoreActionOptions :: #force_inline proc(self: ^ParallelRenderCommandEncoder, storeActionOptions: StoreActionOptions) { + msgSend(nil, self, "setDepthStoreActionOptions:", storeActionOptions) +} +@(objc_type=ParallelRenderCommandEncoder, objc_name="setStencilStoreAction") +ParallelRenderCommandEncoder_setStencilStoreAction :: #force_inline proc(self: ^ParallelRenderCommandEncoder, storeAction: StoreAction) { + msgSend(nil, self, "setStencilStoreAction:", storeAction) +} +@(objc_type=ParallelRenderCommandEncoder, objc_name="setStencilStoreActionOptions") +ParallelRenderCommandEncoder_setStencilStoreActionOptions :: #force_inline proc(self: ^ParallelRenderCommandEncoder, storeActionOptions: StoreActionOptions) { + msgSend(nil, self, "setStencilStoreActionOptions:", storeActionOptions) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + RasterizationRateMap +Class Methods: +Methods: + copyParameterDataToBuffer + device + label + layerCount + mapPhysicalToScreenCoordinates + mapScreenToPhysicalCoordinates + parameterBufferSizeAndAlign + physicalGranularity + physicalSizeForLayer + screenSize +*/ +@(objc_class="MTLRasterizationRateMap") +RasterizationRateMap :: struct { using _: NS.Object } + +@(objc_type=RasterizationRateMap, objc_name="copyParameterDataToBuffer") +RasterizationRateMap_copyParameterDataToBuffer :: #force_inline proc(self: ^RasterizationRateMap, buffer: ^Buffer, offset: NS.UInteger) { + msgSend(nil, self, "copyParameterDataToBuffer:offset:", buffer, offset) +} +@(objc_type=RasterizationRateMap, objc_name="device") +RasterizationRateMap_device :: #force_inline proc(self: ^RasterizationRateMap) -> ^Device { + return msgSend(^Device, self, "device") +} +@(objc_type=RasterizationRateMap, objc_name="label") +RasterizationRateMap_label :: #force_inline proc(self: ^RasterizationRateMap) -> ^NS.String { + return msgSend(^NS.String, self, "label") +} +@(objc_type=RasterizationRateMap, objc_name="layerCount") +RasterizationRateMap_layerCount :: #force_inline proc(self: ^RasterizationRateMap) -> NS.UInteger { + return msgSend(NS.UInteger, self, "layerCount") +} +@(objc_type=RasterizationRateMap, objc_name="mapPhysicalToScreenCoordinates") +RasterizationRateMap_mapPhysicalToScreenCoordinates :: #force_inline proc(self: ^RasterizationRateMap, physicalCoordinates: Coordinate2D, layerIndex: NS.UInteger) -> Coordinate2D { + return msgSend(Coordinate2D, self, "mapPhysicalToScreenCoordinates:forLayer:", physicalCoordinates, layerIndex) +} +@(objc_type=RasterizationRateMap, objc_name="mapScreenToPhysicalCoordinates") +RasterizationRateMap_mapScreenToPhysicalCoordinates :: #force_inline proc(self: ^RasterizationRateMap, screenCoordinates: Coordinate2D, layerIndex: NS.UInteger) -> Coordinate2D { + return msgSend(Coordinate2D, self, "mapScreenToPhysicalCoordinates:forLayer:", screenCoordinates, layerIndex) +} +@(objc_type=RasterizationRateMap, objc_name="parameterBufferSizeAndAlign") +RasterizationRateMap_parameterBufferSizeAndAlign :: #force_inline proc(self: ^RasterizationRateMap) -> (size, align: NS.UInteger) { + res := msgSend(SizeAndAlign, self, "parameterBufferSizeAndAlign") + return res.size, res.align +} +@(objc_type=RasterizationRateMap, objc_name="physicalGranularity") +RasterizationRateMap_physicalGranularity :: #force_inline proc(self: ^RasterizationRateMap) -> Size { + return msgSend(Size, self, "physicalGranularity") +} +@(objc_type=RasterizationRateMap, objc_name="physicalSizeForLayer") +RasterizationRateMap_physicalSizeForLayer :: #force_inline proc(self: ^RasterizationRateMap, layerIndex: NS.UInteger) -> Size { + return msgSend(Size, self, "physicalSizeForLayer:", layerIndex) +} +@(objc_type=RasterizationRateMap, objc_name="screenSize") +RasterizationRateMap_screenSize :: #force_inline proc(self: ^RasterizationRateMap) -> Size { + return msgSend(Size, self, "screenSize") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + RenderCommandEncoder +Class Methods: +Methods: + dispatchThreadsPerTile + drawIndexedPatches + drawIndexedPatches + drawIndexedPrimitives + drawIndexedPrimitives + drawIndexedPrimitives + drawIndexedPrimitives + drawPatches + drawPatches + drawPrimitives + drawPrimitives + drawPrimitives + drawPrimitives + executeCommandsInBuffer + executeCommandsInBuffer + memoryBarrierWithResources + memoryBarrierWithScope + sampleCountersInBuffer + setBlendColorRed + setColorStoreAction + setColorStoreActionOptions + setCullMode + setDepthBias + setDepthClipMode + setDepthStencilState + setDepthStoreAction + setDepthStoreActionOptions + setFragmentBuffer + setFragmentBufferOffset + setFragmentBuffers + setFragmentBytes + setFragmentSamplerState + setFragmentSamplerState + setFragmentSamplerStates + setFragmentSamplerStates + setFragmentTexture + setFragmentTextures + setFrontFacingWinding + setRenderPipelineState + setScissorRect + setScissorRects + setStencilFrontReferenceValue + setStencilReferenceValue + setStencilStoreAction + setStencilStoreActionOptions + setTessellationFactorBuffer + setTessellationFactorScale + setThreadgroupMemoryLength + setTileBuffer + setTileBufferOffset + setTileBuffers + setTileBytes + setTileSamplerState + setTileSamplerState + setTileSamplerStates + setTileSamplerStates + setTileTexture + setTileTextures + setTriangleFillMode + setVertexAmplificationCount + setVertexBuffer + setVertexBufferOffset + setVertexBuffers + setVertexBytes + setVertexSamplerState + setVertexSamplerState + setVertexSamplerStates + setVertexSamplerStates + setVertexTexture + setVertexTextures + setViewport + setViewports + setVisibilityResultMode + textureBarrier + tileHeight + tileWidth + updateFence + useHeap + useHeap + useHeaps + useHeaps + useResource + useResource + useResources + useResources + waitForFence +*/ +@(objc_class="MTLRenderCommandEncoder") +RenderCommandEncoder :: struct { using _: CommandEncoder } + +@(objc_type=RenderCommandEncoder, objc_name="dispatchThreadsPerTile") +RenderCommandEncoder_dispatchThreadsPerTile :: #force_inline proc(self: ^RenderCommandEncoder, threadsPerTile: Size) { + msgSend(nil, self, "dispatchThreadsPerTile:", threadsPerTile) +} +@(objc_type=RenderCommandEncoder, objc_name="drawIndexedPatchesWihtIndirect") +RenderCommandEncoder_drawIndexedPatchesWihtIndirect :: #force_inline proc(self: ^RenderCommandEncoder, numberOfPatchControlPoints: NS.UInteger, patchIndexBuffer: ^Buffer, patchIndexBufferOffset: NS.UInteger, controlPointIndexBuffer: ^Buffer, controlPointIndexBufferOffset: NS.UInteger, indirectBuffer: ^Buffer, indirectBufferOffset: NS.UInteger) { + msgSend(nil, self, "drawIndexedPatches:patchIndexBuffer:patchIndexBufferOffset:controlPointIndexBuffer:controlPointIndexBufferOffset:indirectBuffer:indirectBufferOffset:", numberOfPatchControlPoints, patchIndexBuffer, patchIndexBufferOffset, controlPointIndexBuffer, controlPointIndexBufferOffset, indirectBuffer, indirectBufferOffset) +} +@(objc_type=RenderCommandEncoder, objc_name="drawIndexPatchesWithInstances") +RenderCommandEncoder_drawIndexPatchesWithInstance :: #force_inline proc(self: ^RenderCommandEncoder, numberOfPatchControlPoints: NS.UInteger, patchStart, patchCount: NS.UInteger, patchIndexBuffer: ^Buffer, patchIndexBufferOffset: NS.UInteger, controlPointIndexBuffer: ^Buffer, controlPointIndexBufferOffset: NS.UInteger, instanceCount: NS.UInteger, baseInstance: NS.UInteger) { + msgSend(nil, self, "drawIndexedPatches:patchStart:patchCount:patchIndexBuffer:patchIndexBufferOffset:controlPointIndexBuffer:controlPointIndexBufferOffset:instanceCount:baseInstance:", numberOfPatchControlPoints, patchStart, patchCount, patchIndexBuffer, patchIndexBufferOffset, controlPointIndexBuffer, controlPointIndexBufferOffset, instanceCount, baseInstance) +} +@(objc_type=RenderCommandEncoder, objc_name="drawIndexedPrimitives") +RenderCommandEncoder_drawIndexedPrimitives :: #force_inline proc(self: ^RenderCommandEncoder, primitiveType: PrimitiveType, indexCount: NS.UInteger, indexType: IndexType, indexBuffer: ^Buffer, indexBufferOffset: NS.UInteger) { + msgSend(nil, self, "drawIndexedPrimitives:indexCount:indexType:indexBuffer:indexBufferOffset:", primitiveType, indexCount, indexType, indexBuffer, indexBufferOffset) +} +@(objc_type=RenderCommandEncoder, objc_name="drawIndexedPrimitivesWithInstanceCount") +RenderCommandEncoder_drawIndexedPrimitivesWithInstanceCount :: #force_inline proc(self: ^RenderCommandEncoder, primitiveType: PrimitiveType, indexCount: NS.UInteger, indexType: IndexType, indexBuffer: ^Buffer, indexBufferOffset: NS.UInteger, instanceCount: NS.UInteger) { + msgSend(nil, self, "drawIndexedPrimitives:indexCount:indexType:indexBuffer:indexBufferOffset:instanceCount:", primitiveType, indexCount, indexType, indexBuffer, indexBufferOffset, instanceCount) +} +@(objc_type=RenderCommandEncoder, objc_name="drawIndexPrimitivesWithBaseVertex") +RenderCommandEncoder_drawIndexPrimitivesWithBaseVertex :: #force_inline proc(self: ^RenderCommandEncoder, primitiveType: PrimitiveType, indexCount: NS.UInteger, indexType: IndexType, indexBuffer: ^Buffer, indexBufferOffset: NS.UInteger, instanceCount: NS.UInteger, baseVertex: NS.Integer, baseInstance: NS.UInteger) { + msgSend(nil, self, "drawIndexedPrimitives:indexCount:indexType:indexBuffer:indexBufferOffset:instanceCount:baseVertex:baseInstance:", primitiveType, indexCount, indexType, indexBuffer, indexBufferOffset, instanceCount, baseVertex, baseInstance) +} +@(objc_type=RenderCommandEncoder, objc_name="drawIndexPrimitivesWithIndirect") +RenderCommandEncoder_drawIndexPrimitivesWithIndirect :: #force_inline proc(self: ^RenderCommandEncoder, primitiveType: PrimitiveType, indexType: IndexType, indexBuffer: ^Buffer, indexBufferOffset: NS.UInteger, indirectBuffer: ^Buffer, indirectBufferOffset: NS.UInteger) { + msgSend(nil, self, "drawIndexedPrimitives:indexType:indexBuffer:indexBufferOffset:indirectBuffer:indirectBufferOffset:", primitiveType, indexType, indexBuffer, indexBufferOffset, indirectBuffer, indirectBufferOffset) +} +@(objc_type=RenderCommandEncoder, objc_name="drawPatches") +RenderCommandEncoder_drawPatches :: #force_inline proc(self: ^RenderCommandEncoder, numberOfPatchControlPoints: NS.UInteger, patchIndexBuffer: ^Buffer, patchIndexBufferOffset: NS.UInteger, indirectBuffer: ^Buffer, indirectBufferOffset: NS.UInteger) { + msgSend(nil, self, "drawPatches:patchIndexBuffer:patchIndexBufferOffset:indirectBuffer:indirectBufferOffset:", numberOfPatchControlPoints, patchIndexBuffer, patchIndexBufferOffset, indirectBuffer, indirectBufferOffset) +} +@(objc_type=RenderCommandEncoder, objc_name="drawPatchesWithInstances") +RenderCommandEncoder_drawPatchesWithInstance :: #force_inline proc(self: ^RenderCommandEncoder, numberOfPatchControlPoints: NS.UInteger, patchStart, patchCount: NS.UInteger, patchIndexBuffer: ^Buffer, patchIndexBufferOffset: NS.UInteger, instanceCount: NS.UInteger, baseInstance: NS.UInteger) { + msgSend(nil, self, "drawPatches:patchStart:patchCount:patchIndexBuffer:patchIndexBufferOffset:instanceCount:baseInstance:", numberOfPatchControlPoints, patchStart, patchCount, patchIndexBuffer, patchIndexBufferOffset, instanceCount, baseInstance) +} +@(objc_type=RenderCommandEncoder, objc_name="drawPrimitivesWithIndirect") +RenderCommandEncoder_drawPrimitivesWithIndirect :: #force_inline proc(self: ^RenderCommandEncoder, primitiveType: PrimitiveType, indirectBuffer: ^Buffer, indirectBufferOffset: NS.UInteger) { + msgSend(nil, self, "drawPrimitives:indirectBuffer:indirectBufferOffset:", primitiveType, indirectBuffer, indirectBufferOffset) +} +@(objc_type=RenderCommandEncoder, objc_name="drawPrimitives") +RenderCommandEncoder_drawPrimitives :: #force_inline proc(self: ^RenderCommandEncoder, primitiveType: PrimitiveType, vertexStart: NS.UInteger, vertexCount: NS.UInteger) { + msgSend(nil, self, "drawPrimitives:vertexStart:vertexCount:", primitiveType, vertexStart, vertexCount) +} +@(objc_type=RenderCommandEncoder, objc_name="drawPrimitivesWithInstanceCount") +RenderCommandEncoder_drawPrimitivesWithInstanceCount :: #force_inline proc(self: ^RenderCommandEncoder, primitiveType: PrimitiveType, vertexStart: NS.UInteger, vertexCount: NS.UInteger, instanceCount: NS.UInteger) { + msgSend(nil, self, "drawPrimitives:vertexStart:vertexCount:instanceCount:", primitiveType, vertexStart, vertexCount, instanceCount) +} +@(objc_type=RenderCommandEncoder, objc_name="drawPrimitivesWithInstances") +RenderCommandEncoder_drawPrimitivesWithInstances :: #force_inline proc(self: ^RenderCommandEncoder, primitiveType: PrimitiveType, vertexStart: NS.UInteger, vertexCount: NS.UInteger, instanceCount: NS.UInteger, baseInstance: NS.UInteger) { + msgSend(nil, self, "drawPrimitives:vertexStart:vertexCount:instanceCount:baseInstance:", primitiveType, vertexStart, vertexCount, instanceCount, baseInstance) +} +@(objc_type=RenderCommandEncoder, objc_name="executeCommandsInBuffer") +RenderCommandEncoder_executeCommandsInBuffer :: #force_inline proc(self: ^RenderCommandEncoder, indirectCommandbuffer: ^Buffer, indirectRangeBuffer: ^Buffer, indirectBufferOffset: NS.UInteger) { + msgSend(nil, self, "executeCommandsInBuffer:indirectBuffer:indirectBufferOffset:", indirectCommandbuffer, indirectRangeBuffer, indirectBufferOffset) +} +@(objc_type=RenderCommandEncoder, objc_name="executeCommandsInBufferWithRange") +RenderCommandEncoder_executeCommandsInBufferWithRange :: #force_inline proc(self: ^RenderCommandEncoder, indirectCommandBuffer: ^Buffer, executionRange: NS.Range) { + msgSend(nil, self, "executeCommandsInBuffer:withRange:", indirectCommandBuffer, executionRange) +} +@(objc_type=RenderCommandEncoder, objc_name="memoryBarrierWithResources") +RenderCommandEncoder_memoryBarrierWithResources :: #force_inline proc(self: ^RenderCommandEncoder, resources: []^Resource, after: RenderStages, before: RenderStages) { + msgSend(nil, self, "memoryBarrierWithResources:count:afterStages:beforeStages:", raw_data(resources), NS.UInteger(len(resources)), after, before) +} +@(objc_type=RenderCommandEncoder, objc_name="memoryBarrierWithScope") +RenderCommandEncoder_memoryBarrierWithScope :: #force_inline proc(self: ^RenderCommandEncoder, scope: BarrierScope, after: RenderStages, before: RenderStages) { + msgSend(nil, self, "memoryBarrierWithScope:afterStages:beforeStages:", scope, after, before) +} +@(objc_type=RenderCommandEncoder, objc_name="sampleCountersInBuffer") +RenderCommandEncoder_sampleCountersInBuffer :: #force_inline proc(self: ^RenderCommandEncoder, sampleBuffer: ^Buffer, sampleIndex: NS.UInteger, barrier: BOOL) { + msgSend(nil, self, "sampleCountersInBuffer:atSampleIndex:withBarrier:", sampleBuffer, sampleIndex, barrier) +} +@(objc_type=RenderCommandEncoder, objc_name="setBlendColorRed") +RenderCommandEncoder_setBlendColorRed :: #force_inline proc(self: ^RenderCommandEncoder, red: f32, green: f32, blue: f32, alpha: f32) { + msgSend(nil, self, "setBlendColorRed:green:blue:alpha:", red, green, blue, alpha) +} +@(objc_type=RenderCommandEncoder, objc_name="setColorStoreAction") +RenderCommandEncoder_setColorStoreAction :: #force_inline proc(self: ^RenderCommandEncoder, storeAction: StoreAction, colorAttachmentIndex: NS.UInteger) { + msgSend(nil, self, "setColorStoreAction:atIndex:", storeAction, colorAttachmentIndex) +} +@(objc_type=RenderCommandEncoder, objc_name="setColorStoreActionOptions") +RenderCommandEncoder_setColorStoreActionOptions :: #force_inline proc(self: ^RenderCommandEncoder, storeActionOptions: StoreActionOptions, colorAttachmentIndex: NS.UInteger) { + msgSend(nil, self, "setColorStoreActionOptions:atIndex:", storeActionOptions, colorAttachmentIndex) +} +@(objc_type=RenderCommandEncoder, objc_name="setCullMode") +RenderCommandEncoder_setCullMode :: #force_inline proc(self: ^RenderCommandEncoder, cullMode: CullMode) { + msgSend(nil, self, "setCullMode:", cullMode) +} +@(objc_type=RenderCommandEncoder, objc_name="setDepthBias") +RenderCommandEncoder_setDepthBias :: #force_inline proc(self: ^RenderCommandEncoder, depthBias: f32, slopeScale: f32, clamp: f32) { + msgSend(nil, self, "setDepthBias:slopeScale:clamp:", depthBias, slopeScale, clamp) +} +@(objc_type=RenderCommandEncoder, objc_name="setDepthClipMode") +RenderCommandEncoder_setDepthClipMode :: #force_inline proc(self: ^RenderCommandEncoder, depthClipMode: DepthClipMode) { + msgSend(nil, self, "setDepthClipMode:", depthClipMode) +} +@(objc_type=RenderCommandEncoder, objc_name="setDepthStencilState") +RenderCommandEncoder_setDepthStencilState :: #force_inline proc(self: ^RenderCommandEncoder, depthStencilState: ^DepthStencilState) { + msgSend(nil, self, "setDepthStencilState:", depthStencilState) +} +@(objc_type=RenderCommandEncoder, objc_name="setDepthStoreAction") +RenderCommandEncoder_setDepthStoreAction :: #force_inline proc(self: ^RenderCommandEncoder, storeAction: StoreAction) { + msgSend(nil, self, "setDepthStoreAction:", storeAction) +} +@(objc_type=RenderCommandEncoder, objc_name="setDepthStoreActionOptions") +RenderCommandEncoder_setDepthStoreActionOptions :: #force_inline proc(self: ^RenderCommandEncoder, storeActionOptions: StoreActionOptions) { + msgSend(nil, self, "setDepthStoreActionOptions:", storeActionOptions) +} +@(objc_type=RenderCommandEncoder, objc_name="setFragmentBuffer") +RenderCommandEncoder_setFragmentBuffer :: #force_inline proc(self: ^RenderCommandEncoder, buffer: ^Buffer, offset: NS.UInteger, index: NS.UInteger) { + msgSend(nil, self, "setFragmentBuffer:offset:atIndex:", buffer, offset, index) +} +@(objc_type=RenderCommandEncoder, objc_name="setFragmentBufferOffset") +RenderCommandEncoder_setFragmentBufferOffset :: #force_inline proc(self: ^RenderCommandEncoder, offset: NS.UInteger, index: NS.UInteger) { + msgSend(nil, self, "setFragmentBufferOffset:atIndex:", offset, index) +} +@(objc_type=RenderCommandEncoder, objc_name="setFragmentBuffers") +RenderCommandEncoder_setFragmentBuffers :: #force_inline proc(self: ^RenderCommandEncoder, buffers: []^Buffer, offsets: []NS.UInteger, range: NS.Range) { + msgSend(nil, self, "setFragmentBuffers:offsets:withRange:", raw_data(buffers), raw_data(offsets), range) +} +@(objc_type=RenderCommandEncoder, objc_name="setFragmentBytes") +RenderCommandEncoder_setFragmentBytes :: #force_inline proc(self: ^RenderCommandEncoder, bytes: []byte, index: NS.UInteger) { + msgSend(nil, self, "setFragmentBytes:length:atIndex:", raw_data(bytes), NS.UInteger(len(bytes)), index) +} +@(objc_type=RenderCommandEncoder, objc_name="setFragmentSamplerState") +RenderCommandEncoder_setFragmentSamplerState :: #force_inline proc(self: ^RenderCommandEncoder, sampler: ^SamplerState, index: NS.UInteger) { + msgSend(nil, self, "setFragmentSamplerState:atIndex:", sampler, index) +} +@(objc_type=RenderCommandEncoder, objc_name="setFragmentSamplerStateWithLod") +RenderCommandEncoder_setFragmentSamplerStateWithLod :: #force_inline proc(self: ^RenderCommandEncoder, sampler: ^SamplerState, lodMinClamp: f32, lodMaxClamp: f32, index: NS.UInteger) { + msgSend(nil, self, "setFragmentSamplerState:lodMinClamp:lodMaxClamp:atIndex:", sampler, lodMinClamp, lodMaxClamp, index) +} +@(objc_type=RenderCommandEncoder, objc_name="setFragmentSamplerStatesWithLod") +RenderCommandEncoder_setFragmentSamplerStatesWithLod :: #force_inline proc(self: ^RenderCommandEncoder, samplers: []^SamplerState, lodMinClamps, lodMaxClamps: []f32, range: NS.Range) { + msgSend(nil, self, "setFragmentSamplerStates:lodMinClamps:lodMaxClamps:withRange:", raw_data(samplers), raw_data(lodMinClamps), raw_data(lodMaxClamps), range) +} +@(objc_type=RenderCommandEncoder, objc_name="setFragmentSamplerStatesWithRange") +RenderCommandEncoder_setFragmentSamplerStatesWithRange :: #force_inline proc(self: ^RenderCommandEncoder, samplers: []^SamplerState, range: NS.Range) { + msgSend(nil, self, "setFragmentSamplerStates:withRange:", raw_data(samplers), range) +} +@(objc_type=RenderCommandEncoder, objc_name="setFragmentTexture") +RenderCommandEncoder_setFragmentTexture :: #force_inline proc(self: ^RenderCommandEncoder, texture: ^Texture, index: NS.UInteger) { + msgSend(nil, self, "setFragmentTexture:atIndex:", texture, index) +} +@(objc_type=RenderCommandEncoder, objc_name="setFragmentTextures") +RenderCommandEncoder_setFragmentTextures :: #force_inline proc(self: ^RenderCommandEncoder, textures: []^Texture, range: NS.Range) { + msgSend(nil, self, "setFragmentTextures:withRange:", raw_data(textures), range) +} +@(objc_type=RenderCommandEncoder, objc_name="setFrontFacingWinding") +RenderCommandEncoder_setFrontFacingWinding :: #force_inline proc(self: ^RenderCommandEncoder, frontFacingWinding: Winding) { + msgSend(nil, self, "setFrontFacingWinding:", frontFacingWinding) +} +@(objc_type=RenderCommandEncoder, objc_name="setRenderPipelineState") +RenderCommandEncoder_setRenderPipelineState :: #force_inline proc(self: ^RenderCommandEncoder, pipelineState: ^RenderPipelineState) { + msgSend(nil, self, "setRenderPipelineState:", pipelineState) +} +@(objc_type=RenderCommandEncoder, objc_name="setScissorRect") +RenderCommandEncoder_setScissorRect :: #force_inline proc(self: ^RenderCommandEncoder, rect: ScissorRect) { + msgSend(nil, self, "setScissorRect:", rect) +} +@(objc_type=RenderCommandEncoder, objc_name="setScissorRects") +RenderCommandEncoder_setScissorRects :: #force_inline proc(self: ^RenderCommandEncoder, scissorRects: []ScissorRect) { + msgSend(nil, self, "setScissorRects:count:", raw_data(scissorRects), NS.UInteger(len(scissorRects))) +} +@(objc_type=RenderCommandEncoder, objc_name="setStencilFrontReferenceValue") +RenderCommandEncoder_setStencilFrontReferenceValue :: #force_inline proc(self: ^RenderCommandEncoder, frontReferenceValue: u32, backReferenceValue: u32) { + msgSend(nil, self, "setStencilFrontReferenceValue:backReferenceValue:", frontReferenceValue, backReferenceValue) +} +@(objc_type=RenderCommandEncoder, objc_name="setStencilReferenceValue") +RenderCommandEncoder_setStencilReferenceValue :: #force_inline proc(self: ^RenderCommandEncoder, referenceValue: u32) { + msgSend(nil, self, "setStencilReferenceValue:", referenceValue) +} +@(objc_type=RenderCommandEncoder, objc_name="setStencilStoreAction") +RenderCommandEncoder_setStencilStoreAction :: #force_inline proc(self: ^RenderCommandEncoder, storeAction: StoreAction) { + msgSend(nil, self, "setStencilStoreAction:", storeAction) +} +@(objc_type=RenderCommandEncoder, objc_name="setStencilStoreActionOptions") +RenderCommandEncoder_setStencilStoreActionOptions :: #force_inline proc(self: ^RenderCommandEncoder, storeActionOptions: StoreActionOptions) { + msgSend(nil, self, "setStencilStoreActionOptions:", storeActionOptions) +} +@(objc_type=RenderCommandEncoder, objc_name="setTessellationFactorBuffer") +RenderCommandEncoder_setTessellationFactorBuffer :: #force_inline proc(self: ^RenderCommandEncoder, buffer: ^Buffer, offset: NS.UInteger, instanceStride: NS.UInteger) { + msgSend(nil, self, "setTessellationFactorBuffer:offset:instanceStride:", buffer, offset, instanceStride) +} +@(objc_type=RenderCommandEncoder, objc_name="setTessellationFactorScale") +RenderCommandEncoder_setTessellationFactorScale :: #force_inline proc(self: ^RenderCommandEncoder, scale: f32) { + msgSend(nil, self, "setTessellationFactorScale:", scale) +} +@(objc_type=RenderCommandEncoder, objc_name="setThreadgroupMemoryLength") +RenderCommandEncoder_setThreadgroupMemoryLength :: #force_inline proc(self: ^RenderCommandEncoder, length: NS.UInteger, offset: NS.UInteger, index: NS.UInteger) { + msgSend(nil, self, "setThreadgroupMemoryLength:offset:atIndex:", length, offset, index) +} +@(objc_type=RenderCommandEncoder, objc_name="setTileBuffer") +RenderCommandEncoder_setTileBuffer :: #force_inline proc(self: ^RenderCommandEncoder, buffer: ^Buffer, offset: NS.UInteger, index: NS.UInteger) { + msgSend(nil, self, "setTileBuffer:offset:atIndex:", buffer, offset, index) +} +@(objc_type=RenderCommandEncoder, objc_name="setTileBufferOffset") +RenderCommandEncoder_setTileBufferOffset :: #force_inline proc(self: ^RenderCommandEncoder, offset: NS.UInteger, index: NS.UInteger) { + msgSend(nil, self, "setTileBufferOffset:atIndex:", offset, index) +} +@(objc_type=RenderCommandEncoder, objc_name="setTileBuffers") +RenderCommandEncoder_setTileBuffers :: #force_inline proc(self: ^RenderCommandEncoder, buffers: []^Buffer, offsets: []NS.UInteger, range: NS.Range) { + msgSend(nil, self, "setTileBuffers:offsets:withRange:", raw_data(buffers), raw_data(offsets), range) +} +@(objc_type=RenderCommandEncoder, objc_name="setTileBytes") +RenderCommandEncoder_setTileBytes :: #force_inline proc(self: ^RenderCommandEncoder, bytes: []byte, index: NS.UInteger) { + msgSend(nil, self, "setTileBytes:length:atIndex:", raw_data(bytes), NS.UInteger(len(bytes)), index) +} +@(objc_type=RenderCommandEncoder, objc_name="setTileSamplerState") +RenderCommandEncoder_setTileSamplerState :: #force_inline proc(self: ^RenderCommandEncoder, sampler: ^SamplerState, index: NS.UInteger) { + msgSend(nil, self, "setTileSamplerState:atIndex:", sampler, index) +} +@(objc_type=RenderCommandEncoder, objc_name="setTileSamplerStateWithLod") +RenderCommandEncoder_setTileSamplerStateWithLod :: #force_inline proc(self: ^RenderCommandEncoder, sampler: ^SamplerState, lodMinClamp: f32, lodMaxClamp: f32, index: NS.UInteger) { + msgSend(nil, self, "setTileSamplerState:lodMinClamp:lodMaxClamp:atIndex:", sampler, lodMinClamp, lodMaxClamp, index) +} +@(objc_type=RenderCommandEncoder, objc_name="setTileSamplerStatesWithLod") +RenderCommandEncoder_setTileSamplerStatesWithLod :: #force_inline proc(self: ^RenderCommandEncoder, samplers: []^SamplerState, lodMinClamps, lodMaxClamps: []f32, range: NS.Range) { + msgSend(nil, self, "setTileSamplerStates:lodMinClamps:lodMaxClamps:withRange:", raw_data(samplers), raw_data(lodMinClamps), raw_data(lodMaxClamps), range) +} +@(objc_type=RenderCommandEncoder, objc_name="setTileSamplerStatesWithRange") +RenderCommandEncoder_setTileSamplerStatesWithRange :: #force_inline proc(self: ^RenderCommandEncoder, samplers: []^SamplerState, range: NS.Range) { + msgSend(nil, self, "setTileSamplerStates:withRange:", raw_data(samplers), range) +} +@(objc_type=RenderCommandEncoder, objc_name="setTileTexture") +RenderCommandEncoder_setTileTexture :: #force_inline proc(self: ^RenderCommandEncoder, texture: ^Texture, index: NS.UInteger) { + msgSend(nil, self, "setTileTexture:atIndex:", texture, index) +} +@(objc_type=RenderCommandEncoder, objc_name="setTileTextures") +RenderCommandEncoder_setTileTextures :: #force_inline proc(self: ^RenderCommandEncoder, textures: []^Texture, range: NS.Range) { + msgSend(nil, self, "setTileTextures:withRange:", raw_data(textures), range) +} +@(objc_type=RenderCommandEncoder, objc_name="setTriangleFillMode") +RenderCommandEncoder_setTriangleFillMode :: #force_inline proc(self: ^RenderCommandEncoder, fillMode: TriangleFillMode) { + msgSend(nil, self, "setTriangleFillMode:", fillMode) +} +@(objc_type=RenderCommandEncoder, objc_name="setVertexAmplificationCount") +RenderCommandEncoder_setVertexAmplificationCount :: #force_inline proc(self: ^RenderCommandEncoder, viewMappings: []VertexAmplificationViewMapping) { + msgSend(nil, self, "setVertexAmplificationCount:viewMappings:", NS.UInteger(len(viewMappings)), raw_data(viewMappings)) +} +@(objc_type=RenderCommandEncoder, objc_name="setVertexBuffer") +RenderCommandEncoder_setVertexBuffer :: #force_inline proc(self: ^RenderCommandEncoder, buffer: ^Buffer, offset: NS.UInteger, index: NS.UInteger) { + msgSend(nil, self, "setVertexBuffer:offset:atIndex:", buffer, offset, index) +} +@(objc_type=RenderCommandEncoder, objc_name="setVertexBufferOffset") +RenderCommandEncoder_setVertexBufferOffset :: #force_inline proc(self: ^RenderCommandEncoder, offset: NS.UInteger, index: NS.UInteger) { + msgSend(nil, self, "setVertexBufferOffset:atIndex:", offset, index) +} +@(objc_type=RenderCommandEncoder, objc_name="setVertexBuffers") +RenderCommandEncoder_setVertexBuffers :: #force_inline proc(self: ^RenderCommandEncoder, buffers: []^Buffer, offsets: []NS.UInteger, range: NS.Range) { + msgSend(nil, self, "setVertexBuffers:offsets:withRange:", raw_data(buffers), raw_data(offsets), range) +} +@(objc_type=RenderCommandEncoder, objc_name="setVertexBytes") +RenderCommandEncoder_setVertexBytes :: #force_inline proc(self: ^RenderCommandEncoder, bytes: []byte, index: NS.UInteger) { + msgSend(nil, self, "setVertexBytes:length:atIndex:", raw_data(bytes), NS.UInteger(len(bytes)), index) +} +@(objc_type=RenderCommandEncoder, objc_name="setVertexSamplerState") +RenderCommandEncoder_setVertexSamplerState :: #force_inline proc(self: ^RenderCommandEncoder, sampler: ^SamplerState, index: NS.UInteger) { + msgSend(nil, self, "setVertexSamplerState:atIndex:", sampler, index) +} +@(objc_type=RenderCommandEncoder, objc_name="setVertexSamplerStateWithLod") +RenderCommandEncoder_setVertexSamplerStateWithLod :: #force_inline proc(self: ^RenderCommandEncoder, sampler: ^SamplerState, lodMinClamp: f32, lodMaxClamp: f32, index: NS.UInteger) { + msgSend(nil, self, "setVertexSamplerState:lodMinClamp:lodMaxClamp:atIndex:", sampler, lodMinClamp, lodMaxClamp, index) +} +@(objc_type=RenderCommandEncoder, objc_name="setVertexSamplerStatesWithLod") +RenderCommandEncoder_setVertexSamplerStatesWithLod :: #force_inline proc(self: ^RenderCommandEncoder, samplers: []^SamplerState, lodMinClamps, lodMaxClamps: []f32, range: NS.Range) { + msgSend(nil, self, "setVertexSamplerStates:lodMinClamps:lodMaxClamps:withRange:", raw_data(samplers), raw_data(lodMinClamps), raw_data(lodMaxClamps), range) +} +@(objc_type=RenderCommandEncoder, objc_name="setVertexSamplerStatesWithRange") +RenderCommandEncoder_setVertexSamplerStatesWithRange :: #force_inline proc(self: ^RenderCommandEncoder, samplers: []^SamplerState, range: NS.Range) { + msgSend(nil, self, "setVertexSamplerStates:withRange:", raw_data(samplers), range) +} +@(objc_type=RenderCommandEncoder, objc_name="setVertexTexture") +RenderCommandEncoder_setVertexTexture :: #force_inline proc(self: ^RenderCommandEncoder, texture: ^Texture, index: NS.UInteger) { + msgSend(nil, self, "setVertexTexture:atIndex:", texture, index) +} +@(objc_type=RenderCommandEncoder, objc_name="setVertexTextures") +RenderCommandEncoder_setVertexTextures :: #force_inline proc(self: ^RenderCommandEncoder, textures: []^Texture, range: NS.Range) { + msgSend(nil, self, "setVertexTextures:withRange:", raw_data(textures), range) +} +@(objc_type=RenderCommandEncoder, objc_name="setViewport") +RenderCommandEncoder_setViewport :: #force_inline proc(self: ^RenderCommandEncoder, viewport: Viewport) { + msgSend(nil, self, "setViewport:", viewport) +} +@(objc_type=RenderCommandEncoder, objc_name="setViewports") +RenderCommandEncoder_setViewports :: #force_inline proc(self: ^RenderCommandEncoder, viewports: []Viewport) { + msgSend(nil, self, "setViewports:count:", raw_data(viewports), NS.UInteger(len(viewports))) +} +@(objc_type=RenderCommandEncoder, objc_name="setVisibilityResultMode") +RenderCommandEncoder_setVisibilityResultMode :: #force_inline proc(self: ^RenderCommandEncoder, mode: VisibilityResultMode, offset: NS.UInteger) { + msgSend(nil, self, "setVisibilityResultMode:offset:", mode, offset) +} +@(objc_type=RenderCommandEncoder, objc_name="textureBarrier") +RenderCommandEncoder_textureBarrier :: #force_inline proc(self: ^RenderCommandEncoder) { + msgSend(nil, self, "textureBarrier") +} +@(objc_type=RenderCommandEncoder, objc_name="tileHeight") +RenderCommandEncoder_tileHeight :: #force_inline proc(self: ^RenderCommandEncoder) -> NS.UInteger { + return msgSend(NS.UInteger, self, "tileHeight") +} +@(objc_type=RenderCommandEncoder, objc_name="tileWidth") +RenderCommandEncoder_tileWidth :: #force_inline proc(self: ^RenderCommandEncoder) -> NS.UInteger { + return msgSend(NS.UInteger, self, "tileWidth") +} +@(objc_type=RenderCommandEncoder, objc_name="updateFence") +RenderCommandEncoder_updateFence :: #force_inline proc(self: ^RenderCommandEncoder, fence: ^Fence, stages: RenderStages) { + msgSend(nil, self, "updateFence:afterStages:", fence, stages) +} +@(objc_type=RenderCommandEncoder, objc_name="useHeap") +RenderCommandEncoder_useHeap :: #force_inline proc(self: ^RenderCommandEncoder, heap: ^Heap) { + msgSend(nil, self, "useHeap:", heap) +} +@(objc_type=RenderCommandEncoder, objc_name="useHeapWithStages") +RenderCommandEncoder_useHeapWithStages :: #force_inline proc(self: ^RenderCommandEncoder, heap: ^Heap, stages: RenderStages) { + msgSend(nil, self, "useHeap:stages:", heap, stages) +} +@(objc_type=RenderCommandEncoder, objc_name="useHeaps") +RenderCommandEncoder_useHeaps :: #force_inline proc(self: ^RenderCommandEncoder, heaps: []^Heap) { + msgSend(nil, self, "useHeaps:count:", raw_data(heaps), NS.UInteger(len(heaps))) +} +@(objc_type=RenderCommandEncoder, objc_name="useHeapsWithStages") +RenderCommandEncoder_useHeapsWithStages :: #force_inline proc(self: ^RenderCommandEncoder, heaps: []^Heap, stages: RenderStages) { + msgSend(nil, self, "useHeaps:count:stages:", raw_data(heaps), NS.UInteger(len(heaps)), stages) +} +@(objc_type=RenderCommandEncoder, objc_name="useResource") +RenderCommandEncoder_useResource :: #force_inline proc(self: ^RenderCommandEncoder, resource: ^Resource, usage: ResourceUsage) { + msgSend(nil, self, "useResource:usage:", resource, usage) +} +@(objc_type=RenderCommandEncoder, objc_name="useResourceWithStages") +RenderCommandEncoder_useResourceWithStages :: #force_inline proc(self: ^RenderCommandEncoder, resource: ^Resource, usage: ResourceUsage, stages: RenderStages) { + msgSend(nil, self, "useResource:usage:stages:", resource, usage, stages) +} +@(objc_type=RenderCommandEncoder, objc_name="useResources") +RenderCommandEncoder_useResources :: #force_inline proc(self: ^RenderCommandEncoder, resources: []^Resource, usage: ResourceUsage) { + msgSend(nil, self, "useResources:count:usage:", raw_data(resources), NS.UInteger(len(resources)), usage) +} +@(objc_type=RenderCommandEncoder, objc_name="useResourcesStages") +RenderCommandEncoder_useResourcesStages :: #force_inline proc(self: ^RenderCommandEncoder, resources: []^Resource, usage: ResourceUsage, stages: RenderStages) { + msgSend(nil, self, "useResources:count:usage:stages:", raw_data(resources), NS.UInteger(len(resources)), usage, stages) +} +@(objc_type=RenderCommandEncoder, objc_name="waitForFence") +RenderCommandEncoder_waitForFence :: #force_inline proc(self: ^RenderCommandEncoder, fence: ^Fence, stages: RenderStages) { + msgSend(nil, self, "waitForFence:beforeStages:", fence, stages) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + RenderPipelineFunctionsDescriptor +*/ +@(objc_class="MTLRenderPipelineFunctionsDescriptor") +RenderPipelineFunctionsDescriptor :: struct { using _: NS.Copying(RenderPipelineFunctionsDescriptor) } + +@(objc_type=RenderPipelineFunctionsDescriptor, objc_name="alloc", objc_is_class_method=true) +RenderPipelineFunctionsDescriptor_alloc :: #force_inline proc() -> ^RenderPipelineFunctionsDescriptor { + return msgSend(^RenderPipelineFunctionsDescriptor, RenderPipelineFunctionsDescriptor, "alloc") +} + +@(objc_type=RenderPipelineFunctionsDescriptor, objc_name="init") +RenderPipelineFunctionsDescriptor_init :: #force_inline proc(self: ^RenderPipelineFunctionsDescriptor) -> ^RenderPipelineFunctionsDescriptor { + return msgSend(^RenderPipelineFunctionsDescriptor, self, "init") +} + +@(objc_type=RenderPipelineFunctionsDescriptor, objc_name="vertexAdditionalBinaryFunctions") +RenderPipelineFunctionsDescriptor_vertexAdditionalBinaryFunctions :: #force_inline proc(self: ^RenderPipelineFunctionsDescriptor) -> ^NS.Array { + return msgSend(^NS.Array, self, "vertexAdditionalBinaryFunctions") +} +@(objc_type=RenderPipelineFunctionsDescriptor, objc_name="fragmentAdditionalBinaryFunctions") +RenderPipelineFunctionsDescriptor_fragmentAdditionalBinaryFunctions :: #force_inline proc(self: ^RenderPipelineFunctionsDescriptor) -> ^NS.Array { + return msgSend(^NS.Array, self, "fragmentAdditionalBinaryFunctions") +} +@(objc_type=RenderPipelineFunctionsDescriptor, objc_name="tileAdditionalBinaryFunctions") +RenderPipelineFunctionsDescriptor_tileAdditionalBinaryFunctions :: #force_inline proc(self: ^RenderPipelineFunctionsDescriptor) -> ^NS.Array { + return msgSend(^NS.Array, self, "tileAdditionalBinaryFunctions") +} + +@(objc_type=RenderPipelineFunctionsDescriptor, objc_name="setVertexAdditionalBinaryFunctions") +RenderPipelineFunctionsDescriptor_setVertexAdditionalBinaryFunctions :: #force_inline proc(self: ^RenderPipelineFunctionsDescriptor, binaryFunctions: ^NS.Array) { + msgSend(nil, self, "setVertexAdditionalBinaryFunctions:", binaryFunctions) +} +@(objc_type=RenderPipelineFunctionsDescriptor, objc_name="setFragmentAdditionalBinaryFunctions") +RenderPipelineFunctionsDescriptor_setFragmentAdditionalBinaryFunctions :: #force_inline proc(self: ^RenderPipelineFunctionsDescriptor, binaryFunctions: ^NS.Array) { + msgSend(nil, self, "setFragmentAdditionalBinaryFunctions:", binaryFunctions) +} +@(objc_type=RenderPipelineFunctionsDescriptor, objc_name="setTileAdditionalBinaryFunctions") +RenderPipelineFunctionsDescriptor_setTileAdditionalBinaryFunctions :: #force_inline proc(self: ^RenderPipelineFunctionsDescriptor, binaryFunctions: ^NS.Array) { + msgSend(nil, self, "tileAdditionalBinaryFunctions:", binaryFunctions) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + RenderPipelineState +Class Methods: +Methods: + device + imageblockMemoryLengthForDimensions + imageblockSampleLength + label + maxTotalThreadsPerThreadgroup + supportIndirectCommandBuffers + threadgroupSizeMatchesTileSize +*/ +@(objc_class="MTLRenderPipelineState") +RenderPipelineState :: struct { using _: NS.Object } + +@(objc_type=RenderPipelineState, objc_name="device") +RenderPipelineState_device :: #force_inline proc(self: ^RenderPipelineState) -> ^Device { + return msgSend(^Device, self, "device") +} +@(objc_type=RenderPipelineState, objc_name="imageblockMemoryLengthForDimensions") +RenderPipelineState_imageblockMemoryLengthForDimensions :: #force_inline proc(self: ^RenderPipelineState, imageblockDimensions: Size) -> NS.UInteger { + return msgSend(NS.UInteger, self, "imageblockMemoryLengthForDimensions:", imageblockDimensions) +} +@(objc_type=RenderPipelineState, objc_name="imageblockSampleLength") +RenderPipelineState_imageblockSampleLength :: #force_inline proc(self: ^RenderPipelineState) -> NS.UInteger { + return msgSend(NS.UInteger, self, "imageblockSampleLength") +} +@(objc_type=RenderPipelineState, objc_name="label") +RenderPipelineState_label :: #force_inline proc(self: ^RenderPipelineState) -> ^NS.String { + return msgSend(^NS.String, self, "label") +} +@(objc_type=RenderPipelineState, objc_name="maxTotalThreadsPerThreadgroup") +RenderPipelineState_maxTotalThreadsPerThreadgroup :: #force_inline proc(self: ^RenderPipelineState) -> NS.UInteger { + return msgSend(NS.UInteger, self, "maxTotalThreadsPerThreadgroup") +} +@(objc_type=RenderPipelineState, objc_name="supportIndirectCommandBuffers") +RenderPipelineState_supportIndirectCommandBuffers :: #force_inline proc(self: ^RenderPipelineState) -> BOOL { + return msgSend(BOOL, self, "supportIndirectCommandBuffers") +} +@(objc_type=RenderPipelineState, objc_name="threadgroupSizeMatchesTileSize") +RenderPipelineState_threadgroupSizeMatchesTileSize :: #force_inline proc(self: ^RenderPipelineState) -> BOOL { + return msgSend(BOOL, self, "threadgroupSizeMatchesTileSize") +} + +@(objc_type=RenderPipelineState, objc_name="functionHandle") +RenderPipelineState_functionHandle :: #force_inline proc(self: ^RenderPipelineState, function: ^Function, stage: RenderStages) -> ^FunctionHandle { + return msgSend(^FunctionHandle, self, "functionHandleWithFunction:stage:", function, stage) +} + +@(objc_type=RenderPipelineState, objc_name="newVisibleFunctionTable") +RenderPipelineState_newVisibleFunctionTable :: #force_inline proc(self: ^RenderPipelineState, descriptor: ^VisibleFunctionTableDescriptor, stage: RenderStages) -> ^VisibleFunctionTable { + return msgSend(^VisibleFunctionTable, self, "newVisibleFunctionTableWithDescriptor:stage:", descriptor, stage) +} + +@(objc_type=RenderPipelineState, objc_name="newIntersectionFunctionTable") +RenderPipelineState_newIntersectionFunctionTable :: #force_inline proc(self: ^RenderPipelineState, descriptor: ^IntersectionFunctionTableDescriptor, stage: RenderStages) -> ^IntersectionFunctionTable { + return msgSend(^IntersectionFunctionTable, self, "newIntersectionFunctionTable:stage:", descriptor, stage) +} + +@(objc_type=RenderPipelineState, objc_name="newRenderPipelineState") +RenderPipelineState_newRenderPipelineState :: #force_inline proc(self: ^RenderPipelineState, additionalBinaryFunctions: ^RenderPipelineFunctionsDescriptor) -> (state: ^RenderPipelineState, error: ^NS.Error) { + state = msgSend(^RenderPipelineState, self, "newRenderPipelineStateWithAdditionalBinaryFunctions:error:", additionalBinaryFunctions, &error) + return +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + Resource +Class Methods: +Methods: + allocatedSize + cpuCacheMode + device + hazardTrackingMode + heap + heapOffset + isAliasable + label + makeAliasable + resourceOptions + setLabel + setPurgeableState + storageMode +*/ +@(objc_class="MTLResource") +Resource :: struct { using _: NS.Object } + +@(objc_type=Resource, objc_name="allocatedSize") +Resource_allocatedSize :: #force_inline proc(self: ^Resource) -> NS.UInteger { + return msgSend(NS.UInteger, self, "allocatedSize") +} +@(objc_type=Resource, objc_name="cpuCacheMode") +Resource_cpuCacheMode :: #force_inline proc(self: ^Resource) -> CPUCacheMode { + return msgSend(CPUCacheMode, self, "cpuCacheMode") +} +@(objc_type=Resource, objc_name="device") +Resource_device :: #force_inline proc(self: ^Resource) -> ^Device { + return msgSend(^Device, self, "device") +} +@(objc_type=Resource, objc_name="hazardTrackingMode") +Resource_hazardTrackingMode :: #force_inline proc(self: ^Resource) -> HazardTrackingMode { + return msgSend(HazardTrackingMode, self, "hazardTrackingMode") +} +@(objc_type=Resource, objc_name="heap") +Resource_heap :: #force_inline proc(self: ^Resource) -> ^Heap { + return msgSend(^Heap, self, "heap") +} +@(objc_type=Resource, objc_name="heapOffset") +Resource_heapOffset :: #force_inline proc(self: ^Resource) -> NS.UInteger { + return msgSend(NS.UInteger, self, "heapOffset") +} +@(objc_type=Resource, objc_name="isAliasable") +Resource_isAliasable :: #force_inline proc(self: ^Resource) -> BOOL { + return msgSend(BOOL, self, "isAliasable") +} +@(objc_type=Resource, objc_name="label") +Resource_label :: #force_inline proc(self: ^Resource) -> ^NS.String { + return msgSend(^NS.String, self, "label") +} +@(objc_type=Resource, objc_name="makeAliasable") +Resource_makeAliasable :: #force_inline proc(self: ^Resource) { + msgSend(nil, self, "makeAliasable") +} +@(objc_type=Resource, objc_name="resourceOptions") +Resource_resourceOptions :: #force_inline proc(self: ^Resource) -> ResourceOptions { + return msgSend(ResourceOptions, self, "resourceOptions") +} +@(objc_type=Resource, objc_name="setLabel") +Resource_setLabel :: #force_inline proc(self: ^Resource, label: ^NS.String) { + msgSend(nil, self, "setLabel:", label) +} +@(objc_type=Resource, objc_name="setPurgeableState") +Resource_setPurgeableState :: #force_inline proc(self: ^Resource, state: PurgeableState) -> PurgeableState { + return msgSend(PurgeableState, self, "setPurgeableState:", state) +} +@(objc_type=Resource, objc_name="storageMode") +Resource_storageMode :: #force_inline proc(self: ^Resource) -> StorageMode { + return msgSend(StorageMode, self, "storageMode") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + ResourceStateCommandEncoder +Class Methods: +Methods: + updateFence + updateTextureMapping + updateTextureMapping + updateTextureMappings + waitForFence +*/ +@(objc_class="MTLResourceStateCommandEncoder") +ResourceStateCommandEncoder :: struct { using _: CommandEncoder } + +@(objc_type=ResourceStateCommandEncoder, objc_name="updateFence") +ResourceStateCommandEncoder_updateFence :: #force_inline proc(self: ^ResourceStateCommandEncoder, fence: ^Fence) { + msgSend(nil, self, "updateFence:", fence) +} +@(objc_type=ResourceStateCommandEncoder, objc_name="updateTextureMappingIndirect") +ResourceStateCommandEncoder_updateTextureMappingIndirect :: #force_inline proc(self: ^ResourceStateCommandEncoder, texture: ^Texture, mode: SparseTextureMappingMode, indirectBuffer: ^Buffer, indirectBufferOffset: NS.UInteger) { + msgSend(nil, self, "updateTextureMapping:mode:indirectBuffer:indirectBufferOffset:", texture, mode, indirectBuffer, indirectBufferOffset) +} +@(objc_type=ResourceStateCommandEncoder, objc_name="updateTextureMapping") +ResourceStateCommandEncoder_updateTextureMapping :: #force_inline proc(self: ^ResourceStateCommandEncoder, texture: ^Texture, mode: SparseTextureMappingMode, region: Region, mipLevel: NS.UInteger, slice: NS.UInteger) { + msgSend(nil, self, "updateTextureMapping:mode:region:mipLevel:slice:", texture, mode, region, mipLevel, slice) +} +@(objc_type=ResourceStateCommandEncoder, objc_name="updateTextureMappings") +ResourceStateCommandEncoder_updateTextureMappings :: #force_inline proc(self: ^ResourceStateCommandEncoder, texture: ^Texture, mode: SparseTextureMappingMode, regions: []Region, mipLevels: []NS.UInteger, slices: NS.UInteger) { + msgSend(nil, self, "updateTextureMappings:mode:regions:mipLevels:slices:numRegions:", texture, mode, raw_data(regions), raw_data(mipLevels), slices, NS.UInteger(len(regions))) +} +@(objc_type=ResourceStateCommandEncoder, objc_name="waitForFence") +ResourceStateCommandEncoder_waitForFence :: #force_inline proc(self: ^ResourceStateCommandEncoder, fence: ^Fence) { + msgSend(nil, self, "waitForFence:", fence) +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + SamplerState +Class Methods: +Methods: + device + label +*/ +@(objc_class="MTLSamplerState") +SamplerState :: struct { using _: NS.Object } + +@(objc_type=SamplerState, objc_name="device") +SamplerState_device :: #force_inline proc(self: ^SamplerState) -> ^Device { + return msgSend(^Device, self, "device") +} +@(objc_type=SamplerState, objc_name="label") +SamplerState_label :: #force_inline proc(self: ^SamplerState) -> ^NS.String { + return msgSend(^NS.String, self, "label") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + SharedEvent +Class Methods: +Methods: + newSharedEventHandle + notifyListener + setSignaledValue + signaledValue +*/ +@(objc_class="MTLSharedEvent") +SharedEvent :: struct { using _: Event } + +@(objc_type=SharedEvent, objc_name="newSharedEventHandle") +SharedEvent_newSharedEventHandle :: #force_inline proc(self: ^SharedEvent) -> ^SharedEventHandle { + return msgSend(^SharedEventHandle, self, "newSharedEventHandle") +} +@(objc_type=SharedEvent, objc_name="notifyListener") +SharedEvent_notifyListener :: #force_inline proc(self: ^SharedEvent, listener: ^SharedEventListener, value: u64, block: SharedEventNotificationBlock) { + msgSend(nil, self, "notifyListener:atValue:block:", listener, value, block) +} +@(objc_type=SharedEvent, objc_name="setSignaledValue") +SharedEvent_setSignaledValue :: #force_inline proc(self: ^SharedEvent, signaledValue: u64) { + msgSend(nil, self, "setSignaledValue:", signaledValue) +} +@(objc_type=SharedEvent, objc_name="signaledValue") +SharedEvent_signaledValue :: #force_inline proc(self: ^SharedEvent) -> u64 { + return msgSend(u64, self, "signaledValue") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + Texture +Class Methods: +Methods: + allowGPUOptimizedContents + arrayLength + buffer + bufferBytesPerRow + bufferOffset + depth + firstMipmapInTail + getBytes + getBytes + height + iosurface + iosurfacePlane + isFramebufferOnly + isShareable + isSparse + mipmapLevelCount + newRemoteTextureViewForDevice + newSharedTextureHandle + newTextureViewWithPixelFormat + newTextureViewWithPixelFormat + newTextureViewWithPixelFormat + parentRelativeLevel + parentRelativeSlice + parentTexture + pixelFormat + remoteStorageTexture + replaceRegion + replaceRegion + rootResource + sampleCount + swizzle + tailSizeInBytes + textureType + usage + width +*/ +@(objc_class="MTLTexture") +Texture :: struct { using _: Resource } + +@(objc_type=Texture, objc_name="allowGPUOptimizedContents") +Texture_allowGPUOptimizedContents :: #force_inline proc(self: ^Texture) -> BOOL { + return msgSend(BOOL, self, "allowGPUOptimizedContents") +} +@(objc_type=Texture, objc_name="arrayLength") +Texture_arrayLength :: #force_inline proc(self: ^Texture) -> NS.UInteger { + return msgSend(NS.UInteger, self, "arrayLength") +} +@(objc_type=Texture, objc_name="buffer") +Texture_buffer :: #force_inline proc(self: ^Texture) -> ^Buffer { + return msgSend(^Buffer, self, "buffer") +} +@(objc_type=Texture, objc_name="bufferBytesPerRow") +Texture_bufferBytesPerRow :: #force_inline proc(self: ^Texture) -> NS.UInteger { + return msgSend(NS.UInteger, self, "bufferBytesPerRow") +} +@(objc_type=Texture, objc_name="bufferOffset") +Texture_bufferOffset :: #force_inline proc(self: ^Texture) -> NS.UInteger { + return msgSend(NS.UInteger, self, "bufferOffset") +} +@(objc_type=Texture, objc_name="depth") +Texture_depth :: #force_inline proc(self: ^Texture) -> NS.UInteger { + return msgSend(NS.UInteger, self, "depth") +} +@(objc_type=Texture, objc_name="firstMipmapInTail") +Texture_firstMipmapInTail :: #force_inline proc(self: ^Texture) -> NS.UInteger { + return msgSend(NS.UInteger, self, "firstMipmapInTail") +} +@(objc_type=Texture, objc_name="getBytesWithLevel") +Texture_getBytesWithLevel :: #force_inline proc(self: ^Texture, pixelBytes: rawptr, bytesPerRow: NS.UInteger, bytesPerImage: NS.UInteger, region: Region, level: NS.UInteger, slice: NS.UInteger) { + msgSend(nil, self, "getBytes:bytesPerRow:bytesPerImage:fromRegion:mipmapLevel:slice:", pixelBytes, bytesPerRow, bytesPerImage, region, level, slice) +} +@(objc_type=Texture, objc_name="getBytes") +Texture_getBytes :: #force_inline proc(self: ^Texture, pixelBytes: rawptr, bytesPerRow: NS.UInteger, region: Region, level: NS.UInteger) { + msgSend(nil, self, "getBytes:bytesPerRow:fromRegion:mipmapLevel:", pixelBytes, bytesPerRow, region, level) +} +@(objc_type=Texture, objc_name="height") +Texture_height :: #force_inline proc(self: ^Texture) -> NS.UInteger { + return msgSend(NS.UInteger, self, "height") +} +@(objc_type=Texture, objc_name="iosurface") +Texture_iosurface :: #force_inline proc(self: ^Texture) -> IOSurfaceRef { + return msgSend(IOSurfaceRef, self, "iosurface") +} +@(objc_type=Texture, objc_name="iosurfacePlane") +Texture_iosurfacePlane :: #force_inline proc(self: ^Texture) -> NS.UInteger { + return msgSend(NS.UInteger, self, "iosurfacePlane") +} +@(objc_type=Texture, objc_name="isFramebufferOnly") +Texture_isFramebufferOnly :: #force_inline proc(self: ^Texture) -> BOOL { + return msgSend(BOOL, self, "isFramebufferOnly") +} +@(objc_type=Texture, objc_name="isShareable") +Texture_isShareable :: #force_inline proc(self: ^Texture) -> BOOL { + return msgSend(BOOL, self, "isShareable") +} +@(objc_type=Texture, objc_name="isSparse") +Texture_isSparse :: #force_inline proc(self: ^Texture) -> BOOL { + return msgSend(BOOL, self, "isSparse") +} +@(objc_type=Texture, objc_name="mipmapLevelCount") +Texture_mipmapLevelCount :: #force_inline proc(self: ^Texture) -> NS.UInteger { + return msgSend(NS.UInteger, self, "mipmapLevelCount") +} +@(objc_type=Texture, objc_name="newRemoteTextureViewForDevice") +Texture_newRemoteTextureViewForDevice :: #force_inline proc(self: ^Texture, device: ^Device) -> ^Texture { + return msgSend(^Texture, self, "newRemoteTextureViewForDevice:", device) +} +@(objc_type=Texture, objc_name="newSharedTextureHandle") +Texture_newSharedTextureHandle :: #force_inline proc(self: ^Texture) -> ^SharedTextureHandle { + return msgSend(^SharedTextureHandle, self, "newSharedTextureHandle") +} +@(objc_type=Texture, objc_name="newTextureView") +Texture_newTextureView :: #force_inline proc(self: ^Texture, pixelFormat: PixelFormat) -> ^Texture { + return msgSend(^Texture, self, "newTextureViewWithPixelFormat:", pixelFormat) +} +@(objc_type=Texture, objc_name="newTextureViewWithLevels") +Texture_newTextureViewWithLevels :: #force_inline proc(self: ^Texture, pixelFormat: PixelFormat, textureType: TextureType, levelRange: NS.Range, sliceRange: NS.Range) -> ^Texture { + return msgSend(^Texture, self, "newTextureViewWithPixelFormat:textureType:levels:slices:", pixelFormat, textureType, levelRange, sliceRange) +} +@(objc_type=Texture, objc_name="newTextureViewWithLevelsAndSwizzle") +Texture_newTextureViewWithLevelsAndSwizzle :: #force_inline proc(self: ^Texture, pixelFormat: PixelFormat, textureType: TextureType, levelRange: NS.Range, sliceRange: NS.Range, swizzle: TextureSwizzleChannels) -> ^Texture { + return msgSend(^Texture, self, "newTextureViewWithPixelFormat:textureType:levels:slices:swizzle:", pixelFormat, textureType, levelRange, sliceRange, swizzle) +} +@(objc_type=Texture, objc_name="parentRelativeLevel") +Texture_parentRelativeLevel :: #force_inline proc(self: ^Texture) -> NS.UInteger { + return msgSend(NS.UInteger, self, "parentRelativeLevel") +} +@(objc_type=Texture, objc_name="parentRelativeSlice") +Texture_parentRelativeSlice :: #force_inline proc(self: ^Texture) -> NS.UInteger { + return msgSend(NS.UInteger, self, "parentRelativeSlice") +} +@(objc_type=Texture, objc_name="parentTexture") +Texture_parentTexture :: #force_inline proc(self: ^Texture) -> ^Texture { + return msgSend(^Texture, self, "parentTexture") +} +@(objc_type=Texture, objc_name="pixelFormat") +Texture_pixelFormat :: #force_inline proc(self: ^Texture) -> PixelFormat { + return msgSend(PixelFormat, self, "pixelFormat") +} +@(objc_type=Texture, objc_name="remoteStorageTexture") +Texture_remoteStorageTexture :: #force_inline proc(self: ^Texture) -> ^Texture { + return msgSend(^Texture, self, "remoteStorageTexture") +} +@(objc_type=Texture, objc_name="replaceRegionWithLevel") +Texture_replaceRegionWithLevel :: #force_inline proc(self: ^Texture, region: Region, level: NS.UInteger, slice: NS.UInteger, pixelBytes: rawptr, bytesPerRow: NS.UInteger, bytesPerImage: NS.UInteger) { + msgSend(nil, self, "replaceRegion:mipmapLevel:slice:withBytes:bytesPerRow:bytesPerImage:", region, level, slice, pixelBytes, bytesPerRow, bytesPerImage) +} +@(objc_type=Texture, objc_name="replaceRegion") +Texture_replaceRegion :: #force_inline proc(self: ^Texture, region: Region, level: NS.UInteger, pixelBytes: rawptr, bytesPerRow: NS.UInteger) { + msgSend(nil, self, "replaceRegion:mipmapLevel:withBytes:bytesPerRow:", region, level, pixelBytes, bytesPerRow) +} +@(objc_type=Texture, objc_name="rootResource") +Texture_rootResource :: #force_inline proc(self: ^Texture) -> ^Resource { + return msgSend(^Resource, self, "rootResource") +} +@(objc_type=Texture, objc_name="sampleCount") +Texture_sampleCount :: #force_inline proc(self: ^Texture) -> NS.UInteger { + return msgSend(NS.UInteger, self, "sampleCount") +} +@(objc_type=Texture, objc_name="swizzle") +Texture_swizzle :: #force_inline proc(self: ^Texture) -> TextureSwizzleChannels { + return msgSend(TextureSwizzleChannels, self, "swizzle") +} +@(objc_type=Texture, objc_name="tailSizeInBytes") +Texture_tailSizeInBytes :: #force_inline proc(self: ^Texture) -> NS.UInteger { + return msgSend(NS.UInteger, self, "tailSizeInBytes") +} +@(objc_type=Texture, objc_name="textureType") +Texture_textureType :: #force_inline proc(self: ^Texture) -> TextureType { + return msgSend(TextureType, self, "textureType") +} +@(objc_type=Texture, objc_name="usage") +Texture_usage :: #force_inline proc(self: ^Texture) -> TextureUsage { + return msgSend(TextureUsage, self, "usage") +} +@(objc_type=Texture, objc_name="width") +Texture_width :: #force_inline proc(self: ^Texture) -> NS.UInteger { + return msgSend(NS.UInteger, self, "width") +} + +//////////////////////////////////////////////////////////////////////////////// + +/* +Class: + VisibleFunctionTable +Class Methods: +Methods: + setFunction + setFunctions +*/ +@(objc_class="MTLVisibleFunctionTable") +VisibleFunctionTable :: struct { using _: Resource } + +@(objc_type=VisibleFunctionTable, objc_name="setFunction") +VisibleFunctionTable_setFunction :: #force_inline proc(self: ^VisibleFunctionTable, function: ^FunctionHandle, index: NS.UInteger) { + msgSend(nil, self, "setFunction:atIndex:", function, index) +} +@(objc_type=VisibleFunctionTable, objc_name="setFunctions") +VisibleFunctionTable_setFunctions :: #force_inline proc(self: ^VisibleFunctionTable, functions: []^FunctionHandle, range: NS.Range) { + msgSend(nil, self, "setFunctions:withRange:", raw_data(functions), range) +} + + + +// TODO: Entire FunctionStitching API (which appears not to be in been missed from the generator) + + + diff --git a/vendor/darwin/Metal/MetalEnums.odin b/vendor/darwin/Metal/MetalEnums.odin new file mode 100644 index 000000000..7d72483ff --- /dev/null +++ b/vendor/darwin/Metal/MetalEnums.odin @@ -0,0 +1,972 @@ +package objc_Metal + +import NS "vendor:darwin/Foundation" + +AccelerationStructureUsage :: distinct bit_set[AccelerationStructureUsageFlag; NS.UInteger] +AccelerationStructureUsageFlag :: enum NS.UInteger { + Refit = 0, + PreferFastBuild = 1, + ExtendedLimits = 2, +} + +AccelerationStructureInstanceOptions :: distinct bit_set[AccelerationStructureInstanceOption; u32] +AccelerationStructureInstanceOption :: enum u32 { + DisableTriangleCulling = 0, + TriangleFrontFacingWindingCounterClockwise = 1, + Opaque = 2, + NonOpaque = 3, +} + +MotionBorderMode :: enum u32 { + Clamp = 0, + Vanish = 1, +} + +AccelerationStructureInstanceDescriptorType :: enum NS.UInteger { + Default = 0, + UserID = 1, + Motion = 2, +} + + +DataType :: enum NS.UInteger { + None = 0, + Struct = 1, + Array = 2, + Float = 3, + Float2 = 4, + Float3 = 5, + Float4 = 6, + Float2x2 = 7, + Float2x3 = 8, + Float2x4 = 9, + Float3x2 = 10, + Float3x3 = 11, + Float3x4 = 12, + Float4x2 = 13, + Float4x3 = 14, + Float4x4 = 15, + Half = 16, + Half2 = 17, + Half3 = 18, + Half4 = 19, + Half2x2 = 20, + Half2x3 = 21, + Half2x4 = 22, + Half3x2 = 23, + Half3x3 = 24, + Half3x4 = 25, + Half4x2 = 26, + Half4x3 = 27, + Half4x4 = 28, + Int = 29, + Int2 = 30, + Int3 = 31, + Int4 = 32, + UInt = 33, + UInt2 = 34, + UInt3 = 35, + UInt4 = 36, + Short = 37, + Short2 = 38, + Short3 = 39, + Short4 = 40, + UShort = 41, + UShort2 = 42, + UShort3 = 43, + UShort4 = 44, + Char = 45, + Char2 = 46, + Char3 = 47, + Char4 = 48, + UChar = 49, + UChar2 = 50, + UChar3 = 51, + UChar4 = 52, + Bool = 53, + Bool2 = 54, + Bool3 = 55, + Bool4 = 56, + Texture = 58, + Sampler = 59, + Pointer = 60, + R8Unorm = 62, + R8Snorm = 63, + R16Unorm = 64, + R16Snorm = 65, + RG8Unorm = 66, + RG8Snorm = 67, + RG16Unorm = 68, + RG16Snorm = 69, + RGBA8Unorm = 70, + RGBA8Unorm_sRGB = 71, + RGBA8Snorm = 72, + RGBA16Unorm = 73, + RGBA16Snorm = 74, + RGB10A2Unorm = 75, + RG11B10Float = 76, + RGB9E5Float = 77, + RenderPipeline = 78, + ComputePipeline = 79, + IndirectCommandBuffer = 80, + Long = 81, + Long2 = 82, + Long3 = 83, + Long4 = 84, + ULong = 85, + ULong2 = 86, + ULong3 = 87, + ULong4 = 88, + VisibleFunctionTable = 115, + IntersectionFunctionTable = 116, + PrimitiveAccelerationStructure = 117, + InstanceAccelerationStructure = 118, +} + +ArgumentType :: enum NS.UInteger { + Buffer = 0, + ThreadgroupMemory = 1, + Texture = 2, + Sampler = 3, + ImageblockData = 16, + Imageblock = 17, + VisibleFunctionTable = 24, + PrimitiveAccelerationStructure = 25, + InstanceAccelerationStructure = 26, + IntersectionFunctionTable = 27, +} + +ArgumentAccess :: enum NS.UInteger { + ReadOnly = 0, + ReadWrite = 1, + WriteOnly = 2, +} + + +BinaryArchiveError :: enum NS.UInteger { + None = 0, + InvalidFile = 1, + UnexpectedElement = 2, + CompilationFailure = 3, +} + +BlitOptionFlag :: enum NS.UInteger { + DepthFromDepthStencil = 0, + StencilFromDepthStencil = 1, + RowLinearPVRTC = 2, +} +BlitOption :: distinct bit_set[BlitOptionFlag; NS.UInteger] + +CaptureError :: enum NS.Integer { + NotSupported = 1, + AlreadyCapturing = 2, + InvalidDescriptor = 3, +} + +CaptureDestination :: enum NS.Integer { + DeveloperTools = 1, + GPUTraceDocument = 2, +} + + +CommandBufferStatus :: enum NS.UInteger { + NotEnqueued = 0, + Enqueued = 1, + Committed = 2, + Scheduled = 3, + Completed = 4, + Error = 5, +} + +CommandBufferError :: enum NS.UInteger { + None = 0, + Timeout = 2, + PageFault = 3, + AccessRevoked = 4, + Blacklisted = 4, + NotPermitted = 7, + OutOfMemory = 8, + InvalidResource = 9, + Memoryless = 10, + DeviceRemoved = 11, + StackOverflow = 12, +} + +CommandBufferErrorOptionFlag :: enum NS.UInteger { + EncoderExecutionStatus = 0, +} +CommandBufferErrorOption :: distinct bit_set[CommandBufferErrorOptionFlag; NS.UInteger] + +CommandEncoderErrorState :: enum NS.Integer { + Unknown = 0, + Completed = 1, + Affected = 2, + Pending = 3, + Faulted = 4, +} + +CommandBufferHandler :: distinct rawptr + +DispatchType :: enum NS.UInteger { + Serial = 0, + Concurrent = 1, +} + +ResourceUsageFlag :: enum NS.UInteger { + Read = 0, + Write = 1, + Sample = 2, +} +ResourceUsage :: distinct bit_set[ResourceUsageFlag; NS.UInteger] + + +BarrierScopeFlag :: enum NS.UInteger { + Buffers = 0, + Textures = 1, + RenderTargets = 2, +} +BarrierScope :: distinct bit_set[BarrierScopeFlag; NS.UInteger] + + + +CounterSampleBufferError :: enum NS.Integer { + OutOfMemory = 0, + Invalid = 1, +} + +CompareFunction :: enum NS.UInteger { + Never = 0, + Less = 1, + Equal = 2, + LessEqual = 3, + Greater = 4, + NotEqual = 5, + GreaterEqual = 6, + Always = 7, +} + +StencilOperation :: enum NS.UInteger { + Keep = 0, + Zero = 1, + Replace = 2, + IncrementClamp = 3, + DecrementClamp = 4, + Invert = 5, + IncrementWrap = 6, + DecrementWrap = 7, +} + +FeatureSet :: enum NS.UInteger { + iOS_GPUFamily1_v1 = 0, + iOS_GPUFamily2_v1 = 1, + iOS_GPUFamily1_v2 = 2, + iOS_GPUFamily2_v2 = 3, + iOS_GPUFamily3_v1 = 4, + iOS_GPUFamily1_v3 = 5, + iOS_GPUFamily2_v3 = 6, + iOS_GPUFamily3_v2 = 7, + iOS_GPUFamily1_v4 = 8, + iOS_GPUFamily2_v4 = 9, + iOS_GPUFamily3_v3 = 10, + iOS_GPUFamily4_v1 = 11, + iOS_GPUFamily1_v5 = 12, + iOS_GPUFamily2_v5 = 13, + iOS_GPUFamily3_v4 = 14, + iOS_GPUFamily4_v2 = 15, + iOS_GPUFamily5_v1 = 16, + macOS_GPUFamily1_v1 = 10000, + OSX_GPUFamily1_v1 = 10000, + macOS_GPUFamily1_v2 = 10001, + OSX_GPUFamily1_v2 = 10001, + OSX_ReadWriteTextureTier2 = 10002, + macOS_ReadWriteTextureTier2 = 10002, + macOS_GPUFamily1_v3 = 10003, + macOS_GPUFamily1_v4 = 10004, + macOS_GPUFamily2_v1 = 10005, + watchOS_GPUFamily1_v1 = 20000, + WatchOS_GPUFamily1_v1 = 20000, + watchOS_GPUFamily2_v1 = 20001, + WatchOS_GPUFamily2_v1 = 20001, + tvOS_GPUFamily1_v1 = 30000, + TVOS_GPUFamily1_v1 = 30000, + tvOS_GPUFamily1_v2 = 30001, + tvOS_GPUFamily1_v3 = 30002, + tvOS_GPUFamily2_v1 = 30003, + tvOS_GPUFamily1_v4 = 30004, + tvOS_GPUFamily2_v2 = 30005, +} + +GPUFamily :: enum NS.Integer { + Apple1 = 1001, + Apple2 = 1002, + Apple3 = 1003, + Apple4 = 1004, + Apple5 = 1005, + Apple6 = 1006, + Apple7 = 1007, + Apple8 = 1008, + Mac1 = 2001, + Mac2 = 2002, + Common1 = 3001, + Common2 = 3002, + Common3 = 3003, + MacCatalyst1 = 4001, + MacCatalyst2 = 4002, +} + +DeviceLocation :: enum NS.UInteger { + BuiltIn = 0, + Slot = 1, + External = 2, + Unspecified = NS.UIntegerMax, +} + +PipelineOptionFlag :: enum NS.UInteger { + ArgumentInfo = 0, + BufferTypeInfo = 1, + FailOnBinaryArchiveMiss = 2, +} +PipelineOption :: distinct bit_set[PipelineOptionFlag; NS.UInteger] + +ReadWriteTextureTier :: enum NS.UInteger { + TierNone = 0, + Tier1 = 1, + Tier2 = 2, +} + +ArgumentBuffersTier :: enum NS.UInteger { + Tier1 = 0, + Tier2 = 1, +} + +SparseTextureRegionAlignmentMode :: enum NS.UInteger { + Outward = 0, + Inward = 1, +} + +CounterSamplingPoint :: enum NS.UInteger { + AtStageBoundary = 0, + AtDrawBoundary = 1, + AtDispatchBoundary = 2, + AtTileDispatchBoundary = 3, + AtBlitBoundary = 4, +} + +DynamicLibraryError :: enum NS.UInteger { + None = 0, + InvalidFile = 1, + CompilationFailure = 2, + UnresolvedInstallName = 3, + DependencyLoadFailure = 4, + Unsupported = 5, +} + +FunctionOption :: enum NS.UInteger { + CompileToBinary = 0, +} +FunctionOptions :: distinct bit_set[FunctionOption; NS.UInteger] + + +FunctionLogType :: enum NS.UInteger { + Validation = 0, +} + +HeapType :: enum NS.Integer { + Automatic = 0, + Placement = 1, + Sparse = 2, +} + +IndirectCommandTypeFlag :: enum NS.UInteger { + Draw = 0, + DrawIndexed = 1, + DrawPatches = 2, + DrawIndexedPatches = 3, + ConcurrentDispatch = 5, + ConcurrentDispatchThreads = 6, +} +IndirectCommandType :: distinct bit_set[IndirectCommandTypeFlag; NS.UInteger] + +IntersectionFunctionSignatureFlag :: enum NS.UInteger { + Instancing = 0, + TriangleData = 1, + WorldSpaceData = 2, + InstanceMotion = 3, + PrimitiveMotion = 4, + ExtendedLimits = 5, +} +IntersectionFunctionSignature :: distinct bit_set[IntersectionFunctionSignatureFlag; NS.UInteger] + +PatchType :: enum NS.UInteger { + None = 0, + Triangle = 1, + Quad = 2, +} + +FunctionType :: enum NS.UInteger { + Vertex = 1, + Fragment = 2, + Kernel = 3, + Visible = 5, + Intersection = 6, +} + + +LanguageVersion :: enum NS.UInteger { + Version1_0 = 65536, + Version1_1 = 65537, + Version1_2 = 65538, + Version2_0 = 131072, + Version2_1 = 131073, + Version2_2 = 131074, + Version2_3 = 131075, + Version2_4 = 131076, +} + +LibraryType :: enum NS.Integer { + Executable = 0, + Dynamic = 1, +} + +LibraryError :: enum NS.UInteger { + Unsupported = 1, + CompileFailure = 3, + CompileWarning = 4, + FunctionNotFound = 5, + FileNotFound = 6, +} + +Mutability :: enum NS.UInteger { + Default = 0, + Mutable = 1, + Immutable = 2, +} + +PixelFormat :: enum NS.UInteger { + Invalid = 0, + A8Unorm = 1, + R8Unorm = 10, + R8Unorm_sRGB = 11, + R8Snorm = 12, + R8Uint = 13, + R8Sint = 14, + R16Unorm = 20, + R16Snorm = 22, + R16Uint = 23, + R16Sint = 24, + R16Float = 25, + RG8Unorm = 30, + RG8Unorm_sRGB = 31, + RG8Snorm = 32, + RG8Uint = 33, + RG8Sint = 34, + B5G6R5Unorm = 40, + A1BGR5Unorm = 41, + ABGR4Unorm = 42, + BGR5A1Unorm = 43, + R32Uint = 53, + R32Sint = 54, + R32Float = 55, + RG16Unorm = 60, + RG16Snorm = 62, + RG16Uint = 63, + RG16Sint = 64, + RG16Float = 65, + RGBA8Unorm = 70, + RGBA8Unorm_sRGB = 71, + RGBA8Snorm = 72, + RGBA8Uint = 73, + RGBA8Sint = 74, + BGRA8Unorm = 80, + BGRA8Unorm_sRGB = 81, + RGB10A2Unorm = 90, + RGB10A2Uint = 91, + RG11B10Float = 92, + RGB9E5Float = 93, + BGR10A2Unorm = 94, + RG32Uint = 103, + RG32Sint = 104, + RG32Float = 105, + RGBA16Unorm = 110, + RGBA16Snorm = 112, + RGBA16Uint = 113, + RGBA16Sint = 114, + RGBA16Float = 115, + RGBA32Uint = 123, + RGBA32Sint = 124, + RGBA32Float = 125, + BC1_RGBA = 130, + BC1_RGBA_sRGB = 131, + BC2_RGBA = 132, + BC2_RGBA_sRGB = 133, + BC3_RGBA = 134, + BC3_RGBA_sRGB = 135, + BC4_RUnorm = 140, + BC4_RSnorm = 141, + BC5_RGUnorm = 142, + BC5_RGSnorm = 143, + BC6H_RGBFloat = 150, + BC6H_RGBUfloat = 151, + BC7_RGBAUnorm = 152, + BC7_RGBAUnorm_sRGB = 153, + PVRTC_RGB_2BPP = 160, + PVRTC_RGB_2BPP_sRGB = 161, + PVRTC_RGB_4BPP = 162, + PVRTC_RGB_4BPP_sRGB = 163, + PVRTC_RGBA_2BPP = 164, + PVRTC_RGBA_2BPP_sRGB = 165, + PVRTC_RGBA_4BPP = 166, + PVRTC_RGBA_4BPP_sRGB = 167, + EAC_R11Unorm = 170, + EAC_R11Snorm = 172, + EAC_RG11Unorm = 174, + EAC_RG11Snorm = 176, + EAC_RGBA8 = 178, + EAC_RGBA8_sRGB = 179, + ETC2_RGB8 = 180, + ETC2_RGB8_sRGB = 181, + ETC2_RGB8A1 = 182, + ETC2_RGB8A1_sRGB = 183, + ASTC_4x4_sRGB = 186, + ASTC_5x4_sRGB = 187, + ASTC_5x5_sRGB = 188, + ASTC_6x5_sRGB = 189, + ASTC_6x6_sRGB = 190, + ASTC_8x5_sRGB = 192, + ASTC_8x6_sRGB = 193, + ASTC_8x8_sRGB = 194, + ASTC_10x5_sRGB = 195, + ASTC_10x6_sRGB = 196, + ASTC_10x8_sRGB = 197, + ASTC_10x10_sRGB = 198, + ASTC_12x10_sRGB = 199, + ASTC_12x12_sRGB = 200, + ASTC_4x4_LDR = 204, + ASTC_5x4_LDR = 205, + ASTC_5x5_LDR = 206, + ASTC_6x5_LDR = 207, + ASTC_6x6_LDR = 208, + ASTC_8x5_LDR = 210, + ASTC_8x6_LDR = 211, + ASTC_8x8_LDR = 212, + ASTC_10x5_LDR = 213, + ASTC_10x6_LDR = 214, + ASTC_10x8_LDR = 215, + ASTC_10x10_LDR = 216, + ASTC_12x10_LDR = 217, + ASTC_12x12_LDR = 218, + ASTC_4x4_HDR = 222, + ASTC_5x4_HDR = 223, + ASTC_5x5_HDR = 224, + ASTC_6x5_HDR = 225, + ASTC_6x6_HDR = 226, + ASTC_8x5_HDR = 228, + ASTC_8x6_HDR = 229, + ASTC_8x8_HDR = 230, + ASTC_10x5_HDR = 231, + ASTC_10x6_HDR = 232, + ASTC_10x8_HDR = 233, + ASTC_10x10_HDR = 234, + ASTC_12x10_HDR = 235, + ASTC_12x12_HDR = 236, + GBGR422 = 240, + BGRG422 = 241, + Depth16Unorm = 250, + Depth32Float = 252, + Stencil8 = 253, + Depth24Unorm_Stencil8 = 255, + Depth32Float_Stencil8 = 260, + X32_Stencil8 = 261, + X24_Stencil8 = 262, + BGRA10_XR = 552, + BGRA10_XR_sRGB = 553, + BGR10_XR = 554, + BGR10_XR_sRGB = 555, +} + + +PrimitiveType :: enum NS.UInteger { + Point = 0, + Line = 1, + LineStrip = 2, + Triangle = 3, + TriangleStrip = 4, +} + +VisibilityResultMode :: enum NS.UInteger { + Disabled = 0, + Boolean = 1, + Counting = 2, +} + +CullMode :: enum NS.UInteger { + None = 0, + Front = 1, + Back = 2, +} + +Winding :: enum NS.UInteger { + Clockwise = 0, + CounterClockwise = 1, +} + +DepthClipMode :: enum NS.UInteger { + Clip = 0, + Clamp = 1, +} + +TriangleFillMode :: enum NS.UInteger { + Fill = 0, + Lines = 1, +} + +RenderStage :: enum NS.UInteger { + Vertex = 0, + Fragment = 1, + Tile = 2, +} +RenderStages :: distinct bit_set[RenderStage; NS.UInteger] + + +LoadAction :: enum NS.UInteger { + DontCare = 0, + Load = 1, + Clear = 2, +} + +StoreAction :: enum NS.UInteger { + DontCare = 0, + Store = 1, + MultisampleResolve = 2, + StoreAndMultisampleResolve = 3, + Unknown = 4, + CustomSampleDepthStore = 5, +} + +StoreActionOption :: enum NS.UInteger { + CustomSamplePositions = 1, +} +StoreActionOptions :: distinct bit_set[StoreActionOption; NS.UInteger] + +MultisampleDepthResolveFilter :: enum NS.UInteger { + Sample0 = 0, + Min = 1, + Max = 2, +} + +MultisampleStencilResolveFilter :: enum NS.UInteger { + Sample0 = 0, + DepthResolvedSample = 1, +} + +BlendFactor :: enum NS.UInteger { + Zero = 0, + One = 1, + SourceColor = 2, + OneMinusSourceColor = 3, + SourceAlpha = 4, + OneMinusSourceAlpha = 5, + DestinationColor = 6, + OneMinusDestinationColor = 7, + DestinationAlpha = 8, + OneMinusDestinationAlpha = 9, + SourceAlphaSaturated = 10, + BlendColor = 11, + OneMinusBlendColor = 12, + BlendAlpha = 13, + OneMinusBlendAlpha = 14, + Source1Color = 15, + OneMinusSource1Color = 16, + Source1Alpha = 17, + OneMinusSource1Alpha = 18, +} + +BlendOperation :: enum NS.UInteger { + Add = 0, + Subtract = 1, + ReverseSubtract = 2, + Min = 3, + Max = 4, +} + +ColorWriteMaskFlag :: enum NS.UInteger { + Alpha = 0, + Blue = 1, + Green = 2, + Red = 3, +} +ColorWriteMask :: distinct bit_set[ColorWriteMaskFlag; NS.UInteger] +ColorWriteMaskAll :: ColorWriteMask{.Alpha, .Blue, .Green, .Red} + +PrimitiveTopologyClass :: enum NS.UInteger { + Unspecified = 0, + Point = 1, + Line = 2, + Triangle = 3, +} + +TessellationPartitionMode :: enum NS.UInteger { + Pow2 = 0, + Integer = 1, + FractionalOdd = 2, + FractionalEven = 3, +} + +TessellationFactorStepFunction :: enum NS.UInteger { + Constant = 0, + PerPatch = 1, + PerInstance = 2, + PerPatchAndPerInstance = 3, +} + +TessellationFactorFormat :: enum NS.UInteger { + Half = 0, +} + +TessellationControlPointIndexType :: enum NS.UInteger { + None = 0, + UInt16 = 1, + UInt32 = 2, +} + +PurgeableState :: enum NS.UInteger { + KeepCurrent = 1, + NonVolatile = 2, + Volatile = 3, + Empty = 4, +} + +CPUCacheMode :: enum NS.UInteger { + DefaultCache = 0, + WriteCombined = 1, +} + +StorageMode :: enum NS.UInteger { + Shared = 0, + Managed = 1, + Private = 2, + Memoryless = 3, +} + +HazardTrackingMode :: enum NS.UInteger { + Default = 0, + Untracked = 1, + Tracked = 2, +} + +ResourceOption :: enum NS.UInteger { + CPUCacheModeWriteCombined = 0, + StorageModeManaged = 4, + StorageModePrivate = 5, + HazardTrackingModeUntracked = 8, + HazardTrackingModeTracked = 9, +} +ResourceOptions :: distinct bit_set[ResourceOption; NS.UInteger] + +ResourceStorageModeShared :: ResourceOptions{} +ResourceHazardTrackingModeDefault :: ResourceOptions{} +ResourceCPUCacheModeDefaultCache :: ResourceOptions{} +ResourceOptionCPUCacheModeDefault :: ResourceOptions{} +ResourceStorageModeMemoryless :: ResourceOptions{.StorageModeManaged, .StorageModePrivate} + +SparseTextureMappingMode :: enum NS.UInteger { + Map = 0, + Unmap = 1, +} + +SamplerMinMagFilter :: enum NS.UInteger { + Nearest = 0, + Linear = 1, +} + +SamplerMipFilter :: enum NS.UInteger { + NotMipmapped = 0, + Nearest = 1, + Linear = 2, +} + +SamplerAddressMode :: enum NS.UInteger { + ClampToEdge = 0, + MirrorClampToEdge = 1, + Repeat = 2, + MirrorRepeat = 3, + ClampToZero = 4, + ClampToBorderColor = 5, +} + +SamplerBorderColor :: enum NS.UInteger { + TransparentBlack = 0, + OpaqueBlack = 1, + OpaqueWhite = 2, +} + + +AttributeFormat :: enum NS.UInteger { + Invalid = 0, + UChar2 = 1, + UChar3 = 2, + UChar4 = 3, + Char2 = 4, + Char3 = 5, + Char4 = 6, + UChar2Normalized = 7, + UChar3Normalized = 8, + UChar4Normalized = 9, + Char2Normalized = 10, + Char3Normalized = 11, + Char4Normalized = 12, + UShort2 = 13, + UShort3 = 14, + UShort4 = 15, + Short2 = 16, + Short3 = 17, + Short4 = 18, + UShort2Normalized = 19, + UShort3Normalized = 20, + UShort4Normalized = 21, + Short2Normalized = 22, + Short3Normalized = 23, + Short4Normalized = 24, + Half2 = 25, + Half3 = 26, + Half4 = 27, + Float = 28, + Float2 = 29, + Float3 = 30, + Float4 = 31, + Int = 32, + Int2 = 33, + Int3 = 34, + Int4 = 35, + UInt = 36, + UInt2 = 37, + UInt3 = 38, + UInt4 = 39, + Int1010102Normalized = 40, + UInt1010102Normalized = 41, + UChar4Normalized_BGRA = 42, + UChar = 45, + Char = 46, + UCharNormalized = 47, + CharNormalized = 48, + UShort = 49, + Short = 50, + UShortNormalized = 51, + ShortNormalized = 52, + Half = 53, +} + +IndexType :: enum NS.UInteger { + UInt16 = 0, + UInt32 = 1, +} + +StepFunction :: enum NS.UInteger { + Constant = 0, + PerVertex = 1, + PerInstance = 2, + PerPatch = 3, + PerPatchControlPoint = 4, + ThreadPositionInGridX = 5, + ThreadPositionInGridY = 6, + ThreadPositionInGridXIndexed = 7, + ThreadPositionInGridYIndexed = 8, +} + +TextureType :: enum NS.UInteger { + Type1D = 0, + Type1DArray = 1, + Type2D = 2, + Type2DArray = 3, + Type2DMultisample = 4, + TypeCube = 5, + TypeCubeArray = 6, + Type3D = 7, + Type2DMultisampleArray = 8, + TypeTextureBuffer = 9, +} + +TextureSwizzle :: enum u8 { + Zero = 0, + One = 1, + Red = 2, + Green = 3, + Blue = 4, + Alpha = 5, +} + +TextureUsageFlag :: enum NS.UInteger { + ShaderRead = 0, + ShaderWrite = 1, + RenderTarget = 2, + PixelFormatView = 4, +} +TextureUsage :: distinct bit_set[TextureUsageFlag; NS.UInteger] + +TextureCompressionType :: enum NS.Integer { + Lossless = 0, + Lossy = 1, +} + +VertexFormat :: enum NS.UInteger { + Invalid = 0, + UChar2 = 1, + UChar3 = 2, + UChar4 = 3, + Char2 = 4, + Char3 = 5, + Char4 = 6, + UChar2Normalized = 7, + UChar3Normalized = 8, + UChar4Normalized = 9, + Char2Normalized = 10, + Char3Normalized = 11, + Char4Normalized = 12, + UShort2 = 13, + UShort3 = 14, + UShort4 = 15, + Short2 = 16, + Short3 = 17, + Short4 = 18, + UShort2Normalized = 19, + UShort3Normalized = 20, + UShort4Normalized = 21, + Short2Normalized = 22, + Short3Normalized = 23, + Short4Normalized = 24, + Half2 = 25, + Half3 = 26, + Half4 = 27, + Float = 28, + Float2 = 29, + Float3 = 30, + Float4 = 31, + Int = 32, + Int2 = 33, + Int3 = 34, + Int4 = 35, + UInt = 36, + UInt2 = 37, + UInt3 = 38, + UInt4 = 39, + Int1010102Normalized = 40, + UInt1010102Normalized = 41, + UChar4Normalized_BGRA = 42, + UChar = 45, + Char = 46, + UCharNormalized = 47, + CharNormalized = 48, + UShort = 49, + Short = 50, + UShortNormalized = 51, + ShortNormalized = 52, + Half = 53, +} + +VertexStepFunction :: enum NS.UInteger { + Constant = 0, + PerVertex = 1, + PerInstance = 2, + PerPatch = 3, + PerPatchControlPoint = 4, +} diff --git a/vendor/darwin/Metal/MetalErrors.odin b/vendor/darwin/Metal/MetalErrors.odin new file mode 100644 index 000000000..f214466e5 --- /dev/null +++ b/vendor/darwin/Metal/MetalErrors.odin @@ -0,0 +1,39 @@ +package objc_Metal + +import NS "vendor:darwin/Foundation" + +foreign import "system:Metal.framework" + +CommonCounter :: ^NS.String +CommonCounterSet :: ^NS.String +DeviceNotificationName :: ^NS.String + +foreign Metal { + @(linkage="weak") CommonCounterTimestamp: CommonCounter + @(linkage="weak") CommonCounterTessellationInputPatches: CommonCounter + @(linkage="weak") CommonCounterVertexInvocations: CommonCounter + @(linkage="weak") CommonCounterPostTessellationVertexInvocations: CommonCounter + @(linkage="weak") CommonCounterClipperInvocations: CommonCounter + @(linkage="weak") CommonCounterClipperPrimitivesOut: CommonCounter + @(linkage="weak") CommonCounterFragmentInvocations: CommonCounter + @(linkage="weak") CommonCounterFragmentsPassed: CommonCounter + @(linkage="weak") CommonCounterComputeKernelInvocations: CommonCounter + @(linkage="weak") CommonCounterTotalCycles: CommonCounter + @(linkage="weak") CommonCounterVertexCycles: CommonCounter + @(linkage="weak") CommonCounterTessellationCycles: CommonCounter + @(linkage="weak") CommonCounterPostTessellationVertexCycles: CommonCounter + @(linkage="weak") CommonCounterFragmentCycles: CommonCounter + @(linkage="weak") CommonCounterRenderTargetWriteCycles: CommonCounter +} + +foreign Metal { + @(linkage="weak") CommonCounterSetTimestamp: CommonCounterSet + @(linkage="weak") CommonCounterSetStageUtilization: CommonCounterSet + @(linkage="weak") CommonCounterSetStatistic: CommonCounterSet +} + +foreign Metal { + @(linkage="weak") DeviceWasAddedNotification: DeviceNotificationName + @(linkage="weak") DeviceRemovalRequestedNotification: DeviceNotificationName + @(linkage="weak") DeviceWasRemovedNotification: DeviceNotificationName +} \ No newline at end of file diff --git a/vendor/darwin/Metal/MetalProcedures.odin b/vendor/darwin/Metal/MetalProcedures.odin new file mode 100644 index 000000000..ca8fb1aea --- /dev/null +++ b/vendor/darwin/Metal/MetalProcedures.odin @@ -0,0 +1,19 @@ +package objc_Metal + +import NS "vendor:darwin/Foundation" + +@(require) +foreign import "system:Metal.framework" + +@(default_calling_convention="c", link_prefix="MTL") +foreign Metal { + CopyAllDevices :: proc() -> ^NS.Array --- + CopyAllDevicesWithObserver :: proc(observer: ^id, handler: DeviceNotificationHandler) -> ^NS.Array --- + CreateSystemDefaultDevice :: proc() -> ^Device --- + RemoveDeviceObserver :: proc(observer: id) --- +} + + +new :: proc($T: typeid) -> ^T where intrinsics.type_is_subtype_of(T, NS.Object) { + return T.alloc()->init() +} \ No newline at end of file diff --git a/vendor/darwin/Metal/MetalTypes.odin b/vendor/darwin/Metal/MetalTypes.odin new file mode 100644 index 000000000..cc9d25ca0 --- /dev/null +++ b/vendor/darwin/Metal/MetalTypes.odin @@ -0,0 +1,199 @@ +package objc_Metal + +import NS "vendor:darwin/Foundation" +import "core:intrinsics" + +BOOL :: NS.BOOL +id :: ^NS.Object + +CFTimeInterval :: NS.TimeInterval + +IOSurfaceRef :: distinct rawptr + +dispatch_queue_t :: id +dispatch_data_t :: id + +@(private) +msgSend :: intrinsics.objc_send + +AccelerationStructureInstanceDescriptor :: struct { + transformationMatrix: PackedFloat4x3, + options: AccelerationStructureInstanceOptions, + mask: u32, + intersectionFunctionTableOffset: u32, + accelerationStructureIndex: u32, +} + +AccelerationStructureSizes :: struct { + accelerationStructureSize: NS.Integer, + buildScratchBufferSize: NS.Integer, + refitScratchBufferSize: NS.Integer, +} + +AxisAlignedBoundingBox :: struct { + min: PackedFloat3, + max: PackedFloat3, +} + +ClearColor :: struct { + red: f64, + green: f64, + blue: f64, + alpha: f64, +} + +Coordinate2D :: struct { + x: f32, + y: f32, +} + +CounterResultStageUtilization :: struct { + totalCycles: u64, + vertexCycles: u64, + tessellationCycles: u64, + postTessellationVertexCycles: u64, + fragmentCycles: u64, + renderTargetCycles: u64, +} + +CounterResultStatistic :: struct { + tessellationInputPatches: u64, + vertexInvocations: u64, + postTessellationVertexInvocations: u64, + clipperInvocations: u64, + clipperPrimitivesOut: u64, + fragmentInvocations: u64, + fragmentsPassed: u64, + computeKernelInvocations: u64, +} + +CounterResultTimestamp :: struct { + timestamp: u64, +} + +DispatchThreadgroupsIndirectArguments :: struct { + threadgroupsPerGrid: [3]u32, +} + +DrawIndexedPrimitivesIndirectArguments :: struct { + indexCount: u32, + instanceCount: u32, + indexStart: u32, + baseVertex: i32, + baseInstance: u32, +} + +DrawPatchIndirectArguments :: struct { + patchCount: u32, + instanceCount: u32, + patchStart: u32, + baseInstance: u32, +} + +DrawPrimitivesIndirectArguments :: struct { + vertexCount: u32, + instanceCount: u32, + vertexStart: u32, + baseInstance: u32, +} + +IndirectCommandBufferExecutionRange :: struct { + location: u32, + length: u32, +} + +MapIndirectArguments :: struct { + regionOriginX: u32, + regionOriginY: u32, + regionOriginZ: u32, + regionSizeWidth: u32, + regionSizeHeight: u32, + regionSizeDepth: u32, + mipMapLevel: u32, + sliceId: u32, +} + +Origin :: distinct [3]NS.Integer + +PackedFloat3 :: distinct [3]f32 + +PackedFloat4x3 :: struct { + columns: [4]PackedFloat3, +} + +QuadTessellationFactorsHalf :: struct { + edgeTessellationFactor: [4]u16, + insideTessellationFactor: [2]u16, +} + +Region :: struct { + origin: Origin, + size: Size, +} + +SamplePosition :: distinct [2]f32 + +ScissorRect :: struct { + x: NS.Integer, + y: NS.Integer, + width: NS.Integer, + height: NS.Integer, +} + +Size :: struct { + width: NS.Integer, + height: NS.Integer, + depth: NS.Integer, +} + +SizeAndAlign :: struct { + size: NS.UInteger, + align: NS.UInteger, +} + +StageInRegionIndirectArguments :: struct { + stageInOrigin: [3]u32, + stageInSize: [3]u32, +} + +TextureSwizzleChannels :: struct { + red: TextureSwizzle, + green: TextureSwizzle, + blue: TextureSwizzle, + alpha: TextureSwizzle, +} + +TriangleTessellationFactorsHalf :: struct { + edgeTessellationFactor: [3]u16, + insideTessellationFactor: u16, +} + +VertexAmplificationViewMapping :: struct { + viewportArrayIndexOffset: u32, + renderTargetArrayIndexOffset: u32, +} + +Viewport :: struct { + originX: f64, + originY: f64, + width: f64, + height: f64, + znear: f64, + zfar: f64, +} + +Timestamp :: distinct u64 + +DeviceNotificationHandler :: ^NS.Block +AutoreleasedComputePipelineReflection :: ^ComputePipelineReflection +AutoreleasedRenderPipelineReflection :: ^RenderPipelineReflection +NewLibraryCompletionHandler :: ^NS.Block +NewRenderPipelineStateCompletionHandler :: ^NS.Block +NewRenderPipelineStateWithReflectionCompletionHandler :: ^NS.Block +NewComputePipelineStateCompletionHandler :: ^NS.Block +NewComputePipelineStateWithReflectionCompletionHandler :: ^NS.Block +SharedEventNotificationBlock :: ^NS.Block + +DrawablePresentedHandler :: ^NS.Block + +AutoreleasedArgument :: ^Argument \ No newline at end of file diff --git a/vendor/darwin/Metal/README.md b/vendor/darwin/Metal/README.md new file mode 100644 index 000000000..e61d7f7aa --- /dev/null +++ b/vendor/darwin/Metal/README.md @@ -0,0 +1,155 @@ +## About + +**metal-odin** is a low overhead Odin interface for Metal that helps developers add Metal functionality to graphics applications that are written in Odin. **metal-odin** removes the need to create a shim and allows developers to call Metal functions directly from anywhere in their existing Odin code. + +## Highlights + +- Drop in Odin alternative interface to the Metal Objective-C headers. +- Direct mapping of all Metal Objective-C classes, constants, enums and bit_sets to Odin +- No measurable overhead compared to calling Metal Objective-C headers, due to inlining of Odin procedure calls. +- No usage of wrapper containers that require additional allocations. +- Identical header files and procedure/constant/enum availability for iOS, macOS and tvOS. +- Backwards compatibility: All `MTL.Device.supports...()` procedure check if their required selectors exist and automatically return `false` if not. +- String (`ErrorDomain`) constants are `@(linkage="weak")` and automatically set to `nil` if not available. + +## Memory Allocation Policy + +**metal-odin** follows the object allocation policies of Cocoa and Cocoa Touch. Understanding those rules is especially important when using `metal-odin`, as Odin values are not eligible for automatic reference counting (ARC). + +**metal-odin** objects are reference counted. To help convey and manage object lifecycles, the following conventions are observed: + +### AutoreleasePools and Objects + +Several methods that create temporary objects in **metal-odin** add them to an `AutoreleasePool` to help manage their lifetimes. In these situations, after **metal-odin** creates the object, it adds it to an `AutoreleasePool`, which will release its objects when you release (or drain) it. + +By adding temporary objects to an AutoreleasePool, you do not need to explicitly call `release()` to deallocate them. Instead, you can rely on the `AutoreleasePool` to implicitly manage those lifetimes. + +If you create an object with a method that does not begin with `alloc`, or `copy`, the creating method adds the object to an autorelease pool. + +The typical scope of an `AutoreleasePool` is one frame of rendering for the main thread of the program. When the thread returns control to the RunLoop (an object responsible for receiving input and events from the windowing system), the pool is *drained*, releasing its objects. + +You can create and manage additional `AutoreleasePool`s at smaller scopes to reduce your program's working set, and you are required to do so for any additional threads your program creates. + +If an object's lifecycle needs to be extended beyond the `AutoreleasePool`'s scope, you can claim ownership of it (avoiding its release beyond the pool's scope) by calling its `retain()` method before its pool is drained. In these cases, you will be responsible for making the appropriate `release()` call on the object after you no longer need it. + +You can find a more-detailed introduction to the memory management rules here: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html. + +For more details about the application's RunLoop, please find its documentation here: https://developer.apple.com/documentation/foundation/nsrunloop + +### Use and debug AutoreleasePools + +When you create an autoreleased object and there is no enclosing `AutoreleasePool`, the object is leaked. + +To prevent this, you normally create an `AutoreleasePool` in your program's `main` procedure, and in the entry procedure for every thread you create. You may also create additional `AutoreleasePool`s to avoid growing your program's high memory watermark when you create several autoreleased objects, such as when rendering. + +Use the Environment Variable `OBJC_DEBUG_MISSING_POOLS=YES` to print a runtime warning when an autoreleased object is leaked because no enclosing `AutoreleasePool` is available for its thread. + +You can also run `leaks --autoreleasePools` on a memgraph file or a process ID (macOS only) to view a listing of your program's `AutoreleasePool`s and all objects they contain. + +### nil + +Similar to Objective-C, it is legal to call any method, including `retain()` and `release()`, on `nil` "objects". While calling methods on `nil` still does incur in procedure call overhead, the effective result is equivalent of a NOP. + +Conversely, do not assume that because calling a method on a pointer did not result in a crash, that the pointed-to object is valid. + +## Adding `metal-odin` to a Project + +Simply `import MTL "core:sys/darwin/Metal"`. To ensure that the selector and class symbols are linked. + +```odin +import MTL "core:sys/darwin/Metal" +``` + +## Examples + +#### Creating the device + +###### Objective-C (with automatic reference counting) + +```objc +id< MTLDevice > device = MTLCreateSystemDefaultDevice(); + +// ... +``` + +###### Objective-C + +```objc +id< MTLDevice > device = MTLCreateSystemDefaultDevice(); + +// ... + +[device release]; +``` + +###### Odin + +```odin +device := MTL.CreateSystemDefaultDevice() + +// ... + +device->release() +``` + +#### Metal function calls map directly to Odin + +###### Objective-C (with automatic reference counting) + +```objc +MTLSamplerDescriptor* samplerDescriptor = [[MTLSamplerDescriptor alloc] init]; + +[samplerDescriptor setSAddressMode: MTLSamplerAddressModeRepeat]; +[samplerDescriptor setTAddressMode: MTLSamplerAddressModeRepeat]; +[samplerDescriptor setRAddressMode: MTLSamplerAddressModeRepeat]; +[samplerDescriptor setMagFilter: MTLSamplerMinMagFilterLinear]; +[samplerDescriptor setMinFilter: MTLSamplerMinMagFilterLinear]; +[samplerDescriptor setMipFilter: MTLSamplerMipFilterLinear]; +[samplerDescriptor setSupportArgumentBuffers: YES]; + +id< MTLSamplerState > samplerState = [device newSamplerStateWithDescriptor:samplerDescriptor]; +``` + +###### Objective-C + +```objc +MTLSamplerDescriptor* samplerDescriptor = [[MTLSamplerDescriptor alloc] init]; + +[samplerDescriptor setSAddressMode: MTLSamplerAddressModeRepeat]; +[samplerDescriptor setTAddressMode: MTLSamplerAddressModeRepeat]; +[samplerDescriptor setRAddressMode: MTLSamplerAddressModeRepeat]; +[samplerDescriptor setMagFilter: MTLSamplerMinMagFilterLinear]; +[samplerDescriptor setMinFilter: MTLSamplerMinMagFilterLinear]; +[samplerDescriptor setMipFilter: MTLSamplerMipFilterLinear]; +[samplerDescriptor setSupportArgumentBuffers: YES]; + +id< MTLSamplerState > samplerState = [device newSamplerStateWithDescriptor:samplerDescriptor]; + +[samplerDescriptor release]; + +// ... + +[samplerState release]; +``` + +###### Odin + +```odin +samplerDescriptor := MTL.SamplerDescriptor.alloc()->init() + +samplerDescriptor->setSAddressMode(.Repeat) +samplerDescriptor->setTAddressMode(.Repeat) +samplerDescriptor->setRAddressMode(.Repeat) +samplerDescriptor->setMagFilter(.Linear) +samplerDescriptor->setMinFilter(.Linear) +samplerDescriptor->setMipFilter(.Linear) +samplerDescriptor->setSupportArgumentBuffers(true) + +samplerState := device->newSamplerState(samplerDescriptor) + +samplerDescriptor->release() + +// ... + +samplerState->release() +``` diff --git a/vendor/darwin/MetalKit/MetalKit.odin b/vendor/darwin/MetalKit/MetalKit.odin new file mode 100644 index 000000000..90d147983 --- /dev/null +++ b/vendor/darwin/MetalKit/MetalKit.odin @@ -0,0 +1,259 @@ +package objc_MetalKit + +import NS "vendor:darwin/Foundation" +import MTL "vendor:darwin/Metal" +import CA "vendor:darwin/QuartzCore" +import "core:intrinsics" + +@(require) +foreign import "system:MetalKit.framework" + +@(private) +msgSend :: intrinsics.objc_send + +ColorSpaceRef :: struct {} + +ViewDelegate :: struct { + drawInMTKView: proc "c" (self: ^ViewDelegate, view: ^View), + drawableSizeWillChange: proc "c" (self: ^ViewDelegate, view: ^View, size: NS.Size), + + user_data: rawptr, +} + +@(objc_class="MTKView") +View :: struct {using _: NS.View} + +@(objc_type=View, objc_name="alloc", objc_is_class_method=true) +View_alloc :: proc() -> ^View { + return msgSend(^View, View, "alloc") +} +@(objc_type=View, objc_name="initWithFrame") +View_initWithFrame :: proc(self: ^View, frame: NS.Rect, device: ^MTL.Device) -> ^View { + return msgSend(^View, self, "initWithFrame:device:", frame, device) +} +@(objc_type=View, objc_name="initWithCoder") +View_initWithCoder :: proc(self: ^View, coder: ^NS.Coder) -> ^View { + return msgSend(^View, self, "initWithCoder:", coder) +} + +@(objc_type=View, objc_name="setDevice") +View_setDevice :: proc(self: ^View, device: ^MTL.Device) { + msgSend(nil, self, "setDevice:", device) +} +@(objc_type=View, objc_name="device") +View_device :: proc(self: ^View) -> ^MTL.Device { + return msgSend(^MTL.Device, self, "device") +} + +@(objc_type=View, objc_name="draw") +View_draw :: proc(self: ^View) { + msgSend(nil, self, "draw") +} + +@(objc_type=View, objc_name="setDelegate") +View_setDelegate :: proc(self: ^View, delegate: ^ViewDelegate) { + drawDispatch :: proc "c" (self: ^NS.Value, cmd: NS.SEL, view: ^View) { + del := (^ViewDelegate)(self->pointerValue()) + del->drawInMTKView(view) + } + drawableSizeWillChange :: proc "c" (self: ^NS.Value, cmd: NS.SEL, view: ^View, size: NS.Size) { + del := (^ViewDelegate)(self->pointerValue()) + del->drawableSizeWillChange(view, size) + } + + wrapper := NS.Value.valueWithPointer(delegate) + + NS.class_addMethod(intrinsics.objc_find_class("NSValue"), intrinsics.objc_find_selector("drawInMTKView:"), auto_cast drawDispatch, "v@:@") + + cbparams :: "v@:@{CGSize=ff}" when size_of(NS.Float) == size_of(f32) else "v@:@{CGSize=dd}" + NS.class_addMethod(intrinsics.objc_find_class("NSValue"), intrinsics.objc_find_selector("mtkView:drawableSizeWillChange:"), auto_cast drawableSizeWillChange, cbparams) + + msgSend(nil, self, "setDelegate:", wrapper) +} + +@(objc_type=View, objc_name="delegate") +View_delegate :: proc(self: ^View) -> ^ViewDelegate { + wrapper := msgSend(^NS.Value, self, "delegate") + if wrapper != nil { + return (^ViewDelegate)(wrapper->pointerValue()) + } + return nil +} + +@(objc_type=View, objc_name="currentDrawable") +View_currentDrawable :: proc(self: ^View) -> ^CA.MetalDrawable { + return msgSend(^CA.MetalDrawable, self, "currentDrawable") +} + +@(objc_type=View, objc_name="setFramebufferOnly") +View_setFramebufferOnly :: proc(self: ^View, framebufferOnly: bool) { + msgSend(nil, self, "setFramebufferOnly:", framebufferOnly) +} +@(objc_type=View, objc_name="framebufferOnly") +View_framebufferOnly :: proc(self: ^View) -> bool { + return msgSend(bool, self, "framebufferOnly") +} + +@(objc_type=View, objc_name="setDepthStencilAttachmentTextureUsage") +View_setDepthStencilAttachmentTextureUsage :: proc(self: ^View, textureUsage: MTL.TextureUsage) { + msgSend(nil, self, "setDepthStencilAttachmentTextureUsage:", textureUsage) +} +@(objc_type=View, objc_name="depthStencilAttachmentTextureUsage") +View_depthStencilAttachmentTextureUsage :: proc(self: ^View) -> MTL.TextureUsage { + return msgSend(MTL.TextureUsage, self, "depthStencilAttachmentTextureUsage") +} + +@(objc_type=View, objc_name="setMultisampleColorAttachmentTextureUsage") +View_setMultisampleColorAttachmentTextureUsage :: proc(self: ^View, textureUsage: MTL.TextureUsage) { + msgSend(nil, self, "setMultisampleColorAttachmentTextureUsage:", textureUsage) +} +@(objc_type=View, objc_name="multisampleColorAttachmentTextureUsage") +View_multisampleColorAttachmentTextureUsage :: proc(self: ^View) -> MTL.TextureUsage { + return msgSend(MTL.TextureUsage, self, "multisampleColorAttachmentTextureUsage") +} + +@(objc_type=View, objc_name="setPresentsWithTransaction") +View_setPresentsWithTransaction :: proc(self: ^View, presentsWithTransaction: bool) { + msgSend(nil, self, "setPresentsWithTransaction:", presentsWithTransaction) +} +@(objc_type=View, objc_name="presentsWithTransaction") +View_presentsWithTransaction :: proc(self: ^View) -> bool { + return msgSend(bool, self, "presentsWithTransaction") +} + +@(objc_type=View, objc_name="setColorPixelFormat") +View_setColorPixelFormat :: proc(self: ^View, colorPixelFormat: MTL.PixelFormat) { + msgSend(nil, self, "setColorPixelFormat:", colorPixelFormat) +} +@(objc_type=View, objc_name="colorPixelFormat") +View_colorPixelFormat :: proc(self: ^View) -> MTL.PixelFormat { + return msgSend(MTL.PixelFormat, self, "colorPixelFormat") +} + +@(objc_type=View, objc_name="setDepthStencilPixelFormat") +View_setDepthStencilPixelFormat :: proc(self: ^View, colorPixelFormat: MTL.PixelFormat) { + msgSend(nil, self, "setDepthStencilPixelFormat:", colorPixelFormat) +} +@(objc_type=View, objc_name="depthStencilPixelFormat") +View_depthStencilPixelFormat :: proc(self: ^View) -> MTL.PixelFormat { + return msgSend(MTL.PixelFormat, self, "depthStencilPixelFormat") +} + +@(objc_type=View, objc_name="setSampleCount") +View_setSampleCount :: proc(self: ^View, sampleCount: NS.UInteger) { + msgSend(nil, self, "setSampleCount:", sampleCount) +} +@(objc_type=View, objc_name="sampleCount") +View_sampleCount :: proc(self: ^View) -> NS.UInteger { + return msgSend(NS.UInteger, self, "sampleCount") +} + +@(objc_type=View, objc_name="setClearColor") +View_setClearColor :: proc(self: ^View, clearColor: MTL.ClearColor) { + msgSend(nil, self, "setClearColor:", clearColor) +} +@(objc_type=View, objc_name="clearColor") +View_clearColor :: proc(self: ^View) -> MTL.ClearColor { + return msgSend(MTL.ClearColor, self, "clearColor") +} + +@(objc_type=View, objc_name="setClearDepth") +View_setClearDepth :: proc(self: ^View, clearDepth: f64) { + msgSend(nil, self, "setClearDepth:", clearDepth) +} +@(objc_type=View, objc_name="clearDepth") +View_clearDepth :: proc(self: ^View) -> f64 { + return msgSend(f64, self, "clearDepth") +} + +@(objc_type=View, objc_name="setClearStencil") +View_setClearStencil :: proc(self: ^View, clearStencil: u32) { + msgSend(nil, self, "setClearStencil:", clearStencil) +} +@(objc_type=View, objc_name="clearStencil") +View_clearStencil :: proc(self: ^View) -> u32 { + return msgSend(u32, self, "clearStencil") +} + +@(objc_type=View, objc_name="depthStencilTexture") +View_depthStencilTexture :: proc(self: ^View) -> ^MTL.Texture { + return msgSend(^MTL.Texture, self, "depthStencilTexture") +} +@(objc_type=View, objc_name="multisampleColorTexture") +View_multisampleColorTexture :: proc(self: ^View) -> ^MTL.Texture { + return msgSend(^MTL.Texture, self, "multisampleColorTexture") +} + +@(objc_type=View, objc_name="releaseDrawables") +View_releaseDrawables :: proc(self: ^View) { + msgSend(nil, self, "releaseDrawables") +} + +@(objc_type=View, objc_name="currentRenderPassDescriptor") +View_currentRenderPassDescriptor :: proc(self: ^View) -> ^MTL.RenderPassDescriptor { + return msgSend(^MTL.RenderPassDescriptor, self, "currentRenderPassDescriptor") +} + +@(objc_type=View, objc_name="setPreferredFramesPerSecond") +View_setPreferredFramesPerSecond :: proc(self: ^View, preferredFramesPerSecond: NS.Integer) { + msgSend(nil, self, "setPreferredFramesPerSecond:", preferredFramesPerSecond) +} +@(objc_type=View, objc_name="preferredFramesPerSecond") +View_preferredFramesPerSecond :: proc(self: ^View) -> NS.Integer { + return msgSend(NS.Integer, self, "preferredFramesPerSecond") +} + +@(objc_type=View, objc_name="setEnableSetNeedsDisplay") +View_setEnableSetNeedsDisplay :: proc(self: ^View, enableSetNeedsDisplay: bool) { + msgSend(nil, self, "setEnableSetNeedsDisplay:", enableSetNeedsDisplay) +} +@(objc_type=View, objc_name="enableSetNeedsDisplay") +View_enableSetNeedsDisplay :: proc(self: ^View) -> bool { + return msgSend(bool, self, "enableSetNeedsDisplay") +} + +@(objc_type=View, objc_name="setAutoresizeDrawable") +View_setAutoresizeDrawable :: proc(self: ^View, autoresizeDrawable: bool) { + msgSend(nil, self, "setAutoresizeDrawable:", autoresizeDrawable) +} +@(objc_type=View, objc_name="autoresizeDrawable") +View_autoresizeDrawable :: proc(self: ^View) -> bool { + return msgSend(bool, self, "autoresizeDrawable") +} + +@(objc_type=View, objc_name="setDrawableSize") +View_setDrawableSize :: proc(self: ^View, drawableSize: NS.Size) { + msgSend(nil, self, "setDrawableSize:", drawableSize) +} +@(objc_type=View, objc_name="drawableSize") +View_drawableSize :: proc(self: ^View) -> NS.Size { + return msgSend(NS.Size, self, "drawableSize") +} + +@(objc_type=View, objc_name="preferredDrawableSize") +View_preferredDrawableSize :: proc(self: ^View) -> NS.Size { + return msgSend(NS.Size, self, "preferredDrawableSize") +} + +@(objc_type=View, objc_name="preferredDevice") +View_preferredDevice :: proc(self: ^View) -> ^MTL.Device { + return msgSend(^MTL.Device, self, "preferredDevice") +} + +@(objc_type=View, objc_name="setPaused") +View_setPaused :: proc(self: ^View, isPaused: bool) { + msgSend(nil, self, "setPaused:", isPaused) +} +@(objc_type=View, objc_name="isPaused") +View_isPaused :: proc(self: ^View) -> bool { + return msgSend(bool, self, "isPaused") +} + +@(objc_type=View, objc_name="setColorSpace") +View_setColorSpace :: proc(self: ^View, colorSpace: ColorSpaceRef) { + msgSend(nil, self, "setColorSpace:", colorSpace) +} +@(objc_type=View, objc_name="colorSpace") +View_colorSpace :: proc(self: ^View) -> ColorSpaceRef { + return msgSend(ColorSpaceRef, self, "colorSpace") +} diff --git a/vendor/darwin/QuartzCore/QuartzCore.odin b/vendor/darwin/QuartzCore/QuartzCore.odin new file mode 100644 index 000000000..fb6e04b07 --- /dev/null +++ b/vendor/darwin/QuartzCore/QuartzCore.odin @@ -0,0 +1,87 @@ +package objc_QuartzCore + +import NS "vendor:darwin/Foundation" +import MTL "vendor:darwin/Metal" +import "core:intrinsics" + +@(private) +msgSend :: intrinsics.objc_send + +@(objc_class="CAMetalLayer") +MetalLayer :: struct{ using _: NS.Layer} + +@(objc_type=MetalLayer, objc_name="layer", objc_is_class_method=true) +MetalLayer_layer :: proc() -> ^MetalLayer { + return msgSend(^MetalLayer, MetalLayer, "layer") +} + +@(objc_type=MetalLayer, objc_name="device") +MetalLayer_device :: proc(self: ^MetalLayer) -> ^MTL.Device { + return msgSend(^MTL.Device, self, "device") +} +@(objc_type=MetalLayer, objc_name="setDevice") +MetalLayer_setDevice :: proc(self: ^MetalLayer, device: ^MTL.Device) { + msgSend(nil, self, "setDevice:", device) +} + + +@(objc_type=MetalLayer, objc_name="opaque") +MetalLayer_opaque :: proc(self: ^MetalLayer) -> NS.BOOL { + return msgSend(NS.BOOL, self, "opaque") +} +@(objc_type=MetalLayer, objc_name="setOpaque") +MetalLayer_setOpaque :: proc(self: ^MetalLayer, opaque: NS.BOOL) { + msgSend(nil, self, "setOpaque:", opaque) +} + +@(objc_type=MetalLayer, objc_name="preferredDevice") +MetalLayer_preferredDevice :: proc(self: ^MetalLayer) -> ^MTL.Device { + return msgSend(^MTL.Device, self, "preferredDevice") +} +@(objc_type=MetalLayer, objc_name="pixelFormat") +MetalLayer_pixelFormat :: proc(self: ^MetalLayer) -> MTL.PixelFormat { + return msgSend(MTL.PixelFormat, self, "pixelFormat") +} +@(objc_type=MetalLayer, objc_name="setPixelFormat") +MetalLayer_setPixelFormat :: proc(self: ^MetalLayer, pixelFormat: MTL.PixelFormat) { + msgSend(nil, self, "setPixelFormat:", pixelFormat) +} + +@(objc_type=MetalLayer, objc_name="framebufferOnly") +MetalLayer_framebufferOnly :: proc(self: ^MetalLayer) -> NS.BOOL { + return msgSend(NS.BOOL, self, "framebufferOnly") +} +@(objc_type=MetalLayer, objc_name="setFramebufferOnly") +MetalLayer_setFramebufferOnly :: proc(self: ^MetalLayer, ok: NS.BOOL) { + msgSend(nil, self, "setFramebufferOnly:", ok) +} + +@(objc_type=MetalLayer, objc_name="frame") +MetalLayer_frame :: proc(self: ^MetalLayer) -> NS.Rect { + return msgSend(NS.Rect, self, "frame") +} +@(objc_type=MetalLayer, objc_name="setFrame") +MetalLayer_setFrame :: proc(self: ^MetalLayer, frame: NS.Rect) { + msgSend(nil, self, "setFrame:", frame) +} + + +@(objc_type=MetalLayer, objc_name="nextDrawable") +MetalLayer_nextDrawable :: proc(self: ^MetalLayer) -> ^MetalDrawable { + return msgSend(^MetalDrawable, self, "nextDrawable") +} + + + +@(objc_class="CAMetalDrawable") +MetalDrawable :: struct { using _: MTL.Drawable } + +@(objc_type=MetalDrawable, objc_name="layer") +MetalDrawable_layer :: proc(self: ^MetalDrawable) -> ^MetalLayer { + return msgSend(^MetalLayer, self, "layer") +} + +@(objc_type=MetalDrawable, objc_name="texture") +MetalDrawable_texture :: proc(self: ^MetalDrawable) -> ^MTL.Texture { + return msgSend(^MTL.Texture, self, "texture") +} \ No newline at end of file diff --git a/vendor/directx/d3d11/d3d11.odin b/vendor/directx/d3d11/d3d11.odin new file mode 100644 index 000000000..2adb7925a --- /dev/null +++ b/vendor/directx/d3d11/d3d11.odin @@ -0,0 +1,3627 @@ +package directx_d3d11 + +foreign import "system:d3d11.lib" + +import "../dxgi" +import "../d3d_compiler" + +IUnknown :: dxgi.IUnknown +IUnknown_VTable :: dxgi.IUnknown_VTable + +HANDLE :: dxgi.HANDLE +HMODULE :: dxgi.HMODULE +HRESULT :: dxgi.HRESULT +GUID :: dxgi.GUID +IID :: dxgi.IID +SIZE_T :: dxgi.SIZE_T +BOOL :: dxgi.BOOL + +RECT :: dxgi.RECT +SIZE :: dxgi.SIZE + +IModuleInstance :: d3d_compiler.ID3D11ModuleInstance +IBlob :: d3d_compiler.ID3DBlob +IModule :: d3d_compiler.ID3D11Module + +@(default_calling_convention="stdcall", link_prefix="D3D11") +foreign d3d11 { + CreateDevice :: proc( + pAdapter: ^dxgi.IAdapter, + DriverType: DRIVER_TYPE, + Software: HMODULE, + Flags: CREATE_DEVICE_FLAGS, + pFeatureLevels: ^FEATURE_LEVEL, + FeatureLevels: u32, + SDKVersion: u32, + ppDevice: ^^IDevice, + pFeatureLevel: ^FEATURE_LEVEL, + ppImmediateContext: ^^IDeviceContext, + ) -> HRESULT --- + CreateDeviceAndSwapChain :: proc( + pAdapter: ^dxgi.IAdapter, + DriverType: DRIVER_TYPE, + Software: HMODULE, + Flags: u32, + pFeatureLevels: ^FEATURE_LEVEL, + FeatureLevels: u32, + SDKVersion: u32, + pSwapChainDesc: ^dxgi.SWAP_CHAIN_DESC, + ppSwapChain: ^^dxgi.ISwapChain, + ppDevice: ^^IDevice, + pFeatureLevel: ^FEATURE_LEVEL, + ppImmediateContext: ^^IDeviceContext, + ) -> HRESULT --- +} + +foreign d3d11 { + WKPDID_D3DDebugObjectNameW: GUID + WKPDID_CommentStringW: GUID +} + +@(link_prefix="D3D_") +foreign d3d11 { + TEXTURE_LAYOUT_ROW_MAJOR: GUID + TEXTURE_LAYOUT_64KB_STANDARD_SWIZZLE: GUID +} + +@(link_prefix="D3D11_") +foreign d3d11 { + DECODER_PROFILE_MPEG2_MOCOMP: GUID + DECODER_PROFILE_MPEG2_IDCT: GUID + DECODER_PROFILE_MPEG2_VLD: GUID + DECODER_PROFILE_MPEG1_VLD: GUID + DECODER_PROFILE_MPEG2and1_VLD: GUID + DECODER_PROFILE_H264_MOCOMP_NOFGT: GUID + DECODER_PROFILE_H264_MOCOMP_FGT: GUID + DECODER_PROFILE_H264_IDCT_NOFGT: GUID + DECODER_PROFILE_H264_IDCT_FGT: GUID + DECODER_PROFILE_H264_VLD_NOFGT: GUID + DECODER_PROFILE_H264_VLD_FGT: GUID + DECODER_PROFILE_H264_VLD_WITHFMOASO_NOFGT: GUID + DECODER_PROFILE_H264_VLD_STEREO_PROGRESSIVE_NOFGT: GUID + DECODER_PROFILE_H264_VLD_STEREO_NOFGT: GUID + DECODER_PROFILE_H264_VLD_MULTIVIEW_NOFGT: GUID + DECODER_PROFILE_WMV8_POSTPROC: GUID + DECODER_PROFILE_WMV8_MOCOMP: GUID + DECODER_PROFILE_WMV9_POSTPROC: GUID + DECODER_PROFILE_WMV9_MOCOMP: GUID + DECODER_PROFILE_WMV9_IDCT: GUID + DECODER_PROFILE_VC1_POSTPROC: GUID + DECODER_PROFILE_VC1_MOCOMP: GUID + DECODER_PROFILE_VC1_IDCT: GUID + DECODER_PROFILE_VC1_VLD: GUID + DECODER_PROFILE_VC1_D2010: GUID + DECODER_PROFILE_MPEG4PT2_VLD_SIMPLE: GUID + DECODER_PROFILE_MPEG4PT2_VLD_ADVSIMPLE_NOGMC: GUID + DECODER_PROFILE_MPEG4PT2_VLD_ADVSIMPLE_GMC: GUID + DECODER_PROFILE_HEVC_VLD_MAIN: GUID + DECODER_PROFILE_HEVC_VLD_MAIN10: GUID + DECODER_PROFILE_VP9_VLD_PROFILE0: GUID + DECODER_PROFILE_VP9_VLD_10BIT_PROFILE2: GUID + DECODER_PROFILE_VP8_VLD: GUID + + CRYPTO_TYPE_AES128_CTR: GUID + DECODER_ENCRYPTION_HW_CENC: GUID + DECODER_BITSTREAM_ENCRYPTION_TYPE_CENC: GUID + DECODER_BITSTREAM_ENCRYPTION_TYPE_CBCS: GUID + KEY_EXCHANGE_HW_PROTECTION: GUID + + AUTHENTICATED_QUERY_PROTECTION: GUID + AUTHENTICATED_QUERY_CHANNEL_TYPE: GUID + AUTHENTICATED_QUERY_DEVICE_HANDLE: GUID + AUTHENTICATED_QUERY_CRYPTO_SESSION: GUID + AUTHENTICATED_QUERY_RESTRICTED_SHARED_RESOURCE_PROCESS_COUNT: GUID + AUTHENTICATED_QUERY_RESTRICTED_SHARED_RESOURCE_PROCESS: GUID + AUTHENTICATED_QUERY_UNRESTRICTED_PROTECTED_SHARED_RESOURCE_COUNT: GUID + AUTHENTICATED_QUERY_OUTPUT_ID_COUNT: GUID + AUTHENTICATED_QUERY_OUTPUT_ID: GUID + AUTHENTICATED_QUERY_ACCESSIBILITY_ATTRIBUTES: GUID + AUTHENTICATED_QUERY_ENCRYPTION_WHEN_ACCESSIBLE_GUID_COUNT: GUID + AUTHENTICATED_QUERY_ENCRYPTION_WHEN_ACCESSIBLE_GUID: GUID + AUTHENTICATED_QUERY_CURRENT_ENCRYPTION_WHEN_ACCESSIBLE: GUID + AUTHENTICATED_CONFIGURE_INITIALIZE: GUID + AUTHENTICATED_CONFIGURE_PROTECTION: GUID + AUTHENTICATED_CONFIGURE_CRYPTO_SESSION: GUID + AUTHENTICATED_CONFIGURE_SHARED_RESOURCE: GUID + AUTHENTICATED_CONFIGURE_ENCRYPTION_WHEN_ACCESSIBLE: GUID + + KEY_EXCHANGE_RSAES_OAEP: GUID +} + +FL9_1_REQ_TEXTURE1D_U_DIMENSION :: 2048 +FL9_3_REQ_TEXTURE1D_U_DIMENSION :: 4096 +FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION :: 2048 +FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION :: 4096 +FL9_1_REQ_TEXTURECUBE_DIMENSION :: 512 +FL9_3_REQ_TEXTURECUBE_DIMENSION :: 4096 +FL9_1_REQ_TEXTURE3D_U_V_OR_W_DIMENSION :: 256 +FL9_1_DEFAULT_MAX_ANISOTROPY :: 2 +FL9_1_IA_PRIMITIVE_MAX_COUNT :: 65535 +FL9_2_IA_PRIMITIVE_MAX_COUNT :: 1048575 +FL9_1_SIMULTANEOUS_RENDER_TARGET_COUNT :: 1 +FL9_3_SIMULTANEOUS_RENDER_TARGET_COUNT :: 4 +FL9_1_MAX_TEXTURE_REPEAT :: 128 +FL9_2_MAX_TEXTURE_REPEAT :: 2048 +FL9_3_MAX_TEXTURE_REPEAT :: 8192 + +_8BIT_INDEX_STRIP_CUT_VALUE :: 0xff +_16BIT_INDEX_STRIP_CUT_VALUE :: 0xffff +_32BIT_INDEX_STRIP_CUT_VALUE :: 0xffffffff + +ARRAY_AXIS_ADDRESS_RANGE_BIT_COUNT :: 9 + +CLIP_OR_CULL_DISTANCE_COUNT :: 8 +CLIP_OR_CULL_DISTANCE_ELEMENT_COUNT :: 2 + +COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT :: 14 +COMMONSHADER_CONSTANT_BUFFER_COMPONENTS :: 4 +COMMONSHADER_CONSTANT_BUFFER_COMPONENT_BIT_COUNT :: 32 +COMMONSHADER_CONSTANT_BUFFER_HW_SLOT_COUNT :: 15 +COMMONSHADER_CONSTANT_BUFFER_PARTIAL_UPDATE_EXTENTS_BYTE_ALIGNMENT :: 16 +COMMONSHADER_CONSTANT_BUFFER_REGISTER_COMPONENTS :: 4 +COMMONSHADER_CONSTANT_BUFFER_REGISTER_COUNT :: 15 +COMMONSHADER_CONSTANT_BUFFER_REGISTER_READS_PER_INST :: 1 +COMMONSHADER_CONSTANT_BUFFER_REGISTER_READ_PORTS :: 1 +COMMONSHADER_FLOWCONTROL_NESTING_LIMIT :: 64 +COMMONSHADER_IMMEDIATE_CONSTANT_BUFFER_REGISTER_COMPONENTS :: 4 +COMMONSHADER_IMMEDIATE_CONSTANT_BUFFER_REGISTER_COUNT :: 1 +COMMONSHADER_IMMEDIATE_CONSTANT_BUFFER_REGISTER_READS_PER_INST :: 1 +COMMONSHADER_IMMEDIATE_CONSTANT_BUFFER_REGISTER_READ_PORTS :: 1 +COMMONSHADER_IMMEDIATE_VALUE_COMPONENT_BIT_COUNT :: 32 +COMMONSHADER_INPUT_RESOURCE_REGISTER_COMPONENTS :: 1 +COMMONSHADER_INPUT_RESOURCE_REGISTER_COUNT :: 128 +COMMONSHADER_INPUT_RESOURCE_REGISTER_READS_PER_INST :: 1 +COMMONSHADER_INPUT_RESOURCE_REGISTER_READ_PORTS :: 1 +COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT :: 128 +COMMONSHADER_SAMPLER_REGISTER_COMPONENTS :: 1 +COMMONSHADER_SAMPLER_REGISTER_COUNT :: 16 +COMMONSHADER_SAMPLER_REGISTER_READS_PER_INST :: 1 +COMMONSHADER_SAMPLER_REGISTER_READ_PORTS :: 1 +COMMONSHADER_SAMPLER_SLOT_COUNT :: 16 +COMMONSHADER_SUBROUTINE_NESTING_LIMIT :: 32 +COMMONSHADER_TEMP_REGISTER_COMPONENTS :: 4 +COMMONSHADER_TEMP_REGISTER_COMPONENT_BIT_COUNT :: 32 +COMMONSHADER_TEMP_REGISTER_COUNT :: 4096 +COMMONSHADER_TEMP_REGISTER_READS_PER_INST :: 3 +COMMONSHADER_TEMP_REGISTER_READ_PORTS :: 3 +COMMONSHADER_TEXCOORD_RANGE_REDUCTION_MAX :: 10 +COMMONSHADER_TEXCOORD_RANGE_REDUCTION_MIN :: -10 +COMMONSHADER_TEXEL_OFFSET_MAX_NEGATIVE :: -8 +COMMONSHADER_TEXEL_OFFSET_MAX_POSITIVE :: 7 + +CS_4_X_BUCKET00_MAX_BYTES_TGSM_WRITABLE_PER_THREAD :: 256 +CS_4_X_BUCKET00_MAX_NUM_THREADS_PER_GROUP :: 64 +CS_4_X_BUCKET01_MAX_BYTES_TGSM_WRITABLE_PER_THREAD :: 240 +CS_4_X_BUCKET01_MAX_NUM_THREADS_PER_GROUP :: 68 +CS_4_X_BUCKET02_MAX_BYTES_TGSM_WRITABLE_PER_THREAD :: 224 +CS_4_X_BUCKET02_MAX_NUM_THREADS_PER_GROUP :: 72 +CS_4_X_BUCKET03_MAX_BYTES_TGSM_WRITABLE_PER_THREAD :: 208 +CS_4_X_BUCKET03_MAX_NUM_THREADS_PER_GROUP :: 76 +CS_4_X_BUCKET04_MAX_BYTES_TGSM_WRITABLE_PER_THREAD :: 192 +CS_4_X_BUCKET04_MAX_NUM_THREADS_PER_GROUP :: 84 +CS_4_X_BUCKET05_MAX_BYTES_TGSM_WRITABLE_PER_THREAD :: 176 +CS_4_X_BUCKET05_MAX_NUM_THREADS_PER_GROUP :: 92 +CS_4_X_BUCKET06_MAX_BYTES_TGSM_WRITABLE_PER_THREAD :: 160 +CS_4_X_BUCKET06_MAX_NUM_THREADS_PER_GROUP :: 100 +CS_4_X_BUCKET07_MAX_BYTES_TGSM_WRITABLE_PER_THREAD :: 144 +CS_4_X_BUCKET07_MAX_NUM_THREADS_PER_GROUP :: 112 +CS_4_X_BUCKET08_MAX_BYTES_TGSM_WRITABLE_PER_THREAD :: 128 +CS_4_X_BUCKET08_MAX_NUM_THREADS_PER_GROUP :: 128 +CS_4_X_BUCKET09_MAX_BYTES_TGSM_WRITABLE_PER_THREAD :: 112 +CS_4_X_BUCKET09_MAX_NUM_THREADS_PER_GROUP :: 144 +CS_4_X_BUCKET10_MAX_BYTES_TGSM_WRITABLE_PER_THREAD :: 96 +CS_4_X_BUCKET10_MAX_NUM_THREADS_PER_GROUP :: 168 +CS_4_X_BUCKET11_MAX_BYTES_TGSM_WRITABLE_PER_THREAD :: 80 +CS_4_X_BUCKET11_MAX_NUM_THREADS_PER_GROUP :: 204 +CS_4_X_BUCKET12_MAX_BYTES_TGSM_WRITABLE_PER_THREAD :: 64 +CS_4_X_BUCKET12_MAX_NUM_THREADS_PER_GROUP :: 256 +CS_4_X_BUCKET13_MAX_BYTES_TGSM_WRITABLE_PER_THREAD :: 48 +CS_4_X_BUCKET13_MAX_NUM_THREADS_PER_GROUP :: 340 +CS_4_X_BUCKET14_MAX_BYTES_TGSM_WRITABLE_PER_THREAD :: 32 +CS_4_X_BUCKET14_MAX_NUM_THREADS_PER_GROUP :: 512 +CS_4_X_BUCKET15_MAX_BYTES_TGSM_WRITABLE_PER_THREAD :: 16 +CS_4_X_BUCKET15_MAX_NUM_THREADS_PER_GROUP :: 768 +CS_4_X_DISPATCH_MAX_THREAD_GROUPS_IN_Z_DIMENSION :: 1 +CS_4_X_RAW_UAV_BYTE_ALIGNMENT :: 256 +CS_4_X_THREAD_GROUP_MAX_THREADS_PER_GROUP :: 768 +CS_4_X_THREAD_GROUP_MAX_X :: 768 +CS_4_X_THREAD_GROUP_MAX_Y :: 768 +CS_4_X_UAV_REGISTER_COUNT :: 1 + +CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION :: 65535 +CS_TGSM_REGISTER_COUNT :: 8192 +CS_TGSM_REGISTER_READS_PER_INST :: 1 +CS_TGSM_RESOURCE_REGISTER_COMPONENTS :: 1 +CS_TGSM_RESOURCE_REGISTER_READ_PORTS :: 1 +CS_THREADGROUPID_REGISTER_COMPONENTS :: 3 +CS_THREADGROUPID_REGISTER_COUNT :: 1 +CS_THREADIDINGROUPFLATTENED_REGISTER_COMPONENTS :: 1 +CS_THREADIDINGROUPFLATTENED_REGISTER_COUNT :: 1 +CS_THREADIDINGROUP_REGISTER_COMPONENTS :: 3 +CS_THREADIDINGROUP_REGISTER_COUNT :: 1 +CS_THREADID_REGISTER_COMPONENTS :: 3 +CS_THREADID_REGISTER_COUNT :: 1 +CS_THREAD_GROUP_MAX_THREADS_PER_GROUP :: 1024 +CS_THREAD_GROUP_MAX_X :: 1024 +CS_THREAD_GROUP_MAX_Y :: 1024 +CS_THREAD_GROUP_MAX_Z :: 64 +CS_THREAD_GROUP_MIN_X :: 1 +CS_THREAD_GROUP_MIN_Y :: 1 +CS_THREAD_GROUP_MIN_Z :: 1 +CS_THREAD_LOCAL_TEMP_REGISTER_POOL :: 16384 + +DEFAULT_BLEND_FACTOR_ALPHA :: 1.0 +DEFAULT_BLEND_FACTOR_BLUE :: 1.0 +DEFAULT_BLEND_FACTOR_GREEN :: 1.0 +DEFAULT_BLEND_FACTOR_RED :: 1.0 +DEFAULT_BORDER_COLOR_COMPONENT :: 0.0 +DEFAULT_DEPTH_BIAS :: 0 +DEFAULT_DEPTH_BIAS_CLAMP :: 0.0 +DEFAULT_MAX_ANISOTROPY :: 16 +DEFAULT_MIP_LOD_BIAS :: 0.0 +DEFAULT_RENDER_TARGET_ARRAY_INDEX :: 0 +DEFAULT_SAMPLE_MASK :: 0xffffffff +DEFAULT_SCISSOR_ENDX :: 0 +DEFAULT_SCISSOR_ENDY :: 0 +DEFAULT_SCISSOR_STARTX :: 0 +DEFAULT_SCISSOR_STARTY :: 0 +DEFAULT_SLOPE_SCALED_DEPTH_BIAS :: 0.0 +DEFAULT_STENCIL_READ_MASK :: 0xff +DEFAULT_STENCIL_REFERENCE :: 0 +DEFAULT_STENCIL_WRITE_MASK :: 0xff +DEFAULT_VIEWPORT_AND_SCISSORRECT_INDEX :: 0 +DEFAULT_VIEWPORT_HEIGHT :: 0 +DEFAULT_VIEWPORT_MAX_DEPTH :: 0.0 +DEFAULT_VIEWPORT_MIN_DEPTH :: 0.0 +DEFAULT_VIEWPORT_TOPLEFTX :: 0 +DEFAULT_VIEWPORT_TOPLEFTY :: 0 +DEFAULT_VIEWPORT_WIDTH :: 0 + +DS_INPUT_CONTROL_POINTS_MAX_TOTAL_SCALARS :: 3968 +DS_INPUT_CONTROL_POINT_REGISTER_COMPONENTS :: 4 +DS_INPUT_CONTROL_POINT_REGISTER_COMPONENT_BIT_COUNT :: 32 +DS_INPUT_CONTROL_POINT_REGISTER_COUNT :: 32 +DS_INPUT_CONTROL_POINT_REGISTER_READS_PER_INST :: 2 +DS_INPUT_CONTROL_POINT_REGISTER_READ_PORTS :: 1 +DS_INPUT_DOMAIN_POINT_REGISTER_COMPONENTS :: 3 +DS_INPUT_DOMAIN_POINT_REGISTER_COMPONENT_BIT_COUNT :: 32 +DS_INPUT_DOMAIN_POINT_REGISTER_COUNT :: 1 +DS_INPUT_DOMAIN_POINT_REGISTER_READS_PER_INST :: 2 +DS_INPUT_DOMAIN_POINT_REGISTER_READ_PORTS :: 1 +DS_INPUT_PATCH_CONSTANT_REGISTER_COMPONENTS :: 4 +DS_INPUT_PATCH_CONSTANT_REGISTER_COMPONENT_BIT_COUNT :: 32 +DS_INPUT_PATCH_CONSTANT_REGISTER_COUNT :: 32 +DS_INPUT_PATCH_CONSTANT_REGISTER_READS_PER_INST :: 2 +DS_INPUT_PATCH_CONSTANT_REGISTER_READ_PORTS :: 1 +DS_INPUT_PRIMITIVE_ID_REGISTER_COMPONENTS :: 1 +DS_INPUT_PRIMITIVE_ID_REGISTER_COMPONENT_BIT_COUNT :: 32 +DS_INPUT_PRIMITIVE_ID_REGISTER_COUNT :: 1 +DS_INPUT_PRIMITIVE_ID_REGISTER_READS_PER_INST :: 2 +DS_INPUT_PRIMITIVE_ID_REGISTER_READ_PORTS :: 1 +DS_OUTPUT_REGISTER_COMPONENTS :: 4 +DS_OUTPUT_REGISTER_COMPONENT_BIT_COUNT :: 32 +DS_OUTPUT_REGISTER_COUNT :: 32 + +FLOAT16_FUSED_TOLERANCE_IN_ULP :: 0.6 +FLOAT32_MAX :: 3.402823466e+38 +FLOAT32_TO_INTEGER_TOLERANCE_IN_ULP :: 0.6 +FLOAT_TO_SRGB_EXPONENT_DENOMINATOR :: 2.4 +FLOAT_TO_SRGB_EXPONENT_NUMERATOR :: 1.0 +FLOAT_TO_SRGB_OFFSET :: 0.055 +FLOAT_TO_SRGB_SCALE_1 :: 12.92 +FLOAT_TO_SRGB_SCALE_2 :: 1.055 +FLOAT_TO_SRGB_THRESHOLD :: 0.0031308 +FTOI_INSTRUCTION_MAX_INPUT :: 2147483647.999 +FTOI_INSTRUCTION_MIN_INPUT :: -2147483648.999 +FTOU_INSTRUCTION_MAX_INPUT :: 4294967295.999 +FTOU_INSTRUCTION_MIN_INPUT :: 0.0 +GS_INPUT_INSTANCE_ID_READS_PER_INST :: 2 +GS_INPUT_INSTANCE_ID_READ_PORTS :: 1 +GS_INPUT_INSTANCE_ID_REGISTER_COMPONENTS :: 1 +GS_INPUT_INSTANCE_ID_REGISTER_COMPONENT_BIT_COUNT :: 32 +GS_INPUT_INSTANCE_ID_REGISTER_COUNT :: 1 +GS_INPUT_PRIM_CONST_REGISTER_COMPONENTS :: 1 +GS_INPUT_PRIM_CONST_REGISTER_COMPONENT_BIT_COUNT :: 32 +GS_INPUT_PRIM_CONST_REGISTER_COUNT :: 1 +GS_INPUT_PRIM_CONST_REGISTER_READS_PER_INST :: 2 +GS_INPUT_PRIM_CONST_REGISTER_READ_PORTS :: 1 +GS_INPUT_REGISTER_COMPONENTS :: 4 +GS_INPUT_REGISTER_COMPONENT_BIT_COUNT :: 32 +GS_INPUT_REGISTER_COUNT :: 32 +GS_INPUT_REGISTER_READS_PER_INST :: 2 +GS_INPUT_REGISTER_READ_PORTS :: 1 +GS_INPUT_REGISTER_VERTICES :: 32 +GS_MAX_INSTANCE_COUNT :: 32 +GS_MAX_OUTPUT_VERTEX_COUNT_ACROSS_INSTANCES :: 1024 +GS_OUTPUT_ELEMENTS :: 32 +GS_OUTPUT_REGISTER_COMPONENTS :: 4 +GS_OUTPUT_REGISTER_COMPONENT_BIT_COUNT :: 32 +GS_OUTPUT_REGISTER_COUNT :: 32 +HS_CONTROL_POINT_PHASE_INPUT_REGISTER_COUNT :: 32 +HS_CONTROL_POINT_PHASE_OUTPUT_REGISTER_COUNT :: 32 +HS_CONTROL_POINT_REGISTER_COMPONENTS :: 4 +HS_CONTROL_POINT_REGISTER_COMPONENT_BIT_COUNT :: 32 +HS_CONTROL_POINT_REGISTER_READS_PER_INST :: 2 +HS_CONTROL_POINT_REGISTER_READ_PORTS :: 1 +HS_FORK_PHASE_INSTANCE_COUNT_UPPER_BOUND :: 0xffffffff +HS_INPUT_FORK_INSTANCE_ID_REGISTER_COMPONENTS :: 1 +HS_INPUT_FORK_INSTANCE_ID_REGISTER_COMPONENT_BIT_COUNT :: 32 +HS_INPUT_FORK_INSTANCE_ID_REGISTER_COUNT :: 1 +HS_INPUT_FORK_INSTANCE_ID_REGISTER_READS_PER_INST :: 2 +HS_INPUT_FORK_INSTANCE_ID_REGISTER_READ_PORTS :: 1 +HS_INPUT_JOIN_INSTANCE_ID_REGISTER_COMPONENTS :: 1 +HS_INPUT_JOIN_INSTANCE_ID_REGISTER_COMPONENT_BIT_COUNT :: 32 +HS_INPUT_JOIN_INSTANCE_ID_REGISTER_COUNT :: 1 +HS_INPUT_JOIN_INSTANCE_ID_REGISTER_READS_PER_INST :: 2 +HS_INPUT_JOIN_INSTANCE_ID_REGISTER_READ_PORTS :: 1 +HS_INPUT_PRIMITIVE_ID_REGISTER_COMPONENTS :: 1 +HS_INPUT_PRIMITIVE_ID_REGISTER_COMPONENT_BIT_COUNT :: 32 +HS_INPUT_PRIMITIVE_ID_REGISTER_COUNT :: 1 +HS_INPUT_PRIMITIVE_ID_REGISTER_READS_PER_INST :: 2 +HS_INPUT_PRIMITIVE_ID_REGISTER_READ_PORTS :: 1 +HS_JOIN_PHASE_INSTANCE_COUNT_UPPER_BOUND :: 0xffffffff +HS_MAXTESSFACTOR_LOWER_BOUND :: 1.0 +HS_MAXTESSFACTOR_UPPER_BOUND :: 64.0 +HS_OUTPUT_CONTROL_POINTS_MAX_TOTAL_SCALARS :: 3968 +HS_OUTPUT_CONTROL_POINT_ID_REGISTER_COMPONENTS :: 1 +HS_OUTPUT_CONTROL_POINT_ID_REGISTER_COMPONENT_BIT_COUNT :: 32 +HS_OUTPUT_CONTROL_POINT_ID_REGISTER_COUNT :: 1 +HS_OUTPUT_CONTROL_POINT_ID_REGISTER_READS_PER_INST :: 2 +HS_OUTPUT_CONTROL_POINT_ID_REGISTER_READ_PORTS :: 1 +HS_OUTPUT_PATCH_CONSTANT_REGISTER_COMPONENTS :: 4 +HS_OUTPUT_PATCH_CONSTANT_REGISTER_COMPONENT_BIT_COUNT :: 32 +HS_OUTPUT_PATCH_CONSTANT_REGISTER_COUNT :: 32 +HS_OUTPUT_PATCH_CONSTANT_REGISTER_READS_PER_INST :: 2 +HS_OUTPUT_PATCH_CONSTANT_REGISTER_READ_PORTS :: 1 +HS_OUTPUT_PATCH_CONSTANT_REGISTER_SCALAR_COMPONENTS :: 128 +IA_DEFAULT_INDEX_BUFFER_OFFSET_IN_BYTES :: 0 +IA_DEFAULT_PRIMITIVE_TOPOLOGY :: 0 +IA_DEFAULT_VERTEX_BUFFER_OFFSET_IN_BYTES :: 0 +IA_INDEX_INPUT_RESOURCE_SLOT_COUNT :: 1 +IA_INSTANCE_ID_BIT_COUNT :: 32 +IA_INTEGER_ARITHMETIC_BIT_COUNT :: 32 +IA_PATCH_MAX_CONTROL_POINT_COUNT :: 32 +IA_PRIMITIVE_ID_BIT_COUNT :: 32 +IA_VERTEX_ID_BIT_COUNT :: 32 +IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT :: 32 +IA_VERTEX_INPUT_STRUCTURE_ELEMENTS_COMPONENTS :: 128 +IA_VERTEX_INPUT_STRUCTURE_ELEMENT_COUNT :: 32 +INTEGER_DIVIDE_BY_ZERO_QUOTIENT :: 0xffffffff +INTEGER_DIVIDE_BY_ZERO_REMAINDER :: 0xffffffff +KEEP_RENDER_TARGETS_AND_DEPTH_STENCIL :: 0xffffffff +KEEP_UNORDERED_ACCESS_VIEWS :: 0xffffffff +LINEAR_GAMMA :: 1.0 +MAJOR_VERSION :: 11 +MAX_BORDER_COLOR_COMPONENT :: 1.0 +MAX_DEPTH :: 1.0 +MAX_MAXANISOTROPY :: 16 +MAX_MULTISAMPLE_SAMPLE_COUNT :: 32 +MAX_POSITION_VALUE :: 3.402823466e+34 +MAX_TEXTURE_DIMENSION_2_TO_EXP :: 17 +MINOR_VERSION :: 0 +MIN_BORDER_COLOR_COMPONENT :: 0.0 +MIN_DEPTH :: 0.0 +MIN_MAXANISOTROPY :: 0 +MIP_LOD_BIAS_MAX :: 15.99 +MIP_LOD_BIAS_MIN :: -16.0 +MIP_LOD_FRACTIONAL_BIT_COUNT :: 8 +MIP_LOD_RANGE_BIT_COUNT :: 8 +MULTISAMPLE_ANTIALIAS_LINE_WIDTH :: 1.4 +NONSAMPLE_FETCH_OUT_OF_RANGE_ACCESS_RESULT :: 0 +PIXEL_ADDRESS_RANGE_BIT_COUNT :: 15 +PRE_SCISSOR_PIXEL_ADDRESS_RANGE_BIT_COUNT :: 16 +PS_CS_UAV_REGISTER_COMPONENTS :: 1 +PS_CS_UAV_REGISTER_COUNT :: 8 +PS_CS_UAV_REGISTER_READS_PER_INST :: 1 +PS_CS_UAV_REGISTER_READ_PORTS :: 1 +PS_FRONTFACING_DEFAULT_VALUE :: 0xffffffff +PS_FRONTFACING_FALSE_VALUE :: 0 +PS_FRONTFACING_TRUE_VALUE :: 0xffffffff +PS_INPUT_REGISTER_COMPONENTS :: 4 +PS_INPUT_REGISTER_COMPONENT_BIT_COUNT :: 32 +PS_INPUT_REGISTER_COUNT :: 32 +PS_INPUT_REGISTER_READS_PER_INST :: 2 +PS_INPUT_REGISTER_READ_PORTS :: 1 +PS_LEGACY_PIXEL_CENTER_FRACTIONAL_COMPONENT :: 0.0 +PS_OUTPUT_DEPTH_REGISTER_COMPONENTS :: 1 +PS_OUTPUT_DEPTH_REGISTER_COMPONENT_BIT_COUNT :: 32 +PS_OUTPUT_DEPTH_REGISTER_COUNT :: 1 +PS_OUTPUT_MASK_REGISTER_COMPONENTS :: 1 +PS_OUTPUT_MASK_REGISTER_COMPONENT_BIT_COUNT :: 32 +PS_OUTPUT_MASK_REGISTER_COUNT :: 1 +PS_OUTPUT_REGISTER_COMPONENTS :: 4 +PS_OUTPUT_REGISTER_COMPONENT_BIT_COUNT :: 32 +PS_OUTPUT_REGISTER_COUNT :: 8 +PS_PIXEL_CENTER_FRACTIONAL_COMPONENT :: 0.5 +RAW_UAV_SRV_BYTE_ALIGNMENT :: 16 +REQ_BLEND_OBJECT_COUNT_PER_DEVICE :: 4096 +REQ_BUFFER_RESOURCE_TEXEL_COUNT_2_TO_EXP :: 27 +REQ_CONSTANT_BUFFER_ELEMENT_COUNT :: 4096 +REQ_DEPTH_STENCIL_OBJECT_COUNT_PER_DEVICE :: 4096 +REQ_DRAWINDEXED_INDEX_COUNT_2_TO_EXP :: 32 +REQ_DRAW_VERTEX_COUNT_2_TO_EXP :: 32 +REQ_FILTERING_HW_ADDRESSABLE_RESOURCE_DIMENSION :: 16384 +REQ_GS_INVOCATION_32BIT_OUTPUT_COMPONENT_LIMIT :: 1024 +REQ_IMMEDIATE_CONSTANT_BUFFER_ELEMENT_COUNT :: 4096 +REQ_MAXANISOTROPY :: 16 +REQ_MIP_LEVELS :: 15 +REQ_MULTI_ELEMENT_STRUCTURE_SIZE_IN_BYTES :: 2048 +REQ_RASTERIZER_OBJECT_COUNT_PER_DEVICE :: 4096 +REQ_RENDER_TO_BUFFER_WINDOW_WIDTH :: 16384 +REQ_RESOURCE_SIZE_IN_MEGABYTES_EXPRESSION_A_TERM :: 128 +REQ_RESOURCE_SIZE_IN_MEGABYTES_EXPRESSION_B_TERM :: 0.25 +REQ_RESOURCE_SIZE_IN_MEGABYTES_EXPRESSION_C_TERM :: 2048 +REQ_RESOURCE_VIEW_COUNT_PER_DEVICE_2_TO_EXP :: 20 +REQ_SAMPLER_OBJECT_COUNT_PER_DEVICE :: 4096 +REQ_TEXTURE1D_ARRAY_AXIS_DIMENSION :: 2048 +REQ_TEXTURE1D_U_DIMENSION :: 16384 +REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION :: 2048 +REQ_TEXTURE2D_U_OR_V_DIMENSION :: 16384 +REQ_TEXTURE3D_U_V_OR_W_DIMENSION :: 2048 +REQ_TEXTURECUBE_DIMENSION :: 16384 +RESINFO_INSTRUCTION_MISSING_COMPONENT_RETVAL :: 0 +SHADER_MAJOR_VERSION :: 5 +SHADER_MAX_INSTANCES :: 65535 +SHADER_MAX_INTERFACES :: 253 +SHADER_MAX_INTERFACE_CALL_SITES :: 4096 +SHADER_MAX_TYPES :: 65535 +SHADER_MINOR_VERSION :: 0 +SHIFT_INSTRUCTION_PAD_VALUE :: 0 +SHIFT_INSTRUCTION_SHIFT_VALUE_BIT_COUNT :: 5 +SIMULTANEOUS_RENDER_TARGET_COUNT :: 8 +SO_BUFFER_MAX_STRIDE_IN_BYTES :: 2048 +SO_BUFFER_MAX_WRITE_WINDOW_IN_BYTES :: 512 +SO_BUFFER_SLOT_COUNT :: 4 +SO_DDI_REGISTER_INDEX_DENOTING_GAP :: 0xffffffff +SO_NO_RASTERIZED_STREAM :: 0xffffffff +SO_OUTPUT_COMPONENT_COUNT :: 128 +SO_STREAM_COUNT :: 4 +SPEC_DATE_DAY :: 16 +SPEC_DATE_YEAR :: 2011 +SPEC_VERSION :: 1.07 +SRGB_GAMMA :: 2.2 +SRGB_TO_FLOAT_DENOMINATOR_1 :: 12.92 +SRGB_TO_FLOAT_DENOMINATOR_2 :: 1.055 +SRGB_TO_FLOAT_EXPONENT :: 2.4 +SRGB_TO_FLOAT_OFFSET :: 0.055 +SRGB_TO_FLOAT_THRESHOLD :: 0.04045 +SRGB_TO_FLOAT_TOLERANCE_IN_ULP :: 0.5 +STANDARD_COMPONENT_BIT_COUNT :: 32 +STANDARD_COMPONENT_BIT_COUNT_DOUBLED :: 64 +STANDARD_MAXIMUM_ELEMENT_ALIGNMENT_BYTE_MULTIPLE :: 4 +STANDARD_PIXEL_COMPONENT_COUNT :: 128 +STANDARD_PIXEL_ELEMENT_COUNT :: 32 +STANDARD_VECTOR_SIZE :: 4 +STANDARD_VERTEX_ELEMENT_COUNT :: 32 +STANDARD_VERTEX_TOTAL_COMPONENT_COUNT :: 64 +SUBPIXEL_FRACTIONAL_BIT_COUNT :: 8 +SUBTEXEL_FRACTIONAL_BIT_COUNT :: 8 +TESSELLATOR_MAX_EVEN_TESSELLATION_FACTOR :: 64 +TESSELLATOR_MAX_ISOLINE_DENSITY_TESSELLATION_FACTOR :: 64 +TESSELLATOR_MAX_ODD_TESSELLATION_FACTOR :: 63 +TESSELLATOR_MAX_TESSELLATION_FACTOR :: 64 +TESSELLATOR_MIN_EVEN_TESSELLATION_FACTOR :: 2 +TESSELLATOR_MIN_ISOLINE_DENSITY_TESSELLATION_FACTOR :: 1 +TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR :: 1 +TEXEL_ADDRESS_RANGE_BIT_COUNT :: 16 +UNBOUND_MEMORY_ACCESS_RESULT :: 0 +VIEWPORT_AND_SCISSORRECT_MAX_INDEX :: 15 +VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE :: 16 +VIEWPORT_BOUNDS_MAX :: 32767 +VIEWPORT_BOUNDS_MIN :: -32768 +VS_INPUT_REGISTER_COMPONENTS :: 4 +VS_INPUT_REGISTER_COMPONENT_BIT_COUNT :: 32 +VS_INPUT_REGISTER_COUNT :: 32 +VS_INPUT_REGISTER_READS_PER_INST :: 2 +VS_INPUT_REGISTER_READ_PORTS :: 1 +VS_OUTPUT_REGISTER_COMPONENTS :: 4 +VS_OUTPUT_REGISTER_COMPONENT_BIT_COUNT :: 32 +VS_OUTPUT_REGISTER_COUNT :: 32 +WHQL_CONTEXT_COUNT_FOR_RESOURCE_LIMIT :: 10 +WHQL_DRAWINDEXED_INDEX_COUNT_2_TO_EXP :: 25 +WHQL_DRAW_VERTEX_COUNT_2_TO_EXP :: 25 +_1_UAV_SLOT_COUNT :: 64 +_2_TILED_RESOURCE_TILE_SIZE_IN_BYTES :: 65536 +_4_VIDEO_DECODER_MAX_HISTOGRAM_COMPONENTS :: 4 +_4_VIDEO_DECODER_HISTOGRAM_OFFSET_ALIGNMENT :: 256 + +_FACD3D11 :: 0x87c + +APPEND_ALIGNED_ELEMENT :: 0xffffffff +FILTER_REDUCTION_TYPE_MASK :: 0x3 +FILTER_REDUCTION_TYPE_SHIFT :: 7 +FILTER_TYPE_MASK :: 0x3 +MIN_FILTER_SHIFT :: 4 +MAG_FILTER_SHIFT :: 2 +MIP_FILTER_SHIFT :: 0 +COMPARISON_FILTERING_BIT :: 0x80 +ANISOTROPIC_FILTERING_BIT :: 0x40 +SDK_VERSION :: 7 +RETURN_PARAMETER_INDEX :: -1 + +COMPONENT_MASK :: enum u32 { // TODO: make bit_set + X = 1, + Y = 2, + Z = 4, + W = 8, +} + +SHADER_REQUIRES :: enum u32 { // TODO: make bit_set + DOUBLES = 0x00000001, + EARLY_DEPTH_STENCIL = 0x00000002, + UAVS_AT_EVERY_STAGE = 0x00000004, + _64_UAVS = 0x00000008, + MINIMUM_PRECISION = 0x00000010, + _11_1_DOUBLE_EXTENSIONS = 0x00000020, + _11_1_SHADER_EXTENSIONS = 0x00000040, + LEVEL_9_COMPARISON_FILTERING = 0x00000080, + TILED_RESOURCES = 0x00000100, +} + +DRIVER_TYPE :: enum i32 { + UNKNOWN = 0, + HARDWARE = 1, + REFERENCE = 2, + NULL = 3, + SOFTWARE = 4, + WARP = 5, +} + +FEATURE_LEVEL :: enum i32 { + _1_0_CORE = 4096, + _9_1 = 37120, + _9_2 = 37376, + _9_3 = 37632, + _10_0 = 40960, + _10_1 = 41216, + _11_0 = 45056, + _11_1 = 45312, + _12_0 = 49152, + _12_1 = 49408, +} + +PRIMITIVE_TOPOLOGY :: enum i32 { + UNDEFINED = 0, + POINTLIST = 1, + LINELIST = 2, + LINESTRIP = 3, + TRIANGLELIST = 4, + TRIANGLESTRIP = 5, + LINELIST_ADJ = 10, + LINESTRIP_ADJ = 11, + TRIANGLELIST_ADJ = 12, + TRIANGLESTRIP_ADJ = 13, + _1_CONTROL_POINT_PATCHLIST = 33, + _2_CONTROL_POINT_PATCHLIST = 34, + _3_CONTROL_POINT_PATCHLIST = 35, + _4_CONTROL_POINT_PATCHLIST = 36, + _5_CONTROL_POINT_PATCHLIST = 37, + _6_CONTROL_POINT_PATCHLIST = 38, + _7_CONTROL_POINT_PATCHLIST = 39, + _8_CONTROL_POINT_PATCHLIST = 40, + _9_CONTROL_POINT_PATCHLIST = 41, + _10_CONTROL_POINT_PATCHLIST = 42, + _11_CONTROL_POINT_PATCHLIST = 43, + _12_CONTROL_POINT_PATCHLIST = 44, + _13_CONTROL_POINT_PATCHLIST = 45, + _14_CONTROL_POINT_PATCHLIST = 46, + _15_CONTROL_POINT_PATCHLIST = 47, + _16_CONTROL_POINT_PATCHLIST = 48, + _17_CONTROL_POINT_PATCHLIST = 49, + _18_CONTROL_POINT_PATCHLIST = 50, + _19_CONTROL_POINT_PATCHLIST = 51, + _20_CONTROL_POINT_PATCHLIST = 52, + _21_CONTROL_POINT_PATCHLIST = 53, + _22_CONTROL_POINT_PATCHLIST = 54, + _23_CONTROL_POINT_PATCHLIST = 55, + _24_CONTROL_POINT_PATCHLIST = 56, + _25_CONTROL_POINT_PATCHLIST = 57, + _26_CONTROL_POINT_PATCHLIST = 58, + _27_CONTROL_POINT_PATCHLIST = 59, + _28_CONTROL_POINT_PATCHLIST = 60, + _29_CONTROL_POINT_PATCHLIST = 61, + _30_CONTROL_POINT_PATCHLIST = 62, + _31_CONTROL_POINT_PATCHLIST = 63, + _32_CONTROL_POINT_PATCHLIST = 64, +} + +PRIMITIVE :: enum i32 { + UNDEFINED = 0, + POINT = 1, + LINE = 2, + TRIANGLE = 3, + LINE_ADJ = 6, + TRIANGLE_ADJ = 7, + _1_CONTROL_POINT_PATCH = 8, + _2_CONTROL_POINT_PATCH = 9, + _3_CONTROL_POINT_PATCH = 10, + _4_CONTROL_POINT_PATCH = 11, + _5_CONTROL_POINT_PATCH = 12, + _6_CONTROL_POINT_PATCH = 13, + _7_CONTROL_POINT_PATCH = 14, + _8_CONTROL_POINT_PATCH = 15, + _9_CONTROL_POINT_PATCH = 16, + _10_CONTROL_POINT_PATCH = 17, + _11_CONTROL_POINT_PATCH = 18, + _12_CONTROL_POINT_PATCH = 19, + _13_CONTROL_POINT_PATCH = 20, + _14_CONTROL_POINT_PATCH = 21, + _15_CONTROL_POINT_PATCH = 22, + _16_CONTROL_POINT_PATCH = 23, + _17_CONTROL_POINT_PATCH = 24, + _18_CONTROL_POINT_PATCH = 25, + _19_CONTROL_POINT_PATCH = 26, + _20_CONTROL_POINT_PATCH = 27, + _21_CONTROL_POINT_PATCH = 28, + _22_CONTROL_POINT_PATCH = 29, + _23_CONTROL_POINT_PATCH = 30, + _24_CONTROL_POINT_PATCH = 31, + _25_CONTROL_POINT_PATCH = 32, + _26_CONTROL_POINT_PATCH = 33, + _27_CONTROL_POINT_PATCH = 34, + _28_CONTROL_POINT_PATCH = 35, + _29_CONTROL_POINT_PATCH = 36, + _30_CONTROL_POINT_PATCH = 37, + _31_CONTROL_POINT_PATCH = 38, + _32_CONTROL_POINT_PATCH = 39, +} + +SRV_DIMENSION :: enum i32 { + UNKNOWN = 0, + BUFFER = 1, + TEXTURE1D = 2, + TEXTURE1DARRAY = 3, + TEXTURE2D = 4, + TEXTURE2DARRAY = 5, + TEXTURE2DMS = 6, + TEXTURE2DMSARRAY = 7, + TEXTURE3D = 8, + TEXTURECUBE = 9, + TEXTURECUBEARRAY = 10, + BUFFEREX = 11, +} + +PFN_DESTRUCTION_CALLBACK :: #type proc "c" (a0: rawptr) + + +ID3DDestructionNotifier_UUID_STRING :: "A06EB39A-50DA-425B-8C31-4EECD6C270F3" +ID3DDestructionNotifier_UUID := &IID{0xA06EB39A, 0x50DA, 0x425B, {0x8C, 0x31, 0x4E, 0xEC, 0xD6, 0xC2, 0x70, 0xF3}} +ID3DDestructionNotifier :: struct #raw_union { + #subtype iunknown: IUnknown, + using id3ddestructionnotifier_vtable: ^ID3DDestructionNotifier_VTable, +} +ID3DDestructionNotifier_VTable :: struct { + using iunknown_vtable: IUnknown_VTable, + RegisterDestructionCallback: proc "stdcall" (this: ^ID3DDestructionNotifier, callbackFn: PFN_DESTRUCTION_CALLBACK, pData: rawptr, pCallbackID: ^u32) -> HRESULT, + UnregisterDestructionCallback: proc "stdcall" (this: ^ID3DDestructionNotifier, callbackID: u32) -> HRESULT, +} + + +SHADER_VARIABLE_CLASS :: enum i32 { + SCALAR = 0, + VECTOR = 1, + MATRIX_ROWS = 2, + MATRIX_COLUMNS = 3, + OBJECT = 4, + STRUCT = 5, + INTERFACE_CLASS = 6, + INTERFACE_POINTER = 7, +} + +SHADER_VARIABLE_FLAGS :: enum u32 { // TODO: make bit_set + USERPACKED = 0x1, + USED = 0x2, + INTERFACE_POINTER = 0x4, + INTERFACE_PARAMETER = 0x8, +} + +SHADER_VARIABLE_TYPE :: enum i32 { + VOID = 0, + BOOL = 1, + INT = 2, + FLOAT = 3, + STRING = 4, + TEXTURE = 5, + TEXTURE1D = 6, + TEXTURE2D = 7, + TEXTURE3D = 8, + TEXTURECUBE = 9, + SAMPLER = 10, + SAMPLER1D = 11, + SAMPLER2D = 12, + SAMPLER3D = 13, + SAMPLERCUBE = 14, + PIXELSHADER = 15, + VERTEXSHADER = 16, + PIXELFRAGMENT = 17, + VERTEXFRAGMENT = 18, + UINT = 19, + UINT8 = 20, + GEOMETRYSHADER = 21, + RASTERIZER = 22, + DEPTHSTENCIL = 23, + BLEND = 24, + BUFFER = 25, + CBUFFER = 26, + TBUFFER = 27, + TEXTURE1DARRAY = 28, + TEXTURE2DARRAY = 29, + RENDERTARGETVIEW = 30, + DEPTHSTENCILVIEW = 31, + TEXTURE2DMS = 32, + TEXTURE2DMSARRAY = 33, + TEXTURECUBEARRAY = 34, + HULLSHADER = 35, + DOMAINSHADER = 36, + INTERFACE_POINTER = 37, + COMPUTESHADER = 38, + DOUBLE = 39, + RWTEXTURE1D = 40, + RWTEXTURE1DARRAY = 41, + RWTEXTURE2D = 42, + RWTEXTURE2DARRAY = 43, + RWTEXTURE3D = 44, + RWBUFFER = 45, + BYTEADDRESS_BUFFER = 46, + RWBYTEADDRESS_BUFFER = 47, + STRUCTURED_BUFFER = 48, + RWSTRUCTURED_BUFFER = 49, + APPEND_STRUCTURED_BUFFER = 50, + CONSUME_STRUCTURED_BUFFER = 51, + MIN8FLOAT = 52, + MIN10FLOAT = 53, + MIN16FLOAT = 54, + MIN12INT = 55, + MIN16INT = 56, + MIN16UINT = 57, +} + +SHADER_INPUT_FLAGS :: enum u32 { // TODO: make bit_set + USERPACKED = 0x1, + COMPARISON_SAMPLER = 0x2, + TEXTURE_COMPONENT_0 = 0x4, + TEXTURE_COMPONENT_1 = 0x8, + TEXTURE_COMPONENTS = 0xc, + UNUSED = 0x10, +} + +SHADER_INPUT_TYPE :: enum i32 { + CBUFFER = 0, + TBUFFER = 1, + TEXTURE = 2, + SAMPLER = 3, + UAV_RWTYPED = 4, + STRUCTURED = 5, + UAV_RWSTRUCTURED = 6, + BYTEADDRESS = 7, + UAV_RWBYTEADDRESS = 8, + UAV_APPEND_STRUCTURED = 9, + UAV_CONSUME_STRUCTURED = 10, + UAV_RWSTRUCTURED_WITH_COUNTER = 11, + RTACCELERATIONSTRUCTURE = 12, + UAV_FEEDBACKTEXTURE = 13, +} + +SHADER_CBUFFER_FLAGS :: enum u32 { // TODO: make bit_set + USERPACKED = 0x1, +} + +CBUFFER_TYPE :: enum i32 { + CBUFFER = 0, + TBUFFER = 1, + INTERFACE_POINTERS = 2, + RESOURCE_BIND_INFO = 3, +} + +NAME :: enum i32 { + UNDEFINED = 0, + POSITION = 1, + CLIP_DISTANCE = 2, + CULL_DISTANCE = 3, + RENDER_TARGET_ARRAY_INDEX = 4, + VIEWPORT_ARRAY_INDEX = 5, + VERTEX_ID = 6, + PRIMITIVE_ID = 7, + INSTANCE_ID = 8, + IS_FRONT_FACE = 9, + SAMPLE_INDEX = 10, + FINAL_QUAD_EDGE_TESSFACTOR = 11, + FINAL_QUAD_INSIDE_TESSFACTOR = 12, + FINAL_TRI_EDGE_TESSFACTOR = 13, + FINAL_TRI_INSIDE_TESSFACTOR = 14, + FINAL_LINE_DETAIL_TESSFACTOR = 15, + FINAL_LINE_DENSITY_TESSFACTOR = 16, + BARYCENTRICS = 23, + SHADINGRATE = 24, + CULLPRIMITIVE = 25, + TARGET = 64, + DEPTH = 65, + COVERAGE = 66, + DEPTH_GREATER_EQUAL = 67, + DEPTH_LESS_EQUAL = 68, + STENCIL_REF = 69, + INNER_COVERAGE = 70, +} + +RESOURCE_RETURN_TYPE :: enum i32 { + UNORM = 1, + SNORM = 2, + SINT = 3, + UINT = 4, + FLOAT = 5, + MIXED = 6, + DOUBLE = 7, + CONTINUED = 8, +} + +REGISTER_COMPONENT_TYPE :: enum i32 { + UNKNOWN = 0, + UINT32 = 1, + SINT32 = 2, + FLOAT32 = 3, +} + +TESSELLATOR_DOMAIN :: enum i32 { + UNDEFINED = 0, + ISOLINE = 1, + TRI = 2, + QUAD = 3, +} + +TESSELLATOR_PARTITIONING :: enum i32 { + UNDEFINED = 0, + INTEGER = 1, + POW2 = 2, + FRACTIONAL_ODD = 3, + FRACTIONAL_EVEN = 4, +} + +TESSELLATOR_OUTPUT_PRIMITIVE :: enum i32 { + UNDEFINED = 0, + POINT = 1, + LINE = 2, + TRIANGLE_CW = 3, + TRIANGLE_CCW = 4, +} + +MIN_PRECISION :: enum i32 { + DEFAULT = 0, + FLOAT_16 = 1, + FLOAT_2_8 = 2, + RESERVED = 3, + SINT_16 = 4, + UINT_16 = 5, + ANY_16 = 240, + ANY_10 = 241, +} + +INTERPOLATION_MODE :: enum i32 { + UNDEFINED = 0, + CONSTANT = 1, + LINEAR = 2, + LINEAR_CENTROID = 3, + LINEAR_NOPERSPECTIVE = 4, + LINEAR_NOPERSPECTIVE_CENTROID = 5, + LINEAR_SAMPLE = 6, + LINEAR_NOPERSPECTIVE_SAMPLE = 7, +} + +PARAMETER_FLAGS :: enum u32 { // TODO: make bit_set + NONE = 0x0, + IN = 0x1, + OUT = 0x2, +} + +CDEFAULT :: struct { + _: u8, +} + +INPUT_CLASSIFICATION :: enum i32 { + VERTEX_DATA = 0, + INSTANCE_DATA = 1, +} + +INPUT_ELEMENT_DESC :: struct { + SemanticName: cstring, + SemanticIndex: u32, + Format: dxgi.FORMAT, + InputSlot: u32, + AlignedByteOffset: u32, + InputSlotClass: INPUT_CLASSIFICATION, + InstanceDataStepRate: u32, +} + +FILL_MODE :: enum i32 { + WIREFRAME = 2, + SOLID = 3, +} + +CULL_MODE :: enum i32 { + NONE = 1, + FRONT = 2, + BACK = 3, +} + +SO_DECLARATION_ENTRY :: struct { + Stream: u32, + SemanticName: cstring, + SemanticIndex: u32, + StartComponent: u8, + ComponentCount: u8, + OutputSlot: u8, +} + +VIEWPORT :: struct { + TopLeftX: f32, + TopLeftY: f32, + Width: f32, + Height: f32, + MinDepth: f32, + MaxDepth: f32, +} + +DRAW_INSTANCED_INDIRECT_ARGS :: struct { + VertexCountPerInstance: u32, + InstanceCount: u32, + StartVertexLocation: u32, + StartInstanceLocation: u32, +} + +DRAW_INDEXED_INSTANCED_INDIRECT_ARGS :: struct { + IndexCountPerInstance: u32, + InstanceCount: u32, + StartIndexLocation: u32, + BaseVertexLocation: i32, + StartInstanceLocation: u32, +} + +RESOURCE_DIMENSION :: enum i32 { + UNKNOWN = 0, + BUFFER = 1, + TEXTURE1D = 2, + TEXTURE2D = 3, + TEXTURE3D = 4, +} + +DSV_DIMENSION :: enum i32 { + UNKNOWN = 0, + TEXTURE1D = 1, + TEXTURE1DARRAY = 2, + TEXTURE2D = 3, + TEXTURE2DARRAY = 4, + TEXTURE2DMS = 5, + TEXTURE2DMSARRAY = 6, +} + +RTV_DIMENSION :: enum i32 { + UNKNOWN = 0, + BUFFER = 1, + TEXTURE1D = 2, + TEXTURE1DARRAY = 3, + TEXTURE2D = 4, + TEXTURE2DARRAY = 5, + TEXTURE2DMS = 6, + TEXTURE2DMSARRAY = 7, + TEXTURE3D = 8, +} + +UAV_DIMENSION :: enum i32 { + UNKNOWN = 0, + BUFFER = 1, + TEXTURE1D = 2, + TEXTURE1DARRAY = 3, + TEXTURE2D = 4, + TEXTURE2DARRAY = 5, + TEXTURE3D = 8, +} + +USAGE :: enum i32 { + DEFAULT = 0, + IMMUTABLE = 1, + DYNAMIC = 2, + STAGING = 3, +} + +BIND_FLAG :: enum u32 { // TODO: make bit_set + VERTEX_BUFFER = 0x1, + INDEX_BUFFER = 0x2, + CONSTANT_BUFFER = 0x4, + SHADER_RESOURCE = 0x8, + STREAM_OUTPUT = 0x10, + RENDER_TARGET = 0x20, + DEPTH_STENCIL = 0x40, + UNORDERED_ACCESS = 0x80, + DECODER = 0x200, + VIDEO_ENCODER = 0x400, +} + +CPU_ACCESS_FLAG :: enum u32 { // TODO: make bit_set + WRITE = 0x10000, + READ = 0x20000, + +} + +RESOURCE_MISC_FLAG :: enum u32 { // TODO: make bit_set + GENERATE_MIPS = 0x1, + SHARED = 0x2, + TEXTURECUBE = 0x4, + DRAWINDIRECT_ARGS = 0x10, + BUFFER_ALLOW_RAW_VIEWS = 0x20, + BUFFER_STRUCTURED = 0x40, + RESOURCE_CLAMP = 0x80, + SHARED_KEYEDMUTEX = 0x100, + GDI_COMPATIBLE = 0x200, + SHARED_NTHANDLE = 0x800, + RESTRICTED_CONTENT = 0x1000, + RESTRICT_SHARED_RESOURCE = 0x2000, + RESTRICT_SHARED_RESOURCE_DRIVER = 0x4000, + GUARDED = 0x8000, + TILE_POOL = 0x20000, + TILED = 0x40000, + HW_PROTECTED = 0x80000, +} + +MAP :: enum i32 { + READ = 1, + WRITE = 2, + READ_WRITE = 3, + WRITE_DISCARD = 4, + WRITE_NO_OVERWRITE = 5, +} + +MAP_FLAG :: enum u32 { // TODO: make bit_set + DO_NOT_WAIT = 0x100000, +} + +RAISE_FLAG :: enum u32 { // TODO: make bit_set + DRIVER_INTERNAL_ERROR = 0x1, +} + +CLEAR_FLAG :: enum u32 { // TODO: make bit_set + DEPTH = 0x1, + STENCIL = 0x2, +} + + +CRECT :: struct { + using d3d11_rect: RECT, +} + +BOX :: struct { + left: u32, + top: u32, + front: u32, + right: u32, + bottom: u32, + back: u32, +} + +CBOX :: struct { + using d3d11_box: BOX, +} + + +IDeviceChild_UUID_STRING :: "1841E5C8-16B0-489B-BCC8-44CFB0D5DEAE" +IDeviceChild_UUID := &IID{0x1841E5C8, 0x16B0, 0x489B, {0xBC, 0xC8, 0x44, 0xCF, 0xB0, 0xD5, 0xDE, 0xAE}} +IDeviceChild :: struct #raw_union { + #subtype iunknown: IUnknown, + using id3d11devicechild_vtable: ^IDeviceChild_VTable, +} +IDeviceChild_VTable :: struct { + using iunknown_vtable: IUnknown_VTable, + GetDevice: proc "stdcall" (this: ^IDeviceChild, ppDevice: ^^IDevice), + GetPrivateData: proc "stdcall" (this: ^IDeviceChild, guid: ^GUID, pDataSize: ^u32, pData: rawptr) -> HRESULT, + SetPrivateData: proc "stdcall" (this: ^IDeviceChild, guid: ^GUID, DataSize: u32, pData: rawptr) -> HRESULT, + SetPrivateDataInterface: proc "stdcall" (this: ^IDeviceChild, guid: ^GUID, pData: ^IUnknown) -> HRESULT, +} + + +COMPARISON_FUNC :: enum i32 { + NEVER = 1, + LESS = 2, + EQUAL = 3, + LESS_EQUAL = 4, + GREATER = 5, + NOT_EQUAL = 6, + GREATER_EQUAL = 7, + ALWAYS = 8, +} + +DEPTH_WRITE_MASK :: enum i32 { + ZERO = 0, + ALL = 1, +} + +STENCIL_OP :: enum i32 { + KEEP = 1, + ZERO = 2, + REPLACE = 3, + INCR_SAT = 4, + DECR_SAT = 5, + INVERT = 6, + INCR = 7, + DECR = 8, +} + +DEPTH_STENCILOP_DESC :: struct { + StencilFailOp: STENCIL_OP, + StencilDepthFailOp: STENCIL_OP, + StencilPassOp: STENCIL_OP, + StencilFunc: COMPARISON_FUNC, +} + +DEPTH_STENCIL_DESC :: struct { + DepthEnable: BOOL, + DepthWriteMask: DEPTH_WRITE_MASK, + DepthFunc: COMPARISON_FUNC, + StencilEnable: BOOL, + StencilReadMask: u8, + StencilWriteMask: u8, + FrontFace: DEPTH_STENCILOP_DESC, + BackFace: DEPTH_STENCILOP_DESC, +} + +CDEPTH_STENCIL_DESC :: struct { + using d3d11_depth_stencil_desc: DEPTH_STENCIL_DESC, +} + + +IDepthStencilState_UUID_STRING :: "03823EFB-8D8F-4E1C-9AA2-F64BB2CBFDF1" +IDepthStencilState_UUID := &IID{0x03823EFB, 0x8D8F, 0x4E1C, {0x9A, 0xA2, 0xF6, 0x4B, 0xB2, 0xCB, 0xFD, 0xF1}} +IDepthStencilState :: struct #raw_union { + #subtype id3d11devicechild: IDeviceChild, + using id3d11depthstencilstate_vtable: ^IDepthStencilState_VTable, +} +IDepthStencilState_VTable :: struct { + using id3d11devicechild_vtable: IDeviceChild_VTable, + GetDesc: proc "stdcall" (this: ^IDepthStencilState, pDesc: ^DEPTH_STENCIL_DESC), +} + + +BLEND :: enum i32 { + ZERO = 1, + ONE = 2, + SRC_COLOR = 3, + INV_SRC_COLOR = 4, + SRC_ALPHA = 5, + INV_SRC_ALPHA = 6, + DEST_ALPHA = 7, + INV_DEST_ALPHA = 8, + DEST_COLOR = 9, + INV_DEST_COLOR = 10, + SRC_ALPHA_SAT = 11, + BLEND_FACTOR = 14, + INV_BLEND_FACTOR = 15, + SRC1_COLOR = 16, + INV_SRC1_COLOR = 17, + SRC1_ALPHA = 18, + INV_SRC1_ALPHA = 19, +} + +BLEND_OP :: enum i32 { + ADD = 1, + SUBTRACT = 2, + REV_SUBTRACT = 3, + MIN = 4, + MAX = 5, +} + +COLOR_WRITE_ENABLE :: enum i32 { // TODO: make bit_set + RED = 1, + GREEN = 2, + BLUE = 4, + ALPHA = 8, + ALL = 15, +} + +RENDER_TARGET_BLEND_DESC :: struct { + BlendEnable: BOOL, + SrcBlend: BLEND, + DestBlend: BLEND, + BlendOp: BLEND_OP, + SrcBlendAlpha: BLEND, + DestBlendAlpha: BLEND, + BlendOpAlpha: BLEND_OP, + RenderTargetWriteMask: u8, +} + +BLEND_DESC :: struct { + AlphaToCoverageEnable: BOOL, + IndependentBlendEnable: BOOL, + RenderTarget: [8]RENDER_TARGET_BLEND_DESC, +} + +CBLEND_DESC :: struct { + using d3d11_blend_desc: BLEND_DESC, +} + + +IBlendState_UUID_STRING :: "75B68FAA-347D-4159-8F45-A0640F01CD9A" +IBlendState_UUID := &IID{0x75B68FAA, 0x347D, 0x4159, {0x8F, 0x45, 0xA0, 0x64, 0x0F, 0x01, 0xCD, 0x9A}} +IBlendState :: struct #raw_union { + #subtype id3d11devicechild: IDeviceChild, + using id3d11blendstate_vtable: ^IBlendState_VTable, +} +IBlendState_VTable :: struct { + using id3d11devicechild_vtable: IDeviceChild_VTable, + GetDesc: proc "stdcall" (this: ^IBlendState, pDesc: ^BLEND_DESC), +} + + +RASTERIZER_DESC :: struct { + FillMode: FILL_MODE, + CullMode: CULL_MODE, + FrontCounterClockwise: BOOL, + DepthBias: i32, + DepthBiasClamp: f32, + SlopeScaledDepthBias: f32, + DepthClipEnable: BOOL, + ScissorEnable: BOOL, + MultisampleEnable: BOOL, + AntialiasedLineEnable: BOOL, +} + +CRASTERIZER_DESC :: struct { + using d3d11_rasterizer_desc: RASTERIZER_DESC, +} + + +IRasterizerState_UUID_STRING :: "9BB4AB81-AB1A-4D8F-B506-FC04200B6EE7" +IRasterizerState_UUID := &IID{0x9BB4AB81, 0xAB1A, 0x4D8F, {0xB5, 0x06, 0xFC, 0x04, 0x20, 0x0B, 0x6E, 0xE7}} +IRasterizerState :: struct #raw_union { + #subtype id3d11devicechild: IDeviceChild, + using id3d11rasterizerstate_vtable: ^IRasterizerState_VTable, +} +IRasterizerState_VTable :: struct { + using id3d11devicechild_vtable: IDeviceChild_VTable, + GetDesc: proc "stdcall" (this: ^IRasterizerState, pDesc: ^RASTERIZER_DESC), +} + + +SUBRESOURCE_DATA :: struct { + pSysMem: rawptr, + SysMemPitch: u32, + SysMemSlicePitch: u32, +} + +MAPPED_SUBRESOURCE :: struct { + pData: rawptr, + RowPitch: u32, + DepthPitch: u32, +} + + +IResource_UUID_STRING :: "DC8E63F3-D12B-4952-B47B-5E45026A862D" +IResource_UUID := &IID{0xDC8E63F3, 0xD12B, 0x4952, {0xB4, 0x7B, 0x5E, 0x45, 0x02, 0x6A, 0x86, 0x2D}} +IResource :: struct #raw_union { + #subtype id3d11devicechild: IDeviceChild, + using id3d11resource_vtable: ^IResource_VTable, +} +IResource_VTable :: struct { + using id3d11devicechild_vtable: IDeviceChild_VTable, + GetType: proc "stdcall" (this: ^IResource, pResourceDimension: ^RESOURCE_DIMENSION), + SetEvictionPriority: proc "stdcall" (this: ^IResource, EvictionPriority: u32), + GetEvictionPriority: proc "stdcall" (this: ^IResource) -> u32, +} + + +BUFFER_DESC :: struct { + ByteWidth: u32, + Usage: USAGE, + BindFlags: BIND_FLAG, + CPUAccessFlags: CPU_ACCESS_FLAG, + MiscFlags: RESOURCE_MISC_FLAG, + StructureByteStride: u32, +} + +CBUFFER_DESC :: struct { + using d3d11_buffer_desc: BUFFER_DESC, +} + + +IBuffer_UUID_STRING :: "48570B85-D1EE-4FCD-A250-EB350722B037" +IBuffer_UUID := &IID{0x48570B85, 0xD1EE, 0x4FCD, {0xA2, 0x50, 0xEB, 0x35, 0x07, 0x22, 0xB0, 0x37}} +IBuffer :: struct #raw_union { + #subtype id3d11resource: IResource, + using id3d11buffer_vtable: ^IBuffer_VTable, +} +IBuffer_VTable :: struct { + using id3d11resource_vtable: IResource_VTable, + GetDesc: proc "stdcall" (this: ^IBuffer, pDesc: ^BUFFER_DESC), +} + + +TEXTURE1D_DESC :: struct { + Width: u32, + MipLevels: u32, + ArraySize: u32, + Format: dxgi.FORMAT, + Usage: USAGE, + BindFlags: BIND_FLAG, + CPUAccessFlags: CPU_ACCESS_FLAG, + MiscFlags: RESOURCE_MISC_FLAG, +} + +CTEXTURE1D_DESC :: struct { + using d3d11_texture1d_desc: TEXTURE1D_DESC, +} + + +ITexture1D_UUID_STRING :: "F8FB5C27-C6B3-4F75-A4C8-439AF2EF564C" +ITexture1D_UUID := &IID{0xF8FB5C27, 0xC6B3, 0x4F75, {0xA4, 0xC8, 0x43, 0x9A, 0xF2, 0xEF, 0x56, 0x4C}} +ITexture1D :: struct #raw_union { + #subtype id3d11resource: IResource, + using id3d11texture1d_vtable: ^ITexture1D_VTable, +} +ITexture1D_VTable :: struct { + using id3d11resource_vtable: IResource_VTable, + GetDesc: proc "stdcall" (this: ^ITexture1D, pDesc: ^TEXTURE1D_DESC), +} + + +TEXTURE2D_DESC :: struct { + Width: u32, + Height: u32, + MipLevels: u32, + ArraySize: u32, + Format: dxgi.FORMAT, + SampleDesc: dxgi.SAMPLE_DESC, + Usage: USAGE, + BindFlags: BIND_FLAG, + CPUAccessFlags: CPU_ACCESS_FLAG, + MiscFlags: RESOURCE_MISC_FLAG, +} + +CTEXTURE2D_DESC :: struct { + using d3d11_texture2d_desc: TEXTURE2D_DESC, +} + + +ITexture2D_UUID_STRING :: "6F15AAF2-D208-4E89-9AB4-489535D34F9C" +ITexture2D_UUID := &IID{0x6F15AAF2, 0xD208, 0x4E89, {0x9A, 0xB4, 0x48, 0x95, 0x35, 0xD3, 0x4F, 0x9C}} +ITexture2D :: struct #raw_union { + #subtype id3d11resource: IResource, + using id3d11texture2d_vtable: ^ITexture2D_VTable, +} +ITexture2D_VTable :: struct { + using id3d11resource_vtable: IResource_VTable, + GetDesc: proc "stdcall" (this: ^ITexture2D, pDesc: ^TEXTURE2D_DESC), +} + + +TEXTURE3D_DESC :: struct { + Width: u32, + Height: u32, + Depth: u32, + MipLevels: u32, + Format: dxgi.FORMAT, + Usage: USAGE, + BindFlags: BIND_FLAG, + CPUAccessFlags: CPU_ACCESS_FLAG, + MiscFlags: RESOURCE_MISC_FLAG, +} + +CTEXTURE3D_DESC :: struct { + using d3d11_texture3d_desc: TEXTURE3D_DESC, +} + + +ITexture3D_UUID_STRING :: "037E866E-F56D-4357-A8AF-9DABBE6E250E" +ITexture3D_UUID := &IID{0x037E866E, 0xF56D, 0x4357, {0xA8, 0xAF, 0x9D, 0xAB, 0xBE, 0x6E, 0x25, 0x0E}} +ITexture3D :: struct #raw_union { + #subtype id3d11resource: IResource, + using id3d11texture3d_vtable: ^ITexture3D_VTable, +} +ITexture3D_VTable :: struct { + using id3d11resource_vtable: IResource_VTable, + GetDesc: proc "stdcall" (this: ^ITexture3D, pDesc: ^TEXTURE3D_DESC), +} + + +TEXTURECUBE_FACE :: enum i32 { + POSITIVE_X = 0, + NEGATIVE_X = 1, + POSITIVE_Y = 2, + NEGATIVE_Y = 3, + POSITIVE_Z = 4, + NEGATIVE_Z = 5, +} + + +IView_UUID_STRING :: "839D1216-BB2E-412B-B7F4-A9DBEBE08ED1" +IView_UUID := &IID{0x839D1216, 0xBB2E, 0x412B, {0xB7, 0xF4, 0xA9, 0xDB, 0xEB, 0xE0, 0x8E, 0xD1}} +IView :: struct #raw_union { + #subtype id3d11devicechild: IDeviceChild, + using id3d11view_vtable: ^IView_VTable, +} +IView_VTable :: struct { + using id3d11devicechild_vtable: IDeviceChild_VTable, + GetResource: proc "stdcall" (this: ^IView, ppResource: ^^IResource), +} + + +BUFFER_SRV :: struct { + using _: struct #raw_union { + FirstElement: u32, + ElementOffset: u32, + }, + using _: struct #raw_union { + NumElements: u32, + ElementWidth: u32, + }, +} + +BUFFEREX_SRV_FLAG :: enum u32 { // TODO: make bit_set + RAW = 0x1, +} + +BUFFEREX_SRV :: struct { + FirstElement: u32, + NumElements: u32, + Flags: u32, +} + +TEX1D_SRV :: struct { + MostDetailedMip: u32, + MipLevels: u32, +} + +TEX1D_ARRAY_SRV :: struct { + MostDetailedMip: u32, + MipLevels: u32, + FirstArraySlice: u32, + ArraySize: u32, +} + +TEX2D_SRV :: struct { + MostDetailedMip: u32, + MipLevels: u32, +} + +TEX2D_ARRAY_SRV :: struct { + MostDetailedMip: u32, + MipLevels: u32, + FirstArraySlice: u32, + ArraySize: u32, +} + +TEX3D_SRV :: struct { + MostDetailedMip: u32, + MipLevels: u32, +} + +TEXCUBE_SRV :: struct { + MostDetailedMip: u32, + MipLevels: u32, +} + +TEXCUBE_ARRAY_SRV :: struct { + MostDetailedMip: u32, + MipLevels: u32, + First2DArrayFace: u32, + NumCubes: u32, +} + +TEX2DMS_SRV :: struct { + UnusedField_NothingToDefine: u32, +} + +TEX2DMS_ARRAY_SRV :: struct { + FirstArraySlice: u32, + ArraySize: u32, +} + +SHADER_RESOURCE_VIEW_DESC :: struct { + Format: dxgi.FORMAT, + ViewDimension: SRV_DIMENSION, + using _: struct #raw_union { + Buffer: BUFFER_SRV, + Texture1D: TEX1D_SRV, + Texture1DArray: TEX1D_ARRAY_SRV, + Texture2D: TEX2D_SRV, + Texture2DArray: TEX2D_ARRAY_SRV, + Texture2DMS: TEX2DMS_SRV, + Texture2DMSArray: TEX2DMS_ARRAY_SRV, + Texture3D: TEX3D_SRV, + TextureCube: TEXCUBE_SRV, + TextureCubeArray: TEXCUBE_ARRAY_SRV, + BufferEx: BUFFEREX_SRV, + }, +} + +CSHADER_RESOURCE_VIEW_DESC :: struct { + using d3d11_shader_resource_view_desc: SHADER_RESOURCE_VIEW_DESC, +} + + +IShaderResourceView_UUID_STRING :: "B0E06FE0-8192-4E1A-B1CA-36D7414710B2" +IShaderResourceView_UUID := &IID{0xB0E06FE0, 0x8192, 0x4E1A, {0xB1, 0xCA, 0x36, 0xD7, 0x41, 0x47, 0x10, 0xB2}} +IShaderResourceView :: struct #raw_union { + #subtype id3d11view: IView, + using id3d11shaderresourceview_vtable: ^IShaderResourceView_VTable, +} +IShaderResourceView_VTable :: struct { + using id3d11view_vtable: IView_VTable, + GetDesc: proc "stdcall" (this: ^IShaderResourceView, pDesc: ^SHADER_RESOURCE_VIEW_DESC), +} + + +BUFFER_RTV :: struct { + using _: struct #raw_union { + FirstElement: u32, + ElementOffset: u32, + }, + using _: struct #raw_union { + NumElements: u32, + ElementWidth: u32, + }, +} + +TEX1D_RTV :: struct { + MipSlice: u32, +} + +TEX1D_ARRAY_RTV :: struct { + MipSlice: u32, + FirstArraySlice: u32, + ArraySize: u32, +} + +TEX2D_RTV :: struct { + MipSlice: u32, +} + +TEX2DMS_RTV :: struct { + UnusedField_NothingToDefine: u32, +} + +TEX2D_ARRAY_RTV :: struct { + MipSlice: u32, + FirstArraySlice: u32, + ArraySize: u32, +} + +TEX2DMS_ARRAY_RTV :: struct { + FirstArraySlice: u32, + ArraySize: u32, +} + +TEX3D_RTV :: struct { + MipSlice: u32, + FirstWSlice: u32, + WSize: u32, +} + +RENDER_TARGET_VIEW_DESC :: struct { + Format: dxgi.FORMAT, + ViewDimension: RTV_DIMENSION, + using _: struct #raw_union { + Buffer: BUFFER_RTV, + Texture1D: TEX1D_RTV, + Texture1DArray: TEX1D_ARRAY_RTV, + Texture2D: TEX2D_RTV, + Texture2DArray: TEX2D_ARRAY_RTV, + Texture2DMS: TEX2DMS_RTV, + Texture2DMSArray: TEX2DMS_ARRAY_RTV, + Texture3D: TEX3D_RTV, + }, +} + +CRENDER_TARGET_VIEW_DESC :: struct { + using d3d11_render_target_view_desc: RENDER_TARGET_VIEW_DESC, +} + + +IRenderTargetView_UUID_STRING :: "DFDBA067-0B8D-4865-875B-D7B4516CC164" +IRenderTargetView_UUID := &IID{0xDFDBA067, 0x0B8D, 0x4865, {0x87, 0x5B, 0xD7, 0xB4, 0x51, 0x6C, 0xC1, 0x64}} +IRenderTargetView :: struct #raw_union { + #subtype id3d11view: IView, + using id3d11rendertargetview_vtable: ^IRenderTargetView_VTable, +} +IRenderTargetView_VTable :: struct { + using id3d11view_vtable: IView_VTable, + GetDesc: proc "stdcall" (this: ^IRenderTargetView, pDesc: ^RENDER_TARGET_VIEW_DESC), +} + + +CVIEWPORT :: struct { + using d3d11_viewport: VIEWPORT, +} + +TEX1D_DSV :: struct { + MipSlice: u32, +} + +TEX1D_ARRAY_DSV :: struct { + MipSlice: u32, + FirstArraySlice: u32, + ArraySize: u32, +} + +TEX2D_DSV :: struct { + MipSlice: u32, +} + +TEX2D_ARRAY_DSV :: struct { + MipSlice: u32, + FirstArraySlice: u32, + ArraySize: u32, +} + +TEX2DMS_DSV :: struct { + UnusedField_NothingToDefine: u32, +} + +TEX2DMS_ARRAY_DSV :: struct { + FirstArraySlice: u32, + ArraySize: u32, +} + +DSV_FLAG :: enum u32 { // TODO: make bit_set + DEPTH = 0x1, + STENCIL = 0x2, +} + +DEPTH_STENCIL_VIEW_DESC :: struct { + Format: dxgi.FORMAT, + ViewDimension: DSV_DIMENSION, + Flags: u32, + using _: struct #raw_union { + Texture1D: TEX1D_DSV, + Texture1DArray: TEX1D_ARRAY_DSV, + Texture2D: TEX2D_DSV, + Texture2DArray: TEX2D_ARRAY_DSV, + Texture2DMS: TEX2DMS_DSV, + Texture2DMSArray: TEX2DMS_ARRAY_DSV, + }, +} + +CDEPTH_STENCIL_VIEW_DESC :: struct { + using d3d11_depth_stencil_view_desc: DEPTH_STENCIL_VIEW_DESC, +} + + +IDepthStencilView_UUID_STRING :: "9FDAC92A-1876-48C3-AFAD-25B94F84A9B6" +IDepthStencilView_UUID := &IID{0x9FDAC92A, 0x1876, 0x48C3, {0xAF, 0xAD, 0x25, 0xB9, 0x4F, 0x84, 0xA9, 0xB6}} +IDepthStencilView :: struct #raw_union { + #subtype id3d11view: IView, + using id3d11depthstencilview_vtable: ^IDepthStencilView_VTable, +} +IDepthStencilView_VTable :: struct { + using id3d11view_vtable: IView_VTable, + GetDesc: proc "stdcall" (this: ^IDepthStencilView, pDesc: ^DEPTH_STENCIL_VIEW_DESC), +} + + +BUFFER_UAV_FLAG :: enum u32 { // TODO: make bit_set + RAW = 0x1, + APPEND = 0x2, + COUNTER = 0x4, +} + +BUFFER_UAV :: struct { + FirstElement: u32, + NumElements: u32, + Flags: u32, +} + +TEX1D_UAV :: struct { + MipSlice: u32, +} + +TEX1D_ARRAY_UAV :: struct { + MipSlice: u32, + FirstArraySlice: u32, + ArraySize: u32, +} + +TEX2D_UAV :: struct { + MipSlice: u32, +} + +TEX2D_ARRAY_UAV :: struct { + MipSlice: u32, + FirstArraySlice: u32, + ArraySize: u32, +} + +TEX3D_UAV :: struct { + MipSlice: u32, + FirstWSlice: u32, + WSize: u32, +} + +UNORDERED_ACCESS_VIEW_DESC :: struct { + Format: dxgi.FORMAT, + ViewDimension: UAV_DIMENSION, + using _: struct #raw_union { + Buffer: BUFFER_UAV, + Texture1D: TEX1D_UAV, + Texture1DArray: TEX1D_ARRAY_UAV, + Texture2D: TEX2D_UAV, + Texture2DArray: TEX2D_ARRAY_UAV, + Texture3D: TEX3D_UAV, + }, +} + +CUNORDERED_ACCESS_VIEW_DESC :: struct { + using d3d11_unordered_access_view_desc: UNORDERED_ACCESS_VIEW_DESC, +} + + +IUnorderedAccessView_UUID_STRING :: "28ACF509-7F5C-48F6-8611-F316010A6380" +IUnorderedAccessView_UUID := &IID{0x28ACF509, 0x7F5C, 0x48F6, {0x86, 0x11, 0xF3, 0x16, 0x01, 0x0A, 0x63, 0x80}} +IUnorderedAccessView :: struct #raw_union { + #subtype id3d11view: IView, + using id3d11unorderedaccessview_vtable: ^IUnorderedAccessView_VTable, +} +IUnorderedAccessView_VTable :: struct { + using id3d11view_vtable: IView_VTable, + GetDesc: proc "stdcall" (this: ^IUnorderedAccessView, pDesc: ^UNORDERED_ACCESS_VIEW_DESC), +} + + + +IVertexShader_UUID_STRING :: "3B301D64-D678-4289-8897-22F8928B72F3" +IVertexShader_UUID := &IID{0x3B301D64, 0xD678, 0x4289, {0x88, 0x97, 0x22, 0xF8, 0x92, 0x8B, 0x72, 0xF3}} +IVertexShader :: struct { + using id3d11devicechild: IDeviceChild, +} + + +IHullShader_UUID_STRING :: "8E5C6061-628A-4C8E-8264-BBE45CB3D5DD" +IHullShader_UUID := &IID{0x8E5C6061, 0x628A, 0x4C8E, {0x82, 0x64, 0xBB, 0xE4, 0x5C, 0xB3, 0xD5, 0xDD}} +IHullShader :: struct { + using id3d11devicechild: IDeviceChild, +} + + +IDomainShader_UUID_STRING :: "F582C508-0F36-490C-9977-31EECE268CFA" +IDomainShader_UUID := &IID{0xF582C508, 0x0F36, 0x490C, {0x99, 0x77, 0x31, 0xEE, 0xCE, 0x26, 0x8C, 0xFA}} +IDomainShader :: struct { + using id3d11devicechild: IDeviceChild, +} + + +IGeometryShader_UUID_STRING :: "38325B96-EFFB-4022-BA02-2E795B70275C" +IGeometryShader_UUID := &IID{0x38325B96, 0xEFFB, 0x4022, {0xBA, 0x02, 0x2E, 0x79, 0x5B, 0x70, 0x27, 0x5C}} +IGeometryShader :: struct { + using id3d11devicechild: IDeviceChild, +} + + +IPixelShader_UUID_STRING :: "EA82E40D-51DC-4F33-93D4-DB7C9125AE8C" +IPixelShader_UUID := &IID{0xEA82E40D, 0x51DC, 0x4F33, {0x93, 0xD4, 0xDB, 0x7C, 0x91, 0x25, 0xAE, 0x8C}} +IPixelShader :: struct { + using id3d11devicechild: IDeviceChild, +} + + +IComputeShader_UUID_STRING :: "4F5B196E-C2BD-495E-BD01-1FDED38E4969" +IComputeShader_UUID := &IID{0x4F5B196E, 0xC2BD, 0x495E, {0xBD, 0x01, 0x1F, 0xDE, 0xD3, 0x8E, 0x49, 0x69}} +IComputeShader :: struct { + using id3d11devicechild: IDeviceChild, +} + + +IInputLayout_UUID_STRING :: "E4819DDC-4CF0-4025-BD26-5DE82A3E07B7" +IInputLayout_UUID := &IID{0xE4819DDC, 0x4CF0, 0x4025, {0xBD, 0x26, 0x5D, 0xE8, 0x2A, 0x3E, 0x07, 0xB7}} +IInputLayout :: struct { + using id3d11devicechild: IDeviceChild, +} + +FILTER :: enum i32 { + MIN_MAG_MIP_POINT = 0, + MIN_MAG_POINT_MIP_LINEAR = 1, + MIN_POINT_MAG_LINEAR_MIP_POINT = 4, + MIN_POINT_MAG_MIP_LINEAR = 5, + MIN_LINEAR_MAG_MIP_POINT = 16, + MIN_LINEAR_MAG_POINT_MIP_LINEAR = 17, + MIN_MAG_LINEAR_MIP_POINT = 20, + MIN_MAG_MIP_LINEAR = 21, + ANISOTROPIC = 85, + COMPARISON_MIN_MAG_MIP_POINT = 128, + COMPARISON_MIN_MAG_POINT_MIP_LINEAR = 129, + COMPARISON_MIN_POINT_MAG_LINEAR_MIP_POINT = 132, + COMPARISON_MIN_POINT_MAG_MIP_LINEAR = 133, + COMPARISON_MIN_LINEAR_MAG_MIP_POINT = 144, + COMPARISON_MIN_LINEAR_MAG_POINT_MIP_LINEAR = 145, + COMPARISON_MIN_MAG_LINEAR_MIP_POINT = 148, + COMPARISON_MIN_MAG_MIP_LINEAR = 149, + COMPARISON_ANISOTROPIC = 213, + MINIMUM_MIN_MAG_MIP_POINT = 256, + MINIMUM_MIN_MAG_POINT_MIP_LINEAR = 257, + MINIMUM_MIN_POINT_MAG_LINEAR_MIP_POINT = 260, + MINIMUM_MIN_POINT_MAG_MIP_LINEAR = 261, + MINIMUM_MIN_LINEAR_MAG_MIP_POINT = 272, + MINIMUM_MIN_LINEAR_MAG_POINT_MIP_LINEAR = 273, + MINIMUM_MIN_MAG_LINEAR_MIP_POINT = 276, + MINIMUM_MIN_MAG_MIP_LINEAR = 277, + MINIMUM_ANISOTROPIC = 341, + MAXIMUM_MIN_MAG_MIP_POINT = 384, + MAXIMUM_MIN_MAG_POINT_MIP_LINEAR = 385, + MAXIMUM_MIN_POINT_MAG_LINEAR_MIP_POINT = 388, + MAXIMUM_MIN_POINT_MAG_MIP_LINEAR = 389, + MAXIMUM_MIN_LINEAR_MAG_MIP_POINT = 400, + MAXIMUM_MIN_LINEAR_MAG_POINT_MIP_LINEAR = 401, + MAXIMUM_MIN_MAG_LINEAR_MIP_POINT = 404, + MAXIMUM_MIN_MAG_MIP_LINEAR = 405, + MAXIMUM_ANISOTROPIC = 469, +} + +FILTER_TYPE :: enum i32 { + POINT = 0, + LINEAR = 1, +} + +FILTER_REDUCTION_TYPE :: enum i32 { + STANDARD = 0, + COMPARISON = 1, + MINIMUM = 2, + MAXIMUM = 3, +} + +TEXTURE_ADDRESS_MODE :: enum i32 { + WRAP = 1, + MIRROR = 2, + CLAMP = 3, + BORDER = 4, + MIRROR_ONCE = 5, +} + +SAMPLER_DESC :: struct { + Filter: FILTER, + AddressU: TEXTURE_ADDRESS_MODE, + AddressV: TEXTURE_ADDRESS_MODE, + AddressW: TEXTURE_ADDRESS_MODE, + MipLODBias: f32, + MaxAnisotropy: u32, + ComparisonFunc: COMPARISON_FUNC, + BorderColor: [4]f32, + MinLOD: f32, + MaxLOD: f32, +} + +CSAMPLER_DESC :: struct { + using d3d11_sampler_desc: SAMPLER_DESC, +} + + +ISamplerState_UUID_STRING :: "DA6FEA51-564C-4487-9810-F0D0F9B4E3A5" +ISamplerState_UUID := &IID{0xDA6FEA51, 0x564C, 0x4487, {0x98, 0x10, 0xF0, 0xD0, 0xF9, 0xB4, 0xE3, 0xA5}} +ISamplerState :: struct #raw_union { + #subtype id3d11devicechild: IDeviceChild, + using id3d11samplerstate_vtable: ^ISamplerState_VTable, +} +ISamplerState_VTable :: struct { + using id3d11devicechild_vtable: IDeviceChild_VTable, + GetDesc: proc "stdcall" (this: ^ISamplerState, pDesc: ^SAMPLER_DESC), +} + + +FORMAT_SUPPORT :: enum i32 { // TODO: make bit_set + BUFFER = 1, + IA_VERTEX_BUFFER = 2, + IA_INDEX_BUFFER = 4, + SO_BUFFER = 8, + TEXTURE1D = 16, + TEXTURE2D = 32, + TEXTURE3D = 64, + TEXTURECUBE = 128, + SHADER_LOAD = 256, + SHADER_SAMPLE = 512, + SHADER_SAMPLE_COMPARISON = 1024, + SHADER_SAMPLE_MONO_TEXT = 2048, + MIP = 4096, + MIP_AUTOGEN = 8192, + RENDER_TARGET = 16384, + BLENDABLE = 32768, + DEPTH_STENCIL = 65536, + CPU_LOCKABLE = 131072, + MULTISAMPLE_RESOLVE = 262144, + DISPLAY = 524288, + CAST_WITHIN_BIT_LAYOUT = 1048576, + MULTISAMPLE_RENDERTARGET = 2097152, + MULTISAMPLE_LOAD = 4194304, + SHADER_GATHER = 8388608, + BACK_BUFFER_CAST = 16777216, + TYPED_UNORDERED_ACCESS_VIEW = 33554432, + SHADER_GATHER_COMPARISON = 67108864, + DECODER_OUTPUT = 134217728, + VIDEO_PROCESSOR_OUTPUT = 268435456, + VIDEO_PROCESSOR_INPUT = 536870912, + VIDEO_ENCODER = 1073741824, +} + +FORMAT_SUPPORT2 :: enum i32 { // TODO: make bit_set + UAV_ATOMIC_ADD = 1, + UAV_ATOMIC_BITWISE_OPS = 2, + UAV_ATOMIC_COMPARE_STORE_OR_COMPARE_EXCHANGE = 4, + UAV_ATOMIC_EXCHANGE = 8, + UAV_ATOMIC_SIGNED_MIN_OR_MAX = 16, + UAV_ATOMIC_UNSIGNED_MIN_OR_MAX = 32, + UAV_TYPED_LOAD = 64, + UAV_TYPED_STORE = 128, + OUTPUT_MERGER_LOGIC_OP = 256, + TILED = 512, + SHAREABLE = 1024, + MULTIPLANE_OVERLAY = 16384, +} + + +IAsynchronous_UUID_STRING :: "4B35D0CD-1E15-4258-9C98-1B1333F6DD3B" +IAsynchronous_UUID := &IID{0x4B35D0CD, 0x1E15, 0x4258, {0x9C, 0x98, 0x1B, 0x13, 0x33, 0xF6, 0xDD, 0x3B}} +IAsynchronous :: struct #raw_union { + #subtype id3d11devicechild: IDeviceChild, + using id3d11asynchronous_vtable: ^IAsynchronous_VTable, +} +IAsynchronous_VTable :: struct { + using id3d11devicechild_vtable: IDeviceChild_VTable, + GetDataSize: proc "stdcall" (this: ^IAsynchronous) -> u32, +} + + +ASYNC_GETDATA_FLAG :: enum u32 { // TODO: make bit_set + DONOTFLUSH = 0x1, +} + +QUERY :: enum i32 { + EVENT = 0, + OCCLUSION = 1, + TIMESTAMP = 2, + TIMESTAMP_DISJOINT = 3, + PIPELINE_STATISTICS = 4, + OCCLUSION_PREDICATE = 5, + SO_STATISTICS = 6, + SO_OVERFLOW_PREDICATE = 7, + SO_STATISTICS_STREAM0 = 8, + SO_OVERFLOW_PREDICATE_STREAM0 = 9, + SO_STATISTICS_STREAM1 = 10, + SO_OVERFLOW_PREDICATE_STREAM1 = 11, + SO_STATISTICS_STREAM2 = 12, + SO_OVERFLOW_PREDICATE_STREAM2 = 13, + SO_STATISTICS_STREAM3 = 14, + SO_OVERFLOW_PREDICATE_STREAM3 = 15, +} + +QUERY_MISC_FLAG :: enum u32 { // TODO: make bit_set + QUERY_MISC_PREDICATEHINT = 0x1, +} + +QUERY_DESC :: struct { + Query: QUERY, + MiscFlags: RESOURCE_MISC_FLAG, +} + +CQUERY_DESC :: struct { + using d3d11_query_desc: QUERY_DESC, +} + + +IQuery_UUID_STRING :: "D6C00747-87B7-425E-B84D-44D108560AFD" +IQuery_UUID := &IID{0xD6C00747, 0x87B7, 0x425E, {0xB8, 0x4D, 0x44, 0xD1, 0x08, 0x56, 0x0A, 0xFD}} +IQuery :: struct #raw_union { + #subtype id3d11asynchronous: IAsynchronous, + using id3d11query_vtable: ^IQuery_VTable, +} +IQuery_VTable :: struct { + using id3d11asynchronous_vtable: IAsynchronous_VTable, + GetDesc: proc "stdcall" (this: ^IQuery, pDesc: ^QUERY_DESC), +} + + + +IPredicate_UUID_STRING :: "9EB576DD-9F77-4D86-81AA-8BAB5FE490E2" +IPredicate_UUID := &IID{0x9EB576DD, 0x9F77, 0x4D86, {0x81, 0xAA, 0x8B, 0xAB, 0x5F, 0xE4, 0x90, 0xE2}} +IPredicate :: struct { + using id3d11query: IQuery, +} + +QUERY_DATA_TIMESTAMP_DISJOINT :: struct { + Frequency: u64, + Disjoint: BOOL, +} + +QUERY_DATA_PIPELINE_STATISTICS :: struct { + IAVertices: u64, + IAPrimitives: u64, + VSInvocations: u64, + GSInvocations: u64, + GSPrimitives: u64, + CInvocations: u64, + CPrimitives: u64, + PSInvocations: u64, + HSInvocations: u64, + DSInvocations: u64, + CSInvocations: u64, +} + +QUERY_DATA_SO_STATISTICS :: struct { + NumPrimitivesWritten: u64, + PrimitivesStorageNeeded: u64, +} + +COUNTER :: enum i32 { + DEVICE_DEPENDENT_0 = 1073741824, +} + +COUNTER_TYPE :: enum i32 { + FLOAT32 = 0, + UINT16 = 1, + UINT32 = 2, + UINT64 = 3, +} + +COUNTER_DESC :: struct { + Counter: COUNTER, + MiscFlags: RESOURCE_MISC_FLAG, +} + +CCOUNTER_DESC :: struct { + using d3d11_counter_desc: COUNTER_DESC, +} + +COUNTER_INFO :: struct { + LastDeviceDependentCounter: COUNTER, + NumSimultaneousCounters: u32, + NumDetectableParallelUnits: u8, +} + + +ICounter_UUID_STRING :: "6E8C49FB-A371-4770-B440-29086022B741" +ICounter_UUID := &IID{0x6E8C49FB, 0xA371, 0x4770, {0xB4, 0x40, 0x29, 0x08, 0x60, 0x22, 0xB7, 0x41}} +ICounter :: struct #raw_union { + #subtype id3d11asynchronous: IAsynchronous, + using id3d11counter_vtable: ^ICounter_VTable, +} +ICounter_VTable :: struct { + using id3d11asynchronous_vtable: IAsynchronous_VTable, + GetDesc: proc "stdcall" (this: ^ICounter, pDesc: ^COUNTER_DESC), +} + + +STANDARD_MULTISAMPLE_QUALITY_LEVELS :: enum i32 { + STANDARD_MULTISAMPLE_PATTERN = -1, + CENTER_MULTISAMPLE_PATTERN = -2, +} + +DEVICE_CONTEXT_TYPE :: enum i32 { + IMMEDIATE = 0, + DEFERRED = 1, +} + +CLASS_INSTANCE_DESC :: struct { + InstanceId: u32, + InstanceIndex: u32, + TypeId: u32, + ConstantBuffer: u32, + BaseConstantBufferOffset: u32, + BaseTexture: u32, + BaseSampler: u32, + Created: BOOL, +} + + +IClassInstance_UUID_STRING :: "A6CD7FAA-B0B7-4A2F-9436-8662A65797CB" +IClassInstance_UUID := &IID{0xA6CD7FAA, 0xB0B7, 0x4A2F, {0x94, 0x36, 0x86, 0x62, 0xA6, 0x57, 0x97, 0xCB}} +IClassInstance :: struct #raw_union { + #subtype id3d11devicechild: IDeviceChild, + using id3d11classinstance_vtable: ^IClassInstance_VTable, +} +IClassInstance_VTable :: struct { + using id3d11devicechild_vtable: IDeviceChild_VTable, + GetClassLinkage: proc "stdcall" (this: ^IClassInstance, ppLinkage: ^^IClassLinkage), + GetDesc: proc "stdcall" (this: ^IClassInstance, pDesc: ^CLASS_INSTANCE_DESC), + GetInstanceName: proc "stdcall" (this: ^IClassInstance, pInstanceName: cstring, pBufferLength: ^SIZE_T), + GetTypeName: proc "stdcall" (this: ^IClassInstance, pTypeName: cstring, pBufferLength: ^SIZE_T), +} + + + +IClassLinkage_UUID_STRING :: "DDF57CBA-9543-46E4-A12B-F207A0FE7FED" +IClassLinkage_UUID := &IID{0xDDF57CBA, 0x9543, 0x46E4, {0xA1, 0x2B, 0xF2, 0x07, 0xA0, 0xFE, 0x7F, 0xED}} +IClassLinkage :: struct #raw_union { + #subtype id3d11devicechild: IDeviceChild, + using id3d11classlinkage_vtable: ^IClassLinkage_VTable, +} +IClassLinkage_VTable :: struct { + using id3d11devicechild_vtable: IDeviceChild_VTable, + GetClassInstance: proc "stdcall" (this: ^IClassLinkage, pClassInstanceName: cstring, InstanceIndex: u32, ppInstance: ^^IClassInstance) -> HRESULT, + CreateClassInstance: proc "stdcall" (this: ^IClassLinkage, pClassTypeName: cstring, ConstantBufferOffset: u32, ConstantVectorOffset: u32, TextureOffset: u32, SamplerOffset: u32, ppInstance: ^^IClassInstance) -> HRESULT, +} + + + +ICommandList_UUID_STRING :: "A24BC4D1-769E-43F7-8013-98FF566C18E2" +ICommandList_UUID := &IID{0xA24BC4D1, 0x769E, 0x43F7, {0x80, 0x13, 0x98, 0xFF, 0x56, 0x6C, 0x18, 0xE2}} +ICommandList :: struct #raw_union { + #subtype id3d11devicechild: IDeviceChild, + using id3d11commandlist_vtable: ^ICommandList_VTable, +} +ICommandList_VTable :: struct { + using id3d11devicechild_vtable: IDeviceChild_VTable, + GetContextFlags: proc "stdcall" (this: ^ICommandList) -> u32, +} + + +FEATURE :: enum i32 { + THREADING = 0, + DOUBLES = 1, + FORMAT_SUPPORT = 2, + FORMAT_SUPPORT2 = 3, + D3D10_X_HARDWARE_OPTIONS = 4, + OPTIONS = 5, + ARCHITECTURE_INFO = 6, + D3D9_OPTIONS = 7, + SHADER_MIN_PRECISION_SUPPORT = 8, + D3D9_SHADOW_SUPPORT = 9, + OPTIONS1 = 10, + D3D9_SIMPLE_INSTANCING_SUPPORT = 11, + MARKER_SUPPORT = 12, + D3D9_OPTIONS1 = 13, + OPTIONS2 = 14, + OPTIONS3 = 15, + GPU_VIRTUAL_ADDRESS_SUPPORT = 16, + OPTIONS4 = 17, + SHADER_CACHE = 18, + OPTIONS5 = 19, +} + +FEATURE_DATA_THREADING :: struct { + DriverConcurrentCreates: BOOL, + DriverCommandLists: BOOL, +} + +FEATURE_DATA_DOUBLES :: struct { + DoublePrecisionFloatShaderOps: BOOL, +} + +FEATURE_DATA_FORMAT_SUPPORT :: struct { + InFormat: dxgi.FORMAT, + OutFormatSupport: u32, +} + +FEATURE_DATA_FORMAT_SUPPORT2 :: struct { + InFormat: dxgi.FORMAT, + OutFormatSupport2: u32, +} + +FEATURE_DATA_D3D10_X_HARDWARE_OPTIONS :: struct { + ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x: BOOL, +} + +FEATURE_DATA_OPTIONS :: struct { + OutputMergerLogicOp: BOOL, + UAVOnlyRenderingForcedSampleCount: BOOL, + DiscardAPIsSeenByDriver: BOOL, + FlagsForUpdateAndCopySeenByDriver: BOOL, + ClearView: BOOL, + CopyWithOverlap: BOOL, + ConstantBufferPartialUpdate: BOOL, + ConstantBufferOffsetting: BOOL, + MapNoOverwriteOnDynamicConstantBuffer: BOOL, + MapNoOverwriteOnDynamicBufferSRV: BOOL, + MultisampleRTVWithForcedSampleCountOne: BOOL, + SAD4ShaderInstructions: BOOL, + ExtendedDoublesShaderInstructions: BOOL, + ExtendedResourceSharing: BOOL, +} + +FEATURE_DATA_ARCHITECTURE_INFO :: struct { + TileBasedDeferredRenderer: BOOL, +} + +FEATURE_DATA_D3D9_OPTIONS :: struct { + FullNonPow2TextureSupport: BOOL, +} + +FEATURE_DATA_D3D9_SHADOW_SUPPORT :: struct { + SupportsDepthAsTextureWithLessEqualComparisonFilter: BOOL, +} + +SHADER_MIN_PRECISION_SUPPORT :: enum i32 { + _10_BIT = 1, + _16_BIT = 2, +} + +FEATURE_DATA_SHADER_MIN_PRECISION_SUPPORT :: struct { + PixelShaderMinPrecision: u32, + AllOtherShaderStagesMinPrecision: u32, +} + +TILED_RESOURCES_TIER :: enum i32 { + TILED_RESOURCES_NOT_SUPPORTED = 0, + _1 = 1, + _2 = 2, + _3 = 3, +} + +FEATURE_DATA_OPTIONS1 :: struct { + TiledResourcesTier: TILED_RESOURCES_TIER, + MinMaxFiltering: BOOL, + ClearViewAlsoSupportsDepthOnlyFormats: BOOL, + MapOnDefaultBuffers: BOOL, +} + +FEATURE_DATA_D3D9_SIMPLE_INSTANCING_SUPPORT :: struct { + SimpleInstancingSupported: BOOL, +} + +FEATURE_DATA_MARKER_SUPPORT :: struct { + Profile: BOOL, +} + +FEATURE_DATA_D3D9_OPTIONS1 :: struct { + FullNonPow2TextureSupported: BOOL, + DepthAsTextureWithLessEqualComparisonFilterSupported: BOOL, + SimpleInstancingSupported: BOOL, + TextureCubeFaceRenderTargetWithNonCubeDepthStencilSupported: BOOL, +} + +CONSERVATIVE_RASTERIZATION_TIER :: enum i32 { + CONSERVATIVE_RASTERIZATION_NOT_SUPPORTED = 0, + _1 = 1, + _2 = 2, + _3 = 3, +} + +FEATURE_DATA_OPTIONS2 :: struct { + PSSpecifiedStencilRefSupported: BOOL, + TypedUAVLoadAdditionalFormats: BOOL, + ROVsSupported: BOOL, + ConservativeRasterizationTier: CONSERVATIVE_RASTERIZATION_TIER, + TiledResourcesTier: TILED_RESOURCES_TIER, + MapOnDefaultTextures: BOOL, + StandardSwizzle: BOOL, + UnifiedMemoryArchitecture: BOOL, +} + +FEATURE_DATA_OPTIONS3 :: struct { + VPAndRTArrayIndexFromAnyShaderFeedingRasterizer: BOOL, +} + +FEATURE_DATA_GPU_VIRTUAL_ADDRESS_SUPPORT :: struct { + MaxGPUVirtualAddressBitsPerResource: u32, + MaxGPUVirtualAddressBitsPerProcess: u32, +} + +SHADER_CACHE_SUPPORT_FLAGS :: enum u32 { // TODO: make bit_set + NONE = 0x0, + AUTOMATIC_INPROC_CACHE = 0x1, + AUTOMATIC_DISK_CACHE = 0x2, +} + +FEATURE_DATA_SHADER_CACHE :: struct { + SupportFlags: u32, +} + +SHARED_RESOURCE_TIER :: enum i32 { + _0 = 0, + _1 = 1, + _2 = 2, + _3 = 3, +} + +FEATURE_DATA_OPTIONS5 :: struct { + SharedResourceTier: SHARED_RESOURCE_TIER, +} + + +IDeviceContext_UUID_STRING :: "C0BFA96C-E089-44FB-8EAF-26F8796190DA" +IDeviceContext_UUID := &IID{0xC0BFA96C, 0xE089, 0x44FB, {0x8E, 0xAF, 0x26, 0xF8, 0x79, 0x61, 0x90, 0xDA}} +IDeviceContext :: struct #raw_union { + #subtype id3d11devicechild: IDeviceChild, + using id3d11devicecontext_vtable: ^IDeviceContext_VTable, +} +IDeviceContext_VTable :: struct { + using id3d11devicechild_vtable: IDeviceChild_VTable, + VSSetConstantBuffers: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumBuffers: u32, ppConstantBuffers: ^^IBuffer), + PSSetShaderResources: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumViews: u32, ppShaderResourceViews: ^^IShaderResourceView), + PSSetShader: proc "stdcall" (this: ^IDeviceContext, pPixelShader: ^IPixelShader, ppClassInstances: ^^IClassInstance, NumClassInstances: u32), + PSSetSamplers: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumSamplers: u32, ppSamplers: ^^ISamplerState), + VSSetShader: proc "stdcall" (this: ^IDeviceContext, pVertexShader: ^IVertexShader, ppClassInstances: ^^IClassInstance, NumClassInstances: u32), + DrawIndexed: proc "stdcall" (this: ^IDeviceContext, IndexCount: u32, StartIndexLocation: u32, BaseVertexLocation: i32), + Draw: proc "stdcall" (this: ^IDeviceContext, VertexCount: u32, StartVertexLocation: u32), + Map: proc "stdcall" (this: ^IDeviceContext, pResource: ^IResource, Subresource: u32, MapType: MAP, MapFlags: u32, pMappedResource: ^MAPPED_SUBRESOURCE) -> HRESULT, + Unmap: proc "stdcall" (this: ^IDeviceContext, pResource: ^IResource, Subresource: u32), + PSSetConstantBuffers: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumBuffers: u32, ppConstantBuffers: ^^IBuffer), + IASetInputLayout: proc "stdcall" (this: ^IDeviceContext, pInputLayout: ^IInputLayout), + IASetVertexBuffers: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumBuffers: u32, ppVertexBuffers: ^^IBuffer, pStrides: ^u32, pOffsets: ^u32), + IASetIndexBuffer: proc "stdcall" (this: ^IDeviceContext, pIndexBuffer: ^IBuffer, Format: dxgi.FORMAT, Offset: u32), + DrawIndexedInstanced: proc "stdcall" (this: ^IDeviceContext, IndexCountPerInstance: u32, InstanceCount: u32, StartIndexLocation: u32, BaseVertexLocation: i32, StartInstanceLocation: u32), + DrawInstanced: proc "stdcall" (this: ^IDeviceContext, VertexCountPerInstance: u32, InstanceCount: u32, StartVertexLocation: u32, StartInstanceLocation: u32), + GSSetConstantBuffers: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumBuffers: u32, ppConstantBuffers: ^^IBuffer), + GSSetShader: proc "stdcall" (this: ^IDeviceContext, pShader: ^IGeometryShader, ppClassInstances: ^^IClassInstance, NumClassInstances: u32), + IASetPrimitiveTopology: proc "stdcall" (this: ^IDeviceContext, Topology: PRIMITIVE_TOPOLOGY), + VSSetShaderResources: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumViews: u32, ppShaderResourceViews: ^^IShaderResourceView), + VSSetSamplers: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumSamplers: u32, ppSamplers: ^^ISamplerState), + Begin: proc "stdcall" (this: ^IDeviceContext, pAsync: ^IAsynchronous), + End: proc "stdcall" (this: ^IDeviceContext, pAsync: ^IAsynchronous), + GetData: proc "stdcall" (this: ^IDeviceContext, pAsync: ^IAsynchronous, pData: rawptr, DataSize: u32, GetDataFlags: u32) -> HRESULT, + SetPredication: proc "stdcall" (this: ^IDeviceContext, pPredicate: ^IPredicate, PredicateValue: BOOL), + GSSetShaderResources: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumViews: u32, ppShaderResourceViews: ^^IShaderResourceView), + GSSetSamplers: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumSamplers: u32, ppSamplers: ^^ISamplerState), + OMSetRenderTargets: proc "stdcall" (this: ^IDeviceContext, NumViews: u32, ppRenderTargetViews: ^^IRenderTargetView, pDepthStencilView: ^IDepthStencilView), + OMSetRenderTargetsAndUnorderedAccessViews: proc "stdcall" (this: ^IDeviceContext, NumRTVs: u32, ppRenderTargetViews: ^^IRenderTargetView, pDepthStencilView: ^IDepthStencilView, UAVStartSlot: u32, NumUAVs: u32, ppUnorderedAccessViews: ^^IUnorderedAccessView, pUAVInitialCounts: ^u32), + OMSetBlendState: proc "stdcall" (this: ^IDeviceContext, pBlendState: ^IBlendState, BlendFactor: ^[4]f32, SampleMask: u32), + OMSetDepthStencilState: proc "stdcall" (this: ^IDeviceContext, pDepthStencilState: ^IDepthStencilState, StencilRef: u32), + SOSetTargets: proc "stdcall" (this: ^IDeviceContext, NumBuffers: u32, ppSOTargets: ^^IBuffer, pOffsets: ^u32), + DrawAuto: proc "stdcall" (this: ^IDeviceContext), + DrawIndexedInstancedIndirect: proc "stdcall" (this: ^IDeviceContext, pBufferForArgs: ^IBuffer, AlignedByteOffsetForArgs: u32), + DrawInstancedIndirect: proc "stdcall" (this: ^IDeviceContext, pBufferForArgs: ^IBuffer, AlignedByteOffsetForArgs: u32), + Dispatch: proc "stdcall" (this: ^IDeviceContext, ThreadGroupCountX: u32, ThreadGroupCountY: u32, ThreadGroupCountZ: u32), + DispatchIndirect: proc "stdcall" (this: ^IDeviceContext, pBufferForArgs: ^IBuffer, AlignedByteOffsetForArgs: u32), + RSSetState: proc "stdcall" (this: ^IDeviceContext, pRasterizerState: ^IRasterizerState), + RSSetViewports: proc "stdcall" (this: ^IDeviceContext, NumViewports: u32, pViewports: ^VIEWPORT), + RSSetScissorRects: proc "stdcall" (this: ^IDeviceContext, NumRects: u32, pRects: ^RECT), + CopySubresourceRegion: proc "stdcall" (this: ^IDeviceContext, pDstResource: ^IResource, DstSubresource: u32, DstX: u32, DstY: u32, DstZ: u32, pSrcResource: ^IResource, SrcSubresource: u32, pSrcBox: ^BOX), + CopyResource: proc "stdcall" (this: ^IDeviceContext, pDstResource: ^IResource, pSrcResource: ^IResource), + UpdateSubresource: proc "stdcall" (this: ^IDeviceContext, pDstResource: ^IResource, DstSubresource: u32, pDstBox: ^BOX, pSrcData: rawptr, SrcRowPitch: u32, SrcDepthPitch: u32), + CopyStructureCount: proc "stdcall" (this: ^IDeviceContext, pDstBuffer: ^IBuffer, DstAlignedByteOffset: u32, pSrcView: ^IUnorderedAccessView), + ClearRenderTargetView: proc "stdcall" (this: ^IDeviceContext, pRenderTargetView: ^IRenderTargetView, ColorRGBA: ^[4]f32), + ClearUnorderedAccessViewUint: proc "stdcall" (this: ^IDeviceContext, pUnorderedAccessView: ^IUnorderedAccessView, Values: ^[4]u32), + ClearUnorderedAccessViewFloat: proc "stdcall" (this: ^IDeviceContext, pUnorderedAccessView: ^IUnorderedAccessView, Values: ^[4]f32), + ClearDepthStencilView: proc "stdcall" (this: ^IDeviceContext, pDepthStencilView: ^IDepthStencilView, ClearFlags: CLEAR_FLAG, Depth: f32, Stencil: u8), + GenerateMips: proc "stdcall" (this: ^IDeviceContext, pShaderResourceView: ^IShaderResourceView), + SetResourceMinLOD: proc "stdcall" (this: ^IDeviceContext, pResource: ^IResource, MinLOD: f32), + GetResourceMinLOD: proc "stdcall" (this: ^IDeviceContext, pResource: ^IResource) -> f32, + ResolveSubresource: proc "stdcall" (this: ^IDeviceContext, pDstResource: ^IResource, DstSubresource: u32, pSrcResource: ^IResource, SrcSubresource: u32, Format: dxgi.FORMAT), + ExecuteCommandList: proc "stdcall" (this: ^IDeviceContext, pCommandList: ^ICommandList, RestoreContextState: BOOL), + HSSetShaderResources: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumViews: u32, ppShaderResourceViews: ^^IShaderResourceView), + HSSetShader: proc "stdcall" (this: ^IDeviceContext, pHullShader: ^IHullShader, ppClassInstances: ^^IClassInstance, NumClassInstances: u32), + HSSetSamplers: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumSamplers: u32, ppSamplers: ^^ISamplerState), + HSSetConstantBuffers: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumBuffers: u32, ppConstantBuffers: ^^IBuffer), + DSSetShaderResources: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumViews: u32, ppShaderResourceViews: ^^IShaderResourceView), + DSSetShader: proc "stdcall" (this: ^IDeviceContext, pDomainShader: ^IDomainShader, ppClassInstances: ^^IClassInstance, NumClassInstances: u32), + DSSetSamplers: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumSamplers: u32, ppSamplers: ^^ISamplerState), + DSSetConstantBuffers: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumBuffers: u32, ppConstantBuffers: ^^IBuffer), + CSSetShaderResources: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumViews: u32, ppShaderResourceViews: ^^IShaderResourceView), + CSSetUnorderedAccessViews: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumUAVs: u32, ppUnorderedAccessViews: ^^IUnorderedAccessView, pUAVInitialCounts: ^u32), + CSSetShader: proc "stdcall" (this: ^IDeviceContext, pComputeShader: ^IComputeShader, ppClassInstances: ^^IClassInstance, NumClassInstances: u32), + CSSetSamplers: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumSamplers: u32, ppSamplers: ^^ISamplerState), + CSSetConstantBuffers: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumBuffers: u32, ppConstantBuffers: ^^IBuffer), + VSGetConstantBuffers: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumBuffers: u32, ppConstantBuffers: ^^IBuffer), + PSGetShaderResources: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumViews: u32, ppShaderResourceViews: ^^IShaderResourceView), + PSGetShader: proc "stdcall" (this: ^IDeviceContext, ppPixelShader: ^^IPixelShader, ppClassInstances: ^^IClassInstance, pNumClassInstances: ^u32), + PSGetSamplers: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumSamplers: u32, ppSamplers: ^^ISamplerState), + VSGetShader: proc "stdcall" (this: ^IDeviceContext, ppVertexShader: ^^IVertexShader, ppClassInstances: ^^IClassInstance, pNumClassInstances: ^u32), + PSGetConstantBuffers: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumBuffers: u32, ppConstantBuffers: ^^IBuffer), + IAGetInputLayout: proc "stdcall" (this: ^IDeviceContext, ppInputLayout: ^^IInputLayout), + IAGetVertexBuffers: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumBuffers: u32, ppVertexBuffers: ^^IBuffer, pStrides: ^u32, pOffsets: ^u32), + IAGetIndexBuffer: proc "stdcall" (this: ^IDeviceContext, pIndexBuffer: ^^IBuffer, Format: ^dxgi.FORMAT, Offset: ^u32), + GSGetConstantBuffers: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumBuffers: u32, ppConstantBuffers: ^^IBuffer), + GSGetShader: proc "stdcall" (this: ^IDeviceContext, ppGeometryShader: ^^IGeometryShader, ppClassInstances: ^^IClassInstance, pNumClassInstances: ^u32), + IAGetPrimitiveTopology: proc "stdcall" (this: ^IDeviceContext, pTopology: ^PRIMITIVE_TOPOLOGY), + VSGetShaderResources: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumViews: u32, ppShaderResourceViews: ^^IShaderResourceView), + VSGetSamplers: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumSamplers: u32, ppSamplers: ^^ISamplerState), + GetPredication: proc "stdcall" (this: ^IDeviceContext, ppPredicate: ^^IPredicate, pPredicateValue: ^BOOL), + GSGetShaderResources: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumViews: u32, ppShaderResourceViews: ^^IShaderResourceView), + GSGetSamplers: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumSamplers: u32, ppSamplers: ^^ISamplerState), + OMGetRenderTargets: proc "stdcall" (this: ^IDeviceContext, NumViews: u32, ppRenderTargetViews: ^^IRenderTargetView, ppDepthStencilView: ^^IDepthStencilView), + OMGetRenderTargetsAndUnorderedAccessViews: proc "stdcall" (this: ^IDeviceContext, NumRTVs: u32, ppRenderTargetViews: ^^IRenderTargetView, ppDepthStencilView: ^^IDepthStencilView, UAVStartSlot: u32, NumUAVs: u32, ppUnorderedAccessViews: ^^IUnorderedAccessView), + OMGetBlendState: proc "stdcall" (this: ^IDeviceContext, ppBlendState: ^^IBlendState, BlendFactor: ^[4]f32, pSampleMask: ^u32), + OMGetDepthStencilState: proc "stdcall" (this: ^IDeviceContext, ppDepthStencilState: ^^IDepthStencilState, pStencilRef: ^u32), + SOGetTargets: proc "stdcall" (this: ^IDeviceContext, NumBuffers: u32, ppSOTargets: ^^IBuffer), + RSGetState: proc "stdcall" (this: ^IDeviceContext, ppRasterizerState: ^^IRasterizerState), + RSGetViewports: proc "stdcall" (this: ^IDeviceContext, pNumViewports: ^u32, pViewports: ^VIEWPORT), + RSGetScissorRects: proc "stdcall" (this: ^IDeviceContext, pNumRects: ^u32, pRects: ^RECT), + HSGetShaderResources: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumViews: u32, ppShaderResourceViews: ^^IShaderResourceView), + HSGetShader: proc "stdcall" (this: ^IDeviceContext, ppHullShader: ^^IHullShader, ppClassInstances: ^^IClassInstance, pNumClassInstances: ^u32), + HSGetSamplers: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumSamplers: u32, ppSamplers: ^^ISamplerState), + HSGetConstantBuffers: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumBuffers: u32, ppConstantBuffers: ^^IBuffer), + DSGetShaderResources: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumViews: u32, ppShaderResourceViews: ^^IShaderResourceView), + DSGetShader: proc "stdcall" (this: ^IDeviceContext, ppDomainShader: ^^IDomainShader, ppClassInstances: ^^IClassInstance, pNumClassInstances: ^u32), + DSGetSamplers: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumSamplers: u32, ppSamplers: ^^ISamplerState), + DSGetConstantBuffers: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumBuffers: u32, ppConstantBuffers: ^^IBuffer), + CSGetShaderResources: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumViews: u32, ppShaderResourceViews: ^^IShaderResourceView), + CSGetUnorderedAccessViews: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumUAVs: u32, ppUnorderedAccessViews: ^^IUnorderedAccessView), + CSGetShader: proc "stdcall" (this: ^IDeviceContext, ppComputeShader: ^^IComputeShader, ppClassInstances: ^^IClassInstance, pNumClassInstances: ^u32), + CSGetSamplers: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumSamplers: u32, ppSamplers: ^^ISamplerState), + CSGetConstantBuffers: proc "stdcall" (this: ^IDeviceContext, StartSlot: u32, NumBuffers: u32, ppConstantBuffers: ^^IBuffer), + ClearState: proc "stdcall" (this: ^IDeviceContext), + Flush: proc "stdcall" (this: ^IDeviceContext), + GetType: proc "stdcall" (this: ^IDeviceContext) -> DEVICE_CONTEXT_TYPE, + GetContextFlags: proc "stdcall" (this: ^IDeviceContext) -> u32, + FinishCommandList: proc "stdcall" (this: ^IDeviceContext, RestoreDeferredContextState: BOOL, ppCommandList: ^^ICommandList) -> HRESULT, +} + + +CVIDEO_DEFAULT :: struct { + _: u8, +} + +APP_DEPRECATED_HRESULT :: HRESULT + + +VIDEO_DECODER_DESC :: struct { + Guid: GUID, + SampleWidth: u32, + SampleHeight: u32, + OutputFormat: dxgi.FORMAT, +} + +VIDEO_DECODER_CONFIG :: struct { + guidConfigBitstreamEncryption: GUID, + guidConfigMBcontrolEncryption: GUID, + guidConfigResidDiffEncryption: GUID, + ConfigBitstreamRaw: u32, + ConfigMBcontrolRasterOrder: u32, + ConfigResidDiffHost: u32, + ConfigSpatialResid8: u32, + ConfigResid8Subtraction: u32, + ConfigSpatialHost8or9Clipping: u32, + ConfigSpatialResidInterleaved: u32, + ConfigIntraResidUnsigned: u32, + ConfigResidDiffAccelerator: u32, + ConfigHostInverseScan: u32, + ConfigSpecificIDCT: u32, + Config4GroupedCoefs: u32, + ConfigMinRenderTargetBuffCount: u16, + ConfigDecoderSpecific: u16, +} + +VIDEO_DECODER_BUFFER_TYPE :: enum i32 { + PICTURE_PARAMETERS = 0, + MACROBLOCK_CONTROL = 1, + RESIDUAL_DIFFERENCE = 2, + DEBLOCKING_CONTROL = 3, + INVERSE_QUANTIZATION_MATRIX = 4, + SLICE_CONTROL = 5, + BITSTREAM = 6, + MOTION_VECTOR = 7, + FILM_GRAIN = 8, +} + +AES_CTR_IV :: struct { + IV: u64, + Count: u64, +} + +ENCRYPTED_BLOCK_INFO :: struct { + NumEncryptedBytesAtBeginning: u32, + NumBytesInSkipPattern: u32, + NumBytesInEncryptPattern: u32, +} + +VIDEO_DECODER_BUFFER_DESC :: struct { + BufferType: VIDEO_DECODER_BUFFER_TYPE, + BufferIndex: u32, + DataOffset: u32, + DataSize: u32, + FirstMBaddress: u32, + NumMBsInBuffer: u32, + Width: u32, + Height: u32, + Stride: u32, + ReservedBits: u32, + + pIV: rawptr, + IVSize: u32, + PartialEncryption: BOOL, + EncryptedBlockInfo: ENCRYPTED_BLOCK_INFO, +} + +VIDEO_DECODER_EXTENSION :: struct { + Function: u32, + + pPrivateInputData: rawptr, + PrivateInputDataSize: u32, + + pPrivateOutputData: rawptr, + PrivateOutputDataSize: u32, + ResourceCount: u32, + + ppResourceList: ^^IResource, +} + + +IVideoDecoder_UUID_STRING :: "3C9C5B51-995D-48D1-9B8D-FA5CAEDED65C" +IVideoDecoder_UUID := &IID{0x3C9C5B51, 0x995D, 0x48D1, {0x9B, 0x8D, 0xFA, 0x5C, 0xAE, 0xDE, 0xD6, 0x5C}} +IVideoDecoder :: struct #raw_union { + #subtype id3d11devicechild: IDeviceChild, + using id3d11videodecoder_vtable: ^IVideoDecoder_VTable, +} +IVideoDecoder_VTable :: struct { + using id3d11devicechild_vtable: IDeviceChild_VTable, + GetCreationParameters: proc "stdcall" (this: ^IVideoDecoder, pVideoDesc: ^VIDEO_DECODER_DESC, pConfig: ^VIDEO_DECODER_CONFIG) -> HRESULT, + GetDriverHandle: proc "stdcall" (this: ^IVideoDecoder, pDriverHandle: ^HANDLE) -> HRESULT, +} + + +VIDEO_PROCESSOR_FORMAT_SUPPORT :: enum i32 { + INPUT = 1, + OUTPUT = 2, +} + +VIDEO_PROCESSOR_DEVICE_CAPS :: enum i32 { // TODO: make bit_set + LINEAR_SPACE = 1, + xvYCC = 2, + RGB_RANGE_CONVERSION = 4, + YCbCr_MATRIX_CONVERSION = 8, + NOMINAL_RANGE = 16, +} + +VIDEO_PROCESSOR_FEATURE_CAPS :: enum i32 { // TODO: make bit_set + ALPHA_FILL = 1, + CONSTRICTION = 2, + LUMA_KEY = 4, + ALPHA_PALETTE = 8, + LEGACY = 16, + STEREO = 32, + ROTATION = 64, + ALPHA_STREAM = 128, + PIXEL_ASPECT_RATIO = 256, + MIRROR = 512, + SHADER_USAGE = 1024, + METADATA_HDR10 = 2048, +} + +VIDEO_PROCESSOR_FILTER_CAPS :: enum i32 { // TODO: make bit_set + BRIGHTNESS = 1, + CONTRAST = 2, + HUE = 4, + SATURATION = 8, + NOISE_REDUCTION = 16, + EDGE_ENHANCEMENT = 32, + ANAMORPHIC_SCALING = 64, + STEREO_ADJUSTMENT = 128, +} + +VIDEO_PROCESSOR_FORMAT_CAPS :: enum i32 { // TODO: make bit_set + RGB_INTERLACED = 1, + RGB_PROCAMP = 2, + RGB_LUMA_KEY = 4, + PALETTE_INTERLACED = 8, +} + +VIDEO_PROCESSOR_AUTO_STREAM_CAPS :: enum i32 { // TODO: make bit_set + DENOISE = 1, + DERINGING = 2, + EDGE_ENHANCEMENT = 4, + COLOR_CORRECTION = 8, + FLESH_TONE_MAPPING = 16, + IMAGE_STABILIZATION = 32, + SUPER_RESOLUTION = 64, + ANAMORPHIC_SCALING = 128, +} + +VIDEO_PROCESSOR_STEREO_CAPS :: enum i32 { // TODO: make bit_set + MONO_OFFSET = 1, + ROW_INTERLEAVED = 2, + COLUMN_INTERLEAVED = 4, + CHECKERBOARD = 8, + FLIP_MODE = 16, +} + +VIDEO_PROCESSOR_CAPS :: struct { + DeviceCaps: u32, + FeatureCaps: u32, + FilterCaps: u32, + InputFormatCaps: u32, + AutoStreamCaps: u32, + StereoCaps: u32, + RateConversionCapsCount: u32, + MaxInputStreams: u32, + MaxStreamStates: u32, +} + +VIDEO_PROCESSOR_PROCESSOR_CAPS :: enum i32 { // TODO: make bit_set + DEINTERLACE_BLEND = 1, + DEINTERLACE_BOB = 2, + DEINTERLACE_ADAPTIVE = 4, + DEINTERLACE_MOTION_COMPENSATION = 8, + INVERSE_TELECINE = 16, + FRAME_RATE_CONVERSION = 32, +} + +VIDEO_PROCESSOR_ITELECINE_CAPS :: enum i32 { + _32 = 1, + _22 = 2, + _2224 = 4, + _2332 = 8, + _32322 = 16, + _55 = 32, + _64 = 64, + _87 = 128, + _222222222223 = 256, + OTHER = -2147483648, +} + +VIDEO_PROCESSOR_RATE_CONVERSION_CAPS :: struct { + PastFrames: u32, + FutureFrames: u32, + ProcessorCaps: u32, + ITelecineCaps: u32, + CustomRateCount: u32, +} + +CONTENT_PROTECTION_CAPS :: enum i32 { + SOFTWARE = 1, + HARDWARE = 2, + PROTECTION_ALWAYS_ON = 4, + PARTIAL_DECRYPTION = 8, + CONTENT_KEY = 16, + FRESHEN_SESSION_KEY = 32, + ENCRYPTED_READ_BACK = 64, + ENCRYPTED_READ_BACK_KEY = 128, + SEQUENTIAL_CTR_IV = 256, + ENCRYPT_SLICEDATA_ONLY = 512, + DECRYPTION_BLT = 1024, + HARDWARE_PROTECT_UNCOMPRESSED = 2048, + HARDWARE_PROTECTED_MEMORY_PAGEABLE = 4096, + HARDWARE_TEARDOWN = 8192, + HARDWARE_DRM_COMMUNICATION = 16384, + HARDWARE_DRM_COMMUNICATION_MULTI_THREADED = 32768, +} + + +VIDEO_CONTENT_PROTECTION_CAPS :: struct { + Caps: u32, + KeyExchangeTypeCount: u32, + BlockAlignmentSize: u32, + ProtectedMemorySize: u64, +} + +VIDEO_PROCESSOR_CUSTOM_RATE :: struct { + CustomRate: dxgi.RATIONAL, + OutputFrames: u32, + InputInterlaced: BOOL, + InputFramesOrFields: u32, +} + +VIDEO_PROCESSOR_FILTER :: enum i32 { + BRIGHTNESS = 0, + CONTRAST = 1, + HUE = 2, + SATURATION = 3, + NOISE_REDUCTION = 4, + EDGE_ENHANCEMENT = 5, + ANAMORPHIC_SCALING = 6, + STEREO_ADJUSTMENT = 7, +} + +VIDEO_PROCESSOR_FILTER_RANGE :: struct { + Minimum: i32, + Maximum: i32, + Default: i32, + Multiplier: f32, +} + +VIDEO_FRAME_FORMAT :: enum i32 { + PROGRESSIVE = 0, + INTERLACED_TOP_FIELD_FIRST = 1, + INTERLACED_BOTTOM_FIELD_FIRST = 2, +} + +VIDEO_USAGE :: enum i32 { + PLAYBACK_NORMAL = 0, + OPTIMAL_SPEED = 1, + OPTIMAL_QUALITY = 2, +} + +VIDEO_PROCESSOR_CONTENT_DESC :: struct { + InputFrameFormat: VIDEO_FRAME_FORMAT, + InputFrameRate: dxgi.RATIONAL, + InputWidth: u32, + InputHeight: u32, + OutputFrameRate: dxgi.RATIONAL, + OutputWidth: u32, + OutputHeight: u32, + Usage: VIDEO_USAGE, +} + + +IVideoProcessorEnumerator_UUID_STRING :: "31627037-53AB-4200-9061-05FAA9AB45F9" +IVideoProcessorEnumerator_UUID := &IID{0x31627037, 0x53AB, 0x4200, {0x90, 0x61, 0x05, 0xFA, 0xA9, 0xAB, 0x45, 0xF9}} +IVideoProcessorEnumerator :: struct #raw_union { + #subtype id3d11devicechild: IDeviceChild, + using id3d11videoprocessorenumerator_vtable: ^IVideoProcessorEnumerator_VTable, +} +IVideoProcessorEnumerator_VTable :: struct { + using id3d11devicechild_vtable: IDeviceChild_VTable, + GetVideoProcessorContentDesc: proc "stdcall" (this: ^IVideoProcessorEnumerator, pContentDesc: ^VIDEO_PROCESSOR_CONTENT_DESC) -> HRESULT, + CheckVideoProcessorFormat: proc "stdcall" (this: ^IVideoProcessorEnumerator, Format: dxgi.FORMAT, pFlags: ^u32) -> HRESULT, + GetVideoProcessorCaps: proc "stdcall" (this: ^IVideoProcessorEnumerator, pCaps: ^VIDEO_PROCESSOR_CAPS) -> HRESULT, + GetVideoProcessorRateConversionCaps: proc "stdcall" (this: ^IVideoProcessorEnumerator, TypeIndex: u32, pCaps: ^VIDEO_PROCESSOR_RATE_CONVERSION_CAPS) -> HRESULT, + GetVideoProcessorCustomRate: proc "stdcall" (this: ^IVideoProcessorEnumerator, TypeIndex: u32, CustomRateIndex: u32, pRate: ^VIDEO_PROCESSOR_CUSTOM_RATE) -> HRESULT, + GetVideoProcessorFilterRange: proc "stdcall" (this: ^IVideoProcessorEnumerator, Filter: VIDEO_PROCESSOR_FILTER, pRange: ^VIDEO_PROCESSOR_FILTER_RANGE) -> HRESULT, +} + + +VIDEO_COLOR_RGBA :: struct { + R: f32, + G: f32, + B: f32, + A: f32, +} + +VIDEO_COLOR_YCbCrA :: struct { + Y: f32, + Cb: f32, + Cr: f32, + A: f32, +} + +VIDEO_COLOR :: struct { + using _: struct #raw_union { + YCbCr: VIDEO_COLOR_YCbCrA, + RGBA: VIDEO_COLOR_RGBA, + }, +} + +VIDEO_PROCESSOR_NOMINAL_RANGE :: enum i32 { + UNDEFINED = 0, + _16_235 = 1, + _0_255 = 2, +} + +VIDEO_PROCESSOR_COLOR_SPACE_FLAG :: enum u32 { + Usage = 0, + RGB_Range = 1, + YCbCr_Matrix = 2, + YCbCr_xvYCC = 3, + Nominal_Range = 4, + Reserved = 6, +} + +VIDEO_PROCESSOR_COLOR_SPACE :: distinct bit_set[VIDEO_PROCESSOR_COLOR_SPACE_FLAG; u32] + +VIDEO_PROCESSOR_ALPHA_FILL_MODE :: enum i32 { + OPAQUE = 0, + BACKGROUND = 1, + DESTINATION = 2, + SOURCE_STREAM = 3, +} + +VIDEO_PROCESSOR_OUTPUT_RATE :: enum i32 { + NORMAL = 0, + HALF = 1, + CUSTOM = 2, +} + +VIDEO_PROCESSOR_STEREO_FORMAT :: enum i32 { + MONO = 0, + HORIZONTAL = 1, + VERTICAL = 2, + SEPARATE = 3, + MONO_OFFSET = 4, + ROW_INTERLEAVED = 5, + COLUMN_INTERLEAVED = 6, + CHECKERBOARD = 7, +} + +VIDEO_PROCESSOR_STEREO_FLIP_MODE :: enum i32 { + NONE = 0, + FRAME0 = 1, + FRAME1 = 2, +} + +VIDEO_PROCESSOR_ROTATION :: enum i32 { + IDENTITY = 0, + _90 = 1, + _180 = 2, + _270 = 3, +} + +VIDEO_PROCESSOR_STREAM :: struct { + Enable: BOOL, + OutputIndex: u32, + InputFrameOrField: u32, + PastFrames: u32, + FutureFrames: u32, + + ppPastSurfaces: ^^IVideoProcessorInputView, + pInputSurface: ^IVideoProcessorInputView, + + ppFutureSurfaces: ^^IVideoProcessorInputView, + + ppPastSurfacesRight: ^^IVideoProcessorInputView, + pInputSurfaceRight: ^IVideoProcessorInputView, + + ppFutureSurfacesRight: ^^IVideoProcessorInputView, +} + + +IVideoProcessor_UUID_STRING :: "1D7B0652-185F-41C6-85CE-0C5BE3D4AE6C" +IVideoProcessor_UUID := &IID{0x1D7B0652, 0x185F, 0x41C6, {0x85, 0xCE, 0x0C, 0x5B, 0xE3, 0xD4, 0xAE, 0x6C}} +IVideoProcessor :: struct #raw_union { + #subtype id3d11devicechild: IDeviceChild, + using id3d11videoprocessor_vtable: ^IVideoProcessor_VTable, +} +IVideoProcessor_VTable :: struct { + using id3d11devicechild_vtable: IDeviceChild_VTable, + GetContentDesc: proc "stdcall" (this: ^IVideoProcessor, pDesc: ^VIDEO_PROCESSOR_CONTENT_DESC), + GetRateConversionCaps: proc "stdcall" (this: ^IVideoProcessor, pCaps: ^VIDEO_PROCESSOR_RATE_CONVERSION_CAPS), +} + + +OMAC :: struct { + Omac: [16]u8, +} + +AUTHENTICATED_CHANNEL_TYPE :: enum i32 { + D3D11 = 1, + DRIVER_SOFTWARE = 2, + DRIVER_HARDWARE = 3, +} + + +IAuthenticatedChannel_UUID_STRING :: "3015A308-DCBD-47AA-A747-192486D14D4A" +IAuthenticatedChannel_UUID := &IID{0x3015A308, 0xDCBD, 0x47AA, {0xA7, 0x47, 0x19, 0x24, 0x86, 0xD1, 0x4D, 0x4A}} +IAuthenticatedChannel :: struct #raw_union { + #subtype id3d11devicechild: IDeviceChild, + using id3d11authenticatedchannel_vtable: ^IAuthenticatedChannel_VTable, +} +IAuthenticatedChannel_VTable :: struct { + using id3d11devicechild_vtable: IDeviceChild_VTable, + GetCertificateSize: proc "stdcall" (this: ^IAuthenticatedChannel, pCertificateSize: ^u32) -> HRESULT, + GetCertificate: proc "stdcall" (this: ^IAuthenticatedChannel, CertificateSize: u32, pCertificate: cstring) -> HRESULT, + GetChannelHandle: proc "stdcall" (this: ^IAuthenticatedChannel, pChannelHandle: ^HANDLE), +} + + +AUTHENTICATED_QUERY_INPUT :: struct { + QueryType: GUID, + hChannel: HANDLE, + SequenceNumber: u32, +} + +AUTHENTICATED_QUERY_OUTPUT :: struct { + omac: OMAC, + QueryType: GUID, + hChannel: HANDLE, + SequenceNumber: u32, + ReturnCode: HRESULT, +} + +AUTHENTICATED_PROTECTION_FLAG :: enum u32 { + ProtectionEnabled = 0, + OverlayOrFullscreenRequired = 1, + Reserved = 2, +} +AUTHENTICATED_PROTECTION_FLAGS :: distinct bit_set[AUTHENTICATED_PROTECTION_FLAG; u32] + +AUTHENTICATED_QUERY_PROTECTION_OUTPUT :: struct { + Output: AUTHENTICATED_QUERY_OUTPUT, + ProtectionFlags: AUTHENTICATED_PROTECTION_FLAGS, +} + +AUTHENTICATED_QUERY_CHANNEL_TYPE_OUTPUT :: struct { + Output: AUTHENTICATED_QUERY_OUTPUT, + ChannelType: AUTHENTICATED_CHANNEL_TYPE, +} + +AUTHENTICATED_QUERY_DEVICE_HANDLE_OUTPUT :: struct { + Output: AUTHENTICATED_QUERY_OUTPUT, + DeviceHandle: HANDLE, +} + +AUTHENTICATED_QUERY_CRYPTO_SESSION_INPUT :: struct { + Input: AUTHENTICATED_QUERY_INPUT, + DecoderHandle: HANDLE, +} + +AUTHENTICATED_QUERY_CRYPTO_SESSION_OUTPUT :: struct { + Output: AUTHENTICATED_QUERY_OUTPUT, + DecoderHandle: HANDLE, + CryptoSessionHandle: HANDLE, + DeviceHandle: HANDLE, +} + +AUTHENTICATED_QUERY_RESTRICTED_SHARED_RESOURCE_PROCESS_COUNT_OUTPUT :: struct { + Output: AUTHENTICATED_QUERY_OUTPUT, + RestrictedSharedResourceProcessCount: u32, +} + +AUTHENTICATED_QUERY_RESTRICTED_SHARED_RESOURCE_PROCESS_INPUT :: struct { + Input: AUTHENTICATED_QUERY_INPUT, + ProcessIndex: u32, +} + +AUTHENTICATED_PROCESS_IDENTIFIER_TYPE :: enum i32 { + UNKNOWN = 0, + DWM = 1, + HANDLE = 2, +} + +AUTHENTICATED_QUERY_RESTRICTED_SHARED_RESOURCE_PROCESS_OUTPUT :: struct { + Output: AUTHENTICATED_QUERY_OUTPUT, + ProcessIndex: u32, + ProcessIdentifier: AUTHENTICATED_PROCESS_IDENTIFIER_TYPE, + ProcessHandle: HANDLE, +} + +AUTHENTICATED_QUERY_UNRESTRICTED_PROTECTED_SHARED_RESOURCE_COUNT_OUTPUT :: struct { + Output: AUTHENTICATED_QUERY_OUTPUT, + UnrestrictedProtectedSharedResourceCount: u32, +} + +AUTHENTICATED_QUERY_OUTPUT_ID_COUNT_INPUT :: struct { + Input: AUTHENTICATED_QUERY_INPUT, + DeviceHandle: HANDLE, + CryptoSessionHandle: HANDLE, +} + +AUTHENTICATED_QUERY_OUTPUT_ID_COUNT_OUTPUT :: struct { + Output: AUTHENTICATED_QUERY_OUTPUT, + DeviceHandle: HANDLE, + CryptoSessionHandle: HANDLE, + OutputIDCount: u32, +} + +AUTHENTICATED_QUERY_OUTPUT_ID_INPUT :: struct { + Input: AUTHENTICATED_QUERY_INPUT, + DeviceHandle: HANDLE, + CryptoSessionHandle: HANDLE, + OutputIDIndex: u32, +} + +AUTHENTICATED_QUERY_OUTPUT_ID_OUTPUT :: struct { + Output: AUTHENTICATED_QUERY_OUTPUT, + DeviceHandle: HANDLE, + CryptoSessionHandle: HANDLE, + OutputIDIndex: u32, + OutputID: u64, +} + +BUS_TYPE :: enum i32 { + OTHER = 0, + PCI = 1, + PCIX = 2, + PCIEXPRESS = 3, + AGP = 4, + NSIDE_OF_CHIPSET = 65536, + RACKS_ON_MOTHER_BOARD_TO_CHIP = 131072, + RACKS_ON_MOTHER_BOARD_TO_SOCKET = 196608, + AUGHTER_BOARD_CONNECTOR = 262144, + AUGHTER_BOARD_CONNECTOR_INSIDE_OF_NUAE = 327680, + ON_STANDARD = -2147483648, +} + +AUTHENTICATED_QUERY_ACESSIBILITY_OUTPUT :: struct { + Output: AUTHENTICATED_QUERY_OUTPUT, + BusType: BUS_TYPE, + AccessibleInContiguousBlocks: BOOL, + AccessibleInNonContiguousBlocks: BOOL, +} + +AUTHENTICATED_QUERY_ACCESSIBILITY_OUTPUT :: AUTHENTICATED_QUERY_ACESSIBILITY_OUTPUT + +AUTHENTICATED_QUERY_ACCESSIBILITY_ENCRYPTION_GUID_COUNT_OUTPUT :: struct { + Output: AUTHENTICATED_QUERY_OUTPUT, + EncryptionGuidCount: u32, +} + +AUTHENTICATED_QUERY_ACCESSIBILITY_ENCRYPTION_GUID_INPUT :: struct { + Input: AUTHENTICATED_QUERY_INPUT, + EncryptionGuidIndex: u32, +} + +AUTHENTICATED_QUERY_ACCESSIBILITY_ENCRYPTION_GUID_OUTPUT :: struct { + Output: AUTHENTICATED_QUERY_OUTPUT, + EncryptionGuidIndex: u32, + EncryptionGuid: GUID, +} + +AUTHENTICATED_QUERY_CURRENT_ACCESSIBILITY_ENCRYPTION_OUTPUT :: struct { + Output: AUTHENTICATED_QUERY_OUTPUT, + EncryptionGuid: GUID, +} + +AUTHENTICATED_CONFIGURE_INPUT :: struct { + omac: OMAC, + ConfigureType: GUID, + hChannel: HANDLE, + SequenceNumber: u32, +} + +AUTHENTICATED_CONFIGURE_OUTPUT :: struct { + omac: OMAC, + ConfigureType: GUID, + hChannel: HANDLE, + SequenceNumber: u32, + ReturnCode: HRESULT, +} + +AUTHENTICATED_CONFIGURE_INITIALIZE_INPUT :: struct { + Parameters: AUTHENTICATED_CONFIGURE_INPUT, + StartSequenceQuery: u32, + StartSequenceConfigure: u32, +} + +AUTHENTICATED_CONFIGURE_PROTECTION_INPUT :: struct { + Parameters: AUTHENTICATED_CONFIGURE_INPUT, + Protections: AUTHENTICATED_PROTECTION_FLAGS, +} + +AUTHENTICATED_CONFIGURE_CRYPTO_SESSION_INPUT :: struct { + Parameters: AUTHENTICATED_CONFIGURE_INPUT, + DecoderHandle: HANDLE, + CryptoSessionHandle: HANDLE, + DeviceHandle: HANDLE, +} + +AUTHENTICATED_CONFIGURE_SHARED_RESOURCE_INPUT :: struct { + Parameters: AUTHENTICATED_CONFIGURE_INPUT, + ProcessType: AUTHENTICATED_PROCESS_IDENTIFIER_TYPE, + ProcessHandle: HANDLE, + AllowAccess: BOOL, +} + +AUTHENTICATED_CONFIGURE_ACCESSIBLE_ENCRYPTION_INPUT :: struct { + Parameters: AUTHENTICATED_CONFIGURE_INPUT, + EncryptionGuid: GUID, +} + + + +ICryptoSession_UUID_STRING :: "9B32F9AD-BDCC-40A6-A39D-D5C865845720" +ICryptoSession_UUID := &IID{0x9B32F9AD, 0xBDCC, 0x40A6, {0xA3, 0x9D, 0xD5, 0xC8, 0x65, 0x84, 0x57, 0x20}} +ICryptoSession :: struct #raw_union { + #subtype id3d11devicechild: IDeviceChild, + using id3d11cryptosession_vtable: ^ICryptoSession_VTable, +} +ICryptoSession_VTable :: struct { + using id3d11devicechild_vtable: IDeviceChild_VTable, + GetCryptoType: proc "stdcall" (this: ^ICryptoSession, pCryptoType: ^GUID), + GetDecoderProfile: proc "stdcall" (this: ^ICryptoSession, pDecoderProfile: ^GUID), + GetCertificateSize: proc "stdcall" (this: ^ICryptoSession, pCertificateSize: ^u32) -> HRESULT, + GetCertificate: proc "stdcall" (this: ^ICryptoSession, CertificateSize: u32, pCertificate: cstring) -> HRESULT, + GetCryptoSessionHandle: proc "stdcall" (this: ^ICryptoSession, pCryptoSessionHandle: ^HANDLE), +} + + +VDOV_DIMENSION :: enum i32 { + UNKNOWN = 0, + TEXTURE2D = 1, +} + +TEX2D_VDOV :: struct { + ArraySlice: u32, +} + +VIDEO_DECODER_OUTPUT_VIEW_DESC :: struct { + DecodeProfile: GUID, + ViewDimension: VDOV_DIMENSION, + using _: struct #raw_union { + Texture2D: TEX2D_VDOV, + }, +} + + +IVideoDecoderOutputView_UUID_STRING :: "C2931AEA-2A85-4F20-860F-FBA1FD256E18" +IVideoDecoderOutputView_UUID := &IID{0xC2931AEA, 0x2A85, 0x4F20, {0x86, 0x0F, 0xFB, 0xA1, 0xFD, 0x25, 0x6E, 0x18}} +IVideoDecoderOutputView :: struct #raw_union { + #subtype id3d11view: IView, + using id3d11videodecoderoutputview_vtable: ^IVideoDecoderOutputView_VTable, +} +IVideoDecoderOutputView_VTable :: struct { + using id3d11view_vtable: IView_VTable, + GetDesc: proc "stdcall" (this: ^IVideoDecoderOutputView, pDesc: ^VIDEO_DECODER_OUTPUT_VIEW_DESC), +} + + +VPIV_DIMENSION :: enum i32 { + UNKNOWN = 0, + TEXTURE2D = 1, +} + +TEX2D_VPIV :: struct { + MipSlice: u32, + ArraySlice: u32, +} + +VIDEO_PROCESSOR_INPUT_VIEW_DESC :: struct { + FourCC: u32, + ViewDimension: VPIV_DIMENSION, + using _: struct #raw_union { + Texture2D: TEX2D_VPIV, + }, +} + + +IVideoProcessorInputView_UUID_STRING :: "11EC5A5F-51DC-4945-AB34-6E8C21300EA5" +IVideoProcessorInputView_UUID := &IID{0x11EC5A5F, 0x51DC, 0x4945, {0xAB, 0x34, 0x6E, 0x8C, 0x21, 0x30, 0x0E, 0xA5}} +IVideoProcessorInputView :: struct #raw_union { + #subtype id3d11view: IView, + using id3d11videoprocessorinputview_vtable: ^IVideoProcessorInputView_VTable, +} +IVideoProcessorInputView_VTable :: struct { + using id3d11view_vtable: IView_VTable, + GetDesc: proc "stdcall" (this: ^IVideoProcessorInputView, pDesc: ^VIDEO_PROCESSOR_INPUT_VIEW_DESC), +} + + +VPOV_DIMENSION :: enum i32 { + UNKNOWN = 0, + TEXTURE2D = 1, + TEXTURE2DARRAY = 2, +} + +TEX2D_VPOV :: struct { + MipSlice: u32, +} + +TEX2D_ARRAY_VPOV :: struct { + MipSlice: u32, + FirstArraySlice: u32, + ArraySize: u32, +} + +VIDEO_PROCESSOR_OUTPUT_VIEW_DESC :: struct { + ViewDimension: VPOV_DIMENSION, + using _: struct #raw_union { + Texture2D: TEX2D_VPOV, + Texture2DArray: TEX2D_ARRAY_VPOV, + }, +} + + +IVideoProcessorOutputView_UUID_STRING :: "A048285E-25A9-4527-BD93-D68B68C44254" +IVideoProcessorOutputView_UUID := &IID{0xA048285E, 0x25A9, 0x4527, {0xBD, 0x93, 0xD6, 0x8B, 0x68, 0xC4, 0x42, 0x54}} +IVideoProcessorOutputView :: struct #raw_union { + #subtype id3d11view: IView, + using id3d11videoprocessoroutputview_vtable: ^IVideoProcessorOutputView_VTable, +} +IVideoProcessorOutputView_VTable :: struct { + using id3d11view_vtable: IView_VTable, + GetDesc: proc "stdcall" (this: ^IVideoProcessorOutputView, pDesc: ^VIDEO_PROCESSOR_OUTPUT_VIEW_DESC), +} + + + +IVideoContext_UUID_STRING :: "61F21C45-3C0E-4A74-9CEA-67100D9AD5E4" +IVideoContext_UUID := &IID{0x61F21C45, 0x3C0E, 0x4A74, {0x9C, 0xEA, 0x67, 0x10, 0x0D, 0x9A, 0xD5, 0xE4}} +IVideoContext :: struct #raw_union { + #subtype id3d11devicechild: IDeviceChild, + using id3d11videocontext_vtable: ^IVideoContext_VTable, +} +IVideoContext_VTable :: struct { + using id3d11devicechild_vtable: IDeviceChild_VTable, + GetDecoderBuffer: proc "stdcall" (this: ^IVideoContext, pDecoder: ^IVideoDecoder, Type: VIDEO_DECODER_BUFFER_TYPE, pBufferSize: ^u32, ppBuffer: ^rawptr) -> HRESULT, + ReleaseDecoderBuffer: proc "stdcall" (this: ^IVideoContext, pDecoder: ^IVideoDecoder, Type: VIDEO_DECODER_BUFFER_TYPE) -> HRESULT, + DecoderBeginFrame: proc "stdcall" (this: ^IVideoContext, pDecoder: ^IVideoDecoder, pView: ^IVideoDecoderOutputView, ContentKeySize: u32, pContentKey: rawptr) -> HRESULT, + DecoderEndFrame: proc "stdcall" (this: ^IVideoContext, pDecoder: ^IVideoDecoder) -> HRESULT, + SubmitDecoderBuffers: proc "stdcall" (this: ^IVideoContext, pDecoder: ^IVideoDecoder, NumBuffers: u32, pBufferDesc: ^VIDEO_DECODER_BUFFER_DESC) -> HRESULT, + DecoderExtension: proc "stdcall" (this: ^IVideoContext, pDecoder: ^IVideoDecoder, pExtensionData: ^VIDEO_DECODER_EXTENSION) -> APP_DEPRECATED_HRESULT, + VideoProcessorSetOutputTargetRect: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, Enable: BOOL, pRect: ^RECT), + VideoProcessorSetOutputBackgroundColor: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, YCbCr: BOOL, pColor: ^VIDEO_COLOR), + VideoProcessorSetOutputColorSpace: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, pColorSpace: ^VIDEO_PROCESSOR_COLOR_SPACE), + VideoProcessorSetOutputAlphaFillMode: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, AlphaFillMode: VIDEO_PROCESSOR_ALPHA_FILL_MODE, StreamIndex: u32), + VideoProcessorSetOutputConstriction: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, Enable: BOOL, Size: SIZE), + VideoProcessorSetOutputStereoMode: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, Enable: BOOL), + VideoProcessorSetOutputExtension: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, pExtensionGuid: ^GUID, DataSize: u32, pData: rawptr) -> APP_DEPRECATED_HRESULT, + VideoProcessorGetOutputTargetRect: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, Enabled: ^BOOL, pRect: ^RECT), + VideoProcessorGetOutputBackgroundColor: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, pYCbCr: ^BOOL, pColor: ^VIDEO_COLOR), + VideoProcessorGetOutputColorSpace: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, pColorSpace: ^VIDEO_PROCESSOR_COLOR_SPACE), + VideoProcessorGetOutputAlphaFillMode: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, pAlphaFillMode: ^VIDEO_PROCESSOR_ALPHA_FILL_MODE, pStreamIndex: ^u32), + VideoProcessorGetOutputConstriction: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, pEnabled: ^BOOL, pSize: ^SIZE), + VideoProcessorGetOutputStereoMode: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, pEnabled: ^BOOL), + VideoProcessorGetOutputExtension: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, pExtensionGuid: ^GUID, DataSize: u32, pData: rawptr) -> APP_DEPRECATED_HRESULT, + VideoProcessorSetStreamFrameFormat: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, StreamIndex: u32, FrameFormat: VIDEO_FRAME_FORMAT), + VideoProcessorSetStreamColorSpace: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, StreamIndex: u32, pColorSpace: ^VIDEO_PROCESSOR_COLOR_SPACE), + VideoProcessorSetStreamOutputRate: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, StreamIndex: u32, OutputRate: VIDEO_PROCESSOR_OUTPUT_RATE, RepeatFrame: BOOL, pCustomRate: ^dxgi.RATIONAL), + VideoProcessorSetStreamSourceRect: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, StreamIndex: u32, Enable: BOOL, pRect: ^RECT), + VideoProcessorSetStreamDestRect: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, StreamIndex: u32, Enable: BOOL, pRect: ^RECT), + VideoProcessorSetStreamAlpha: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, StreamIndex: u32, Enable: BOOL, Alpha: f32), + VideoProcessorSetStreamPalette: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, StreamIndex: u32, Count: u32, pEntries: ^u32), + VideoProcessorSetStreamPixelAspectRatio: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, StreamIndex: u32, Enable: BOOL, pSourceAspectRatio: ^dxgi.RATIONAL, pDestinationAspectRatio: ^dxgi.RATIONAL), + VideoProcessorSetStreamLumaKey: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, StreamIndex: u32, Enable: BOOL, Lower: f32, Upper: f32), + VideoProcessorSetStreamStereoFormat: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, StreamIndex: u32, Enable: BOOL, Format: VIDEO_PROCESSOR_STEREO_FORMAT, LeftViewFrame0: BOOL, BaseViewFrame0: BOOL, FlipMode: VIDEO_PROCESSOR_STEREO_FLIP_MODE, MonoOffset: i32), + VideoProcessorSetStreamAutoProcessingMode: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, StreamIndex: u32, Enable: BOOL), + VideoProcessorSetStreamFilter: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, StreamIndex: u32, Filter: VIDEO_PROCESSOR_FILTER, Enable: BOOL, Level: i32), + VideoProcessorSetStreamExtension: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, StreamIndex: u32, pExtensionGuid: ^GUID, DataSize: u32, pData: rawptr) -> APP_DEPRECATED_HRESULT, + VideoProcessorGetStreamFrameFormat: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, StreamIndex: u32, pFrameFormat: ^VIDEO_FRAME_FORMAT), + VideoProcessorGetStreamColorSpace: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, StreamIndex: u32, pColorSpace: ^VIDEO_PROCESSOR_COLOR_SPACE), + VideoProcessorGetStreamOutputRate: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, StreamIndex: u32, pOutputRate: ^VIDEO_PROCESSOR_OUTPUT_RATE, pRepeatFrame: ^BOOL, pCustomRate: ^dxgi.RATIONAL), + VideoProcessorGetStreamSourceRect: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, StreamIndex: u32, pEnabled: ^BOOL, pRect: ^RECT), + VideoProcessorGetStreamDestRect: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, StreamIndex: u32, pEnabled: ^BOOL, pRect: ^RECT), + VideoProcessorGetStreamAlpha: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, StreamIndex: u32, pEnabled: ^BOOL, pAlpha: ^f32), + VideoProcessorGetStreamPalette: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, StreamIndex: u32, Count: u32, pEntries: ^u32), + VideoProcessorGetStreamPixelAspectRatio: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, StreamIndex: u32, pEnabled: ^BOOL, pSourceAspectRatio: ^dxgi.RATIONAL, pDestinationAspectRatio: ^dxgi.RATIONAL), + VideoProcessorGetStreamLumaKey: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, StreamIndex: u32, pEnabled: ^BOOL, pLower: ^f32, pUpper: ^f32), + VideoProcessorGetStreamStereoFormat: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, StreamIndex: u32, pEnable: ^BOOL, pFormat: ^VIDEO_PROCESSOR_STEREO_FORMAT, pLeftViewFrame0: ^BOOL, pBaseViewFrame0: ^BOOL, pFlipMode: ^VIDEO_PROCESSOR_STEREO_FLIP_MODE, MonoOffset: ^i32), + VideoProcessorGetStreamAutoProcessingMode: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, StreamIndex: u32, pEnabled: ^BOOL), + VideoProcessorGetStreamFilter: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, StreamIndex: u32, Filter: VIDEO_PROCESSOR_FILTER, pEnabled: ^BOOL, pLevel: ^i32), + VideoProcessorGetStreamExtension: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, StreamIndex: u32, pExtensionGuid: ^GUID, DataSize: u32, pData: rawptr) -> APP_DEPRECATED_HRESULT, + VideoProcessorBlt: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, pView: ^IVideoProcessorOutputView, OutputFrame: u32, StreamCount: u32, pStreams: ^VIDEO_PROCESSOR_STREAM) -> HRESULT, + NegotiateCryptoSessionKeyExchange: proc "stdcall" (this: ^IVideoContext, pCryptoSession: ^ICryptoSession, DataSize: u32, pData: rawptr) -> HRESULT, + EncryptionBlt: proc "stdcall" (this: ^IVideoContext, pCryptoSession: ^ICryptoSession, pSrcSurface: ^ITexture2D, pDstSurface: ^ITexture2D, IVSize: u32, pIV: rawptr), + DecryptionBlt: proc "stdcall" (this: ^IVideoContext, pCryptoSession: ^ICryptoSession, pSrcSurface: ^ITexture2D, pDstSurface: ^ITexture2D, pEncryptedBlockInfo: ^ENCRYPTED_BLOCK_INFO, ContentKeySize: u32, pContentKey: rawptr, IVSize: u32, pIV: rawptr), + StartSessionKeyRefresh: proc "stdcall" (this: ^IVideoContext, pCryptoSession: ^ICryptoSession, RandomNumberSize: u32, pRandomNumber: rawptr), + FinishSessionKeyRefresh: proc "stdcall" (this: ^IVideoContext, pCryptoSession: ^ICryptoSession), + GetEncryptionBltKey: proc "stdcall" (this: ^IVideoContext, pCryptoSession: ^ICryptoSession, KeySize: u32, pReadbackKey: rawptr) -> HRESULT, + NegotiateAuthenticatedChannelKeyExchange: proc "stdcall" (this: ^IVideoContext, pChannel: ^IAuthenticatedChannel, DataSize: u32, pData: rawptr) -> HRESULT, + QueryAuthenticatedChannel: proc "stdcall" (this: ^IVideoContext, pChannel: ^IAuthenticatedChannel, InputSize: u32, pInput: rawptr, OutputSize: u32, pOutput: rawptr) -> HRESULT, + ConfigureAuthenticatedChannel: proc "stdcall" (this: ^IVideoContext, pChannel: ^IAuthenticatedChannel, InputSize: u32, pInput: rawptr, pOutput: ^AUTHENTICATED_CONFIGURE_OUTPUT) -> HRESULT, + VideoProcessorSetStreamRotation: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, StreamIndex: u32, Enable: BOOL, Rotation: VIDEO_PROCESSOR_ROTATION), + VideoProcessorGetStreamRotation: proc "stdcall" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, StreamIndex: u32, pEnable: ^BOOL, pRotation: ^VIDEO_PROCESSOR_ROTATION), +} + + + +IVideoDevice_UUID_STRING :: "10EC4D5B-975A-4689-B9E4-D0AAC30FE333" +IVideoDevice_UUID := &IID{0x10EC4D5B, 0x975A, 0x4689, {0xB9, 0xE4, 0xD0, 0xAA, 0xC3, 0x0F, 0xE3, 0x33}} +IVideoDevice :: struct #raw_union { + #subtype iunknown: IUnknown, + using id3d11videodevice_vtable: ^IVideoDevice_VTable, +} +IVideoDevice_VTable :: struct { + using iunknown_vtable: IUnknown_VTable, + CreateVideoDecoder: proc "stdcall" (this: ^IVideoDevice, pVideoDesc: ^VIDEO_DECODER_DESC, pConfig: ^VIDEO_DECODER_CONFIG, ppDecoder: ^^IVideoDecoder) -> HRESULT, + CreateVideoProcessor: proc "stdcall" (this: ^IVideoDevice, pEnum: ^IVideoProcessorEnumerator, RateConversionIndex: u32, ppVideoProcessor: ^^IVideoProcessor) -> HRESULT, + CreateAuthenticatedChannel: proc "stdcall" (this: ^IVideoDevice, ChannelType: AUTHENTICATED_CHANNEL_TYPE, ppAuthenticatedChannel: ^^IAuthenticatedChannel) -> HRESULT, + CreateCryptoSession: proc "stdcall" (this: ^IVideoDevice, pCryptoType: ^GUID, pDecoderProfile: ^GUID, pKeyExchangeType: ^GUID, ppCryptoSession: ^^ICryptoSession) -> HRESULT, + CreateVideoDecoderOutputView: proc "stdcall" (this: ^IVideoDevice, pResource: ^IResource, pDesc: ^VIDEO_DECODER_OUTPUT_VIEW_DESC, ppVDOVView: ^^IVideoDecoderOutputView) -> HRESULT, + CreateVideoProcessorInputView: proc "stdcall" (this: ^IVideoDevice, pResource: ^IResource, pEnum: ^IVideoProcessorEnumerator, pDesc: ^VIDEO_PROCESSOR_INPUT_VIEW_DESC, ppVPIView: ^^IVideoProcessorInputView) -> HRESULT, + CreateVideoProcessorOutputView: proc "stdcall" (this: ^IVideoDevice, pResource: ^IResource, pEnum: ^IVideoProcessorEnumerator, pDesc: ^VIDEO_PROCESSOR_OUTPUT_VIEW_DESC, ppVPOView: ^^IVideoProcessorOutputView) -> HRESULT, + CreateVideoProcessorEnumerator: proc "stdcall" (this: ^IVideoDevice, pDesc: ^VIDEO_PROCESSOR_CONTENT_DESC, ppEnum: ^^IVideoProcessorEnumerator) -> HRESULT, + GetVideoDecoderProfileCount: proc "stdcall" (this: ^IVideoDevice) -> u32, + GetVideoDecoderProfile: proc "stdcall" (this: ^IVideoDevice, Index: u32, pDecoderProfile: ^GUID) -> HRESULT, + CheckVideoDecoderFormat: proc "stdcall" (this: ^IVideoDevice, pDecoderProfile: ^GUID, Format: dxgi.FORMAT, pSupported: ^BOOL) -> HRESULT, + GetVideoDecoderConfigCount: proc "stdcall" (this: ^IVideoDevice, pDesc: ^VIDEO_DECODER_DESC, pCount: ^u32) -> HRESULT, + GetVideoDecoderConfig: proc "stdcall" (this: ^IVideoDevice, pDesc: ^VIDEO_DECODER_DESC, Index: u32, pConfig: ^VIDEO_DECODER_CONFIG) -> HRESULT, + GetContentProtectionCaps: proc "stdcall" (this: ^IVideoDevice, pCryptoType: ^GUID, pDecoderProfile: ^GUID, pCaps: ^VIDEO_CONTENT_PROTECTION_CAPS) -> HRESULT, + CheckCryptoKeyExchange: proc "stdcall" (this: ^IVideoDevice, pCryptoType: ^GUID, pDecoderProfile: ^GUID, Index: u32, pKeyExchangeType: ^GUID) -> HRESULT, + SetPrivateData: proc "stdcall" (this: ^IVideoDevice, guid: ^GUID, DataSize: u32, pData: rawptr) -> HRESULT, + SetPrivateDataInterface: proc "stdcall" (this: ^IVideoDevice, guid: ^GUID, pData: ^IUnknown) -> HRESULT, +} + + + +IDevice_UUID_STRING :: "DB6F6DDB-AC77-4E88-8253-819DF9BBF140" +IDevice_UUID := &IID{0xDB6F6DDB, 0xAC77, 0x4E88, {0x82, 0x53, 0x81, 0x9D, 0xF9, 0xBB, 0xF1, 0x40}} +IDevice :: struct #raw_union { + #subtype iunknown: IUnknown, + using id3d11device_vtable: ^IDevice_VTable, +} +IDevice_VTable :: struct { + using iunknown_vtable: IUnknown_VTable, + CreateBuffer: proc "stdcall" (this: ^IDevice, pDesc: ^BUFFER_DESC, pInitialData: ^SUBRESOURCE_DATA, ppBuffer: ^^IBuffer) -> HRESULT, + CreateTexture1D: proc "stdcall" (this: ^IDevice, pDesc: ^TEXTURE1D_DESC, pInitialData: ^SUBRESOURCE_DATA, ppTexture1D: ^^ITexture1D) -> HRESULT, + CreateTexture2D: proc "stdcall" (this: ^IDevice, pDesc: ^TEXTURE2D_DESC, pInitialData: ^SUBRESOURCE_DATA, ppTexture2D: ^^ITexture2D) -> HRESULT, + CreateTexture3D: proc "stdcall" (this: ^IDevice, pDesc: ^TEXTURE3D_DESC, pInitialData: ^SUBRESOURCE_DATA, ppTexture3D: ^^ITexture3D) -> HRESULT, + CreateShaderResourceView: proc "stdcall" (this: ^IDevice, pResource: ^IResource, pDesc: ^SHADER_RESOURCE_VIEW_DESC, ppSRView: ^^IShaderResourceView) -> HRESULT, + CreateUnorderedAccessView: proc "stdcall" (this: ^IDevice, pResource: ^IResource, pDesc: ^UNORDERED_ACCESS_VIEW_DESC, ppUAView: ^^IUnorderedAccessView) -> HRESULT, + CreateRenderTargetView: proc "stdcall" (this: ^IDevice, pResource: ^IResource, pDesc: ^RENDER_TARGET_VIEW_DESC, ppRTView: ^^IRenderTargetView) -> HRESULT, + CreateDepthStencilView: proc "stdcall" (this: ^IDevice, pResource: ^IResource, pDesc: ^DEPTH_STENCIL_VIEW_DESC, ppDepthStencilView: ^^IDepthStencilView) -> HRESULT, + CreateInputLayout: proc "stdcall" (this: ^IDevice, pInputElementDescs: ^INPUT_ELEMENT_DESC, NumElements: u32, pShaderBytecodeWithInputSignature: rawptr, BytecodeLength: SIZE_T, ppInputLayout: ^^IInputLayout) -> HRESULT, + CreateVertexShader: proc "stdcall" (this: ^IDevice, pShaderBytecode: rawptr, BytecodeLength: SIZE_T, pClassLinkage: ^IClassLinkage, ppVertexShader: ^^IVertexShader) -> HRESULT, + CreateGeometryShader: proc "stdcall" (this: ^IDevice, pShaderBytecode: rawptr, BytecodeLength: SIZE_T, pClassLinkage: ^IClassLinkage, ppGeometryShader: ^^IGeometryShader) -> HRESULT, + CreateGeometryShaderWithStreamOutput: proc "stdcall" (this: ^IDevice, pShaderBytecode: rawptr, BytecodeLength: SIZE_T, pSODeclaration: ^SO_DECLARATION_ENTRY, NumEntries: u32, pBufferStrides: ^u32, NumStrides: u32, RasterizedStream: u32, pClassLinkage: ^IClassLinkage, ppGeometryShader: ^^IGeometryShader) -> HRESULT, + CreatePixelShader: proc "stdcall" (this: ^IDevice, pShaderBytecode: rawptr, BytecodeLength: SIZE_T, pClassLinkage: ^IClassLinkage, ppPixelShader: ^^IPixelShader) -> HRESULT, + CreateHullShader: proc "stdcall" (this: ^IDevice, pShaderBytecode: rawptr, BytecodeLength: SIZE_T, pClassLinkage: ^IClassLinkage, ppHullShader: ^^IHullShader) -> HRESULT, + CreateDomainShader: proc "stdcall" (this: ^IDevice, pShaderBytecode: rawptr, BytecodeLength: SIZE_T, pClassLinkage: ^IClassLinkage, ppDomainShader: ^^IDomainShader) -> HRESULT, + CreateComputeShader: proc "stdcall" (this: ^IDevice, pShaderBytecode: rawptr, BytecodeLength: SIZE_T, pClassLinkage: ^IClassLinkage, ppComputeShader: ^^IComputeShader) -> HRESULT, + CreateClassLinkage: proc "stdcall" (this: ^IDevice, ppLinkage: ^^IClassLinkage) -> HRESULT, + CreateBlendState: proc "stdcall" (this: ^IDevice, pBlendStateDesc: ^BLEND_DESC, ppBlendState: ^^IBlendState) -> HRESULT, + CreateDepthStencilState: proc "stdcall" (this: ^IDevice, pDepthStencilDesc: ^DEPTH_STENCIL_DESC, ppDepthStencilState: ^^IDepthStencilState) -> HRESULT, + CreateRasterizerState: proc "stdcall" (this: ^IDevice, pRasterizerDesc: ^RASTERIZER_DESC, ppRasterizerState: ^^IRasterizerState) -> HRESULT, + CreateSamplerState: proc "stdcall" (this: ^IDevice, pSamplerDesc: ^SAMPLER_DESC, ppSamplerState: ^^ISamplerState) -> HRESULT, + CreateQuery: proc "stdcall" (this: ^IDevice, pQueryDesc: ^QUERY_DESC, ppQuery: ^^IQuery) -> HRESULT, + CreatePredicate: proc "stdcall" (this: ^IDevice, pPredicateDesc: ^QUERY_DESC, ppPredicate: ^^IPredicate) -> HRESULT, + CreateCounter: proc "stdcall" (this: ^IDevice, pCounterDesc: ^COUNTER_DESC, ppCounter: ^^ICounter) -> HRESULT, + CreateDeferredContext: proc "stdcall" (this: ^IDevice, ContextFlags: u32, ppDeferredContext: ^^IDeviceContext) -> HRESULT, + OpenSharedResource: proc "stdcall" (this: ^IDevice, hResource: HANDLE, ReturnedInterface: ^IID, ppResource: ^rawptr) -> HRESULT, + CheckFormatSupport: proc "stdcall" (this: ^IDevice, Format: dxgi.FORMAT, pFormatSupport: ^u32) -> HRESULT, + CheckMultisampleQualityLevels: proc "stdcall" (this: ^IDevice, Format: dxgi.FORMAT, SampleCount: u32, pNumQualityLevels: ^u32) -> HRESULT, + CheckCounterInfo: proc "stdcall" (this: ^IDevice, pCounterInfo: ^COUNTER_INFO), + CheckCounter: proc "stdcall" (this: ^IDevice, pDesc: ^COUNTER_DESC, pType: ^COUNTER_TYPE, pActiveCounters: ^u32, szName: cstring, pNameLength: ^u32, szUnits: ^u8, pUnitsLength: ^u32, szDescription: cstring, pDescriptionLength: ^u32) -> HRESULT, + CheckFeatureSupport: proc "stdcall" (this: ^IDevice, Feature: FEATURE, pFeatureSupportData: rawptr, FeatureSupportDataSize: u32) -> HRESULT, + GetPrivateData: proc "stdcall" (this: ^IDevice, guid: ^GUID, pDataSize: ^u32, pData: rawptr) -> HRESULT, + SetPrivateData: proc "stdcall" (this: ^IDevice, guid: ^GUID, DataSize: u32, pData: rawptr) -> HRESULT, + SetPrivateDataInterface: proc "stdcall" (this: ^IDevice, guid: ^GUID, pData: ^IUnknown) -> HRESULT, + GetFeatureLevel: proc "stdcall" (this: ^IDevice) -> FEATURE_LEVEL, + GetCreationFlags: proc "stdcall" (this: ^IDevice) -> u32, + GetDeviceRemovedReason: proc "stdcall" (this: ^IDevice) -> HRESULT, + GetImmediateContext: proc "stdcall" (this: ^IDevice, ppImmediateContext: ^^IDeviceContext), + SetExceptionMode: proc "stdcall" (this: ^IDevice, RaiseFlags: u32) -> HRESULT, + GetExceptionMode: proc "stdcall" (this: ^IDevice) -> u32, +} + + +CREATE_DEVICE_FLAGS :: distinct bit_set[CREATE_DEVICE_FLAG; u32] +CREATE_DEVICE_FLAG :: enum u32 { // TODO: make bit_set + SINGLETHREADED = 0, + DEBUG = 1, + SWITCH_TO_REF = 2, + PREVENT_INTERNAL_THREADING_OPTIMIZATIONS = 3, + BGRA_SUPPORT = 5, + DEBUGGABLE = 6, + PREVENT_ALTERING_LAYER_SETTINGS_FROM_REGISTRY = 7, + DISABLE_GPU_TIMEOUT = 8, + VIDEO_SUPPORT = 12, +} + +PFN_CREATE_DEVICE :: #type proc "c" (a0: ^dxgi.IAdapter, a1: DRIVER_TYPE, a2: HMODULE, a3: u32, a4: ^FEATURE_LEVEL, a5: u32, a6: u32, a7: ^^IDevice, a8: ^FEATURE_LEVEL, a9: ^^IDeviceContext) -> HRESULT +PFN_CREATE_DEVICE_AND_SWAP_CHAIN :: #type proc "c" (a0: ^dxgi.IAdapter, a1: DRIVER_TYPE, a2: HMODULE, a3: u32, a4: ^FEATURE_LEVEL, a5: u32, a6: u32, a7: ^dxgi.SWAP_CHAIN_DESC, a8: ^^dxgi.ISwapChain, a9: ^^IDevice, a10: ^FEATURE_LEVEL, a11: ^^IDeviceContext) -> HRESULT + +SHADER_VERSION_TYPE :: enum i32 { + PIXEL_SHADER = 0, + VERTEX_SHADER = 1, + GEOMETRY_SHADER = 2, + + HULL_SHADER = 3, + DOMAIN_SHADER = 4, + COMPUTE_SHADER = 5, + + RESERVED0 = 65520, +} + +SIGNATURE_PARAMETER_DESC :: struct { + SemanticName: cstring, + SemanticIndex: u32, + Register: u32, + SystemValueType: NAME, + ComponentType: REGISTER_COMPONENT_TYPE, + Mask: u8, + + ReadWriteMask: u8, + + Stream: u32, + MinPrecision: MIN_PRECISION, +} + +SHADER_BUFFER_DESC :: struct { + Name: cstring, + Type: CBUFFER_TYPE, + Variables: u32, + Size: u32, + uFlags: u32, +} + +SHADER_VARIABLE_DESC :: struct { + Name: cstring, + StartOffset: u32, + Size: u32, + uFlags: u32, + DefaultValue: rawptr, + StartTexture: u32, + TextureSize: u32, + StartSampler: u32, + SamplerSize: u32, +} + +SHADER_TYPE_DESC :: struct { + Class: SHADER_VARIABLE_CLASS, + Type: SHADER_VARIABLE_TYPE, + Rows: u32, + Columns: u32, + Elements: u32, + Members: u32, + Offset: u32, + Name: cstring, +} + +SHADER_DESC :: struct { + Version: u32, + Creator: cstring, + Flags: u32, + + ConstantBuffers: u32, + BoundResources: u32, + InputParameters: u32, + OutputParameters: u32, + + InstructionCount: u32, + TempRegisterCount: u32, + TempArrayCount: u32, + DefCount: u32, + DclCount: u32, + TextureNormalInstructions: u32, + TextureLoadInstructions: u32, + TextureCompInstructions: u32, + TextureBiasInstructions: u32, + TextureGradientInstructions: u32, + FloatInstructionCount: u32, + IntInstructionCount: u32, + UintInstructionCount: u32, + StaticFlowControlCount: u32, + DynamicFlowControlCount: u32, + MacroInstructionCount: u32, + ArrayInstructionCount: u32, + CutInstructionCount: u32, + EmitInstructionCount: u32, + GSOutputTopology: PRIMITIVE_TOPOLOGY, + GSMaxOutputVertexCount: u32, + InputPrimitive: PRIMITIVE, + PatchConstantParameters: u32, + cGSInstanceCount: u32, + cControlPoints: u32, + HSOutputPrimitive: TESSELLATOR_OUTPUT_PRIMITIVE, + HSPartitioning: TESSELLATOR_PARTITIONING, + TessellatorDomain: TESSELLATOR_DOMAIN, + + cBarrierInstructions: u32, + cInterlockedInstructions: u32, + cTextureStoreInstructions: u32, +} + +SHADER_INPUT_BIND_DESC :: struct { + Name: cstring, + Type: SHADER_INPUT_TYPE, + BindPoint: u32, + BindCount: u32, + + uFlags: u32, + ReturnType: RESOURCE_RETURN_TYPE, + Dimension: SRV_DIMENSION, + NumSamples: u32, +} + +LIBRARY_DESC :: struct { + Creator: cstring, + Flags: u32, + FunctionCount: u32, +} + +FUNCTION_DESC :: struct { + Version: u32, + Creator: cstring, + Flags: u32, + + ConstantBuffers: u32, + BoundResources: u32, + + InstructionCount: u32, + TempRegisterCount: u32, + TempArrayCount: u32, + DefCount: u32, + DclCount: u32, + TextureNormalInstructions: u32, + TextureLoadInstructions: u32, + TextureCompInstructions: u32, + TextureBiasInstructions: u32, + TextureGradientInstructions: u32, + FloatInstructionCount: u32, + IntInstructionCount: u32, + UintInstructionCount: u32, + StaticFlowControlCount: u32, + DynamicFlowControlCount: u32, + MacroInstructionCount: u32, + ArrayInstructionCount: u32, + MovInstructionCount: u32, + MovcInstructionCount: u32, + ConversionInstructionCount: u32, + BitwiseInstructionCount: u32, + MinFeatureLevel: FEATURE_LEVEL, + RequiredFeatureFlags: u64, + + Name: cstring, + FunctionParameterCount: i32, + HasReturn: BOOL, + Has10Level9VertexShader: BOOL, + Has10Level9PixelShader: BOOL, +} + +PARAMETER_DESC :: struct { + Name: cstring, + SemanticName: cstring, + Type: SHADER_VARIABLE_TYPE, + Class: SHADER_VARIABLE_CLASS, + Rows: u32, + Columns: u32, + InterpolationMode: INTERPOLATION_MODE, + Flags: PARAMETER_FLAGS, + + FirstInRegister: u32, + FirstInComponent: u32, + FirstOutRegister: u32, + FirstOutComponent: u32, +} + +IShaderReflectionType :: struct { + using vtable: ^IShaderReflectionType_VTable, +} +IShaderReflectionType_VTable :: struct { + GetDesc: proc "stdcall" (this: ^IShaderReflectionType, pDesc: ^SHADER_TYPE_DESC) -> HRESULT, + GetMemberTypeByIndex: proc "stdcall" (this: ^IShaderReflectionType, Index: u32) -> ^IShaderReflectionType, + GetMemberTypeByName: proc "stdcall" (this: ^IShaderReflectionType, Name: cstring) -> ^IShaderReflectionType, + GetMemberTypeName: proc "stdcall" (this: ^IShaderReflectionType, Index: u32) -> cstring, + IsEqual: proc "stdcall" (this: ^IShaderReflectionType, pType: ^IShaderReflectionType) -> HRESULT, + GetSubType: proc "stdcall" (this: ^IShaderReflectionType) -> ^IShaderReflectionType, + GetBaseClass: proc "stdcall" (this: ^IShaderReflectionType) -> ^IShaderReflectionType, + GetNumInterfaces: proc "stdcall" (this: ^IShaderReflectionType) -> u32, + GetInterfaceByIndex: proc "stdcall" (this: ^IShaderReflectionType, uIndex: u32) -> ^IShaderReflectionType, + IsOfType: proc "stdcall" (this: ^IShaderReflectionType, pType: ^IShaderReflectionType) -> HRESULT, + ImplementsInterface: proc "stdcall" (this: ^IShaderReflectionType, pBase: ^IShaderReflectionType) -> HRESULT, +} + +IShaderReflectionVariable :: struct { + using vtable: ^IShaderReflectionVariable_VTable, +} +IShaderReflectionVariable_VTable :: struct { + GetDesc: proc "stdcall" (this: ^IShaderReflectionVariable, pDesc: ^SHADER_VARIABLE_DESC) -> HRESULT, + GetType: proc "stdcall" (this: ^IShaderReflectionVariable) -> ^IShaderReflectionType, + GetBuffer: proc "stdcall" (this: ^IShaderReflectionVariable) -> ^IShaderReflectionConstantBuffer, + GetInterfaceSlot: proc "stdcall" (this: ^IShaderReflectionVariable, uArrayIndex: u32) -> u32, +} + +IShaderReflectionConstantBuffer :: struct { + using vtable: ^IShaderReflectionConstantBuffer_VTable, +} +IShaderReflectionConstantBuffer_VTable :: struct { + GetDesc: proc "stdcall" (this: ^IShaderReflectionConstantBuffer, pDesc: ^SHADER_BUFFER_DESC) -> HRESULT, + GetVariableByIndex: proc "stdcall" (this: ^IShaderReflectionConstantBuffer, Index: u32) -> ^IShaderReflectionVariable, + GetVariableByName: proc "stdcall" (this: ^IShaderReflectionConstantBuffer, Name: cstring) -> ^IShaderReflectionVariable, +} + + +IShaderReflection :: struct #raw_union { + #subtype iunknown: IUnknown, + using id3d11shaderreflection_vtable: ^IShaderReflection_VTable, +} +IShaderReflection_VTable :: struct { + using iunknown_vtable: IUnknown_VTable, + GetDesc: proc "stdcall" (this: ^IShaderReflection, pDesc: ^SHADER_DESC) -> HRESULT, + GetConstantBufferByIndex: proc "stdcall" (this: ^IShaderReflection, Index: u32) -> ^IShaderReflectionConstantBuffer, + GetConstantBufferByName: proc "stdcall" (this: ^IShaderReflection, Name: cstring) -> ^IShaderReflectionConstantBuffer, + GetResourceBindingDesc: proc "stdcall" (this: ^IShaderReflection, ResourceIndex: u32, pDesc: ^SHADER_INPUT_BIND_DESC) -> HRESULT, + GetInputParameterDesc: proc "stdcall" (this: ^IShaderReflection, ParameterIndex: u32, pDesc: ^SIGNATURE_PARAMETER_DESC) -> HRESULT, + GetOutputParameterDesc: proc "stdcall" (this: ^IShaderReflection, ParameterIndex: u32, pDesc: ^SIGNATURE_PARAMETER_DESC) -> HRESULT, + GetPatchConstantParameterDesc: proc "stdcall" (this: ^IShaderReflection, ParameterIndex: u32, pDesc: ^SIGNATURE_PARAMETER_DESC) -> HRESULT, + GetVariableByName: proc "stdcall" (this: ^IShaderReflection, Name: cstring) -> ^IShaderReflectionVariable, + GetResourceBindingDescByName: proc "stdcall" (this: ^IShaderReflection, Name: cstring, pDesc: ^SHADER_INPUT_BIND_DESC) -> HRESULT, + GetMovInstructionCount: proc "stdcall" (this: ^IShaderReflection) -> u32, + GetMovcInstructionCount: proc "stdcall" (this: ^IShaderReflection) -> u32, + GetConversionInstructionCount: proc "stdcall" (this: ^IShaderReflection) -> u32, + GetBitwiseInstructionCount: proc "stdcall" (this: ^IShaderReflection) -> u32, + GetGSInputPrimitive: proc "stdcall" (this: ^IShaderReflection) -> PRIMITIVE, + IsSampleFrequencyShader: proc "stdcall" (this: ^IShaderReflection) -> BOOL, + GetNumInterfaceSlots: proc "stdcall" (this: ^IShaderReflection) -> u32, + GetMinFeatureLevel: proc "stdcall" (this: ^IShaderReflection, pLevel: ^FEATURE_LEVEL) -> HRESULT, + GetThreadGroupSize: proc "stdcall" (this: ^IShaderReflection, pSizeX: ^u32, pSizeY: ^u32, pSizeZ: ^u32) -> u32, + GetRequiresFlags: proc "stdcall" (this: ^IShaderReflection) -> u64, +} + + +ILibraryReflection :: struct #raw_union { + #subtype iunknown: IUnknown, + using id3d11libraryreflection_vtable: ^ILibraryReflection_VTable, +} +ILibraryReflection_VTable :: struct { + using iunknown_vtable: IUnknown_VTable, + GetDesc: proc "stdcall" (this: ^ILibraryReflection, pDesc: ^LIBRARY_DESC) -> HRESULT, + GetFunctionByIndex: proc "stdcall" (this: ^ILibraryReflection, FunctionIndex: i32) -> ^IFunctionReflection, +} + +IFunctionReflection :: struct { + using vtable: ^IFunctionReflection_VTable, +} +IFunctionReflection_VTable :: struct { + GetDesc: proc "stdcall" (this: ^IFunctionReflection, pDesc: ^FUNCTION_DESC) -> HRESULT, + GetConstantBufferByIndex: proc "stdcall" (this: ^IFunctionReflection, BufferIndex: u32) -> ^IShaderReflectionConstantBuffer, + GetConstantBufferByName: proc "stdcall" (this: ^IFunctionReflection, Name: cstring) -> ^IShaderReflectionConstantBuffer, + GetResourceBindingDesc: proc "stdcall" (this: ^IFunctionReflection, ResourceIndex: u32, pDesc: ^SHADER_INPUT_BIND_DESC) -> HRESULT, + GetVariableByName: proc "stdcall" (this: ^IFunctionReflection, Name: cstring) -> ^IShaderReflectionVariable, + GetResourceBindingDescByName: proc "stdcall" (this: ^IFunctionReflection, Name: cstring, pDesc: ^SHADER_INPUT_BIND_DESC) -> HRESULT, + GetFunctionParameter: proc "stdcall" (this: ^IFunctionReflection, ParameterIndex: i32) -> ^IFunctionParameterReflection, +} + +IFunctionParameterReflection :: struct { + using vtable: ^IFunctionParameterReflection_VTable, +} +IFunctionParameterReflection_VTable :: struct { + GetDesc: proc "stdcall" (this: ^IFunctionParameterReflection, pDesc: ^PARAMETER_DESC) -> HRESULT, +} + + +ILinkingNode :: struct { + using iunknown: IUnknown, +} + +IFunctionLinkingGraph :: struct #raw_union { + #subtype iunknown: IUnknown, + using id3d11functionlinkinggraph_vtable: ^IFunctionLinkingGraph_VTable, +} +IFunctionLinkingGraph_VTable :: struct { + using iunknown_vtable: IUnknown_VTable, + CreateModuleInstance: proc "stdcall" (this: ^IFunctionLinkingGraph, ppModuleInstance: ^^IModuleInstance, ppErrorBuffer: ^^IBlob) -> HRESULT, + SetInputSignature: proc "stdcall" (this: ^IFunctionLinkingGraph, pInputParameters: ^PARAMETER_DESC, cInputParameters: u32, ppInputNode: ^^ILinkingNode) -> HRESULT, + SetOutputSignature: proc "stdcall" (this: ^IFunctionLinkingGraph, pOutputParameters: ^PARAMETER_DESC, cOutputParameters: u32, ppOutputNode: ^^ILinkingNode) -> HRESULT, + CallFunction: proc "stdcall" (this: ^IFunctionLinkingGraph, pModuleInstanceNamespace: cstring, pModuleWithFunctionPrototype: ^IModule, pFunctionName: cstring, ppCallNode: ^^ILinkingNode) -> HRESULT, + PassValue: proc "stdcall" (this: ^IFunctionLinkingGraph, pSrcNode: ^ILinkingNode, SrcParameterIndex: i32, pDstNode: ^ILinkingNode, DstParameterIndex: i32) -> HRESULT, + PassValueWithSwizzle: proc "stdcall" (this: ^IFunctionLinkingGraph, pSrcNode: ^ILinkingNode, SrcParameterIndex: i32, pSrcSwizzle: ^u8, pDstNode: ^ILinkingNode, DstParameterIndex: i32, pDstSwizzle: ^u8) -> HRESULT, + GetLastError: proc "stdcall" (this: ^IFunctionLinkingGraph, ppErrorBuffer: ^^IBlob) -> HRESULT, + GenerateHlsl: proc "stdcall" (this: ^IFunctionLinkingGraph, uFlags: u32, ppBuffer: ^^IBlob) -> HRESULT, +} diff --git a/vendor/directx/d3d12/d3d12.odin b/vendor/directx/d3d12/d3d12.odin new file mode 100644 index 000000000..6a7eed22b --- /dev/null +++ b/vendor/directx/d3d12/d3d12.odin @@ -0,0 +1,5169 @@ +package directx_d3d12 + +foreign import "system:d3d12.lib" + +import "../dxgi" +import "../d3d_compiler" +import win32 "core:sys/windows" + +IUnknown :: dxgi.IUnknown +IUnknown_VTable :: dxgi.IUnknown_VTable + +HANDLE :: dxgi.HANDLE +HMODULE :: dxgi.HMODULE +HRESULT :: dxgi.HRESULT +HWND :: dxgi.HWND +LUID :: dxgi.LUID +UUID :: dxgi.UUID +GUID :: dxgi.GUID +IID :: dxgi.IID +SIZE_T :: dxgi.SIZE_T +BOOL :: dxgi.BOOL + +RECT :: dxgi.RECT + +IModuleInstance :: d3d_compiler.ID3D11ModuleInstance +IBlob :: d3d_compiler.ID3DBlob +IModule :: d3d_compiler.ID3D11Module + +@(default_calling_convention="stdcall", link_prefix="D3D12") +foreign d3d12 { + CreateDevice :: proc(pAdapter: ^IUnknown, MinimumFeatureLevel: FEATURE_LEVEL, riid: ^IID, ppDevice: ^rawptr) -> HRESULT --- + CreateRootSignatureDeserializer :: proc(pSrcData: rawptr, SrcDataSizeInBytes: SIZE_T, pRootSignatureDeserializerInterface: ^IID, ppRootSignatureDeserializer: ^rawptr) -> HRESULT --- + CreateVersionedRootSignatureDeserializer :: proc(pSrcData: rawptr, SrcDataSizeInBytes: SIZE_T, pRootSignatureDeserializerInterface: ^IID, ppRootSignatureDeserializer: ^rawptr) -> HRESULT --- + EnableExperimentalFeatures :: proc(NumFeatures: u32, pIIDs: ^IID, pConfigurationStructs: rawptr, pConfigurationStructSizes: ^u32) -> HRESULT --- + GetDebugInterface :: proc(riid: ^IID, ppvDebug: ^rawptr) -> HRESULT --- + SerializeRootSignature :: proc(pRootSignature: ^ROOT_SIGNATURE_DESC, Version: ROOT_SIGNATURE_VERSION, ppBlob: ^^IBlob, ppErrorBlob: ^^IBlob) -> HRESULT --- + SerializeVersionedRootSignature :: proc(pRootSignature: ^VERSIONED_ROOT_SIGNATURE_DESC, ppBlob: ^^IBlob, ppErrorBlob: ^^IBlob) -> HRESULT --- +} + +foreign d3d12 { + WKPDID_D3DDebugObjectNameW: GUID + WKPDID_CommentStringW: GUID + + @(link_name="DXGI_DEBUG_D3D12") + DEBUG_D3D12: GUID + + @(link_name="D3D12_PROTECTED_RESOURCES_SESSION_HARDWARE_PROTECTED") + PROTECTED_RESOURCES_SESSION_HARDWARE_PROTECTED: GUID +} + +@(link_prefix="D3D_") +foreign d3d12 { + TEXTURE_LAYOUT_ROW_MAJOR: GUID + TEXTURE_LAYOUT_64KB_STANDARD_SWIZZLE: GUID +} + +@(link_prefix="D3D12") +foreign d3d12 { + ExperimentalShaderModels: UUID + TiledResourceTier4: UUID + MetaCommand: UUID +} + + +DRIVER_TYPE :: enum i32 { + UNKNOWN = 0, + HARDWARE = 1, + REFERENCE = 2, + NULL = 3, + SOFTWARE = 4, + WARP = 5, +} + +FEATURE_LEVEL :: enum i32 { + _1_0_CORE = 4096, + _9_1 = 37120, + _9_2 = 37376, + _9_3 = 37632, + _10_0 = 40960, + _10_1 = 41216, + _11_0 = 45056, + _11_1 = 45312, + _12_0 = 49152, + _12_1 = 49408, +} + +PRIMITIVE_TOPOLOGY :: enum i32 { + UNDEFINED = 0, + POINTLIST = 1, + LINELIST = 2, + LINESTRIP = 3, + TRIANGLELIST = 4, + TRIANGLESTRIP = 5, + LINELIST_ADJ = 10, + LINESTRIP_ADJ = 11, + TRIANGLELIST_ADJ = 12, + TRIANGLESTRIP_ADJ = 13, + _1_CONTROL_POINT_PATCHLIST = 33, + _2_CONTROL_POINT_PATCHLIST = 34, + _3_CONTROL_POINT_PATCHLIST = 35, + _4_CONTROL_POINT_PATCHLIST = 36, + _5_CONTROL_POINT_PATCHLIST = 37, + _6_CONTROL_POINT_PATCHLIST = 38, + _7_CONTROL_POINT_PATCHLIST = 39, + _8_CONTROL_POINT_PATCHLIST = 40, + _9_CONTROL_POINT_PATCHLIST = 41, + _10_CONTROL_POINT_PATCHLIST = 42, + _11_CONTROL_POINT_PATCHLIST = 43, + _12_CONTROL_POINT_PATCHLIST = 44, + _13_CONTROL_POINT_PATCHLIST = 45, + _14_CONTROL_POINT_PATCHLIST = 46, + _15_CONTROL_POINT_PATCHLIST = 47, + _16_CONTROL_POINT_PATCHLIST = 48, + _17_CONTROL_POINT_PATCHLIST = 49, + _18_CONTROL_POINT_PATCHLIST = 50, + _19_CONTROL_POINT_PATCHLIST = 51, + _20_CONTROL_POINT_PATCHLIST = 52, + _21_CONTROL_POINT_PATCHLIST = 53, + _22_CONTROL_POINT_PATCHLIST = 54, + _23_CONTROL_POINT_PATCHLIST = 55, + _24_CONTROL_POINT_PATCHLIST = 56, + _25_CONTROL_POINT_PATCHLIST = 57, + _26_CONTROL_POINT_PATCHLIST = 58, + _27_CONTROL_POINT_PATCHLIST = 59, + _28_CONTROL_POINT_PATCHLIST = 60, + _29_CONTROL_POINT_PATCHLIST = 61, + _30_CONTROL_POINT_PATCHLIST = 62, + _31_CONTROL_POINT_PATCHLIST = 63, + _32_CONTROL_POINT_PATCHLIST = 64, +} + +PRIMITIVE :: enum i32 { + UNDEFINED = 0, + POINT = 1, + LINE = 2, + TRIANGLE = 3, + LINE_ADJ = 6, + TRIANGLE_ADJ = 7, + _1_CONTROL_POINT_PATCH = 8, + _2_CONTROL_POINT_PATCH = 9, + _3_CONTROL_POINT_PATCH = 10, + _4_CONTROL_POINT_PATCH = 11, + _5_CONTROL_POINT_PATCH = 12, + _6_CONTROL_POINT_PATCH = 13, + _7_CONTROL_POINT_PATCH = 14, + _8_CONTROL_POINT_PATCH = 15, + _9_CONTROL_POINT_PATCH = 16, + _10_CONTROL_POINT_PATCH = 17, + _11_CONTROL_POINT_PATCH = 18, + _12_CONTROL_POINT_PATCH = 19, + _13_CONTROL_POINT_PATCH = 20, + _14_CONTROL_POINT_PATCH = 21, + _15_CONTROL_POINT_PATCH = 22, + _16_CONTROL_POINT_PATCH = 23, + _17_CONTROL_POINT_PATCH = 24, + _18_CONTROL_POINT_PATCH = 25, + _19_CONTROL_POINT_PATCH = 26, + _20_CONTROL_POINT_PATCH = 27, + _21_CONTROL_POINT_PATCH = 28, + _22_CONTROL_POINT_PATCH = 29, + _23_CONTROL_POINT_PATCH = 30, + _24_CONTROL_POINT_PATCH = 31, + _25_CONTROL_POINT_PATCH = 32, + _26_CONTROL_POINT_PATCH = 33, + _27_CONTROL_POINT_PATCH = 34, + _28_CONTROL_POINT_PATCH = 35, + _29_CONTROL_POINT_PATCH = 36, + _30_CONTROL_POINT_PATCH = 37, + _31_CONTROL_POINT_PATCH = 38, + _32_CONTROL_POINT_PATCH = 39, +} + +SRV_DIMENSION :: enum i32 { + UNKNOWN = 0, + BUFFER = 1, + TEXTURE1D = 2, + TEXTURE1DARRAY = 3, + TEXTURE2D = 4, + TEXTURE2DARRAY = 5, + TEXTURE2DMS = 6, + TEXTURE2DMSARRAY = 7, + TEXTURE3D = 8, + TEXTURECUBE = 9, + TEXTURECUBEARRAY = 10, + BUFFEREX = 11, + RAYTRACING_ACCELERATION_STRUCTURE = 11, +} + +PFN_DESTRUCTION_CALLBACK :: #type proc "c" (a0: rawptr) + + +ID3DDestructionNotifier_UUID_STRING :: "a06eb39a-50da-425b-8c31-4eecd6c270f3" +ID3DDestructionNotifier_UUID := &IID{0xa06eb39a, 0x50da, 0x425b, {0x8c, 0x31, 0x4e, 0xec, 0xd6, 0xc2, 0x70, 0xf3}} +ID3DDestructionNotifier :: struct #raw_union { + #subtype iunknown: IUnknown, + using id3ddestructionnotifier_vtable: ^ID3DDestructionNotifier_VTable, +} +ID3DDestructionNotifier_VTable :: struct { + using iunknown_vtable: IUnknown_VTable, + RegisterDestructionCallback: proc "stdcall" (this: ^ID3DDestructionNotifier, callbackFn: PFN_DESTRUCTION_CALLBACK, pData: rawptr, pCallbackID: ^u32) -> HRESULT, + UnregisterDestructionCallback: proc "stdcall" (this: ^ID3DDestructionNotifier, callbackID: u32) -> HRESULT, +} + +SHADER_VARIABLE_CLASS :: enum i32 { + SCALAR = 0, + VECTOR = 1, + MATRIX_ROWS = 2, + MATRIX_COLUMNS = 3, + OBJECT = 4, + STRUCT = 5, + INTERFACE_CLASS = 6, + INTERFACE_POINTER = 7, +} + +SHADER_VARIABLE_FLAGS :: enum u32 { // TODO: make bit_set + USERPACKED = 0x1, + USED = 0x2, + INTERFACE_POINTER = 0x4, + INTERFACE_PARAMETER = 0x8, +} + +SHADER_VARIABLE_TYPE :: enum i32 { + VOID = 0, + BOOL = 1, + INT = 2, + FLOAT = 3, + STRING = 4, + TEXTURE = 5, + TEXTURE1D = 6, + TEXTURE2D = 7, + TEXTURE3D = 8, + TEXTURECUBE = 9, + SAMPLER = 10, + SAMPLER1D = 11, + SAMPLER2D = 12, + SAMPLER3D = 13, + SAMPLERCUBE = 14, + PIXELSHADER = 15, + VERTEXSHADER = 16, + PIXELFRAGMENT = 17, + VERTEXFRAGMENT = 18, + UINT = 19, + UINT8 = 20, + GEOMETRYSHADER = 21, + RASTERIZER = 22, + DEPTHSTENCIL = 23, + BLEND = 24, + BUFFER = 25, + CBUFFER = 26, + TBUFFER = 27, + TEXTURE1DARRAY = 28, + TEXTURE2DARRAY = 29, + RENDERTARGETVIEW = 30, + DEPTHSTENCILVIEW = 31, + TEXTURE2DMS = 32, + TEXTURE2DMSARRAY = 33, + TEXTURECUBEARRAY = 34, + HULLSHADER = 35, + DOMAINSHADER = 36, + INTERFACE_POINTER = 37, + COMPUTESHADER = 38, + DOUBLE = 39, + RWTEXTURE1D = 40, + RWTEXTURE1DARRAY = 41, + RWTEXTURE2D = 42, + RWTEXTURE2DARRAY = 43, + RWTEXTURE3D = 44, + RWBUFFER = 45, + BYTEADDRESS_BUFFER = 46, + RWBYTEADDRESS_BUFFER = 47, + STRUCTURED_BUFFER = 48, + RWSTRUCTURED_BUFFER = 49, + APPEND_STRUCTURED_BUFFER = 50, + CONSUME_STRUCTURED_BUFFER = 51, + MIN8FLOAT = 52, + MIN10FLOAT = 53, + MIN16FLOAT = 54, + MIN12INT = 55, + MIN16INT = 56, + MIN16UINT = 57, +} + +SHADER_INPUT_FLAGS :: enum u32 { // TODO: make bit_set + USERPACKED = 0x1, + COMPARISON_SAMPLER = 0x2, + TEXTURE_COMPONENT_0 = 0x4, + TEXTURE_COMPONENT_1 = 0x8, + TEXTURE_COMPONENTS = 0xc, + UNUSED = 0x10, +} + +SHADER_INPUT_TYPE :: enum i32 { + CBUFFER = 0, + TBUFFER = 1, + TEXTURE = 2, + SAMPLER = 3, + UAV_RWTYPED = 4, + STRUCTURED = 5, + UAV_RWSTRUCTURED = 6, + BYTEADDRESS = 7, + UAV_RWBYTEADDRESS = 8, + UAV_APPEND_STRUCTURED = 9, + UAV_CONSUME_STRUCTURED = 10, + UAV_RWSTRUCTURED_WITH_COUNTER = 11, + RTACCELERATIONSTRUCTURE = 12, + UAV_FEEDBACKTEXTURE = 13, +} + +SHADER_CBUFFER_FLAGS :: enum u32 { // TODO: make bit_set + USERPACKED = 0x1, +} + +CBUFFER_TYPE :: enum i32 { + CBUFFER = 0, + TBUFFER = 1, + INTERFACE_POINTERS = 2, + RESOURCE_BIND_INFO = 3, +} + +NAME :: enum i32 { + UNDEFINED = 0, + POSITION = 1, + CLIP_DISTANCE = 2, + CULL_DISTANCE = 3, + RENDER_TARGET_ARRAY_INDEX = 4, + VIEWPORT_ARRAY_INDEX = 5, + VERTEX_ID = 6, + PRIMITIVE_ID = 7, + INSTANCE_ID = 8, + IS_FRONT_FACE = 9, + SAMPLE_INDEX = 10, + FINAL_QUAD_EDGE_TESSFACTOR = 11, + FINAL_QUAD_INSIDE_TESSFACTOR = 12, + FINAL_TRI_EDGE_TESSFACTOR = 13, + FINAL_TRI_INSIDE_TESSFACTOR = 14, + FINAL_LINE_DETAIL_TESSFACTOR = 15, + FINAL_LINE_DENSITY_TESSFACTOR = 16, + BARYCENTRICS = 23, + SHADINGRATE = 24, + CULLPRIMITIVE = 25, + TARGET = 64, + DEPTH = 65, + COVERAGE = 66, + DEPTH_GREATER_EQUAL = 67, + DEPTH_LESS_EQUAL = 68, + STENCIL_REF = 69, + INNER_COVERAGE = 70, +} + +RESOURCE_RETURN_TYPE :: enum i32 { + UNORM = 1, + SNORM = 2, + SINT = 3, + UINT = 4, + FLOAT = 5, + MIXED = 6, + DOUBLE = 7, + CONTINUED = 8, +} + +REGISTER_COMPONENT_TYPE :: enum i32 { + UNKNOWN = 0, + UINT32 = 1, + SINT32 = 2, + FLOAT32 = 3, +} + +TESSELLATOR_DOMAIN :: enum i32 { + UNDEFINED = 0, + ISOLINE = 1, + TRI = 2, + QUAD = 3, +} + +TESSELLATOR_PARTITIONING :: enum i32 { + UNDEFINED = 0, + INTEGER = 1, + POW2 = 2, + FRACTIONAL_ODD = 3, + FRACTIONAL_EVEN = 4, +} + +TESSELLATOR_OUTPUT_PRIMITIVE :: enum i32 { + UNDEFINED = 0, + POINT = 1, + LINE = 2, + TRIANGLE_CW = 3, + TRIANGLE_CCW = 4, +} + +MIN_PRECISION :: enum i32 { + DEFAULT = 0, + FLOAT_16 = 1, + FLOAT_2_8 = 2, + RESERVED = 3, + SINT_16 = 4, + UINT_16 = 5, + ANY_16 = 240, + ANY_10 = 241, +} + +INTERPOLATION_MODE :: enum i32 { + UNDEFINED = 0, + CONSTANT = 1, + LINEAR = 2, + LINEAR_CENTROID = 3, + LINEAR_NOPERSPECTIVE = 4, + LINEAR_NOPERSPECTIVE_CENTROID = 5, + LINEAR_SAMPLE = 6, + LINEAR_NOPERSPECTIVE_SAMPLE = 7, +} + +PARAMETER_FLAGS :: enum u32 { // TODO: make bit_set + NONE = 0x0, + IN = 0x1, + OUT = 0x2, +} + + +GPU_VIRTUAL_ADDRESS :: u64 + +COMMAND_LIST_TYPE :: enum i32 { + DIRECT = 0, + BUNDLE = 1, + COMPUTE = 2, + COPY = 3, + VIDEO_DECODE = 4, + VIDEO_PROCESS = 5, + VIDEO_ENCODE = 6, +} + +COMMAND_QUEUE_FLAGS :: enum u32 { // TODO: make bit_set + NONE = 0x0, + DISABLE_GPU_TIMEOUT = 0x1, +} + +COMMAND_QUEUE_PRIORITY :: enum i32 { + NORMAL = 0, + HIGH = 100, + GLOBAL_REALTIME = 10000, +} + +COMMAND_QUEUE_DESC :: struct { + Type: COMMAND_LIST_TYPE, + Priority: i32, + Flags: COMMAND_QUEUE_FLAGS, + NodeMask: u32, +} + +PRIMITIVE_TOPOLOGY_TYPE :: enum i32 { + UNDEFINED = 0, + POINT = 1, + LINE = 2, + TRIANGLE = 3, + PATCH = 4, +} + +INPUT_CLASSIFICATION :: enum i32 { + PER_VERTEX_DATA = 0, + PER_INSTANCE_DATA = 1, +} + +INPUT_ELEMENT_DESC :: struct { + SemanticName: cstring, + SemanticIndex: u32, + Format: dxgi.FORMAT, + InputSlot: u32, + AlignedByteOffset: u32, + InputSlotClass: INPUT_CLASSIFICATION, + InstanceDataStepRate: u32, +} + +FILL_MODE :: enum i32 { + WIREFRAME = 2, + SOLID = 3, +} + +CULL_MODE :: enum i32 { + NONE = 1, + FRONT = 2, + BACK = 3, +} + +SO_DECLARATION_ENTRY :: struct { + Stream: u32, + SemanticName: cstring, + SemanticIndex: u32, + StartComponent: u8, + ComponentCount: u8, + OutputSlot: u8, +} + +VIEWPORT :: struct { + TopLeftX: f32, + TopLeftY: f32, + Width: f32, + Height: f32, + MinDepth: f32, + MaxDepth: f32, +} + +BOX :: struct { + left: u32, + top: u32, + front: u32, + right: u32, + bottom: u32, + back: u32, +} + +COMPARISON_FUNC :: enum i32 { + NEVER = 1, + LESS = 2, + EQUAL = 3, + LESS_EQUAL = 4, + GREATER = 5, + NOT_EQUAL = 6, + GREATER_EQUAL = 7, + ALWAYS = 8, +} + +DEPTH_WRITE_MASK :: enum i32 { + ZERO = 0, + ALL = 1, +} + +STENCIL_OP :: enum i32 { + KEEP = 1, + ZERO = 2, + REPLACE = 3, + INCR_SAT = 4, + DECR_SAT = 5, + INVERT = 6, + INCR = 7, + DECR = 8, +} + +DEPTH_STENCILOP_DESC :: struct { + StencilFailOp: STENCIL_OP, + StencilDepthFailOp: STENCIL_OP, + StencilPassOp: STENCIL_OP, + StencilFunc: COMPARISON_FUNC, +} + +DEPTH_STENCIL_DESC :: struct { + DepthEnable: BOOL, + DepthWriteMask: DEPTH_WRITE_MASK, + DepthFunc: COMPARISON_FUNC, + StencilEnable: BOOL, + StencilReadMask: u8, + StencilWriteMask: u8, + FrontFace: DEPTH_STENCILOP_DESC, + BackFace: DEPTH_STENCILOP_DESC, +} + +DEPTH_STENCIL_DESC1 :: struct { + DepthEnable: BOOL, + DepthWriteMask: DEPTH_WRITE_MASK, + DepthFunc: COMPARISON_FUNC, + StencilEnable: BOOL, + StencilReadMask: u8, + StencilWriteMask: u8, + FrontFace: DEPTH_STENCILOP_DESC, + BackFace: DEPTH_STENCILOP_DESC, + DepthBoundsTestEnable: BOOL, +} + +BLEND :: enum i32 { + ZERO = 1, + ONE = 2, + SRC_COLOR = 3, + INV_SRC_COLOR = 4, + SRC_ALPHA = 5, + INV_SRC_ALPHA = 6, + DEST_ALPHA = 7, + INV_DEST_ALPHA = 8, + DEST_COLOR = 9, + INV_DEST_COLOR = 10, + SRC_ALPHA_SAT = 11, + BLEND_FACTOR = 14, + INV_BLEND_FACTOR = 15, + SRC1_COLOR = 16, + INV_SRC1_COLOR = 17, + SRC1_ALPHA = 18, + INV_SRC1_ALPHA = 19, +} + +BLEND_OP :: enum i32 { + ADD = 1, + SUBTRACT = 2, + REV_SUBTRACT = 3, + MIN = 4, + MAX = 5, +} + +COLOR_WRITE_ENABLE :: enum i32 { // TODO: make bit_set + RED = 1, + GREEN = 2, + BLUE = 4, + ALPHA = 8, + ALL = 15, +} + +LOGIC_OP :: enum i32 { + CLEAR = 0, + SET = 1, + COPY = 2, + COPY_INVERTED = 3, + NOOP = 4, + INVERT = 5, + AND = 6, + NAND = 7, + OR = 8, + NOR = 9, + XOR = 10, + EQUIV = 11, + AND_REVERSE = 12, + AND_INVERTED = 13, + OR_REVERSE = 14, + OR_INVERTED = 15, +} + +RENDER_TARGET_BLEND_DESC :: struct { + BlendEnable: BOOL, + LogicOpEnable: BOOL, + SrcBlend: BLEND, + DestBlend: BLEND, + BlendOp: BLEND_OP, + SrcBlendAlpha: BLEND, + DestBlendAlpha: BLEND, + BlendOpAlpha: BLEND_OP, + LogicOp: LOGIC_OP, + RenderTargetWriteMask: u8, +} + +BLEND_DESC :: struct { + AlphaToCoverageEnable: BOOL, + IndependentBlendEnable: BOOL, + RenderTarget: [8]RENDER_TARGET_BLEND_DESC, +} + +CONSERVATIVE_RASTERIZATION_MODE :: enum i32 { + OFF = 0, + ON = 1, +} + +RASTERIZER_DESC :: struct { + FillMode: FILL_MODE, + CullMode: CULL_MODE, + FrontCounterClockwise: BOOL, + DepthBias: i32, + DepthBiasClamp: f32, + SlopeScaledDepthBias: f32, + DepthClipEnable: BOOL, + MultisampleEnable: BOOL, + AntialiasedLineEnable: BOOL, + ForcedSampleCount: u32, + ConservativeRaster: CONSERVATIVE_RASTERIZATION_MODE, +} + + +IObject_UUID_STRING :: "c4fec28f-7966-4e95-9f94-f431cb56c3b8" +IObject_UUID := &IID{0xc4fec28f, 0x7966, 0x4e95, {0x9f, 0x94, 0xf4, 0x31, 0xcb, 0x56, 0xc3, 0xb8}} +IObject :: struct #raw_union { + #subtype iunknown: IUnknown, + using id3d12object_vtable: ^IObject_VTable, +} +IObject_VTable :: struct { + using iunknown_vtable: IUnknown_VTable, + GetPrivateData: proc "stdcall" (this: ^IObject, guid: ^GUID, pDataSize: ^u32, pData: rawptr) -> HRESULT, + SetPrivateData: proc "stdcall" (this: ^IObject, guid: ^GUID, DataSize: u32, pData: rawptr) -> HRESULT, + SetPrivateDataInterface: proc "stdcall" (this: ^IObject, guid: ^GUID, pData: ^IUnknown) -> HRESULT, + SetName: proc "stdcall" (this: ^IObject, Name: [^]u16) -> HRESULT, +} + + +IDeviceChild_UUID_STRING :: "905db94b-a00c-4140-9df5-2b64ca9ea357" +IDeviceChild_UUID := &IID{0x905db94b, 0xa00c, 0x4140, {0x9d, 0xf5, 0x2b, 0x64, 0xca, 0x9e, 0xa3, 0x57}} +IDeviceChild :: struct #raw_union { + #subtype id3d12object: IObject, + using id3d12devicechild_vtable: ^IDeviceChild_VTable, +} +IDeviceChild_VTable :: struct { + using id3d12object_vtable: IObject_VTable, + GetDevice: proc "stdcall" (this: ^IDeviceChild, riid: ^IID, ppvDevice: ^rawptr) -> HRESULT, +} + + +IRootSignature_UUID_STRING :: "c54a6b66-72df-4ee8-8be5-a946a1429214" +IRootSignature_UUID := &IID{0xc54a6b66, 0x72df, 0x4ee8, {0x8b, 0xe5, 0xa9, 0x46, 0xa1, 0x42, 0x92, 0x14}} +IRootSignature :: struct { + using id3d12devicechild: IDeviceChild, +} + +SHADER_BYTECODE :: struct { + pShaderBytecode: rawptr, + BytecodeLength: SIZE_T, +} + +STREAM_OUTPUT_DESC :: struct { + pSODeclaration: ^SO_DECLARATION_ENTRY, + NumEntries: u32, + pBufferStrides: ^u32, + NumStrides: u32, + RasterizedStream: u32, +} + +INPUT_LAYOUT_DESC :: struct { + pInputElementDescs: ^INPUT_ELEMENT_DESC, + NumElements: u32, +} + +INDEX_BUFFER_STRIP_CUT_VALUE :: enum i32 { + DISABLED = 0, + _0xFFFF = 1, + _0xFFFFFFFF = 2, +} + +CACHED_PIPELINE_STATE :: struct { + pCachedBlob: rawptr, + CachedBlobSizeInBytes: SIZE_T, +} + +PIPELINE_STATE_FLAGS :: enum u32 { // TODO: make bit_set + NONE = 0x0, + TOOL_DEBUG = 0x1, +} + +GRAPHICS_PIPELINE_STATE_DESC :: struct { + pRootSignature: ^IRootSignature, + VS: SHADER_BYTECODE, + PS: SHADER_BYTECODE, + DS: SHADER_BYTECODE, + HS: SHADER_BYTECODE, + GS: SHADER_BYTECODE, + StreamOutput: STREAM_OUTPUT_DESC, + BlendState: BLEND_DESC, + SampleMask: u32, + RasterizerState: RASTERIZER_DESC, + DepthStencilState: DEPTH_STENCIL_DESC, + InputLayout: INPUT_LAYOUT_DESC, + IBStripCutValue: INDEX_BUFFER_STRIP_CUT_VALUE, + PrimitiveTopologyType: PRIMITIVE_TOPOLOGY_TYPE, + NumRenderTargets: u32, + RTVFormats: [8]dxgi.FORMAT, + DSVFormat: dxgi.FORMAT, + SampleDesc: dxgi.SAMPLE_DESC, + NodeMask: u32, + CachedPSO: CACHED_PIPELINE_STATE, + Flags: PIPELINE_STATE_FLAGS, +} + +COMPUTE_PIPELINE_STATE_DESC :: struct { + pRootSignature: ^IRootSignature, + CS: SHADER_BYTECODE, + NodeMask: u32, + CachedPSO: CACHED_PIPELINE_STATE, + Flags: PIPELINE_STATE_FLAGS, +} + +RT_FORMAT_ARRAY :: struct { + RTFormats: [8]dxgi.FORMAT, + NumRenderTargets: u32, +} + +PIPELINE_STATE_STREAM_DESC :: struct { + SizeInBytes: SIZE_T, + pPipelineStateSubobjectStream: rawptr, +} + +PIPELINE_STATE_SUBOBJECT_TYPE :: enum i32 { + ROOT_SIGNATURE = 0, + VS = 1, + PS = 2, + DS = 3, + HS = 4, + GS = 5, + CS = 6, + STREAM_OUTPUT = 7, + BLEND = 8, + SAMPLE_MASK = 9, + RASTERIZER = 10, + DEPTH_STENCIL = 11, + INPUT_LAYOUT = 12, + IB_STRIP_CUT_VALUE = 13, + PRIMITIVE_TOPOLOGY = 14, + RENDER_TARGET_FORMATS = 15, + DEPTH_STENCIL_FORMAT = 16, + SAMPLE_DESC = 17, + NODE_MASK = 18, + CACHED_PSO = 19, + FLAGS = 20, + DEPTH_STENCIL1 = 21, + VIEW_INSTANCING = 22, + AS = 24, + MS = 25, + MAX_VALID = 26, +} + +FEATURE :: enum i32 { + OPTIONS = 0, + ARCHITECTURE = 1, + FEATURE_LEVELS = 2, + FORMAT_SUPPORT = 3, + MULTISAMPLE_QUALITY_LEVELS = 4, + FORMAT_INFO = 5, + GPU_VIRTUAL_ADDRESS_SUPPORT = 6, + SHADER_MODEL = 7, + OPTIONS1 = 8, + PROTECTED_RESOURCE_SESSION_SUPPORT = 10, + ROOT_SIGNATURE = 12, + ARCHITECTURE1 = 16, + OPTIONS2 = 18, + SHADER_CACHE = 19, + COMMAND_QUEUE_PRIORITY = 20, + OPTIONS3 = 21, + EXISTING_HEAPS = 22, + OPTIONS4 = 23, + SERIALIZATION = 24, + CROSS_NODE = 25, + OPTIONS5 = 27, + OPTIONS6 = 30, + QUERY_META_COMMAND = 31, + OPTIONS7 = 32, + PROTECTED_RESOURCE_SESSION_TYPE_COUNT = 33, + PROTECTED_RESOURCE_SESSION_TYPES = 34, +} + +SHADER_MIN_PRECISION_SUPPORT :: enum i32 { + NONE = 0, + _10_BIT = 1, + _16_BIT = 2, +} + +TILED_RESOURCES_TIER :: enum i32 { + NOT_SUPPORTED = 0, + _1 = 1, + _2 = 2, + _3 = 3, + _4 = 4, +} + +RESOURCE_BINDING_TIER :: enum i32 { + _1 = 1, + _2 = 2, + _3 = 3, +} + +CONSERVATIVE_RASTERIZATION_TIER :: enum i32 { + NOT_SUPPORTED = 0, + _1 = 1, + _2 = 2, + _3 = 3, +} + +FORMAT_SUPPORT1 :: enum i32 { // TODO: make bit_set + NONE = 0, + BUFFER = 1, + IA_VERTEX_BUFFER = 2, + IA_INDEX_BUFFER = 4, + SO_BUFFER = 8, + TEXTURE1D = 16, + TEXTURE2D = 32, + TEXTURE3D = 64, + TEXTURECUBE = 128, + SHADER_LOAD = 256, + SHADER_SAMPLE = 512, + SHADER_SAMPLE_COMPARISON = 1024, + SHADER_SAMPLE_MONO_TEXT = 2048, + MIP = 4096, + RENDER_TARGET = 16384, + BLENDABLE = 32768, + DEPTH_STENCIL = 65536, + MULTISAMPLE_RESOLVE = 262144, + DISPLAY = 524288, + CAST_WITHIN_BIT_LAYOUT = 1048576, + MULTISAMPLE_RENDERTARGET = 2097152, + MULTISAMPLE_LOAD = 4194304, + SHADER_GATHER = 8388608, + BACK_BUFFER_CAST = 16777216, + TYPED_UNORDERED_ACCESS_VIEW = 33554432, + SHADER_GATHER_COMPARISON = 67108864, + DECODER_OUTPUT = 134217728, + VIDEO_PROCESSOR_OUTPUT = 268435456, + VIDEO_PROCESSOR_INPUT = 536870912, + VIDEO_ENCODER = 1073741824, +} + +FORMAT_SUPPORT2 :: enum i32 { // TODO: make bit_set + NONE = 0, + UAV_ATOMIC_ADD = 1, + UAV_ATOMIC_BITWISE_OPS = 2, + UAV_ATOMIC_COMPARE_STORE_OR_COMPARE_EXCHANGE = 4, + UAV_ATOMIC_EXCHANGE = 8, + UAV_ATOMIC_SIGNED_MIN_OR_MAX = 16, + UAV_ATOMIC_UNSIGNED_MIN_OR_MAX = 32, + UAV_TYPED_LOAD = 64, + UAV_TYPED_STORE = 128, + OUTPUT_MERGER_LOGIC_OP = 256, + TILED = 512, + MULTIPLANE_OVERLAY = 16384, + SAMPLER_FEEDBACK = 32768, +} + +MULTISAMPLE_QUALITY_LEVEL_FLAGS :: enum u32 { // TODO: make bit_set + NONE = 0x0, + TILED_RESOURCE = 0x1, +} + +CROSS_NODE_SHARING_TIER :: enum i32 { + NOT_SUPPORTED = 0, + _1_EMULATED = 1, + _1 = 2, + _2 = 3, + _3 = 4, +} + +RESOURCE_HEAP_TIER :: enum i32 { + _1 = 1, + _2 = 2, +} + +PROGRAMMABLE_SAMPLE_POSITIONS_TIER :: enum i32 { + NOT_SUPPORTED = 0, + _1 = 1, + _2 = 2, +} + +VIEW_INSTANCING_TIER :: enum i32 { + NOT_SUPPORTED = 0, + _1 = 1, + _2 = 2, + _3 = 3, +} + +FEATURE_DATA_OPTIONS :: struct { + DoublePrecisionFloatShaderOps: BOOL, + OutputMergerLogicOp: BOOL, + MinPrecisionSupport: SHADER_MIN_PRECISION_SUPPORT, + TiledResourcesTier: TILED_RESOURCES_TIER, + ResourceBindingTier: RESOURCE_BINDING_TIER, + PSSpecifiedStencilRefSupported: BOOL, + TypedUAVLoadAdditionalFormats: BOOL, + ROVsSupported: BOOL, + ConservativeRasterizationTier: CONSERVATIVE_RASTERIZATION_TIER, + MaxGPUVirtualAddressBitsPerResource: u32, + StandardSwizzle64KBSupported: BOOL, + CrossNodeSharingTier: CROSS_NODE_SHARING_TIER, + CrossAdapterRowMajorTextureSupported: BOOL, + VPAndRTArrayIndexFromAnyShaderFeedingRasterizerSupportedWithoutGSEmulation: BOOL, + ResourceHeapTier: RESOURCE_HEAP_TIER, +} + +FEATURE_DATA_OPTIONS1 :: struct { + WaveOps: BOOL, + WaveLaneCountMin: u32, + WaveLaneCountMax: u32, + TotalLaneCount: u32, + ExpandedComputeResourceStates: BOOL, + Int64ShaderOps: BOOL, +} + +FEATURE_DATA_OPTIONS2 :: struct { + DepthBoundsTestSupported: BOOL, + ProgrammableSamplePositionsTier: PROGRAMMABLE_SAMPLE_POSITIONS_TIER, +} + +ROOT_SIGNATURE_VERSION :: enum i32 { + _1 = 1, + _1_0 = 1, + _1_1 = 2, +} + +FEATURE_DATA_ROOT_SIGNATURE :: struct { + HighestVersion: ROOT_SIGNATURE_VERSION, +} + +FEATURE_DATA_ARCHITECTURE :: struct { + NodeIndex: u32, + TileBasedRenderer: BOOL, + UMA: BOOL, + CacheCoherentUMA: BOOL, +} + +FEATURE_DATA_ARCHITECTURE1 :: struct { + NodeIndex: u32, + TileBasedRenderer: BOOL, + UMA: BOOL, + CacheCoherentUMA: BOOL, + IsolatedMMU: BOOL, +} + +FEATURE_DATA_FEATURE_LEVELS :: struct { + NumFeatureLevels: u32, + pFeatureLevelsRequested: ^FEATURE_LEVEL, + MaxSupportedFeatureLevel: FEATURE_LEVEL, +} + +SHADER_MODEL :: enum i32 { + _5_1 = 81, + _6_0 = 96, + _6_1 = 97, + _6_2 = 98, + _6_3 = 99, + _6_4 = 100, + _6_5 = 101, + _6_6 = 102, +} + +FEATURE_DATA_SHADER_MODEL :: struct { + HighestShaderModel: SHADER_MODEL, +} + +FEATURE_DATA_FORMAT_SUPPORT :: struct { + Format: dxgi.FORMAT, + Support1: FORMAT_SUPPORT1, + Support2: FORMAT_SUPPORT2, +} + +FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS :: struct { + Format: dxgi.FORMAT, + SampleCount: u32, + Flags: MULTISAMPLE_QUALITY_LEVEL_FLAGS, + NumQualityLevels: u32, +} + +FEATURE_DATA_FORMAT_INFO :: struct { + Format: dxgi.FORMAT, + PlaneCount: u8, +} + +FEATURE_DATA_GPU_VIRTUAL_ADDRESS_SUPPORT :: struct { + MaxGPUVirtualAddressBitsPerResource: u32, + MaxGPUVirtualAddressBitsPerProcess: u32, +} + +SHADER_CACHE_SUPPORT_FLAGS :: enum u32 { // TODO: make bit_set + NONE = 0x0, + SINGLE_PSO = 0x1, + LIBRARY = 0x2, + AUTOMATIC_INPROC_CACHE = 0x4, + AUTOMATIC_DISK_CACHE = 0x8, +} + +FEATURE_DATA_SHADER_CACHE :: struct { + SupportFlags: SHADER_CACHE_SUPPORT_FLAGS, +} + +FEATURE_DATA_COMMAND_QUEUE_PRIORITY :: struct { + CommandListType: COMMAND_LIST_TYPE, + Priority: u32, + PriorityForTypeIsSupported: BOOL, +} + +COMMAND_LIST_SUPPORT_FLAGS :: enum u32 { // TODO: make bit_set + NONE = 0x0, + DIRECT = 0x1, + BUNDLE = 0x2, + COMPUTE = 0x4, + COPY = 0x8, + VIDEO_DECODE = 0x10, + VIDEO_PROCESS = 0x20, + VIDEO_ENCODE = 0x40, +} + +FEATURE_DATA_OPTIONS3 :: struct { + CopyQueueTimestampQueriesSupported: BOOL, + CastingFullyTypedFormatSupported: BOOL, + WriteBufferImmediateSupportFlags: COMMAND_LIST_SUPPORT_FLAGS, + ViewInstancingTier: VIEW_INSTANCING_TIER, + BarycentricsSupported: BOOL, +} + +FEATURE_DATA_EXISTING_HEAPS :: struct { + Supported: BOOL, +} + +SHARED_RESOURCE_COMPATIBILITY_TIER :: enum i32 { + _0 = 0, + _1 = 1, + _2 = 2, +} + +FEATURE_DATA_OPTIONS4 :: struct { + MSAA64KBAlignedTextureSupported: BOOL, + SharedResourceCompatibilityTier: SHARED_RESOURCE_COMPATIBILITY_TIER, + Native16BitShaderOpsSupported: BOOL, +} + +HEAP_SERIALIZATION_TIER :: enum i32 { + _0 = 0, + _10 = 10, +} + +FEATURE_DATA_SERIALIZATION :: struct { + NodeIndex: u32, + HeapSerializationTier: HEAP_SERIALIZATION_TIER, +} + +FEATURE_DATA_CROSS_NODE :: struct { + SharingTier: CROSS_NODE_SHARING_TIER, + AtomicShaderInstructions: BOOL, +} + +RENDER_PASS_TIER :: enum i32 { + _0 = 0, + _1 = 1, + _2 = 2, +} + +RAYTRACING_TIER :: enum i32 { + NOT_SUPPORTED = 0, + _1_0 = 10, + _1_1 = 11, +} + +FEATURE_DATA_OPTIONS5 :: struct { + SRVOnlyTiledResourceTier3: BOOL, + RenderPassesTier: RENDER_PASS_TIER, + RaytracingTier: RAYTRACING_TIER, +} + +VARIABLE_SHADING_RATE_TIER :: enum i32 { + NOT_SUPPORTED = 0, + _1 = 1, + _2 = 2, +} + +FEATURE_DATA_OPTIONS6 :: struct { + AdditionalShadingRatesSupported: BOOL, + PerPrimitiveShadingRateSupportedWithViewportIndexing: BOOL, + VariableShadingRateTier: VARIABLE_SHADING_RATE_TIER, + ShadingRateImageTileSize: u32, + BackgroundProcessingSupported: BOOL, +} + +MESH_SHADER_TIER :: enum i32 { + NOT_SUPPORTED = 0, + _1 = 10, +} + +SAMPLER_FEEDBACK_TIER :: enum i32 { + NOT_SUPPORTED = 0, + _0_9 = 90, + _1_0 = 100, +} + +FEATURE_DATA_OPTIONS7 :: struct { + MeshShaderTier: MESH_SHADER_TIER, + SamplerFeedbackTier: SAMPLER_FEEDBACK_TIER, +} + +FEATURE_DATA_QUERY_META_COMMAND :: struct { + CommandId: GUID, + NodeMask: u32, + pQueryInputData: rawptr, + QueryInputDataSizeInBytes: SIZE_T, + pQueryOutputData: rawptr, + QueryOutputDataSizeInBytes: SIZE_T, +} + +RESOURCE_ALLOCATION_INFO :: struct { + SizeInBytes: u64, + Alignment: u64, +} + +RESOURCE_ALLOCATION_INFO1 :: struct { + Offset: u64, + Alignment: u64, + SizeInBytes: u64, +} + +HEAP_TYPE :: enum i32 { + DEFAULT = 1, + UPLOAD = 2, + READBACK = 3, + CUSTOM = 4, +} + +CPU_PAGE_PROPERTY :: enum i32 { + UNKNOWN = 0, + NOT_AVAILABLE = 1, + WRITE_COMBINE = 2, + WRITE_BACK = 3, +} + +MEMORY_POOL :: enum i32 { + UNKNOWN = 0, + L0 = 1, + L1 = 2, +} + +HEAP_PROPERTIES :: struct { + Type: HEAP_TYPE, + CPUPageProperty: CPU_PAGE_PROPERTY, + MemoryPoolPreference: MEMORY_POOL, + CreationNodeMask: u32, + VisibleNodeMask: u32, +} + +HEAP_FLAGS :: enum u32 { // TODO: make bit_set ??? + NONE = 0x0, + SHARED = 0x1, + DENY_BUFFERS = 0x4, + ALLOW_DISPLAY = 0x8, + SHARED_CROSS_ADAPTER = 0x20, + DENY_RT_DS_TEXTURES = 0x40, + DENY_NON_RT_DS_TEXTURES = 0x80, + HARDWARE_PROTECTED = 0x100, + ALLOW_WRITE_WATCH = 0x200, + ALLOW_SHADER_ATOMICS = 0x400, + CREATE_NOT_RESIDENT = 0x800, + CREATE_NOT_ZEROED = 0x1000, + ALLOW_ALL_BUFFERS_AND_TEXTURES = 0x0, + ALLOW_ONLY_BUFFERS = 0xc0, + ALLOW_ONLY_NON_RT_DS_TEXTURES = 0x44, + ALLOW_ONLY_RT_DS_TEXTURES = 0x84, +} + +HEAP_DESC :: struct { + SizeInBytes: u64, + Properties: HEAP_PROPERTIES, + Alignment: u64, + Flags: HEAP_FLAGS, +} + +RESOURCE_DIMENSION :: enum i32 { + UNKNOWN = 0, + BUFFER = 1, + TEXTURE1D = 2, + TEXTURE2D = 3, + TEXTURE3D = 4, +} + +TEXTURE_LAYOUT :: enum i32 { + UNKNOWN = 0, + ROW_MAJOR = 1, + _64KB_UNDEFINED_SWIZZLE = 2, + _64KB_STANDARD_SWIZZLE = 3, +} + +RESOURCE_FLAGS :: enum u32 { // TODO: make bit_set + NONE = 0x0, + ALLOW_RENDER_TARGET = 0x1, + ALLOW_DEPTH_STENCIL = 0x2, + ALLOW_UNORDERED_ACCESS = 0x4, + DENY_SHADER_RESOURCE = 0x8, + ALLOW_CROSS_ADAPTER = 0x10, + ALLOW_SIMULTANEOUS_ACCESS = 0x20, + VIDEO_DECODE_REFERENCE_ONLY = 0x40, +} + +MIP_REGION :: struct { + Width: u32, + Height: u32, + Depth: u32, +} + +RESOURCE_DESC :: struct { + Dimension: RESOURCE_DIMENSION, + Alignment: u64, + Width: u64, + Height: u32, + DepthOrArraySize: u16, + MipLevels: u16, + Format: dxgi.FORMAT, + SampleDesc: dxgi.SAMPLE_DESC, + Layout: TEXTURE_LAYOUT, + Flags: RESOURCE_FLAGS, +} + +RESOURCE_DESC1 :: struct { + Dimension: RESOURCE_DIMENSION, + Alignment: u64, + Width: u64, + Height: u32, + DepthOrArraySize: u16, + MipLevels: u16, + Format: dxgi.FORMAT, + SampleDesc: dxgi.SAMPLE_DESC, + Layout: TEXTURE_LAYOUT, + Flags: RESOURCE_FLAGS, + SamplerFeedbackMipRegion: MIP_REGION, +} + +DEPTH_STENCIL_VALUE :: struct { + Depth: f32, + Stencil: u8, +} + +CLEAR_VALUE :: struct { + Format: dxgi.FORMAT, + using _: struct #raw_union { + Color: [4]f32, + DepthStencil: DEPTH_STENCIL_VALUE, + }, +} + +RANGE :: struct { + Begin: SIZE_T, + End: SIZE_T, +} + +RANGE_UINT64 :: struct { + Begin: u64, + End: u64, +} + +SUBRESOURCE_RANGE_UINT64 :: struct { + Subresource: u32, + Range: RANGE_UINT64, +} + +SUBRESOURCE_INFO :: struct { + Offset: u64, + RowPitch: u32, + DepthPitch: u32, +} + +TILED_RESOURCE_COORDINATE :: struct { + X: u32, + Y: u32, + Z: u32, + Subresource: u32, +} + +TILE_REGION_SIZE :: struct { + NumTiles: u32, + UseBox: BOOL, + Width: u32, + Height: u16, + Depth: u16, +} + +TILE_RANGE_FLAGS :: enum u32 { // TODO: make bit_set + NONE = 0x0, + NULL = 0x1, + SKIP = 0x2, + REUSE_SINGLE_TILE = 0x4, +} + +SUBRESOURCE_TILING :: struct { + WidthInTiles: u32, + HeightInTiles: u16, + DepthInTiles: u16, + StartTileIndexInOverallResource: u32, +} + +TILE_SHAPE :: struct { + WidthInTexels: u32, + HeightInTexels: u32, + DepthInTexels: u32, +} + +PACKED_MIP_INFO :: struct { + NumStandardMips: u8, + NumPackedMips: u8, + NumTilesForPackedMips: u32, + StartTileIndexInOverallResource: u32, +} + +TILE_MAPPING_FLAGS :: enum u32 { // TODO: make bit_set + NONE = 0x0, + NO_HAZARD = 0x1, +} + +TILE_COPY_FLAGS :: enum u32 { // TODO: make bit_set + NONE = 0x0, + NO_HAZARD = 0x1, + LINEAR_BUFFER_TO_SWIZZLED_TILED_RESOURCE = 0x2, + SWIZZLED_TILED_RESOURCE_TO_LINEAR_BUFFER = 0x4, +} + +RESOURCE_STATES :: enum i32 { // TODO: make bit_set + COMMON = 0, + VERTEX_AND_CONSTANT_BUFFER = 1, + INDEX_BUFFER = 2, + RENDER_TARGET = 4, + UNORDERED_ACCESS = 8, + DEPTH_WRITE = 16, + DEPTH_READ = 32, + NON_PIXEL_SHADER_RESOURCE = 64, + PIXEL_SHADER_RESOURCE = 128, + STREAM_OUT = 256, + INDIRECT_ARGUMENT = 512, + COPY_DEST = 1024, + COPY_SOURCE = 2048, + RESOLVE_DEST = 4096, + RESOLVE_SOURCE = 8192, + RAYTRACING_ACCELERATION_STRUCTURE = 4194304, + SHADING_RATE_SOURCE = 16777216, + GENERIC_READ = 2755, + PRESENT = 0, + PREDICATION = 512, + VIDEO_DECODE_READ = 65536, + VIDEO_DECODE_WRITE = 131072, + VIDEO_PROCESS_READ = 262144, + VIDEO_PROCESS_WRITE = 524288, + VIDEO_ENCODE_READ = 2097152, + VIDEO_ENCODE_WRITE = 8388608, +} + +RESOURCE_BARRIER_TYPE :: enum i32 { + TRANSITION = 0, + ALIASING = 1, + UAV = 2, +} + +RESOURCE_TRANSITION_BARRIER :: struct { + pResource: ^IResource, + Subresource: u32, + StateBefore: RESOURCE_STATES, + StateAfter: RESOURCE_STATES, +} + +RESOURCE_ALIASING_BARRIER :: struct { + pResourceBefore: ^IResource, + pResourceAfter: ^IResource, +} + +RESOURCE_UAV_BARRIER :: struct { + pResource: ^IResource, +} + +RESOURCE_BARRIER_FLAGS :: enum u32 { // TODO: make bit_set + NONE = 0x0, + BEGIN_ONLY = 0x1, + END_ONLY = 0x2, +} + +RESOURCE_BARRIER :: struct { + Type: RESOURCE_BARRIER_TYPE, + Flags: RESOURCE_BARRIER_FLAGS, + using _: struct #raw_union { + Transition: RESOURCE_TRANSITION_BARRIER, + Aliasing: RESOURCE_ALIASING_BARRIER, + UAV: RESOURCE_UAV_BARRIER, + }, +} + +SUBRESOURCE_FOOTPRINT :: struct { + Format: dxgi.FORMAT, + Width: u32, + Height: u32, + Depth: u32, + RowPitch: u32, +} + +PLACED_SUBRESOURCE_FOOTPRINT :: struct { + Offset: u64, + Footprint: SUBRESOURCE_FOOTPRINT, +} + +TEXTURE_COPY_TYPE :: enum i32 { + SUBRESOURCE_INDEX = 0, + PLACED_FOOTPRINT = 1, +} + +TEXTURE_COPY_LOCATION :: struct { + pResource: ^IResource, + Type: TEXTURE_COPY_TYPE, + using _: struct #raw_union { + PlacedFootprint: PLACED_SUBRESOURCE_FOOTPRINT, + SubresourceIndex: u32, + }, +} + +RESOLVE_MODE :: enum i32 { + DECOMPRESS = 0, + MIN = 1, + MAX = 2, + AVERAGE = 3, + ENCODE_SAMPLER_FEEDBACK = 4, + DECODE_SAMPLER_FEEDBACK = 5, +} + +SAMPLE_POSITION :: struct { + X: i8, + Y: i8, +} + +VIEW_INSTANCE_LOCATION :: struct { + ViewportArrayIndex: u32, + RenderTargetArrayIndex: u32, +} + +VIEW_INSTANCING_FLAGS :: enum u32 { // TODO: make bit_set + NONE = 0x0, + ENABLE_VIEW_INSTANCE_MASKING = 0x1, +} + +VIEW_INSTANCING_DESC :: struct { + ViewInstanceCount: u32, + pViewInstanceLocations: ^VIEW_INSTANCE_LOCATION, + Flags: VIEW_INSTANCING_FLAGS, +} + +SHADER_COMPONENT_MAPPING :: enum i32 { + FROM_MEMORY_COMPONENT_0 = 0, + FROM_MEMORY_COMPONENT_1 = 1, + FROM_MEMORY_COMPONENT_2 = 2, + FROM_MEMORY_COMPONENT_3 = 3, + FORCE_VALUE_0 = 4, + FORCE_VALUE_1 = 5, +} + +BUFFER_SRV_FLAGS :: enum u32 { // TODO: make bit_set + NONE = 0x0, + RAW = 0x1, +} + +BUFFER_SRV :: struct { + FirstElement: u64, + NumElements: u32, + StructureByteStride: u32, + Flags: BUFFER_SRV_FLAGS, +} + +TEX1D_SRV :: struct { + MostDetailedMip: u32, + MipLevels: u32, + ResourceMinLODClamp: f32, +} + +TEX1D_ARRAY_SRV :: struct { + MostDetailedMip: u32, + MipLevels: u32, + FirstArraySlice: u32, + ArraySize: u32, + ResourceMinLODClamp: f32, +} + +TEX2D_SRV :: struct { + MostDetailedMip: u32, + MipLevels: u32, + PlaneSlice: u32, + ResourceMinLODClamp: f32, +} + +TEX2D_ARRAY_SRV :: struct { + MostDetailedMip: u32, + MipLevels: u32, + FirstArraySlice: u32, + ArraySize: u32, + PlaneSlice: u32, + ResourceMinLODClamp: f32, +} + +TEX3D_SRV :: struct { + MostDetailedMip: u32, + MipLevels: u32, + ResourceMinLODClamp: f32, +} + +TEXCUBE_SRV :: struct { + MostDetailedMip: u32, + MipLevels: u32, + ResourceMinLODClamp: f32, +} + +TEXCUBE_ARRAY_SRV :: struct { + MostDetailedMip: u32, + MipLevels: u32, + First2DArrayFace: u32, + NumCubes: u32, + ResourceMinLODClamp: f32, +} + +TEX2DMS_SRV :: struct { + UnusedField_NothingToDefine: u32, +} + +TEX2DMS_ARRAY_SRV :: struct { + FirstArraySlice: u32, + ArraySize: u32, +} + +RAYTRACING_ACCELERATION_STRUCTURE_SRV :: struct { + Location: GPU_VIRTUAL_ADDRESS, +} + +SHADER_RESOURCE_VIEW_DESC :: struct { + Format: dxgi.FORMAT, + ViewDimension: SRV_DIMENSION, + Shader4ComponentMapping: u32, + using _: struct #raw_union { + Buffer: BUFFER_SRV, + Texture1D: TEX1D_SRV, + Texture1DArray: TEX1D_ARRAY_SRV, + Texture2D: TEX2D_SRV, + Texture2DArray: TEX2D_ARRAY_SRV, + Texture2DMS: TEX2DMS_SRV, + Texture2DMSArray: TEX2DMS_ARRAY_SRV, + Texture3D: TEX3D_SRV, + TextureCube: TEXCUBE_SRV, + TextureCubeArray: TEXCUBE_ARRAY_SRV, + RaytracingAccelerationStructure: RAYTRACING_ACCELERATION_STRUCTURE_SRV, + }, +} + +CONSTANT_BUFFER_VIEW_DESC :: struct { + BufferLocation: GPU_VIRTUAL_ADDRESS, + SizeInBytes: u32, +} + +FILTER :: enum i32 { + MIN_MAG_MIP_POINT = 0, + MIN_MAG_POINT_MIP_LINEAR = 1, + MIN_POINT_MAG_LINEAR_MIP_POINT = 4, + MIN_POINT_MAG_MIP_LINEAR = 5, + MIN_LINEAR_MAG_MIP_POINT = 16, + MIN_LINEAR_MAG_POINT_MIP_LINEAR = 17, + MIN_MAG_LINEAR_MIP_POINT = 20, + MIN_MAG_MIP_LINEAR = 21, + ANISOTROPIC = 85, + COMPARISON_MIN_MAG_MIP_POINT = 128, + COMPARISON_MIN_MAG_POINT_MIP_LINEAR = 129, + COMPARISON_MIN_POINT_MAG_LINEAR_MIP_POINT = 132, + COMPARISON_MIN_POINT_MAG_MIP_LINEAR = 133, + COMPARISON_MIN_LINEAR_MAG_MIP_POINT = 144, + COMPARISON_MIN_LINEAR_MAG_POINT_MIP_LINEAR = 145, + COMPARISON_MIN_MAG_LINEAR_MIP_POINT = 148, + COMPARISON_MIN_MAG_MIP_LINEAR = 149, + COMPARISON_ANISOTROPIC = 213, + MINIMUM_MIN_MAG_MIP_POINT = 256, + MINIMUM_MIN_MAG_POINT_MIP_LINEAR = 257, + MINIMUM_MIN_POINT_MAG_LINEAR_MIP_POINT = 260, + MINIMUM_MIN_POINT_MAG_MIP_LINEAR = 261, + MINIMUM_MIN_LINEAR_MAG_MIP_POINT = 272, + MINIMUM_MIN_LINEAR_MAG_POINT_MIP_LINEAR = 273, + MINIMUM_MIN_MAG_LINEAR_MIP_POINT = 276, + MINIMUM_MIN_MAG_MIP_LINEAR = 277, + MINIMUM_ANISOTROPIC = 341, + MAXIMUM_MIN_MAG_MIP_POINT = 384, + MAXIMUM_MIN_MAG_POINT_MIP_LINEAR = 385, + MAXIMUM_MIN_POINT_MAG_LINEAR_MIP_POINT = 388, + MAXIMUM_MIN_POINT_MAG_MIP_LINEAR = 389, + MAXIMUM_MIN_LINEAR_MAG_MIP_POINT = 400, + MAXIMUM_MIN_LINEAR_MAG_POINT_MIP_LINEAR = 401, + MAXIMUM_MIN_MAG_LINEAR_MIP_POINT = 404, + MAXIMUM_MIN_MAG_MIP_LINEAR = 405, + MAXIMUM_ANISOTROPIC = 469, +} + +FILTER_TYPE :: enum i32 { + POINT = 0, + LINEAR = 1, +} + +FILTER_REDUCTION_TYPE :: enum i32 { + STANDARD = 0, + COMPARISON = 1, + MINIMUM = 2, + MAXIMUM = 3, +} + +TEXTURE_ADDRESS_MODE :: enum i32 { + WRAP = 1, + MIRROR = 2, + CLAMP = 3, + BORDER = 4, + MIRROR_ONCE = 5, +} + +SAMPLER_DESC :: struct { + Filter: FILTER, + AddressU: TEXTURE_ADDRESS_MODE, + AddressV: TEXTURE_ADDRESS_MODE, + AddressW: TEXTURE_ADDRESS_MODE, + MipLODBias: f32, + MaxAnisotropy: u32, + ComparisonFunc: COMPARISON_FUNC, + BorderColor: [4]f32, + MinLOD: f32, + MaxLOD: f32, +} + +BUFFER_UAV_FLAGS :: enum u32 { // TODO: make bit_set + NONE = 0x0, + RAW = 0x1, +} + +BUFFER_UAV :: struct { + FirstElement: u64, + NumElements: u32, + StructureByteStride: u32, + CounterOffsetInBytes: u64, + Flags: BUFFER_UAV_FLAGS, +} + +TEX1D_UAV :: struct { + MipSlice: u32, +} + +TEX1D_ARRAY_UAV :: struct { + MipSlice: u32, + FirstArraySlice: u32, + ArraySize: u32, +} + +TEX2D_UAV :: struct { + MipSlice: u32, + PlaneSlice: u32, +} + +TEX2D_ARRAY_UAV :: struct { + MipSlice: u32, + FirstArraySlice: u32, + ArraySize: u32, + PlaneSlice: u32, +} + +TEX3D_UAV :: struct { + MipSlice: u32, + FirstWSlice: u32, + WSize: u32, +} + +UAV_DIMENSION :: enum i32 { + UNKNOWN = 0, + BUFFER = 1, + TEXTURE1D = 2, + TEXTURE1DARRAY = 3, + TEXTURE2D = 4, + TEXTURE2DARRAY = 5, + TEXTURE3D = 8, +} + +UNORDERED_ACCESS_VIEW_DESC :: struct { + Format: dxgi.FORMAT, + ViewDimension: UAV_DIMENSION, + using _: struct #raw_union { + Buffer: BUFFER_UAV, + Texture1D: TEX1D_UAV, + Texture1DArray: TEX1D_ARRAY_UAV, + Texture2D: TEX2D_UAV, + Texture2DArray: TEX2D_ARRAY_UAV, + Texture3D: TEX3D_UAV, + }, +} + +BUFFER_RTV :: struct { + FirstElement: u64, + NumElements: u32, +} + +TEX1D_RTV :: struct { + MipSlice: u32, +} + +TEX1D_ARRAY_RTV :: struct { + MipSlice: u32, + FirstArraySlice: u32, + ArraySize: u32, +} + +TEX2D_RTV :: struct { + MipSlice: u32, + PlaneSlice: u32, +} + +TEX2DMS_RTV :: struct { + UnusedField_NothingToDefine: u32, +} + +TEX2D_ARRAY_RTV :: struct { + MipSlice: u32, + FirstArraySlice: u32, + ArraySize: u32, + PlaneSlice: u32, +} + +TEX2DMS_ARRAY_RTV :: struct { + FirstArraySlice: u32, + ArraySize: u32, +} + +TEX3D_RTV :: struct { + MipSlice: u32, + FirstWSlice: u32, + WSize: u32, +} + +RTV_DIMENSION :: enum i32 { + UNKNOWN = 0, + BUFFER = 1, + TEXTURE1D = 2, + TEXTURE1DARRAY = 3, + TEXTURE2D = 4, + TEXTURE2DARRAY = 5, + TEXTURE2DMS = 6, + TEXTURE2DMSARRAY = 7, + TEXTURE3D = 8, +} + +RENDER_TARGET_VIEW_DESC :: struct { + Format: dxgi.FORMAT, + ViewDimension: RTV_DIMENSION, + using _: struct #raw_union { + Buffer: BUFFER_RTV, + Texture1D: TEX1D_RTV, + Texture1DArray: TEX1D_ARRAY_RTV, + Texture2D: TEX2D_RTV, + Texture2DArray: TEX2D_ARRAY_RTV, + Texture2DMS: TEX2DMS_RTV, + Texture2DMSArray: TEX2DMS_ARRAY_RTV, + Texture3D: TEX3D_RTV, + }, +} + +TEX1D_DSV :: struct { + MipSlice: u32, +} + +TEX1D_ARRAY_DSV :: struct { + MipSlice: u32, + FirstArraySlice: u32, + ArraySize: u32, +} + +TEX2D_DSV :: struct { + MipSlice: u32, +} + +TEX2D_ARRAY_DSV :: struct { + MipSlice: u32, + FirstArraySlice: u32, + ArraySize: u32, +} + +TEX2DMS_DSV :: struct { + UnusedField_NothingToDefine: u32, +} + +TEX2DMS_ARRAY_DSV :: struct { + FirstArraySlice: u32, + ArraySize: u32, +} + +DSV_FLAGS :: enum u32 { // TODO: make bit_set + NONE = 0x0, + READ_ONLY_DEPTH = 0x1, + READ_ONLY_STENCIL = 0x2, +} + +DSV_DIMENSION :: enum i32 { + UNKNOWN = 0, + TEXTURE1D = 1, + TEXTURE1DARRAY = 2, + TEXTURE2D = 3, + TEXTURE2DARRAY = 4, + TEXTURE2DMS = 5, + TEXTURE2DMSARRAY = 6, +} + +DEPTH_STENCIL_VIEW_DESC :: struct { + Format: dxgi.FORMAT, + ViewDimension: DSV_DIMENSION, + Flags: DSV_FLAGS, + using _: struct #raw_union { + Texture1D: TEX1D_DSV, + Texture1DArray: TEX1D_ARRAY_DSV, + Texture2D: TEX2D_DSV, + Texture2DArray: TEX2D_ARRAY_DSV, + Texture2DMS: TEX2DMS_DSV, + Texture2DMSArray: TEX2DMS_ARRAY_DSV, + }, +} + +CLEAR_FLAGS :: enum u32 { // TODO: make bit_set + DEPTH = 0x1, + STENCIL = 0x2, +} + +FENCE_FLAGS :: enum u32 { // TODO: make bit_set + NONE = 0x0, + SHARED = 0x1, + SHARED_CROSS_ADAPTER = 0x2, + NON_MONITORED = 0x4, +} + +DESCRIPTOR_HEAP_TYPE :: enum i32 { + CBV_SRV_UAV = 0, + SAMPLER = 1, + RTV = 2, + DSV = 3, + NUM_TYPES = 4, +} + +DESCRIPTOR_HEAP_FLAGS :: enum u32 { // TODO: make bit_set + NONE = 0x0, + SHADER_VISIBLE = 0x1, +} + +DESCRIPTOR_HEAP_DESC :: struct { + Type: DESCRIPTOR_HEAP_TYPE, + NumDescriptors: u32, + Flags: DESCRIPTOR_HEAP_FLAGS, + NodeMask: u32, +} + +DESCRIPTOR_RANGE_TYPE :: enum i32 { + SRV = 0, + UAV = 1, + CBV = 2, + SAMPLER = 3, +} + +DESCRIPTOR_RANGE :: struct { + RangeType: DESCRIPTOR_RANGE_TYPE, + NumDescriptors: u32, + BaseShaderRegister: u32, + RegisterSpace: u32, + OffsetInDescriptorsFromTableStart: u32, +} + +ROOT_DESCRIPTOR_TABLE :: struct { + NumDescriptorRanges: u32, + pDescriptorRanges: ^DESCRIPTOR_RANGE, +} + +ROOT_CONSTANTS :: struct { + ShaderRegister: u32, + RegisterSpace: u32, + Num32BitValues: u32, +} + +ROOT_DESCRIPTOR :: struct { + ShaderRegister: u32, + RegisterSpace: u32, +} + +SHADER_VISIBILITY :: enum i32 { + ALL = 0, + VERTEX = 1, + HULL = 2, + DOMAIN = 3, + GEOMETRY = 4, + PIXEL = 5, + AMPLIFICATION = 6, + MESH = 7, +} + +ROOT_PARAMETER_TYPE :: enum i32 { + DESCRIPTOR_TABLE = 0, + _32BIT_CONSTANTS = 1, + CBV = 2, + SRV = 3, + UAV = 4, +} + +ROOT_PARAMETER :: struct { + ParameterType: ROOT_PARAMETER_TYPE, + using _: struct #raw_union { + DescriptorTable: ROOT_DESCRIPTOR_TABLE, + Constants: ROOT_CONSTANTS, + Descriptor: ROOT_DESCRIPTOR, + }, + ShaderVisibility: SHADER_VISIBILITY, +} + +ROOT_SIGNATURE_FLAGS :: enum u32 { // TODO: make bit_set + NONE = 0x0, + ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT = 0x1, + DENY_VERTEX_SHADER_ROOT_ACCESS = 0x2, + DENY_HULL_SHADER_ROOT_ACCESS = 0x4, + DENY_DOMAIN_SHADER_ROOT_ACCESS = 0x8, + DENY_GEOMETRY_SHADER_ROOT_ACCESS = 0x10, + DENY_PIXEL_SHADER_ROOT_ACCESS = 0x20, + ALLOW_STREAM_OUTPUT = 0x40, + LOCAL_ROOT_SIGNATURE = 0x80, + DENY_AMPLIFICATION_SHADER_ROOT_ACCESS = 0x100, + DENY_MESH_SHADER_ROOT_ACCESS = 0x200, +} + +STATIC_BORDER_COLOR :: enum i32 { + TRANSPARENT_BLACK = 0, + OPAQUE_BLACK = 1, + OPAQUE_WHITE = 2, +} + +STATIC_SAMPLER_DESC :: struct { + Filter: FILTER, + AddressU: TEXTURE_ADDRESS_MODE, + AddressV: TEXTURE_ADDRESS_MODE, + AddressW: TEXTURE_ADDRESS_MODE, + MipLODBias: f32, + MaxAnisotropy: u32, + ComparisonFunc: COMPARISON_FUNC, + BorderColor: STATIC_BORDER_COLOR, + MinLOD: f32, + MaxLOD: f32, + ShaderRegister: u32, + RegisterSpace: u32, + ShaderVisibility: SHADER_VISIBILITY, +} + +ROOT_SIGNATURE_DESC :: struct { + NumParameters: u32, + pParameters: ^ROOT_PARAMETER, + NumStaticSamplers: u32, + pStaticSamplers: ^STATIC_SAMPLER_DESC, + Flags: ROOT_SIGNATURE_FLAGS, +} + +DESCRIPTOR_RANGE_FLAGS :: enum u32 { // TODO: make bit_set + NONE = 0x0, + DESCRIPTORS_VOLATILE = 0x1, + DATA_VOLATILE = 0x2, + DATA_STATIC_WHILE_SET_AT_EXECUTE = 0x4, + DATA_STATIC = 0x8, + DESCRIPTORS_STATIC_KEEPING_BUFFER_BOUNDS_CHECKS = 0x10000, +} + +DESCRIPTOR_RANGE1 :: struct { + RangeType: DESCRIPTOR_RANGE_TYPE, + NumDescriptors: u32, + BaseShaderRegister: u32, + RegisterSpace: u32, + Flags: DESCRIPTOR_RANGE_FLAGS, + OffsetInDescriptorsFromTableStart: u32, +} + +ROOT_DESCRIPTOR_TABLE1 :: struct { + NumDescriptorRanges: u32, + pDescriptorRanges: ^DESCRIPTOR_RANGE1, +} + +ROOT_DESCRIPTOR_FLAGS :: enum u32 { // TODO: make bit_set + NONE = 0x0, + DATA_VOLATILE = 0x2, + DATA_STATIC_WHILE_SET_AT_EXECUTE = 0x4, + DATA_STATIC = 0x8, +} + +ROOT_DESCRIPTOR1 :: struct { + ShaderRegister: u32, + RegisterSpace: u32, + Flags: ROOT_DESCRIPTOR_FLAGS, +} + +ROOT_PARAMETER1 :: struct { + ParameterType: ROOT_PARAMETER_TYPE, + using _: struct #raw_union { + DescriptorTable: ROOT_DESCRIPTOR_TABLE1, + Constants: ROOT_CONSTANTS, + Descriptor: ROOT_DESCRIPTOR1, + }, + ShaderVisibility: SHADER_VISIBILITY, +} + +ROOT_SIGNATURE_DESC1 :: struct { + NumParameters: u32, + pParameters: ^ROOT_PARAMETER1, + NumStaticSamplers: u32, + pStaticSamplers: ^STATIC_SAMPLER_DESC, + Flags: ROOT_SIGNATURE_FLAGS, +} + +VERSIONED_ROOT_SIGNATURE_DESC :: struct { + Version: ROOT_SIGNATURE_VERSION, + using _: struct #raw_union { + Desc_1_0: ROOT_SIGNATURE_DESC, + Desc_1_1: ROOT_SIGNATURE_DESC1, + }, +} + + +IRootSignatureDeserializer_UUID_STRING :: "34AB647B-3CC8-46AC-841B-C0965645C046" +IRootSignatureDeserializer_UUID := &IID{0x34AB647B, 0x3CC8, 0x46AC, {0x84, 0x1B, 0xC0, 0x96, 0x56, 0x45, 0xC0, 0x46}} +IRootSignatureDeserializer :: struct #raw_union { + #subtype iunknown: IUnknown, + using id3d12rootsignaturedeserializer_vtable: ^IRootSignatureDeserializer_VTable, +} +IRootSignatureDeserializer_VTable :: struct { + using iunknown_vtable: IUnknown_VTable, + GetRootSignatureDesc: proc "stdcall" (this: ^IRootSignatureDeserializer) -> ^ROOT_SIGNATURE_DESC, +} + + +IVersionedRootSignatureDeserializer_UUID_STRING :: "7F91CE67-090C-4BB7-B78E-ED8FF2E31DA0" +IVersionedRootSignatureDeserializer_UUID := &IID{0x7F91CE67, 0x090C, 0x4BB7, {0xB7, 0x8E, 0xED, 0x8F, 0xF2, 0xE3, 0x1D, 0xA0}} +IVersionedRootSignatureDeserializer :: struct #raw_union { + #subtype iunknown: IUnknown, + using id3d12versionedrootsignaturedeserializer_vtable: ^IVersionedRootSignatureDeserializer_VTable, +} +IVersionedRootSignatureDeserializer_VTable :: struct { + using iunknown_vtable: IUnknown_VTable, + GetRootSignatureDescAtVersion: proc "stdcall" (this: ^IVersionedRootSignatureDeserializer, convertToVersion: ROOT_SIGNATURE_VERSION, ppDesc: ^^VERSIONED_ROOT_SIGNATURE_DESC) -> HRESULT, + GetUnconvertedRootSignatureDesc: proc "stdcall" (this: ^IVersionedRootSignatureDeserializer) -> ^VERSIONED_ROOT_SIGNATURE_DESC, +} + +PFN_SERIALIZE_ROOT_SIGNATURE :: #type proc "c" (a0: ^ROOT_SIGNATURE_DESC, a1: ROOT_SIGNATURE_VERSION, a2: ^^IBlob, a3: ^^IBlob) -> HRESULT +PFN_CREATE_ROOT_SIGNATURE_DESERIALIZER :: #type proc "c" (a0: rawptr, a1: SIZE_T, a2: ^IID, a3: ^rawptr) -> HRESULT +PFN_SERIALIZE_VERSIONED_ROOT_SIGNATURE :: #type proc "c" (a0: ^VERSIONED_ROOT_SIGNATURE_DESC, a1: ^^IBlob, a2: ^^IBlob) -> HRESULT +PFN_CREATE_VERSIONED_ROOT_SIGNATURE_DESERIALIZER :: #type proc "c" (a0: rawptr, a1: SIZE_T, a2: ^IID, a3: ^rawptr) -> HRESULT + + +CPU_DESCRIPTOR_HANDLE :: struct { + ptr: SIZE_T, +} + +GPU_DESCRIPTOR_HANDLE :: struct { + ptr: u64, +} + +DISCARD_REGION :: struct { + NumRects: u32, + pRects: ^RECT, + FirstSubresource: u32, + NumSubresources: u32, +} + +QUERY_HEAP_TYPE :: enum i32 { + OCCLUSION = 0, + TIMESTAMP = 1, + PIPELINE_STATISTICS = 2, + SO_STATISTICS = 3, + VIDEO_DECODE_STATISTICS = 4, + COPY_QUEUE_TIMESTAMP = 5, +} + +QUERY_HEAP_DESC :: struct { + Type: QUERY_HEAP_TYPE, + Count: u32, + NodeMask: u32, +} + +QUERY_TYPE :: enum i32 { + OCCLUSION = 0, + BINARY_OCCLUSION = 1, + TIMESTAMP = 2, + PIPELINE_STATISTICS = 3, + SO_STATISTICS_STREAM0 = 4, + SO_STATISTICS_STREAM1 = 5, + SO_STATISTICS_STREAM2 = 6, + SO_STATISTICS_STREAM3 = 7, + VIDEO_DECODE_STATISTICS = 8, +} + +PREDICATION_OP :: enum i32 { + EQUAL_ZERO = 0, + NOT_EQUAL_ZERO = 1, +} + +QUERY_DATA_PIPELINE_STATISTICS :: struct { + IAVertices: u64, + IAPrimitives: u64, + VSInvocations: u64, + GSInvocations: u64, + GSPrimitives: u64, + CInvocations: u64, + CPrimitives: u64, + PSInvocations: u64, + HSInvocations: u64, + DSInvocations: u64, + CSInvocations: u64, +} + +QUERY_DATA_SO_STATISTICS :: struct { + NumPrimitivesWritten: u64, + PrimitivesStorageNeeded: u64, +} + +STREAM_OUTPUT_BUFFER_VIEW :: struct { + BufferLocation: GPU_VIRTUAL_ADDRESS, + SizeInBytes: u64, + BufferFilledSizeLocation: GPU_VIRTUAL_ADDRESS, +} + +DRAW_ARGUMENTS :: struct { + VertexCountPerInstance: u32, + InstanceCount: u32, + StartVertexLocation: u32, + StartInstanceLocation: u32, +} + +DRAW_INDEXED_ARGUMENTS :: struct { + IndexCountPerInstance: u32, + InstanceCount: u32, + StartIndexLocation: u32, + BaseVertexLocation: i32, + StartInstanceLocation: u32, +} + +DISPATCH_ARGUMENTS :: struct { + ThreadGroupCountX: u32, + ThreadGroupCountY: u32, + ThreadGroupCountZ: u32, +} + +VERTEX_BUFFER_VIEW :: struct { + BufferLocation: GPU_VIRTUAL_ADDRESS, + SizeInBytes: u32, + StrideInBytes: u32, +} + +INDEX_BUFFER_VIEW :: struct { + BufferLocation: GPU_VIRTUAL_ADDRESS, + SizeInBytes: u32, + Format: dxgi.FORMAT, +} + +INDIRECT_ARGUMENT_TYPE :: enum i32 { + DRAW = 0, + DRAW_INDEXED = 1, + DISPATCH = 2, + VERTEX_BUFFER_VIEW = 3, + INDEX_BUFFER_VIEW = 4, + CONSTANT = 5, + CONSTANT_BUFFER_VIEW = 6, + SHADER_RESOURCE_VIEW = 7, + UNORDERED_ACCESS_VIEW = 8, + DISPATCH_RAYS = 9, + DISPATCH_MESH = 10, +} + +INDIRECT_ARGUMENT_DESC :: struct { + Type: INDIRECT_ARGUMENT_TYPE, + using _: struct #raw_union { + VertexBuffer: struct { + Slot: u32, + }, + Constant: struct { + RootParameterIndex: u32, + DestOffsetIn32BitValues: u32, + Num32BitValuesToSet: u32, + }, + ConstantBufferView: struct { + RootParameterIndex: u32, + }, + ShaderResourceView: struct { + RootParameterIndex: u32, + }, + UnorderedAccessView: struct { + RootParameterIndex: u32, + }, + }, +} + +COMMAND_SIGNATURE_DESC :: struct { + ByteStride: u32, + NumArgumentDescs: u32, + pArgumentDescs: ^INDIRECT_ARGUMENT_DESC, + NodeMask: u32, +} + + +IPageable_UUID_STRING :: "63ee58fb-1268-4835-86da-f008ce62f0d6" +IPageable_UUID := &IID{0x63ee58fb, 0x1268, 0x4835, {0x86, 0xda, 0xf0, 0x08, 0xce, 0x62, 0xf0, 0xd6}} +IPageable :: struct { + using id3d12devicechild: IDeviceChild, +} + + +IHeap_UUID_STRING :: "6b3b2502-6e51-45b3-90ee-9884265e8df3" +IHeap_UUID := &IID{0x6b3b2502, 0x6e51, 0x45b3, {0x90, 0xee, 0x98, 0x84, 0x26, 0x5e, 0x8d, 0xf3}} +IHeap :: struct #raw_union { + #subtype id3d12pageable: IPageable, + using id3d12heap_vtable: ^IHeap_VTable, +} +IHeap_VTable :: struct { + using id3d12devicechild_vtable: IDeviceChild_VTable, + GetDesc: proc "stdcall" (this: ^IHeap) -> HEAP_DESC, +} + + +IResource_UUID_STRING :: "696442be-a72e-4059-bc79-5b5c98040fad" +IResource_UUID := &IID{0x696442be, 0xa72e, 0x4059, {0xbc, 0x79, 0x5b, 0x5c, 0x98, 0x04, 0x0f, 0xad}} +IResource :: struct #raw_union { + #subtype id3d12pageable: IPageable, + using id3d12resource_vtable: ^IResource_VTable, +} +IResource_VTable :: struct { + using id3d12devicechild_vtable: IDeviceChild_VTable, + Map: proc "stdcall" (this: ^IResource, Subresource: u32, pReadRange: ^RANGE, ppData: ^rawptr) -> HRESULT, + Unmap: proc "stdcall" (this: ^IResource, Subresource: u32, pWrittenRange: ^RANGE), + GetDesc: proc "stdcall" (this: ^IResource) -> RESOURCE_DESC, + GetGPUVirtualAddress: proc "stdcall" (this: ^IResource) -> GPU_VIRTUAL_ADDRESS, + WriteToSubresource: proc "stdcall" (this: ^IResource, DstSubresource: u32, pDstBox: ^BOX, pSrcData: rawptr, SrcRowPitch: u32, SrcDepthPitch: u32) -> HRESULT, + ReadFromSubresource: proc "stdcall" (this: ^IResource, pDstData: rawptr, DstRowPitch: u32, DstDepthPitch: u32, SrcSubresource: u32, pSrcBox: ^BOX) -> HRESULT, + GetHeapProperties: proc "stdcall" (this: ^IResource, pHeapProperties: ^HEAP_PROPERTIES, pHeapFlags: ^HEAP_FLAGS) -> HRESULT, +} + + +ICommandAllocator_UUID_STRING :: "6102dee4-af59-4b09-b999-b44d73f09b24" +ICommandAllocator_UUID := &IID{0x6102dee4, 0xaf59, 0x4b09, {0xb9, 0x99, 0xb4, 0x4d, 0x73, 0xf0, 0x9b, 0x24}} +ICommandAllocator :: struct #raw_union { + #subtype id3d12pageable: IPageable, + using id3d12commandallocator_vtable: ^ICommandAllocator_VTable, +} +ICommandAllocator_VTable :: struct { + using id3d12devicechild_vtable: IDeviceChild_VTable, + Reset: proc "stdcall" (this: ^ICommandAllocator) -> HRESULT, +} + + +IFence_UUID_STRING :: "0a753dcf-c4d8-4b91-adf6-be5a60d95a76" +IFence_UUID := &IID {0x0a753dcf, 0xc4d8, 0x4b91, {0xad, 0xf6, 0xbe, 0x5a, 0x60, 0xd9, 0x5a, 0x76}} +IFence :: struct #raw_union { + #subtype id3d12pageable: IPageable, + using id3d12fence_vtable: ^IFence_VTable, +} +IFence_VTable :: struct { + using id3d12devicechild_vtable: IDeviceChild_VTable, + GetCompletedValue: proc "stdcall" (this: ^IFence) -> u64, + SetEventOnCompletion: proc "stdcall" (this: ^IFence, Value: u64, hEvent: HANDLE) -> HRESULT, + Signal: proc "stdcall" (this: ^IFence, Value: u64) -> HRESULT, +} + + +IFence1_UUID_STRING :: "433685fe-e22b-4ca0-a8db-b5b4f4dd0e4a" +IFence1_UUID := &IID{0x433685fe, 0xe22b, 0x4ca0, {0xa8, 0xdb, 0xb5, 0xb4, 0xf4, 0xdd, 0x0e, 0x4a}} +IFence1 :: struct #raw_union { + #subtype id3d12fence: IFence, + using id3d12fence1_vtable: ^IFence1_VTable, +} +IFence1_VTable :: struct { + #subtype id3d12fence_vtable: IFence_VTable, + GetCreationFlags: proc "stdcall" (this: ^IFence1) -> FENCE_FLAGS, +} + + +IPipelineState_UUID_STRING :: "765a30f3-f624-4c6f-a828-ace948622445" +IPipelineState_UUID := &IID{0x765a30f3, 0xf624, 0x4c6f, {0xa8, 0x28, 0xac, 0xe9, 0x48, 0x62, 0x24, 0x45}} +IPipelineState :: struct #raw_union { + #subtype id3d12pageable: IPageable, + using id3d12pipelinestate_vtable: ^IPipelineState_VTable, +} +IPipelineState_VTable :: struct { + using id3d12devicechild_vtable: IDeviceChild_VTable, + GetCachedBlob: proc "stdcall" (this: ^IPipelineState, ppBlob: ^^IBlob) -> HRESULT, +} + + +IDescriptorHeap_UUID_STRING :: "8efb471d-616c-4f49-90f7-127bb763fa51" +IDescriptorHeap_UUID := &IID{0x8efb471d, 0x616c, 0x4f49, { 0x90, 0xf7, 0x12, 0x7b, 0xb7, 0x63, 0xfa, 0x51}} +IDescriptorHeap :: struct #raw_union { + #subtype id3d12pageable: IPageable, + using id3d12descriptorheap_vtable: ^IDescriptorHeap_VTable, +} +IDescriptorHeap_VTable :: struct { + using id3d12devicechild_vtable: IDeviceChild_VTable, + GetDesc: proc "stdcall" (this: ^IDescriptorHeap, desc: ^DESCRIPTOR_HEAP_DESC), + GetCPUDescriptorHandleForHeapStart: proc "stdcall" (this: ^IDescriptorHeap, handle: ^CPU_DESCRIPTOR_HANDLE), + GetGPUDescriptorHandleForHeapStart: proc "stdcall" (this: ^IDescriptorHeap, handle: ^GPU_DESCRIPTOR_HANDLE), +} + +IQueryHeap_UUID_STRING :: "0d9658ae-ed45-469e-a61d-970ec583cab4" +IQueryHeap_UUID := &IID{0x0d9658ae, 0xed45, 0x469e, {0xa6, 0x1d, 0x97, 0x0e, 0xc5, 0x83, 0xca, 0xb4}} +IQueryHeap :: struct { + #subtype id3d12pageable: IPageable, +} + + +ICommandSignature_UUID_STRING :: "c36a797c-ec80-4f0a-8985-a7b2475082d1" +ICommandSignature_UUID := &IID{0xc36a797c, 0xec80, 0x4f0a, {0x89, 0x85, 0xa7, 0xb2, 0x47, 0x50, 0x82, 0xd1}} +ICommandSignature :: struct { + #subtype id3d12pageable: IPageable, +} + + +ICommandList_UUID_STRING :: "7116d91c-e7e4-47ce-b8c6-ec8168f437e5" +ICommandList_UUID := &IID {0x7116d91c, 0xe7e4, 0x47ce, {0xb8, 0xc6, 0xec, 0x81, 0x68, 0xf4, 0x37, 0xe5}} +ICommandList :: struct #raw_union { + #subtype id3d12devicechild: IDeviceChild, + using id3d12commandlist_vtable: ^ICommandList_VTable, +} +ICommandList_VTable :: struct { + using id3d12devicechild_vtable: IDeviceChild_VTable, + GetType: proc "stdcall" (this: ^ICommandList) -> COMMAND_LIST_TYPE, +} + + +IGraphicsCommandList_UUID_STRING :: "5b160d0f-ac1b-4185-8ba8-b3ae42a5a455" +IGraphicsCommandList_UUID := &IID{0x5b160d0f, 0xac1b, 0x4185, {0x8b, 0xa8, 0xb3, 0xae, 0x42, 0xa5, 0xa4, 0x55}} +IGraphicsCommandList :: struct #raw_union { + #subtype id3d12commandlist: ICommandList, + using id3d12graphicscommandlist_vtable: ^IGraphicsCommandList_VTable, +} +IGraphicsCommandList_VTable :: struct { + using id3d12commandlist_vtable: ICommandList_VTable, + Close: proc "stdcall" (this: ^IGraphicsCommandList) -> HRESULT, + Reset: proc "stdcall" (this: ^IGraphicsCommandList, pAllocator: ^ICommandAllocator, pInitialState: ^IPipelineState) -> HRESULT, + ClearState: proc "stdcall" (this: ^IGraphicsCommandList, pPipelineState: ^IPipelineState), + DrawInstanced: proc "stdcall" (this: ^IGraphicsCommandList, VertexCountPerInstance: u32, InstanceCount: u32, StartVertexLocation: u32, StartInstanceLocation: u32), + DrawIndexedInstanced: proc "stdcall" (this: ^IGraphicsCommandList, IndexCountPerInstance: u32, InstanceCount: u32, StartIndexLocation: u32, BaseVertexLocation: i32, StartInstanceLocation: u32), + Dispatch: proc "stdcall" (this: ^IGraphicsCommandList, ThreadGroupCountX: u32, ThreadGroupCountY: u32, ThreadGroupCountZ: u32), + CopyBufferRegion: proc "stdcall" (this: ^IGraphicsCommandList, pDstBuffer: ^IResource, DstOffset: u64, pSrcBuffer: ^IResource, SrcOffset: u64, NumBytes: u64), + CopyTextureRegion: proc "stdcall" (this: ^IGraphicsCommandList, pDst: ^TEXTURE_COPY_LOCATION, DstX: u32, DstY: u32, DstZ: u32, pSrc: ^TEXTURE_COPY_LOCATION, pSrcBox: ^BOX), + CopyResource: proc "stdcall" (this: ^IGraphicsCommandList, pDstResource: ^IResource, pSrcResource: ^IResource), + CopyTiles: proc "stdcall" (this: ^IGraphicsCommandList, pTiledResource: ^IResource, pTileRegionStartCoordinate: ^TILED_RESOURCE_COORDINATE, pTileRegionSize: ^TILE_REGION_SIZE, pBuffer: ^IResource, BufferStartOffsetInBytes: u64, Flags: TILE_COPY_FLAGS), + ResolveSubresource: proc "stdcall" (this: ^IGraphicsCommandList, pDstResource: ^IResource, DstSubresource: u32, pSrcResource: ^IResource, SrcSubresource: u32, Format: dxgi.FORMAT), + IASetPrimitiveTopology: proc "stdcall" (this: ^IGraphicsCommandList, PrimitiveTopology: PRIMITIVE_TOPOLOGY), + RSSetViewports: proc "stdcall" (this: ^IGraphicsCommandList, NumViewports: u32, pViewports: ^VIEWPORT), + RSSetScissorRects: proc "stdcall" (this: ^IGraphicsCommandList, NumRects: u32, pRects: ^RECT), + OMSetBlendFactor: proc "stdcall" (this: ^IGraphicsCommandList, BlendFactor: ^[4]f32), + OMSetStencilRef: proc "stdcall" (this: ^IGraphicsCommandList, StencilRef: u32), + SetPipelineState: proc "stdcall" (this: ^IGraphicsCommandList, pPipelineState: ^IPipelineState), + ResourceBarrier: proc "stdcall" (this: ^IGraphicsCommandList, NumBarriers: u32, pBarriers: ^RESOURCE_BARRIER), + ExecuteBundle: proc "stdcall" (this: ^IGraphicsCommandList, pCommandList: ^IGraphicsCommandList), + SetDescriptorHeaps: proc "stdcall" (this: ^IGraphicsCommandList, NumDescriptorHeaps: u32, ppDescriptorHeaps: ^^IDescriptorHeap), + SetComputeRootSignature: proc "stdcall" (this: ^IGraphicsCommandList, pRootSignature: ^IRootSignature), + SetGraphicsRootSignature: proc "stdcall" (this: ^IGraphicsCommandList, pRootSignature: ^IRootSignature), + SetComputeRootDescriptorTable: proc "stdcall" (this: ^IGraphicsCommandList, RootParameterIndex: u32, BaseDescriptor: GPU_DESCRIPTOR_HANDLE), + SetGraphicsRootDescriptorTable: proc "stdcall" (this: ^IGraphicsCommandList, RootParameterIndex: u32, BaseDescriptor: GPU_DESCRIPTOR_HANDLE), + SetComputeRoot32BitConstant: proc "stdcall" (this: ^IGraphicsCommandList, RootParameterIndex: u32, SrcData: u32, DestOffsetIn32BitValues: u32), + SetGraphicsRoot32BitConstant: proc "stdcall" (this: ^IGraphicsCommandList, RootParameterIndex: u32, SrcData: u32, DestOffsetIn32BitValues: u32), + SetComputeRoot32BitConstants: proc "stdcall" (this: ^IGraphicsCommandList, RootParameterIndex: u32, Num32BitValuesToSet: u32, pSrcData: rawptr, DestOffsetIn32BitValues: u32), + SetGraphicsRoot32BitConstants: proc "stdcall" (this: ^IGraphicsCommandList, RootParameterIndex: u32, Num32BitValuesToSet: u32, pSrcData: rawptr, DestOffsetIn32BitValues: u32), + SetComputeRootConstantBufferView: proc "stdcall" (this: ^IGraphicsCommandList, RootParameterIndex: u32, BufferLocation: GPU_VIRTUAL_ADDRESS), + SetGraphicsRootConstantBufferView: proc "stdcall" (this: ^IGraphicsCommandList, RootParameterIndex: u32, BufferLocation: GPU_VIRTUAL_ADDRESS), + SetComputeRootShaderResourceView: proc "stdcall" (this: ^IGraphicsCommandList, RootParameterIndex: u32, BufferLocation: GPU_VIRTUAL_ADDRESS), + SetGraphicsRootShaderResourceView: proc "stdcall" (this: ^IGraphicsCommandList, RootParameterIndex: u32, BufferLocation: GPU_VIRTUAL_ADDRESS), + SetComputeRootUnorderedAccessView: proc "stdcall" (this: ^IGraphicsCommandList, RootParameterIndex: u32, BufferLocation: GPU_VIRTUAL_ADDRESS), + SetGraphicsRootUnorderedAccessView: proc "stdcall" (this: ^IGraphicsCommandList, RootParameterIndex: u32, BufferLocation: GPU_VIRTUAL_ADDRESS), + IASetIndexBuffer: proc "stdcall" (this: ^IGraphicsCommandList, pView: ^INDEX_BUFFER_VIEW), + IASetVertexBuffers: proc "stdcall" (this: ^IGraphicsCommandList, StartSlot: u32, NumViews: u32, pViews: ^VERTEX_BUFFER_VIEW), + SOSetTargets: proc "stdcall" (this: ^IGraphicsCommandList, StartSlot: u32, NumViews: u32, pViews: ^STREAM_OUTPUT_BUFFER_VIEW), + OMSetRenderTargets: proc "stdcall" (this: ^IGraphicsCommandList, NumRenderTargetDescriptors: u32, pRenderTargetDescriptors: ^CPU_DESCRIPTOR_HANDLE, RTsSingleHandleToDescriptorRange: BOOL, pDepthStencilDescriptor: ^CPU_DESCRIPTOR_HANDLE), + ClearDepthStencilView: proc "stdcall" (this: ^IGraphicsCommandList, DepthStencilView: CPU_DESCRIPTOR_HANDLE, ClearFlags: CLEAR_FLAGS, Depth: f32, Stencil: u8, NumRects: u32, pRects: ^RECT), + ClearRenderTargetView: proc "stdcall" (this: ^IGraphicsCommandList, RenderTargetView: CPU_DESCRIPTOR_HANDLE, ColorRGBA: ^[4]f32, NumRects: u32, pRects: ^RECT), + ClearUnorderedAccessViewUint: proc "stdcall" (this: ^IGraphicsCommandList, ViewGPUHandleInCurrentHeap: GPU_DESCRIPTOR_HANDLE, ViewCPUHandle: CPU_DESCRIPTOR_HANDLE, pResource: ^IResource, Values: ^[4]u32, NumRects: u32, pRects: ^RECT), + ClearUnorderedAccessViewFloat: proc "stdcall" (this: ^IGraphicsCommandList, ViewGPUHandleInCurrentHeap: GPU_DESCRIPTOR_HANDLE, ViewCPUHandle: CPU_DESCRIPTOR_HANDLE, pResource: ^IResource, Values: ^[4]f32, NumRects: u32, pRects: ^RECT), + DiscardResource: proc "stdcall" (this: ^IGraphicsCommandList, pResource: ^IResource, pRegion: ^DISCARD_REGION), + BeginQuery: proc "stdcall" (this: ^IGraphicsCommandList, pQueryHeap: ^IQueryHeap, Type: QUERY_TYPE, Index: u32), + EndQuery: proc "stdcall" (this: ^IGraphicsCommandList, pQueryHeap: ^IQueryHeap, Type: QUERY_TYPE, Index: u32), + ResolveQueryData: proc "stdcall" (this: ^IGraphicsCommandList, pQueryHeap: ^IQueryHeap, Type: QUERY_TYPE, StartIndex: u32, NumQueries: u32, pDestinationBuffer: ^IResource, AlignedDestinationBufferOffset: u64), + SetPredication: proc "stdcall" (this: ^IGraphicsCommandList, pBuffer: ^IResource, AlignedBufferOffset: u64, Operation: PREDICATION_OP), + SetMarker: proc "stdcall" (this: ^IGraphicsCommandList, Metadata: u32, pData: rawptr, Size: u32), + BeginEvent: proc "stdcall" (this: ^IGraphicsCommandList, Metadata: u32, pData: rawptr, Size: u32), + EndEvent: proc "stdcall" (this: ^IGraphicsCommandList), + ExecuteIndirect: proc "stdcall" (this: ^IGraphicsCommandList, pCommandSignature: ^ICommandSignature, MaxCommandCount: u32, pArgumentBuffer: ^IResource, ArgumentBufferOffset: u64, pCountBuffer: ^IResource, CountBufferOffset: u64), +} + + +IGraphicsCommandList1_UUID_STRING :: "553103fb-1fe7-4557-bb38-946d7d0e7ca7" +IGraphicsCommandList1_UUID := &IID{0x553103fb, 0x1fe7, 0x4557, {0xbb, 0x38, 0x94, 0x6d, 0x7d, 0x0e, 0x7c, 0xa7}} +IGraphicsCommandList1 :: struct #raw_union { + #subtype id3d12graphicscommandlist: IGraphicsCommandList, + using id3d12graphicscommandlist1_vtable: ^IGraphicsCommandList1_VTable, +} +IGraphicsCommandList1_VTable :: struct { + using id3d12graphicscommandlist_vtable: IGraphicsCommandList_VTable, + AtomicCopyBufferUINT: proc "stdcall" (this: ^IGraphicsCommandList1, pDstBuffer: ^IResource, DstOffset: u64, pSrcBuffer: ^IResource, SrcOffset: u64, Dependencies: u32, ppDependentResources: ^^IResource, pDependentSubresourceRanges: ^SUBRESOURCE_RANGE_UINT64), + AtomicCopyBufferUINT64: proc "stdcall" (this: ^IGraphicsCommandList1, pDstBuffer: ^IResource, DstOffset: u64, pSrcBuffer: ^IResource, SrcOffset: u64, Dependencies: u32, ppDependentResources: ^^IResource, pDependentSubresourceRanges: ^SUBRESOURCE_RANGE_UINT64), + OMSetDepthBounds: proc "stdcall" (this: ^IGraphicsCommandList1, Min: f32, Max: f32), + SetSamplePositions: proc "stdcall" (this: ^IGraphicsCommandList1, NumSamplesPerPixel: u32, NumPixels: u32, pSamplePositions: ^SAMPLE_POSITION), + ResolveSubresourceRegion: proc "stdcall" (this: ^IGraphicsCommandList1, pDstResource: ^IResource, DstSubresource: u32, DstX: u32, DstY: u32, pSrcResource: ^IResource, SrcSubresource: u32, pSrcRect: ^RECT, Format: dxgi.FORMAT, ResolveMode: RESOLVE_MODE), + SetViewInstanceMask: proc "stdcall" (this: ^IGraphicsCommandList1, Mask: u32), +} + +WRITEBUFFERIMMEDIATE_PARAMETER :: struct { + Dest: GPU_VIRTUAL_ADDRESS, + Value: u32, +} + +WRITEBUFFERIMMEDIATE_MODE :: enum i32 { + DEFAULT = 0, + MARKER_IN = 1, + MARKER_OUT = 2, +} + + +IGraphicsCommandList2_UUID_STRING :: "38C3E585-FF17-412C-9150-4FC6F9D72A28" +IGraphicsCommandList2_UUID := &IID{0x38C3E585, 0xFF17, 0x412C, {0x91, 0x50, 0x4F, 0xC6, 0xF9, 0xD7, 0x2A, 0x28}} +IGraphicsCommandList2 :: struct #raw_union { + #subtype id3d12graphicscommandlist1: IGraphicsCommandList1, + using id3d12graphicscommandlist2_vtable: ^IGraphicsCommandList2_VTable, +} +IGraphicsCommandList2_VTable :: struct { + using id3d12graphicscommandlist1_vtable: IGraphicsCommandList1_VTable, + WriteBufferImmediate: proc "stdcall" (this: ^IGraphicsCommandList2, Count: u32, pParams: ^WRITEBUFFERIMMEDIATE_PARAMETER, pModes: ^WRITEBUFFERIMMEDIATE_MODE), +} + + +ICommandQueue_UUID_STRING :: "0ec870a6-5d7e-4c22-8cfc-5baae07616ed" +ICommandQueue_UUID := &IID{0x0ec870a6, 0x5d7e, 0x4c22, { 0x8c, 0xfc, 0x5b, 0xaa, 0xe0, 0x76, 0x16, 0xed}} +ICommandQueue :: struct #raw_union { + #subtype id3d12pageable: IPageable, + using id3d12commandqueue_vtable: ^ICommandQueue_VTable, +} +ICommandQueue_VTable :: struct { + using id3d12devicechild_vtable: IDeviceChild_VTable, + UpdateTileMappings: proc "stdcall" (this: ^ICommandQueue, pResource: ^IResource, NumResourceRegions: u32, pResourceRegionStartCoordinates: ^TILED_RESOURCE_COORDINATE, pResourceRegionSizes: ^TILE_REGION_SIZE, pHeap: ^IHeap, NumRanges: u32, pRangeFlags: ^TILE_RANGE_FLAGS, pHeapRangeStartOffsets: ^u32, pRangeTileCounts: ^u32, Flags: TILE_MAPPING_FLAGS), + CopyTileMappings: proc "stdcall" (this: ^ICommandQueue, pDstResource: ^IResource, pDstRegionStartCoordinate: ^TILED_RESOURCE_COORDINATE, pSrcResource: ^IResource, pSrcRegionStartCoordinate: ^TILED_RESOURCE_COORDINATE, pRegionSize: ^TILE_REGION_SIZE, Flags: TILE_MAPPING_FLAGS), + ExecuteCommandLists: proc "stdcall" (this: ^ICommandQueue, NumCommandLists: u32, ppCommandLists: ^^ICommandList), + SetMarker: proc "stdcall" (this: ^ICommandQueue, Metadata: u32, pData: rawptr, Size: u32), + BeginEvent: proc "stdcall" (this: ^ICommandQueue, Metadata: u32, pData: rawptr, Size: u32), + EndEvent: proc "stdcall" (this: ^ICommandQueue), + Signal: proc "stdcall" (this: ^ICommandQueue, pFence: ^IFence, Value: u64) -> HRESULT, + Wait: proc "stdcall" (this: ^ICommandQueue, pFence: ^IFence, Value: u64) -> HRESULT, + GetTimestampFrequency: proc "stdcall" (this: ^ICommandQueue, pFrequency: ^u64) -> HRESULT, + GetClockCalibration: proc "stdcall" (this: ^ICommandQueue, pGpuTimestamp: ^u64, pCpuTimestamp: ^u64) -> HRESULT, + GetDesc: proc "stdcall" (this: ^ICommandQueue) -> COMMAND_QUEUE_DESC, +} + + +IDevice_UUID_STRING :: "189819f1-1db6-4b57-be54-1821339b85f7" +IDevice_UUID := &IID{0x189819f1, 0x1db6, 0x4b57, { 0xbe, 0x54, 0x18, 0x21, 0x33, 0x9b, 0x85, 0xf7}} +IDevice :: struct #raw_union { + #subtype id3d12object: IObject, + using id3d12device_vtable: ^IDevice_VTable, +} +IDevice_VTable :: struct { + using id3d12object_vtable: IObject_VTable, + GetNodeCount: proc "stdcall" (this: ^IDevice) -> u32, + CreateCommandQueue: proc "stdcall" (this: ^IDevice, pDesc: ^COMMAND_QUEUE_DESC, riid: ^IID, ppCommandQueue: ^rawptr) -> HRESULT, + CreateCommandAllocator: proc "stdcall" (this: ^IDevice, type: COMMAND_LIST_TYPE, riid: ^IID, ppCommandAllocator: ^rawptr) -> HRESULT, + CreateGraphicsPipelineState: proc "stdcall" (this: ^IDevice, pDesc: ^GRAPHICS_PIPELINE_STATE_DESC, riid: ^IID, ppPipelineState: ^rawptr) -> HRESULT, + CreateComputePipelineState: proc "stdcall" (this: ^IDevice, pDesc: ^COMPUTE_PIPELINE_STATE_DESC, riid: ^IID, ppPipelineState: ^rawptr) -> HRESULT, + CreateCommandList: proc "stdcall" (this: ^IDevice, nodeMask: u32, type: COMMAND_LIST_TYPE, pCommandAllocator: ^ICommandAllocator, pInitialState: ^IPipelineState, riid: ^IID, ppCommandList: ^rawptr) -> HRESULT, + CheckFeatureSupport: proc "stdcall" (this: ^IDevice, Feature: FEATURE, pFeatureSupportData: rawptr, FeatureSupportDataSize: u32) -> HRESULT, + CreateDescriptorHeap: proc "stdcall" (this: ^IDevice, pDescriptorHeapDesc: ^DESCRIPTOR_HEAP_DESC, riid: ^IID, ppvHeap: ^rawptr) -> HRESULT, + GetDescriptorHandleIncrementSize: proc "stdcall" (this: ^IDevice, DescriptorHeapType: DESCRIPTOR_HEAP_TYPE) -> u32, + CreateRootSignature: proc "stdcall" (this: ^IDevice, nodeMask: u32, pBlobWithRootSignature: rawptr, blobLengthInBytes: SIZE_T, riid: ^IID, ppvRootSignature: ^rawptr) -> HRESULT, + CreateConstantBufferView: proc "stdcall" (this: ^IDevice, pDesc: ^CONSTANT_BUFFER_VIEW_DESC, DestDescriptor: CPU_DESCRIPTOR_HANDLE), + CreateShaderResourceView: proc "stdcall" (this: ^IDevice, pResource: ^IResource, pDesc: ^SHADER_RESOURCE_VIEW_DESC, DestDescriptor: CPU_DESCRIPTOR_HANDLE), + CreateUnorderedAccessView: proc "stdcall" (this: ^IDevice, pResource: ^IResource, pCounterResource: ^IResource, pDesc: ^UNORDERED_ACCESS_VIEW_DESC, DestDescriptor: CPU_DESCRIPTOR_HANDLE), + CreateRenderTargetView: proc "stdcall" (this: ^IDevice, pResource: ^IResource, pDesc: ^RENDER_TARGET_VIEW_DESC, DestDescriptor: CPU_DESCRIPTOR_HANDLE), + CreateDepthStencilView: proc "stdcall" (this: ^IDevice, pResource: ^IResource, pDesc: ^DEPTH_STENCIL_VIEW_DESC, DestDescriptor: CPU_DESCRIPTOR_HANDLE), + CreateSampler: proc "stdcall" (this: ^IDevice, pDesc: ^SAMPLER_DESC, DestDescriptor: CPU_DESCRIPTOR_HANDLE), + CopyDescriptors: proc "stdcall" (this: ^IDevice, NumDestDescriptorRanges: u32, pDestDescriptorRangeStarts: ^CPU_DESCRIPTOR_HANDLE, pDestDescriptorRangeSizes: ^u32, NumSrcDescriptorRanges: u32, pSrcDescriptorRangeStarts: ^CPU_DESCRIPTOR_HANDLE, pSrcDescriptorRangeSizes: ^u32, DescriptorHeapsType: DESCRIPTOR_HEAP_TYPE), + CopyDescriptorsSimple: proc "stdcall" (this: ^IDevice, NumDescriptors: u32, DestDescriptorRangeStart: CPU_DESCRIPTOR_HANDLE, SrcDescriptorRangeStart: CPU_DESCRIPTOR_HANDLE, DescriptorHeapsType: DESCRIPTOR_HEAP_TYPE), + GetResourceAllocationInfo: proc "stdcall" (this: ^IDevice, visibleMask: u32, numResourceDescs: u32, pResourceDescs: ^RESOURCE_DESC) -> RESOURCE_ALLOCATION_INFO, + GetCustomHeapProperties: proc "stdcall" (this: ^IDevice, nodeMask: u32, heapType: HEAP_TYPE) -> HEAP_PROPERTIES, + CreateCommittedResource: proc "stdcall" (this: ^IDevice, pHeapProperties: ^HEAP_PROPERTIES, HeapFlags: HEAP_FLAGS, pDesc: ^RESOURCE_DESC, InitialResourceState: RESOURCE_STATES, pOptimizedClearValue: ^CLEAR_VALUE, riidResource: ^IID, ppvResource: ^rawptr) -> HRESULT, + CreateHeap: proc "stdcall" (this: ^IDevice, pDesc: ^HEAP_DESC, riid: ^IID, ppvHeap: ^rawptr) -> HRESULT, + CreatePlacedResource: proc "stdcall" (this: ^IDevice, pHeap: ^IHeap, HeapOffset: u64, pDesc: ^RESOURCE_DESC, InitialState: RESOURCE_STATES, pOptimizedClearValue: ^CLEAR_VALUE, riid: ^IID, ppvResource: ^rawptr) -> HRESULT, + CreateReservedResource: proc "stdcall" (this: ^IDevice, pDesc: ^RESOURCE_DESC, InitialState: RESOURCE_STATES, pOptimizedClearValue: ^CLEAR_VALUE, riid: ^IID, ppvResource: ^rawptr) -> HRESULT, + CreateSharedHandle: proc "stdcall" (this: ^IDevice, pObject: ^IDeviceChild, pAttributes: ^win32.SECURITY_ATTRIBUTES, Access: u32, Name: [^]u16, pHandle: ^HANDLE) -> HRESULT, + OpenSharedHandle: proc "stdcall" (this: ^IDevice, NTHandle: HANDLE, riid: ^IID, ppvObj: ^rawptr) -> HRESULT, + OpenSharedHandleByName: proc "stdcall" (this: ^IDevice, Name: [^]u16, Access: u32, pNTHandle: ^HANDLE) -> HRESULT, + MakeResident: proc "stdcall" (this: ^IDevice, NumObjects: u32, ppObjects: ^^IPageable) -> HRESULT, + Evict: proc "stdcall" (this: ^IDevice, NumObjects: u32, ppObjects: ^^IPageable) -> HRESULT, + CreateFence: proc "stdcall" (this: ^IDevice, InitialValue: u64, Flags: FENCE_FLAGS, riid: ^IID, ppFence: ^rawptr) -> HRESULT, + GetDeviceRemovedReason: proc "stdcall" (this: ^IDevice) -> HRESULT, + GetCopyableFootprints: proc "stdcall" (this: ^IDevice, pResourceDesc: ^RESOURCE_DESC, FirstSubresource: u32, NumSubresources: u32, BaseOffset: u64, pLayouts: ^PLACED_SUBRESOURCE_FOOTPRINT, pNumRows: ^u32, pRowSizeInBytes: ^u64, pTotalBytes: ^u64), + CreateQueryHeap: proc "stdcall" (this: ^IDevice, pDesc: ^QUERY_HEAP_DESC, riid: ^IID, ppvHeap: ^rawptr) -> HRESULT, + SetStablePowerState: proc "stdcall" (this: ^IDevice, Enable: BOOL) -> HRESULT, + CreateCommandSignature: proc "stdcall" (this: ^IDevice, pDesc: ^COMMAND_SIGNATURE_DESC, pRootSignature: ^IRootSignature, riid: ^IID, ppvCommandSignature: ^rawptr) -> HRESULT, + GetResourceTiling: proc "stdcall" (this: ^IDevice, pTiledResource: ^IResource, pNumTilesForEntireResource: ^u32, pPackedMipDesc: ^PACKED_MIP_INFO, pStandardTileShapeForNonPackedMips: ^TILE_SHAPE, pNumSubresourceTilings: ^u32, FirstSubresourceTilingToGet: u32, pSubresourceTilingsForNonPackedMips: ^SUBRESOURCE_TILING), + GetAdapterLuid: proc "stdcall" (this: ^IDevice) -> LUID, +} + + +IPipelineLibrary_UUID_STRING :: "c64226a8-9201-46af-b4cc-53fb9ff7414f" +IPipelineLibrary_UUID := &IID{0xc64226a8, 0x9201, 0x46af, {0xb4, 0xcc, 0x53, 0xfb, 0x9f, 0xf7, 0x41, 0x4f}} +IPipelineLibrary :: struct #raw_union { + #subtype id3d12devicechild: IDeviceChild, + using id3d12pipelinelibrary_vtable: ^IPipelineLibrary_VTable, +} +IPipelineLibrary_VTable :: struct { + using id3d12devicechild_vtable: IDeviceChild_VTable, + StorePipeline: proc "stdcall" (this: ^IPipelineLibrary, pName: [^]u16, pPipeline: ^IPipelineState) -> HRESULT, + LoadGraphicsPipeline: proc "stdcall" (this: ^IPipelineLibrary, pName: [^]u16, pDesc: ^GRAPHICS_PIPELINE_STATE_DESC, riid: ^IID, ppPipelineState: ^rawptr) -> HRESULT, + LoadComputePipeline: proc "stdcall" (this: ^IPipelineLibrary, pName: [^]u16, pDesc: ^COMPUTE_PIPELINE_STATE_DESC, riid: ^IID, ppPipelineState: ^rawptr) -> HRESULT, + GetSerializedSize: proc "stdcall" (this: ^IPipelineLibrary) -> SIZE_T, + Serialize: proc "stdcall" (this: ^IPipelineLibrary, pData: rawptr, DataSizeInBytes: SIZE_T) -> HRESULT, +} + + +IPipelineLibrary1_UUID_STRING :: "80eabf42-2568-4e5e-bd82-c37f86961dc3" +IPipelineLibrary1_UUID := &IID{0x80eabf42, 0x2568, 0x4e5e, {0xbd, 0x82, 0xc3, 0x7f, 0x86, 0x96, 0x1d, 0xc3}} +IPipelineLibrary1 :: struct #raw_union { + #subtype id3d12pipelinelibrary: IPipelineLibrary, + using id3d12pipelinelibrary1_vtable: ^IPipelineLibrary1_VTable, +} +IPipelineLibrary1_VTable :: struct { + using id3d12pipelinelibrary_vtable: IPipelineLibrary_VTable, + LoadPipeline: proc "stdcall" (this: ^IPipelineLibrary1, pName: [^]u16, pDesc: ^PIPELINE_STATE_STREAM_DESC, riid: ^IID, ppPipelineState: ^rawptr) -> HRESULT, +} + +MULTIPLE_FENCE_WAIT_FLAGS :: enum u32 { // TODO: make bit_set + NONE = 0x0, + ANY = 0x1, + ALL = 0x0, +} + +RESIDENCY_PRIORITY :: enum i32 { + MINIMUM = 671088640, + LOW = 1342177280, + NORMAL = 2013265920, + HIGH = -1610547200, + MAXIMUM = -939524096, +} + + +IDevice1_UUID_STRING :: "77acce80-638e-4e65-8895-c1f23386863e" +IDevice1_UUID := &IID{0x77acce80, 0x638e, 0x4e65, {0x88, 0x95, 0xc1, 0xf2, 0x33, 0x86, 0x86, 0x3e}} +IDevice1 :: struct #raw_union { + #subtype id3d12device: IDevice, + using id3d12device1_vtable: ^IDevice1_VTable, +} +IDevice1_VTable :: struct { + using id3d12device_vtable: IDevice_VTable, + CreatePipelineLibrary: proc "stdcall" (this: ^IDevice1, pLibraryBlob: rawptr, BlobLength: SIZE_T, riid: ^IID, ppPipelineLibrary: ^rawptr) -> HRESULT, + SetEventOnMultipleFenceCompletion: proc "stdcall" (this: ^IDevice1, ppFences: ^^IFence, pFenceValues: ^u64, NumFences: u32, Flags: MULTIPLE_FENCE_WAIT_FLAGS, hEvent: HANDLE) -> HRESULT, + SetResidencyPriority: proc "stdcall" (this: ^IDevice1, NumObjects: u32, ppObjects: ^^IPageable, pPriorities: ^RESIDENCY_PRIORITY) -> HRESULT, +} + + +IDevice2_UUID_STRING :: "30baa41e-b15b-475c-a0bb-1af5c5b64328" +IDevice2_UUID := &IID{0x30baa41e, 0xb15b, 0x475c, {0xa0, 0xbb, 0x1a, 0xf5, 0xc5, 0xb6, 0x43, 0x28}} +IDevice2 :: struct #raw_union { + #subtype id3d12device1: IDevice1, + using id3d12device2_vtable: ^IDevice2_VTable, +} +IDevice2_VTable :: struct { + using id3d12device1_vtable: IDevice1_VTable, + CreatePipelineState: proc "stdcall" (this: ^IDevice2, pDesc: ^PIPELINE_STATE_STREAM_DESC, riid: ^IID, ppPipelineState: ^rawptr) -> HRESULT, +} + +RESIDENCY_FLAGS :: enum u32 { // TODO: make bit_set + NONE = 0x0, + DENY_OVERBUDGET = 0x1, +} + + +IDevice3_UUID_STRING :: "81dadc15-2bad-4392-93c5-101345c4aa98" +IDevice3_UUID := &IID{0x81dadc15, 0x2bad, 0x4392, {0x93, 0xc5, 0x10, 0x13, 0x45, 0xc4, 0xaa, 0x98}} +IDevice3 :: struct #raw_union { + #subtype id3d12device2: IDevice2, + using id3d12device3_vtable: ^IDevice3_VTable, +} +IDevice3_VTable :: struct { + using id3d12device2_vtable: IDevice2_VTable, + OpenExistingHeapFromAddress: proc "stdcall" (this: ^IDevice3, pAddress: rawptr, riid: ^IID, ppvHeap: ^rawptr) -> HRESULT, + OpenExistingHeapFromFileMapping: proc "stdcall" (this: ^IDevice3, hFileMapping: HANDLE, riid: ^IID, ppvHeap: ^rawptr) -> HRESULT, + EnqueueMakeResident: proc "stdcall" (this: ^IDevice3, Flags: RESIDENCY_FLAGS, NumObjects: u32, ppObjects: ^^IPageable, pFenceToSignal: ^IFence, FenceValueToSignal: u64) -> HRESULT, +} + +COMMAND_LIST_FLAGS :: enum u32 { // TODO: make bit_set + COMMAND_LIST_FLAG_NONE = 0x0, +} + +COMMAND_POOL_FLAGS :: enum u32 { // TODO: make bit_set + COMMAND_POOL_FLAG_NONE = 0x0, +} + +COMMAND_RECORDER_FLAGS :: enum u32 { // TODO: make bit_set + COMMAND_RECORDER_FLAG_NONE = 0x0, +} + +PROTECTED_SESSION_STATUS :: enum i32 { + OK = 0, + INVALID = 1, +} + + +IProtectedSession_UUID_STRING :: "A1533D18-0AC1-4084-85B9-89A96116806B" +IProtectedSession_UUID := &IID{0xA1533D18, 0x0AC1, 0x4084, {0x85, 0xB9, 0x89, 0xA9, 0x61, 0x16, 0x80, 0x6B}} +IProtectedSession :: struct #raw_union { + #subtype id3d12devicechild: IDeviceChild, + using id3d12protectedsession_vtable: ^IProtectedSession_VTable, +} +IProtectedSession_VTable :: struct { + using id3d12devicechild_vtable: IDeviceChild_VTable, + GetStatusFence: proc "stdcall" (this: ^IProtectedSession, riid: ^IID, ppFence: ^rawptr) -> HRESULT, + GetSessionStatus: proc "stdcall" (this: ^IProtectedSession) -> PROTECTED_SESSION_STATUS, +} + +PROTECTED_RESOURCE_SESSION_SUPPORT_FLAGS :: enum u32 { // TODO: make bit_set + NONE = 0x0, + SUPPORTED = 0x1, +} + +FEATURE_DATA_PROTECTED_RESOURCE_SESSION_SUPPORT :: struct { + NodeIndex: u32, + Support: PROTECTED_RESOURCE_SESSION_SUPPORT_FLAGS, +} + +PROTECTED_RESOURCE_SESSION_FLAGS :: enum u32 { // TODO: make bit_set + PROTECTED_RESOURCE_SESSION_FLAG_NONE = 0x0, +} + +PROTECTED_RESOURCE_SESSION_DESC :: struct { + NodeMask: u32, + Flags: PROTECTED_RESOURCE_SESSION_FLAGS, +} + + +IProtectedResourceSession_UUID_STRING :: "6CD696F4-F289-40CC-8091-5A6C0A099C3D" +IProtectedResourceSession_UUID := &IID{0x6CD696F4, 0xF289, 0x40CC, {0x80, 0x91, 0x5A, 0x6C, 0x0A, 0x09, 0x9C, 0x3D}} +IProtectedResourceSession :: struct #raw_union { + #subtype id3d12protectedsession: IProtectedSession, + using id3d12protectedresourcesession_vtable: ^IProtectedResourceSession_VTable, +} +IProtectedResourceSession_VTable :: struct { + using id3d12protectedsession_vtable: IProtectedSession_VTable, + GetDesc: proc "stdcall" (this: ^IProtectedResourceSession) -> PROTECTED_RESOURCE_SESSION_DESC, +} + + +IDevice4_UUID_STRING :: "e865df17-a9ee-46f9-a463-3098315aa2e5" +IDevice4_UUID := &IID{0xe865df17, 0xa9ee, 0x46f9, {0xa4, 0x63, 0x30, 0x98, 0x31, 0x5a, 0xa2, 0xe5}} +IDevice4 :: struct #raw_union { + #subtype id3d12device3: IDevice3, + using id3d12device4_vtable: ^IDevice4_VTable, +} +IDevice4_VTable :: struct { + using id3d12device3_vtable: IDevice3_VTable, + CreateCommandList1: proc "stdcall" (this: ^IDevice4, nodeMask: u32, type: COMMAND_LIST_TYPE, flags: COMMAND_LIST_FLAGS, riid: ^IID, ppCommandList: ^rawptr) -> HRESULT, + CreateProtectedResourceSession: proc "stdcall" (this: ^IDevice4, pDesc: ^PROTECTED_RESOURCE_SESSION_DESC, riid: ^IID, ppSession: ^rawptr) -> HRESULT, + CreateCommittedResource1: proc "stdcall" (this: ^IDevice4, pHeapProperties: ^HEAP_PROPERTIES, HeapFlags: HEAP_FLAGS, pDesc: ^RESOURCE_DESC, InitialResourceState: RESOURCE_STATES, pOptimizedClearValue: ^CLEAR_VALUE, pProtectedSession: ^IProtectedResourceSession, riidResource: ^IID, ppvResource: ^rawptr) -> HRESULT, + CreateHeap1: proc "stdcall" (this: ^IDevice4, pDesc: ^HEAP_DESC, pProtectedSession: ^IProtectedResourceSession, riid: ^IID, ppvHeap: ^rawptr) -> HRESULT, + CreateReservedResource1: proc "stdcall" (this: ^IDevice4, pDesc: ^RESOURCE_DESC, InitialState: RESOURCE_STATES, pOptimizedClearValue: ^CLEAR_VALUE, pProtectedSession: ^IProtectedResourceSession, riid: ^IID, ppvResource: ^rawptr) -> HRESULT, + GetResourceAllocationInfo1: proc "stdcall" (this: ^IDevice4, visibleMask: u32, numResourceDescs: u32, pResourceDescs: ^RESOURCE_DESC, pResourceAllocationInfo1: ^RESOURCE_ALLOCATION_INFO1) -> RESOURCE_ALLOCATION_INFO, +} + +LIFETIME_STATE :: enum i32 { + IN_USE = 0, + NOT_IN_USE = 1, +} + + +ILifetimeOwner_UUID_STRING :: "e667af9f-cd56-4f46-83ce-032e595d70a8" +ILifetimeOwner_UUID := &IID{0xe667af9f, 0xcd56, 0x4f46, {0x83, 0xce, 0x03, 0x2e, 0x59, 0x5d, 0x70, 0xa8}} +ILifetimeOwner :: struct #raw_union { + #subtype iunknown: IUnknown, + using id3d12lifetimeowner_vtable: ^ILifetimeOwner_VTable, +} +ILifetimeOwner_VTable :: struct { + using iunknown_vtable: IUnknown_VTable, + LifetimeStateUpdated: proc "stdcall" (this: ^ILifetimeOwner, NewState: LIFETIME_STATE), +} + + +ISwapChainAssistant_UUID_STRING :: "f1df64b6-57fd-49cd-8807-c0eb88b45c8f" +ISwapChainAssistant_UUID := &IID{0xf1df64b6, 0x57fd, 0x49cd, {0x88, 0x07, 0xc0, 0xeb, 0x88, 0xb4, 0x5c, 0x8f}} +ISwapChainAssistant :: struct #raw_union { + #subtype iunknown: IUnknown, + using id3d12swapchainassistant_vtable: ^ISwapChainAssistant_VTable, +} +ISwapChainAssistant_VTable :: struct { + using iunknown_vtable: IUnknown_VTable, + GetLUID: proc "stdcall" (this: ^ISwapChainAssistant) -> LUID, + GetSwapChainObject: proc "stdcall" (this: ^ISwapChainAssistant, riid: ^IID, ppv: ^rawptr) -> HRESULT, + GetCurrentResourceAndCommandQueue: proc "stdcall" (this: ^ISwapChainAssistant, riidResource: ^IID, ppvResource: ^rawptr, riidQueue: ^IID, ppvQueue: ^rawptr) -> HRESULT, + InsertImplicitSync: proc "stdcall" (this: ^ISwapChainAssistant) -> HRESULT, +} + + +ILifetimeTracker_UUID_STRING :: "3fd03d36-4eb1-424a-a582-494ecb8ba813" +ILifetimeTracker_UUID := &IID{0x3fd03d36, 0x4eb1, 0x424a, {0xa5, 0x82, 0x49, 0x4e, 0xcb, 0x8b, 0xa8, 0x13}} +ILifetimeTracker :: struct #raw_union { + #subtype id3d12devicechild: IDeviceChild, + using id3d12lifetimetracker_vtable: ^ILifetimeTracker_VTable, +} +ILifetimeTracker_VTable :: struct { + using id3d12devicechild_vtable: IDeviceChild_VTable, + DestroyOwnedObject: proc "stdcall" (this: ^ILifetimeTracker, pObject: ^IDeviceChild) -> HRESULT, +} + +META_COMMAND_PARAMETER_TYPE :: enum i32 { + FLOAT = 0, + UINT64 = 1, + GPU_VIRTUAL_ADDRESS = 2, + CPU_DESCRIPTOR_HANDLE_HEAP_TYPE_CBV_SRV_UAV = 3, + GPU_DESCRIPTOR_HANDLE_HEAP_TYPE_CBV_SRV_UAV = 4, +} + +META_COMMAND_PARAMETER_FLAGS :: enum u32 { // TODO: make bit_set + INPUT = 0x1, + OUTPUT = 0x2, +} + +META_COMMAND_PARAMETER_STAGE :: enum i32 { + CREATION = 0, + INITIALIZATION = 1, + EXECUTION = 2, +} + +META_COMMAND_PARAMETER_DESC :: struct { + Name: [^]u16, + Type: META_COMMAND_PARAMETER_TYPE, + Flags: META_COMMAND_PARAMETER_FLAGS, + RequiredResourceState: RESOURCE_STATES, + StructureOffset: u32, +} + +GRAPHICS_STATES :: enum i32 { + NONE = 0, + IA_VERTEX_BUFFERS = 1, + IA_INDEX_BUFFER = 2, + IA_PRIMITIVE_TOPOLOGY = 4, + DESCRIPTOR_HEAP = 8, + GRAPHICS_ROOT_SIGNATURE = 16, + COMPUTE_ROOT_SIGNATURE = 32, + RS_VIEWPORTS = 64, + RS_SCISSOR_RECTS = 128, + PREDICATION = 256, + OM_RENDER_TARGETS = 512, + OM_STENCIL_REF = 1024, + OM_BLEND_FACTOR = 2048, + PIPELINE_STATE = 4096, + SO_TARGETS = 8192, + OM_DEPTH_BOUNDS = 16384, + SAMPLE_POSITIONS = 32768, + VIEW_INSTANCE_MASK = 65536, +} + +META_COMMAND_DESC :: struct { + Id: GUID, + Name: [^]u16, + InitializationDirtyState: GRAPHICS_STATES, + ExecutionDirtyState: GRAPHICS_STATES, +} + + +IStateObject_UUID_STRING :: "47016943-fca8-4594-93ea-af258b55346d" +IStateObject_UUID := &IID{0x47016943, 0xfca8, 0x4594, {0x93, 0xea, 0xaf, 0x25, 0x8b, 0x55, 0x34, 0x6d}} +IStateObject :: struct #raw_union { + #subtype id3d12pageable: IPageable, +} + + +IStateObjectProperties_UUID_STRING :: "de5fa827-9bf9-4f26-89ff-d7f56fde3860" +IStateObjectProperties_IID := &IID{0xde5fa827, 0x9bf9, 0x4f26, {0x89, 0xff, 0xd7, 0xf5, 0x6f, 0xde, 0x38, 0x60}} +IStateObjectProperties :: struct #raw_union { + #subtype iunknown: IUnknown, + using id3d12stateobjectproperties_vtable: ^IStateObjectProperties_VTable, +} +IStateObjectProperties_VTable :: struct { + using iunknown_vtable: IUnknown_VTable, + GetShaderIdentifier: proc "stdcall" (this: ^IStateObjectProperties, pExportName: [^]u16) -> rawptr, + GetShaderStackSize: proc "stdcall" (this: ^IStateObjectProperties, pExportName: [^]u16) -> u64, + GetPipelineStackSize: proc "stdcall" (this: ^IStateObjectProperties) -> u64, + SetPipelineStackSize: proc "stdcall" (this: ^IStateObjectProperties, PipelineStackSizeInBytes: u64), +} + +STATE_SUBOBJECT_TYPE :: enum i32 { + STATE_OBJECT_CONFIG = 0, + GLOBAL_ROOT_SIGNATURE = 1, + LOCAL_ROOT_SIGNATURE = 2, + NODE_MASK = 3, + DXIL_LIBRARY = 5, + EXISTING_COLLECTION = 6, + SUBOBJECT_TO_EXPORTS_ASSOCIATION = 7, + DXIL_SUBOBJECT_TO_EXPORTS_ASSOCIATION = 8, + RAYTRACING_SHADER_CONFIG = 9, + RAYTRACING_PIPELINE_CONFIG = 10, + HIT_GROUP = 11, + RAYTRACING_PIPELINE_CONFIG1 = 12, + MAX_VALID = 13, +} + +STATE_SUBOBJECT :: struct { + Type: STATE_SUBOBJECT_TYPE, + pDesc: rawptr, +} + +STATE_OBJECT_FLAGS :: enum u32 { // TODO: make bit_set + NONE = 0x0, + ALLOW_LOCAL_DEPENDENCIES_ON_EXTERNAL_DEFINITIONS = 0x1, + ALLOW_EXTERNAL_DEPENDENCIES_ON_LOCAL_DEFINITIONS = 0x2, + ALLOW_STATE_OBJECT_ADDITIONS = 0x4, +} + +STATE_OBJECT_CONFIG :: struct { + Flags: STATE_OBJECT_FLAGS, +} + +GLOBAL_ROOT_SIGNATURE :: struct { + pGlobalRootSignature: ^IRootSignature, +} + +LOCAL_ROOT_SIGNATURE :: struct { + pLocalRootSignature: ^IRootSignature, +} + +NODE_MASK :: struct { + NodeMask: u32, +} + +EXPORT_FLAGS :: enum u32 { // TODO: make bit_set + EXPORT_FLAG_NONE = 0x0, +} + +EXPORT_DESC :: struct { + Name: [^]u16, + ExportToRename: [^]u16, + Flags: EXPORT_FLAGS, +} + +DXIL_LIBRARY_DESC :: struct { + DXILLibrary: SHADER_BYTECODE, + NumExports: u32, + pExports: ^EXPORT_DESC, +} + +EXISTING_COLLECTION_DESC :: struct { + pExistingCollection: ^IStateObject, + NumExports: u32, + pExports: ^EXPORT_DESC, +} + +SUBOBJECT_TO_EXPORTS_ASSOCIATION :: struct { + pSubobjectToAssociate: ^STATE_SUBOBJECT, + NumExports: u32, + pExports: [^]^i16, +} + +DXIL_SUBOBJECT_TO_EXPORTS_ASSOCIATION :: struct { + SubobjectToAssociate: ^i16, + NumExports: u32, + pExports: [^]^i16, +} + +HIT_GROUP_TYPE :: enum i32 { + TRIANGLES = 0, + PROCEDURAL_PRIMITIVE = 1, +} + +HIT_GROUP_DESC :: struct { + HitGroupExport: ^i16, + Type: HIT_GROUP_TYPE, + AnyHitShaderImport: ^i16, + ClosestHitShaderImport: ^i16, + IntersectionShaderImport: ^i16, +} + +RAYTRACING_SHADER_CONFIG :: struct { + MaxPayloadSizeInBytes: u32, + MaxAttributeSizeInBytes: u32, +} + +RAYTRACING_PIPELINE_CONFIG :: struct { + MaxTraceRecursionDepth: u32, +} + +RAYTRACING_PIPELINE_FLAGS :: enum u32 { // TODO: make bit_set + NONE = 0x0, + SKIP_TRIANGLES = 0x100, + SKIP_PROCEDURAL_PRIMITIVES = 0x200, +} + +RAYTRACING_PIPELINE_CONFIG1 :: struct { + MaxTraceRecursionDepth: u32, + Flags: RAYTRACING_PIPELINE_FLAGS, +} + +STATE_OBJECT_TYPE :: enum i32 { + COLLECTION = 0, + RAYTRACING_PIPELINE = 3, +} + +STATE_OBJECT_DESC :: struct { + Type: STATE_OBJECT_TYPE, + NumSubobjects: u32, + pSubobjects: ^STATE_SUBOBJECT, +} + +RAYTRACING_GEOMETRY_FLAGS :: enum u32 { // TODO: make bit_set + NONE = 0x0, + OPAQUE = 0x1, + NO_DUPLICATE_ANYHIT_INVOCATION = 0x2, +} + +RAYTRACING_GEOMETRY_TYPE :: enum i32 { + TRIANGLES = 0, + PROCEDURAL_PRIMITIVE_AABBS = 1, +} + +RAYTRACING_INSTANCE_FLAGS :: enum u32 { // TODO: make bit_set + NONE = 0x0, + TRIANGLE_CULL_DISABLE = 0x1, + TRIANGLE_FRONT_COUNTERCLOCKWISE = 0x2, + FORCE_OPAQUE = 0x4, + FORCE_NON_OPAQUE = 0x8, +} + +GPU_VIRTUAL_ADDRESS_AND_STRIDE :: struct { + StartAddress: GPU_VIRTUAL_ADDRESS, + StrideInBytes: u64, +} + +GPU_VIRTUAL_ADDRESS_RANGE :: struct { + StartAddress: GPU_VIRTUAL_ADDRESS, + SizeInBytes: u64, +} + +GPU_VIRTUAL_ADDRESS_RANGE_AND_STRIDE :: struct { + StartAddress: GPU_VIRTUAL_ADDRESS, + SizeInBytes: u64, + StrideInBytes: u64, +} + +RAYTRACING_GEOMETRY_TRIANGLES_DESC :: struct { + Transform3x4: GPU_VIRTUAL_ADDRESS, + IndexFormat: dxgi.FORMAT, + VertexFormat: dxgi.FORMAT, + IndexCount: u32, + VertexCount: u32, + IndexBuffer: GPU_VIRTUAL_ADDRESS, + VertexBuffer: GPU_VIRTUAL_ADDRESS_AND_STRIDE, +} + +RAYTRACING_AABB :: struct { + MinX: f32, + MinY: f32, + MinZ: f32, + MaxX: f32, + MaxY: f32, + MaxZ: f32, +} + +RAYTRACING_GEOMETRY_AABBS_DESC :: struct { + AABBCount: u64, + AABBs: GPU_VIRTUAL_ADDRESS_AND_STRIDE, +} + +RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAGS :: enum u32 { // TODO: make bit_set + NONE = 0x0, + ALLOW_UPDATE = 0x1, + ALLOW_COMPACTION = 0x2, + PREFER_FAST_TRACE = 0x4, + PREFER_FAST_BUILD = 0x8, + MINIMIZE_MEMORY = 0x10, + PERFORM_UPDATE = 0x20, +} + +RAYTRACING_ACCELERATION_STRUCTURE_COPY_MODE :: enum i32 { + CLONE = 0, + COMPACT = 1, + VISUALIZATION_DECODE_FOR_TOOLS = 2, + SERIALIZE = 3, + DESERIALIZE = 4, +} + +RAYTRACING_ACCELERATION_STRUCTURE_TYPE :: enum i32 { + TOP_LEVEL = 0, + BOTTOM_LEVEL = 1, +} + +ELEMENTS_LAYOUT :: enum i32 { + ARRAY = 0, + ARRAY_OF_POINTERS = 1, +} + +RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO_TYPE :: enum i32 { + COMPACTED_SIZE = 0, + TOOLS_VISUALIZATION = 1, + SERIALIZATION = 2, + CURRENT_SIZE = 3, +} + +RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO_DESC :: struct { + DestBuffer: GPU_VIRTUAL_ADDRESS, + InfoType: RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO_TYPE, +} + +RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO_COMPACTED_SIZE_DESC :: struct { + CompactedSizeInBytes: u64, +} + +RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO_TOOLS_VISUALIZATION_DESC :: struct { + DecodedSizeInBytes: u64, +} + +BUILD_RAYTRACING_ACCELERATION_STRUCTURE_TOOLS_VISUALIZATION_HEADER :: struct { + Type: RAYTRACING_ACCELERATION_STRUCTURE_TYPE, + NumDescs: u32, +} + +RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO_SERIALIZATION_DESC :: struct { + SerializedSizeInBytes: u64, + NumBottomLevelAccelerationStructurePointers: u64, +} + +SERIALIZED_DATA_DRIVER_MATCHING_IDENTIFIER :: struct { + DriverOpaqueGUID: GUID, + DriverOpaqueVersioningData: [16]u8, +} + +SERIALIZED_DATA_TYPE :: enum i32 { + SERIALIZED_DATA_RAYTRACING_ACCELERATION_STRUCTURE = 0, +} + +DRIVER_MATCHING_IDENTIFIER_STATUS :: enum i32 { + COMPATIBLE_WITH_DEVICE = 0, + UNSUPPORTED_TYPE = 1, + UNRECOGNIZED = 2, + INCOMPATIBLE_VERSION = 3, + INCOMPATIBLE_TYPE = 4, +} + +SERIALIZED_RAYTRACING_ACCELERATION_STRUCTURE_HEADER :: struct { + DriverMatchingIdentifier: SERIALIZED_DATA_DRIVER_MATCHING_IDENTIFIER, + SerializedSizeInBytesIncludingHeader: u64, + DeserializedSizeInBytes: u64, + NumBottomLevelAccelerationStructurePointersAfterHeader: u64, +} + +RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO_CURRENT_SIZE_DESC :: struct { + CurrentSizeInBytes: u64, +} + +RAYTRACING_INSTANCE_DESC :: struct { + Transform: [3][4]f32, + InstanceID: u32, + InstanceContributionToHitGroupIndex: u32, + AccelerationStructure: GPU_VIRTUAL_ADDRESS, +} + +RAYTRACING_GEOMETRY_DESC :: struct { + Type: RAYTRACING_GEOMETRY_TYPE, + Flags: RAYTRACING_GEOMETRY_FLAGS, + using _: struct #raw_union { + Triangles: RAYTRACING_GEOMETRY_TRIANGLES_DESC, + AABBs: RAYTRACING_GEOMETRY_AABBS_DESC, + }, +} + +BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS :: struct { + Type: RAYTRACING_ACCELERATION_STRUCTURE_TYPE, + Flags: RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAGS, + NumDescs: u32, + DescsLayout: ELEMENTS_LAYOUT, + using _: struct #raw_union { + InstanceDescs: GPU_VIRTUAL_ADDRESS, + pGeometryDescs: ^RAYTRACING_GEOMETRY_DESC, + ppGeometryDescs: ^^RAYTRACING_GEOMETRY_DESC, + }, +} + +BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC :: struct { + DestAccelerationStructureData: GPU_VIRTUAL_ADDRESS, + Inputs: BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS, + SourceAccelerationStructureData: GPU_VIRTUAL_ADDRESS, + ScratchAccelerationStructureData: GPU_VIRTUAL_ADDRESS, +} + +RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO :: struct { + ResultDataMaxSizeInBytes: u64, + ScratchDataSizeInBytes: u64, + UpdateScratchDataSizeInBytes: u64, +} + +RAY_FLAGS :: enum u32 { // TODO: make bit_set + NONE = 0x0, + FORCE_OPAQUE = 0x1, + FORCE_NON_OPAQUE = 0x2, + ACCEPT_FIRST_HIT_AND_END_SEARCH = 0x4, + SKIP_CLOSEST_HIT_SHADER = 0x8, + CULL_BACK_FACING_TRIANGLES = 0x10, + CULL_FRONT_FACING_TRIANGLES = 0x20, + CULL_OPAQUE = 0x40, + CULL_NON_OPAQUE = 0x80, + SKIP_TRIANGLES = 0x100, + SKIP_PROCEDURAL_PRIMITIVES = 0x200, +} + +HIT_KIND :: enum i32 { + TRIANGLE_FRONT_FACE = 254, + TRIANGLE_BACK_FACE = 255, +} + + +IDevice5_UUID_STRING :: "8b4f173b-2fea-4b80-8f58-4307191ab95d" +IDevice5_UUID := &IID{0x8b4f173b, 0x2fea, 0x4b80, {0x8f, 0x58, 0x43, 0x07, 0x19, 0x1a, 0xb9, 0x5d}} +IDevice5 :: struct #raw_union { + #subtype id3d12device4: IDevice4, + using id3d12device5_vtable: ^IDevice5_VTable, +} +IDevice5_VTable :: struct { + using id3d12device4_vtable: IDevice4_VTable, + CreateLifetimeTracker: proc "stdcall" (this: ^IDevice5, pOwner: ^ILifetimeOwner, riid: ^IID, ppvTracker: ^rawptr) -> HRESULT, + RemoveDevice: proc "stdcall" (this: ^IDevice5), + EnumerateMetaCommands: proc "stdcall" (this: ^IDevice5, pNumMetaCommands: ^u32, pDescs: ^META_COMMAND_DESC) -> HRESULT, + EnumerateMetaCommandParameters: proc "stdcall" (this: ^IDevice5, CommandId: ^GUID, Stage: META_COMMAND_PARAMETER_STAGE, pTotalStructureSizeInBytes: ^u32, pParameterCount: ^u32, pParameterDescs: ^META_COMMAND_PARAMETER_DESC) -> HRESULT, + CreateMetaCommand: proc "stdcall" (this: ^IDevice5, CommandId: ^GUID, NodeMask: u32, pCreationParametersData: rawptr, CreationParametersDataSizeInBytes: SIZE_T, riid: ^IID, ppMetaCommand: ^rawptr) -> HRESULT, + CreateStateObject: proc "stdcall" (this: ^IDevice5, pDesc: ^STATE_OBJECT_DESC, riid: ^IID, ppStateObject: ^rawptr) -> HRESULT, + GetRaytracingAccelerationStructurePrebuildInfo: proc "stdcall" (this: ^IDevice5, pDesc: ^BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS, pInfo: ^RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO), + CheckDriverMatchingIdentifier: proc "stdcall" (this: ^IDevice5, SerializedDataType: SERIALIZED_DATA_TYPE, pIdentifierToCheck: ^SERIALIZED_DATA_DRIVER_MATCHING_IDENTIFIER) -> DRIVER_MATCHING_IDENTIFIER_STATUS, +} + +AUTO_BREADCRUMB_OP :: enum i32 { + SETMARKER = 0, + BEGINEVENT = 1, + ENDEVENT = 2, + DRAWINSTANCED = 3, + DRAWINDEXEDINSTANCED = 4, + EXECUTEINDIRECT = 5, + DISPATCH = 6, + COPYBUFFERREGION = 7, + COPYTEXTUREREGION = 8, + COPYRESOURCE = 9, + COPYTILES = 10, + RESOLVESUBRESOURCE = 11, + CLEARRENDERTARGETVIEW = 12, + CLEARUNORDEREDACCESSVIEW = 13, + CLEARDEPTHSTENCILVIEW = 14, + RESOURCEBARRIER = 15, + EXECUTEBUNDLE = 16, + PRESENT = 17, + RESOLVEQUERYDATA = 18, + BEGINSUBMISSION = 19, + ENDSUBMISSION = 20, + DECODEFRAME = 21, + PROCESSFRAMES = 22, + ATOMICCOPYBUFFERUINT = 23, + ATOMICCOPYBUFFERUINT64 = 24, + RESOLVESUBRESOURCEREGION = 25, + WRITEBUFFERIMMEDIATE = 26, + DECODEFRAME1 = 27, + SETPROTECTEDRESOURCESESSION = 28, + DECODEFRAME2 = 29, + PROCESSFRAMES1 = 30, + BUILDRAYTRACINGACCELERATIONSTRUCTURE = 31, + EMITRAYTRACINGACCELERATIONSTRUCTUREPOSTBUILDINFO = 32, + COPYRAYTRACINGACCELERATIONSTRUCTURE = 33, + DISPATCHRAYS = 34, + INITIALIZEMETACOMMAND = 35, + EXECUTEMETACOMMAND = 36, + ESTIMATEMOTION = 37, + RESOLVEMOTIONVECTORHEAP = 38, + SETPIPELINESTATE1 = 39, + INITIALIZEEXTENSIONCOMMAND = 40, + EXECUTEEXTENSIONCOMMAND = 41, + DISPATCHMESH = 42, +} + +AUTO_BREADCRUMB_NODE :: struct { + pCommandListDebugNameA: cstring, + pCommandListDebugNameW: [^]u16, + pCommandQueueDebugNameA: cstring, + pCommandQueueDebugNameW: [^]u16, + pCommandList: ^IGraphicsCommandList, + pCommandQueue: ^ICommandQueue, + BreadcrumbCount: u32, + pLastBreadcrumbValue: ^u32, + pCommandHistory: ^AUTO_BREADCRUMB_OP, + pNext: ^AUTO_BREADCRUMB_NODE, +} + +DRED_BREADCRUMB_CONTEXT :: struct { + BreadcrumbIndex: u32, + pContextString: [^]u16, +} + +AUTO_BREADCRUMB_NODE1 :: struct { + pCommandListDebugNameA: cstring, + pCommandListDebugNameW: [^]u16, + pCommandQueueDebugNameA: cstring, + pCommandQueueDebugNameW: [^]u16, + pCommandList: ^IGraphicsCommandList, + pCommandQueue: ^ICommandQueue, + BreadcrumbCount: u32, + pLastBreadcrumbValue: ^u32, + pCommandHistory: ^AUTO_BREADCRUMB_OP, + pNext: ^AUTO_BREADCRUMB_NODE1, + BreadcrumbContextsCount: u32, + pBreadcrumbContexts: ^DRED_BREADCRUMB_CONTEXT, +} + +DRED_VERSION :: enum i32 { + _1_0 = 1, + _1_1 = 2, + _1_2 = 3, +} + +DRED_FLAGS :: enum u32 { // TODO: make bit_set + NONE = 0x0, + FORCE_ENABLE = 0x1, + DISABLE_AUTOBREADCRUMBS = 0x2, +} + +DRED_ENABLEMENT :: enum i32 { + SYSTEM_CONTROLLED = 0, + FORCED_OFF = 1, + FORCED_ON = 2, +} + +DEVICE_REMOVED_EXTENDED_DATA :: struct { + Flags: DRED_FLAGS, + pHeadAutoBreadcrumbNode: ^AUTO_BREADCRUMB_NODE, +} + +DRED_ALLOCATION_TYPE :: enum i32 { + COMMAND_QUEUE = 19, + COMMAND_ALLOCATOR = 20, + PIPELINE_STATE = 21, + COMMAND_LIST = 22, + FENCE = 23, + DESCRIPTOR_HEAP = 24, + HEAP = 25, + QUERY_HEAP = 27, + COMMAND_SIGNATURE = 28, + PIPELINE_LIBRARY = 29, + VIDEO_DECODER = 30, + VIDEO_PROCESSOR = 32, + RESOURCE = 34, + PASS = 35, + CRYPTOSESSION = 36, + CRYPTOSESSIONPOLICY = 37, + PROTECTEDRESOURCESESSION = 38, + VIDEO_DECODER_HEAP = 39, + COMMAND_POOL = 40, + COMMAND_RECORDER = 41, + STATE_OBJECT = 42, + METACOMMAND = 43, + SCHEDULINGGROUP = 44, + VIDEO_MOTION_ESTIMATOR = 45, + VIDEO_MOTION_VECTOR_HEAP = 46, + VIDEO_EXTENSION_COMMAND = 47, + INVALID = -1, +} + +DRED_ALLOCATION_NODE :: struct { + ObjectNameA: cstring, + ObjectNameW: ^i16, + AllocationType: DRED_ALLOCATION_TYPE, + pNext: ^DRED_ALLOCATION_NODE, +} + +DRED_ALLOCATION_NODE1 :: struct { + ObjectNameA: cstring, + ObjectNameW: ^i16, + AllocationType: DRED_ALLOCATION_TYPE, + pNext: ^DRED_ALLOCATION_NODE1, + pObject: ^IUnknown, +} + +DRED_AUTO_BREADCRUMBS_OUTPUT :: struct { + pHeadAutoBreadcrumbNode: ^AUTO_BREADCRUMB_NODE, +} + +DRED_AUTO_BREADCRUMBS_OUTPUT1 :: struct { + pHeadAutoBreadcrumbNode: ^AUTO_BREADCRUMB_NODE1, +} + +DRED_PAGE_FAULT_OUTPUT :: struct { + PageFaultVA: GPU_VIRTUAL_ADDRESS, + pHeadExistingAllocationNode: ^DRED_ALLOCATION_NODE, + pHeadRecentFreedAllocationNode: ^DRED_ALLOCATION_NODE, +} + +DRED_PAGE_FAULT_OUTPUT1 :: struct { + PageFaultVA: GPU_VIRTUAL_ADDRESS, + pHeadExistingAllocationNode: ^DRED_ALLOCATION_NODE1, + pHeadRecentFreedAllocationNode: ^DRED_ALLOCATION_NODE1, +} + +DEVICE_REMOVED_EXTENDED_DATA1 :: struct { + DeviceRemovedReason: HRESULT, + AutoBreadcrumbsOutput: DRED_AUTO_BREADCRUMBS_OUTPUT, + PageFaultOutput: DRED_PAGE_FAULT_OUTPUT, +} + +DEVICE_REMOVED_EXTENDED_DATA2 :: struct { + DeviceRemovedReason: HRESULT, + AutoBreadcrumbsOutput: DRED_AUTO_BREADCRUMBS_OUTPUT1, + PageFaultOutput: DRED_PAGE_FAULT_OUTPUT1, +} + +VERSIONED_DEVICE_REMOVED_EXTENDED_DATA :: struct { + Version: DRED_VERSION, + using _: struct #raw_union { + Dred_1_0: DEVICE_REMOVED_EXTENDED_DATA, + Dred_1_1: DEVICE_REMOVED_EXTENDED_DATA1, + Dred_1_2: DEVICE_REMOVED_EXTENDED_DATA2, + }, +} + + +IDeviceRemovedExtendedDataSettings_UUID_STRING :: "82BC481C-6B9B-4030-AEDB-7EE3D1DF1E63" +IDeviceRemovedExtendedDataSettings_UUID := &IID{0x82BC481C, 0x6B9B, 0x4030, {0xAE, 0xDB, 0x7E, 0xE3, 0xD1, 0xDF, 0x1E, 0x63}} +IDeviceRemovedExtendedDataSettings :: struct #raw_union { + #subtype iunknown: IUnknown, + using id3d12deviceremovedextendeddatasettings_vtable: ^IDeviceRemovedExtendedDataSettings_VTable, +} +IDeviceRemovedExtendedDataSettings_VTable :: struct { + using iunknown_vtable: IUnknown_VTable, + SetAutoBreadcrumbsEnablement: proc "stdcall" (this: ^IDeviceRemovedExtendedDataSettings, Enablement: DRED_ENABLEMENT), + SetPageFaultEnablement: proc "stdcall" (this: ^IDeviceRemovedExtendedDataSettings, Enablement: DRED_ENABLEMENT), + SetWatsonDumpEnablement: proc "stdcall" (this: ^IDeviceRemovedExtendedDataSettings, Enablement: DRED_ENABLEMENT), +} + + +IDeviceRemovedExtendedDataSettings1_UUID_STRING :: "DBD5AE51-3317-4F0A-ADF9-1D7CEDCAAE0B" +IDeviceRemovedExtendedDataSettings1_UUID := &IID{0xDBD5AE51, 0x3317, 0x4F0A, {0xAD, 0xF9, 0x1D, 0x7C, 0xED, 0xCA, 0xAE, 0x0B}} +IDeviceRemovedExtendedDataSettings1 :: struct #raw_union { + #subtype id3d12deviceremovedextendeddatasettings: IDeviceRemovedExtendedDataSettings, + using id3d12deviceremovedextendeddatasettings1_vtable: ^IDeviceRemovedExtendedDataSettings1_VTable, +} +IDeviceRemovedExtendedDataSettings1_VTable :: struct { + using id3d12deviceremovedextendeddatasettings_vtable: IDeviceRemovedExtendedDataSettings_VTable, + SetBreadcrumbContextEnablement: proc "stdcall" (this: ^IDeviceRemovedExtendedDataSettings1, Enablement: DRED_ENABLEMENT), +} + + +IDeviceRemovedExtendedData_UUID_STRING :: "98931D33-5AE8-4791-AA3C-1A73A2934E71" +IDeviceRemovedExtendedData_UUID := &IID{0x98931D33, 0x5AE8, 0x4791, {0xAA, 0x3C, 0x1A, 0x73, 0xA2, 0x93, 0x4E, 0x71}} +IDeviceRemovedExtendedData :: struct #raw_union { + #subtype iunknown: IUnknown, + using id3d12deviceremovedextendeddata_vtable: ^IDeviceRemovedExtendedData_VTable, +} +IDeviceRemovedExtendedData_VTable :: struct { + using iunknown_vtable: IUnknown_VTable, + GetAutoBreadcrumbsOutput: proc "stdcall" (this: ^IDeviceRemovedExtendedData, pOutput: ^DRED_AUTO_BREADCRUMBS_OUTPUT) -> HRESULT, + GetPageFaultAllocationOutput: proc "stdcall" (this: ^IDeviceRemovedExtendedData, pOutput: ^DRED_PAGE_FAULT_OUTPUT) -> HRESULT, +} + + +IDeviceRemovedExtendedData1_UUID_STRING :: "9727A022-CF1D-4DDA-9EBA-EFFA653FC506" +IDeviceRemovedExtendedData1_UUID := &IID{0x9727A022, 0xCF1D, 0x4DDA, {0x9E, 0xBA, 0xEF, 0xFA, 0x65, 0x3F, 0xC5, 0x06}} +IDeviceRemovedExtendedData1 :: struct #raw_union { + #subtype id3d12deviceremovedextendeddata: IDeviceRemovedExtendedData, + using id3d12deviceremovedextendeddata1_vtable: ^IDeviceRemovedExtendedData1_VTable, +} +IDeviceRemovedExtendedData1_VTable :: struct { + using id3d12deviceremovedextendeddata_vtable: IDeviceRemovedExtendedData_VTable, + GetAutoBreadcrumbsOutput1: proc "stdcall" (this: ^IDeviceRemovedExtendedData1, pOutput: ^DRED_AUTO_BREADCRUMBS_OUTPUT1) -> HRESULT, + GetPageFaultAllocationOutput1: proc "stdcall" (this: ^IDeviceRemovedExtendedData1, pOutput: ^DRED_PAGE_FAULT_OUTPUT1) -> HRESULT, +} + +BACKGROUND_PROCESSING_MODE :: enum i32 { + ALLOWED = 0, + ALLOW_INTRUSIVE_MEASUREMENTS = 1, + DISABLE_BACKGROUND_WORK = 2, + DISABLE_PROFILING_BY_SYSTEM = 3, +} + +MEASUREMENTS_ACTION :: enum i32 { + KEEP_ALL = 0, + COMMIT_RESULTS = 1, + COMMIT_RESULTS_HIGH_PRIORITY = 2, + DISCARD_PREVIOUS = 3, +} + + +IDevice6_UUID_STRING :: "c70b221b-40e4-4a17-89af-025a0727a6dc" +IDevice6_UUID := &IID{0xc70b221b, 0x40e4, 0x4a17, {0x89, 0xaf, 0x02, 0x5a, 0x07, 0x27, 0xa6, 0xdc}} +IDevice6 :: struct #raw_union { + #subtype id3d12device5: IDevice5, + using id3d12device6_vtable: ^IDevice6_VTable, +} +IDevice6_VTable :: struct { + using id3d12device5_vtable: IDevice5_VTable, + SetBackgroundProcessingMode: proc "stdcall" (this: ^IDevice6, Mode: BACKGROUND_PROCESSING_MODE, MeasurementsAction: MEASUREMENTS_ACTION, hEventToSignalUponCompletion: HANDLE, pbFurtherMeasurementsDesired: ^BOOL) -> HRESULT, +} + +FEATURE_DATA_PROTECTED_RESOURCE_SESSION_TYPE_COUNT :: struct { + NodeIndex: u32, + Count: u32, +} + +FEATURE_DATA_PROTECTED_RESOURCE_SESSION_TYPES :: struct { + NodeIndex: u32, + Count: u32, + pTypes: ^GUID, +} + +PROTECTED_RESOURCE_SESSION_DESC1 :: struct { + NodeMask: u32, + Flags: PROTECTED_RESOURCE_SESSION_FLAGS, + ProtectionType: GUID, +} + + +IProtectedResourceSession1_UUID_STRING :: "D6F12DD6-76FB-406E-8961-4296EEFC0409" +IProtectedResourceSession1_UUID := &IID{0xD6F12DD6, 0x76FB, 0x406E, {0x89, 0x61, 0x42, 0x96, 0xEE, 0xFC, 0x04, 0x09}} +IProtectedResourceSession1 :: struct #raw_union { + #subtype id3d12protectedresourcesession: IProtectedResourceSession, + using id3d12protectedresourcesession1_vtable: ^IProtectedResourceSession1_VTable, +} +IProtectedResourceSession1_VTable :: struct { + using id3d12protectedresourcesession_vtable: IProtectedResourceSession_VTable, + GetDesc1: proc "stdcall" (this: ^IProtectedResourceSession1) -> PROTECTED_RESOURCE_SESSION_DESC1, +} + + +IDevice7_UUID_STRING :: "5c014b53-68a1-4b9b-8bd1-dd6046b9358b" +IDevice7_UUID := &IID{0x5c014b53, 0x68a1, 0x4b9b, {0x8b, 0xd1, 0xdd, 0x60, 0x46, 0xb9, 0x35, 0x8b}} +IDevice7 :: struct #raw_union { + #subtype id3d12device6: IDevice6, + using id3d12device7_vtable: ^IDevice7_VTable, +} +IDevice7_VTable :: struct { + using id3d12device6_vtable: IDevice6_VTable, + AddToStateObject: proc "stdcall" (this: ^IDevice7, pAddition: ^STATE_OBJECT_DESC, pStateObjectToGrowFrom: ^IStateObject, riid: ^IID, ppNewStateObject: ^rawptr) -> HRESULT, + CreateProtectedResourceSession1: proc "stdcall" (this: ^IDevice7, pDesc: ^PROTECTED_RESOURCE_SESSION_DESC1, riid: ^IID, ppSession: ^rawptr) -> HRESULT, +} + + +IDevice8_UUID_STRING :: "9218E6BB-F944-4F7E-A75C-B1B2C7B701F3" +IDevice8_UUID := &IID{0x9218E6BB, 0xF944, 0x4F7E, {0xA7, 0x5C, 0xB1, 0xB2, 0xC7, 0xB7, 0x01, 0xF3}} +IDevice8 :: struct #raw_union { + #subtype id3d12device7: IDevice7, + using id3d12device8_vtable: ^IDevice8_VTable, +} +IDevice8_VTable :: struct { + using id3d12device7_vtable: IDevice7_VTable, + GetResourceAllocationInfo2: proc "stdcall" (this: ^IDevice8, visibleMask: u32, numResourceDescs: u32, pResourceDescs: ^RESOURCE_DESC1, pResourceAllocationInfo1: ^RESOURCE_ALLOCATION_INFO1) -> RESOURCE_ALLOCATION_INFO, + CreateCommittedResource2: proc "stdcall" (this: ^IDevice8, pHeapProperties: ^HEAP_PROPERTIES, HeapFlags: HEAP_FLAGS, pDesc: ^RESOURCE_DESC1, InitialResourceState: RESOURCE_STATES, pOptimizedClearValue: ^CLEAR_VALUE, pProtectedSession: ^IProtectedResourceSession, riidResource: ^IID, ppvResource: ^rawptr) -> HRESULT, + CreatePlacedResource1: proc "stdcall" (this: ^IDevice8, pHeap: ^IHeap, HeapOffset: u64, pDesc: ^RESOURCE_DESC1, InitialState: RESOURCE_STATES, pOptimizedClearValue: ^CLEAR_VALUE, riid: ^IID, ppvResource: ^rawptr) -> HRESULT, + CreateSamplerFeedbackUnorderedAccessView: proc "stdcall" (this: ^IDevice8, pTargetedResource: ^IResource, pFeedbackResource: ^IResource, DestDescriptor: CPU_DESCRIPTOR_HANDLE), + GetCopyableFootprints1: proc "stdcall" (this: ^IDevice8, pResourceDesc: ^RESOURCE_DESC1, FirstSubresource: u32, NumSubresources: u32, BaseOffset: u64, pLayouts: ^PLACED_SUBRESOURCE_FOOTPRINT, pNumRows: ^u32, pRowSizeInBytes: ^u64, pTotalBytes: ^u64), +} + + +IResource1_UUID_STRING :: "9D5E227A-4430-4161-88B3-3ECA6BB16E19" +IResource1_UUID := &IID{0x9D5E227A, 0x4430, 0x4161, {0x88, 0xB3, 0x3E, 0xCA, 0x6B, 0xB1, 0x6E, 0x19}} +IResource1 :: struct #raw_union { + #subtype id3d12resource: IResource, + using id3d12resource1_vtable: ^IResource1_VTable, +} +IResource1_VTable :: struct { + using id3d12resource_vtable: IResource_VTable, + GetProtectedResourceSession: proc "stdcall" (this: ^IResource1, riid: ^IID, ppProtectedSession: ^rawptr) -> HRESULT, +} + + +IResource2_UUID_STRING :: "BE36EC3B-EA85-4AEB-A45A-E9D76404A495" +IResource2_UUID := &IID{0xBE36EC3B, 0xEA85, 0x4AEB, {0xA4, 0x5A, 0xE9, 0xD7, 0x64, 0x04, 0xA4, 0x95}} +IResource2 :: struct #raw_union { + #subtype id3d12resource1: IResource1, + using id3d12resource2_vtable: ^IResource2_VTable, +} +IResource2_VTable :: struct { + using id3d12resource1_vtable: IResource1_VTable, + GetDesc1: proc "stdcall" (this: ^IResource2) -> RESOURCE_DESC1, +} + + +IHeap1_UUID_STRING :: "572F7389-2168-49E3-9693-D6DF5871BF6D" +IHeap1_UUID := &IID{0x572F7389, 0x2168, 0x49E3, {0x96, 0x93, 0xD6, 0xDF, 0x58, 0x71, 0xBF, 0x6D}} +IHeap1 :: struct #raw_union { + #subtype id3d12heap: IHeap, + using id3d12heap1_vtable: ^IHeap1_VTable, +} +IHeap1_VTable :: struct { + using id3d12heap_vtable: IHeap_VTable, + GetProtectedResourceSession: proc "stdcall" (this: ^IHeap1, riid: ^IID, ppProtectedSession: ^rawptr) -> HRESULT, +} + + +IGraphicsCommandList3_UUID_STRING :: "6FDA83A7-B84C-4E38-9AC8-C7BD22016B3D" +IGraphicsCommandList3_UUID := &IID{0x6FDA83A7, 0xB84C, 0x4E38, {0x9A, 0xC8, 0xC7, 0xBD, 0x22, 0x01, 0x6B, 0x3D}} +IGraphicsCommandList3 :: struct #raw_union { + #subtype id3d12graphicscommandlist2: IGraphicsCommandList2, + using id3d12graphicscommandlist3_vtable: ^IGraphicsCommandList3_VTable, +} +IGraphicsCommandList3_VTable :: struct { + using id3d12graphicscommandlist2_vtable: IGraphicsCommandList2_VTable, + SetProtectedResourceSession: proc "stdcall" (this: ^IGraphicsCommandList3, pProtectedResourceSession: ^IProtectedResourceSession), +} + +RENDER_PASS_BEGINNING_ACCESS_TYPE :: enum i32 { + DISCARD = 0, + PRESERVE = 1, + CLEAR = 2, + NO_ACCESS = 3, +} + +RENDER_PASS_BEGINNING_ACCESS_CLEAR_PARAMETERS :: struct { + ClearValue: CLEAR_VALUE, +} + +RENDER_PASS_BEGINNING_ACCESS :: struct { + Type: RENDER_PASS_BEGINNING_ACCESS_TYPE, + using _: struct #raw_union { + Clear: RENDER_PASS_BEGINNING_ACCESS_CLEAR_PARAMETERS, + }, +} + +RENDER_PASS_ENDING_ACCESS_TYPE :: enum i32 { + DISCARD = 0, + PRESERVE = 1, + RESOLVE = 2, + NO_ACCESS = 3, +} + +RENDER_PASS_ENDING_ACCESS_RESOLVE_SUBRESOURCE_PARAMETERS :: struct { + SrcSubresource: u32, + DstSubresource: u32, + DstX: u32, + DstY: u32, + SrcRect: RECT, +} + +RENDER_PASS_ENDING_ACCESS_RESOLVE_PARAMETERS :: struct { + pSrcResource: ^IResource, + pDstResource: ^IResource, + SubresourceCount: u32, + pSubresourceParameters: ^RENDER_PASS_ENDING_ACCESS_RESOLVE_SUBRESOURCE_PARAMETERS, + Format: dxgi.FORMAT, + ResolveMode: RESOLVE_MODE, + PreserveResolveSource: BOOL, +} + +RENDER_PASS_ENDING_ACCESS :: struct { + Type: RENDER_PASS_ENDING_ACCESS_TYPE, + using _: struct #raw_union { + Resolve: RENDER_PASS_ENDING_ACCESS_RESOLVE_PARAMETERS, + }, +} + +RENDER_PASS_RENDER_TARGET_DESC :: struct { + cpuDescriptor: CPU_DESCRIPTOR_HANDLE, + BeginningAccess: RENDER_PASS_BEGINNING_ACCESS, + EndingAccess: RENDER_PASS_ENDING_ACCESS, +} + +RENDER_PASS_DEPTH_STENCIL_DESC :: struct { + cpuDescriptor: CPU_DESCRIPTOR_HANDLE, + DepthBeginningAccess: RENDER_PASS_BEGINNING_ACCESS, + StencilBeginningAccess: RENDER_PASS_BEGINNING_ACCESS, + DepthEndingAccess: RENDER_PASS_ENDING_ACCESS, + StencilEndingAccess: RENDER_PASS_ENDING_ACCESS, +} + +RENDER_PASS_FLAGS :: enum u32 { // TODO: make bit_set + NONE = 0x0, + ALLOW_UAV_WRITES = 0x1, + SUSPENDING_PASS = 0x2, + RESUMING_PASS = 0x4, +} + + +IMetaCommand_UUID_STRING :: "DBB84C27-36CE-4FC9-B801-F048C46AC570" +IMetaCommand_UUID := &IID{0xDBB84C27, 0x36CE, 0x4FC9, {0xB8, 0x01, 0xF0, 0x48, 0xC4, 0x6A, 0xC5, 0x70}} +IMetaCommand :: struct #raw_union { + #subtype id3d12pageable: IPageable, + using id3d12metacommand_vtable: ^IMetaCommand_VTable, +} +IMetaCommand_VTable :: struct { + using id3d12devicechild_vtable: IDeviceChild_VTable, + GetRequiredParameterResourceSize: proc "stdcall" (this: ^IMetaCommand, Stage: META_COMMAND_PARAMETER_STAGE, ParameterIndex: u32) -> u64, +} + +DISPATCH_RAYS_DESC :: struct { + RayGenerationShaderRecord: GPU_VIRTUAL_ADDRESS_RANGE, + MissShaderTable: GPU_VIRTUAL_ADDRESS_RANGE_AND_STRIDE, + HitGroupTable: GPU_VIRTUAL_ADDRESS_RANGE_AND_STRIDE, + CallableShaderTable: GPU_VIRTUAL_ADDRESS_RANGE_AND_STRIDE, + Width: u32, + Height: u32, + Depth: u32, +} + + +IGraphicsCommandList4_UUID_STRING :: "8754318e-d3a9-4541-98cf-645b50dc4874" +IGraphicsCommandList4_UUID := &IID{0x8754318e, 0xd3a9, 0x4541, {0x98, 0xcf, 0x64, 0x5b, 0x50, 0xdc, 0x48, 0x74}} +IGraphicsCommandList4 :: struct #raw_union { + #subtype id3d12graphicscommandlist3: IGraphicsCommandList3, + using id3d12graphicscommandlist4_vtable: ^IGraphicsCommandList4_VTable, +} +IGraphicsCommandList4_VTable :: struct { + using id3d12graphicscommandlist3_vtable: IGraphicsCommandList3_VTable, + BeginRenderPass: proc "stdcall" (this: ^IGraphicsCommandList4, NumRenderTargets: u32, pRenderTargets: ^RENDER_PASS_RENDER_TARGET_DESC, pDepthStencil: ^RENDER_PASS_DEPTH_STENCIL_DESC, Flags: RENDER_PASS_FLAGS), + EndRenderPass: proc "stdcall" (this: ^IGraphicsCommandList4), + InitializeMetaCommand: proc "stdcall" (this: ^IGraphicsCommandList4, pMetaCommand: ^IMetaCommand, pInitializationParametersData: rawptr, InitializationParametersDataSizeInBytes: SIZE_T), + ExecuteMetaCommand: proc "stdcall" (this: ^IGraphicsCommandList4, pMetaCommand: ^IMetaCommand, pExecutionParametersData: rawptr, ExecutionParametersDataSizeInBytes: SIZE_T), + BuildRaytracingAccelerationStructure: proc "stdcall" (this: ^IGraphicsCommandList4, pDesc: ^BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC, NumPostbuildInfoDescs: u32, pPostbuildInfoDescs: ^RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO_DESC), + EmitRaytracingAccelerationStructurePostbuildInfo: proc "stdcall" (this: ^IGraphicsCommandList4, pDesc: ^RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO_DESC, NumSourceAccelerationStructures: u32, pSourceAccelerationStructureData: ^GPU_VIRTUAL_ADDRESS), + CopyRaytracingAccelerationStructure: proc "stdcall" (this: ^IGraphicsCommandList4, DestAccelerationStructureData: GPU_VIRTUAL_ADDRESS, SourceAccelerationStructureData: GPU_VIRTUAL_ADDRESS, Mode: RAYTRACING_ACCELERATION_STRUCTURE_COPY_MODE), + SetPipelineState1: proc "stdcall" (this: ^IGraphicsCommandList4, pStateObject: ^IStateObject), + DispatchRays: proc "stdcall" (this: ^IGraphicsCommandList4, pDesc: ^DISPATCH_RAYS_DESC), +} + + +ITools_UUID_STRING :: "7071e1f0-e84b-4b33-974f-12fa49de65c5" +ITools_UUID := &IID{0x7071e1f0, 0xe84b, 0x4b33, {0x97, 0x4f, 0x12, 0xfa, 0x49, 0xde, 0x65, 0xc5}} +ITools :: struct #raw_union { + #subtype iunknown: IUnknown, + using id3d12tools_vtable: ^ITools_VTable, +} +ITools_VTable :: struct { + using iunknown_vtable: IUnknown_VTable, + EnableShaderInstrumentation: proc "stdcall" (this: ^ITools, bEnable: BOOL), + ShaderInstrumentationEnabled: proc "stdcall" (this: ^ITools) -> BOOL, +} + +SUBRESOURCE_DATA :: struct { + pData: rawptr, + RowPitch: i64, + SlicePitch: i64, +} + +MEMCPY_DEST :: struct { + pData: rawptr, + RowPitch: SIZE_T, + SlicePitch: SIZE_T, +} + + +IDebug_UUID_STRING :: "344488b7-6846-474b-b989-f027448245e0" +IDebug_UUID := &IID{0x344488b7, 0x6846, 0x474b, {0xb9, 0x89, 0xf0, 0x27, 0x44, 0x82, 0x45, 0xe0}} +IDebug :: struct #raw_union { + #subtype iunknown: IUnknown, + using id3d12debug_vtable: ^IDebug_VTable, +} +IDebug_VTable :: struct { + using iunknown_vtable: IUnknown_VTable, + EnableDebugLayer: proc "stdcall" (this: ^IDebug), +} + +GPU_BASED_VALIDATION_FLAGS :: enum u32 { // TODO: make bit_set + NONE = 0x0, + DISABLE_STATE_TRACKING = 0x1, +} + + +IDebug1_UUID_STRING :: "affaa4ca-63fe-4d8e-b8ad-159000af4304" +IDebug1_UUID := &IID{0xaffaa4ca, 0x63fe, 0x4d8e, {0xb8, 0xad, 0x15, 0x90, 0x00, 0xaf, 0x43, 0x04}} +IDebug1 :: struct #raw_union { + #subtype iunknown: IUnknown, + using id3d12debug1_vtable: ^IDebug1_VTable, +} +IDebug1_VTable :: struct { + using iunknown_vtable: IUnknown_VTable, + EnableDebugLayer: proc "stdcall" (this: ^IDebug1), + SetEnableGPUBasedValidation: proc "stdcall" (this: ^IDebug1, Enable: BOOL), + SetEnableSynchronizedCommandQueueValidation: proc "stdcall" (this: ^IDebug1, Enable: BOOL), +} + + +IDebug2_UUID :: "93a665c4-a3b2-4e5d-b692-a26ae14e3374" +IDebug2 :: struct #raw_union { + #subtype iunknown: IUnknown, + using id3d12debug2_vtable: ^IDebug2_VTable, +} +IDebug2_VTable :: struct { + using iunknown_vtable: IUnknown_VTable, + SetGPUBasedValidationFlags: proc "stdcall" (this: ^IDebug2, Flags: GPU_BASED_VALIDATION_FLAGS), +} + + +IDebug3_UUID_STRING :: "5cf4e58f-f671-4ff1-a542-3686e3d153d1" +IDebug3_UUID := &IID{0x5cf4e58f, 0xf671, 0x4ff1, {0xa5, 0x42, 0x36, 0x86, 0xe3, 0xd1, 0x53, 0xd1}} +IDebug3 :: struct #raw_union { + #subtype id3d12debug: IDebug, + using id3d12debug3_vtable: ^IDebug3_VTable, +} +IDebug3_VTable :: struct { + using id3d12debug_vtable: IDebug_VTable, + SetEnableGPUBasedValidation: proc "stdcall" (this: ^IDebug3, Enable: BOOL), + SetEnableSynchronizedCommandQueueValidation: proc "stdcall" (this: ^IDebug3, Enable: BOOL), + SetGPUBasedValidationFlags: proc "stdcall" (this: ^IDebug3, Flags: GPU_BASED_VALIDATION_FLAGS), +} + +RLDO_FLAGS :: enum u32 { // TODO: make bit_set + NONE = 0x0, + SUMMARY = 0x1, + DETAIL = 0x2, + IGNORE_INTERNAL = 0x4, +} + +DEBUG_DEVICE_PARAMETER_TYPE :: enum i32 { + FEATURE_FLAGS = 0, + GPU_BASED_VALIDATION_SETTINGS = 1, + GPU_SLOWDOWN_PERFORMANCE_FACTOR = 2, +} + +DEBUG_FEATURE :: enum i32 { // TODO: make bit_set + NONE = 0, + ALLOW_BEHAVIOR_CHANGING_DEBUG_AIDS = 1, + CONSERVATIVE_RESOURCE_STATE_TRACKING = 2, + DISABLE_VIRTUALIZED_BUNDLES_VALIDATION = 4, + EMULATE_WINDOWS7 = 8, +} + +GPU_BASED_VALIDATION_SHADER_PATCH_MODE :: enum i32 { + NONE = 0, + STATE_TRACKING_ONLY = 1, + UNGUARDED_VALIDATION = 2, + GUARDED_VALIDATION = 3, + NUM_GPU_BASED_VALIDATION_SHADER_PATCH_MODES = 4, +} + +GPU_BASED_VALIDATION_PIPELINE_STATE_CREATE_FLAGS :: enum u32 { // TODO: make bit_set + GPU_BASED_VALIDATION_PIPELINE_STATE_CREATE_FLAG_NONE = 0x0, + GPU_BASED_VALIDATION_PIPELINE_STATE_CREATE_FLAG_FRONT_LOAD_CREATE_TRACKING_ONLY_SHADERS = 0x1, + GPU_BASED_VALIDATION_PIPELINE_STATE_CREATE_FLAG_FRONT_LOAD_CREATE_UNGUARDED_VALIDATION_SHADERS = 0x2, + GPU_BASED_VALIDATION_PIPELINE_STATE_CREATE_FLAG_FRONT_LOAD_CREATE_GUARDED_VALIDATION_SHADERS = 0x4, + VALID_MASK = 0x7, +} + +DEBUG_DEVICE_GPU_BASED_VALIDATION_SETTINGS :: struct { + MaxMessagesPerCommandList: u32, + DefaultShaderPatchMode: GPU_BASED_VALIDATION_SHADER_PATCH_MODE, + PipelineStateCreateFlags: GPU_BASED_VALIDATION_PIPELINE_STATE_CREATE_FLAGS, +} + +DEBUG_DEVICE_GPU_SLOWDOWN_PERFORMANCE_FACTOR :: struct { + SlowdownFactor: f32, +} + + +IDebugDevice1_UUID_STRING :: "a9b71770-d099-4a65-a698-3dee10020f88" +IDebugDevice1_UUID := &IID{0xa9b71770, 0xd099, 0x4a65, {0xa6, 0x98, 0x3d, 0xee, 0x10, 0x02, 0x0f, 0x88}} +IDebugDevice1 :: struct #raw_union { + #subtype iunknown: IUnknown, + using id3d12debugdevice1_vtable: ^IDebugDevice1_VTable, +} +IDebugDevice1_VTable :: struct { + using iunknown_vtable: IUnknown_VTable, + SetDebugParameter: proc "stdcall" (this: ^IDebugDevice1, Type: DEBUG_DEVICE_PARAMETER_TYPE, pData: rawptr, DataSize: u32) -> HRESULT, + GetDebugParameter: proc "stdcall" (this: ^IDebugDevice1, Type: DEBUG_DEVICE_PARAMETER_TYPE, pData: rawptr, DataSize: u32) -> HRESULT, + ReportLiveDeviceObjects: proc "stdcall" (this: ^IDebugDevice1, Flags: RLDO_FLAGS) -> HRESULT, +} + + +IDebugDevice_UUID_STRING :: "3febd6dd-4973-4787-8194-e45f9e28923e" +IDebugDevice_UUID := &IID{0x3febd6dd, 0x4973, 0x4787, {0x81, 0x94, 0xe4, 0x5f, 0x9e, 0x28, 0x92, 0x3e}} +IDebugDevice :: struct #raw_union { + #subtype iunknown: IUnknown, + using id3d12debugdevice_vtable: ^IDebugDevice_VTable, +} +IDebugDevice_VTable :: struct { + using iunknown_vtable: IUnknown_VTable, + SetFeatureMask: proc "stdcall" (this: ^IDebugDevice, Mask: DEBUG_FEATURE) -> HRESULT, + GetFeatureMask: proc "stdcall" (this: ^IDebugDevice) -> DEBUG_FEATURE, + ReportLiveDeviceObjects: proc "stdcall" (this: ^IDebugDevice, Flags: RLDO_FLAGS) -> HRESULT, +} + + +IDebugDevice2_UUID_STRING :: "60eccbc1-378d-4df1-894c-f8ac5ce4d7dd" +IDebugDevice2_UUID := &IID{0x60eccbc1, 0x378d, 0x4df1, {0x89, 0x4c, 0xf8, 0xac, 0x5c, 0xe4, 0xd7, 0xdd}} +IDebugDevice2 :: struct #raw_union { + #subtype id3d12debugdevice: IDebugDevice, + using id3d12debugdevice2_vtable: ^IDebugDevice2_VTable, +} +IDebugDevice2_VTable :: struct { + using id3d12debugdevice_vtable: IDebugDevice_VTable, + SetDebugParameter: proc "stdcall" (this: ^IDebugDevice2, Type: DEBUG_DEVICE_PARAMETER_TYPE, pData: rawptr, DataSize: u32) -> HRESULT, + GetDebugParameter: proc "stdcall" (this: ^IDebugDevice2, Type: DEBUG_DEVICE_PARAMETER_TYPE, pData: rawptr, DataSize: u32) -> HRESULT, +} + + +IDebugCommandQueue_UUID_STRING :: "09e0bf36-54ac-484f-8847-4baeeab6053a" +IDebugCommandQueue_UUID := &IID{0x09e0bf36, 0x54ac, 0x484f, {0x88, 0x47, 0x4b, 0xae, 0xea, 0xb6, 0x05, 0x3a}} +IDebugCommandQueue :: struct #raw_union { + #subtype iunknown: IUnknown, + using id3d12debugcommandqueue_vtable: ^IDebugCommandQueue_VTable, +} +IDebugCommandQueue_VTable :: struct { + using iunknown_vtable: IUnknown_VTable, + AssertResourceState: proc "stdcall" (this: ^IDebugCommandQueue, pResource: ^IResource, Subresource: u32, State: u32) -> BOOL, +} + +DEBUG_COMMAND_LIST_PARAMETER_TYPE :: enum i32 { + DEBUG_COMMAND_LIST_PARAMETER_GPU_BASED_VALIDATION_SETTINGS = 0, +} + +DEBUG_COMMAND_LIST_GPU_BASED_VALIDATION_SETTINGS :: struct { + ShaderPatchMode: GPU_BASED_VALIDATION_SHADER_PATCH_MODE, +} + + +IDebugCommandList1_UUID_STRING :: "102ca951-311b-4b01-b11f-ecb83e061b37" +IDebugCommandList1_UUID := &IID{0x102ca951, 0x311b, 0x4b01, {0xb1, 0x1f, 0xec, 0xb8, 0x3e, 0x06, 0x1b, 0x37}} +IDebugCommandList1 :: struct #raw_union { + #subtype iunknown: IUnknown, + using id3d12debugcommandlist1_vtable: ^IDebugCommandList1_VTable, +} +IDebugCommandList1_VTable :: struct { + using iunknown_vtable: IUnknown_VTable, + AssertResourceState: proc "stdcall" (this: ^IDebugCommandList1, pResource: ^IResource, Subresource: u32, State: u32) -> BOOL, + SetDebugParameter: proc "stdcall" (this: ^IDebugCommandList1, Type: DEBUG_COMMAND_LIST_PARAMETER_TYPE, pData: rawptr, DataSize: u32) -> HRESULT, + GetDebugParameter: proc "stdcall" (this: ^IDebugCommandList1, Type: DEBUG_COMMAND_LIST_PARAMETER_TYPE, pData: rawptr, DataSize: u32) -> HRESULT, +} + + +IDebugCommandList_UUID_STRING :: "09e0bf36-54ac-484f-8847-4baeeab6053f" +IDebugCommandList_UUID := &IID{0x09e0bf36, 0x54ac, 0x484f, {0x88, 0x47, 0x4b, 0xae, 0xea, 0xb6, 0x05, 0x3f}} +IDebugCommandList :: struct #raw_union { + #subtype iunknown: IUnknown, + using id3d12debugcommandlist_vtable: ^IDebugCommandList_VTable, +} +IDebugCommandList_VTable :: struct { + using iunknown_vtable: IUnknown_VTable, + AssertResourceState: proc "stdcall" (this: ^IDebugCommandList, pResource: ^IResource, Subresource: u32, State: u32) -> BOOL, + SetFeatureMask: proc "stdcall" (this: ^IDebugCommandList, Mask: DEBUG_FEATURE) -> HRESULT, + GetFeatureMask: proc "stdcall" (this: ^IDebugCommandList) -> DEBUG_FEATURE, +} + + +IDebugCommandList2_UUID_STRING :: "aeb575cf-4e06-48be-ba3b-c450fc96652e" +IDebugCommandList2_UUID := &IID{0xaeb575cf, 0x4e06, 0x48be, {0xba, 0x3b, 0xc4, 0x50, 0xfc, 0x96, 0x65, 0x2e}} +IDebugCommandList2 :: struct #raw_union { + #subtype id3d12debugcommandlist: IDebugCommandList, + using id3d12debugcommandlist2_vtable: ^IDebugCommandList2_VTable, +} +IDebugCommandList2_VTable :: struct { + using id3d12debugcommandlist_vtable: IDebugCommandList_VTable, + SetDebugParameter: proc "stdcall" (this: ^IDebugCommandList2, Type: DEBUG_COMMAND_LIST_PARAMETER_TYPE, pData: rawptr, DataSize: u32) -> HRESULT, + GetDebugParameter: proc "stdcall" (this: ^IDebugCommandList2, Type: DEBUG_COMMAND_LIST_PARAMETER_TYPE, pData: rawptr, DataSize: u32) -> HRESULT, +} + + +ISharingContract_UUID_STRING :: "0adf7d52-929c-4e61-addb-ffed30de66ef" +ISharingContract_UUID := &IID{0x0adf7d52, 0x929c, 0x4e61, {0xad, 0xdb, 0xff, 0xed, 0x30, 0xde, 0x66, 0xef}} +ISharingContract :: struct #raw_union { + #subtype iunknown: IUnknown, + using id3d12sharingcontract_vtable: ^ISharingContract_VTable, +} +ISharingContract_VTable :: struct { + using iunknown_vtable: IUnknown_VTable, + Present: proc "stdcall" (this: ^ISharingContract, pResource: ^IResource, Subresource: u32, window: HWND), + SharedFenceSignal: proc "stdcall" (this: ^ISharingContract, pFence: ^IFence, FenceValue: u64), + BeginCapturableWork: proc "stdcall" (this: ^ISharingContract, guid: ^GUID), + EndCapturableWork: proc "stdcall" (this: ^ISharingContract, guid: ^GUID), +} + +MESSAGE_CATEGORY :: enum i32 { + APPLICATION_DEFINED = 0, + MISCELLANEOUS = 1, + INITIALIZATION = 2, + CLEANUP = 3, + COMPILATION = 4, + STATE_CREATION = 5, + STATE_SETTING = 6, + STATE_GETTING = 7, + RESOURCE_MANIPULATION = 8, + EXECUTION = 9, + SHADER = 10, +} + +MESSAGE_SEVERITY :: enum i32 { + CORRUPTION = 0, + ERROR = 1, + WARNING = 2, + INFO = 3, + MESSAGE = 4, +} + +MESSAGE_ID :: enum i32 { + UNKNOWN = 0, + STRING_FROM_APPLICATION = 1, + CORRUPTED_THIS = 2, + CORRUPTED_PARAMETER1 = 3, + CORRUPTED_PARAMETER2 = 4, + CORRUPTED_PARAMETER3 = 5, + CORRUPTED_PARAMETER4 = 6, + CORRUPTED_PARAMETER5 = 7, + CORRUPTED_PARAMETER6 = 8, + CORRUPTED_PARAMETER7 = 9, + CORRUPTED_PARAMETER8 = 10, + CORRUPTED_PARAMETER9 = 11, + CORRUPTED_PARAMETER10 = 12, + CORRUPTED_PARAMETER11 = 13, + CORRUPTED_PARAMETER12 = 14, + CORRUPTED_PARAMETER13 = 15, + CORRUPTED_PARAMETER14 = 16, + CORRUPTED_PARAMETER15 = 17, + CORRUPTED_MULTITHREADING = 18, + MESSAGE_REPORTING_OUTOFMEMORY = 19, + GETPRIVATEDATA_MOREDATA = 20, + SETPRIVATEDATA_INVALIDFREEDATA = 21, + SETPRIVATEDATA_CHANGINGPARAMS = 24, + SETPRIVATEDATA_OUTOFMEMORY = 25, + CREATESHADERRESOURCEVIEW_UNRECOGNIZEDFORMAT = 26, + CREATESHADERRESOURCEVIEW_INVALIDDESC = 27, + CREATESHADERRESOURCEVIEW_INVALIDFORMAT = 28, + CREATESHADERRESOURCEVIEW_INVALIDVIDEOPLANESLICE = 29, + CREATESHADERRESOURCEVIEW_INVALIDPLANESLICE = 30, + CREATESHADERRESOURCEVIEW_INVALIDDIMENSIONS = 31, + CREATESHADERRESOURCEVIEW_INVALIDRESOURCE = 32, + CREATERENDERTARGETVIEW_UNRECOGNIZEDFORMAT = 35, + CREATERENDERTARGETVIEW_UNSUPPORTEDFORMAT = 36, + CREATERENDERTARGETVIEW_INVALIDDESC = 37, + CREATERENDERTARGETVIEW_INVALIDFORMAT = 38, + CREATERENDERTARGETVIEW_INVALIDVIDEOPLANESLICE = 39, + CREATERENDERTARGETVIEW_INVALIDPLANESLICE = 40, + CREATERENDERTARGETVIEW_INVALIDDIMENSIONS = 41, + CREATERENDERTARGETVIEW_INVALIDRESOURCE = 42, + CREATEDEPTHSTENCILVIEW_UNRECOGNIZEDFORMAT = 45, + CREATEDEPTHSTENCILVIEW_INVALIDDESC = 46, + CREATEDEPTHSTENCILVIEW_INVALIDFORMAT = 47, + CREATEDEPTHSTENCILVIEW_INVALIDDIMENSIONS = 48, + CREATEDEPTHSTENCILVIEW_INVALIDRESOURCE = 49, + CREATEINPUTLAYOUT_OUTOFMEMORY = 52, + CREATEINPUTLAYOUT_TOOMANYELEMENTS = 53, + CREATEINPUTLAYOUT_INVALIDFORMAT = 54, + CREATEINPUTLAYOUT_INCOMPATIBLEFORMAT = 55, + CREATEINPUTLAYOUT_INVALIDSLOT = 56, + CREATEINPUTLAYOUT_INVALIDINPUTSLOTCLASS = 57, + CREATEINPUTLAYOUT_STEPRATESLOTCLASSMISMATCH = 58, + CREATEINPUTLAYOUT_INVALIDSLOTCLASSCHANGE = 59, + CREATEINPUTLAYOUT_INVALIDSTEPRATECHANGE = 60, + CREATEINPUTLAYOUT_INVALIDALIGNMENT = 61, + CREATEINPUTLAYOUT_DUPLICATESEMANTIC = 62, + CREATEINPUTLAYOUT_UNPARSEABLEINPUTSIGNATURE = 63, + CREATEINPUTLAYOUT_NULLSEMANTIC = 64, + CREATEINPUTLAYOUT_MISSINGELEMENT = 65, + CREATEVERTEXSHADER_OUTOFMEMORY = 66, + CREATEVERTEXSHADER_INVALIDSHADERBYTECODE = 67, + CREATEVERTEXSHADER_INVALIDSHADERTYPE = 68, + CREATEGEOMETRYSHADER_OUTOFMEMORY = 69, + CREATEGEOMETRYSHADER_INVALIDSHADERBYTECODE = 70, + CREATEGEOMETRYSHADER_INVALIDSHADERTYPE = 71, + CREATEGEOMETRYSHADERWITHSTREAMOUTPUT_OUTOFMEMORY = 72, + CREATEGEOMETRYSHADERWITHSTREAMOUTPUT_INVALIDSHADERBYTECODE = 73, + CREATEGEOMETRYSHADERWITHSTREAMOUTPUT_INVALIDSHADERTYPE = 74, + CREATEGEOMETRYSHADERWITHSTREAMOUTPUT_INVALIDNUMENTRIES = 75, + CREATEGEOMETRYSHADERWITHSTREAMOUTPUT_OUTPUTSTREAMSTRIDEUNUSED = 76, + CREATEGEOMETRYSHADERWITHSTREAMOUTPUT_OUTPUTSLOT0EXPECTED = 79, + CREATEGEOMETRYSHADERWITHSTREAMOUTPUT_INVALIDOUTPUTSLOT = 80, + CREATEGEOMETRYSHADERWITHSTREAMOUTPUT_ONLYONEELEMENTPERSLOT = 81, + CREATEGEOMETRYSHADERWITHSTREAMOUTPUT_INVALIDCOMPONENTCOUNT = 82, + CREATEGEOMETRYSHADERWITHSTREAMOUTPUT_INVALIDSTARTCOMPONENTANDCOMPONENTCOUNT = 83, + CREATEGEOMETRYSHADERWITHSTREAMOUTPUT_INVALIDGAPDEFINITION = 84, + CREATEGEOMETRYSHADERWITHSTREAMOUTPUT_REPEATEDOUTPUT = 85, + CREATEGEOMETRYSHADERWITHSTREAMOUTPUT_INVALIDOUTPUTSTREAMSTRIDE = 86, + CREATEGEOMETRYSHADERWITHSTREAMOUTPUT_MISSINGSEMANTIC = 87, + CREATEGEOMETRYSHADERWITHSTREAMOUTPUT_MASKMISMATCH = 88, + CREATEGEOMETRYSHADERWITHSTREAMOUTPUT_CANTHAVEONLYGAPS = 89, + CREATEGEOMETRYSHADERWITHSTREAMOUTPUT_DECLTOOCOMPLEX = 90, + CREATEGEOMETRYSHADERWITHSTREAMOUTPUT_MISSINGOUTPUTSIGNATURE = 91, + CREATEPIXELSHADER_OUTOFMEMORY = 92, + CREATEPIXELSHADER_INVALIDSHADERBYTECODE = 93, + CREATEPIXELSHADER_INVALIDSHADERTYPE = 94, + CREATERASTERIZERSTATE_INVALIDFILLMODE = 95, + CREATERASTERIZERSTATE_INVALIDCULLMODE = 96, + CREATERASTERIZERSTATE_INVALIDDEPTHBIASCLAMP = 97, + CREATERASTERIZERSTATE_INVALIDSLOPESCALEDDEPTHBIAS = 98, + CREATEDEPTHSTENCILSTATE_INVALIDDEPTHWRITEMASK = 100, + CREATEDEPTHSTENCILSTATE_INVALIDDEPTHFUNC = 101, + CREATEDEPTHSTENCILSTATE_INVALIDFRONTFACESTENCILFAILOP = 102, + CREATEDEPTHSTENCILSTATE_INVALIDFRONTFACESTENCILZFAILOP = 103, + CREATEDEPTHSTENCILSTATE_INVALIDFRONTFACESTENCILPASSOP = 104, + CREATEDEPTHSTENCILSTATE_INVALIDFRONTFACESTENCILFUNC = 105, + CREATEDEPTHSTENCILSTATE_INVALIDBACKFACESTENCILFAILOP = 106, + CREATEDEPTHSTENCILSTATE_INVALIDBACKFACESTENCILZFAILOP = 107, + CREATEDEPTHSTENCILSTATE_INVALIDBACKFACESTENCILPASSOP = 108, + CREATEDEPTHSTENCILSTATE_INVALIDBACKFACESTENCILFUNC = 109, + CREATEBLENDSTATE_INVALIDSRCBLEND = 111, + CREATEBLENDSTATE_INVALIDDESTBLEND = 112, + CREATEBLENDSTATE_INVALIDBLENDOP = 113, + CREATEBLENDSTATE_INVALIDSRCBLENDALPHA = 114, + CREATEBLENDSTATE_INVALIDDESTBLENDALPHA = 115, + CREATEBLENDSTATE_INVALIDBLENDOPALPHA = 116, + CREATEBLENDSTATE_INVALIDRENDERTARGETWRITEMASK = 117, + CLEARDEPTHSTENCILVIEW_INVALID = 135, + COMMAND_LIST_DRAW_ROOT_SIGNATURE_NOT_SET = 200, + COMMAND_LIST_DRAW_ROOT_SIGNATURE_MISMATCH = 201, + COMMAND_LIST_DRAW_VERTEX_BUFFER_NOT_SET = 202, + COMMAND_LIST_DRAW_VERTEX_BUFFER_STRIDE_TOO_SMALL = 209, + COMMAND_LIST_DRAW_VERTEX_BUFFER_TOO_SMALL = 210, + COMMAND_LIST_DRAW_INDEX_BUFFER_NOT_SET = 211, + COMMAND_LIST_DRAW_INDEX_BUFFER_FORMAT_INVALID = 212, + COMMAND_LIST_DRAW_INDEX_BUFFER_TOO_SMALL = 213, + COMMAND_LIST_DRAW_INVALID_PRIMITIVETOPOLOGY = 219, + COMMAND_LIST_DRAW_VERTEX_STRIDE_UNALIGNED = 221, + COMMAND_LIST_DRAW_INDEX_OFFSET_UNALIGNED = 222, + DEVICE_REMOVAL_PROCESS_AT_FAULT = 232, + DEVICE_REMOVAL_PROCESS_POSSIBLY_AT_FAULT = 233, + DEVICE_REMOVAL_PROCESS_NOT_AT_FAULT = 234, + CREATEINPUTLAYOUT_TRAILING_DIGIT_IN_SEMANTIC = 239, + CREATEGEOMETRYSHADERWITHSTREAMOUTPUT_TRAILING_DIGIT_IN_SEMANTIC = 240, + CREATEINPUTLAYOUT_TYPE_MISMATCH = 245, + CREATEINPUTLAYOUT_EMPTY_LAYOUT = 253, + LIVE_OBJECT_SUMMARY = 255, + LIVE_DEVICE = 274, + LIVE_SWAPCHAIN = 275, + CREATEDEPTHSTENCILVIEW_INVALIDFLAGS = 276, + CREATEVERTEXSHADER_INVALIDCLASSLINKAGE = 277, + CREATEGEOMETRYSHADER_INVALIDCLASSLINKAGE = 278, + CREATEGEOMETRYSHADERWITHSTREAMOUTPUT_INVALIDSTREAMTORASTERIZER = 280, + CREATEPIXELSHADER_INVALIDCLASSLINKAGE = 283, + CREATEGEOMETRYSHADERWITHSTREAMOUTPUT_INVALIDSTREAM = 284, + CREATEGEOMETRYSHADERWITHSTREAMOUTPUT_UNEXPECTEDENTRIES = 285, + CREATEGEOMETRYSHADERWITHSTREAMOUTPUT_UNEXPECTEDSTRIDES = 286, + CREATEGEOMETRYSHADERWITHSTREAMOUTPUT_INVALIDNUMSTRIDES = 287, + CREATEHULLSHADER_OUTOFMEMORY = 289, + CREATEHULLSHADER_INVALIDSHADERBYTECODE = 290, + CREATEHULLSHADER_INVALIDSHADERTYPE = 291, + CREATEHULLSHADER_INVALIDCLASSLINKAGE = 292, + CREATEDOMAINSHADER_OUTOFMEMORY = 294, + CREATEDOMAINSHADER_INVALIDSHADERBYTECODE = 295, + CREATEDOMAINSHADER_INVALIDSHADERTYPE = 296, + CREATEDOMAINSHADER_INVALIDCLASSLINKAGE = 297, + RESOURCE_UNMAP_NOTMAPPED = 310, + DEVICE_CHECKFEATURESUPPORT_MISMATCHED_DATA_SIZE = 318, + CREATECOMPUTESHADER_OUTOFMEMORY = 321, + CREATECOMPUTESHADER_INVALIDSHADERBYTECODE = 322, + CREATECOMPUTESHADER_INVALIDCLASSLINKAGE = 323, + DEVICE_CREATEVERTEXSHADER_DOUBLEFLOATOPSNOTSUPPORTED = 331, + DEVICE_CREATEHULLSHADER_DOUBLEFLOATOPSNOTSUPPORTED = 332, + DEVICE_CREATEDOMAINSHADER_DOUBLEFLOATOPSNOTSUPPORTED = 333, + DEVICE_CREATEGEOMETRYSHADER_DOUBLEFLOATOPSNOTSUPPORTED = 334, + DEVICE_CREATEGEOMETRYSHADERWITHSTREAMOUTPUT_DOUBLEFLOATOPSNOTSUPPORTED = 335, + DEVICE_CREATEPIXELSHADER_DOUBLEFLOATOPSNOTSUPPORTED = 336, + DEVICE_CREATECOMPUTESHADER_DOUBLEFLOATOPSNOTSUPPORTED = 337, + CREATEUNORDEREDACCESSVIEW_INVALIDRESOURCE = 340, + CREATEUNORDEREDACCESSVIEW_INVALIDDESC = 341, + CREATEUNORDEREDACCESSVIEW_INVALIDFORMAT = 342, + CREATEUNORDEREDACCESSVIEW_INVALIDVIDEOPLANESLICE = 343, + CREATEUNORDEREDACCESSVIEW_INVALIDPLANESLICE = 344, + CREATEUNORDEREDACCESSVIEW_INVALIDDIMENSIONS = 345, + CREATEUNORDEREDACCESSVIEW_UNRECOGNIZEDFORMAT = 346, + CREATEUNORDEREDACCESSVIEW_INVALIDFLAGS = 354, + CREATERASTERIZERSTATE_INVALIDFORCEDSAMPLECOUNT = 401, + CREATEBLENDSTATE_INVALIDLOGICOPS = 403, + DEVICE_CREATEVERTEXSHADER_DOUBLEEXTENSIONSNOTSUPPORTED = 410, + DEVICE_CREATEHULLSHADER_DOUBLEEXTENSIONSNOTSUPPORTED = 412, + DEVICE_CREATEDOMAINSHADER_DOUBLEEXTENSIONSNOTSUPPORTED = 414, + DEVICE_CREATEGEOMETRYSHADER_DOUBLEEXTENSIONSNOTSUPPORTED = 416, + DEVICE_CREATEGEOMETRYSHADERWITHSTREAMOUTPUT_DOUBLEEXTENSIONSNOTSUPPORTED = 418, + DEVICE_CREATEPIXELSHADER_DOUBLEEXTENSIONSNOTSUPPORTED = 420, + DEVICE_CREATECOMPUTESHADER_DOUBLEEXTENSIONSNOTSUPPORTED = 422, + DEVICE_CREATEVERTEXSHADER_UAVSNOTSUPPORTED = 425, + DEVICE_CREATEHULLSHADER_UAVSNOTSUPPORTED = 426, + DEVICE_CREATEDOMAINSHADER_UAVSNOTSUPPORTED = 427, + DEVICE_CREATEGEOMETRYSHADER_UAVSNOTSUPPORTED = 428, + DEVICE_CREATEGEOMETRYSHADERWITHSTREAMOUTPUT_UAVSNOTSUPPORTED = 429, + DEVICE_CREATEPIXELSHADER_UAVSNOTSUPPORTED = 430, + DEVICE_CREATECOMPUTESHADER_UAVSNOTSUPPORTED = 431, + DEVICE_CLEARVIEW_INVALIDSOURCERECT = 447, + DEVICE_CLEARVIEW_EMPTYRECT = 448, + UPDATETILEMAPPINGS_INVALID_PARAMETER = 493, + COPYTILEMAPPINGS_INVALID_PARAMETER = 494, + CREATEDEVICE_INVALIDARGS = 506, + CREATEDEVICE_WARNING = 507, + RESOURCE_BARRIER_INVALID_TYPE = 519, + RESOURCE_BARRIER_NULL_POINTER = 520, + RESOURCE_BARRIER_INVALID_SUBRESOURCE = 521, + RESOURCE_BARRIER_RESERVED_BITS = 522, + RESOURCE_BARRIER_MISSING_BIND_FLAGS = 523, + RESOURCE_BARRIER_MISMATCHING_MISC_FLAGS = 524, + RESOURCE_BARRIER_MATCHING_STATES = 525, + RESOURCE_BARRIER_INVALID_COMBINATION = 526, + RESOURCE_BARRIER_BEFORE_AFTER_MISMATCH = 527, + RESOURCE_BARRIER_INVALID_RESOURCE = 528, + RESOURCE_BARRIER_SAMPLE_COUNT = 529, + RESOURCE_BARRIER_INVALID_FLAGS = 530, + RESOURCE_BARRIER_INVALID_COMBINED_FLAGS = 531, + RESOURCE_BARRIER_INVALID_FLAGS_FOR_FORMAT = 532, + RESOURCE_BARRIER_INVALID_SPLIT_BARRIER = 533, + RESOURCE_BARRIER_UNMATCHED_END = 534, + RESOURCE_BARRIER_UNMATCHED_BEGIN = 535, + RESOURCE_BARRIER_INVALID_FLAG = 536, + RESOURCE_BARRIER_INVALID_COMMAND_LIST_TYPE = 537, + INVALID_SUBRESOURCE_STATE = 538, + COMMAND_ALLOCATOR_CONTENTION = 540, + COMMAND_ALLOCATOR_RESET = 541, + COMMAND_ALLOCATOR_RESET_BUNDLE = 542, + COMMAND_ALLOCATOR_CANNOT_RESET = 543, + COMMAND_LIST_OPEN = 544, + INVALID_BUNDLE_API = 546, + COMMAND_LIST_CLOSED = 547, + WRONG_COMMAND_ALLOCATOR_TYPE = 549, + COMMAND_ALLOCATOR_SYNC = 552, + COMMAND_LIST_SYNC = 553, + SET_DESCRIPTOR_HEAP_INVALID = 554, + CREATE_COMMANDQUEUE = 557, + CREATE_COMMANDALLOCATOR = 558, + CREATE_PIPELINESTATE = 559, + CREATE_COMMANDLIST12 = 560, + CREATE_RESOURCE = 562, + CREATE_DESCRIPTORHEAP = 563, + CREATE_ROOTSIGNATURE = 564, + CREATE_LIBRARY = 565, + CREATE_HEAP = 566, + CREATE_MONITOREDFENCE = 567, + CREATE_QUERYHEAP = 568, + CREATE_COMMANDSIGNATURE = 569, + LIVE_COMMANDQUEUE = 570, + LIVE_COMMANDALLOCATOR = 571, + LIVE_PIPELINESTATE = 572, + LIVE_COMMANDLIST12 = 573, + LIVE_RESOURCE = 575, + LIVE_DESCRIPTORHEAP = 576, + LIVE_ROOTSIGNATURE = 577, + LIVE_LIBRARY = 578, + LIVE_HEAP = 579, + LIVE_MONITOREDFENCE = 580, + LIVE_QUERYHEAP = 581, + LIVE_COMMANDSIGNATURE = 582, + DESTROY_COMMANDQUEUE = 583, + DESTROY_COMMANDALLOCATOR = 584, + DESTROY_PIPELINESTATE = 585, + DESTROY_COMMANDLIST12 = 586, + DESTROY_RESOURCE = 588, + DESTROY_DESCRIPTORHEAP = 589, + DESTROY_ROOTSIGNATURE = 590, + DESTROY_LIBRARY = 591, + DESTROY_HEAP = 592, + DESTROY_MONITOREDFENCE = 593, + DESTROY_QUERYHEAP = 594, + DESTROY_COMMANDSIGNATURE = 595, + CREATERESOURCE_INVALIDDIMENSIONS = 597, + CREATERESOURCE_INVALIDMISCFLAGS = 599, + CREATERESOURCE_INVALIDARG_RETURN = 602, + CREATERESOURCE_OUTOFMEMORY_RETURN = 603, + CREATERESOURCE_INVALIDDESC = 604, + POSSIBLY_INVALID_SUBRESOURCE_STATE = 607, + INVALID_USE_OF_NON_RESIDENT_RESOURCE = 608, + POSSIBLE_INVALID_USE_OF_NON_RESIDENT_RESOURCE = 609, + BUNDLE_PIPELINE_STATE_MISMATCH = 610, + PRIMITIVE_TOPOLOGY_MISMATCH_PIPELINE_STATE = 611, + RENDER_TARGET_FORMAT_MISMATCH_PIPELINE_STATE = 613, + RENDER_TARGET_SAMPLE_DESC_MISMATCH_PIPELINE_STATE = 614, + DEPTH_STENCIL_FORMAT_MISMATCH_PIPELINE_STATE = 615, + DEPTH_STENCIL_SAMPLE_DESC_MISMATCH_PIPELINE_STATE = 616, + CREATESHADER_INVALIDBYTECODE = 622, + CREATEHEAP_NULLDESC = 623, + CREATEHEAP_INVALIDSIZE = 624, + CREATEHEAP_UNRECOGNIZEDHEAPTYPE = 625, + CREATEHEAP_UNRECOGNIZEDCPUPAGEPROPERTIES = 626, + CREATEHEAP_UNRECOGNIZEDMEMORYPOOL = 627, + CREATEHEAP_INVALIDPROPERTIES = 628, + CREATEHEAP_INVALIDALIGNMENT = 629, + CREATEHEAP_UNRECOGNIZEDMISCFLAGS = 630, + CREATEHEAP_INVALIDMISCFLAGS = 631, + CREATEHEAP_INVALIDARG_RETURN = 632, + CREATEHEAP_OUTOFMEMORY_RETURN = 633, + CREATERESOURCEANDHEAP_NULLHEAPPROPERTIES = 634, + CREATERESOURCEANDHEAP_UNRECOGNIZEDHEAPTYPE = 635, + CREATERESOURCEANDHEAP_UNRECOGNIZEDCPUPAGEPROPERTIES = 636, + CREATERESOURCEANDHEAP_UNRECOGNIZEDMEMORYPOOL = 637, + CREATERESOURCEANDHEAP_INVALIDHEAPPROPERTIES = 638, + CREATERESOURCEANDHEAP_UNRECOGNIZEDHEAPMISCFLAGS = 639, + CREATERESOURCEANDHEAP_INVALIDHEAPMISCFLAGS = 640, + CREATERESOURCEANDHEAP_INVALIDARG_RETURN = 641, + CREATERESOURCEANDHEAP_OUTOFMEMORY_RETURN = 642, + GETCUSTOMHEAPPROPERTIES_UNRECOGNIZEDHEAPTYPE = 643, + GETCUSTOMHEAPPROPERTIES_INVALIDHEAPTYPE = 644, + CREATE_DESCRIPTOR_HEAP_INVALID_DESC = 645, + INVALID_DESCRIPTOR_HANDLE = 646, + CREATERASTERIZERSTATE_INVALID_CONSERVATIVERASTERMODE = 647, + CREATE_CONSTANT_BUFFER_VIEW_INVALID_RESOURCE = 649, + CREATE_CONSTANT_BUFFER_VIEW_INVALID_DESC = 650, + CREATE_UNORDEREDACCESS_VIEW_INVALID_COUNTER_USAGE = 652, + COPY_DESCRIPTORS_INVALID_RANGES = 653, + COPY_DESCRIPTORS_WRITE_ONLY_DESCRIPTOR = 654, + CREATEGRAPHICSPIPELINESTATE_RTV_FORMAT_NOT_UNKNOWN = 655, + CREATEGRAPHICSPIPELINESTATE_INVALID_RENDER_TARGET_COUNT = 656, + CREATEGRAPHICSPIPELINESTATE_VERTEX_SHADER_NOT_SET = 657, + CREATEGRAPHICSPIPELINESTATE_INPUTLAYOUT_NOT_SET = 658, + CREATEGRAPHICSPIPELINESTATE_SHADER_LINKAGE_HS_DS_SIGNATURE_MISMATCH = 659, + CREATEGRAPHICSPIPELINESTATE_SHADER_LINKAGE_REGISTERINDEX = 660, + CREATEGRAPHICSPIPELINESTATE_SHADER_LINKAGE_COMPONENTTYPE = 661, + CREATEGRAPHICSPIPELINESTATE_SHADER_LINKAGE_REGISTERMASK = 662, + CREATEGRAPHICSPIPELINESTATE_SHADER_LINKAGE_SYSTEMVALUE = 663, + CREATEGRAPHICSPIPELINESTATE_SHADER_LINKAGE_NEVERWRITTEN_ALWAYSREADS = 664, + CREATEGRAPHICSPIPELINESTATE_SHADER_LINKAGE_MINPRECISION = 665, + CREATEGRAPHICSPIPELINESTATE_SHADER_LINKAGE_SEMANTICNAME_NOT_FOUND = 666, + CREATEGRAPHICSPIPELINESTATE_HS_XOR_DS_MISMATCH = 667, + CREATEGRAPHICSPIPELINESTATE_HULL_SHADER_INPUT_TOPOLOGY_MISMATCH = 668, + CREATEGRAPHICSPIPELINESTATE_HS_DS_CONTROL_POINT_COUNT_MISMATCH = 669, + CREATEGRAPHICSPIPELINESTATE_HS_DS_TESSELLATOR_DOMAIN_MISMATCH = 670, + CREATEGRAPHICSPIPELINESTATE_INVALID_USE_OF_CENTER_MULTISAMPLE_PATTERN = 671, + CREATEGRAPHICSPIPELINESTATE_INVALID_USE_OF_FORCED_SAMPLE_COUNT = 672, + CREATEGRAPHICSPIPELINESTATE_INVALID_PRIMITIVETOPOLOGY = 673, + CREATEGRAPHICSPIPELINESTATE_INVALID_SYSTEMVALUE = 674, + CREATEGRAPHICSPIPELINESTATE_OM_DUAL_SOURCE_BLENDING_CAN_ONLY_HAVE_RENDER_TARGET_0 = 675, + CREATEGRAPHICSPIPELINESTATE_OM_RENDER_TARGET_DOES_NOT_SUPPORT_BLENDING = 676, + CREATEGRAPHICSPIPELINESTATE_PS_OUTPUT_TYPE_MISMATCH = 677, + CREATEGRAPHICSPIPELINESTATE_OM_RENDER_TARGET_DOES_NOT_SUPPORT_LOGIC_OPS = 678, + CREATEGRAPHICSPIPELINESTATE_RENDERTARGETVIEW_NOT_SET = 679, + CREATEGRAPHICSPIPELINESTATE_DEPTHSTENCILVIEW_NOT_SET = 680, + CREATEGRAPHICSPIPELINESTATE_GS_INPUT_PRIMITIVE_MISMATCH = 681, + CREATEGRAPHICSPIPELINESTATE_POSITION_NOT_PRESENT = 682, + CREATEGRAPHICSPIPELINESTATE_MISSING_ROOT_SIGNATURE_FLAGS = 683, + CREATEGRAPHICSPIPELINESTATE_INVALID_INDEX_BUFFER_PROPERTIES = 684, + CREATEGRAPHICSPIPELINESTATE_INVALID_SAMPLE_DESC = 685, + CREATEGRAPHICSPIPELINESTATE_HS_ROOT_SIGNATURE_MISMATCH = 686, + CREATEGRAPHICSPIPELINESTATE_DS_ROOT_SIGNATURE_MISMATCH = 687, + CREATEGRAPHICSPIPELINESTATE_VS_ROOT_SIGNATURE_MISMATCH = 688, + CREATEGRAPHICSPIPELINESTATE_GS_ROOT_SIGNATURE_MISMATCH = 689, + CREATEGRAPHICSPIPELINESTATE_PS_ROOT_SIGNATURE_MISMATCH = 690, + CREATEGRAPHICSPIPELINESTATE_MISSING_ROOT_SIGNATURE = 691, + EXECUTE_BUNDLE_OPEN_BUNDLE = 692, + EXECUTE_BUNDLE_DESCRIPTOR_HEAP_MISMATCH = 693, + EXECUTE_BUNDLE_TYPE = 694, + DRAW_EMPTY_SCISSOR_RECTANGLE = 695, + CREATE_ROOT_SIGNATURE_BLOB_NOT_FOUND = 696, + CREATE_ROOT_SIGNATURE_DESERIALIZE_FAILED = 697, + CREATE_ROOT_SIGNATURE_INVALID_CONFIGURATION = 698, + CREATE_ROOT_SIGNATURE_NOT_SUPPORTED_ON_DEVICE = 699, + CREATERESOURCEANDHEAP_NULLRESOURCEPROPERTIES = 700, + CREATERESOURCEANDHEAP_NULLHEAP = 701, + GETRESOURCEALLOCATIONINFO_INVALIDRDESCS = 702, + MAKERESIDENT_NULLOBJECTARRAY = 703, + EVICT_NULLOBJECTARRAY = 705, + SET_DESCRIPTOR_TABLE_INVALID = 708, + SET_ROOT_CONSTANT_INVALID = 709, + SET_ROOT_CONSTANT_BUFFER_VIEW_INVALID = 710, + SET_ROOT_SHADER_RESOURCE_VIEW_INVALID = 711, + SET_ROOT_UNORDERED_ACCESS_VIEW_INVALID = 712, + SET_VERTEX_BUFFERS_INVALID_DESC = 713, + SET_INDEX_BUFFER_INVALID_DESC = 715, + SET_STREAM_OUTPUT_BUFFERS_INVALID_DESC = 717, + CREATERESOURCE_UNRECOGNIZEDDIMENSIONALITY = 718, + CREATERESOURCE_UNRECOGNIZEDLAYOUT = 719, + CREATERESOURCE_INVALIDDIMENSIONALITY = 720, + CREATERESOURCE_INVALIDALIGNMENT = 721, + CREATERESOURCE_INVALIDMIPLEVELS = 722, + CREATERESOURCE_INVALIDSAMPLEDESC = 723, + CREATERESOURCE_INVALIDLAYOUT = 724, + SET_INDEX_BUFFER_INVALID = 725, + SET_VERTEX_BUFFERS_INVALID = 726, + SET_STREAM_OUTPUT_BUFFERS_INVALID = 727, + SET_RENDER_TARGETS_INVALID = 728, + CREATEQUERY_HEAP_INVALID_PARAMETERS = 729, + BEGIN_END_QUERY_INVALID_PARAMETERS = 731, + CLOSE_COMMAND_LIST_OPEN_QUERY = 732, + RESOLVE_QUERY_DATA_INVALID_PARAMETERS = 733, + SET_PREDICATION_INVALID_PARAMETERS = 734, + TIMESTAMPS_NOT_SUPPORTED = 735, + CREATERESOURCE_UNRECOGNIZEDFORMAT = 737, + CREATERESOURCE_INVALIDFORMAT = 738, + GETCOPYABLEFOOTPRINTS_INVALIDSUBRESOURCERANGE = 739, + GETCOPYABLEFOOTPRINTS_INVALIDBASEOFFSET = 740, + GETCOPYABLELAYOUT_INVALIDSUBRESOURCERANGE = 739, + GETCOPYABLELAYOUT_INVALIDBASEOFFSET = 740, + RESOURCE_BARRIER_INVALID_HEAP = 741, + CREATE_SAMPLER_INVALID = 742, + CREATECOMMANDSIGNATURE_INVALID = 743, + EXECUTE_INDIRECT_INVALID_PARAMETERS = 744, + GETGPUVIRTUALADDRESS_INVALID_RESOURCE_DIMENSION = 745, + CREATERESOURCE_INVALIDCLEARVALUE = 815, + CREATERESOURCE_UNRECOGNIZEDCLEARVALUEFORMAT = 816, + CREATERESOURCE_INVALIDCLEARVALUEFORMAT = 817, + CREATERESOURCE_CLEARVALUEDENORMFLUSH = 818, + CLEARRENDERTARGETVIEW_MISMATCHINGCLEARVALUE = 820, + CLEARDEPTHSTENCILVIEW_MISMATCHINGCLEARVALUE = 821, + MAP_INVALIDHEAP = 822, + UNMAP_INVALIDHEAP = 823, + MAP_INVALIDRESOURCE = 824, + UNMAP_INVALIDRESOURCE = 825, + MAP_INVALIDSUBRESOURCE = 826, + UNMAP_INVALIDSUBRESOURCE = 827, + MAP_INVALIDRANGE = 828, + UNMAP_INVALIDRANGE = 829, + MAP_INVALIDDATAPOINTER = 832, + MAP_INVALIDARG_RETURN = 833, + MAP_OUTOFMEMORY_RETURN = 834, + EXECUTECOMMANDLISTS_BUNDLENOTSUPPORTED = 835, + EXECUTECOMMANDLISTS_COMMANDLISTMISMATCH = 836, + EXECUTECOMMANDLISTS_OPENCOMMANDLIST = 837, + EXECUTECOMMANDLISTS_FAILEDCOMMANDLIST = 838, + COPYBUFFERREGION_NULLDST = 839, + COPYBUFFERREGION_INVALIDDSTRESOURCEDIMENSION = 840, + COPYBUFFERREGION_DSTRANGEOUTOFBOUNDS = 841, + COPYBUFFERREGION_NULLSRC = 842, + COPYBUFFERREGION_INVALIDSRCRESOURCEDIMENSION = 843, + COPYBUFFERREGION_SRCRANGEOUTOFBOUNDS = 844, + COPYBUFFERREGION_INVALIDCOPYFLAGS = 845, + COPYTEXTUREREGION_NULLDST = 846, + COPYTEXTUREREGION_UNRECOGNIZEDDSTTYPE = 847, + COPYTEXTUREREGION_INVALIDDSTRESOURCEDIMENSION = 848, + COPYTEXTUREREGION_INVALIDDSTRESOURCE = 849, + COPYTEXTUREREGION_INVALIDDSTSUBRESOURCE = 850, + COPYTEXTUREREGION_INVALIDDSTOFFSET = 851, + COPYTEXTUREREGION_UNRECOGNIZEDDSTFORMAT = 852, + COPYTEXTUREREGION_INVALIDDSTFORMAT = 853, + COPYTEXTUREREGION_INVALIDDSTDIMENSIONS = 854, + COPYTEXTUREREGION_INVALIDDSTROWPITCH = 855, + COPYTEXTUREREGION_INVALIDDSTPLACEMENT = 856, + COPYTEXTUREREGION_INVALIDDSTDSPLACEDFOOTPRINTFORMAT = 857, + COPYTEXTUREREGION_DSTREGIONOUTOFBOUNDS = 858, + COPYTEXTUREREGION_NULLSRC = 859, + COPYTEXTUREREGION_UNRECOGNIZEDSRCTYPE = 860, + COPYTEXTUREREGION_INVALIDSRCRESOURCEDIMENSION = 861, + COPYTEXTUREREGION_INVALIDSRCRESOURCE = 862, + COPYTEXTUREREGION_INVALIDSRCSUBRESOURCE = 863, + COPYTEXTUREREGION_INVALIDSRCOFFSET = 864, + COPYTEXTUREREGION_UNRECOGNIZEDSRCFORMAT = 865, + COPYTEXTUREREGION_INVALIDSRCFORMAT = 866, + COPYTEXTUREREGION_INVALIDSRCDIMENSIONS = 867, + COPYTEXTUREREGION_INVALIDSRCROWPITCH = 868, + COPYTEXTUREREGION_INVALIDSRCPLACEMENT = 869, + COPYTEXTUREREGION_INVALIDSRCDSPLACEDFOOTPRINTFORMAT = 870, + COPYTEXTUREREGION_SRCREGIONOUTOFBOUNDS = 871, + COPYTEXTUREREGION_INVALIDDSTCOORDINATES = 872, + COPYTEXTUREREGION_INVALIDSRCBOX = 873, + COPYTEXTUREREGION_FORMATMISMATCH = 874, + COPYTEXTUREREGION_EMPTYBOX = 875, + COPYTEXTUREREGION_INVALIDCOPYFLAGS = 876, + RESOLVESUBRESOURCE_INVALID_SUBRESOURCE_INDEX = 877, + RESOLVESUBRESOURCE_INVALID_FORMAT = 878, + RESOLVESUBRESOURCE_RESOURCE_MISMATCH = 879, + RESOLVESUBRESOURCE_INVALID_SAMPLE_COUNT = 880, + CREATECOMPUTEPIPELINESTATE_INVALID_SHADER = 881, + CREATECOMPUTEPIPELINESTATE_CS_ROOT_SIGNATURE_MISMATCH = 882, + CREATECOMPUTEPIPELINESTATE_MISSING_ROOT_SIGNATURE = 883, + CREATEPIPELINESTATE_INVALIDCACHEDBLOB = 884, + CREATEPIPELINESTATE_CACHEDBLOBADAPTERMISMATCH = 885, + CREATEPIPELINESTATE_CACHEDBLOBDRIVERVERSIONMISMATCH = 886, + CREATEPIPELINESTATE_CACHEDBLOBDESCMISMATCH = 887, + CREATEPIPELINESTATE_CACHEDBLOBIGNORED = 888, + WRITETOSUBRESOURCE_INVALIDHEAP = 889, + WRITETOSUBRESOURCE_INVALIDRESOURCE = 890, + WRITETOSUBRESOURCE_INVALIDBOX = 891, + WRITETOSUBRESOURCE_INVALIDSUBRESOURCE = 892, + WRITETOSUBRESOURCE_EMPTYBOX = 893, + READFROMSUBRESOURCE_INVALIDHEAP = 894, + READFROMSUBRESOURCE_INVALIDRESOURCE = 895, + READFROMSUBRESOURCE_INVALIDBOX = 896, + READFROMSUBRESOURCE_INVALIDSUBRESOURCE = 897, + READFROMSUBRESOURCE_EMPTYBOX = 898, + TOO_MANY_NODES_SPECIFIED = 899, + INVALID_NODE_INDEX = 900, + GETHEAPPROPERTIES_INVALIDRESOURCE = 901, + NODE_MASK_MISMATCH = 902, + COMMAND_LIST_OUTOFMEMORY = 903, + COMMAND_LIST_MULTIPLE_SWAPCHAIN_BUFFER_REFERENCES = 904, + COMMAND_LIST_TOO_MANY_SWAPCHAIN_REFERENCES = 905, + COMMAND_QUEUE_TOO_MANY_SWAPCHAIN_REFERENCES = 906, + EXECUTECOMMANDLISTS_WRONGSWAPCHAINBUFFERREFERENCE = 907, + COMMAND_LIST_SETRENDERTARGETS_INVALIDNUMRENDERTARGETS = 908, + CREATE_QUEUE_INVALID_TYPE = 909, + CREATE_QUEUE_INVALID_FLAGS = 910, + CREATESHAREDRESOURCE_INVALIDFLAGS = 911, + CREATESHAREDRESOURCE_INVALIDFORMAT = 912, + CREATESHAREDHEAP_INVALIDFLAGS = 913, + REFLECTSHAREDPROPERTIES_UNRECOGNIZEDPROPERTIES = 914, + REFLECTSHAREDPROPERTIES_INVALIDSIZE = 915, + REFLECTSHAREDPROPERTIES_INVALIDOBJECT = 916, + KEYEDMUTEX_INVALIDOBJECT = 917, + KEYEDMUTEX_INVALIDKEY = 918, + KEYEDMUTEX_WRONGSTATE = 919, + CREATE_QUEUE_INVALID_PRIORITY = 920, + OBJECT_DELETED_WHILE_STILL_IN_USE = 921, + CREATEPIPELINESTATE_INVALID_FLAGS = 922, + HEAP_ADDRESS_RANGE_HAS_NO_RESOURCE = 923, + COMMAND_LIST_DRAW_RENDER_TARGET_DELETED = 924, + CREATEGRAPHICSPIPELINESTATE_ALL_RENDER_TARGETS_HAVE_UNKNOWN_FORMAT = 925, + HEAP_ADDRESS_RANGE_INTERSECTS_MULTIPLE_BUFFERS = 926, + EXECUTECOMMANDLISTS_GPU_WRITTEN_READBACK_RESOURCE_MAPPED = 927, + UNMAP_RANGE_NOT_EMPTY = 929, + MAP_INVALID_NULLRANGE = 930, + UNMAP_INVALID_NULLRANGE = 931, + NO_GRAPHICS_API_SUPPORT = 932, + NO_COMPUTE_API_SUPPORT = 933, + RESOLVESUBRESOURCE_RESOURCE_FLAGS_NOT_SUPPORTED = 934, + GPU_BASED_VALIDATION_ROOT_ARGUMENT_UNINITIALIZED = 935, + GPU_BASED_VALIDATION_DESCRIPTOR_HEAP_INDEX_OUT_OF_BOUNDS = 936, + GPU_BASED_VALIDATION_DESCRIPTOR_TABLE_REGISTER_INDEX_OUT_OF_BOUNDS = 937, + GPU_BASED_VALIDATION_DESCRIPTOR_UNINITIALIZED = 938, + GPU_BASED_VALIDATION_DESCRIPTOR_TYPE_MISMATCH = 939, + GPU_BASED_VALIDATION_SRV_RESOURCE_DIMENSION_MISMATCH = 940, + GPU_BASED_VALIDATION_UAV_RESOURCE_DIMENSION_MISMATCH = 941, + GPU_BASED_VALIDATION_INCOMPATIBLE_RESOURCE_STATE = 942, + COPYRESOURCE_NULLDST = 943, + COPYRESOURCE_INVALIDDSTRESOURCE = 944, + COPYRESOURCE_NULLSRC = 945, + COPYRESOURCE_INVALIDSRCRESOURCE = 946, + RESOLVESUBRESOURCE_NULLDST = 947, + RESOLVESUBRESOURCE_INVALIDDSTRESOURCE = 948, + RESOLVESUBRESOURCE_NULLSRC = 949, + RESOLVESUBRESOURCE_INVALIDSRCRESOURCE = 950, + PIPELINE_STATE_TYPE_MISMATCH = 951, + COMMAND_LIST_DISPATCH_ROOT_SIGNATURE_NOT_SET = 952, + COMMAND_LIST_DISPATCH_ROOT_SIGNATURE_MISMATCH = 953, + RESOURCE_BARRIER_ZERO_BARRIERS = 954, + BEGIN_END_EVENT_MISMATCH = 955, + RESOURCE_BARRIER_POSSIBLE_BEFORE_AFTER_MISMATCH = 956, + RESOURCE_BARRIER_MISMATCHING_BEGIN_END = 957, + GPU_BASED_VALIDATION_INVALID_RESOURCE = 958, + USE_OF_ZERO_REFCOUNT_OBJECT = 959, + OBJECT_EVICTED_WHILE_STILL_IN_USE = 960, + GPU_BASED_VALIDATION_ROOT_DESCRIPTOR_ACCESS_OUT_OF_BOUNDS = 961, + CREATEPIPELINELIBRARY_INVALIDLIBRARYBLOB = 962, + CREATEPIPELINELIBRARY_DRIVERVERSIONMISMATCH = 963, + CREATEPIPELINELIBRARY_ADAPTERVERSIONMISMATCH = 964, + CREATEPIPELINELIBRARY_UNSUPPORTED = 965, + CREATE_PIPELINELIBRARY = 966, + LIVE_PIPELINELIBRARY = 967, + DESTROY_PIPELINELIBRARY = 968, + STOREPIPELINE_NONAME = 969, + STOREPIPELINE_DUPLICATENAME = 970, + LOADPIPELINE_NAMENOTFOUND = 971, + LOADPIPELINE_INVALIDDESC = 972, + PIPELINELIBRARY_SERIALIZE_NOTENOUGHMEMORY = 973, + CREATEGRAPHICSPIPELINESTATE_PS_OUTPUT_RT_OUTPUT_MISMATCH = 974, + SETEVENTONMULTIPLEFENCECOMPLETION_INVALIDFLAGS = 975, + CREATE_QUEUE_VIDEO_NOT_SUPPORTED = 976, + CREATE_COMMAND_ALLOCATOR_VIDEO_NOT_SUPPORTED = 977, + CREATEQUERY_HEAP_VIDEO_DECODE_STATISTICS_NOT_SUPPORTED = 978, + CREATE_VIDEODECODECOMMANDLIST = 979, + CREATE_VIDEODECODER = 980, + CREATE_VIDEODECODESTREAM = 981, + LIVE_VIDEODECODECOMMANDLIST = 982, + LIVE_VIDEODECODER = 983, + LIVE_VIDEODECODESTREAM = 984, + DESTROY_VIDEODECODECOMMANDLIST = 985, + DESTROY_VIDEODECODER = 986, + DESTROY_VIDEODECODESTREAM = 987, + DECODE_FRAME_INVALID_PARAMETERS = 988, + DEPRECATED_API = 989, + RESOURCE_BARRIER_MISMATCHING_COMMAND_LIST_TYPE = 990, + COMMAND_LIST_DESCRIPTOR_TABLE_NOT_SET = 991, + COMMAND_LIST_ROOT_CONSTANT_BUFFER_VIEW_NOT_SET = 992, + COMMAND_LIST_ROOT_SHADER_RESOURCE_VIEW_NOT_SET = 993, + COMMAND_LIST_ROOT_UNORDERED_ACCESS_VIEW_NOT_SET = 994, + DISCARD_INVALID_SUBRESOURCE_RANGE = 995, + DISCARD_ONE_SUBRESOURCE_FOR_MIPS_WITH_RECTS = 996, + DISCARD_NO_RECTS_FOR_NON_TEXTURE2D = 997, + COPY_ON_SAME_SUBRESOURCE = 998, + SETRESIDENCYPRIORITY_INVALID_PAGEABLE = 999, + GPU_BASED_VALIDATION_UNSUPPORTED = 1000, + STATIC_DESCRIPTOR_INVALID_DESCRIPTOR_CHANGE = 1001, + DATA_STATIC_DESCRIPTOR_INVALID_DATA_CHANGE = 1002, + DATA_STATIC_WHILE_SET_AT_EXECUTE_DESCRIPTOR_INVALID_DATA_CHANGE = 1003, + EXECUTE_BUNDLE_STATIC_DESCRIPTOR_DATA_STATIC_NOT_SET = 1004, + GPU_BASED_VALIDATION_RESOURCE_ACCESS_OUT_OF_BOUNDS = 1005, + GPU_BASED_VALIDATION_SAMPLER_MODE_MISMATCH = 1006, + CREATE_FENCE_INVALID_FLAGS = 1007, + RESOURCE_BARRIER_DUPLICATE_SUBRESOURCE_TRANSITIONS = 1008, + SETRESIDENCYPRIORITY_INVALID_PRIORITY = 1009, + CREATE_DESCRIPTOR_HEAP_LARGE_NUM_DESCRIPTORS = 1013, + BEGIN_EVENT = 1014, + END_EVENT = 1015, + CREATEDEVICE_DEBUG_LAYER_STARTUP_OPTIONS = 1016, + CREATEDEPTHSTENCILSTATE_DEPTHBOUNDSTEST_UNSUPPORTED = 1017, + CREATEPIPELINESTATE_DUPLICATE_SUBOBJECT = 1018, + CREATEPIPELINESTATE_UNKNOWN_SUBOBJECT = 1019, + CREATEPIPELINESTATE_ZERO_SIZE_STREAM = 1020, + CREATEPIPELINESTATE_INVALID_STREAM = 1021, + CREATEPIPELINESTATE_CANNOT_DEDUCE_TYPE = 1022, + COMMAND_LIST_STATIC_DESCRIPTOR_RESOURCE_DIMENSION_MISMATCH = 1023, + CREATE_COMMAND_QUEUE_INSUFFICIENT_PRIVILEGE_FOR_GLOBAL_REALTIME = 1024, + CREATE_COMMAND_QUEUE_INSUFFICIENT_HARDWARE_SUPPORT_FOR_GLOBAL_REALTIME = 1025, + ATOMICCOPYBUFFER_INVALID_ARCHITECTURE = 1026, + ATOMICCOPYBUFFER_NULL_DST = 1027, + ATOMICCOPYBUFFER_INVALID_DST_RESOURCE_DIMENSION = 1028, + ATOMICCOPYBUFFER_DST_RANGE_OUT_OF_BOUNDS = 1029, + ATOMICCOPYBUFFER_NULL_SRC = 1030, + ATOMICCOPYBUFFER_INVALID_SRC_RESOURCE_DIMENSION = 1031, + ATOMICCOPYBUFFER_SRC_RANGE_OUT_OF_BOUNDS = 1032, + ATOMICCOPYBUFFER_INVALID_OFFSET_ALIGNMENT = 1033, + ATOMICCOPYBUFFER_NULL_DEPENDENT_RESOURCES = 1034, + ATOMICCOPYBUFFER_NULL_DEPENDENT_SUBRESOURCE_RANGES = 1035, + ATOMICCOPYBUFFER_INVALID_DEPENDENT_RESOURCE = 1036, + ATOMICCOPYBUFFER_INVALID_DEPENDENT_SUBRESOURCE_RANGE = 1037, + ATOMICCOPYBUFFER_DEPENDENT_SUBRESOURCE_OUT_OF_BOUNDS = 1038, + ATOMICCOPYBUFFER_DEPENDENT_RANGE_OUT_OF_BOUNDS = 1039, + ATOMICCOPYBUFFER_ZERO_DEPENDENCIES = 1040, + DEVICE_CREATE_SHARED_HANDLE_INVALIDARG = 1041, + DESCRIPTOR_HANDLE_WITH_INVALID_RESOURCE = 1042, + SETDEPTHBOUNDS_INVALIDARGS = 1043, + GPU_BASED_VALIDATION_RESOURCE_STATE_IMPRECISE = 1044, + COMMAND_LIST_PIPELINE_STATE_NOT_SET = 1045, + CREATEGRAPHICSPIPELINESTATE_SHADER_MODEL_MISMATCH = 1046, + OBJECT_ACCESSED_WHILE_STILL_IN_USE = 1047, + PROGRAMMABLE_MSAA_UNSUPPORTED = 1048, + SETSAMPLEPOSITIONS_INVALIDARGS = 1049, + RESOLVESUBRESOURCEREGION_INVALID_RECT = 1050, + CREATE_VIDEODECODECOMMANDQUEUE = 1051, + CREATE_VIDEOPROCESSCOMMANDLIST = 1052, + CREATE_VIDEOPROCESSCOMMANDQUEUE = 1053, + LIVE_VIDEODECODECOMMANDQUEUE = 1054, + LIVE_VIDEOPROCESSCOMMANDLIST = 1055, + LIVE_VIDEOPROCESSCOMMANDQUEUE = 1056, + DESTROY_VIDEODECODECOMMANDQUEUE = 1057, + DESTROY_VIDEOPROCESSCOMMANDLIST = 1058, + DESTROY_VIDEOPROCESSCOMMANDQUEUE = 1059, + CREATE_VIDEOPROCESSOR = 1060, + CREATE_VIDEOPROCESSSTREAM = 1061, + LIVE_VIDEOPROCESSOR = 1062, + LIVE_VIDEOPROCESSSTREAM = 1063, + DESTROY_VIDEOPROCESSOR = 1064, + DESTROY_VIDEOPROCESSSTREAM = 1065, + PROCESS_FRAME_INVALID_PARAMETERS = 1066, + COPY_INVALIDLAYOUT = 1067, + CREATE_CRYPTO_SESSION = 1068, + CREATE_CRYPTO_SESSION_POLICY = 1069, + CREATE_PROTECTED_RESOURCE_SESSION = 1070, + LIVE_CRYPTO_SESSION = 1071, + LIVE_CRYPTO_SESSION_POLICY = 1072, + LIVE_PROTECTED_RESOURCE_SESSION = 1073, + DESTROY_CRYPTO_SESSION = 1074, + DESTROY_CRYPTO_SESSION_POLICY = 1075, + DESTROY_PROTECTED_RESOURCE_SESSION = 1076, + PROTECTED_RESOURCE_SESSION_UNSUPPORTED = 1077, + FENCE_INVALIDOPERATION = 1078, + CREATEQUERY_HEAP_COPY_QUEUE_TIMESTAMPS_NOT_SUPPORTED = 1079, + SAMPLEPOSITIONS_MISMATCH_DEFERRED = 1080, + SAMPLEPOSITIONS_MISMATCH_RECORDTIME_ASSUMEDFROMFIRSTUSE = 1081, + SAMPLEPOSITIONS_MISMATCH_RECORDTIME_ASSUMEDFROMCLEAR = 1082, + CREATE_VIDEODECODERHEAP = 1083, + LIVE_VIDEODECODERHEAP = 1084, + DESTROY_VIDEODECODERHEAP = 1085, + OPENEXISTINGHEAP_INVALIDARG_RETURN = 1086, + OPENEXISTINGHEAP_OUTOFMEMORY_RETURN = 1087, + OPENEXISTINGHEAP_INVALIDADDRESS = 1088, + OPENEXISTINGHEAP_INVALIDHANDLE = 1089, + WRITEBUFFERIMMEDIATE_INVALID_DEST = 1090, + WRITEBUFFERIMMEDIATE_INVALID_MODE = 1091, + WRITEBUFFERIMMEDIATE_INVALID_ALIGNMENT = 1092, + WRITEBUFFERIMMEDIATE_NOT_SUPPORTED = 1093, + SETVIEWINSTANCEMASK_INVALIDARGS = 1094, + VIEW_INSTANCING_UNSUPPORTED = 1095, + VIEW_INSTANCING_INVALIDARGS = 1096, + COPYTEXTUREREGION_MISMATCH_DECODE_REFERENCE_ONLY_FLAG = 1097, + COPYRESOURCE_MISMATCH_DECODE_REFERENCE_ONLY_FLAG = 1098, + CREATE_VIDEO_DECODE_HEAP_CAPS_FAILURE = 1099, + CREATE_VIDEO_DECODE_HEAP_CAPS_UNSUPPORTED = 1100, + VIDEO_DECODE_SUPPORT_INVALID_INPUT = 1101, + CREATE_VIDEO_DECODER_UNSUPPORTED = 1102, + CREATEGRAPHICSPIPELINESTATE_METADATA_ERROR = 1103, + CREATEGRAPHICSPIPELINESTATE_VIEW_INSTANCING_VERTEX_SIZE_EXCEEDED = 1104, + CREATEGRAPHICSPIPELINESTATE_RUNTIME_INTERNAL_ERROR = 1105, + NO_VIDEO_API_SUPPORT = 1106, + VIDEO_PROCESS_SUPPORT_INVALID_INPUT = 1107, + CREATE_VIDEO_PROCESSOR_CAPS_FAILURE = 1108, + VIDEO_PROCESS_SUPPORT_UNSUPPORTED_FORMAT = 1109, + VIDEO_DECODE_FRAME_INVALID_ARGUMENT = 1110, + ENQUEUE_MAKE_RESIDENT_INVALID_FLAGS = 1111, + OPENEXISTINGHEAP_UNSUPPORTED = 1112, + VIDEO_PROCESS_FRAMES_INVALID_ARGUMENT = 1113, + VIDEO_DECODE_SUPPORT_UNSUPPORTED = 1114, + CREATE_COMMANDRECORDER = 1115, + LIVE_COMMANDRECORDER = 1116, + DESTROY_COMMANDRECORDER = 1117, + CREATE_COMMAND_RECORDER_VIDEO_NOT_SUPPORTED = 1118, + CREATE_COMMAND_RECORDER_INVALID_SUPPORT_FLAGS = 1119, + CREATE_COMMAND_RECORDER_INVALID_FLAGS = 1120, + CREATE_COMMAND_RECORDER_MORE_RECORDERS_THAN_LOGICAL_PROCESSORS = 1121, + CREATE_COMMANDPOOL = 1122, + LIVE_COMMANDPOOL = 1123, + DESTROY_COMMANDPOOL = 1124, + CREATE_COMMAND_POOL_INVALID_FLAGS = 1125, + CREATE_COMMAND_LIST_VIDEO_NOT_SUPPORTED = 1126, + COMMAND_RECORDER_SUPPORT_FLAGS_MISMATCH = 1127, + COMMAND_RECORDER_CONTENTION = 1128, + COMMAND_RECORDER_USAGE_WITH_CREATECOMMANDLIST_COMMAND_LIST = 1129, + COMMAND_ALLOCATOR_USAGE_WITH_CREATECOMMANDLIST1_COMMAND_LIST = 1130, + CANNOT_EXECUTE_EMPTY_COMMAND_LIST = 1131, + CANNOT_RESET_COMMAND_POOL_WITH_OPEN_COMMAND_LISTS = 1132, + CANNOT_USE_COMMAND_RECORDER_WITHOUT_CURRENT_TARGET = 1133, + CANNOT_CHANGE_COMMAND_RECORDER_TARGET_WHILE_RECORDING = 1134, + COMMAND_POOL_SYNC = 1135, + EVICT_UNDERFLOW = 1136, + CREATE_META_COMMAND = 1137, + LIVE_META_COMMAND = 1138, + DESTROY_META_COMMAND = 1139, + COPYBUFFERREGION_INVALID_DST_RESOURCE = 1140, + COPYBUFFERREGION_INVALID_SRC_RESOURCE = 1141, + ATOMICCOPYBUFFER_INVALID_DST_RESOURCE = 1142, + ATOMICCOPYBUFFER_INVALID_SRC_RESOURCE = 1143, + CREATEPLACEDRESOURCEONBUFFER_NULL_BUFFER = 1144, + CREATEPLACEDRESOURCEONBUFFER_NULL_RESOURCE_DESC = 1145, + CREATEPLACEDRESOURCEONBUFFER_UNSUPPORTED = 1146, + CREATEPLACEDRESOURCEONBUFFER_INVALID_BUFFER_DIMENSION = 1147, + CREATEPLACEDRESOURCEONBUFFER_INVALID_BUFFER_FLAGS = 1148, + CREATEPLACEDRESOURCEONBUFFER_INVALID_BUFFER_OFFSET = 1149, + CREATEPLACEDRESOURCEONBUFFER_INVALID_RESOURCE_DIMENSION = 1150, + CREATEPLACEDRESOURCEONBUFFER_INVALID_RESOURCE_FLAGS = 1151, + CREATEPLACEDRESOURCEONBUFFER_OUTOFMEMORY_RETURN = 1152, + CANNOT_CREATE_GRAPHICS_AND_VIDEO_COMMAND_RECORDER = 1153, + UPDATETILEMAPPINGS_POSSIBLY_MISMATCHING_PROPERTIES = 1154, + CREATE_COMMAND_LIST_INVALID_COMMAND_LIST_TYPE = 1155, + CLEARUNORDEREDACCESSVIEW_INCOMPATIBLE_WITH_STRUCTURED_BUFFERS = 1156, + COMPUTE_ONLY_DEVICE_OPERATION_UNSUPPORTED = 1157, + BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INVALID = 1158, + EMIT_RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO_INVALID = 1159, + COPY_RAYTRACING_ACCELERATION_STRUCTURE_INVALID = 1160, + DISPATCH_RAYS_INVALID = 1161, + GET_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO_INVALID = 1162, + CREATE_LIFETIMETRACKER = 1163, + LIVE_LIFETIMETRACKER = 1164, + DESTROY_LIFETIMETRACKER = 1165, + DESTROYOWNEDOBJECT_OBJECTNOTOWNED = 1166, + CREATE_TRACKEDWORKLOAD = 1167, + LIVE_TRACKEDWORKLOAD = 1168, + DESTROY_TRACKEDWORKLOAD = 1169, + RENDER_PASS_ERROR = 1170, + META_COMMAND_ID_INVALID = 1171, + META_COMMAND_UNSUPPORTED_PARAMS = 1172, + META_COMMAND_FAILED_ENUMERATION = 1173, + META_COMMAND_PARAMETER_SIZE_MISMATCH = 1174, + UNINITIALIZED_META_COMMAND = 1175, + META_COMMAND_INVALID_GPU_VIRTUAL_ADDRESS = 1176, + CREATE_VIDEOENCODECOMMANDLIST = 1177, + LIVE_VIDEOENCODECOMMANDLIST = 1178, + DESTROY_VIDEOENCODECOMMANDLIST = 1179, + CREATE_VIDEOENCODECOMMANDQUEUE = 1180, + LIVE_VIDEOENCODECOMMANDQUEUE = 1181, + DESTROY_VIDEOENCODECOMMANDQUEUE = 1182, + CREATE_VIDEOMOTIONESTIMATOR = 1183, + LIVE_VIDEOMOTIONESTIMATOR = 1184, + DESTROY_VIDEOMOTIONESTIMATOR = 1185, + CREATE_VIDEOMOTIONVECTORHEAP = 1186, + LIVE_VIDEOMOTIONVECTORHEAP = 1187, + DESTROY_VIDEOMOTIONVECTORHEAP = 1188, + MULTIPLE_TRACKED_WORKLOADS = 1189, + MULTIPLE_TRACKED_WORKLOAD_PAIRS = 1190, + OUT_OF_ORDER_TRACKED_WORKLOAD_PAIR = 1191, + CANNOT_ADD_TRACKED_WORKLOAD = 1192, + INCOMPLETE_TRACKED_WORKLOAD_PAIR = 1193, + CREATE_STATE_OBJECT_ERROR = 1194, + GET_SHADER_IDENTIFIER_ERROR = 1195, + GET_SHADER_STACK_SIZE_ERROR = 1196, + GET_PIPELINE_STACK_SIZE_ERROR = 1197, + SET_PIPELINE_STACK_SIZE_ERROR = 1198, + GET_SHADER_IDENTIFIER_SIZE_INVALID = 1199, + CHECK_DRIVER_MATCHING_IDENTIFIER_INVALID = 1200, + CHECK_DRIVER_MATCHING_IDENTIFIER_DRIVER_REPORTED_ISSUE = 1201, + RENDER_PASS_INVALID_RESOURCE_BARRIER = 1202, + RENDER_PASS_DISALLOWED_API_CALLED = 1203, + RENDER_PASS_CANNOT_NEST_RENDER_PASSES = 1204, + RENDER_PASS_CANNOT_END_WITHOUT_BEGIN = 1205, + RENDER_PASS_CANNOT_CLOSE_COMMAND_LIST = 1206, + RENDER_PASS_GPU_WORK_WHILE_SUSPENDED = 1207, + RENDER_PASS_MISMATCHING_SUSPEND_RESUME = 1208, + RENDER_PASS_NO_PRIOR_SUSPEND_WITHIN_EXECUTECOMMANDLISTS = 1209, + RENDER_PASS_NO_SUBSEQUENT_RESUME_WITHIN_EXECUTECOMMANDLISTS = 1210, + TRACKED_WORKLOAD_COMMAND_QUEUE_MISMATCH = 1211, + TRACKED_WORKLOAD_NOT_SUPPORTED = 1212, + RENDER_PASS_MISMATCHING_NO_ACCESS = 1213, + RENDER_PASS_UNSUPPORTED_RESOLVE = 1214, + CLEARUNORDEREDACCESSVIEW_INVALID_RESOURCE_PTR = 1215, + WINDOWS7_FENCE_OUTOFORDER_SIGNAL = 1216, + WINDOWS7_FENCE_OUTOFORDER_WAIT = 1217, + VIDEO_CREATE_MOTION_ESTIMATOR_INVALID_ARGUMENT = 1218, + VIDEO_CREATE_MOTION_VECTOR_HEAP_INVALID_ARGUMENT = 1219, + ESTIMATE_MOTION_INVALID_ARGUMENT = 1220, + RESOLVE_MOTION_VECTOR_HEAP_INVALID_ARGUMENT = 1221, + GETGPUVIRTUALADDRESS_INVALID_HEAP_TYPE = 1222, + SET_BACKGROUND_PROCESSING_MODE_INVALID_ARGUMENT = 1223, + CREATE_COMMAND_LIST_INVALID_COMMAND_LIST_TYPE_FOR_FEATURE_LEVEL = 1224, + CREATE_VIDEOEXTENSIONCOMMAND = 1225, + LIVE_VIDEOEXTENSIONCOMMAND = 1226, + DESTROY_VIDEOEXTENSIONCOMMAND = 1227, + INVALID_VIDEO_EXTENSION_COMMAND_ID = 1228, + VIDEO_EXTENSION_COMMAND_INVALID_ARGUMENT = 1229, + CREATE_ROOT_SIGNATURE_NOT_UNIQUE_IN_DXIL_LIBRARY = 1230, + VARIABLE_SHADING_RATE_NOT_ALLOWED_WITH_TIR = 1231, + GEOMETRY_SHADER_OUTPUTTING_BOTH_VIEWPORT_ARRAY_INDEX_AND_SHADING_RATE_NOT_SUPPORTED_ON_DEVICE = 1232, + RSSETSHADING_RATE_INVALID_SHADING_RATE = 1233, + RSSETSHADING_RATE_SHADING_RATE_NOT_PERMITTED_BY_CAP = 1234, + RSSETSHADING_RATE_INVALID_COMBINER = 1235, + RSSETSHADINGRATEIMAGE_REQUIRES_TIER_2 = 1236, + RSSETSHADINGRATE_REQUIRES_TIER_1 = 1237, + SHADING_RATE_IMAGE_INCORRECT_FORMAT = 1238, + SHADING_RATE_IMAGE_INCORRECT_ARRAY_SIZE = 1239, + SHADING_RATE_IMAGE_INCORRECT_MIP_LEVEL = 1240, + SHADING_RATE_IMAGE_INCORRECT_SAMPLE_COUNT = 1241, + SHADING_RATE_IMAGE_INCORRECT_SAMPLE_QUALITY = 1242, + NON_RETAIL_SHADER_MODEL_WONT_VALIDATE = 1243, + CREATEGRAPHICSPIPELINESTATE_AS_ROOT_SIGNATURE_MISMATCH = 1244, + CREATEGRAPHICSPIPELINESTATE_MS_ROOT_SIGNATURE_MISMATCH = 1245, + ADD_TO_STATE_OBJECT_ERROR = 1246, + CREATE_PROTECTED_RESOURCE_SESSION_INVALID_ARGUMENT = 1247, + CREATEGRAPHICSPIPELINESTATE_MS_PSO_DESC_MISMATCH = 1248, + CREATEPIPELINESTATE_MS_INCOMPLETE_TYPE = 1249, + CREATEGRAPHICSPIPELINESTATE_AS_NOT_MS_MISMATCH = 1250, + CREATEGRAPHICSPIPELINESTATE_MS_NOT_PS_MISMATCH = 1251, + NONZERO_SAMPLER_FEEDBACK_MIP_REGION_WITH_INCOMPATIBLE_FORMAT = 1252, + CREATEGRAPHICSPIPELINESTATE_INPUTLAYOUT_SHADER_MISMATCH = 1253, + EMPTY_DISPATCH = 1254, + RESOURCE_FORMAT_REQUIRES_SAMPLER_FEEDBACK_CAPABILITY = 1255, + SAMPLER_FEEDBACK_MAP_INVALID_MIP_REGION = 1256, + SAMPLER_FEEDBACK_MAP_INVALID_DIMENSION = 1257, + SAMPLER_FEEDBACK_MAP_INVALID_SAMPLE_COUNT = 1258, + SAMPLER_FEEDBACK_MAP_INVALID_SAMPLE_QUALITY = 1259, + SAMPLER_FEEDBACK_MAP_INVALID_LAYOUT = 1260, + SAMPLER_FEEDBACK_MAP_REQUIRES_UNORDERED_ACCESS_FLAG = 1261, + SAMPLER_FEEDBACK_CREATE_UAV_NULL_ARGUMENTS = 1262, + SAMPLER_FEEDBACK_UAV_REQUIRES_SAMPLER_FEEDBACK_CAPABILITY = 1263, + SAMPLER_FEEDBACK_CREATE_UAV_REQUIRES_FEEDBACK_MAP_FORMAT = 1264, + CREATEMESHSHADER_INVALIDSHADERBYTECODE = 1265, + CREATEMESHSHADER_OUTOFMEMORY = 1266, + CREATEMESHSHADERWITHSTREAMOUTPUT_INVALIDSHADERTYPE = 1267, + RESOLVESUBRESOURCE_SAMPLER_FEEDBACK_TRANSCODE_INVALID_FORMAT = 1268, + RESOLVESUBRESOURCE_SAMPLER_FEEDBACK_INVALID_MIP_LEVEL_COUNT = 1269, + RESOLVESUBRESOURCE_SAMPLER_FEEDBACK_TRANSCODE_ARRAY_SIZE_MISMATCH = 1270, + SAMPLER_FEEDBACK_CREATE_UAV_MISMATCHING_TARGETED_RESOURCE = 1271, + CREATEMESHSHADER_OUTPUTEXCEEDSMAXSIZE = 1272, + CREATEMESHSHADER_GROUPSHAREDEXCEEDSMAXSIZE = 1273, + VERTEX_SHADER_OUTPUTTING_BOTH_VIEWPORT_ARRAY_INDEX_AND_SHADING_RATE_NOT_SUPPORTED_ON_DEVICE = 1274, + MESH_SHADER_OUTPUTTING_BOTH_VIEWPORT_ARRAY_INDEX_AND_SHADING_RATE_NOT_SUPPORTED_ON_DEVICE = 1275, + CREATEMESHSHADER_MISMATCHEDASMSPAYLOADSIZE = 1276, + CREATE_ROOT_SIGNATURE_UNBOUNDED_STATIC_DESCRIPTORS = 1277, + CREATEAMPLIFICATIONSHADER_INVALIDSHADERBYTECODE = 1278, + CREATEAMPLIFICATIONSHADER_OUTOFMEMORY = 1279, + MESSAGES_END = 1280, +} + +MESSAGE :: struct { + Category: MESSAGE_CATEGORY, + Severity: MESSAGE_SEVERITY, + ID: MESSAGE_ID, + pDescription: cstring, + DescriptionByteLength: SIZE_T, +} + +INFO_QUEUE_FILTER_DESC :: struct { + NumCategories: u32, + pCategoryList: ^MESSAGE_CATEGORY, + NumSeverities: u32, + pSeverityList: ^MESSAGE_SEVERITY, + NumIDs: u32, + pIDList: ^MESSAGE_ID, +} + +INFO_QUEUE_FILTER :: struct { + AllowList: INFO_QUEUE_FILTER_DESC, + DenyList: INFO_QUEUE_FILTER_DESC, +} + + +IInfoQueue_UUID_STRING :: "0742a90b-c387-483f-b946-30a7e4e61458" +IInfoQueue_UUID := &IID{0x0742a90b, 0xc387, 0x483f, {0xb9, 0x46, 0x30, 0xa7, 0xe4, 0xe6, 0x14, 0x58}} +IInfoQueue :: struct #raw_union { + #subtype iunknown: IUnknown, + using id3d12infoqueue_vtable: ^IInfoQueue_VTable, +} +IInfoQueue_VTable :: struct { + using iunknown_vtable: IUnknown_VTable, + SetMessageCountLimit: proc "stdcall" (this: ^IInfoQueue, MessageCountLimit: u64) -> HRESULT, + ClearStoredMessages: proc "stdcall" (this: ^IInfoQueue), + GetMessageA: proc "stdcall" (this: ^IInfoQueue, MessageIndex: u64, pMessage: ^MESSAGE, pMessageByteLength: ^SIZE_T) -> HRESULT, + GetNumMessagesAllowedByStorageFilter: proc "stdcall" (this: ^IInfoQueue) -> u64, + GetNumMessagesDeniedByStorageFilter: proc "stdcall" (this: ^IInfoQueue) -> u64, + GetNumStoredMessages: proc "stdcall" (this: ^IInfoQueue) -> u64, + GetNumStoredMessagesAllowedByRetrievalFilter: proc "stdcall" (this: ^IInfoQueue) -> u64, + GetNumMessagesDiscardedByMessageCountLimit: proc "stdcall" (this: ^IInfoQueue) -> u64, + GetMessageCountLimit: proc "stdcall" (this: ^IInfoQueue) -> u64, + AddStorageFilterEntries: proc "stdcall" (this: ^IInfoQueue, pFilter: ^INFO_QUEUE_FILTER) -> HRESULT, + GetStorageFilter: proc "stdcall" (this: ^IInfoQueue, pFilter: ^INFO_QUEUE_FILTER, pFilterByteLength: ^SIZE_T) -> HRESULT, + ClearStorageFilter: proc "stdcall" (this: ^IInfoQueue), + PushEmptyStorageFilter: proc "stdcall" (this: ^IInfoQueue) -> HRESULT, + PushCopyOfStorageFilter: proc "stdcall" (this: ^IInfoQueue) -> HRESULT, + PushStorageFilter: proc "stdcall" (this: ^IInfoQueue, pFilter: ^INFO_QUEUE_FILTER) -> HRESULT, + PopStorageFilter: proc "stdcall" (this: ^IInfoQueue), + GetStorageFilterStackSize: proc "stdcall" (this: ^IInfoQueue) -> u32, + AddRetrievalFilterEntries: proc "stdcall" (this: ^IInfoQueue, pFilter: ^INFO_QUEUE_FILTER) -> HRESULT, + GetRetrievalFilter: proc "stdcall" (this: ^IInfoQueue, pFilter: ^INFO_QUEUE_FILTER, pFilterByteLength: ^SIZE_T) -> HRESULT, + ClearRetrievalFilter: proc "stdcall" (this: ^IInfoQueue), + PushEmptyRetrievalFilter: proc "stdcall" (this: ^IInfoQueue) -> HRESULT, + PushCopyOfRetrievalFilter: proc "stdcall" (this: ^IInfoQueue) -> HRESULT, + PushRetrievalFilter: proc "stdcall" (this: ^IInfoQueue, pFilter: ^INFO_QUEUE_FILTER) -> HRESULT, + PopRetrievalFilter: proc "stdcall" (this: ^IInfoQueue), + GetRetrievalFilterStackSize: proc "stdcall" (this: ^IInfoQueue) -> u32, + AddMessage: proc "stdcall" (this: ^IInfoQueue, Category: MESSAGE_CATEGORY, Severity: MESSAGE_SEVERITY, ID: MESSAGE_ID, pDescription: cstring) -> HRESULT, + AddApplicationMessage: proc "stdcall" (this: ^IInfoQueue, Severity: MESSAGE_SEVERITY, pDescription: cstring) -> HRESULT, + SetBreakOnCategory: proc "stdcall" (this: ^IInfoQueue, Category: MESSAGE_CATEGORY, bEnable: BOOL) -> HRESULT, + SetBreakOnSeverity: proc "stdcall" (this: ^IInfoQueue, Severity: MESSAGE_SEVERITY, bEnable: BOOL) -> HRESULT, + SetBreakOnID: proc "stdcall" (this: ^IInfoQueue, ID: MESSAGE_ID, bEnable: BOOL) -> HRESULT, + GetBreakOnCategory: proc "stdcall" (this: ^IInfoQueue, Category: MESSAGE_CATEGORY) -> BOOL, + GetBreakOnSeverity: proc "stdcall" (this: ^IInfoQueue, Severity: MESSAGE_SEVERITY) -> BOOL, + GetBreakOnID: proc "stdcall" (this: ^IInfoQueue, ID: MESSAGE_ID) -> BOOL, + SetMuteDebugOutput: proc "stdcall" (this: ^IInfoQueue, bMute: BOOL), + GetMuteDebugOutput: proc "stdcall" (this: ^IInfoQueue) -> BOOL, +} + +PFN_CREATE_DEVICE :: #type proc "c" (a0: ^IUnknown, a1: FEATURE_LEVEL, a2: ^IID, a3: ^rawptr) -> HRESULT +PFN_GET_DEBUG_INTERFACE :: #type proc "c" (a0: ^IID, a1: ^rawptr) -> HRESULT + +AXIS_SHADING_RATE :: enum i32 { + _1X = 0, + _2X = 1, + _4X = 2, +} + +SHADING_RATE :: enum i32 { + _1X1 = 0, + _1X2 = 1, + _2X1 = 4, + _2X2 = 5, + _2X4 = 6, + _4X2 = 9, + _4X4 = 10, +} + +SHADING_RATE_COMBINER :: enum i32 { + PASSTHROUGH = 0, + OVERRIDE = 1, + MIN = 2, + MAX = 3, + SUM = 4, +} + + +IGraphicsCommandList5_UUID_STRING :: "55050859-4024-474c-87f5-6472eaee44ea" +IGraphicsCommandList5_UUID := &IID{0x55050859, 0x4024, 0x474c, {0x87, 0xf5, 0x64, 0x72, 0xea, 0xee, 0x44, 0xea}} +IGraphicsCommandList5 :: struct #raw_union { + #subtype id3d12graphicscommandlist4: IGraphicsCommandList4, + using id3d12graphicscommandlist5_vtable: ^IGraphicsCommandList5_VTable, +} +IGraphicsCommandList5_VTable :: struct { + using id3d12graphicscommandlist4_vtable: IGraphicsCommandList4_VTable, + RSSetShadingRate: proc "stdcall" (this: ^IGraphicsCommandList5, baseShadingRate: SHADING_RATE, combiners: ^SHADING_RATE_COMBINER), + RSSetShadingRateImage: proc "stdcall" (this: ^IGraphicsCommandList5, shadingRateImage: ^IResource), +} + +DISPATCH_MESH_ARGUMENTS :: struct { + ThreadGroupCountX: u32, + ThreadGroupCountY: u32, + ThreadGroupCountZ: u32, +} + + +IGraphicsCommandList6_UUID_STRING :: "c3827890-e548-4cfa-96cf-5689a9370f80" +IGraphicsCommandList6_UUID := &IID{0xc3827890, 0xe548, 0x4cfa, {0x96, 0xcf, 0x56, 0x89, 0xa9, 0x37, 0x0f, 0x80}} +IGraphicsCommandList6 :: struct #raw_union { + #subtype id3d12graphicscommandlist5: IGraphicsCommandList5, + using id3d12graphicscommandlist6_vtable: ^IGraphicsCommandList6_VTable, +} +IGraphicsCommandList6_VTable :: struct { + using id3d12graphicscommandlist5_vtable: IGraphicsCommandList5_VTable, + DispatchMesh: proc "stdcall" (this: ^IGraphicsCommandList6, ThreadGroupCountX: u32, ThreadGroupCountY: u32, ThreadGroupCountZ: u32), +} + +SHADER_VERSION_TYPE :: enum i32 { + PIXEL_SHADER = 0, + VERTEX_SHADER = 1, + GEOMETRY_SHADER = 2, + + HULL_SHADER = 3, + DOMAIN_SHADER = 4, + COMPUTE_SHADER = 5, + + RESERVED0 = 65520, +} + +SIGNATURE_PARAMETER_DESC :: struct { + SemanticName: cstring, + SemanticIndex: u32, + Register: u32, + SystemValueType: NAME, + ComponentType: REGISTER_COMPONENT_TYPE, + Mask: u8, + + ReadWriteMask: u8, + + Stream: u32, + MinPrecision: MIN_PRECISION, +} + +SHADER_BUFFER_DESC :: struct { + Name: cstring, + Type: CBUFFER_TYPE, + Variables: u32, + Size: u32, + uFlags: u32, +} + +SHADER_VARIABLE_DESC :: struct { + Name: cstring, + StartOffset: u32, + Size: u32, + uFlags: u32, + DefaultValue: rawptr, + StartTexture: u32, + TextureSize: u32, + StartSampler: u32, + SamplerSize: u32, +} + +SHADER_TYPE_DESC :: struct { + Class: SHADER_VARIABLE_CLASS, + Type: SHADER_VARIABLE_TYPE, + Rows: u32, + Columns: u32, + Elements: u32, + Members: u32, + Offset: u32, + Name: cstring, +} +SHADER_DESC :: struct { + Version: u32, + Creator: cstring, + Flags: u32, + + ConstantBuffers: u32, + BoundResources: u32, + InputParameters: u32, + OutputParameters: u32, + + InstructionCount: u32, + TempRegisterCount: u32, + TempArrayCount: u32, + DefCount: u32, + DclCount: u32, + TextureNormalInstructions: u32, + TextureLoadInstructions: u32, + TextureCompInstructions: u32, + TextureBiasInstructions: u32, + TextureGradientInstructions: u32, + FloatInstructionCount: u32, + IntInstructionCount: u32, + UintInstructionCount: u32, + StaticFlowControlCount: u32, + DynamicFlowControlCount: u32, + MacroInstructionCount: u32, + ArrayInstructionCount: u32, + CutInstructionCount: u32, + EmitInstructionCount: u32, + GSOutputTopology: PRIMITIVE_TOPOLOGY, + GSMaxOutputVertexCount: u32, + InputPrimitive: PRIMITIVE, + PatchConstantParameters: u32, + cGSInstanceCount: u32, + cControlPoints: u32, + HSOutputPrimitive: TESSELLATOR_OUTPUT_PRIMITIVE, + HSPartitioning: TESSELLATOR_PARTITIONING, + TessellatorDomain: TESSELLATOR_DOMAIN, + + cBarrierInstructions: u32, + cInterlockedInstructions: u32, + cTextureStoreInstructions: u32, +} + +SHADER_INPUT_BIND_DESC :: struct { + Name: cstring, + Type: SHADER_INPUT_TYPE, + BindPoint: u32, + BindCount: u32, + + uFlags: u32, + ReturnType: RESOURCE_RETURN_TYPE, + Dimension: SRV_DIMENSION, + NumSamples: u32, + Space: u32, + uID: u32, +} + +LIBRARY_DESC :: struct { + Creator: cstring, + Flags: u32, + FunctionCount: u32, +} + +FUNCTION_DESC :: struct { + Version: u32, + Creator: cstring, + Flags: u32, + + ConstantBuffers: u32, + BoundResources: u32, + + InstructionCount: u32, + TempRegisterCount: u32, + TempArrayCount: u32, + DefCount: u32, + DclCount: u32, + TextureNormalInstructions: u32, + TextureLoadInstructions: u32, + TextureCompInstructions: u32, + TextureBiasInstructions: u32, + TextureGradientInstructions: u32, + FloatInstructionCount: u32, + IntInstructionCount: u32, + UintInstructionCount: u32, + StaticFlowControlCount: u32, + DynamicFlowControlCount: u32, + MacroInstructionCount: u32, + ArrayInstructionCount: u32, + MovInstructionCount: u32, + MovcInstructionCount: u32, + ConversionInstructionCount: u32, + BitwiseInstructionCount: u32, + MinFeatureLevel: FEATURE_LEVEL, + RequiredFeatureFlags: u64, + + Name: cstring, + FunctionParameterCount: i32, + HasReturn: BOOL, + Has10Level9VertexShader: BOOL, + Has10Level9PixelShader: BOOL, +} + +PARAMETER_DESC :: struct { + Name: cstring, + SemanticName: cstring, + Type: SHADER_VARIABLE_TYPE, + Class: SHADER_VARIABLE_CLASS, + Rows: u32, + Columns: u32, + InterpolationMode: INTERPOLATION_MODE, + Flags: PARAMETER_FLAGS, + + FirstInRegister: u32, + FirstInComponent: u32, + FirstOutRegister: u32, + FirstOutComponent: u32, +} + +IShaderReflectionType :: struct { + vtable: ^IShaderReflectionType_VTable, +} +IShaderReflectionType_VTable :: struct { + GetDesc: proc "stdcall" (this: ^IShaderReflectionType, pDesc: ^SHADER_TYPE_DESC) -> HRESULT, + GetMemberTypeByIndex: proc "stdcall" (this: ^IShaderReflectionType, Index: u32) -> ^IShaderReflectionType, + GetMemberTypeByName: proc "stdcall" (this: ^IShaderReflectionType, Name: cstring) -> ^IShaderReflectionType, + GetMemberTypeName: proc "stdcall" (this: ^IShaderReflectionType, Index: u32) -> cstring, + IsEqual: proc "stdcall" (this: ^IShaderReflectionType, pType: ^IShaderReflectionType) -> HRESULT, + GetSubType: proc "stdcall" (this: ^IShaderReflectionType) -> ^IShaderReflectionType, + GetBaseClass: proc "stdcall" (this: ^IShaderReflectionType) -> ^IShaderReflectionType, + GetNumInterfaces: proc "stdcall" (this: ^IShaderReflectionType) -> u32, + GetInterfaceByIndex: proc "stdcall" (this: ^IShaderReflectionType, uIndex: u32) -> ^IShaderReflectionType, + IsOfType: proc "stdcall" (this: ^IShaderReflectionType, pType: ^IShaderReflectionType) -> HRESULT, + ImplementsInterface: proc "stdcall" (this: ^IShaderReflectionType, pBase: ^IShaderReflectionType) -> HRESULT, +} + +IShaderReflectionVariable :: struct { + vtable: ^IShaderReflectionVariable_VTable, +} +IShaderReflectionVariable_VTable :: struct { + GetDesc: proc "stdcall" (this: ^IShaderReflectionVariable, pDesc: ^SHADER_VARIABLE_DESC) -> HRESULT, + GetType: proc "stdcall" (this: ^IShaderReflectionVariable) -> ^IShaderReflectionType, + GetBuffer: proc "stdcall" (this: ^IShaderReflectionVariable) -> ^IShaderReflectionConstantBuffer, + GetInterfaceSlot: proc "stdcall" (this: ^IShaderReflectionVariable, uArrayIndex: u32) -> u32, +} + +IShaderReflectionConstantBuffer :: struct { + vtable: ^IShaderReflectionConstantBuffer_VTable, +} +IShaderReflectionConstantBuffer_VTable :: struct { + GetDesc: proc "stdcall" (this: ^IShaderReflectionConstantBuffer, pDesc: ^SHADER_BUFFER_DESC) -> HRESULT, + GetVariableByIndex: proc "stdcall" (this: ^IShaderReflectionConstantBuffer, Index: u32) -> ^IShaderReflectionVariable, + GetVariableByName: proc "stdcall" (this: ^IShaderReflectionConstantBuffer, Name: cstring) -> ^IShaderReflectionVariable, +} + +IShaderReflection :: struct #raw_union { + #subtype iunknown: IUnknown, + using id3d12shaderreflection_vtable: ^IShaderReflection_VTable, +} +IShaderReflection_VTable :: struct { + using iunknown_vtable: IUnknown_VTable, + GetDesc: proc "stdcall" (this: ^IShaderReflection, pDesc: ^SHADER_DESC) -> HRESULT, + GetConstantBufferByIndex: proc "stdcall" (this: ^IShaderReflection, Index: u32) -> ^IShaderReflectionConstantBuffer, + GetConstantBufferByName: proc "stdcall" (this: ^IShaderReflection, Name: cstring) -> ^IShaderReflectionConstantBuffer, + GetResourceBindingDesc: proc "stdcall" (this: ^IShaderReflection, ResourceIndex: u32, pDesc: ^SHADER_INPUT_BIND_DESC) -> HRESULT, + GetInputParameterDesc: proc "stdcall" (this: ^IShaderReflection, ParameterIndex: u32, pDesc: ^SIGNATURE_PARAMETER_DESC) -> HRESULT, + GetOutputParameterDesc: proc "stdcall" (this: ^IShaderReflection, ParameterIndex: u32, pDesc: ^SIGNATURE_PARAMETER_DESC) -> HRESULT, + GetPatchConstantParameterDesc: proc "stdcall" (this: ^IShaderReflection, ParameterIndex: u32, pDesc: ^SIGNATURE_PARAMETER_DESC) -> HRESULT, + GetVariableByName: proc "stdcall" (this: ^IShaderReflection, Name: cstring) -> ^IShaderReflectionVariable, + GetResourceBindingDescByName: proc "stdcall" (this: ^IShaderReflection, Name: cstring, pDesc: ^SHADER_INPUT_BIND_DESC) -> HRESULT, + GetMovInstructionCount: proc "stdcall" (this: ^IShaderReflection) -> u32, + GetMovcInstructionCount: proc "stdcall" (this: ^IShaderReflection) -> u32, + GetConversionInstructionCount: proc "stdcall" (this: ^IShaderReflection) -> u32, + GetBitwiseInstructionCount: proc "stdcall" (this: ^IShaderReflection) -> u32, + GetGSInputPrimitive: proc "stdcall" (this: ^IShaderReflection) -> PRIMITIVE, + IsSampleFrequencyShader: proc "stdcall" (this: ^IShaderReflection) -> BOOL, + GetNumInterfaceSlots: proc "stdcall" (this: ^IShaderReflection) -> u32, + GetMinFeatureLevel: proc "stdcall" (this: ^IShaderReflection, pLevel: ^FEATURE_LEVEL) -> HRESULT, + GetThreadGroupSize: proc "stdcall" (this: ^IShaderReflection, pSizeX: ^u32, pSizeY: ^u32, pSizeZ: ^u32) -> u32, + GetRequiresFlags: proc "stdcall" (this: ^IShaderReflection) -> u64, +} + +ILibraryReflection :: struct #raw_union { + #subtype iunknown: IUnknown, + using id3d12libraryreflection_vtable: ^ILibraryReflection_VTable, +} +ILibraryReflection_VTable :: struct { + using iunknown_vtable: IUnknown_VTable, + GetDesc: proc "stdcall" (this: ^ILibraryReflection, pDesc: ^LIBRARY_DESC) -> HRESULT, + GetFunctionByIndex: proc "stdcall" (this: ^ILibraryReflection, FunctionIndex: i32) -> ^IFunctionReflection, +} + +IFunctionReflection :: struct { + vtable: ^IFunctionReflection_VTable, +} +IFunctionReflection_VTable :: struct { + GetDesc: proc "stdcall" (this: ^IFunctionReflection, pDesc: ^FUNCTION_DESC) -> HRESULT, + GetConstantBufferByIndex: proc "stdcall" (this: ^IFunctionReflection, BufferIndex: u32) -> ^IShaderReflectionConstantBuffer, + GetConstantBufferByName: proc "stdcall" (this: ^IFunctionReflection, Name: cstring) -> ^IShaderReflectionConstantBuffer, + GetResourceBindingDesc: proc "stdcall" (this: ^IFunctionReflection, ResourceIndex: u32, pDesc: ^SHADER_INPUT_BIND_DESC) -> HRESULT, + GetVariableByName: proc "stdcall" (this: ^IFunctionReflection, Name: cstring) -> ^IShaderReflectionVariable, + GetResourceBindingDescByName: proc "stdcall" (this: ^IFunctionReflection, Name: cstring, pDesc: ^SHADER_INPUT_BIND_DESC) -> HRESULT, + GetFunctionParameter: proc "stdcall" (this: ^IFunctionReflection, ParameterIndex: i32) -> ^IFunctionParameterReflection, +} + +IFunctionParameterReflection :: struct { + vtable: ^IFunctionParameterReflection_VTable, +} +IFunctionParameterReflection_VTable :: struct { + GetDesc: proc "stdcall" (this: ^IFunctionParameterReflection, pDesc: ^PARAMETER_DESC) -> HRESULT, +} diff --git a/vendor/directx/d3d12/d3d12_constants.odin b/vendor/directx/d3d12/d3d12_constants.odin new file mode 100644 index 000000000..a30296cc1 --- /dev/null +++ b/vendor/directx/d3d12/d3d12_constants.odin @@ -0,0 +1,532 @@ +package directx_d3d12 + +FL9_1_REQ_TEXTURE1D_U_DIMENSION :: 2048 +FL9_3_REQ_TEXTURE1D_U_DIMENSION :: 4096 +FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION :: 2048 +FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION :: 4096 +FL9_1_REQ_TEXTURECUBE_DIMENSION :: 512 +FL9_3_REQ_TEXTURECUBE_DIMENSION :: 4096 +FL9_1_REQ_TEXTURE3D_U_V_OR_W_DIMENSION :: 256 +FL9_1_DEFAULT_MAX_ANISOTROPY :: 2 +FL9_1_IA_PRIMITIVE_MAX_COUNT :: 65535 +FL9_2_IA_PRIMITIVE_MAX_COUNT :: 1048575 +FL9_1_SIMULTANEOUS_RENDER_TARGET_COUNT :: 1 +FL9_3_SIMULTANEOUS_RENDER_TARGET_COUNT :: 4 +FL9_1_MAX_TEXTURE_REPEAT :: 128 +FL9_2_MAX_TEXTURE_REPEAT :: 2048 +FL9_3_MAX_TEXTURE_REPEAT :: 8192 + +COMPONENT_MASK_X :: 1 +COMPONENT_MASK_Y :: 2 +COMPONENT_MASK_Z :: 4 +COMPONENT_MASK_W :: 8 + +_16BIT_INDEX_STRIP_CUT_VALUE :: 0xffff +_32BIT_INDEX_STRIP_CUT_VALUE :: 0xffffffff +_8BIT_INDEX_STRIP_CUT_VALUE :: 0xff + +APPEND_ALIGNED_ELEMENT :: 0xffffffff +ARRAY_AXIS_ADDRESS_RANGE_BIT_COUNT :: 9 + +CLIP_OR_CULL_DISTANCE_COUNT :: 8 +CLIP_OR_CULL_DISTANCE_ELEMENT_COUNT :: 2 + +COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT :: 14 +COMMONSHADER_CONSTANT_BUFFER_COMPONENTS :: 4 +COMMONSHADER_CONSTANT_BUFFER_COMPONENT_BIT_COUNT :: 32 +COMMONSHADER_CONSTANT_BUFFER_HW_SLOT_COUNT :: 15 +COMMONSHADER_CONSTANT_BUFFER_PARTIAL_UPDATE_EXTENTS_BYTE_ALIGNMENT :: 16 +COMMONSHADER_CONSTANT_BUFFER_REGISTER_COMPONENTS :: 4 +COMMONSHADER_CONSTANT_BUFFER_REGISTER_COUNT :: 15 +COMMONSHADER_CONSTANT_BUFFER_REGISTER_READS_PER_INST :: 1 +COMMONSHADER_CONSTANT_BUFFER_REGISTER_READ_PORTS :: 1 +COMMONSHADER_FLOWCONTROL_NESTING_LIMIT :: 64 +COMMONSHADER_IMMEDIATE_CONSTANT_BUFFER_REGISTER_COMPONENTS :: 4 +COMMONSHADER_IMMEDIATE_CONSTANT_BUFFER_REGISTER_COUNT :: 1 +COMMONSHADER_IMMEDIATE_CONSTANT_BUFFER_REGISTER_READS_PER_INST :: 1 +COMMONSHADER_IMMEDIATE_CONSTANT_BUFFER_REGISTER_READ_PORTS :: 1 +COMMONSHADER_IMMEDIATE_VALUE_COMPONENT_BIT_COUNT :: 32 +COMMONSHADER_INPUT_RESOURCE_REGISTER_COMPONENTS :: 1 +COMMONSHADER_INPUT_RESOURCE_REGISTER_COUNT :: 128 +COMMONSHADER_INPUT_RESOURCE_REGISTER_READS_PER_INST :: 1 +COMMONSHADER_INPUT_RESOURCE_REGISTER_READ_PORTS :: 1 +COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT :: 128 +COMMONSHADER_SAMPLER_REGISTER_COMPONENTS :: 1 +COMMONSHADER_SAMPLER_REGISTER_COUNT :: 16 +COMMONSHADER_SAMPLER_REGISTER_READS_PER_INST :: 1 +COMMONSHADER_SAMPLER_REGISTER_READ_PORTS :: 1 +COMMONSHADER_SAMPLER_SLOT_COUNT :: 16 +COMMONSHADER_SUBROUTINE_NESTING_LIMIT :: 32 +COMMONSHADER_TEMP_REGISTER_COMPONENTS :: 4 +COMMONSHADER_TEMP_REGISTER_COMPONENT_BIT_COUNT :: 32 +COMMONSHADER_TEMP_REGISTER_COUNT :: 4096 +COMMONSHADER_TEMP_REGISTER_READS_PER_INST :: 3 +COMMONSHADER_TEMP_REGISTER_READ_PORTS :: 3 +COMMONSHADER_TEXCOORD_RANGE_REDUCTION_MAX :: 10 +COMMONSHADER_TEXCOORD_RANGE_REDUCTION_MIN :: -10 +COMMONSHADER_TEXEL_OFFSET_MAX_NEGATIVE :: -8 +COMMONSHADER_TEXEL_OFFSET_MAX_POSITIVE :: 7 + +CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT :: 256 + +CS_4_X_BUCKET00_MAX_BYTES_TGSM_WRITABLE_PER_THREAD :: 256 +CS_4_X_BUCKET00_MAX_NUM_THREADS_PER_GROUP :: 64 +CS_4_X_BUCKET01_MAX_BYTES_TGSM_WRITABLE_PER_THREAD :: 240 +CS_4_X_BUCKET01_MAX_NUM_THREADS_PER_GROUP :: 68 +CS_4_X_BUCKET02_MAX_BYTES_TGSM_WRITABLE_PER_THREAD :: 224 +CS_4_X_BUCKET02_MAX_NUM_THREADS_PER_GROUP :: 72 +CS_4_X_BUCKET03_MAX_BYTES_TGSM_WRITABLE_PER_THREAD :: 208 +CS_4_X_BUCKET03_MAX_NUM_THREADS_PER_GROUP :: 76 +CS_4_X_BUCKET04_MAX_BYTES_TGSM_WRITABLE_PER_THREAD :: 192 +CS_4_X_BUCKET04_MAX_NUM_THREADS_PER_GROUP :: 84 +CS_4_X_BUCKET05_MAX_BYTES_TGSM_WRITABLE_PER_THREAD :: 176 +CS_4_X_BUCKET05_MAX_NUM_THREADS_PER_GROUP :: 92 +CS_4_X_BUCKET06_MAX_BYTES_TGSM_WRITABLE_PER_THREAD :: 160 +CS_4_X_BUCKET06_MAX_NUM_THREADS_PER_GROUP :: 100 +CS_4_X_BUCKET07_MAX_BYTES_TGSM_WRITABLE_PER_THREAD :: 144 +CS_4_X_BUCKET07_MAX_NUM_THREADS_PER_GROUP :: 112 +CS_4_X_BUCKET08_MAX_BYTES_TGSM_WRITABLE_PER_THREAD :: 128 +CS_4_X_BUCKET08_MAX_NUM_THREADS_PER_GROUP :: 128 +CS_4_X_BUCKET09_MAX_BYTES_TGSM_WRITABLE_PER_THREAD :: 112 +CS_4_X_BUCKET09_MAX_NUM_THREADS_PER_GROUP :: 144 +CS_4_X_BUCKET10_MAX_BYTES_TGSM_WRITABLE_PER_THREAD :: 96 +CS_4_X_BUCKET10_MAX_NUM_THREADS_PER_GROUP :: 168 +CS_4_X_BUCKET11_MAX_BYTES_TGSM_WRITABLE_PER_THREAD :: 80 +CS_4_X_BUCKET11_MAX_NUM_THREADS_PER_GROUP :: 204 +CS_4_X_BUCKET12_MAX_BYTES_TGSM_WRITABLE_PER_THREAD :: 64 +CS_4_X_BUCKET12_MAX_NUM_THREADS_PER_GROUP :: 256 +CS_4_X_BUCKET13_MAX_BYTES_TGSM_WRITABLE_PER_THREAD :: 48 +CS_4_X_BUCKET13_MAX_NUM_THREADS_PER_GROUP :: 340 +CS_4_X_BUCKET14_MAX_BYTES_TGSM_WRITABLE_PER_THREAD :: 32 +CS_4_X_BUCKET14_MAX_NUM_THREADS_PER_GROUP :: 512 +CS_4_X_BUCKET15_MAX_BYTES_TGSM_WRITABLE_PER_THREAD :: 16 +CS_4_X_BUCKET15_MAX_NUM_THREADS_PER_GROUP :: 768 +CS_4_X_DISPATCH_MAX_THREAD_GROUPS_IN_Z_DIMENSION :: 1 +CS_4_X_RAW_UAV_BYTE_ALIGNMENT :: 256 +CS_4_X_THREAD_GROUP_MAX_THREADS_PER_GROUP :: 768 +CS_4_X_THREAD_GROUP_MAX_X :: 768 +CS_4_X_THREAD_GROUP_MAX_Y :: 768 +CS_4_X_UAV_REGISTER_COUNT :: 1 + +CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION :: 65535 +CS_TGSM_REGISTER_COUNT :: 8192 +CS_TGSM_REGISTER_READS_PER_INST :: 1 +CS_TGSM_RESOURCE_REGISTER_COMPONENTS :: 1 +CS_TGSM_RESOURCE_REGISTER_READ_PORTS :: 1 +CS_THREADGROUPID_REGISTER_COMPONENTS :: 3 +CS_THREADGROUPID_REGISTER_COUNT :: 1 +CS_THREADIDINGROUPFLATTENED_REGISTER_COMPONENTS :: 1 +CS_THREADIDINGROUPFLATTENED_REGISTER_COUNT :: 1 +CS_THREADIDINGROUP_REGISTER_COMPONENTS :: 3 +CS_THREADIDINGROUP_REGISTER_COUNT :: 1 +CS_THREADID_REGISTER_COMPONENTS :: 3 +CS_THREADID_REGISTER_COUNT :: 1 +CS_THREAD_GROUP_MAX_THREADS_PER_GROUP :: 1024 +CS_THREAD_GROUP_MAX_X :: 1024 +CS_THREAD_GROUP_MAX_Y :: 1024 +CS_THREAD_GROUP_MAX_Z :: 64 +CS_THREAD_GROUP_MIN_X :: 1 +CS_THREAD_GROUP_MIN_Y :: 1 +CS_THREAD_GROUP_MIN_Z :: 1 +CS_THREAD_LOCAL_TEMP_REGISTER_POOL :: 16384 + +DEFAULT_BLEND_FACTOR_ALPHA :: 1.0 +DEFAULT_BLEND_FACTOR_BLUE :: 1.0 +DEFAULT_BLEND_FACTOR_GREEN :: 1.0 +DEFAULT_BLEND_FACTOR_RED :: 1.0 +DEFAULT_BORDER_COLOR_COMPONENT :: 0.0 +DEFAULT_DEPTH_BIAS :: 0 +DEFAULT_DEPTH_BIAS_CLAMP :: 0.0 +DEFAULT_MAX_ANISOTROPY :: 16 +DEFAULT_MIP_LOD_BIAS :: 0.0 +DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT :: 4194304 +DEFAULT_RENDER_TARGET_ARRAY_INDEX :: 0 + +DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT :: 65536 +DEFAULT_SAMPLE_MASK :: 0xffffffff +DEFAULT_SCISSOR_ENDX :: 0 +DEFAULT_SCISSOR_ENDY :: 0 +DEFAULT_SCISSOR_STARTX :: 0 +DEFAULT_SCISSOR_STARTY :: 0 +DEFAULT_SLOPE_SCALED_DEPTH_BIAS :: 0.0 +DEFAULT_STENCIL_READ_MASK :: 0xff +DEFAULT_STENCIL_REFERENCE :: 0 +DEFAULT_STENCIL_WRITE_MASK :: 0xff +DEFAULT_VIEWPORT_AND_SCISSORRECT_INDEX :: 0 +DEFAULT_VIEWPORT_HEIGHT :: 0 +DEFAULT_VIEWPORT_MAX_DEPTH :: 0.0 +DEFAULT_VIEWPORT_MIN_DEPTH :: 0.0 +DEFAULT_VIEWPORT_TOPLEFTX :: 0 +DEFAULT_VIEWPORT_TOPLEFTY :: 0 +DEFAULT_VIEWPORT_WIDTH :: 0 + +DESCRIPTOR_RANGE_OFFSET_APPEND :: 0xffffffff + +DRIVER_RESERVED_REGISTER_SPACE_VALUES_END :: 0xfffffff7 +DRIVER_RESERVED_REGISTER_SPACE_VALUES_START :: 0xfffffff0 + +DS_INPUT_CONTROL_POINTS_MAX_TOTAL_SCALARS :: 3968 +DS_INPUT_CONTROL_POINT_REGISTER_COMPONENTS :: 4 +DS_INPUT_CONTROL_POINT_REGISTER_COMPONENT_BIT_COUNT :: 32 +DS_INPUT_CONTROL_POINT_REGISTER_COUNT :: 32 +DS_INPUT_CONTROL_POINT_REGISTER_READS_PER_INST :: 2 +DS_INPUT_CONTROL_POINT_REGISTER_READ_PORTS :: 1 +DS_INPUT_DOMAIN_POINT_REGISTER_COMPONENTS :: 3 +DS_INPUT_DOMAIN_POINT_REGISTER_COMPONENT_BIT_COUNT :: 32 +DS_INPUT_DOMAIN_POINT_REGISTER_COUNT :: 1 +DS_INPUT_DOMAIN_POINT_REGISTER_READS_PER_INST :: 2 +DS_INPUT_DOMAIN_POINT_REGISTER_READ_PORTS :: 1 +DS_INPUT_PATCH_CONSTANT_REGISTER_COMPONENTS :: 4 +DS_INPUT_PATCH_CONSTANT_REGISTER_COMPONENT_BIT_COUNT :: 32 +DS_INPUT_PATCH_CONSTANT_REGISTER_COUNT :: 32 +DS_INPUT_PATCH_CONSTANT_REGISTER_READS_PER_INST :: 2 +DS_INPUT_PATCH_CONSTANT_REGISTER_READ_PORTS :: 1 +DS_INPUT_PRIMITIVE_ID_REGISTER_COMPONENTS :: 1 +DS_INPUT_PRIMITIVE_ID_REGISTER_COMPONENT_BIT_COUNT :: 32 +DS_INPUT_PRIMITIVE_ID_REGISTER_COUNT :: 1 +DS_INPUT_PRIMITIVE_ID_REGISTER_READS_PER_INST :: 2 +DS_INPUT_PRIMITIVE_ID_REGISTER_READ_PORTS :: 1 +DS_OUTPUT_REGISTER_COMPONENTS :: 4 +DS_OUTPUT_REGISTER_COMPONENT_BIT_COUNT :: 32 +DS_OUTPUT_REGISTER_COUNT :: 32 + +FLOAT16_FUSED_TOLERANCE_IN_ULP :: 0.6 +FLOAT32_MAX :: 3.402823466e+38 +FLOAT32_TO_INTEGER_TOLERANCE_IN_ULP :: 0.6 +FLOAT_TO_SRGB_EXPONENT_DENOMINATOR :: 2.4 +FLOAT_TO_SRGB_EXPONENT_NUMERATOR :: 1.0 +FLOAT_TO_SRGB_OFFSET :: 0.055 +FLOAT_TO_SRGB_SCALE_1 :: 12.92 +FLOAT_TO_SRGB_SCALE_2 :: 1.055 +FLOAT_TO_SRGB_THRESHOLD :: 0.0031308 +FTOI_INSTRUCTION_MAX_INPUT :: 2147483647.999 +FTOI_INSTRUCTION_MIN_INPUT :: -2147483648.999 +FTOU_INSTRUCTION_MAX_INPUT :: 4294967295.999 +FTOU_INSTRUCTION_MIN_INPUT :: 0.0 + +GS_INPUT_INSTANCE_ID_READS_PER_INST :: 2 +GS_INPUT_INSTANCE_ID_READ_PORTS :: 1 +GS_INPUT_INSTANCE_ID_REGISTER_COMPONENTS :: 1 +GS_INPUT_INSTANCE_ID_REGISTER_COMPONENT_BIT_COUNT :: 32 +GS_INPUT_INSTANCE_ID_REGISTER_COUNT :: 1 +GS_INPUT_PRIM_CONST_REGISTER_COMPONENTS :: 1 +GS_INPUT_PRIM_CONST_REGISTER_COMPONENT_BIT_COUNT :: 32 +GS_INPUT_PRIM_CONST_REGISTER_COUNT :: 1 +GS_INPUT_PRIM_CONST_REGISTER_READS_PER_INST :: 2 +GS_INPUT_PRIM_CONST_REGISTER_READ_PORTS :: 1 +GS_INPUT_REGISTER_COMPONENTS :: 4 +GS_INPUT_REGISTER_COMPONENT_BIT_COUNT :: 32 +GS_INPUT_REGISTER_COUNT :: 32 +GS_INPUT_REGISTER_READS_PER_INST :: 2 +GS_INPUT_REGISTER_READ_PORTS :: 1 +GS_INPUT_REGISTER_VERTICES :: 32 +GS_MAX_INSTANCE_COUNT :: 32 +GS_MAX_OUTPUT_VERTEX_COUNT_ACROSS_INSTANCES :: 1024 +GS_OUTPUT_ELEMENTS :: 32 +GS_OUTPUT_REGISTER_COMPONENTS :: 4 +GS_OUTPUT_REGISTER_COMPONENT_BIT_COUNT :: 32 +GS_OUTPUT_REGISTER_COUNT :: 32 + +HS_CONTROL_POINT_PHASE_INPUT_REGISTER_COUNT :: 32 +HS_CONTROL_POINT_PHASE_OUTPUT_REGISTER_COUNT :: 32 +HS_CONTROL_POINT_REGISTER_COMPONENTS :: 4 +HS_CONTROL_POINT_REGISTER_COMPONENT_BIT_COUNT :: 32 +HS_CONTROL_POINT_REGISTER_READS_PER_INST :: 2 +HS_CONTROL_POINT_REGISTER_READ_PORTS :: 1 +HS_FORK_PHASE_INSTANCE_COUNT_UPPER_BOUND :: 0xffffffff +HS_INPUT_FORK_INSTANCE_ID_REGISTER_COMPONENTS :: 1 +HS_INPUT_FORK_INSTANCE_ID_REGISTER_COMPONENT_BIT_COUNT :: 32 +HS_INPUT_FORK_INSTANCE_ID_REGISTER_COUNT :: 1 +HS_INPUT_FORK_INSTANCE_ID_REGISTER_READS_PER_INST :: 2 +HS_INPUT_FORK_INSTANCE_ID_REGISTER_READ_PORTS :: 1 +HS_INPUT_JOIN_INSTANCE_ID_REGISTER_COMPONENTS :: 1 +HS_INPUT_JOIN_INSTANCE_ID_REGISTER_COMPONENT_BIT_COUNT :: 32 +HS_INPUT_JOIN_INSTANCE_ID_REGISTER_COUNT :: 1 +HS_INPUT_JOIN_INSTANCE_ID_REGISTER_READS_PER_INST :: 2 +HS_INPUT_JOIN_INSTANCE_ID_REGISTER_READ_PORTS :: 1 +HS_INPUT_PRIMITIVE_ID_REGISTER_COMPONENTS :: 1 +HS_INPUT_PRIMITIVE_ID_REGISTER_COMPONENT_BIT_COUNT :: 32 +HS_INPUT_PRIMITIVE_ID_REGISTER_COUNT :: 1 +HS_INPUT_PRIMITIVE_ID_REGISTER_READS_PER_INST :: 2 +HS_INPUT_PRIMITIVE_ID_REGISTER_READ_PORTS :: 1 +HS_JOIN_PHASE_INSTANCE_COUNT_UPPER_BOUND :: 0xffffffff +HS_MAXTESSFACTOR_LOWER_BOUND :: 1.0 +HS_MAXTESSFACTOR_UPPER_BOUND :: 64.0 +HS_OUTPUT_CONTROL_POINTS_MAX_TOTAL_SCALARS :: 3968 +HS_OUTPUT_CONTROL_POINT_ID_REGISTER_COMPONENTS :: 1 +HS_OUTPUT_CONTROL_POINT_ID_REGISTER_COMPONENT_BIT_COUNT :: 32 +HS_OUTPUT_CONTROL_POINT_ID_REGISTER_COUNT :: 1 +HS_OUTPUT_CONTROL_POINT_ID_REGISTER_READS_PER_INST :: 2 +HS_OUTPUT_CONTROL_POINT_ID_REGISTER_READ_PORTS :: 1 +HS_OUTPUT_PATCH_CONSTANT_REGISTER_COMPONENTS :: 4 +HS_OUTPUT_PATCH_CONSTANT_REGISTER_COMPONENT_BIT_COUNT :: 32 +HS_OUTPUT_PATCH_CONSTANT_REGISTER_COUNT :: 32 +HS_OUTPUT_PATCH_CONSTANT_REGISTER_READS_PER_INST :: 2 +HS_OUTPUT_PATCH_CONSTANT_REGISTER_READ_PORTS :: 1 +HS_OUTPUT_PATCH_CONSTANT_REGISTER_SCALAR_COMPONENTS :: 128 + +IA_DEFAULT_INDEX_BUFFER_OFFSET_IN_BYTES :: 0 +IA_DEFAULT_PRIMITIVE_TOPOLOGY :: 0 +IA_DEFAULT_VERTEX_BUFFER_OFFSET_IN_BYTES :: 0 +IA_INDEX_INPUT_RESOURCE_SLOT_COUNT :: 1 +IA_INSTANCE_ID_BIT_COUNT :: 32 +IA_INTEGER_ARITHMETIC_BIT_COUNT :: 32 +IA_PATCH_MAX_CONTROL_POINT_COUNT :: 32 +IA_PRIMITIVE_ID_BIT_COUNT :: 32 +IA_VERTEX_ID_BIT_COUNT :: 32 +IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT :: 32 +IA_VERTEX_INPUT_STRUCTURE_ELEMENTS_COMPONENTS :: 128 +IA_VERTEX_INPUT_STRUCTURE_ELEMENT_COUNT :: 32 + +INTEGER_DIVIDE_BY_ZERO_QUOTIENT :: 0xffffffff +INTEGER_DIVIDE_BY_ZERO_REMAINDER :: 0xffffffff + +KEEP_RENDER_TARGETS_AND_DEPTH_STENCIL :: 0xffffffff +KEEP_UNORDERED_ACCESS_VIEWS :: 0xffffffff + +LINEAR_GAMMA :: 1.0 +MAJOR_VERSION :: 12 + +MAX_BORDER_COLOR_COMPONENT :: 1.0 +MAX_DEPTH :: 1.0 +MAX_LIVE_STATIC_SAMPLERS :: 2032 +MAX_MAXANISOTROPY :: 16 +MAX_MULTISAMPLE_SAMPLE_COUNT :: 32 +MAX_POSITION_VALUE :: 3.402823466e+34 +MAX_ROOT_COST :: 64 +MAX_SHADER_VISIBLE_DESCRIPTOR_HEAP_SIZE_TIER_1 :: 1000000 +MAX_SHADER_VISIBLE_DESCRIPTOR_HEAP_SIZE_TIER_2 :: 1000000 +MAX_SHADER_VISIBLE_SAMPLER_HEAP_SIZE :: 2048 +MAX_TEXTURE_DIMENSION_2_TO_EXP :: 17 +MAX_VIEW_INSTANCE_COUNT :: 4 + +MINOR_VERSION :: 0 + +MIN_BORDER_COLOR_COMPONENT :: 0.0 +MIN_DEPTH :: 0.0 +MIN_MAXANISOTROPY :: 0 + +MIP_LOD_BIAS_MAX :: 15.99 +MIP_LOD_BIAS_MIN :: -16.0 +MIP_LOD_FRACTIONAL_BIT_COUNT :: 8 +MIP_LOD_RANGE_BIT_COUNT :: 8 + +MULTISAMPLE_ANTIALIAS_LINE_WIDTH :: 1.4 +NONSAMPLE_FETCH_OUT_OF_RANGE_ACCESS_RESULT :: 0 + +OS_RESERVED_REGISTER_SPACE_VALUES_END :: 0xffffffff +OS_RESERVED_REGISTER_SPACE_VALUES_START :: 0xfffffff8 + +PACKED_TILE :: 0xffffffff + +PIXEL_ADDRESS_RANGE_BIT_COUNT :: 15 + +PRE_SCISSOR_PIXEL_ADDRESS_RANGE_BIT_COUNT :: 16 + +PS_CS_UAV_REGISTER_COMPONENTS :: 1 +PS_CS_UAV_REGISTER_COUNT :: 8 +PS_CS_UAV_REGISTER_READS_PER_INST :: 1 +PS_CS_UAV_REGISTER_READ_PORTS :: 1 +PS_FRONTFACING_DEFAULT_VALUE :: 0xffffffff +PS_FRONTFACING_FALSE_VALUE :: 0 +PS_FRONTFACING_TRUE_VALUE :: 0xffffffff +PS_INPUT_REGISTER_COMPONENTS :: 4 +PS_INPUT_REGISTER_COMPONENT_BIT_COUNT :: 32 +PS_INPUT_REGISTER_COUNT :: 32 +PS_INPUT_REGISTER_READS_PER_INST :: 2 +PS_INPUT_REGISTER_READ_PORTS :: 1 +PS_LEGACY_PIXEL_CENTER_FRACTIONAL_COMPONENT :: 0.0 +PS_OUTPUT_DEPTH_REGISTER_COMPONENTS :: 1 +PS_OUTPUT_DEPTH_REGISTER_COMPONENT_BIT_COUNT :: 32 +PS_OUTPUT_DEPTH_REGISTER_COUNT :: 1 +PS_OUTPUT_MASK_REGISTER_COMPONENTS :: 1 +PS_OUTPUT_MASK_REGISTER_COMPONENT_BIT_COUNT :: 32 +PS_OUTPUT_MASK_REGISTER_COUNT :: 1 +PS_OUTPUT_REGISTER_COMPONENTS :: 4 +PS_OUTPUT_REGISTER_COMPONENT_BIT_COUNT :: 32 +PS_OUTPUT_REGISTER_COUNT :: 8 +PS_PIXEL_CENTER_FRACTIONAL_COMPONENT :: 0.5 + +RAW_UAV_SRV_BYTE_ALIGNMENT :: 16 + +RAYTRACING_AABB_BYTE_ALIGNMENT :: 8 +RAYTRACING_ACCELERATION_STRUCTURE_BYTE_ALIGNMENT :: 256 +RAYTRACING_INSTANCE_DESCS_BYTE_ALIGNMENT :: 16 +RAYTRACING_MAX_ATTRIBUTE_SIZE_IN_BYTES :: 32 +RAYTRACING_MAX_DECLARABLE_TRACE_RECURSION_DEPTH :: 31 +RAYTRACING_MAX_GEOMETRIES_PER_BOTTOM_LEVEL_ACCELERATION_STRUCTURE :: 16777216 +RAYTRACING_MAX_INSTANCES_PER_TOP_LEVEL_ACCELERATION_STRUCTURE :: 16777216 +RAYTRACING_MAX_PRIMITIVES_PER_BOTTOM_LEVEL_ACCELERATION_STRUCTURE :: 536870912 +RAYTRACING_MAX_RAY_GENERATION_SHADER_THREADS :: 1073741824 +RAYTRACING_MAX_SHADER_RECORD_STRIDE :: 4096 +RAYTRACING_SHADER_RECORD_BYTE_ALIGNMENT :: 32 +RAYTRACING_SHADER_TABLE_BYTE_ALIGNMENT :: 64 +RAYTRACING_TRANSFORM3X4_BYTE_ALIGNMENT :: 16 + +REQ_BLEND_OBJECT_COUNT_PER_DEVICE :: 4096 +REQ_BUFFER_RESOURCE_TEXEL_COUNT_2_TO_EXP :: 27 +REQ_CONSTANT_BUFFER_ELEMENT_COUNT :: 4096 +REQ_DEPTH_STENCIL_OBJECT_COUNT_PER_DEVICE :: 4096 +REQ_DRAWINDEXED_INDEX_COUNT_2_TO_EXP :: 32 +REQ_DRAW_VERTEX_COUNT_2_TO_EXP :: 32 +REQ_FILTERING_HW_ADDRESSABLE_RESOURCE_DIMENSION :: 16384 +REQ_GS_INVOCATION_32BIT_OUTPUT_COMPONENT_LIMIT :: 1024 +REQ_IMMEDIATE_CONSTANT_BUFFER_ELEMENT_COUNT :: 4096 +REQ_MAXANISOTROPY :: 16 +REQ_MIP_LEVELS :: 15 +REQ_MULTI_ELEMENT_STRUCTURE_SIZE_IN_BYTES :: 2048 +REQ_RASTERIZER_OBJECT_COUNT_PER_DEVICE :: 4096 +REQ_RENDER_TO_BUFFER_WINDOW_WIDTH :: 16384 +REQ_RESOURCE_SIZE_IN_MEGABYTES_EXPRESSION_A_TERM :: 128 +REQ_RESOURCE_SIZE_IN_MEGABYTES_EXPRESSION_B_TERM :: 0.25 +REQ_RESOURCE_SIZE_IN_MEGABYTES_EXPRESSION_C_TERM :: 2048 +REQ_RESOURCE_VIEW_COUNT_PER_DEVICE_2_TO_EXP :: 20 +REQ_SAMPLER_OBJECT_COUNT_PER_DEVICE :: 4096 +REQ_SUBRESOURCES :: 30720 +REQ_TEXTURE1D_ARRAY_AXIS_DIMENSION :: 2048 +REQ_TEXTURE1D_U_DIMENSION :: 16384 +REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION :: 2048 +REQ_TEXTURE2D_U_OR_V_DIMENSION :: 16384 +REQ_TEXTURE3D_U_V_OR_W_DIMENSION :: 2048 +REQ_TEXTURECUBE_DIMENSION :: 16384 + +RESINFO_INSTRUCTION_MISSING_COMPONENT_RETVAL :: 0 + +RESOURCE_BARRIER_ALL_SUBRESOURCES :: 0xffffffff + +RS_SET_SHADING_RATE_COMBINER_COUNT :: 2 + +SHADER_IDENTIFIER_SIZE_IN_BYTES :: 32 +SHADER_MAJOR_VERSION :: 5 +SHADER_MAX_INSTANCES :: 65535 +SHADER_MAX_INTERFACES :: 253 +SHADER_MAX_INTERFACE_CALL_SITES :: 4096 +SHADER_MAX_TYPES :: 65535 +SHADER_MINOR_VERSION :: 1 + +SHIFT_INSTRUCTION_PAD_VALUE :: 0 +SHIFT_INSTRUCTION_SHIFT_VALUE_BIT_COUNT :: 5 + +SIMULTANEOUS_RENDER_TARGET_COUNT :: 8 + +SMALL_MSAA_RESOURCE_PLACEMENT_ALIGNMENT :: 65536 +SMALL_RESOURCE_PLACEMENT_ALIGNMENT :: 4096 + +SO_BUFFER_MAX_STRIDE_IN_BYTES :: 2048 +SO_BUFFER_MAX_WRITE_WINDOW_IN_BYTES :: 512 +SO_BUFFER_SLOT_COUNT :: 4 +SO_DDI_REGISTER_INDEX_DENOTING_GAP :: 0xffffffff +SO_NO_RASTERIZED_STREAM :: 0xffffffff +SO_OUTPUT_COMPONENT_COUNT :: 128 +SO_STREAM_COUNT :: 4 + +SPEC_DATE_DAY :: 14 +SPEC_DATE_MONTH :: 11 +SPEC_DATE_YEAR :: 2014 +SPEC_VERSION :: 1.16 + +SRGB_GAMMA :: 2.2 +SRGB_TO_FLOAT_DENOMINATOR_1 :: 12.92 +SRGB_TO_FLOAT_DENOMINATOR_2 :: 1.055 +SRGB_TO_FLOAT_EXPONENT :: 2.4 +SRGB_TO_FLOAT_OFFSET :: 0.055 +SRGB_TO_FLOAT_THRESHOLD :: 0.04045 +SRGB_TO_FLOAT_TOLERANCE_IN_ULP :: 0.5 + +STANDARD_COMPONENT_BIT_COUNT :: 32 +STANDARD_COMPONENT_BIT_COUNT_DOUBLED :: 64 +STANDARD_MAXIMUM_ELEMENT_ALIGNMENT_BYTE_MULTIPLE :: 4 +STANDARD_PIXEL_COMPONENT_COUNT :: 128 +STANDARD_PIXEL_ELEMENT_COUNT :: 32 +STANDARD_VECTOR_SIZE :: 4 +STANDARD_VERTEX_ELEMENT_COUNT :: 32 +STANDARD_VERTEX_TOTAL_COMPONENT_COUNT :: 64 + +SUBPIXEL_FRACTIONAL_BIT_COUNT :: 8 +SUBTEXEL_FRACTIONAL_BIT_COUNT :: 8 + +SYSTEM_RESERVED_REGISTER_SPACE_VALUES_END :: 0xffffffff +SYSTEM_RESERVED_REGISTER_SPACE_VALUES_START :: 0xfffffff0 + +TESSELLATOR_MAX_EVEN_TESSELLATION_FACTOR :: 64 +TESSELLATOR_MAX_ISOLINE_DENSITY_TESSELLATION_FACTOR :: 64 +TESSELLATOR_MAX_ODD_TESSELLATION_FACTOR :: 63 +TESSELLATOR_MAX_TESSELLATION_FACTOR :: 64 +TESSELLATOR_MIN_EVEN_TESSELLATION_FACTOR :: 2 +TESSELLATOR_MIN_ISOLINE_DENSITY_TESSELLATION_FACTOR :: 1 +TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR :: 1 + +TEXEL_ADDRESS_RANGE_BIT_COUNT :: 16 + +TEXTURE_DATA_PITCH_ALIGNMENT :: 256 +TEXTURE_DATA_PLACEMENT_ALIGNMENT :: 512 + +TILED_RESOURCE_TILE_SIZE_IN_BYTES :: 65536 + +TRACKED_WORKLOAD_MAX_INSTANCES :: 32 + +UAV_COUNTER_PLACEMENT_ALIGNMENT :: 4096 +UAV_SLOT_COUNT :: 64 + +UNBOUND_MEMORY_ACCESS_RESULT :: 0 + +VIDEO_DECODE_MAX_ARGUMENTS :: 10 +VIDEO_DECODE_MAX_HISTOGRAM_COMPONENTS :: 4 +VIDEO_DECODE_MIN_BITSTREAM_OFFSET_ALIGNMENT :: 256 +VIDEO_DECODE_MIN_HISTOGRAM_OFFSET_ALIGNMENT :: 256 +VIDEO_DECODE_STATUS_MACROBLOCKS_AFFECTED_UNKNOWN :: 0xffffffff +VIDEO_PROCESS_MAX_FILTERS :: 32 +VIDEO_PROCESS_STEREO_VIEWS :: 2 + +VIEWPORT_AND_SCISSORRECT_MAX_INDEX :: 15 +VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE :: 16 +VIEWPORT_BOUNDS_MAX :: 32767 +VIEWPORT_BOUNDS_MIN :: -32768 + +VS_INPUT_REGISTER_COMPONENTS :: 4 +VS_INPUT_REGISTER_COMPONENT_BIT_COUNT :: 32 +VS_INPUT_REGISTER_COUNT :: 32 +VS_INPUT_REGISTER_READS_PER_INST :: 2 +VS_INPUT_REGISTER_READ_PORTS :: 1 +VS_OUTPUT_REGISTER_COMPONENTS :: 4 +VS_OUTPUT_REGISTER_COMPONENT_BIT_COUNT :: 32 +VS_OUTPUT_REGISTER_COUNT :: 32 + +WHQL_CONTEXT_COUNT_FOR_RESOURCE_LIMIT :: 10 +WHQL_DRAWINDEXED_INDEX_COUNT_2_TO_EXP :: 25 +WHQL_DRAW_VERTEX_COUNT_2_TO_EXP :: 25 + +SHADER_COMPONENT_MAPPING_MASK :: 0x7 +SHADER_COMPONENT_MAPPING_SHIFT :: 3 + +FILTER_REDUCTION_TYPE_MASK :: 0x3 +FILTER_REDUCTION_TYPE_SHIFT :: 7 +FILTER_TYPE_MASK :: 0x3 + +MIN_FILTER_SHIFT :: 4 +MAG_FILTER_SHIFT :: 2 +MIP_FILTER_SHIFT :: 0 + +ANISOTROPIC_FILTERING_BIT :: 0x40 + +INFO_QUEUE_DEFAULT_MESSAGE_COUNT_LIMIT :: 1024 + +SHADING_RATE_X_AXIS_SHIFT :: 2 +SHADING_RATE_VALID_MASK :: 3 + +RETURN_PARAMETER_INDEX :: -1 + +SHADER_REQUIRES_DOUBLES :: 0x00000001 +SHADER_REQUIRES_EARLY_DEPTH_STENCIL :: 0x00000002 +SHADER_REQUIRES_UAVS_AT_EVERY_STAGE :: 0x00000004 +SHADER_REQUIRES_64_UAVS :: 0x00000008 +SHADER_REQUIRES_MINIMUM_PRECISION :: 0x00000010 +SHADER_REQUIRES_11_1_DOUBLE_EXTENSIONS :: 0x00000020 +SHADER_REQUIRES_11_1_SHADER_EXTENSIONS :: 0x00000040 +SHADER_REQUIRES_LEVEL_9_COMPARISON_FILTERING :: 0x00000080 +SHADER_REQUIRES_TILED_RESOURCES :: 0x00000100 +SHADER_REQUIRES_STENCIL_REF :: 0x00000200 +SHADER_REQUIRES_INNER_COVERAGE :: 0x00000400 +SHADER_REQUIRES_TYPED_UAV_LOAD_ADDITIONAL_FORMATS :: 0x00000800 +SHADER_REQUIRES_ROVS :: 0x00001000 +SHADER_REQUIRES_VIEWPORT_AND_RT_ARRAY_INDEX_FROM_ANY_SHADER_FEEDING_RASTERIZER :: 0x00002000 diff --git a/vendor/directx/d3d_compiler/d3d_compiler.odin b/vendor/directx/d3d_compiler/d3d_compiler.odin new file mode 100644 index 000000000..2506f239c --- /dev/null +++ b/vendor/directx/d3d_compiler/d3d_compiler.odin @@ -0,0 +1,228 @@ +package directx_d3d_compiler + +foreign import d3dcompiler "d3dcompiler_47.lib" + +D3DCOMPILER_DLL_A :: "d3dcompiler_47.dll" +COMPILER_VERSION :: 47 + + +import "../dxgi" + +BOOL :: dxgi.BOOL +IID :: dxgi.IID +SIZE_T :: dxgi.SIZE_T +HRESULT :: dxgi.HRESULT +IUnknown :: dxgi.IUnknown +IUnknown_VTable :: dxgi.IUnknown_VTable + +@(default_calling_convention="stdcall", link_prefix="D3D") +foreign d3dcompiler { + ReadFileToBlob :: proc(pFileName: [^]u16, ppContents: ^^ID3DBlob) -> HRESULT --- + WriteBlobToFile :: proc(pBlob: ^ID3DBlob, pFileName: [^]u16, bOverwrite: BOOL) -> HRESULT --- + Compile :: proc(pSrcData: rawptr, SrcDataSize: SIZE_T, pSourceName: cstring, pDefines: ^SHADER_MACRO, pInclude: ^ID3DInclude, pEntrypoint: cstring, pTarget: cstring, Flags1: u32, Flags2: u32, ppCode: ^^ID3DBlob, ppErrorMsgs: ^^ID3DBlob) -> HRESULT --- + Compile2 :: proc(pSrcData: rawptr, SrcDataSize: SIZE_T, pSourceName: cstring, pDefines: ^SHADER_MACRO, pInclude: ^ID3DInclude, pEntrypoint: cstring, pTarget: cstring, Flags1: u32, Flags2: u32, SecondaryDataFlags: u32, pSecondaryData: rawptr, SecondaryDataSize: SIZE_T, ppCode: ^^ID3DBlob, ppErrorMsgs: ^^ID3DBlob) -> HRESULT --- + CompileFromFile :: proc(pFileName: [^]u16, pDefines: ^SHADER_MACRO, pInclude: ^ID3DInclude, pEntrypoint: cstring, pTarget: cstring, Flags1: u32, Flags2: u32, ppCode: ^^ID3DBlob, ppErrorMsgs: ^^ID3DBlob) -> HRESULT --- + Preprocess :: proc(pSrcData: rawptr, SrcDataSize: SIZE_T, pSourceName: cstring, pDefines: ^SHADER_MACRO, pInclude: ^ID3DInclude, ppCodeText: ^^ID3DBlob, ppErrorMsgs: ^^ID3DBlob) -> HRESULT --- + GetDebugInfo :: proc(pSrcData: rawptr, SrcDataSize: SIZE_T, ppDebugInfo: ^^ID3DBlob) -> HRESULT --- + Reflect :: proc(pSrcData: rawptr, SrcDataSize: SIZE_T, pInterface: ^IID, ppReflector: ^rawptr) -> HRESULT --- + ReflectLibrary :: proc(pSrcData: rawptr, SrcDataSize: SIZE_T, riid: ^IID, ppReflector: ^rawptr) -> HRESULT --- + Disassemble :: proc(pSrcData: rawptr, SrcDataSize: SIZE_T, Flags: u32, szComments: cstring, ppDisassembly: ^^ID3DBlob) -> HRESULT --- + DisassembleRegion :: proc(pSrcData: rawptr, SrcDataSize: SIZE_T, Flags: u32, szComments: cstring, StartByteOffset: SIZE_T, NumInsts: SIZE_T, pFinishByteOffset: ^SIZE_T, ppDisassembly: ^^ID3DBlob) -> HRESULT --- + CreateLinker :: proc(ppLinker: ^^ID3D11Linker) -> HRESULT --- + LoadModule :: proc(pSrcData: rawptr, cbSrcDataSize: SIZE_T, ppModule: ^^ID3D11Module) -> HRESULT --- + GetTraceInstructionOffsets :: proc(pSrcData: rawptr, SrcDataSize: SIZE_T, Flags: u32, StartInstIndex: SIZE_T, NumInsts: SIZE_T, pOffsets: ^SIZE_T, pTotalInsts: ^SIZE_T) -> HRESULT --- + GetInputSignatureBlob :: proc(pSrcData: rawptr, SrcDataSize: SIZE_T, ppSignatureBlob: ^^ID3DBlob) -> HRESULT --- + GetOutputSignatureBlob :: proc(pSrcData: rawptr, SrcDataSize: SIZE_T, ppSignatureBlob: ^^ID3DBlob) -> HRESULT --- + GetInputAndOutputSignatureBlob :: proc(pSrcData: rawptr, SrcDataSize: SIZE_T, ppSignatureBlob: ^^ID3DBlob) -> HRESULT --- + StripShader :: proc(pShaderBytecode: rawptr, BytecodeLength: SIZE_T, uStripFlags: u32, ppStrippedBlob: ^^ID3DBlob) -> HRESULT --- + GetBlobPart :: proc(pSrcData: rawptr, SrcDataSize: SIZE_T, Part: BLOB_PART, Flags: u32, ppPart: ^^ID3DBlob) -> HRESULT --- + SetBlobPart :: proc(pSrcData: rawptr, SrcDataSize: SIZE_T, Part: BLOB_PART, Flags: u32, pPart: rawptr, PartSize: SIZE_T, ppNewShader: ^^ID3DBlob) -> HRESULT --- + CreateBlob :: proc(Size: SIZE_T, ppBlob: ^^ID3DBlob) -> HRESULT --- + CompressShaders :: proc(uNumShaders: u32, pShaderData: ^SHADER_DATA, uFlags: u32, ppCompressedData: ^^ID3DBlob) -> HRESULT --- + DecompressShaders :: proc(pSrcData: rawptr, SrcDataSize: SIZE_T, uNumShaders: u32, uStartIndex: u32, pIndices: ^u32, uFlags: u32, ppShaders: ^^ID3DBlob, pTotalShaders: ^u32) -> HRESULT --- + Disassemble10Effect :: proc(pEffect: ^ID3D10Effect, Flags: u32, ppDisassembly: ^^ID3DBlob) -> HRESULT --- +} + + + +D3DCOMPILE :: enum u32 { // TODO: make bit_field + DEBUG = 1 << 0, + SKIP_VALIDATION = 1 << 1, + SKIP_OPTIMIZATION = 1 << 2, + PACK_MATRIX_ROW_MAJOR = 1 << 3, + PACK_MATRIX_COLUMN_MAJOR = 1 << 4, + PARTIAL_PRECISION = 1 << 5, + FORCE_VS_SOFTWARE_NO_OPT = 1 << 6, + FORCE_PS_SOFTWARE_NO_OPT = 1 << 7, + NO_PRESHADER = 1 << 8, + AVOID_FLOW_CONTROL = 1 << 9, + PREFER_FLOW_CONTROL = 1 << 10, + ENABLE_STRICTNESS = 1 << 11, + ENABLE_BACKWARDS_COMPATIBILITY = 1 << 12, + IEEE_STRICTNESS = 1 << 13, + OPTIMIZATION_LEVEL0 = 1 << 14, + OPTIMIZATION_LEVEL1 = 0, + OPTIMIZATION_LEVEL2 = (1 << 14)|(1 << 15), // Added manually + OPTIMIZATION_LEVEL3 = 1 << 15, + RESERVED16 = 1 << 16, + RESERVED17 = 1 << 17, + WARNINGS_ARE_ERRORS = 1 << 18, + RESOURCES_MAY_ALIAS = 1 << 19, + ENABLE_UNBOUNDED_DESCRIPTOR_TABLES = 1 << 20, + ALL_RESOURCES_BOUND = 1 << 21, + DEBUG_NAME_FOR_SOURCE = 1 << 22, + DEBUG_NAME_FOR_BINARY = 1 << 23, +} + +EFFECT :: enum u32 { // TODO: make bit_field + CHILD_EFFECT = 1 << 0, + ALLOW_SLOW_OPS = 1 << 1, +} + +FLAGS2 :: enum u32 { // TODO: make bit_field + FORCE_ROOT_SIGNATURE_LATEST = 0, + FORCE_ROOT_SIGNATURE_1_0 = 1 << 4, + FORCE_ROOT_SIGNATURE_1_1 = 1 << 5, +} + +SECDATA :: enum u32 { // TODO: make bit_field + MERGE_UAV_SLOTS = 0x00000001, + PRESERVE_TEMPLATE_SLOTS = 0x00000002, + REQUIRE_TEMPLATE_MATCH = 0x00000004, +} + +DISASM_ENABLE_COLOR_CODE :: 0x00000001 +DISASM_ENABLE_DEFAULT_VALUE_PRINTS :: 0x00000002 +DISASM_ENABLE_INSTRUCTION_NUMBERING :: 0x00000004 +DISASM_ENABLE_INSTRUCTION_CYCLE :: 0x00000008 +DISASM_DISABLE_DEBUG_INFO :: 0x00000010 +DISASM_ENABLE_INSTRUCTION_OFFSET :: 0x00000020 +DISASM_INSTRUCTION_ONLY :: 0x00000040 +DISASM_PRINT_HEX_LITERALS :: 0x00000080 + +GET_INST_OFFSETS_INCLUDE_NON_EXECUTABLE :: 0x00000001 + +COMPRESS_SHADER_KEEP_ALL_PARTS :: 0x00000001 + +SHADER_MACRO :: struct { + Name: cstring, + Definition: cstring, +} + +ID3D10Blob_UUID_STRING :: "8BA5FB08-5195-40E2-AC58-0D989C3A0102" +ID3D10Blob_UUID := &IID{0x8BA5FB08, 0x5195, 0x40E2, {0xAC, 0x58, 0x0D, 0x98, 0x9C, 0x3A, 0x01, 0x02}} +ID3D10Blob :: struct #raw_union { + #subtype iunknown: IUnknown, + using id3d10blob_vtable: ^ID3D10Blob_VTable, +} +ID3D10Blob_VTable :: struct { + using iunknown_vtable: IUnknown_VTable, + GetBufferPointer: proc "stdcall" (this: ^ID3D10Blob) -> rawptr, + GetBufferSize: proc "stdcall" (this: ^ID3D10Blob) -> SIZE_T, +} + + +ID3DBlob :: ID3D10Blob +ID3DBlob_VTable :: ID3D10Blob_VTable + + +INCLUDE_TYPE :: enum i32 { + INCLUDE_LOCAL = 0, + INCLUDE_SYSTEM = 1, + _10_INCLUDE_LOCAL = 0, + _10_INCLUDE_SYSTEM = 1, + INCLUDE_FORCE_DWORD = 2147483647, +} + +ID3DInclude :: struct { + vtable: ^ID3DInclude_VTable, +} +ID3DInclude_VTable :: struct { + Open: proc "stdcall" (this: ^ID3DInclude, IncludeType: INCLUDE_TYPE, pFileName: cstring, pParentData: rawptr, ppData: ^rawptr, pBytes: ^u32) -> HRESULT, + Close: proc "stdcall" (this: ^ID3DInclude, pData: rawptr) -> HRESULT, +} + + +ID3D11Module :: struct #raw_union { + #subtype iunknown: IUnknown, + using id3d11module_vtable: ^ID3D11Module_VTable, +} +ID3D11Module_VTable :: struct { + using iunknown_vtable: IUnknown_VTable, + CreateInstance: proc "stdcall" (this: ^ID3D11Module, pNamespace: cstring, ppModuleInstance: ^^ID3D11ModuleInstance) -> HRESULT, +} + + +ID3D11ModuleInstance :: struct #raw_union { + #subtype iunknown: IUnknown, + using id3d11moduleinstance_vtable: ^ID3D11ModuleInstance_VTable, +} +ID3D11ModuleInstance_VTable :: struct { + using iunknown_vtable: IUnknown_VTable, + BindConstantBuffer: proc "stdcall" (this: ^ID3D11ModuleInstance, uSrcSlot: u32, uDstSlot: u32, cbDstOffset: u32) -> HRESULT, + BindConstantBufferByName: proc "stdcall" (this: ^ID3D11ModuleInstance, pName: cstring, uDstSlot: u32, cbDstOffset: u32) -> HRESULT, + BindResource: proc "stdcall" (this: ^ID3D11ModuleInstance, uSrcSlot: u32, uDstSlot: u32, uCount: u32) -> HRESULT, + BindResourceByName: proc "stdcall" (this: ^ID3D11ModuleInstance, pName: cstring, uDstSlot: u32, uCount: u32) -> HRESULT, + BindSampler: proc "stdcall" (this: ^ID3D11ModuleInstance, uSrcSlot: u32, uDstSlot: u32, uCount: u32) -> HRESULT, + BindSamplerByName: proc "stdcall" (this: ^ID3D11ModuleInstance, pName: cstring, uDstSlot: u32, uCount: u32) -> HRESULT, + BindUnorderedAccessView: proc "stdcall" (this: ^ID3D11ModuleInstance, uSrcSlot: u32, uDstSlot: u32, uCount: u32) -> HRESULT, + BindUnorderedAccessViewByName: proc "stdcall" (this: ^ID3D11ModuleInstance, pName: cstring, uDstSlot: u32, uCount: u32) -> HRESULT, + BindResourceAsUnorderedAccessView: proc "stdcall" (this: ^ID3D11ModuleInstance, uSrcSrvSlot: u32, uDstUavSlot: u32, uCount: u32) -> HRESULT, + BindResourceAsUnorderedAccessViewByName: proc "stdcall" (this: ^ID3D11ModuleInstance, pSrvName: cstring, uDstUavSlot: u32, uCount: u32) -> HRESULT, +} + + +ID3D11Linker :: struct #raw_union { + #subtype iunknown: IUnknown, + using id3d11linker_vtable: ^ID3D11Linker_VTable, +} +ID3D11Linker_VTable :: struct { + using iunknown_vtable: IUnknown_VTable, + Link: proc "stdcall" (this: ^ID3D11Linker, pEntry: ^ID3D11ModuleInstance, pEntryName: cstring, pTargetName: cstring, uFlags: u32, ppShaderBlob: ^^ID3DBlob, ppErrorBuffer: ^^ID3DBlob) -> HRESULT, + UseLibrary: proc "stdcall" (this: ^ID3D11Linker, pLibraryMI: ^ID3D11ModuleInstance) -> HRESULT, + AddClipPlaneFromCBuffer: proc "stdcall" (this: ^ID3D11Linker, uCBufferSlot: u32, uCBufferEntry: u32) -> HRESULT, +} + + +pD3DCompile :: #type proc "c" (a0: rawptr, a1: SIZE_T, a2: cstring, a3: ^SHADER_MACRO, a4: ^ID3DInclude, a5: cstring, a6: cstring, a7: u32, a8: u32, a9: ^^ID3DBlob, a10: ^^ID3DBlob) -> HRESULT +pD3DPreprocess :: #type proc "c" (a0: rawptr, a1: SIZE_T, a2: cstring, a3: ^SHADER_MACRO, a4: ^ID3DInclude, a5: ^^ID3DBlob, a6: ^^ID3DBlob) -> HRESULT +pD3DDisassemble :: #type proc "c" (a0: rawptr, a1: SIZE_T, a2: u32, a3: cstring, a4: ^^ID3DBlob) -> HRESULT + +D3DCOMPILER_STRIP_FLAGS :: enum u32 { // TODO: make bit_field + REFLECTION_DATA = 0x1, + DEBUG_INFO = 0x2, + TEST_BLOBS = 0x4, + PRIVATE_DATA = 0x8, + ROOT_SIGNATURE = 0x10, + FORCE_DWORD = 0x7fffffff, +} + +BLOB_PART :: enum i32 { + INPUT_SIGNATURE_BLOB = 0, + OUTPUT_SIGNATURE_BLOB = 1, + INPUT_AND_OUTPUT_SIGNATURE_BLOB = 2, + PATCH_CONSTANT_SIGNATURE_BLOB = 3, + ALL_SIGNATURE_BLOB = 4, + DEBUG_INFO = 5, + LEGACY_SHADER = 6, + XNA_PREPASS_SHADER = 7, + XNA_SHADER = 8, + PDB = 9, + PRIVATE_DATA = 10, + ROOT_SIGNATURE = 11, + DEBUG_NAME = 12, + + TEST_ALTERNATE_SHADER = 32768, + TEST_COMPILE_DETAILS = 32769, + TEST_COMPILE_PERF = 32770, + TEST_COMPILE_REPORT = 32771, +} + +SHADER_DATA :: struct { + pBytecode: rawptr, + BytecodeLength: SIZE_T, +} + +ID3D10Effect :: struct { + // ???? +} diff --git a/vendor/directx/d3d_compiler/d3dcompiler_47.dll b/vendor/directx/d3d_compiler/d3dcompiler_47.dll new file mode 100644 index 000000000..d30a17652 Binary files /dev/null and b/vendor/directx/d3d_compiler/d3dcompiler_47.dll differ diff --git a/vendor/directx/d3d_compiler/d3dcompiler_47.lib b/vendor/directx/d3d_compiler/d3dcompiler_47.lib new file mode 100644 index 000000000..022890ad9 Binary files /dev/null and b/vendor/directx/d3d_compiler/d3dcompiler_47.lib differ diff --git a/vendor/directx/dxgi/dxgi.odin b/vendor/directx/dxgi/dxgi.odin new file mode 100644 index 000000000..ae0cfd17a --- /dev/null +++ b/vendor/directx/dxgi/dxgi.odin @@ -0,0 +1,1170 @@ +package directx_dxgi + +foreign import dxgi { + "system:dxgi.lib", + "system:user32.lib", + "system:gdi32.lib", +} + +import win32 "core:sys/windows" + +LUID :: win32.LUID +IID :: win32.GUID +UUID :: win32.GUID +GUID :: win32.GUID +HANDLE :: win32.HANDLE +HRESULT :: win32.HRESULT +HMONITOR :: win32.HMONITOR +HWND :: win32.HWND +HMODULE :: win32.HMODULE +HDC :: win32.HANDLE +BOOL :: win32.BOOL +LARGE_INTEGER :: win32.LARGE_INTEGER +SIZE_T :: win32.SIZE_T +ULONG :: win32.ULONG +LONG :: win32.LONG +RECT :: win32.RECT +POINT :: win32.POINT +SIZE :: win32.SIZE + +IUnknown :: struct { + using _iunknown_vtable: ^IUnknown_VTable, +} +IUnknown_VTable :: struct { + QueryInterface: proc "stdcall" (this: ^IUnknown, riid: ^IID, ppvObject: ^rawptr) -> HRESULT, + AddRef: proc "stdcall" (this: ^IUnknown) -> ULONG, + Release: proc "stdcall" (this: ^IUnknown) -> ULONG, +} + +@(default_calling_convention="stdcall") +foreign dxgi { + CreateDXGIFactory :: proc(riid: ^IID, ppFactory: rawptr) -> HRESULT --- + CreateDXGIFactory1 :: proc(riid: ^IID, ppFactory: rawptr) -> HRESULT --- + CreateDXGIFactory2 :: proc(Flags: u32, riid: ^IID, ppFactory: rawptr) -> HRESULT --- + DXGIGetDebugInterface1 :: proc(Flags: u32, riid: ^IID, pDebug: rawptr) -> HRESULT --- +} + +STANDARD_MULTISAMPLE_QUALITY_PATTERN :: 0xffffffff +CENTER_MULTISAMPLE_QUALITY_PATTERN :: 0xfffffffe +FORMAT_DEFINED :: 1 +_FACDXGI :: 0x87a + +CPU_ACCESS :: enum u32 { + NONE = 0, + DYNAMIC = 1, + READ_WRITE = 2, + SCRATCH = 3, + FIELD = 15, +} + +USAGE :: enum u32 { // TODO: convert to bit_set + SHADER_INPUT = 0x00000010, + RENDER_TARGET_OUTPUT = 0x00000020, + BACK_BUFFER = 0x00000040, + SHARED = 0x00000080, + READ_ONLY = 0x00000100, + DISCARD_ON_PRESENT = 0x00000200, + UNORDERED_ACCESS = 0x00000400, +} + +RESOURCE_PRIORITY :: enum u32 { + MINIMUM = 0x28000000, + LOW = 0x50000000, + NORMAL = 0x78000000, + HIGH = 0xa0000000, + MAXIMUM = 0xc8000000, +} + +MAP :: enum u32 { // TODO: convert to bit_set + READ = 1, + WRITE = 2, + DISCARD = 4, +} + +ENUM_MODES :: enum u32 { // TODO: convert to bit_set + INTERLACED = 1, + SCALING = 2, + STEREO = 4, + DISABLED_STEREO = 8, +} + +MAX_SWAP_CHAIN_BUFFERS :: 16 +PRESENT :: enum u32 { // TODO: convert to bit_set + TEST = 0x00000001, + DO_NOT_SEQUENCE = 0x00000002, + RESTART = 0x00000004, + DO_NOT_WAIT = 0x00000008, + STEREO_PREFER_RIGHT = 0x00000010, + STEREO_TEMPORARY_MONO = 0x00000020, + RESTRICT_TO_OUTPUT = 0x00000040, + USE_DURATION = 0x00000100, + ALLOW_TEARING = 0x00000200, +} + +MWA :: enum u32 { // TODO: convert to bit_set + NO_WINDOW_CHANGES = 1 << 0, + NO_ALT_ENTER = 1 << 1, + NO_PRINT_SCREEN = 1 << 2, + VALID = 0x7, +} + +SHARED_RESOURCE_READ :: 0x80000000 +SHARED_RESOURCE_WRITE :: 1 +CREATE_FACTORY_DEBUG :: 0x1 + +RATIONAL :: struct { + Numerator: u32, + Denominator: u32, +} + +SAMPLE_DESC :: struct { + Count: u32, + Quality: u32, +} + +COLOR_SPACE_TYPE :: enum i32 { + RGB_FULL_G22_NONE_P709 = 0, + RGB_FULL_G10_NONE_P709 = 1, + RGB_STUDIO_G22_NONE_P709 = 2, + RGB_STUDIO_G22_NONE_P2020 = 3, + RESERVED = 4, + YCBCR_FULL_G22_NONE_P709_X601 = 5, + YCBCR_STUDIO_G22_LEFT_P601 = 6, + YCBCR_FULL_G22_LEFT_P601 = 7, + YCBCR_STUDIO_G22_LEFT_P709 = 8, + YCBCR_FULL_G22_LEFT_P709 = 9, + YCBCR_STUDIO_G22_LEFT_P2020 = 10, + YCBCR_FULL_G22_LEFT_P2020 = 11, + RGB_FULL_G2084_NONE_P2020 = 12, + YCBCR_STUDIO_G2084_LEFT_P2020 = 13, + RGB_STUDIO_G2084_NONE_P2020 = 14, + YCBCR_STUDIO_G22_TOPLEFT_P2020 = 15, + YCBCR_STUDIO_G2084_TOPLEFT_P2020 = 16, + RGB_FULL_G22_NONE_P2020 = 17, + YCBCR_STUDIO_GHLG_TOPLEFT_P2020 = 18, + YCBCR_FULL_GHLG_TOPLEFT_P2020 = 19, + RGB_STUDIO_G24_NONE_P709 = 20, + RGB_STUDIO_G24_NONE_P2020 = 21, + YCBCR_STUDIO_G24_LEFT_P709 = 22, + YCBCR_STUDIO_G24_LEFT_P2020 = 23, + YCBCR_STUDIO_G24_TOPLEFT_P2020 = 24, + CUSTOM = -1, +} + +FORMAT :: enum i32 { + UNKNOWN = 0, + R32G32B32A32_TYPELESS = 1, + R32G32B32A32_FLOAT = 2, + R32G32B32A32_UINT = 3, + R32G32B32A32_SINT = 4, + R32G32B32_TYPELESS = 5, + R32G32B32_FLOAT = 6, + R32G32B32_UINT = 7, + R32G32B32_SINT = 8, + R16G16B16A16_TYPELESS = 9, + R16G16B16A16_FLOAT = 10, + R16G16B16A16_UNORM = 11, + R16G16B16A16_UINT = 12, + R16G16B16A16_SNORM = 13, + R16G16B16A16_SINT = 14, + R32G32_TYPELESS = 15, + R32G32_FLOAT = 16, + R32G32_UINT = 17, + R32G32_SINT = 18, + R32G8X24_TYPELESS = 19, + D32_FLOAT_S8X24_UINT = 20, + R32_FLOAT_X8X24_TYPELESS = 21, + X32_TYPELESS_G8X24_UINT = 22, + R10G10B10A2_TYPELESS = 23, + R10G10B10A2_UNORM = 24, + R10G10B10A2_UINT = 25, + R11G11B10_FLOAT = 26, + R8G8B8A8_TYPELESS = 27, + R8G8B8A8_UNORM = 28, + R8G8B8A8_UNORM_SRGB = 29, + R8G8B8A8_UINT = 30, + R8G8B8A8_SNORM = 31, + R8G8B8A8_SINT = 32, + R16G16_TYPELESS = 33, + R16G16_FLOAT = 34, + R16G16_UNORM = 35, + R16G16_UINT = 36, + R16G16_SNORM = 37, + R16G16_SINT = 38, + R32_TYPELESS = 39, + D32_FLOAT = 40, + R32_FLOAT = 41, + R32_UINT = 42, + R32_SINT = 43, + R24G8_TYPELESS = 44, + D24_UNORM_S8_UINT = 45, + R24_UNORM_X8_TYPELESS = 46, + X24_TYPELESS_G8_UINT = 47, + R8G8_TYPELESS = 48, + R8G8_UNORM = 49, + R8G8_UINT = 50, + R8G8_SNORM = 51, + R8G8_SINT = 52, + R16_TYPELESS = 53, + R16_FLOAT = 54, + D16_UNORM = 55, + R16_UNORM = 56, + R16_UINT = 57, + R16_SNORM = 58, + R16_SINT = 59, + R8_TYPELESS = 60, + R8_UNORM = 61, + R8_UINT = 62, + R8_SNORM = 63, + R8_SINT = 64, + A8_UNORM = 65, + R1_UNORM = 66, + R9G9B9E5_SHAREDEXP = 67, + R8G8_B8G8_UNORM = 68, + G8R8_G8B8_UNORM = 69, + BC1_TYPELESS = 70, + BC1_UNORM = 71, + BC1_UNORM_SRGB = 72, + BC2_TYPELESS = 73, + BC2_UNORM = 74, + BC2_UNORM_SRGB = 75, + BC3_TYPELESS = 76, + BC3_UNORM = 77, + BC3_UNORM_SRGB = 78, + BC4_TYPELESS = 79, + BC4_UNORM = 80, + BC4_SNORM = 81, + BC5_TYPELESS = 82, + BC5_UNORM = 83, + BC5_SNORM = 84, + B5G6R5_UNORM = 85, + B5G5R5A1_UNORM = 86, + B8G8R8A8_UNORM = 87, + B8G8R8X8_UNORM = 88, + R10G10B10_XR_BIAS_A2_UNORM = 89, + B8G8R8A8_TYPELESS = 90, + B8G8R8A8_UNORM_SRGB = 91, + B8G8R8X8_TYPELESS = 92, + B8G8R8X8_UNORM_SRGB = 93, + BC6H_TYPELESS = 94, + BC6H_UF16 = 95, + BC6H_SF16 = 96, + BC7_TYPELESS = 97, + BC7_UNORM = 98, + BC7_UNORM_SRGB = 99, + AYUV = 100, + Y410 = 101, + Y416 = 102, + NV12 = 103, + P010 = 104, + P016 = 105, + _420_OPAQUE = 106, + YUY2 = 107, + Y210 = 108, + Y216 = 109, + NV11 = 110, + AI44 = 111, + IA44 = 112, + P8 = 113, + A8P8 = 114, + B4G4R4A4_UNORM = 115, + + P208 = 130, + V208 = 131, + V408 = 132, + + SAMPLER_FEEDBACK_MIN_MIP_OPAQUE = 189, + SAMPLER_FEEDBACK_MIP_REGION_USED_OPAQUE = 190, + + FORCE_UINT = -1, +} + +RGB :: struct { + Red: f32, + Green: f32, + Blue: f32, +} + +D3DCOLORVALUE :: struct { + r: f32, + g: f32, + b: f32, + a: f32, +} + +RGBA :: D3DCOLORVALUE + +GAMMA_CONTROL :: struct { + Scale: RGB, + Offset: RGB, + GammaCurve: [1025]RGB, +} + +GAMMA_CONTROL_CAPABILITIES :: struct { + ScaleAndOffsetSupported: BOOL, + MaxConvertedValue: f32, + MinConvertedValue: f32, + NumGammaControlPoints: u32, + ControlPointPositions: [1025]f32, +} + +MODE_SCANLINE_ORDER :: enum i32 { + UNSPECIFIED = 0, + PROGRESSIVE = 1, + UPPER_FIELD_FIRST = 2, + LOWER_FIELD_FIRST = 3, +} + +MODE_SCALING :: enum i32 { + UNSPECIFIED = 0, + CENTERED = 1, + STRETCHED = 2, +} + +MODE_ROTATION :: enum i32 { + UNSPECIFIED = 0, + IDENTITY = 1, + ROTATE90 = 2, + ROTATE180 = 3, + ROTATE270 = 4, +} + +MODE_DESC :: struct { + Width: u32, + Height: u32, + RefreshRate: RATIONAL, + Format: FORMAT, + ScanlineOrdering: MODE_SCANLINE_ORDER, + Scaling: MODE_SCALING, +} + +JPEG_DC_HUFFMAN_TABLE :: struct { + CodeCounts: [12]u8, + CodeValues: [12]u8, +} + +JPEG_AC_HUFFMAN_TABLE :: struct { + CodeCounts: [16]u8, + CodeValues: [162]u8, +} + +JPEG_QUANTIZATION_TABLE :: struct { + Elements: [64]u8, +} + +FRAME_STATISTICS :: struct { + PresentCount: u32, + PresentRefreshCount: u32, + SyncRefreshCount: u32, + SyncQPCTime: LARGE_INTEGER, + SyncGPUTime: LARGE_INTEGER, +} + +MAPPED_RECT :: struct { + Pitch: i32, + pBits: [^]u8, +} + +ADAPTER_DESC :: struct { + Description: [128]i16, + VendorId: u32, + DeviceId: u32, + SubSysId: u32, + Revision: u32, + DedicatedVideoMemory: SIZE_T, + DedicatedSystemMemory: SIZE_T, + SharedSystemMemory: SIZE_T, + AdapterLuid: LUID, +} + +OUTPUT_DESC :: struct { + DeviceName: [32]i16, + DesktopCoordinates: RECT, + AttachedToDesktop: BOOL, + Rotation: MODE_ROTATION, + Monitor: HMONITOR, +} + +SHARED_RESOURCE :: struct { + Handle: HANDLE, +} + +RESIDENCY :: enum i32 { + FULLY_RESIDENT = 1, + RESIDENT_IN_SHARED_MEMORY = 2, + EVICTED_TO_DISK = 3, +} + +SURFACE_DESC :: struct { + Width: u32, + Height: u32, + Format: FORMAT, + SampleDesc: SAMPLE_DESC, +} + +SWAP_EFFECT :: enum i32 { + DISCARD = 0, + SEQUENTIAL = 1, + FLIP_SEQUENTIAL = 3, + FLIP_DISCARD = 4, +} + +SWAP_CHAIN_FLAG :: enum u32 { // TODO: convert to bit_set + NONPREROTATED = 0x1, + ALLOW_MODE_SWITCH = 0x2, + GDI_COMPATIBLE = 0x4, + RESTRICTED_CONTENT = 0x8, + RESTRICT_SHARED_RESOURCE_DRIVER = 0x10, + DISPLAY_ONLY = 0x20, + FRAME_LATENCY_WAITABLE_OBJECT = 0x40, + FOREGROUND_LAYER = 0x80, + FULLSCREEN_VIDEO = 0x100, + YUV_VIDEO = 0x200, + HW_PROTECTED = 0x400, + ALLOW_TEARING = 0x800, + RESTRICTED_TO_ALL_HOLOGRAPHIC_DISPLAYS = 0x1000, +} + +SWAP_CHAIN_DESC :: struct { + BufferDesc: MODE_DESC, + SampleDesc: SAMPLE_DESC, + BufferUsage: USAGE, + BufferCount: u32, + OutputWindow: HWND, + Windowed: BOOL, + SwapEffect: SWAP_EFFECT, + Flags: u32, +} + + +IObject_UUID_STRING :: "AEC22FB8-76F3-4639-9BE0-28EB43A67A2E" +IObject_UUID := &IID{0xAEC22FB8, 0x76F3, 0x4639, {0x9B, 0xE0, 0x28, 0xEB, 0x43, 0xA6, 0x7A, 0x2E}} +IObject :: struct #raw_union { + #subtype iunknown: IUnknown, + using vtable: ^IObject_VTable, +} +IObject_VTable :: struct { + using iunknown_vtable: IUnknown_VTable, + SetPrivateData: proc "stdcall" (this: ^IObject, Name: ^GUID, DataSize: u32, pData: rawptr) -> HRESULT, + SetPrivateDataInterface: proc "stdcall" (this: ^IObject, Name: ^GUID, pUnknown: ^IUnknown) -> HRESULT, + GetPrivateData: proc "stdcall" (this: ^IObject, Name: ^GUID, pDataSize: ^u32, pData: rawptr) -> HRESULT, + GetParent: proc "stdcall" (this: ^IObject, riid: ^IID, ppParent: ^rawptr) -> HRESULT, +} + +IDeviceSubObject_UUID_STRING :: "3D3E0379-F9DE-4D58-BB6C-18D62992F1A6" +IDeviceSubObject_UUID := &IID{0x3D3E0379, 0xF9DE, 0x4D58, {0xBB, 0x6C, 0x18, 0xD6, 0x29, 0x92, 0xF1, 0xA6}} +IDeviceSubObject :: struct #raw_union { + #subtype idxgiobject: IObject, + using idxgidevicesubobject_vtable: ^IDeviceSubObject_VTable, +} +IDeviceSubObject_VTable :: struct { + using idxgiobject_vtable: IObject_VTable, + GetDevice: proc "stdcall" (this: ^IDeviceSubObject, riid: ^IID, ppDevice: ^rawptr) -> HRESULT, +} + +IResource_UUID_STRING :: "035F3AB4-482E-4E50-B41F-8A7F8BD8960B" +IResource_UUID := &IID{0x035F3AB4, 0x482E, 0x4E50, {0xB4, 0x1F, 0x8A, 0x7F, 0x8B, 0xD8, 0x96, 0x0B}} +IResource :: struct #raw_union { + #subtype idxgidevicesubobject: IDeviceSubObject, + using idxgiresource_vtable: ^IResource_VTable, +} +IResource_VTable :: struct { + using idxgidevicesubobject_vtable: IDeviceSubObject_VTable, + GetSharedHandle: proc "stdcall" (this: ^IResource, pSharedHandle: ^HANDLE) -> HRESULT, + GetUsage: proc "stdcall" (this: ^IResource, pUsage: ^USAGE) -> HRESULT, + SetEvictionPriority: proc "stdcall" (this: ^IResource, EvictionPriority: u32) -> HRESULT, + GetEvictionPriority: proc "stdcall" (this: ^IResource, pEvictionPriority: ^u32) -> HRESULT, +} + +IKeyedMutex_UUID_STRING :: "9D8E1289-D7B3-465F-8126-250E349AF85D" +IKeyedMutex_UUID := &IID{0x9D8E1289, 0xD7B3, 0x465F, {0x81, 0x26, 0x25, 0x0E, 0x34, 0x9A, 0xF8, 0x5D}} +IKeyedMutex :: struct #raw_union { + #subtype idxgidevicesubobject: IDeviceSubObject, + using idxgikeyedmutex_vtable: ^IKeyedMutex_VTable, +} +IKeyedMutex_VTable :: struct { + using idxgidevicesubobject_vtable: IDeviceSubObject_VTable, + AcquireSync: proc "stdcall" (this: ^IKeyedMutex, Key: u64, dwMilliseconds: u32) -> HRESULT, + ReleaseSync: proc "stdcall" (this: ^IKeyedMutex, Key: u64) -> HRESULT, +} + +ISurface_UUID_STRING :: "CAFCB56C-6AC3-4889-BF47-9E23BBD260EC" +ISurface_UUID := &IID{0xCAFCB56C, 0x6AC3, 0x4889, {0xBF, 0x47, 0x9E, 0x23, 0xBB, 0xD2, 0x60, 0xEC}} +ISurface :: struct #raw_union { + #subtype idxgidevicesubobject: IDeviceSubObject, + using idxgisurface_vtable: ^ISurface_VTable, +} +ISurface_VTable :: struct { + using idxgidevicesubobject_vtable: IDeviceSubObject_VTable, + GetDesc: proc "stdcall" (this: ^ISurface, pDesc: ^SURFACE_DESC) -> HRESULT, + Map: proc "stdcall" (this: ^ISurface, pLockedRect: ^MAPPED_RECT, MapFlags: u32) -> HRESULT, + Unmap: proc "stdcall" (this: ^ISurface) -> HRESULT, +} + +ISurface1_UUID_STRING :: "4AE63092-6327-4C1B-80AE-BFE12EA32B86" +ISurface1_UUID := &IID{0x4AE63092, 0x6327, 0x4C1B, {0x80, 0xAE, 0xBF, 0xE1, 0x2E, 0xA3, 0x2B, 0x86}} +ISurface1 :: struct #raw_union { + #subtype idxgisurface: ISurface, + using idxgisurface1_vtable: ^ISurface1_VTable, +} +ISurface1_VTable :: struct { + using idxgisurface_vtable: ISurface_VTable, + GetDC: proc "stdcall" (this: ^ISurface1, Discard: BOOL, phdc: ^HDC) -> HRESULT, + ReleaseDC: proc "stdcall" (this: ^ISurface1, pDirtyRect: ^RECT) -> HRESULT, +} + +IAdapter_UUID_STRING :: "2411E7E1-12AC-4CCF-BD14-9798E8534DC0" +IAdapter_UUID := &IID{0x2411E7E1, 0x12AC, 0x4CCF, {0xBD, 0x14, 0x97, 0x98, 0xE8, 0x53, 0x4D, 0xC0}} +IAdapter :: struct #raw_union { + #subtype idxgiobject: IObject, + using idxgiadapter_vtable: ^IAdapter_VTable, +} +IAdapter_VTable :: struct { + using idxgiobject_vtable: IObject_VTable, + EnumOutputs: proc "stdcall" (this: ^IAdapter, Output: u32, ppOutput: ^^IOutput) -> HRESULT, + GetDesc: proc "stdcall" (this: ^IAdapter, pDesc: ^ADAPTER_DESC) -> HRESULT, + CheckInterfaceSupport: proc "stdcall" (this: ^IAdapter, InterfaceName: ^GUID, pUMDVersion: ^LARGE_INTEGER) -> HRESULT, +} + +IOutput_UUID_STRING :: "AE02EEDB-C735-4690-8D52-5A8DC20213AA" +IOutput_UUID := &IID{0xAE02EEDB, 0xC735, 0x4690, {0x8D, 0x52, 0x5A, 0x8D, 0xC2, 0x02, 0x13, 0xAA}} +IOutput :: struct #raw_union { + #subtype idxgiobject: IObject, + using idxgioutput_vtable: ^IOutput_VTable, +} +IOutput_VTable :: struct { + using idxgiobject_vtable: IObject_VTable, + GetDesc: proc "stdcall" (this: ^IOutput, pDesc: ^OUTPUT_DESC) -> HRESULT, + GetDisplayModeList: proc "stdcall" (this: ^IOutput, EnumFormat: FORMAT, Flags: u32, pNumModes: ^u32, pDesc: ^MODE_DESC) -> HRESULT, + FindClosestMatchingMode: proc "stdcall" (this: ^IOutput, pModeToMatch: ^MODE_DESC, pClosestMatch: ^MODE_DESC, pConcernedDevice: ^IUnknown) -> HRESULT, + WaitForVBlank: proc "stdcall" (this: ^IOutput) -> HRESULT, + TakeOwnership: proc "stdcall" (this: ^IOutput, pDevice: ^IUnknown, Exclusive: BOOL) -> HRESULT, + ReleaseOwnership: proc "stdcall" (this: ^IOutput), + GetGammaControlCapabilities: proc "stdcall" (this: ^IOutput, pGammaCaps: ^GAMMA_CONTROL_CAPABILITIES) -> HRESULT, + SetGammaControl: proc "stdcall" (this: ^IOutput, pArray: ^GAMMA_CONTROL) -> HRESULT, + GetGammaControl: proc "stdcall" (this: ^IOutput, pArray: ^GAMMA_CONTROL) -> HRESULT, + SetDisplaySurface: proc "stdcall" (this: ^IOutput, pScanoutSurface: ^ISurface) -> HRESULT, + GetDisplaySurfaceData: proc "stdcall" (this: ^IOutput, pDestination: ^ISurface) -> HRESULT, + GetFrameStatistics: proc "stdcall" (this: ^IOutput, pStats: ^FRAME_STATISTICS) -> HRESULT, +} + +ISwapChain_UUID_STRING :: "310D36A0-D2E7-4C0A-AA04-6A9D23B8886A" +ISwapChain_UUID := &IID{0x310D36A0, 0xD2E7, 0x4C0A, {0xAA, 0x04, 0x6A, 0x9D, 0x23, 0xB8, 0x88, 0x6A}} +ISwapChain :: struct #raw_union { + #subtype idxgidevicesubobject: IDeviceSubObject, + using idxgiswapchain_vtable: ^ISwapChain_VTable, +} +ISwapChain_VTable :: struct { + using idxgidevicesubobject_vtable: IDeviceSubObject_VTable, + Present: proc "stdcall" (this: ^ISwapChain, SyncInterval: u32, Flags: u32) -> HRESULT, + GetBuffer: proc "stdcall" (this: ^ISwapChain, Buffer: u32, riid: ^IID, ppSurface: ^rawptr) -> HRESULT, + SetFullscreenState: proc "stdcall" (this: ^ISwapChain, Fullscreen: BOOL, pTarget: ^IOutput) -> HRESULT, + GetFullscreenState: proc "stdcall" (this: ^ISwapChain, pFullscreen: ^BOOL, ppTarget: ^^IOutput) -> HRESULT, + GetDesc: proc "stdcall" (this: ^ISwapChain, pDesc: ^SWAP_CHAIN_DESC) -> HRESULT, + ResizeBuffers: proc "stdcall" (this: ^ISwapChain, BufferCount: u32, Width: u32, Height: u32, NewFormat: FORMAT, SwapChainFlags: u32) -> HRESULT, + ResizeTarget: proc "stdcall" (this: ^ISwapChain, pNewTargetParameters: ^MODE_DESC) -> HRESULT, + GetContainingOutput: proc "stdcall" (this: ^ISwapChain, ppOutput: ^^IOutput) -> HRESULT, + GetFrameStatistics: proc "stdcall" (this: ^ISwapChain, pStats: ^FRAME_STATISTICS) -> HRESULT, + GetLastPresentCount: proc "stdcall" (this: ^ISwapChain, pLastPresentCount: ^u32) -> HRESULT, +} + +IFactory_UUID_STRING :: "7B7166EC-21C7-44AE-B21A-C9AE321AE369" +IFactory_UUID := &IID{0x7B7166EC, 0x21C7, 0x44AE, {0xB2, 0x1A, 0xC9, 0xAE, 0x32, 0x1A, 0xE3, 0x69}} +IFactory :: struct #raw_union { + #subtype idxgiobject: IObject, + using idxgifactory_vtable: ^IFactory_VTable, +} +IFactory_VTable :: struct { + using idxgiobject_vtable: IObject_VTable, + EnumAdapters: proc "stdcall" (this: ^IFactory, Adapter: u32, ppAdapter: ^^IAdapter) -> HRESULT, + MakeWindowAssociation: proc "stdcall" (this: ^IFactory, WindowHandle: HWND, Flags: u32) -> HRESULT, + GetWindowAssociation: proc "stdcall" (this: ^IFactory, pWindowHandle: ^HWND) -> HRESULT, + CreateSwapChain: proc "stdcall" (this: ^IFactory, pDevice: ^IUnknown, pDesc: ^SWAP_CHAIN_DESC, ppSwapChain: ^^ISwapChain) -> HRESULT, + CreateSoftwareAdapter: proc "stdcall" (this: ^IFactory, Module: HMODULE, ppAdapter: ^^IAdapter) -> HRESULT, +} +IDevice_UUID_STRING :: "54EC77FA-1377-44E6-8C32-88FD5F44C84C" +IDevice_UUID := &IID{0x54EC77FA, 0x1377, 0x44E6, {0x8C, 0x32, 0x88, 0xFD, 0x5F, 0x44, 0xC8, 0x4C}} +IDevice :: struct #raw_union { + #subtype idxgiobject: IObject, + using idxgidevice_vtable: ^IDevice_VTable, +} +IDevice_VTable :: struct { + using idxgiobject_vtable: IObject_VTable, + GetAdapter: proc "stdcall" (this: ^IDevice, pAdapter: ^^IAdapter) -> HRESULT, + CreateSurface: proc "stdcall" (this: ^IDevice, pDesc: ^SURFACE_DESC, NumSurfaces: u32, Usage: USAGE, pSharedResource: ^SHARED_RESOURCE, ppSurface: ^^ISurface) -> HRESULT, + QueryResourceResidency: proc "stdcall" (this: ^IDevice, ppResources: ^^IUnknown, pResidencyStatus: ^RESIDENCY, NumResources: u32) -> HRESULT, + SetGPUThreadPriority: proc "stdcall" (this: ^IDevice, Priority: i32) -> HRESULT, + GetGPUThreadPriority: proc "stdcall" (this: ^IDevice, pPriority: ^i32) -> HRESULT, +} +ADAPTER_FLAG :: enum u32 { // TODO: convert to bit_set + NONE = 0x0, + REMOTE = 0x1, + SOFTWARE = 0x2, + FORCE_DWORD = 0xffffffff, +} + +ADAPTER_DESC1 :: struct { + Description: [128]i16, + VendorId: u32, + DeviceId: u32, + SubSysId: u32, + Revision: u32, + DedicatedVideoMemory: SIZE_T, + DedicatedSystemMemory: SIZE_T, + SharedSystemMemory: SIZE_T, + AdapterLuid: LUID, + Flags: u32, +} + +DISPLAY_COLOR_SPACE :: struct { + PrimaryCoordinates: [8][2]f32, + WhitePoints: [16][2]f32, +} + + +IFactory1_UUID_STRING :: "770AAE78-F26F-4DBA-A829-253C83D1B387" +IFactory1_UUID := &IID{0x770AAE78, 0xF26F, 0x4DBA, {0xA8, 0x29, 0x25, 0x3C, 0x83, 0xD1, 0xB3, 0x87}} +IFactory1 :: struct #raw_union { + #subtype idxgifactory: IFactory, + using idxgifactory1_vtable: ^IFactory1_VTable, +} +IFactory1_VTable :: struct { + using idxgifactory_vtable: IFactory_VTable, + EnumAdapters1: proc "stdcall" (this: ^IFactory1, Adapter: u32, ppAdapter: ^^IAdapter1) -> HRESULT, + IsCurrent: proc "stdcall" (this: ^IFactory1) -> BOOL, +} + +IAdapter1_UUID_STRING :: "29038F61-3839-4626-91FD-086879011A05" +IAdapter1_UUID := &IID{0x29038F61, 0x3839, 0x4626, {0x91, 0xFD, 0x08, 0x68, 0x79, 0x01, 0x1A, 0x05}} +IAdapter1 :: struct #raw_union { + #subtype idxgiadapter: IAdapter, + using idxgiadapter1_vtable: ^IAdapter1_VTable, +} +IAdapter1_VTable :: struct { + using idxgiadapter_vtable: IAdapter_VTable, + GetDesc1: proc "stdcall" (this: ^IAdapter1, pDesc: ^ADAPTER_DESC1) -> HRESULT, +} + +IDevice1_UUID_STRING :: "77DB970F-6276-48BA-BA28-070143B4392C" +IDevice1_UUID := &IID{0x77DB970F, 0x6276, 0x48BA, {0xBA, 0x28, 0x07, 0x01, 0x43, 0xB4, 0x39, 0x2C}} +IDevice1 :: struct #raw_union { + #subtype idxgidevice: IDevice, + using idxgidevice1_vtable: ^IDevice1_VTable, +} +IDevice1_VTable :: struct { + using idxgidevice_vtable: IDevice_VTable, + SetMaximumFrameLatency: proc "stdcall" (this: ^IDevice1, MaxLatency: u32) -> HRESULT, + GetMaximumFrameLatency: proc "stdcall" (this: ^IDevice1, pMaxLatency: ^u32) -> HRESULT, +} + +IDisplayControl_UUID_STRING :: "EA9DBF1A-C88E-4486-854A-98AA0138F30C" +IDisplayControl_UUID := &IID{0xEA9DBF1A, 0xC88E, 0x4486, {0x85, 0x4A, 0x98, 0xAA, 0x01, 0x38, 0xF3, 0x0C}} +IDisplayControl :: struct #raw_union { + #subtype iunknown: IUnknown, + using idxgidisplaycontrol_vtable: ^IDisplayControl_VTable, +} +IDisplayControl_VTable :: struct { + using iunknown_vtable: IUnknown_VTable, + IsStereoEnabled: proc "stdcall" (this: ^IDisplayControl) -> BOOL, + SetStereoEnabled: proc "stdcall" (this: ^IDisplayControl, enabled: BOOL), +} +OUTDUPL_MOVE_RECT :: struct { + SourcePoint: POINT, + DestinationRect: RECT, +} + +OUTDUPL_DESC :: struct { + ModeDesc: MODE_DESC, + Rotation: MODE_ROTATION, + DesktopImageInSystemMemory: BOOL, +} + +OUTDUPL_POINTER_POSITION :: struct { + Position: POINT, + Visible: BOOL, +} + +OUTDUPL_POINTER_SHAPE_TYPE :: enum i32 { + MONOCHROME = 1, + COLOR = 2, + MASKED_COLOR = 4, +} + +OUTDUPL_POINTER_SHAPE_INFO :: struct { + Type: u32, + Width: u32, + Height: u32, + Pitch: u32, + HotSpot: POINT, +} + +OUTDUPL_FRAME_INFO :: struct { + LastPresentTime: LARGE_INTEGER, + LastMouseUpdateTime: LARGE_INTEGER, + AccumulatedFrames: u32, + RectsCoalesced: BOOL, + ProtectedContentMaskedOut: BOOL, + PointerPosition: OUTDUPL_POINTER_POSITION, + TotalMetadataBufferSize: u32, + PointerShapeBufferSize: u32, +} + + +IOutputDuplication_UUID_STRING :: "191CFAC3-A341-470D-B26E-A864F428319C" +IOutputDuplication_UUID := &IID{0x191CFAC3, 0xA341, 0x470D, {0xB2, 0x6E, 0xA8, 0x64, 0xF4, 0x28, 0x31, 0x9C}} +IOutputDuplication :: struct #raw_union { + #subtype idxgiobject: IObject, + using idxgioutputduplication_vtable: ^IOutputDuplication_VTable, +} +IOutputDuplication_VTable :: struct { + using idxgiobject_vtable: IObject_VTable, + GetDesc: proc "stdcall" (this: ^IOutputDuplication, pDesc: ^OUTDUPL_DESC), + AcquireNextFrame: proc "stdcall" (this: ^IOutputDuplication, TimeoutInMilliseconds: u32, pFrameInfo: ^OUTDUPL_FRAME_INFO, ppDesktopResource: ^^IResource) -> HRESULT, + GetFrameDirtyRects: proc "stdcall" (this: ^IOutputDuplication, DirtyRectsBufferSize: u32, pDirtyRectsBuffer: ^RECT, pDirtyRectsBufferSizeRequired: ^u32) -> HRESULT, + GetFrameMoveRects: proc "stdcall" (this: ^IOutputDuplication, MoveRectsBufferSize: u32, pMoveRectBuffer: ^OUTDUPL_MOVE_RECT, pMoveRectsBufferSizeRequired: ^u32) -> HRESULT, + GetFramePointerShape: proc "stdcall" (this: ^IOutputDuplication, PointerShapeBufferSize: u32, pPointerShapeBuffer: rawptr, pPointerShapeBufferSizeRequired: ^u32, pPointerShapeInfo: ^OUTDUPL_POINTER_SHAPE_INFO) -> HRESULT, + MapDesktopSurface: proc "stdcall" (this: ^IOutputDuplication, pLockedRect: ^MAPPED_RECT) -> HRESULT, + UnMapDesktopSurface: proc "stdcall" (this: ^IOutputDuplication) -> HRESULT, + ReleaseFrame: proc "stdcall" (this: ^IOutputDuplication) -> HRESULT, +} +ALPHA_MODE :: enum i32 { + UNSPECIFIED = 0, + PREMULTIPLIED = 1, + STRAIGHT = 2, + IGNORE = 3, + FORCE_DWORD = -1, +} + + +ISurface2_UUID_STRING :: "ABA496DD-B617-4CB8-A866-BC44D7EB1FA2" +ISurface2_UUID := &IID{0xABA496DD, 0xB617, 0x4CB8, {0xA8, 0x66, 0xBC, 0x44, 0xD7, 0xEB, 0x1F, 0xA2}} +ISurface2 :: struct #raw_union { + #subtype idxgisurface1: ISurface1, + using idxgisurface2_vtable: ^ISurface2_VTable, +} +ISurface2_VTable :: struct { + using idxgisurface1_vtable: ISurface1_VTable, + GetResource: proc "stdcall" (this: ^ISurface2, riid: ^IID, ppParentResource: ^rawptr, pSubresourceIndex: ^u32) -> HRESULT, +} + +IResource1_UUID_STRING :: "30961379-4609-4A41-998E-54FE567EE0C1" +IResource1_UUID := &IID{0x30961379, 0x4609, 0x4A41, {0x99, 0x8E, 0x54, 0xFE, 0x56, 0x7E, 0xE0, 0xC1}} +IResource1 :: struct #raw_union { + #subtype idxgiresource: IResource, + using idxgiresource1_vtable: ^IResource1_VTable, +} +IResource1_VTable :: struct { + using idxgiresource_vtable: IResource_VTable, + CreateSubresourceSurface: proc "stdcall" (this: ^IResource1, index: u32, ppSurface: ^^ISurface2) -> HRESULT, + CreateSharedHandle: proc "stdcall" (this: ^IResource1, pAttributes: ^win32.SECURITY_ATTRIBUTES, dwAccess: u32, lpName: ^i16, pHandle: ^HANDLE) -> HRESULT, +} +OFFER_RESOURCE_PRIORITY :: enum i32 { + LOW = 1, + NORMAL = 2, + HIGH = 3, +} + + +IDevice2_UUID_STRING :: "05008617-FBFD-4051-A790-144884B4F6A9" +IDevice2_UUID := &IID{0x05008617, 0xFBFD, 0x4051, {0xA7, 0x90, 0x14, 0x48, 0x84, 0xB4, 0xF6, 0xA9}} +IDevice2 :: struct #raw_union { + #subtype idxgidevice1: IDevice1, + using idxgidevice2_vtable: ^IDevice2_VTable, +} +IDevice2_VTable :: struct { + using idxgidevice1_vtable: IDevice1_VTable, + OfferResources: proc "stdcall" (this: ^IDevice2, NumResources: u32, ppResources: ^^IResource, Priority: OFFER_RESOURCE_PRIORITY) -> HRESULT, + ReclaimResources: proc "stdcall" (this: ^IDevice2, NumResources: u32, ppResources: ^^IResource, pDiscarded: ^BOOL) -> HRESULT, + EnqueueSetEvent: proc "stdcall" (this: ^IDevice2, hEvent: HANDLE) -> HRESULT, +} +MODE_DESC1 :: struct { + Width: u32, + Height: u32, + RefreshRate: RATIONAL, + Format: FORMAT, + ScanlineOrdering: MODE_SCANLINE_ORDER, + Scaling: MODE_SCALING, + Stereo: BOOL, +} + +SCALING :: enum i32 { + STRETCH = 0, + NONE = 1, + ASPECT_RATIO_STRETCH = 2, +} + +SWAP_CHAIN_DESC1 :: struct { + Width: u32, + Height: u32, + Format: FORMAT, + Stereo: BOOL, + SampleDesc: SAMPLE_DESC, + BufferUsage: USAGE, + BufferCount: u32, + Scaling: SCALING, + SwapEffect: SWAP_EFFECT, + AlphaMode: ALPHA_MODE, + Flags: u32, +} + +SWAP_CHAIN_FULLSCREEN_DESC :: struct { + RefreshRate: RATIONAL, + ScanlineOrdering: MODE_SCANLINE_ORDER, + Scaling: MODE_SCALING, + Windowed: BOOL, +} + +PRESENT_PARAMETERS :: struct { + DirtyRectsCount: u32, + + pDirtyRects: [^]RECT, + pScrollRect: ^RECT, + pScrollOffset: ^POINT, +} + + +ISwapChain1_UUID_STRING :: "790A45F7-0D42-4876-983A-0A55CFE6F4AA" +ISwapChain1_UUID := &IID{0x790A45F7, 0x0D42, 0x4876, {0x98, 0x3A, 0x0A, 0x55, 0xCF, 0xE6, 0xF4, 0xAA}} +ISwapChain1 :: struct #raw_union { + #subtype idxgiswapchain: ISwapChain, + using idxgiswapchain1_vtable: ^ISwapChain1_VTable, +} +ISwapChain1_VTable :: struct { + using idxgiswapchain_vtable: ISwapChain_VTable, + GetDesc1: proc "stdcall" (this: ^ISwapChain1, pDesc: ^SWAP_CHAIN_DESC1) -> HRESULT, + GetFullscreenDesc: proc "stdcall" (this: ^ISwapChain1, pDesc: ^SWAP_CHAIN_FULLSCREEN_DESC) -> HRESULT, + GetHwnd: proc "stdcall" (this: ^ISwapChain1, pHwnd: ^HWND) -> HRESULT, + GetCoreWindow: proc "stdcall" (this: ^ISwapChain1, refiid: ^IID, ppUnk: ^rawptr) -> HRESULT, + Present1: proc "stdcall" (this: ^ISwapChain1, SyncInterval: u32, PresentFlags: u32, pPresentParameters: ^PRESENT_PARAMETERS) -> HRESULT, + IsTemporaryMonoSupported: proc "stdcall" (this: ^ISwapChain1) -> BOOL, + GetRestrictToOutput: proc "stdcall" (this: ^ISwapChain1, ppRestrictToOutput: ^^IOutput) -> HRESULT, + SetBackgroundColor: proc "stdcall" (this: ^ISwapChain1, pColor: ^RGBA) -> HRESULT, + GetBackgroundColor: proc "stdcall" (this: ^ISwapChain1, pColor: ^RGBA) -> HRESULT, + SetRotation: proc "stdcall" (this: ^ISwapChain1, Rotation: MODE_ROTATION) -> HRESULT, + GetRotation: proc "stdcall" (this: ^ISwapChain1, pRotation: ^MODE_ROTATION) -> HRESULT, +} + +IFactory2_UUID_STRING :: "50C83A1C-E072-4C48-87B0-3630FA36A6D0" +IFactory2_UUID := &IID{0x50C83A1C, 0xE072, 0x4C48, {0x87, 0xB0, 0x36, 0x30, 0xFA, 0x36, 0xA6, 0xD0}} +IFactory2 :: struct #raw_union { + #subtype idxgifactory1: IFactory1, + using idxgifactory2_vtable: ^IFactory2_VTable, +} +IFactory2_VTable :: struct { + using idxgifactory1_vtable: IFactory1_VTable, + IsWindowedStereoEnabled: proc "stdcall" (this: ^IFactory2) -> BOOL, + CreateSwapChainForHwnd: proc "stdcall" (this: ^IFactory2, pDevice: ^IUnknown, hWnd: HWND, pDesc: ^SWAP_CHAIN_DESC1, pFullscreenDesc: ^SWAP_CHAIN_FULLSCREEN_DESC, pRestrictToOutput: ^IOutput, ppSwapChain: ^^ISwapChain1) -> HRESULT, + CreateSwapChainForCoreWindow: proc "stdcall" (this: ^IFactory2, pDevice: ^IUnknown, pWindow: ^IUnknown, pDesc: ^SWAP_CHAIN_DESC1, pRestrictToOutput: ^IOutput, ppSwapChain: ^^ISwapChain1) -> HRESULT, + GetSharedResourceAdapterLuid: proc "stdcall" (this: ^IFactory2, hResource: HANDLE, pLuid: ^LUID) -> HRESULT, + RegisterStereoStatusWindow: proc "stdcall" (this: ^IFactory2, WindowHandle: HWND, wMsg: u32, pdwCookie: ^u32) -> HRESULT, + RegisterStereoStatusEvent: proc "stdcall" (this: ^IFactory2, hEvent: HANDLE, pdwCookie: ^u32) -> HRESULT, + UnregisterStereoStatus: proc "stdcall" (this: ^IFactory2, dwCookie: u32), + RegisterOcclusionStatusWindow: proc "stdcall" (this: ^IFactory2, WindowHandle: HWND, wMsg: u32, pdwCookie: ^u32) -> HRESULT, + RegisterOcclusionStatusEvent: proc "stdcall" (this: ^IFactory2, hEvent: HANDLE, pdwCookie: ^u32) -> HRESULT, + UnregisterOcclusionStatus: proc "stdcall" (this: ^IFactory2, dwCookie: u32), + CreateSwapChainForComposition: proc "stdcall" (this: ^IFactory2, pDevice: ^IUnknown, pDesc: ^SWAP_CHAIN_DESC1, pRestrictToOutput: ^IOutput, ppSwapChain: ^^ISwapChain1) -> HRESULT, +} +GRAPHICS_PREEMPTION_GRANULARITY :: enum i32 { + DMA_BUFFER_BOUNDARY = 0, + PRIMITIVE_BOUNDARY = 1, + TRIANGLE_BOUNDARY = 2, + PIXEL_BOUNDARY = 3, + INSTRUCTION_BOUNDARY = 4, +} + +COMPUTE_PREEMPTION_GRANULARITY :: enum i32 { + DMA_BUFFER_BOUNDARY = 0, + DISPATCH_BOUNDARY = 1, + THREAD_GROUP_BOUNDARY = 2, + THREAD_BOUNDARY = 3, + INSTRUCTION_BOUNDARY = 4, +} + +ADAPTER_DESC2 :: struct { + Description: [128]i16, + VendorId: u32, + DeviceId: u32, + SubSysId: u32, + Revision: u32, + DedicatedVideoMemory: SIZE_T, + DedicatedSystemMemory: SIZE_T, + SharedSystemMemory: SIZE_T, + AdapterLuid: LUID, + Flags: u32, + GraphicsPreemptionGranularity: GRAPHICS_PREEMPTION_GRANULARITY, + ComputePreemptionGranularity: COMPUTE_PREEMPTION_GRANULARITY, +} + + +IAdapter2_UUID_STRING :: "0AA1AE0A-FA0E-4B84-8644-E05FF8E5ACB5" +IAdapter2_UUID := &IID{0x0AA1AE0A, 0xFA0E, 0x4B84, {0x86, 0x44, 0xE0, 0x5F, 0xF8, 0xE5, 0xAC, 0xB5}} +IAdapter2 :: struct #raw_union { + #subtype idxgiadapter1: IAdapter1, + using idxgiadapter2_vtable: ^IAdapter2_VTable, +} +IAdapter2_VTable :: struct { + using idxgiadapter1_vtable: IAdapter1_VTable, + GetDesc2: proc "stdcall" (this: ^IAdapter2, pDesc: ^ADAPTER_DESC2) -> HRESULT, +} + +IOutput1_UUID_STRING :: "00CDDEA8-939B-4B83-A340-A685226666CC" +IOutput1_UUID := &IID{0x00CDDEA8, 0x939B, 0x4B83, {0xA3, 0x40, 0xA6, 0x85, 0x22, 0x66, 0x66, 0xCC}} +IOutput1 :: struct #raw_union { + #subtype idxgioutput: IOutput, + using idxgioutput1_vtable: ^IOutput1_VTable, +} +IOutput1_VTable :: struct { + using idxgioutput_vtable: IOutput_VTable, + GetDisplayModeList1: proc "stdcall" (this: ^IOutput1, EnumFormat: FORMAT, Flags: u32, pNumModes: ^u32, pDesc: ^MODE_DESC1) -> HRESULT, + FindClosestMatchingMode1: proc "stdcall" (this: ^IOutput1, pModeToMatch: ^MODE_DESC1, pClosestMatch: ^MODE_DESC1, pConcernedDevice: ^IUnknown) -> HRESULT, + GetDisplaySurfaceData1: proc "stdcall" (this: ^IOutput1, pDestination: ^IResource) -> HRESULT, + DuplicateOutput: proc "stdcall" (this: ^IOutput1, pDevice: ^IUnknown, ppOutputDuplication: ^^IOutputDuplication) -> HRESULT, +} +IDevice3_UUID_STRING :: "6007896C-3244-4AFD-BF18-A6D3BEDA5023" +IDevice3_UUID := &IID{0x6007896C, 0x3244, 0x4AFD, {0xBF, 0x18, 0xA6, 0xD3, 0xBE, 0xDA, 0x50, 0x23}} +IDevice3 :: struct #raw_union { + #subtype idxgidevice2: IDevice2, + using idxgidevice3_vtable: ^IDevice3_VTable, +} +IDevice3_VTable :: struct { + using idxgidevice2_vtable: IDevice2_VTable, + Trim: proc "stdcall" (this: ^IDevice3), +} +MATRIX_3X2_F :: struct { + _11: f32, + _12: f32, + _21: f32, + _22: f32, + _31: f32, + _32: f32, +} + + +ISwapChain2_UUID_STRING :: "A8BE2AC4-199F-4946-B331-79599FB98DE7" +ISwapChain2_UUID := &IID{0xA8BE2AC4, 0x199F, 0x4946, {0xB3, 0x31, 0x79, 0x59, 0x9F, 0xB9, 0x8D, 0xE7}} +ISwapChain2 :: struct #raw_union { + #subtype idxgiswapchain1: ISwapChain1, + using idxgiswapchain2_vtable: ^ISwapChain2_VTable, +} +ISwapChain2_VTable :: struct { + using idxgiswapchain1_vtable: ISwapChain1_VTable, + SetSourceSize: proc "stdcall" (this: ^ISwapChain2, Width: u32, Height: u32) -> HRESULT, + GetSourceSize: proc "stdcall" (this: ^ISwapChain2, pWidth: ^u32, pHeight: ^u32) -> HRESULT, + SetMaximumFrameLatency: proc "stdcall" (this: ^ISwapChain2, MaxLatency: u32) -> HRESULT, + GetMaximumFrameLatency: proc "stdcall" (this: ^ISwapChain2, pMaxLatency: ^u32) -> HRESULT, + GetFrameLatencyWaitableObject: proc "stdcall" (this: ^ISwapChain2) -> HANDLE, + SetMatrixTransform: proc "stdcall" (this: ^ISwapChain2, pMatrix: ^MATRIX_3X2_F) -> HRESULT, + GetMatrixTransform: proc "stdcall" (this: ^ISwapChain2, pMatrix: ^MATRIX_3X2_F) -> HRESULT, +} + +IOutput2_UUID_STRING :: "595E39D1-2724-4663-99B1-DA969DE28364" +IOutput2_UUID := &IID{0x595E39D1, 0x2724, 0x4663, {0x99, 0xB1, 0xDA, 0x96, 0x9D, 0xE2, 0x83, 0x64}} +IOutput2 :: struct #raw_union { + #subtype idxgioutput1: IOutput1, + using idxgioutput2_vtable: ^IOutput2_VTable, +} +IOutput2_VTable :: struct { + using idxgioutput1_vtable: IOutput1_VTable, + SupportsOverlays: proc "stdcall" (this: ^IOutput2) -> BOOL, +} + +IFactory3_UUID_STRING :: "25483823-CD46-4C7D-86CA-47AA95B837BD" +IFactory3_UUID := &IID{0x25483823, 0xCD46, 0x4C7D, {0x86, 0xCA, 0x47, 0xAA, 0x95, 0xB8, 0x37, 0xBD}} +IFactory3 :: struct #raw_union { + #subtype idxgifactory2: IFactory2, + using idxgifactory3_vtable: ^IFactory3_VTable, +} +IFactory3_VTable :: struct { + using idxgifactory2_vtable: IFactory2_VTable, + GetCreationFlags: proc "stdcall" (this: ^IFactory3) -> u32, +} +DECODE_SWAP_CHAIN_DESC :: struct { + Flags: u32, +} + +MULTIPLANE_OVERLAY_YCbCr_FLAGS :: enum u32 { // TODO: convert to bit_set + NOMINAL_RANGE = 0x1, + BT709 = 0x2, + xvYCC = 0x4, +} + + +IDecodeSwapChain_UUID_STRING :: "2633066B-4514-4C7A-8FD8-12EA98059D18" +IDecodeSwapChain_UUID := &IID{0x2633066B, 0x4514, 0x4C7A, {0x8F, 0xD8, 0x12, 0xEA, 0x98, 0x05, 0x9D, 0x18}} +IDecodeSwapChain :: struct #raw_union { + #subtype iunknown: IUnknown, + using idxgidecodeswapchain_vtable: ^IDecodeSwapChain_VTable, +} +IDecodeSwapChain_VTable :: struct { + using iunknown_vtable: IUnknown_VTable, + PresentBuffer: proc "stdcall" (this: ^IDecodeSwapChain, BufferToPresent: u32, SyncInterval: u32, Flags: u32) -> HRESULT, + SetSourceRect: proc "stdcall" (this: ^IDecodeSwapChain, pRect: ^RECT) -> HRESULT, + SetTargetRect: proc "stdcall" (this: ^IDecodeSwapChain, pRect: ^RECT) -> HRESULT, + SetDestSize: proc "stdcall" (this: ^IDecodeSwapChain, Width: u32, Height: u32) -> HRESULT, + GetSourceRect: proc "stdcall" (this: ^IDecodeSwapChain, pRect: ^RECT) -> HRESULT, + GetTargetRect: proc "stdcall" (this: ^IDecodeSwapChain, pRect: ^RECT) -> HRESULT, + GetDestSize: proc "stdcall" (this: ^IDecodeSwapChain, pWidth: ^u32, pHeight: ^u32) -> HRESULT, + SetColorSpace: proc "stdcall" (this: ^IDecodeSwapChain, ColorSpace: MULTIPLANE_OVERLAY_YCbCr_FLAGS) -> HRESULT, + GetColorSpace: proc "stdcall" (this: ^IDecodeSwapChain) -> MULTIPLANE_OVERLAY_YCbCr_FLAGS, +} + +IFactoryMedia_UUID_STRING :: "41E7D1F2-A591-4F7B-A2E5-FA9C843E1C12" +IFactoryMedia_UUID := &IID{0x41E7D1F2, 0xA591, 0x4F7B, {0xA2, 0xE5, 0xFA, 0x9C, 0x84, 0x3E, 0x1C, 0x12}} +IFactoryMedia :: struct #raw_union { + #subtype iunknown: IUnknown, + using idxgifactorymedia_vtable: ^IFactoryMedia_VTable, +} +IFactoryMedia_VTable :: struct { + using iunknown_vtable: IUnknown_VTable, + CreateSwapChainForCompositionSurfaceHandle: proc "stdcall" (this: ^IFactoryMedia, pDevice: ^IUnknown, hSurface: HANDLE, pDesc: ^SWAP_CHAIN_DESC1, pRestrictToOutput: ^IOutput, ppSwapChain: ^^ISwapChain1) -> HRESULT, + CreateDecodeSwapChainForCompositionSurfaceHandle: proc "stdcall" (this: ^IFactoryMedia, pDevice: ^IUnknown, hSurface: HANDLE, pDesc: ^DECODE_SWAP_CHAIN_DESC, pYuvDecodeBuffers: ^IResource, pRestrictToOutput: ^IOutput, ppSwapChain: ^^IDecodeSwapChain) -> HRESULT, +} +FRAME_PRESENTATION_MODE :: enum i32 { + COMPOSED = 0, + OVERLAY = 1, + NONE = 2, + COMPOSITION_FAILURE = 3, +} + +FRAME_STATISTICS_MEDIA :: struct { + PresentCount: u32, + PresentRefreshCount: u32, + SyncRefreshCount: u32, + SyncQPCTime: LARGE_INTEGER, + SyncGPUTime: LARGE_INTEGER, + CompositionMode: FRAME_PRESENTATION_MODE, + ApprovedPresentDuration: u32, +} + + +ISwapChainMedia_UUID_STRING :: "DD95B90B-F05F-4F6A-BD65-25BFB264BD84" +ISwapChainMedia_UUID := &IID{0xDD95B90B, 0xF05F, 0x4F6A, {0xBD, 0x65, 0x25, 0xBF, 0xB2, 0x64, 0xBD, 0x84}} +ISwapChainMedia :: struct #raw_union { + #subtype iunknown: IUnknown, + using idxgiswapchainmedia_vtable: ^ISwapChainMedia_VTable, +} +ISwapChainMedia_VTable :: struct { + using iunknown_vtable: IUnknown_VTable, + GetFrameStatisticsMedia: proc "stdcall" (this: ^ISwapChainMedia, pStats: ^FRAME_STATISTICS_MEDIA) -> HRESULT, + SetPresentDuration: proc "stdcall" (this: ^ISwapChainMedia, Duration: u32) -> HRESULT, + CheckPresentDurationSupport: proc "stdcall" (this: ^ISwapChainMedia, DesiredPresentDuration: u32, pClosestSmallerPresentDuration: ^u32, pClosestLargerPresentDuration: ^u32) -> HRESULT, +} +OVERLAY_SUPPORT_FLAG :: enum u32 { // TODO: convert to bit_set + DIRECT = 0x1, + SCALING = 0x2, +} + + +IOutput3_UUID_STRING :: "8A6BB301-7E7E-41F4-A8E0-5B32F7F99B18" +IOutput3_UUID := &IID{0x8A6BB301, 0x7E7E, 0x41F4, {0xA8, 0xE0, 0x5B, 0x32, 0xF7, 0xF9, 0x9B, 0x18}} +IOutput3 :: struct #raw_union { + #subtype idxgioutput2: IOutput2, + using idxgioutput3_vtable: ^IOutput3_VTable, +} +IOutput3_VTable :: struct { + using idxgioutput2_vtable: IOutput2_VTable, + CheckOverlaySupport: proc "stdcall" (this: ^IOutput3, EnumFormat: FORMAT, pConcernedDevice: ^IUnknown, pFlags: ^u32) -> HRESULT, +} +SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG :: enum u32 { // TODO: convert to bit_set + PRESENT = 0x1, + OVERLAY_PRESENT = 0x2, +} + + +ISwapChain3_UUID_STRING :: "94D99BDB-F1F8-4AB0-B236-7DA0170EDAB1" +ISwapChain3_UUID := &IID{0x94D99BDB, 0xF1F8, 0x4AB0, {0xB2, 0x36, 0x7D, 0xA0, 0x17, 0x0E, 0xDA, 0xB1}} +ISwapChain3 :: struct #raw_union { + #subtype idxgiswapchain2: ISwapChain2, + using idxgiswapchain3_vtable: ^ISwapChain3_VTable, +} +ISwapChain3_VTable :: struct { + using idxgiswapchain2_vtable: ISwapChain2_VTable, + GetCurrentBackBufferIndex: proc "stdcall" (this: ^ISwapChain3) -> u32, + CheckColorSpaceSupport: proc "stdcall" (this: ^ISwapChain3, ColorSpace: COLOR_SPACE_TYPE, pColorSpaceSupport: ^u32) -> HRESULT, + SetColorSpace1: proc "stdcall" (this: ^ISwapChain3, ColorSpace: COLOR_SPACE_TYPE) -> HRESULT, + ResizeBuffers1: proc "stdcall" (this: ^ISwapChain3, BufferCount: u32, Width: u32, Height: u32, Format: FORMAT, SwapChainFlags: u32, pCreationNodeMask: ^u32, ppPresentQueue: ^^IUnknown) -> HRESULT, +} +OVERLAY_COLOR_SPACE_SUPPORT_FLAG :: enum u32 { // TODO: convert to bit_set + PRESENT = 0x1, +} + + +IOutput4_UUID_STRING :: "DC7DCA35-2196-414D-9F53-617884032A60" +IOutput4_UUID := &IID{0xDC7DCA35, 0x2196, 0x414D, {0x9F, 0x53, 0x61, 0x78, 0x84, 0x03, 0x2A, 0x60}} +IOutput4 :: struct #raw_union { + #subtype idxgioutput3: IOutput3, + using idxgioutput4_vtable: ^IOutput4_VTable, +} +IOutput4_VTable :: struct { + using idxgioutput3_vtable: IOutput3_VTable, + CheckOverlayColorSpaceSupport: proc "stdcall" (this: ^IOutput4, Format: FORMAT, ColorSpace: COLOR_SPACE_TYPE, pConcernedDevice: ^IUnknown, pFlags: ^u32) -> HRESULT, +} + +IFactory4_UUID_STRING :: "1BC6EA02-EF36-464F-BF0C-21CA39E5168A" +IFactory4_UUID := &IID{0x1BC6EA02, 0xEF36, 0x464F, {0xBF, 0x0C, 0x21, 0xCA, 0x39, 0xE5, 0x16, 0x8A}} +IFactory4 :: struct #raw_union { + #subtype idxgifactory3: IFactory3, + using idxgifactory4_vtable: ^IFactory4_VTable, +} +IFactory4_VTable :: struct { + using idxgifactory3_vtable: IFactory3_VTable, + EnumAdapterByLuid: proc "stdcall" (this: ^IFactory4, AdapterLuid: LUID, riid: ^IID, ppvAdapter: ^rawptr) -> HRESULT, + EnumWarpAdapter: proc "stdcall" (this: ^IFactory4, riid: ^IID, ppvAdapter: ^rawptr) -> HRESULT, +} +MEMORY_SEGMENT_GROUP :: enum i32 { + LOCAL = 0, + NON_LOCAL = 1, +} + +QUERY_VIDEO_MEMORY_INFO :: struct { + Budget: u64, + CurrentUsage: u64, + AvailableForReservation: u64, + CurrentReservation: u64, +} + + +IAdapter3_UUID_STRING :: "645967A4-1392-4310-A798-8053CE3E93FD" +IAdapter3_UUID := &IID{0x645967A4, 0x1392, 0x4310, {0xA7, 0x98, 0x80, 0x53, 0xCE, 0x3E, 0x93, 0xFD}} +IAdapter3 :: struct #raw_union { + #subtype idxgiadapter2: IAdapter2, + using idxgiadapter3_vtable: ^IAdapter3_VTable, +} +IAdapter3_VTable :: struct { + using idxgiadapter2_vtable: IAdapter2_VTable, + RegisterHardwareContentProtectionTeardownStatusEvent: proc "stdcall" (this: ^IAdapter3, hEvent: HANDLE, pdwCookie: ^u32) -> HRESULT, + UnregisterHardwareContentProtectionTeardownStatus: proc "stdcall" (this: ^IAdapter3, dwCookie: u32), + QueryVideoMemoryInfo: proc "stdcall" (this: ^IAdapter3, NodeIndex: u32, MemorySegmentGroup: MEMORY_SEGMENT_GROUP, pVideoMemoryInfo: ^QUERY_VIDEO_MEMORY_INFO) -> HRESULT, + SetVideoMemoryReservation: proc "stdcall" (this: ^IAdapter3, NodeIndex: u32, MemorySegmentGroup: MEMORY_SEGMENT_GROUP, Reservation: u64) -> HRESULT, + RegisterVideoMemoryBudgetChangeNotificationEvent: proc "stdcall" (this: ^IAdapter3, hEvent: HANDLE, pdwCookie: ^u32) -> HRESULT, + UnregisterVideoMemoryBudgetChangeNotification: proc "stdcall" (this: ^IAdapter3, dwCookie: u32), +} + +ERROR_ACCESS_DENIED :: HRESULT(-2005270485) //0x887A002B +ERROR_ACCESS_LOST :: HRESULT(-2005270490) //0x887A0026 +ERROR_ALREADY_EXISTS :: HRESULT(-2005270474) //0x887A0036 +ERROR_CANNOT_PROTECT_CONTENT :: HRESULT(-2005270486) //0x887A002A +ERROR_DEVICE_HUNG :: HRESULT(-2005270522) //0x887A0006 +ERROR_DEVICE_REMOVED :: HRESULT(-2005270523) //0x887A0005 +ERROR_DEVICE_RESET :: HRESULT(-2005270521) //0x887A0007 +ERROR_DRIVER_INTERNAL_ERROR :: HRESULT(-2005270496) //0x887A0020 +ERROR_FRAME_STATISTICS_DISJOINT :: HRESULT(-2005270517) //0x887A000B +ERROR_GRAPHICS_VIDPN_SOURCE_IN_USE :: HRESULT(-2005270516) //0x887A000C +ERROR_INVALID_CALL :: HRESULT(-2005270527) //0x887A0001 +ERROR_MORE_DATA :: HRESULT(-2005270525) //0x887A0003 +ERROR_NAME_ALREADY_EXISTS :: HRESULT(-2005270484) //0x887A002C +ERROR_NONEXCLUSIVE :: HRESULT(-2005270495) //0x887A0021 +ERROR_NOT_CURRENTLY_AVAILABLE :: HRESULT(-2005270494) //0x887A0022 +ERROR_NOT_FOUND :: HRESULT(-2005270526) //0x887A0002 +ERROR_REMOTE_CLIENT_DISCONNECTED :: HRESULT(-2005270493) //0x887A0023 +ERROR_REMOTE_OUTOFMEMORY :: HRESULT(-2005270492) //0x887A0024 +ERROR_RESTRICT_TO_OUTPUT_STALE :: HRESULT(-2005270487) //0x887A0029 +ERROR_SDK_COMPONENT_MISSING :: HRESULT(-2005270483) //0x887A002D +ERROR_SESSION_DISCONNECTED :: HRESULT(-2005270488) //0x887A0028 +ERROR_UNSUPPORTED :: HRESULT(-2005270524) //0x887A0004 +ERROR_WAIT_TIMEOUT :: HRESULT(-2005270489) //0x887A0027 +ERROR_WAS_STILL_DRAWING :: HRESULT(-2005270518) //0x887A000A + +STATUS_OCCLUDED :: HRESULT( 142213121) //0x087A0001 +STATUS_MODE_CHANGED :: HRESULT( 142213127) //0x087A0007 +STATUS_MODE_CHANGE_IN_PROGRESS :: HRESULT( 142213128) //0x087A0008 \ No newline at end of file diff --git a/vendor/ggpo/GGPO.lib b/vendor/ggpo/GGPO.lib new file mode 100644 index 000000000..70feb57da Binary files /dev/null and b/vendor/ggpo/GGPO.lib differ diff --git a/vendor/ggpo/LICENSE b/vendor/ggpo/LICENSE new file mode 100644 index 000000000..49e89062c --- /dev/null +++ b/vendor/ggpo/LICENSE @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) 2009-2019 GroundStorm Studios, LLC. (http://ggpo.net) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/vendor/ggpo/ggpo.odin b/vendor/ggpo/ggpo.odin new file mode 100644 index 000000000..c727e4a67 --- /dev/null +++ b/vendor/ggpo/ggpo.odin @@ -0,0 +1,526 @@ +package ggpo + +foreign import lib "GGPO.lib" + +import c "core:c/libc" + +Session :: distinct rawptr + +MAX_PLAYERS :: 4 +MAX_PREDICTION_FRAMES :: 8 +MAX_SPECTATORS :: 32 + +SPECTATOR_INPUT_INTERVAL :: 4 + +PlayerHandle :: distinct c.int +PlayerType :: enum c.int { + LOCAL, + REMOTE, + SPECTATOR, +} + +/* + * The Player structure used to describe players in add_player + * + * size: Should be set to the size_of(Player) + * + * type: One of the PlayerType values describing how inputs should be handled + * Local players must have their inputs updated every frame via + * add_local_inputs. Remote players values will come over the + * network. + * + * player_num: The player number. Should be between 1 and the number of players + * In the game (e.g. in a 2 player game, either 1 or 2). + * + * If type == PLAYERTYPE_REMOTE: + * + * remote.ip_address: The ip address of the ggpo session which will host this + * player. + * + * remote.port: The port where udp packets should be sent to reach this player. + * All the local inputs for this session will be sent to this player at + * ip_address:port. + * + */ + + +Player :: struct { + size: c.int, + type: PlayerType, + player_num: c.int, + using u: struct #raw_union { + local: struct {}, + remove: struct { + ip_address: [32]byte, + port: u16, + }, + }, +} + +LocalEndpoint :: struct { + player_num: c.int, +} + +ErrorCode :: enum c.int { + OK = 0, + SUCCESS = 0, + GENERAL_FAILURE = -1, + INVALID_SESSION = 1, + INVALID_PLAYER_HANDLE = 2, + PLAYER_OUT_OF_RANGE = 3, + PREDICTION_THRESHOLD = 4, + UNSUPPORTED = 5, + NOT_SYNCHRONIZED = 6, + IN_ROLLBACK = 7, + INPUT_DROPPED = 8, + PLAYER_DISCONNECTED = 9, + TOO_MANY_SPECTATORS = 10, + INVALID_REQUEST = 11, +} + +INVALID_HANDLE :: PlayerHandle(-1) + +/* + * The EventCode enumeration describes what type of event just happened. + * + * CONNECTED_TO_PEER - Handshake with the game running on the + * other side of the network has been completed. + * + * SYNCHRONIZING_WITH_PEER - Beginning the synchronization + * process with the client on the other end of the networking. The count + * and total fields in the u.synchronizing struct of the Event + * object indicate progress. + * + * SYNCHRONIZED_WITH_PEER - The synchronziation with this + * peer has finished. + * + * RUNNING - All the clients have synchronized. You may begin + * sending inputs with synchronize_inputs. + * + * DISCONNECTED_FROM_PEER - The network connection on + * the other end of the network has closed. + * + * TIMESYNC - The time synchronziation code has determined + * that this client is too far ahead of the other one and should slow + * down to ensure fairness. The u.timesync.frames_ahead parameter in + * the Event object indicates how many frames the client is. + * + */ +EventCode :: enum c.int { + CONNECTED_TO_PEER = 1000, + SYNCHRONIZING_WITH_PEER = 1001, + SYNCHRONIZED_WITH_PEER = 1002, + RUNNING = 1003, + DISCONNECTED_FROM_PEER = 1004, + TIMESYNC = 1005, + CONNECTION_INTERRUPTED = 1006, + CONNECTION_RESUMED = 1007, +} + +/* + * The Event structure contains an asynchronous event notification sent + * by the on_event callback. See EventCode, above, for a detailed + * explanation of each event. + */ + + Event :: struct { + code: EventCode, + using u: struct #raw_union { + connected: struct { + player: PlayerHandle, + }, + synchronizing: struct { + player: PlayerHandle, + count: c.int, + total: c.int, + }, + synchronized: struct { + player: PlayerHandle, + }, + disconnected: struct { + player: PlayerHandle, + }, + timesync: struct { + frames_ahead: c.int, + }, + connection_interrupted: struct { + player: PlayerHandle, + disconnect_timeout: c.int, + }, + connection_resumed: struct { + player: PlayerHandle, + }, + }, +} + +/* + * The SessionCallbacks structure contains the callback functions that + * your application must implement. GGPO.net will periodically call these + * functions during the game. All callback functions must be implemented. + */ +SessionCallbacks :: struct { + /* + * begin_game callback - This callback has been deprecated. You must + * implement it, but should ignore the 'game' parameter. + */ + begin_game: proc "c" (game: cstring) -> bool, + + /* + * save_game_state - The client should allocate a buffer, copy the + * entire contents of the current game state into it, and copy the + * length into the len parameter. Optionally, the client can compute + * a checksum of the data and store it in the checksum argument. + */ + save_game_state: proc "c" (buffer: ^[^]byte, len: ^c.int, checksum: ^c.int, frame: c.int) -> bool, + + /* + * load_game_state - GGPO.net will call this function at the beginning + * of a rollback. The buffer and len parameters contain a previously + * saved state returned from the save_game_state function. The client + * should make the current game state match the state contained in the + * buffer. + */ + load_game_state: proc "c" (buffer: [^]byte, len: c.int) -> bool, + + /* + * log_game_state - Used in diagnostic testing. The client should use + * the log function to write the contents of the specified save + * state in a human readible form. + */ + log_game_state: proc "c" (filename: cstring, buffer: [^]byte, len: c.int) -> bool, + + /* + * free_buffer - Frees a game state allocated in save_game_state. You + * should deallocate the memory contained in the buffer. + */ + free_buffer: proc "c" (buffer: rawptr), + + /* + * advance_frame - Called during a rollback. You should advance your game + * state by exactly one frame. Before each frame, call synchronize_input + * to retrieve the inputs you should use for that frame. After each frame, + * you should call advance_frame to notify GGPO.net that you're + * finished. + * + * The flags parameter is reserved. It can safely be ignored at this time. + */ + advance_frame: proc "c" (flags: c.int) -> bool, + + /* + * on_event - Notification that something has happened. See the EventCode + * structure above for more information. + */ + on_event: proc "c" (info: ^Event) -> bool, +} + +/* + * The NetworkStats function contains some statistics about the current + * session. + * + * network.send_queue_len - The length of the queue containing UDP packets + * which have not yet been acknowledged by the end client. The length of + * the send queue is a rough indication of the quality of the connection. + * The longer the send queue, the higher the round-trip time between the + * clients. The send queue will also be longer than usual during high + * packet loss situations. + * + * network.recv_queue_len - The number of inputs currently buffered by the + * GGPO.net network layer which have yet to be validated. The length of + * the prediction queue is roughly equal to the current frame number + * minus the frame number of the last packet in the remote queue. + * + * network.ping - The roundtrip packet transmission time as calcuated + * by GGPO.net. This will be roughly equal to the actual round trip + * packet transmission time + 2 the interval at which you call idle + * or advance_frame. + * + * network.kbps_sent - The estimated bandwidth used between the two + * clients, in kilobits per second. + * + * timesync.local_frames_behind - The number of frames GGPO.net calculates + * that the local client is behind the remote client at this instant in + * time. For example, if at this instant the current game client is running + * frame 1002 and the remote game client is running frame 1009, this value + * will mostly likely roughly equal 7. + * + * timesync.remote_frames_behind - The same as local_frames_behind, but + * calculated from the perspective of the remote player. + * + */ +NetworkStats :: struct { + network: struct { + send_queue_len: c.int, + recv_queue_len: c.int, + ping: c.int, + kbps_sent: c.int, + }, + timesync: struct { + local_frames_behind: c.int, + remote_frames_behind: c.int, + }, +} + +@(default_calling_convention="c") +@(link_prefix="ggpo_") +foreign lib { + /* + * start_session -- + * + * Used to being a new GGPO.net session. The ggpo object returned by start_session + * uniquely identifies the state for this session and should be passed to all other + * functions. + * + * session - An out parameter to the new ggpo session object. + * + * cb - A SessionCallbacks structure which contains the callbacks you implement + * to help GGPO.net synchronize the two games. You must implement all functions in + * cb, even if they do nothing but 'return true'; + * + * game - The name of the game. This is used internally for GGPO for logging purposes only. + * + * num_players - The number of players which will be in this game. The number of players + * per session is fixed. If you need to change the number of players or any player + * disconnects, you must start a new session. + * + * input_size - The size of the game inputs which will be passsed to add_local_input. + * + * local_port - The port GGPO should bind to for UDP traffic. + */ + start_session :: proc(session: ^^Session, + cb: ^SessionCallbacks, + game: cstring, + num_players: c.int, + input_size: c.int, + localport: u16) -> ErrorCode --- + + + /* + * add_player -- + * + * Must be called for each player in the session (e.g. in a 3 player session, must + * be called 3 times). + * + * player - A Player struct used to describe the player. + * + * handle - An out parameter to a handle used to identify this player in the future. + * (e.g. in the on_event callbacks). + */ + add_player :: proc(session: ^Session, + player: ^Player, + handle: ^PlayerHandle) -> ErrorCode --- + + + /* + * start_synctest -- + * + * Used to being a new GGPO.net sync test session. During a sync test, every + * frame of execution is run twice: once in prediction mode and once again to + * verify the result of the prediction. If the checksums of your save states + * do not match, the test is aborted. + * + * cb - A SessionCallbacks structure which contains the callbacks you implement + * to help GGPO.net synchronize the two games. You must implement all functions in + * cb, even if they do nothing but 'return true'; + * + * game - The name of the game. This is used internally for GGPO for logging purposes only. + * + * num_players - The number of players which will be in this game. The number of players + * per session is fixed. If you need to change the number of players or any player + * disconnects, you must start a new session. + * + * input_size - The size of the game inputs which will be passsed to add_local_input. + * + * frames - The number of frames to run before verifying the prediction. The + * recommended value is 1. + * + */ + start_synctest :: proc(session: ^^Session, + cb: ^SessionCallbacks, + game: cstring, + num_players: c.int, + input_size: c.int, + frames: c.int) -> ErrorCode --- + + + /* + * start_spectating -- + * + * Start a spectator session. + * + * cb - A SessionCallbacks structure which contains the callbacks you implement + * to help GGPO.net synchronize the two games. You must implement all functions in + * cb, even if they do nothing but 'return true'; + * + * game - The name of the game. This is used internally for GGPO for logging purposes only. + * + * num_players - The number of players which will be in this game. The number of players + * per session is fixed. If you need to change the number of players or any player + * disconnects, you must start a new session. + * + * input_size - The size of the game inputs which will be passsed to add_local_input. + * + * local_port - The port GGPO should bind to for UDP traffic. + * + * host_ip - The IP address of the host who will serve you the inputs for the game. Any + * player partcipating in the session can serve as a host. + * + * host_port - The port of the session on the host + */ + start_spectating :: proc(session: ^^Session, + cb: ^SessionCallbacks, + game: cstring, + num_players: c.int, + input_size: c.int, + local_port: u16, + host_ip: cstring, + host_port: u16) -> ErrorCode --- + + /* + * close_session -- + * Used to close a session. You must call close_session to + * free the resources allocated in start_session. + */ + close_session :: proc(session: ^Session) -> ErrorCode --- + + + /* + * set_frame_delay -- + * + * Change the amount of frames ggpo will delay local input. Must be called + * before the first call to synchronize_input. + */ + set_frame_delay :: proc(session: ^Session, + player: PlayerHandle, + frame_delay: c.int) -> ErrorCode --- + + /* + * idle -- + * Should be called periodically by your application to give GGPO.net + * a chance to do some work. Most packet transmissions and rollbacks occur + * in idle. + * + * timeout - The amount of time GGPO.net is allowed to spend in this function, + * in milliseconds. + */ + idle :: proc(session: ^Session, + timeout: c.int) -> ErrorCode --- + + /* + * add_local_input -- + * + * Used to notify GGPO.net of inputs that should be trasmitted to remote + * players. add_local_input must be called once every frame for + * all player of type PLAYERTYPE_LOCAL. + * + * player - The player handle returned for this player when you called + * add_local_player. + * + * values - The controller inputs for this player. + * + * size - The size of the controller inputs. This must be exactly equal to the + * size passed into start_session. + */ + add_local_input :: proc(session: ^Session, + player: PlayerHandle, + values: rawptr, + size: c.int) -> ErrorCode --- + + /* + * synchronize_input -- + * + * You should call synchronize_input before every frame of execution, + * including those frames which happen during rollback. + * + * values - When the function returns, the values parameter will contain + * inputs for this frame for all players. The values array must be at + * least (size * players) large. + * + * size - The size of the values array. + * + * disconnect_flags - Indicated whether the input in slot (1 << flag) is + * valid. If a player has disconnected, the input in the values array for + * that player will be zeroed and the i-th flag will be set. For example, + * if only player 3 has disconnected, disconnect flags will be 8 (i.e. 1 << 3). + */ + synchronize_input :: proc(session: ^Session, + values: rawptr, + size: c.int, + disconnect_flags: ^c.int) -> ErrorCode --- + + /* + * disconnect_player -- + * + * Disconnects a remote player from a game. Will return ERRORCODE_PLAYER_DISCONNECTED + * if you try to disconnect a player who has already been disconnected. + */ + disconnect_player :: proc(session: ^Session, + player: PlayerHandle) -> ErrorCode --- + + /* + * advance_frame -- + * + * You should call advance_frame to notify GGPO.net that you have + * advanced your gamestate by a single frame. You should call this everytime + * you advance the gamestate by a frame, even during rollbacks. GGPO.net + * may call your save_state callback before this function returns. + */ + advance_frame :: proc(session: ^Session) -> ErrorCode --- + + /* + * get_network_stats -- + * + * Used to fetch some statistics about the quality of the network connection. + * + * player - The player handle returned from the add_player function you used + * to add the remote player. + * + * stats - Out parameter to the network statistics. + */ + get_network_stats :: proc(session: ^Session, + player: PlayerHandle, + stats: ^NetworkStats) -> ErrorCode --- + + /* + * set_disconnect_timeout -- + * + * Sets the disconnect timeout. The session will automatically disconnect + * from a remote peer if it has not received a packet in the timeout window. + * You will be notified of the disconnect via a EVENTCODE_DISCONNECTED_FROM_PEER + * event. + * + * Setting a timeout value of 0 will disable automatic disconnects. + * + * timeout - The time in milliseconds to wait before disconnecting a peer. + */ + set_disconnect_timeout :: proc(session: ^Session, + timeout: c.int) -> ErrorCode --- + + /* + * set_disconnect_notify_start -- + * + * The time to wait before the first EVENTCODE_NETWORK_INTERRUPTED timeout + * will be sent. + * + * timeout - The amount of time which needs to elapse without receiving a packet + * before the EVENTCODE_NETWORK_INTERRUPTED event is sent. + */ + set_disconnect_notify_start :: proc(session: ^Session, + timeout: c.int) -> ErrorCode --- + + /* + * log -- + * + * Used to write to the ggpo.net log. In the current versions of the + * SDK, a log file is only generated if the "quark.log" environment + * variable is set to 1. This will change in future versions of the + * SDK. + */ + log :: proc(session: ^Session, fmt: cstring, #c_vararg args: ..any) --- + /* + * logv -- + * + * A varargs compatible version of log. See log for + * more details. + */ + logv :: proc(session: ^Session, fmt: cstring, args: c.va_list) --- +} \ No newline at end of file diff --git a/vendor/glfw/native.odin b/vendor/glfw/native.odin index 871c42af9..902b30656 100644 --- a/vendor/glfw/native.odin +++ b/vendor/glfw/native.odin @@ -1,6 +1,6 @@ package glfw -when ODIN_OS == "windows" { +when ODIN_OS == .Windows { import win32 "core:sys/windows" foreign import glfw { "lib/glfw3.lib", "system:user32.lib", "system:gdi32.lib", "system:shell32.lib" } @@ -12,7 +12,7 @@ when ODIN_OS == "windows" { GetWin32Window :: proc(window: WindowHandle) -> win32.HWND --- GetWGLContext :: proc(window: WindowHandle) -> rawptr --- } -} else when ODIN_OS == "linux" { +} else when ODIN_OS == .Linux { // TODO: Native Linux // Display* glfwGetX11Display(void); // RRCrtc glfwGetX11Adapter(GLFWmonitor* monitor); @@ -24,7 +24,7 @@ when ODIN_OS == "windows" { // struct wl_display* glfwGetWaylandDisplay(void); // struct wl_output* glfwGetWaylandMonitor(GLFWmonitor* monitor); // struct wl_surface* glfwGetWaylandWindow(GLFWwindow* window); -} else when ODIN_OS == "darwin" { +} else when ODIN_OS == .Darwin { // TODO: Native Darwin // CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor* monitor); // id glfwGetCocoaWindow(GLFWwindow* window); diff --git a/vendor/microui/microui.odin b/vendor/microui/microui.odin index 947f59f40..7967f7f4b 100644 --- a/vendor/microui/microui.odin +++ b/vendor/microui/microui.odin @@ -309,7 +309,7 @@ init :: proc(ctx: ^Context) { ctx.draw_frame = default_draw_frame ctx._style = default_style ctx.style = &ctx._style - ctx.text_input = strings.builder_from_slice(ctx._text_store[:]) + ctx.text_input = strings.builder_from_bytes(ctx._text_store[:]) } begin :: proc(ctx: ^Context) { @@ -353,7 +353,7 @@ end :: proc(ctx: ^Context) { /* reset input state */ ctx.key_pressed_bits = {} // clear - strings.reset_builder(&ctx.text_input) + strings.builder_reset(&ctx.text_input) ctx.mouse_pressed_bits = {} // clear ctx.mouse_released_bits = {} // clear ctx.scroll_delta = Vec2{0, 0} diff --git a/vendor/miniaudio/common.odin b/vendor/miniaudio/common.odin index 62e32e8b1..1d64dc182 100644 --- a/vendor/miniaudio/common.odin +++ b/vendor/miniaudio/common.odin @@ -2,19 +2,26 @@ package miniaudio import "core:c" -when ODIN_OS == "windows" { foreign import lib "lib/miniaudio.lib" } -when ODIN_OS == "linux" { foreign import lib "lib/miniaudio.a" } +when ODIN_OS == .Windows { + foreign import lib "lib/miniaudio.lib" +} else when ODIN_OS == .Linux { + foreign import lib "lib/miniaudio.a" +} else { + foreign import lib "system:miniaudio" +} handle :: distinct rawptr -/* SIMD alignment in bytes. Currently set to 64 bytes in preparation for future AVX-512 optimizations. */ -SIMD_ALIGNMENT :: 64 +/* SIMD alignment in bytes. Currently set to 32 bytes in preparation for future AVX optimizations. */ +SIMD_ALIGNMENT :: 32 -LOG_LEVEL_DEBUG :: 4 -LOG_LEVEL_INFO :: 3 -LOG_LEVEL_WARNING :: 2 -LOG_LEVEL_ERROR :: 1 +log_level :: enum c.int { + LOG_LEVEL_DEBUG = 4, + LOG_LEVEL_INFO = 3, + LOG_LEVEL_WARNING = 2, + LOG_LEVEL_ERROR = 1, +} channel :: enum u8 { @@ -153,13 +160,13 @@ result :: enum c.int { FAILED_TO_STOP_BACKEND_DEVICE = -303, } + MIN_CHANNELS :: 1 -MAX_CHANNELS :: 32 +MAX_CHANNELS :: 254 MAX_FILTER_ORDER :: 8 - stream_format :: enum c.int { pcm = 0, } @@ -170,9 +177,9 @@ stream_layout :: enum c.int { } dither_mode :: enum c.int { - none = 0, - rectangle, - triangle, + none = 0, + rectangle, + triangle, } format :: enum c.int { @@ -219,7 +226,6 @@ channel_mix_mode :: enum c.int { rectangular = 0, /* Simple averaging based on the plane(s) the channel is sitting on. */ simple, /* Drop excess channels; zeroed out extra channels. */ custom_weights, /* Use custom weights specified in ma_channel_router_config. */ - planar_blend = rectangular, default = rectangular, } @@ -252,6 +258,10 @@ lcg :: struct { state: i32, } + +/* Spinlocks are 32-bit for compatibility reasons. */ +spinlock :: distinct u32 + NO_THREADING :: false when !NO_THREADING { @@ -267,10 +277,8 @@ thread_priority :: enum c.int { default = 0, } -/* Spinlocks are 32-bit for compatibility reasons. */ -spinlock :: distinct u32 -when ODIN_OS == "windows" { +when ODIN_OS == .Windows { thread :: distinct rawptr mutex :: distinct rawptr event :: distinct rawptr @@ -292,69 +300,6 @@ when ODIN_OS == "windows" { } } - -@(default_calling_convention="c", link_prefix="ma_") -foreign lib { - /* - Locks a spinlock. - */ - spinlock_lock :: proc(/*volatile*/ pSpinlock: ^spinlock) -> result --- - - /* - Locks a spinlock, but does not yield() when looping. - */ - spinlock_lock_noyield :: proc(/*volatile*/ pSpinlock: ^spinlock) -> result --- - - /* - Unlocks a spinlock. - */ - spinlock_unlock :: proc(/*volatile*/ pSpinlock: ^spinlock) -> result --- - - - /* - Creates a mutex. - - A mutex must be created from a valid context. A mutex is initially unlocked. - */ - mutex_init :: proc(pMutex: ^mutex) -> result --- - - /* - Deletes a mutex. - */ - mutex_uninit :: proc(pMutex: ^mutex) --- - - /* - Locks a mutex with an infinite timeout. - */ - mutex_lock :: proc(pMutex: ^mutex) --- - - /* - Unlocks a mutex. - */ - mutex_unlock :: proc(pMutex: ^mutex) --- - - - /* - Initializes an auto-reset event. - */ - event_init :: proc(pEvent: ^event) -> result --- - - /* - Uninitializes an auto-reset event. - */ - event_uninit :: proc(pEvent: ^event) --- - - /* - Waits for the specified auto-reset event to become signalled. - */ - event_wait :: proc(pEvent: ^event) -> result --- - - /* - Signals the specified auto-reset event. - */ - event_signal :: proc(pEvent: ^event) -> result --- -} - } /* NO_THREADING */ @@ -380,17 +325,22 @@ foreign lib { result_description :: proc(result: result) -> cstring --- /* - malloc(). Calls MA_MALLOC(). + malloc() */ malloc :: proc(sz: c.size_t, pAllocationCallbacks: ^allocation_callbacks) -> rawptr --- /* - realloc(). Calls MA_REALLOC(). + calloc() + */ + calloc :: proc(sz: c.size_t, pAllocationCallbacks: ^allocation_callbacks) -> rawptr --- + + /* + realloc() */ realloc :: proc(p: rawptr, sz: c.size_t, pAllocationCallbacks: ^allocation_callbacks) -> rawptr --- /* - free(). Calls MA_FREE(). + free() */ free :: proc(p: rawptr, pAllocationCallbacks: ^allocation_callbacks) --- @@ -412,7 +362,7 @@ foreign lib { /* Blends two frames in floating point format. */ - blend_f32 :: proc(pOut, pInA, pInB: ^f32, factor: f32, channels: u32) --- + blend_f32 :: proc(pOut, pInA, pInB: [^]f32, factor: f32, channels: u32) --- /* Retrieves the size of a sample in bytes for the given format. diff --git a/vendor/miniaudio/data_conversion.odin b/vendor/miniaudio/data_conversion.odin index 1a5c9d265..ffcf2fcb3 100644 --- a/vendor/miniaudio/data_conversion.odin +++ b/vendor/miniaudio/data_conversion.odin @@ -2,9 +2,13 @@ package miniaudio import "core:c" -when ODIN_OS == "windows" { foreign import lib "lib/miniaudio.lib" } -when ODIN_OS == "linux" { foreign import lib "lib/miniaudio.a" } - +when ODIN_OS == .Windows { + foreign import lib "lib/miniaudio.lib" +} else when ODIN_OS == .Linux { + foreign import lib "lib/miniaudio.a" +} else { + foreign import lib "system:miniaudio" +} /************************************************************************************************************************************************************ ************************************************************************************************************************************************************* @@ -32,77 +36,106 @@ linear_resampler_config :: struct { } linear_resampler :: struct { - config: linear_resampler_config, + config: linear_resampler_config, inAdvanceInt: u32, inAdvanceFrac: u32, inTimeInt: u32, inTimeFrac: u32, x0: struct #raw_union { - f32: [MAX_CHANNELS]f32, - s16: [MAX_CHANNELS]i16, + f32: [^]f32, + s16: [^]i16, }, /* The previous input frame. */ x1: struct #raw_union { - f32: [MAX_CHANNELS]f32, - s16: [MAX_CHANNELS]i16, + f32: [^]f32, + s16: [^]i16, }, /* The next input frame. */ lpf: lpf, + + /* Memory management. */ + _pHeap: rawptr, + _ownsHeap: b32, +} + +resampling_backend :: struct {} +resampling_backend_vtable :: struct { + onGetHeapSize: proc "c" (pUserData: rawptr, pConfig: ^resampler_config, pHeapSizeInBytes: ^c.size_t) -> result, + onInit: proc "c" (pUserData: rawptr, pConfig: ^resampler_config, pHeap: rawptr, ppBackend: ^^resampling_backend) -> result, + onUninit: proc "c" (pUserData: rawptr, pBackend: ^resampling_backend, pAllocationCallbacks: ^allocation_callbacks), + onProcess: proc "c" (pUserData: rawptr, pBackend: ^resampling_backend, pFramesIn: rawptr, pFrameCountIn: ^u64, pFramesOut: rawptr, pFrameCountOut: ^u64) -> result, + onSetRate: proc "c" (pUserData: rawptr, pBackend: ^resampling_backend, sampleRateIn: u32, sampleRateOut: u32) -> result, /* Optional. Rate changes will be disabled. */ + onGetInputLatency: proc "c" (pUserData: rawptr, pBackend: ^resampling_backend) -> u64, /* Optional. Latency will be reported as 0. */ + onGetOutputLatency: proc "c" (pUserData: rawptr, pBackend: ^resampling_backend) -> u64, /* Optional. Latency will be reported as 0. */ + onGetRequiredInputFrameCount: proc "c" (pUserData: rawptr, pBackend: ^resampling_backend, outputFrameCount: u64, pInputFrameCount: ^u64) -> result, /* Optional. Latency mitigation will be disabled. */ + onGetExpectedOutputFrameCount: proc "c" (pUserData: rawptr, pBackend: ^resampling_backend, inputFrameCount: u64, pOutputFrameCount: ^u64) -> result, /* Optional. Latency mitigation will be disabled. */ + onReset: proc "c" (pUserData: rawptr, pBackend: ^resampling_backend) -> result, } resample_algorithm :: enum { linear = 0, /* Fastest, lowest quality. Optional low-pass filtering. Default. */ - speex, + custom, } resampler_config :: struct { - format: format, /* Must be either ma_format_f32 or ma_format_s16. */ - channels: u32, - sampleRateIn: u32, - sampleRateOut: u32, - algorithm: resample_algorithm, + format: format, /* Must be either ma_format_f32 or ma_format_s16. */ + channels: u32, + sampleRateIn: u32, + sampleRateOut: u32, + algorithm: resample_algorithm, /* When set to ma_resample_algorithm_custom, pBackendVTable will be used. */ + pBackendVTable: ^resampling_backend_vtable, + pBackendUserData: rawptr, linear: struct { lpfOrder: u32, - lpfNyquistFactor: f64, - }, - speex: struct { - quality: c.int, /* 0 to 10. Defaults to 3. */ }, } resampler :: struct { - config: resampler_config, + pBackend: ^resampling_backend, + pBackendVTable: ^resampling_backend_vtable, + pBackendUserData: rawptr, + format: format, + channels: u32, + sampleRateIn: u32, + sampleRateOut: u32, state: struct #raw_union { linear: linear_resampler, - speex: struct { - pSpeexResamplerState: rawptr, /* SpeexResamplerState* */ - }, - }, + }, /* State for stock resamplers so we can avoid a malloc. For stock resamplers, pBackend will point here. */ + + /* Memory management. */ + _pHeap: rawptr, + _ownsHeap: b32, } @(default_calling_convention="c", link_prefix="ma_") foreign lib { linear_resampler_config_init :: proc(format: format, channels: u32, sampleRateIn, sampleRateOut: u32) -> linear_resampler_config --- - linear_resampler_init :: proc(pConfig: ^linear_resampler_config, pResampler: ^linear_resampler) -> result --- - linear_resampler_uninit :: proc(pResampler: ^linear_resampler) --- + linear_resampler_get_heap_size :: proc(pConfig: ^linear_resampler_config, pHeapSizeInBytes: ^c.size_t) -> result --- + linear_resampler_init_preallocated :: proc(pConfig: ^linear_resampler_config, pHeap: rawptr, pResampler: ^linear_resampler) -> result --- + linear_resampler_init :: proc(pConfig: ^linear_resampler_config, pAllocationCallbacks: ^allocation_callbacks, pResampler: ^linear_resampler) -> result --- + linear_resampler_uninit :: proc(pResampler: ^linear_resampler, pAllocationCallbacks: ^allocation_callbacks) --- linear_resampler_process_pcm_frames :: proc(pResampler: ^linear_resampler, pFramesIn: rawptr, pFrameCountIn: ^u64, pFramesOut: rawptr, pFrameCountOut: ^u64) -> result --- linear_resampler_set_rate :: proc(pResampler: ^linear_resampler, sampleRateIn, sampleRateOut: u32) -> result --- linear_resampler_set_rate_ratio :: proc(pResampler: ^linear_resampler, ratioInOut: f32) -> result --- - linear_resampler_get_required_input_frame_count :: proc(pResampler: ^linear_resampler, outputFrameCount: u64) -> u64 --- - linear_resampler_get_expected_output_frame_count :: proc(pResampler: ^linear_resampler, inputFrameCount: u64) -> u64 --- linear_resampler_get_input_latency :: proc(pResampler: ^linear_resampler) -> u64 --- linear_resampler_get_output_latency :: proc(pResampler: ^linear_resampler) -> u64 --- + linear_resampler_get_required_input_frame_count :: proc(pResampler: ^linear_resampler, outputFrameCount: u64, pInputFrameCount: ^u64) -> result --- + linear_resampler_get_expected_output_frame_count :: proc(pResampler: ^linear_resampler, inputFrameCount: u64, pOutputFrameCount: ^u64) -> result --- + linear_resampler_reset :: proc(pResampler: ^linear_resampler) -> result --- resampler_config_init :: proc(format: format, channels: u32, sampleRateIn, sampleRateOut: u32, algorithm: resample_algorithm) -> resampler_config --- + resampler_get_heap_size :: proc(pConfig: ^resampler_config, pHeapSizeInBytes: ^c.size_t) -> result --- + resampler_init_preallocated :: proc(pConfig: ^resampler_config, pHeap: rawptr, pResampler: ^resampler) -> result --- + /* Initializes a new resampler object from a config. */ - resampler_init :: proc(pConfig: ^resampler_config, pResampler: ^resampler) -> result --- + resampler_init :: proc(pConfig: ^resampler_config, pAllocationCallbacks: ^allocation_callbacks, pResampler: ^resampler) -> result --- /* Uninitializes a resampler. */ - resampler_uninit :: proc(pResampler: ^resampler) --- + resampler_uninit :: proc(pResampler: ^resampler, pAllocationCallbacks: ^allocation_callbacks) --- /* Converts the given input data. @@ -141,23 +174,6 @@ foreign lib { */ resampler_set_rate_ratio :: proc(pResampler: ^resampler, ratio: f32) -> result --- - - /* - Calculates the number of whole input frames that would need to be read from the client in order to output the specified - number of output frames. - - The returned value does not include cached input frames. It only returns the number of extra frames that would need to be - read from the input buffer in order to output the specified number of output frames. - */ - resampler_get_required_input_frame_count :: proc(pResampler: ^resampler, outputFrameCount: u64) -> u64 --- - - /* - Calculates the number of whole output frames that would be output after fully reading and consuming the specified number of - input frames. - */ - resampler_get_expected_output_frame_count :: proc(pResampler: ^resampler, inputFrameCount: u64) -> u64 --- - - /* Retrieves the latency introduced by the resampler in input frames. */ @@ -167,6 +183,26 @@ foreign lib { Retrieves the latency introduced by the resampler in output frames. */ resampler_get_output_latency :: proc(pResampler: ^resampler) -> u64 --- + + /* + Calculates the number of whole input frames that would need to be read from the client in order to output the specified + number of output frames. + + The returned value does not include cached input frames. It only returns the number of extra frames that would need to be + read from the input buffer in order to output the specified number of output frames. + */ + resampler_get_required_input_frame_count :: proc(pResampler: ^resampler, outputFrameCount: u64, pInputFrameCount: ^u64) -> result --- + + /* + Calculates the number of whole output frames that would be output after fully reading and consuming the specified number of + input frames. + */ + resampler_get_expected_output_frame_count :: proc(pResampler: ^resampler, inputFrameCount: u64, pOutputFrameCount: ^u64) -> result --- + + /* + Resets the resampler's timer and clears it's internal cache. + */ + resampler_reset :: proc(pResampler: ^resampler) -> result --- } @@ -175,42 +211,63 @@ foreign lib { Channel Conversion **************************************************************************************************************************************************************/ +channel_conversion_path :: enum c.int { + unknown, + passthrough, + mono_out, /* Converting to mono. */ + mono_in, /* Converting from mono. */ + shuffle, /* Simple shuffle. Will use this when all channels are present in both input and output channel maps, but just in a different order. */ + weights, /* Blended based on weights. */ +} + +mono_expansion_mode :: enum c.int { + duplicate = 0, /* The default. */ + average, /* Average the mono channel across all channels. */ + stereo_only, /* Duplicate to the left and right channels only and ignore the others. */ + default = duplicate, +} + channel_converter_config :: struct { - format: format, - channelsIn: u32, - channelsOut: u32, - channelMapIn: [MAX_CHANNELS]channel, - channelMapOut: [MAX_CHANNELS]channel, - mixingMode: channel_mix_mode, - weights: [MAX_CHANNELS][MAX_CHANNELS]f32, /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */ + format: format, + channelsIn: u32, + channelsOut: u32, + pChannelMapIn: [^]channel, + pChannelMapOut: [^]channel, + mixingMode: channel_mix_mode, + ppWeights: ^[^]f32, /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */ } channel_converter :: struct { - format: format, - channelsIn: u32, - channelsOut: u32, - channelMapIn: [MAX_CHANNELS]channel, - channelMapOut: [MAX_CHANNELS]channel, - mixingMode: channel_mix_mode, - weights: struct #raw_union { - f32: [MAX_CHANNELS][MAX_CHANNELS]f32, - s16: [MAX_CHANNELS][MAX_CHANNELS]i32, + format: format, + channelsIn: u32, + channelsOut: u32, + mixingMode: channel_mix_mode, + conversionPath: channel_conversion_path, + pChannelMapIn: [^]channel, + pChannelMapOut: [^]channel, + pShuffleTable: [^]u8, + weights: struct #raw_union { /* [in][out] */ + f32: ^[^]f32, + s16: ^[^]i32, }, - isPassthrough: b8, - isSimpleShuffle: b8, - isSimpleMonoExpansion: b8, - isStereoToMono: b8, - shuffleTable: [MAX_CHANNELS]u8, + + /* Memory management. */ + _pHeap: rawptr, + _ownsHeap: b32, } @(default_calling_convention="c", link_prefix="ma_") foreign lib { - channel_converter_config_init :: proc(format: format, channelsIn: u32, pChannelMapIn: ^channel, channelsOut: u32, pChannelMapOut: ^channel, mixingMode: channel_mix_mode) -> channel_converter_config --- + channel_converter_config_init :: proc(format: format, channelsIn: u32, pChannelMapIn: [^]channel, channelsOut: u32, pChannelMapOut: [^]channel, mixingMode: channel_mix_mode) -> channel_converter_config --- - channel_converter_init :: proc(pConfig: ^channel_converter_config, pConverter: ^channel_converter) -> result --- - channel_converter_uninit :: proc(pConverter: ^channel_converter) --- - channel_converter_process_pcm_frames :: proc(pConverter: ^channel_converter, pFramesOut: rawptr, pFramesIn: rawptr, frameCount: u64) -> result --- + channel_converter_get_heap_size :: proc(pConfig: ^channel_converter_config, pHeapSizeInBytes: ^c.size_t) -> result --- + channel_converter_init_preallocated :: proc(pConfig: ^channel_converter_config, pHeap: rawptr, pConverter: ^channel_converter) -> result --- + channel_converter_init :: proc(pConfig: ^channel_converter_config, pAllocationCallbacks: ^allocation_callbacks, pConverter: ^channel_converter) -> result --- + channel_converter_uninit :: proc(pConverter: ^channel_converter, pAllocationCallbacks: ^allocation_callbacks) --- + channel_converter_process_pcm_frames :: proc(pConverter: ^channel_converter, pFramesOut, pFramesIn: rawptr, frameCount: u64) -> result --- + channel_converter_get_input_channel_map :: proc(pConverter: ^channel_converter, pChannelMap: [^]channel, channelMapCap: c.size_t) -> result --- + channel_converter_get_output_channel_map :: proc(pConverter: ^channel_converter, pChannelMap: [^]channel, channelMapCap: c.size_t) -> result --- } @@ -220,32 +277,39 @@ Data Conversion **************************************************************************************************************************************************************/ data_converter_config :: struct { - formatIn: format, - formatOut: format, - channelsIn: u32, - channelsOut: u32, - sampleRateIn: u32, - sampleRateOut: u32, - channelMapIn: [MAX_CHANNELS]channel, - channelMapOut: [MAX_CHANNELS]channel, - ditherMode: dither_mode, - channelMixMode: channel_mix_mode, - channelWeights: [MAX_CHANNELS][MAX_CHANNELS]f32, /* [in][out]. Only used when channelMixMode is set to ma_channel_mix_mode_custom_weights. */ - resampling: struct { - algorithm: resample_algorithm, - allowDynamicSampleRate: b32, - linear: struct { - lpfOrderL: u32, - lpfNyquistFactor: f64, - }, - speex: struct { - quality: c.int, - }, - }, + formatIn: format, + formatOut: format, + channelsIn: u32, + channelsOut: u32, + sampleRateIn: u32, + sampleRateOut: u32, + pChannelMapIn: [^]channel, + pChannelMapOut: [^]channel, + ditherMode: dither_mode, + channelMixMode: channel_mix_mode, + ppChannelWeights: ^[^]f32, /* [in][out]. Only used when channelMixMode is set to ma_channel_mix_mode_custom_weights. */ + allowDynamicSampleRate: b32, + resampling: resampler_config, +} + +data_converter_execution_path :: enum c.int { + passthrough, /* No conversion. */ + format_only, /* Only format conversion. */ + channels_only, /* Only channel conversion. */ + resample_only, /* Only resampling. */ + resample_first, /* All conversions, but resample as the first step. */ + channels_first, /* All conversions, but channels as the first step. */ } data_converter :: struct { - config: data_converter_config, + formatIn: format, + formatOut: format, + channelsIn: u32, + channelsOut: u32, + sampleRateIn: u32, + sampleRateOut: u32, + ditherMode: dither_mode, + executionPath: data_converter_execution_path, /* The execution path the data converter will follow when processing. */ channelConverter: channel_converter, resampler: resampler, hasPreFormatConversion: b8, @@ -253,6 +317,10 @@ data_converter :: struct { hasChannelConverter: b8, hasResampler: b8, isPassthrough: b8, + + /* Memory management. */ + _ownsHeap: b8, + _pHeap: rawptr, } @@ -261,15 +329,20 @@ foreign lib { data_converter_config_init_default :: proc() -> data_converter_config --- data_converter_config_init :: proc(formatIn, formatOut: format, channelsIn, channelsOut: u32, sampleRateIn, sampleRateOut: u32) -> data_converter_config --- - data_converter_init :: proc(pConfig: ^data_converter_config, pConverter: ^data_converter) -> result --- - data_converter_uninit :: proc(pConverter: ^data_converter) --- + data_converter_get_heap_size :: proc(pConfig: ^data_converter_config, pHeapSizeInBytes: ^c.size_t) -> result --- + data_converter_init_preallocated :: proc(pConfig: ^data_converter_config, pHeap: rawptr, pConverter: ^data_converter) -> result --- + data_converter_init :: proc(pConfig: ^data_converter_config, pAllocationCallbacks: ^allocation_callbacks, pConverter: ^data_converter) -> result --- + data_converter_uninit :: proc(pConverter: ^data_converter, pAllocationCallbacks: ^allocation_callbacks) --- data_converter_process_pcm_frames :: proc(pConverter: ^data_converter, pFramesIn: rawptr, pFrameCountIn: ^u64, pFramesOut: rawptr, pFrameCountOut: ^u64) -> result --- data_converter_set_rate :: proc(pConverter: ^data_converter, sampleRateIn, sampleRateOut: u32) -> result --- data_converter_set_rate_ratio :: proc(pConverter: ^data_converter, ratioInOut: f32) -> result --- - data_converter_get_required_input_frame_count :: proc(pConverter: ^data_converter, outputFrameCount: u64) -> u64 --- - data_converter_get_expected_output_frame_count :: proc(pConverter: ^data_converter, inputFrameCount: u64) -> u64 --- data_converter_get_input_latency :: proc(pConverter: ^data_converter) -> u64 --- data_converter_get_output_latency :: proc(pConverter: ^data_converter) -> u64 --- + data_converter_get_required_input_frame_count :: proc(pConverter: ^data_converter, outputFrameCount: u64, pInputFrameCount: ^u64) -> result --- + data_converter_get_expected_output_frame_count :: proc(pConverter: ^data_converter, inputFrameCount: u64, pOutputFrameCount: ^u64) -> result --- + data_converter_get_input_channel_map :: proc(pConverter: ^data_converter, pChannelMap: [^]channel, channelMapCap: c.size_t) -> result --- + data_converter_get_output_channel_map :: proc(pConverter: ^data_converter, pChannelMap: [^]channel, channelMapCap: c.size_t) -> result --- + data_converter_reset :: proc(pConverter: ^data_converter) -> result --- } /************************************************************************************************************************************************************ @@ -328,43 +401,40 @@ CHANNEL_INDEX_NULL :: 255 @(default_calling_convention="c", link_prefix="ma_") foreign lib { - /* Retrieves the channel position of the specified channel based on miniaudio's default channel map. */ - channel_map_get_default_channel :: proc(channelCount: u32, channelIndex: u32) -> channel --- - /* Retrieves the channel position of the specified channel in the given channel map. The pChannelMap parameter can be null, in which case miniaudio's default channel map will be assumed. */ - channel_map_get_channel :: proc(pChannelMap: ^channel, channelCount: u32, channelIndex: u32) -> channel --- + channel_map_get_channel :: proc(pChannelMap: [^]channel, channelCount: u32, channelIndex: u32) -> channel --- /* Initializes a blank channel map. When a blank channel map is specified anywhere it indicates that the native channel map should be used. */ - channel_map_init_blank :: proc(channels: u32, pChannelMap: ^channel) --- + channel_map_init_blank :: proc(pChannelMap: [^]channel, channels: u32) --- /* Helper for retrieving a standard channel map. - The output channel map buffer must have a capacity of at least `channels`. + The output channel map buffer must have a capacity of at least `channelMapCap`. */ - get_standard_channel_map :: proc(standardChannelMap: standard_channel_map, channels: u32, pChannelMap: ^channel) --- + channel_map_init_standard :: proc(standardChannelMap: standard_channel_map, pChannelMap: [^]channel, channelMapCap: c.size_t, channels: u32) --- /* Copies a channel map. Both input and output channel map buffers must have a capacity of at at least `channels`. */ - channel_map_copy :: proc(pOut: ^channel, pIn: ^channel, channels: u32) --- + channel_map_copy :: proc(pOut: [^]channel, pIn: [^]channel, channels: u32) --- /* Copies a channel map if one is specified, otherwise copies the default channel map. The output buffer must have a capacity of at least `channels`. If not NULL, the input channel map must also have a capacity of at least `channels`. */ - channel_map_copy_or_default :: proc(pOut: ^channel, pIn: ^channel, channels: u32) --- + channel_map_copy_or_default :: proc(pOut: [^]channel, channelMapCapOut: c.size_t, pIn: [^]channel, channels: u32) --- /* @@ -374,12 +444,12 @@ foreign lib { is usually treated as a passthrough. Invalid channel maps: - - A channel map with no channels - - A channel map with more than one channel and a mono channel + - A channel map with no channels + - A channel map with more than one channel and a mono channel The channel map buffer must have a capacity of at least `channels`. */ - channel_map_valid :: proc(channels: u32, pChannelMap: ^channel) -> b32 --- + channel_map_is_valid :: proc(pChannelMap: [^]channel, channels: u32) -> b32 --- /* Helper for comparing two channel maps for equality. @@ -388,23 +458,24 @@ foreign lib { Both channels map buffers must have a capacity of at least `channels`. */ - channel_map_equal :: proc(channels: u32, pChannelMapA, pChannelMapB: ^channel) -> b32 --- + channel_map_is_equal :: proc(pChannelMapA, pChannelMapB: [^]channel, channels: u32) -> b32 --- /* Helper for determining if a channel map is blank (all channels set to MA_CHANNEL_NONE). The channel map buffer must have a capacity of at least `channels`. */ - channel_map_blank :: proc(channels: u32, pChannelMap: ^channel) -> b32 --- + channel_map_is_blank :: proc(pChannelMap: [^]channel, channels: u32) -> b32 --- /* Helper for determining whether or not a channel is present in the given channel map. The channel map buffer must have a capacity of at least `channels`. */ - channel_map_contains_channel_position :: proc(channels: u32, pChannelMap: ^channel, channelPosition: channel) -> b32 --- + channel_map_contains_channel_position :: proc(channels: u32, pChannelMap: [^]channel, channelPosition: channel) -> b32 --- } + /************************************************************************************************************************************************************ Conversion Helpers @@ -457,9 +528,9 @@ foreign lib { rb_uninit :: proc(pRB: ^rb) --- rb_reset :: proc(pRB: ^rb) --- rb_acquire_read :: proc(pRB: ^rb, pSizeInBytes: ^c.size_t, ppBufferOut: ^rawptr) -> result --- - rb_commit_read :: proc(pRB: ^rb, sizeInBytes: c.size_t, pBufferOut: rawptr) -> result --- + rb_commit_read :: proc(pRB: ^rb, sizeInBytes: c.size_t) -> result --- rb_acquire_write :: proc(pRB: ^rb, pSizeInBytes: ^c.size_t, ppBufferOut: ^rawptr) -> result --- - rb_commit_write :: proc(pRB: ^rb, sizeInBytes: c.size_t, pBufferOut: rawptr) -> result --- + rb_commit_write :: proc(pRB: ^rb, sizeInBytes: c.size_t) -> result --- rb_seek_read :: proc(pRB: ^rb, offsetInBytes: c.size_t) -> result --- rb_seek_write :: proc(pRB: ^rb, offsetInBytes: c.size_t) -> result --- rb_pointer_distance :: proc(pRB: ^rb) -> i32 --- /* Returns the distance between the write pointer and the read pointer. Should never be negative for a correct program. Will return the number of bytes that can be read before the read pointer hits the write pointer. */ diff --git a/vendor/miniaudio/decoding.odin b/vendor/miniaudio/decoding.odin index cdddd06fe..003f6f950 100644 --- a/vendor/miniaudio/decoding.odin +++ b/vendor/miniaudio/decoding.odin @@ -2,10 +2,13 @@ package miniaudio import "core:c" -when ODIN_OS == "windows" { foreign import lib "lib/miniaudio.lib" } -when ODIN_OS == "linux" { foreign import lib "lib/miniaudio.a" } - - +when ODIN_OS == .Windows { + foreign import lib "lib/miniaudio.lib" +} else when ODIN_OS == .Linux { + foreign import lib "lib/miniaudio.a" +} else { + foreign import lib "system:miniaudio" +} /************************************************************************************************************************************************************ @@ -19,68 +22,63 @@ you do your own synchronization. decoding_backend_config :: struct { preferredFormat: format, + seekPointCount: u32, /* Set to > 0 to generate a seektable if the decoding backend supports it. */ } @(default_calling_convention="c", link_prefix="ma_") foreign lib { - decoding_backend_config_init :: proc(preferredFormat: format) -> decoding_backend_config --- + decoding_backend_config_init :: proc(preferredFormat: format, seekPointCount: u32) -> decoding_backend_config --- } decoding_backend_vtable :: struct { onInit: proc "c" (pUserData: rawptr, onRead: decoder_read_proc, onSeek: decoder_seek_proc, onTell: decoder_tell_proc, pReadSeekTellUserData: rawptr, pConfig: ^decoding_backend_config, pAllocationCallbacks: ^allocation_callbacks, ppBackend: ^^data_source) -> result, - onInitFile: proc "c" (pUserData: rawptr, pFilePath: cstring, pConfig: ^decoding_backend_config, pAllocationCallbacks: ^allocation_callbacks, ppBackend: ^^data_source) -> result, /* Optional. */ + onInitFile: proc "c" (pUserData: rawptr, pFilePath: cstring, pConfig: ^decoding_backend_config, pAllocationCallbacks: ^allocation_callbacks, ppBackend: ^^data_source) -> result, /* Optional. */ onInitFileW: proc "c" (pUserData: rawptr, pFilePath: [^]c.wchar_t, pConfig: ^decoding_backend_config, pAllocationCallbacks: ^allocation_callbacks, ppBackend: ^^data_source) -> result, /* Optional. */ onInitMemory: proc "c" (pUserData: rawptr, pData: rawptr, dataSize: c.size_t, pConfig: ^decoding_backend_config, pAllocationCallbacks: ^allocation_callbacks, ppBackend: ^^data_source) -> result, /* Optional. */ onUninit: proc "c" (pUserData: rawptr, pBackend: ^data_source, pAllocationCallbacks: ^allocation_callbacks), - onGetChannelMap: proc "c" (pUserData: rawptr, pBackend: ^data_source, pChannelMap: ^channel, channelMapCap: c.size_t) -> result, } -/* TODO: Convert read and seek to be consistent with the VFS API (ma_result return value, bytes read moved to an output parameter). */ -decoder_read_proc :: proc "c" (pDecoder: ^decoder, pBufferOut: rawptr, bytesToRead: c.size_t) -> c.size_t /* Returns the number of bytes read. */ -decoder_seek_proc :: proc "c" (pDecoder: ^decoder, byteOffset: i64, origin: seek_origin) -> b32 +decoder_read_proc :: proc "c" (pDecoder: ^decoder, pBufferOut: rawptr, bytesToRead: c.size_t, pBytesRead: ^c.size_t) -> result /* Returns the number of bytes read. */ +decoder_seek_proc :: proc "c" (pDecoder: ^decoder, byteOffset: i64, origin: seek_origin) -> result decoder_tell_proc :: proc "c" (pDecoder: ^decoder, pCursor: ^i64) -> result decoder_config :: struct { - format: format, /* Set to 0 or ma_format_unknown to use the stream's internal format. */ - channels: u32, /* Set to 0 to use the stream's internal channels. */ - sampleRate: u32, /* Set to 0 to use the stream's internal sample rate. */ - channelMap: [MAX_CHANNELS]channel, - channelMixMode: channel_mix_mode, - ditherMode: dither_mode, - resampling: struct { - algorithm: resample_algorithm, - linear: struct { - lpfOrder: u32, - }, - speex: struct { - quality: c.int, - }, - }, + format: format, /* Set to 0 or ma_format_unknown to use the stream's internal format. */ + channels: u32, /* Set to 0 to use the stream's internal channels. */ + sampleRate: u32, /* Set to 0 to use the stream's internal sample rate. */ + channelMap: [^]channel, + channelMixMode: channel_mix_mode, + ditherMode: dither_mode, + resampling: resampler_config, allocationCallbacks: allocation_callbacks, encodingFormat: encoding_format, - ppCustomBackendVTables: ^^decoding_backend_vtable, + seekPointCount: u32, /* When set to > 0, specifies the number of seek points to use for the generation of a seek table. Not all decoding backends support this. */ + ppCustomBackendVTables: ^[^]decoding_backend_vtable, customBackendCount: u32, pCustomBackendUserData: rawptr, } decoder :: struct { - ds: data_source_base, - pBackend: ^data_source, /* The decoding backend we'll be pulling data from. */ - pBackendVTable: ^^decoding_backend_vtable, /* The vtable for the decoding backend. This needs to be stored so we can access the onUninit() callback. */ - pBackendUserData: rawptr, - onRead: decoder_read_proc, - onSeek: decoder_seek_proc, - onTell: decoder_tell_proc, - pUserData: rawptr, + ds: data_source_base, + pBackend: ^data_source, /* The decoding backend we'll be pulling data from. */ + pBackendVTable: ^decoding_backend_vtable, /* The vtable for the decoding backend. This needs to be stored so we can access the onUninit() callback. */ + pBackendUserData: rawptr, + onRead: decoder_read_proc, + onSeek: decoder_seek_proc, + onTell: decoder_tell_proc, + pUserData: rawptr, readPointerInPCMFrames: u64, /* In output sample rate. Used for keeping track of how many frames are available for decoding. */ - outputFormat: format, - outputChannels: u32, - outputSampleRate: u32, - outputChannelMap: [MAX_CHANNELS]channel, - converter: data_converter, /* <-- Data conversion is achieved by running frames through this. */ - allocationCallbacks: allocation_callbacks, + outputFormat: format, + outputChannels: u32, + outputSampleRate: u32, + converter: data_converter, /* <-- Data conversion is achieved by running frames through this. */ + pInputCache: rawptr, /* In input format. Can be null if it's not needed. */ + inputCacheCap: u64, /* The capacity of the input cache. */ + inputCacheConsumed: u64, /* The number of frames that have been consumed in the cache. Used for determining the next valid frame. */ + inputCacheRemaining: u64, /* The number of valid frames remaining in the cahce. */ + allocationCallbacks: allocation_callbacks, data: struct #raw_union { vfs: struct { pVFS: ^vfs, @@ -111,6 +109,25 @@ foreign lib { */ decoder_uninit :: proc(pDecoder: ^decoder) -> result --- + /* + Reads PCM frames from the given decoder. + + This is not thread safe without your own synchronization. + */ + decoder_read_pcm_frames :: proc(pDecoder: ^decoder, pFramesOut: rawptr, frameCount: u64, pFramesRead: ^u64) -> result --- + + /* + Seeks to a PCM frame based on it's absolute index. + + This is not thread safe without your own synchronization. + */ + decoder_seek_to_pcm_frame :: proc(pDecoder: ^decoder, frameIndex: u64) -> result --- + + /* + Retrieves the decoder's output data format. + */ + decoder_get_data_format :: proc(pDecoder: ^decoder, pFormat: ^format, pChannels, pSampleRate: ^u32, pChannelMap: ^channel, channelMapCap: c.size_t) -> result --- + /* Retrieves the current position of the read cursor in PCM frames. */ @@ -130,21 +147,7 @@ foreign lib { This function is not thread safe without your own synchronization. */ - decoder_get_length_in_pcm_frames :: proc(pDecoder: ^decoder) -> u64 --- - - /* - Reads PCM frames from the given decoder. - - This is not thread safe without your own synchronization. - */ - decoder_read_pcm_frames :: proc(pDecoder: ^decoder, pFramesOut: rawptr, frameCount: u64) -> u64 --- - - /* - Seeks to a PCM frame based on it's absolute index. - - This is not thread safe without your own synchronization. - */ - decoder_seek_to_pcm_frame :: proc(pDecoder: ^decoder, frameIndex: u64) -> result --- + decoder_get_length_in_pcm_frames :: proc(pDecoder: ^decoder, pLength: ^u64) -> result --- /* Retrieves the number of frames that can be read before reaching the end. @@ -164,4 +167,4 @@ foreign lib { decode_from_vfs :: proc(pVFS: ^vfs, pFilePath: cstring, pConfig: ^decoder_config, pFrameCountOut: ^u64, ppPCMFramesOut: ^rawptr) -> result --- decode_file :: proc(pFilePath: cstring, pConfig: ^decoder_config, pFrameCountOut: ^u64, ppPCMFramesOut: ^rawptr) -> result --- decode_memory :: proc(pData: rawptr, dataSize: c.size_t, pConfig: ^decoder_config, pFrameCountOut: ^u64, ppPCMFramesOut: ^rawptr) -> result --- -} \ No newline at end of file +} diff --git a/vendor/miniaudio/device_io_procs.odin b/vendor/miniaudio/device_io_procs.odin index c9cfb7c04..de60645e4 100644 --- a/vendor/miniaudio/device_io_procs.odin +++ b/vendor/miniaudio/device_io_procs.odin @@ -1,12 +1,23 @@ package miniaudio -when ODIN_OS == "windows" { foreign import lib "lib/miniaudio.lib" } -when ODIN_OS == "linux" { foreign import lib "lib/miniaudio.a" } +when ODIN_OS == .Windows { + foreign import lib "lib/miniaudio.lib" +} else when ODIN_OS == .Linux { + foreign import lib "lib/miniaudio.a" +} else { + foreign import lib "system:miniaudio" +} import "core:c" @(default_calling_convention="c", link_prefix="ma_") foreign lib { + device_job_thread_config_init :: proc() -> device_job_thread_config --- + + device_job_thread_init :: proc(pConfig: ^device_job_thread_config, pAllocationCallbacks: ^allocation_callbacks, pJobThread: ^device_job_thread) -> result --- + device_job_thread_uninit :: proc(pJobThread: ^device_job_thread, pAllocationCallbacks: ^allocation_callbacks) --- + device_job_thread_post :: proc(pJobThread: ^device_job_thread, pJob: ^job) -> result --- + device_job_thread_next :: proc(pJobThread: ^device_job_thread, pJob: ^job) -> result --- /* Initializes a `ma_context_config` object. @@ -41,16 +52,16 @@ foreign lib { Parameters ---------- backends (in, optional) - A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order. + A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order. backendCount (in, optional) - The number of items in `backend`. Ignored if `backend` is NULL. + The number of items in `backend`. Ignored if `backend` is NULL. pConfig (in, optional) - The context configuration. + The context configuration. pContext (in) - A pointer to the context object being initialized. + A pointer to the context object being initialized. Return Value @@ -67,107 +78,116 @@ foreign lib { ------- When `backends` is NULL, the default priority order will be used. Below is a list of backends in priority order: - |-------------|-----------------------|--------------------------------------------------------| - | Name | Enum Name | Supported Operating Systems | - |-------------|-----------------------|--------------------------------------------------------| - | WASAPI | ma_backend_wasapi | Windows Vista+ | - | DirectSound | ma_backend_dsound | Windows XP+ | - | WinMM | ma_backend_winmm | Windows XP+ (may work on older versions, but untested) | - | Core Audio | ma_backend_coreaudio | macOS, iOS | - | ALSA | ma_backend_alsa | Linux | - | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) | - | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) | - | sndio | ma_backend_sndio | OpenBSD | - | audio(4) | ma_backend_audio4 | NetBSD, OpenBSD | - | OSS | ma_backend_oss | FreeBSD | - | AAudio | ma_backend_aaudio | Android 8+ | - | OpenSL|ES | ma_backend_opensl | Android (API level 16+) | - | Web Audio | ma_backend_webaudio | Web (via Emscripten) | - | Null | ma_backend_null | Cross Platform (not used on Web) | - |-------------|-----------------------|--------------------------------------------------------| + |-------------|-----------------------|--------------------------------------------------------| + | Name | Enum Name | Supported Operating Systems | + |-------------|-----------------------|--------------------------------------------------------| + | WASAPI | ma_backend_wasapi | Windows Vista+ | + | DirectSound | ma_backend_dsound | Windows XP+ | + | WinMM | ma_backend_winmm | Windows XP+ (may work on older versions, but untested) | + | Core Audio | ma_backend_coreaudio | macOS, iOS | + | ALSA | ma_backend_alsa | Linux | + | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) | + | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) | + | sndio | ma_backend_sndio | OpenBSD | + | audio(4) | ma_backend_audio4 | NetBSD, OpenBSD | + | OSS | ma_backend_oss | FreeBSD | + | AAudio | ma_backend_aaudio | Android 8+ | + | OpenSL|ES | ma_backend_opensl | Android (API level 16+) | + | Web Audio | ma_backend_webaudio | Web (via Emscripten) | + | Null | ma_backend_null | Cross Platform (not used on Web) | + |-------------|-----------------------|--------------------------------------------------------| The context can be configured via the `pConfig` argument. The config object is initialized with `ma_context_config_init()`. Individual configuration settings can then be set directly on the structure. Below are the members of the `ma_context_config` object. - pLog - A pointer to the `ma_log` to post log messages to. Can be NULL if the application does not - require logging. See the `ma_log` API for details on how to use the logging system. + pLog + A pointer to the `ma_log` to post log messages to. Can be NULL if the application does not + require logging. See the `ma_log` API for details on how to use the logging system. - threadPriority - The desired priority to use for the audio thread. Allowable values include the following: + threadPriority + The desired priority to use for the audio thread. Allowable values include the following: - |--------------------------------------| - | Thread Priority | - |--------------------------------------| - | ma_thread_priority_idle | - | ma_thread_priority_lowest | - | ma_thread_priority_low | - | ma_thread_priority_normal | - | ma_thread_priority_high | - | ma_thread_priority_highest (default) | - | ma_thread_priority_realtime | - | ma_thread_priority_default | - |--------------------------------------| + |--------------------------------------| + | Thread Priority | + |--------------------------------------| + | ma_thread_priority_idle | + | ma_thread_priority_lowest | + | ma_thread_priority_low | + | ma_thread_priority_normal | + | ma_thread_priority_high | + | ma_thread_priority_highest (default) | + | ma_thread_priority_realtime | + | ma_thread_priority_default | + |--------------------------------------| - pUserData - A pointer to application-defined data. This can be accessed from the context object directly such as `context.pUserData`. + threadStackSize + The desired size of the stack for the audio thread. Defaults to the operating system's default. - allocationCallbacks - Structure containing custom allocation callbacks. Leaving this at defaults will cause it to use MA_MALLOC, MA_REALLOC and MA_FREE. These allocation - callbacks will be used for anything tied to the context, including devices. + pUserData + A pointer to application-defined data. This can be accessed from the context object directly such as `context.pUserData`. - alsa.useVerboseDeviceEnumeration - ALSA will typically enumerate many different devices which can be intrusive and not user-friendly. To combat this, miniaudio will enumerate only unique - card/device pairs by default. The problem with this is that you lose a bit of flexibility and control. Setting alsa.useVerboseDeviceEnumeration makes - it so the ALSA backend includes all devices. Defaults to false. + allocationCallbacks + Structure containing custom allocation callbacks. Leaving this at defaults will cause it to use MA_MALLOC, MA_REALLOC and MA_FREE. These allocation + callbacks will be used for anything tied to the context, including devices. - pulse.pApplicationName - PulseAudio only. The application name to use when initializing the PulseAudio context with `pa_context_new()`. + alsa.useVerboseDeviceEnumeration + ALSA will typically enumerate many different devices which can be intrusive and not user-friendly. To combat this, miniaudio will enumerate only unique + card/device pairs by default. The problem with this is that you lose a bit of flexibility and control. Setting alsa.useVerboseDeviceEnumeration makes + it so the ALSA backend includes all devices. Defaults to false. - pulse.pServerName - PulseAudio only. The name of the server to connect to with `pa_context_connect()`. + pulse.pApplicationName + PulseAudio only. The application name to use when initializing the PulseAudio context with `pa_context_new()`. - pulse.tryAutoSpawn - PulseAudio only. Whether or not to try automatically starting the PulseAudio daemon. Defaults to false. If you set this to true, keep in mind that - miniaudio uses a trial and error method to find the most appropriate backend, and this will result in the PulseAudio daemon starting which may be - intrusive for the end user. + pulse.pServerName + PulseAudio only. The name of the server to connect to with `pa_context_connect()`. - coreaudio.sessionCategory - iOS only. The session category to use for the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents. + pulse.tryAutoSpawn + PulseAudio only. Whether or not to try automatically starting the PulseAudio daemon. Defaults to false. If you set this to true, keep in mind that + miniaudio uses a trial and error method to find the most appropriate backend, and this will result in the PulseAudio daemon starting which may be + intrusive for the end user. - |-----------------------------------------|-------------------------------------| - | miniaudio Token | Core Audio Token | - |-----------------------------------------|-------------------------------------| - | ma_ios_session_category_ambient | AVAudioSessionCategoryAmbient | - | ma_ios_session_category_solo_ambient | AVAudioSessionCategorySoloAmbient | - | ma_ios_session_category_playback | AVAudioSessionCategoryPlayback | - | ma_ios_session_category_record | AVAudioSessionCategoryRecord | - | ma_ios_session_category_play_and_record | AVAudioSessionCategoryPlayAndRecord | - | ma_ios_session_category_multi_route | AVAudioSessionCategoryMultiRoute | - | ma_ios_session_category_none | AVAudioSessionCategoryAmbient | - | ma_ios_session_category_default | AVAudioSessionCategoryAmbient | - |-----------------------------------------|-------------------------------------| + coreaudio.sessionCategory + iOS only. The session category to use for the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents. - coreaudio.sessionCategoryOptions - iOS only. Session category options to use with the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents. + |-----------------------------------------|-------------------------------------| + | miniaudio Token | Core Audio Token | + |-----------------------------------------|-------------------------------------| + | ma_ios_session_category_ambient | AVAudioSessionCategoryAmbient | + | ma_ios_session_category_solo_ambient | AVAudioSessionCategorySoloAmbient | + | ma_ios_session_category_playback | AVAudioSessionCategoryPlayback | + | ma_ios_session_category_record | AVAudioSessionCategoryRecord | + | ma_ios_session_category_play_and_record | AVAudioSessionCategoryPlayAndRecord | + | ma_ios_session_category_multi_route | AVAudioSessionCategoryMultiRoute | + | ma_ios_session_category_none | AVAudioSessionCategoryAmbient | + | ma_ios_session_category_default | AVAudioSessionCategoryAmbient | + |-----------------------------------------|-------------------------------------| - |---------------------------------------------------------------------------|------------------------------------------------------------------| - | miniaudio Token | Core Audio Token | - |---------------------------------------------------------------------------|------------------------------------------------------------------| - | ma_ios_session_category_option_mix_with_others | AVAudioSessionCategoryOptionMixWithOthers | - | ma_ios_session_category_option_duck_others | AVAudioSessionCategoryOptionDuckOthers | - | ma_ios_session_category_option_allow_bluetooth | AVAudioSessionCategoryOptionAllowBluetooth | - | ma_ios_session_category_option_default_to_speaker | AVAudioSessionCategoryOptionDefaultToSpeaker | - | ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others | AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers | - | ma_ios_session_category_option_allow_bluetooth_a2dp | AVAudioSessionCategoryOptionAllowBluetoothA2DP | - | ma_ios_session_category_option_allow_air_play | AVAudioSessionCategoryOptionAllowAirPlay | - |---------------------------------------------------------------------------|------------------------------------------------------------------| + coreaudio.sessionCategoryOptions + iOS only. Session category options to use with the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents. - jack.pClientName - The name of the client to pass to `jack_client_open()`. + |---------------------------------------------------------------------------|------------------------------------------------------------------| + | miniaudio Token | Core Audio Token | + |---------------------------------------------------------------------------|------------------------------------------------------------------| + | ma_ios_session_category_option_mix_with_others | AVAudioSessionCategoryOptionMixWithOthers | + | ma_ios_session_category_option_duck_others | AVAudioSessionCategoryOptionDuckOthers | + | ma_ios_session_category_option_allow_bluetooth | AVAudioSessionCategoryOptionAllowBluetooth | + | ma_ios_session_category_option_default_to_speaker | AVAudioSessionCategoryOptionDefaultToSpeaker | + | ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others | AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers | + | ma_ios_session_category_option_allow_bluetooth_a2dp | AVAudioSessionCategoryOptionAllowBluetoothA2DP | + | ma_ios_session_category_option_allow_air_play | AVAudioSessionCategoryOptionAllowAirPlay | + |---------------------------------------------------------------------------|------------------------------------------------------------------| - jack.tryStartServer - Whether or not to try auto-starting the JACK server. Defaults to false. + coreaudio.noAudioSessionActivate + iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:true] on initialization. + + coreaudio.noAudioSessionDeactivate + iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:false] on uninitialization. + + jack.pClientName + The name of the client to pass to `jack_client_open()`. + + jack.tryStartServer + Whether or not to try auto-starting the JACK server. Defaults to false. It is recommended that only a single context is active at any given time because it's a bulky data structure which performs run-time linking for the @@ -185,7 +205,7 @@ foreign lib { ma_context context; ma_result result = ma_context_init(NULL, 0, NULL, &context); if (result != MA_SUCCESS) { - // Error. + // Error. } ``` @@ -200,24 +220,30 @@ foreign lib { ```c ma_backend backends[] = { - ma_backend_alsa, - ma_backend_pulseaudio, - ma_backend_wasapi, - ma_backend_dsound + ma_backend_alsa, + ma_backend_pulseaudio, + ma_backend_wasapi, + ma_backend_dsound }; + ma_log log; + ma_log_init(&log); + ma_log_register_callback(&log, ma_log_callback_init(my_log_callbac, pMyLogUserData)); + ma_context_config config = ma_context_config_init(); - config.logCallback = my_log_callback; - config.pUserData = pMyUserData; + config.pLog = &log; // Specify a custom log object in the config so any logs that are posted from ma_context_init() are captured. ma_context context; ma_result result = ma_context_init(backends, sizeof(backends)/sizeof(backends[0]), &config, &context); if (result != MA_SUCCESS) { - // Error. - if (result == MA_NO_BACKEND) { - // Couldn't find an appropriate backend. - } + // Error. + if (result == MA_NO_BACKEND) { + // Couldn't find an appropriate backend. + } } + + // You could also attach a log callback post-initialization: + ma_log_register_callback(ma_context_get_log(&context), ma_log_callback_init(my_log_callback, pMyLogUserData)); ``` @@ -293,13 +319,13 @@ foreign lib { Parameters ---------- pContext (in) - A pointer to the context performing the enumeration. + A pointer to the context performing the enumeration. callback (in) - The callback to fire for each enumerated device. + The callback to fire for each enumerated device. pUserData (in) - A pointer to application-defined data passed to the callback. + A pointer to application-defined data passed to the callback. Return Value @@ -326,15 +352,15 @@ foreign lib { Example 1 - Simple Enumeration ------------------------------ - ma_bool32 ma_device_enum_callback(pContext: ^context_type, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData) + ma_bool32 ma_device_enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData) { - printf("Device Name: %s\n", pInfo->name); - return MA_TRUE; + printf("Device Name: %s\n", pInfo->name); + return MA_TRUE; } ma_result result = ma_context_enumerate_devices(&context, my_device_enum_callback, pMyUserData); if (result != MA_SUCCESS) { - // Error. + // Error. } @@ -354,19 +380,19 @@ foreign lib { Parameters ---------- pContext (in) - A pointer to the context performing the enumeration. + A pointer to the context performing the enumeration. ppPlaybackDeviceInfos (out) - A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for playback devices. + A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for playback devices. pPlaybackDeviceCount (out) - A pointer to an unsigned integer that will receive the number of playback devices. + A pointer to an unsigned integer that will receive the number of playback devices. ppCaptureDeviceInfos (out) - A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for capture devices. + A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for capture devices. pCaptureDeviceCount (out) - A pointer to an unsigned integer that will receive the number of capture devices. + A pointer to an unsigned integer that will receive the number of capture devices. Return Value @@ -402,20 +428,16 @@ foreign lib { Parameters ---------- pContext (in) - A pointer to the context performing the query. + A pointer to the context performing the query. deviceType (in) - The type of the device being queried. Must be either `ma_device_type_playback` or `ma_device_type_capture`. + The type of the device being queried. Must be either `ma_device_type_playback` or `ma_device_type_capture`. pDeviceID (in) - The ID of the device being queried. - - shareMode (in) - The share mode to query for device capabilities. This should be set to whatever you're intending on using when initializing the device. If you're unsure, - set this to `ma_share_mode_shared`. + The ID of the device being queried. pDeviceInfo (out) - A pointer to the `ma_device_info` structure that will receive the device information. + A pointer to the `ma_device_info` structure that will receive the device information. Return Value @@ -439,7 +461,7 @@ foreign lib { This leaves pDeviceInfo unmodified in the result of an error. */ - context_get_device_info :: proc(pContext: ^context_type, deviceType: device_type, pDeviceID: ^device_id, shareMode: share_mode, pDeviceInfo: ^device_info) -> result --- + context_get_device_info :: proc(pContext: ^context_type, deviceType: device_type, pDeviceID: ^device_id, pDeviceInfo: ^device_info) -> result --- /* Determines if the given context supports loopback mode. @@ -466,16 +488,16 @@ foreign lib { Parameters ---------- deviceType (in) - The type of the device this config is being initialized for. This must set to one of the following: + The type of the device this config is being initialized for. This must set to one of the following: - |-------------------------| - | Device Type | - |-------------------------| - | ma_device_type_playback | - | ma_device_type_capture | - | ma_device_type_duplex | - | ma_device_type_loopback | - |-------------------------| + |-------------------------| + | Device Type | + |-------------------------| + | ma_device_type_playback | + | ma_device_type_capture | + | ma_device_type_duplex | + | ma_device_type_loopback | + |-------------------------| Return Value @@ -549,13 +571,13 @@ foreign lib { Parameters ---------- pContext (in, optional) - A pointer to the context that owns the device. This can be null, in which case it creates a default context internally. + A pointer to the context that owns the device. This can be null, in which case it creates a default context internally. pConfig (in) - A pointer to the device configuration. Cannot be null. See remarks for details. + A pointer to the device configuration. Cannot be null. See remarks for details. pDevice (out) - A pointer to the device object being initialized. + A pointer to the device object being initialized. Return Value @@ -578,9 +600,9 @@ foreign lib { ------- Setting `pContext` to NULL will result in miniaudio creating a default context internally and is equivalent to passing in a context initialized like so: - ```c - ma_context_init(NULL, 0, NULL, &context); - ``` + ```c + ma_context_init(NULL, 0, NULL, &context); + ``` Do not set `pContext` to NULL if you are needing to open multiple devices. You can, however, use NULL when initializing the first device, and then use device.pContext for the initialization of other devices. @@ -588,136 +610,173 @@ foreign lib { The device can be configured via the `pConfig` argument. The config object is initialized with `ma_device_config_init()`. Individual configuration settings can then be set directly on the structure. Below are the members of the `ma_device_config` object. - deviceType - Must be `ma_device_type_playback`, `ma_device_type_capture`, `ma_device_type_duplex` of `ma_device_type_loopback`. + deviceType + Must be `ma_device_type_playback`, `ma_device_type_capture`, `ma_device_type_duplex` of `ma_device_type_loopback`. - sampleRate - The sample rate, in hertz. The most common sample rates are 48000 and 44100. Setting this to 0 will use the device's native sample rate. + sampleRate + The sample rate, in hertz. The most common sample rates are 48000 and 44100. Setting this to 0 will use the device's native sample rate. - periodSizeInFrames - The desired size of a period in PCM frames. If this is 0, `periodSizeInMilliseconds` will be used instead. If both are 0 the default buffer size will - be used depending on the selected performance profile. This value affects latency. See below for details. + periodSizeInFrames + The desired size of a period in PCM frames. If this is 0, `periodSizeInMilliseconds` will be used instead. If both are 0 the default buffer size will + be used depending on the selected performance profile. This value affects latency. See below for details. - periodSizeInMilliseconds - The desired size of a period in milliseconds. If this is 0, `periodSizeInFrames` will be used instead. If both are 0 the default buffer size will be - used depending on the selected performance profile. The value affects latency. See below for details. + periodSizeInMilliseconds + The desired size of a period in milliseconds. If this is 0, `periodSizeInFrames` will be used instead. If both are 0 the default buffer size will be + used depending on the selected performance profile. The value affects latency. See below for details. - periods - The number of periods making up the device's entire buffer. The total buffer size is `periodSizeInFrames` or `periodSizeInMilliseconds` multiplied by - this value. This is just a hint as backends will be the ones who ultimately decide how your periods will be configured. + periods + The number of periods making up the device's entire buffer. The total buffer size is `periodSizeInFrames` or `periodSizeInMilliseconds` multiplied by + this value. This is just a hint as backends will be the ones who ultimately decide how your periods will be configured. - performanceProfile - A hint to miniaudio as to the performance requirements of your program. Can be either `ma_performance_profile_low_latency` (default) or - `ma_performance_profile_conservative`. This mainly affects the size of default buffers and can usually be left at it's default value. + performanceProfile + A hint to miniaudio as to the performance requirements of your program. Can be either `ma_performance_profile_low_latency` (default) or + `ma_performance_profile_conservative`. This mainly affects the size of default buffers and can usually be left at it's default value. - noPreZeroedOutputBuffer - When set to true, the contents of the output buffer passed into the data callback will be left undefined. When set to false (default), the contents of - the output buffer will be cleared the zero. You can use this to avoid the overhead of zeroing out the buffer if you can guarantee that your data - callback will write to every sample in the output buffer, or if you are doing your own clearing. + noPreSilencedOutputBuffer + When set to true, the contents of the output buffer passed into the data callback will be left undefined. When set to false (default), the contents of + the output buffer will be cleared the zero. You can use this to avoid the overhead of zeroing out the buffer if you can guarantee that your data + callback will write to every sample in the output buffer, or if you are doing your own clearing. - noClip - When set to true, the contents of the output buffer passed into the data callback will be clipped after returning. When set to false (default), the - contents of the output buffer are left alone after returning and it will be left up to the backend itself to decide whether or not the clip. This only - applies when the playback sample format is f32. + noClip + When set to true, the contents of the output buffer passed into the data callback will be clipped after returning. When set to false (default), the + contents of the output buffer are left alone after returning and it will be left up to the backend itself to decide whether or not the clip. This only + applies when the playback sample format is f32. - dataCallback - The callback to fire whenever data is ready to be delivered to or from the device. + noDisableDenormals + By default, miniaudio will disable denormals when the data callback is called. Setting this to true will prevent the disabling of denormals. - stopCallback - The callback to fire whenever the device has stopped, either explicitly via `ma_device_stop()`, or implicitly due to things like the device being - disconnected. + noFixedSizedCallback + Allows miniaudio to fire the data callback with any frame count. When this is set to true, the data callback will be fired with a consistent frame + count as specified by `periodSizeInFrames` or `periodSizeInMilliseconds`. When set to false, miniaudio will fire the callback with whatever the + backend requests, which could be anything. - pUserData - The user data pointer to use with the device. You can access this directly from the device object like `device.pUserData`. + dataCallback + The callback to fire whenever data is ready to be delivered to or from the device. - resampling.algorithm - The resampling algorithm to use when miniaudio needs to perform resampling between the rate specified by `sampleRate` and the device's native rate. The - default value is `ma_resample_algorithm_linear`, and the quality can be configured with `resampling.linear.lpfOrder`. + notificationCallback + The callback to fire when something has changed with the device, such as whether or not it has been started or stopped. - resampling.linear.lpfOrder - The linear resampler applies a low-pass filter as part of it's procesing for anti-aliasing. This setting controls the order of the filter. The higher - the value, the better the quality, in general. Setting this to 0 will disable low-pass filtering altogether. The maximum value is - `MA_MAX_FILTER_ORDER`. The default value is `min(4, MA_MAX_FILTER_ORDER)`. + pUserData + The user data pointer to use with the device. You can access this directly from the device object like `device.pUserData`. - playback.pDeviceID - A pointer to a `ma_device_id` structure containing the ID of the playback device to initialize. Setting this NULL (default) will use the system's - default playback device. Retrieve the device ID from the `ma_device_info` structure, which can be retrieved using device enumeration. + resampling.algorithm + The resampling algorithm to use when miniaudio needs to perform resampling between the rate specified by `sampleRate` and the device's native rate. The + default value is `ma_resample_algorithm_linear`, and the quality can be configured with `resampling.linear.lpfOrder`. - playback.format - The sample format to use for playback. When set to `ma_format_unknown` the device's native format will be used. This can be retrieved after - initialization from the device object directly with `device.playback.format`. + resampling.pBackendVTable + A pointer to an optional vtable that can be used for plugging in a custom resampler. - playback.channels - The number of channels to use for playback. When set to 0 the device's native channel count will be used. This can be retrieved after initialization - from the device object directly with `device.playback.channels`. + resampling.pBackendUserData + A pointer that will passed to callbacks in pBackendVTable. - playback.channelMap - The channel map to use for playback. When left empty, the device's native channel map will be used. This can be retrieved after initialization from the - device object direct with `device.playback.channelMap`. + resampling.linear.lpfOrder + The linear resampler applies a low-pass filter as part of it's procesing for anti-aliasing. This setting controls the order of the filter. The higher + the value, the better the quality, in general. Setting this to 0 will disable low-pass filtering altogether. The maximum value is + `MA_MAX_FILTER_ORDER`. The default value is `min(4, MA_MAX_FILTER_ORDER)`. - playback.shareMode - The preferred share mode to use for playback. Can be either `ma_share_mode_shared` (default) or `ma_share_mode_exclusive`. Note that if you specify - exclusive mode, but it's not supported by the backend, initialization will fail. You can then fall back to shared mode if desired by changing this to - ma_share_mode_shared and reinitializing. + playback.pDeviceID + A pointer to a `ma_device_id` structure containing the ID of the playback device to initialize. Setting this NULL (default) will use the system's + default playback device. Retrieve the device ID from the `ma_device_info` structure, which can be retrieved using device enumeration. - capture.pDeviceID - A pointer to a `ma_device_id` structure containing the ID of the capture device to initialize. Setting this NULL (default) will use the system's - default capture device. Retrieve the device ID from the `ma_device_info` structure, which can be retrieved using device enumeration. + playback.format + The sample format to use for playback. When set to `ma_format_unknown` the device's native format will be used. This can be retrieved after + initialization from the device object directly with `device.playback.format`. - capture.format - The sample format to use for capture. When set to `ma_format_unknown` the device's native format will be used. This can be retrieved after - initialization from the device object directly with `device.capture.format`. + playback.channels + The number of channels to use for playback. When set to 0 the device's native channel count will be used. This can be retrieved after initialization + from the device object directly with `device.playback.channels`. - capture.channels - The number of channels to use for capture. When set to 0 the device's native channel count will be used. This can be retrieved after initialization - from the device object directly with `device.capture.channels`. + playback.pChannelMap + The channel map to use for playback. When left empty, the device's native channel map will be used. This can be retrieved after initialization from the + device object direct with `device.playback.pChannelMap`. When set, the buffer should contain `channels` items. - capture.channelMap - The channel map to use for capture. When left empty, the device's native channel map will be used. This can be retrieved after initialization from the - device object direct with `device.capture.channelMap`. + playback.shareMode + The preferred share mode to use for playback. Can be either `ma_share_mode_shared` (default) or `ma_share_mode_exclusive`. Note that if you specify + exclusive mode, but it's not supported by the backend, initialization will fail. You can then fall back to shared mode if desired by changing this to + ma_share_mode_shared and reinitializing. - capture.shareMode - The preferred share mode to use for capture. Can be either `ma_share_mode_shared` (default) or `ma_share_mode_exclusive`. Note that if you specify - exclusive mode, but it's not supported by the backend, initialization will fail. You can then fall back to shared mode if desired by changing this to - ma_share_mode_shared and reinitializing. + capture.pDeviceID + A pointer to a `ma_device_id` structure containing the ID of the capture device to initialize. Setting this NULL (default) will use the system's + default capture device. Retrieve the device ID from the `ma_device_info` structure, which can be retrieved using device enumeration. - wasapi.noAutoConvertSRC - WASAPI only. When set to true, disables WASAPI's automatic resampling and forces the use of miniaudio's resampler. Defaults to false. + capture.format + The sample format to use for capture. When set to `ma_format_unknown` the device's native format will be used. This can be retrieved after + initialization from the device object directly with `device.capture.format`. - wasapi.noDefaultQualitySRC - WASAPI only. Only used when `wasapi.noAutoConvertSRC` is set to false. When set to true, disables the use of `AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY`. - You should usually leave this set to false, which is the default. + capture.channels + The number of channels to use for capture. When set to 0 the device's native channel count will be used. This can be retrieved after initialization + from the device object directly with `device.capture.channels`. - wasapi.noAutoStreamRouting - WASAPI only. When set to true, disables automatic stream routing on the WASAPI backend. Defaults to false. + capture.pChannelMap + The channel map to use for capture. When left empty, the device's native channel map will be used. This can be retrieved after initialization from the + device object direct with `device.capture.pChannelMap`. When set, the buffer should contain `channels` items. - wasapi.noHardwareOffloading - WASAPI only. When set to true, disables the use of WASAPI's hardware offloading feature. Defaults to false. + capture.shareMode + The preferred share mode to use for capture. Can be either `ma_share_mode_shared` (default) or `ma_share_mode_exclusive`. Note that if you specify + exclusive mode, but it's not supported by the backend, initialization will fail. You can then fall back to shared mode if desired by changing this to + ma_share_mode_shared and reinitializing. - alsa.noMMap - ALSA only. When set to true, disables MMap mode. Defaults to false. + wasapi.noAutoConvertSRC + WASAPI only. When set to true, disables WASAPI's automatic resampling and forces the use of miniaudio's resampler. Defaults to false. - alsa.noAutoFormat - ALSA only. When set to true, disables ALSA's automatic format conversion by including the SND_PCM_NO_AUTO_FORMAT flag. Defaults to false. + wasapi.noDefaultQualitySRC + WASAPI only. Only used when `wasapi.noAutoConvertSRC` is set to false. When set to true, disables the use of `AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY`. + You should usually leave this set to false, which is the default. - alsa.noAutoChannels - ALSA only. When set to true, disables ALSA's automatic channel conversion by including the SND_PCM_NO_AUTO_CHANNELS flag. Defaults to false. + wasapi.noAutoStreamRouting + WASAPI only. When set to true, disables automatic stream routing on the WASAPI backend. Defaults to false. - alsa.noAutoResample - ALSA only. When set to true, disables ALSA's automatic resampling by including the SND_PCM_NO_AUTO_RESAMPLE flag. Defaults to false. + wasapi.noHardwareOffloading + WASAPI only. When set to true, disables the use of WASAPI's hardware offloading feature. Defaults to false. - pulse.pStreamNamePlayback - PulseAudio only. Sets the stream name for playback. + alsa.noMMap + ALSA only. When set to true, disables MMap mode. Defaults to false. - pulse.pStreamNameCapture - PulseAudio only. Sets the stream name for capture. + alsa.noAutoFormat + ALSA only. When set to true, disables ALSA's automatic format conversion by including the SND_PCM_NO_AUTO_FORMAT flag. Defaults to false. - coreaudio.allowNominalSampleRateChange - Core Audio only. Desktop only. When enabled, allows the sample rate of the device to be changed at the operating system level. This - is disabled by default in order to prevent intrusive changes to the user's system. This is useful if you want to use a sample rate - that is known to be natively supported by the hardware thereby avoiding the cost of resampling. When set to true, miniaudio will - find the closest match between the sample rate requested in the device config and the sample rates natively supported by the - hardware. When set to false, the sample rate currently set by the operating system will always be used. + alsa.noAutoChannels + ALSA only. When set to true, disables ALSA's automatic channel conversion by including the SND_PCM_NO_AUTO_CHANNELS flag. Defaults to false. + + alsa.noAutoResample + ALSA only. When set to true, disables ALSA's automatic resampling by including the SND_PCM_NO_AUTO_RESAMPLE flag. Defaults to false. + + pulse.pStreamNamePlayback + PulseAudio only. Sets the stream name for playback. + + pulse.pStreamNameCapture + PulseAudio only. Sets the stream name for capture. + + coreaudio.allowNominalSampleRateChange + Core Audio only. Desktop only. When enabled, allows the sample rate of the device to be changed at the operating system level. This + is disabled by default in order to prevent intrusive changes to the user's system. This is useful if you want to use a sample rate + that is known to be natively supported by the hardware thereby avoiding the cost of resampling. When set to true, miniaudio will + find the closest match between the sample rate requested in the device config and the sample rates natively supported by the + hardware. When set to false, the sample rate currently set by the operating system will always be used. + + opensl.streamType + OpenSL only. Explicitly sets the stream type. If left unset (`ma_opensl_stream_type_default`), the + stream type will be left unset. Think of this as the type of audio you're playing. + + opensl.recordingPreset + OpenSL only. Explicitly sets the type of recording your program will be doing. When left + unset, the recording preset will be left unchanged. + + aaudio.usage + AAudio only. Explicitly sets the nature of the audio the program will be consuming. When + left unset, the usage will be left unchanged. + + aaudio.contentType + AAudio only. Sets the content type. When left unset, the content type will be left unchanged. + + aaudio.inputPreset + AAudio only. Explicitly sets the type of recording your program will be doing. When left + unset, the input preset will be left unchanged. + + aaudio.noAutoStartAfterReroute + AAudio only. Controls whether or not the device should be automatically restarted after a + stream reroute. When set to false (default) the device will be restarted automatically; + otherwise the device will be stopped. Once initialized, the device's config is immutable. If you need to change the config you will need to initialize a new device. @@ -762,7 +821,7 @@ foreign lib { ma_device device; ma_result result = ma_device_init(NULL, &config, &device); if (result != MA_SUCCESS) { - // Error + // Error } ``` @@ -777,14 +836,14 @@ foreign lib { ma_context context; ma_result result = ma_context_init(NULL, 0, NULL, &context); if (result != MA_SUCCESS) { - // Error + // Error } ma_device_info* pPlaybackDeviceInfos; ma_uint32 playbackDeviceCount; result = ma_context_get_devices(&context, &pPlaybackDeviceInfos, &playbackDeviceCount, NULL, NULL); if (result != MA_SUCCESS) { - // Error + // Error } // ... choose a device from pPlaybackDeviceInfos ... @@ -802,7 +861,7 @@ foreign lib { ma_device device; result = ma_device_init(&context, &config, &device); if (result != MA_SUCCESS) { - // Error + // Error } ``` @@ -828,19 +887,19 @@ foreign lib { Parameters ---------- backends (in, optional) - A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order. + A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order. backendCount (in, optional) - The number of items in `backend`. Ignored if `backend` is NULL. + The number of items in `backend`. Ignored if `backend` is NULL. pContextConfig (in, optional) - The context configuration. + The context configuration. pConfig (in) - A pointer to the device configuration. Cannot be null. See remarks for details. + A pointer to the device configuration. Cannot be null. See remarks for details. pDevice (out) - A pointer to the device object being initialized. + A pointer to the device object being initialized. Return Value @@ -885,7 +944,7 @@ foreign lib { Parameters ---------- pDevice (in) - A pointer to the device to stop. + A pointer to the device to stop. Return Value @@ -922,6 +981,95 @@ foreign lib { device_get_log :: proc(pDevice: ^device) -> ^log --- + /* + Retrieves information about the device. + + + Parameters + ---------- + pDevice (in) + A pointer to the device whose information is being retrieved. + + type (in) + The device type. This parameter is required for duplex devices. When retrieving device + information, you are doing so for an individual playback or capture device. + + pDeviceInfo (out) + A pointer to the `ma_device_info` that will receive the device information. + + + Return Value + ------------ + MA_SUCCESS if successful; any other error code otherwise. + + + Thread Safety + ------------- + Unsafe. This should be considered unsafe because it may be calling into the backend which may or + may not be safe. + + + Callback Safety + --------------- + Unsafe. You should avoid calling this in the data callback because it may call into the backend + which may or may not be safe. + */ + device_get_info :: proc(pDevice: ^device, type: device_type, pDeviceInfo: ^device_info) -> result --- + + + /* + Retrieves the name of the device. + + + Parameters + ---------- + pDevice (in) + A pointer to the device whose information is being retrieved. + + type (in) + The device type. This parameter is required for duplex devices. When retrieving device + information, you are doing so for an individual playback or capture device. + + pName (out) + A pointer to the buffer that will receive the name. + + nameCap (in) + The capacity of the output buffer, including space for the null terminator. + + pLengthNotIncludingNullTerminator (out, optional) + A pointer to the variable that will receive the length of the name, not including the null + terminator. + + + Return Value + ------------ + MA_SUCCESS if successful; any other error code otherwise. + + + Thread Safety + ------------- + Unsafe. This should be considered unsafe because it may be calling into the backend which may or + may not be safe. + + + Callback Safety + --------------- + Unsafe. You should avoid calling this in the data callback because it may call into the backend + which may or may not be safe. + + + Remarks + ------- + If the name does not fully fit into the output buffer, it'll be truncated. You can pass in NULL to + `pName` if you want to first get the length of the name for the purpose of memory allocation of the + output buffer. Allocating a buffer of size `MA_MAX_DEVICE_NAME_LENGTH + 1` should be enough for + most cases and will avoid the need for the inefficiency of calling this function twice. + + This is implemented in terms of `ma_device_get_info()`. + */ + device_get_name :: proc(pDevice: ^device, type: device_type, pName: [^]c.char, nameCap: c.size_t, pLengthNotIncludingNullTerminator: ^c.size_t) -> result --- + + /* Starts the device. For playback devices this begins playback. For capture devices it begins recording. @@ -931,7 +1079,7 @@ foreign lib { Parameters ---------- pDevice (in) - A pointer to the device to start. + A pointer to the device to start. Return Value @@ -974,7 +1122,7 @@ foreign lib { Parameters ---------- pDevice (in) - A pointer to the device to stop. + A pointer to the device to stop. Return Value @@ -1020,7 +1168,7 @@ foreign lib { Parameters ---------- pDevice (in) - A pointer to the device whose start state is being retrieved. + A pointer to the device whose start state is being retrieved. Return Value @@ -1054,24 +1202,24 @@ foreign lib { Parameters ---------- pDevice (in) - A pointer to the device whose state is being retrieved. + A pointer to the device whose state is being retrieved. Return Value ------------ The current state of the device. The return value will be one of the following: - +------------------------+------------------------------------------------------------------------------+ - | MA_STATE_UNINITIALIZED | Will only be returned if the device is in the middle of initialization. | - +------------------------+------------------------------------------------------------------------------+ - | MA_STATE_STOPPED | The device is stopped. The initial state of the device after initialization. | - +------------------------+------------------------------------------------------------------------------+ - | MA_STATE_STARTED | The device started and requesting and/or delivering audio data. | - +------------------------+------------------------------------------------------------------------------+ - | MA_STATE_STARTING | The device is in the process of starting. | - +------------------------+------------------------------------------------------------------------------+ - | MA_STATE_STOPPING | The device is in the process of stopping. | - +------------------------+------------------------------------------------------------------------------+ + +-------------------------------+------------------------------------------------------------------------------+ + | ma_device_state_uninitialized | Will only be returned if the device is in the middle of initialization. | + +-------------------------------+------------------------------------------------------------------------------+ + | ma_device_state_stopped | The device is stopped. The initial state of the device after initialization. | + +-------------------------------+------------------------------------------------------------------------------+ + | ma_device_state_started | The device started and requesting and/or delivering audio data. | + +-------------------------------+------------------------------------------------------------------------------+ + | ma_device_state_starting | The device is in the process of starting. | + +-------------------------------+------------------------------------------------------------------------------+ + | ma_device_state_stopping | The device is in the process of stopping. | + +-------------------------------+------------------------------------------------------------------------------+ Thread Safety @@ -1089,40 +1237,89 @@ foreign lib { ------- The general flow of a devices state goes like this: - ``` - ma_device_init() -> MA_STATE_UNINITIALIZED -> MA_STATE_STOPPED - ma_device_start() -> MA_STATE_STARTING -> MA_STATE_STARTED - ma_device_stop() -> MA_STATE_STOPPING -> MA_STATE_STOPPED - ``` + ``` + ma_device_init() -> ma_device_state_uninitialized -> ma_device_state_stopped + ma_device_start() -> ma_device_state_starting -> ma_device_state_started + ma_device_stop() -> ma_device_state_stopping -> ma_device_state_stopped + ``` When the state of the device is changed with `ma_device_start()` or `ma_device_stop()` at this same time as this function is called, the value returned by this function could potentially be out of sync. If this is significant to your program you need to implement your own synchronization. */ - device_get_state :: proc(pDevice: ^device) -> u32 --- + device_get_state :: proc(pDevice: ^device) -> device_state --- + + + /* + Performs post backend initialization routines for setting up internal data conversion. + + This should be called whenever the backend is initialized. The only time this should be called from + outside of miniaudio is if you're implementing a custom backend, and you would only do it if you + are reinitializing the backend due to rerouting or reinitializing for some reason. + + + Parameters + ---------- + pDevice [in] + A pointer to the device. + + deviceType [in] + The type of the device that was just reinitialized. + + pPlaybackDescriptor [in] + The descriptor of the playback device containing the internal data format and buffer sizes. + + pPlaybackDescriptor [in] + The descriptor of the capture device containing the internal data format and buffer sizes. + + + Return Value + ------------ + MA_SUCCESS if successful; any other error otherwise. + + + Thread Safety + ------------- + Unsafe. This will be reinitializing internal data converters which may be in use by another thread. + + + Callback Safety + --------------- + Unsafe. This will be reinitializing internal data converters which may be in use by the callback. + + + Remarks + ------- + For a duplex device, you can call this for only one side of the system. This is why the deviceType + is specified as a parameter rather than deriving it from the device. + + You do not need to call this manually unless you are doing a custom backend, in which case you need + only do it if you're manually performing rerouting or reinitialization. + */ + device_post_init :: proc(pDevice: ^device, deviceType: device_type, pPlaybackDescriptor, pCaptureDescriptor: ^device_descriptor) -> result --- /* Sets the master volume factor for the device. - The volume factor must be between 0 (silence) and 1 (full volume). Use `ma_device_set_master_gain_db()` to use decibel notation, where 0 is full volume and + The volume factor must be between 0 (silence) and 1 (full volume). Use `ma_device_set_master_volume_db()` to use decibel notation, where 0 is full volume and values less than 0 decreases the volume. Parameters ---------- pDevice (in) - A pointer to the device whose volume is being set. + A pointer to the device whose volume is being set. volume (in) - The new volume factor. Must be within the range of [0, 1]. + The new volume factor. Must be >= 0. Return Value ------------ MA_SUCCESS if the volume was set successfully. MA_INVALID_ARGS if pDevice is NULL. - MA_INVALID_ARGS if the volume factor is not within the range of [0, 1]. + MA_INVALID_ARGS if volume is negative. Thread Safety @@ -1145,8 +1342,8 @@ foreign lib { See Also -------- ma_device_get_master_volume() - ma_device_set_master_volume_gain_db() - ma_device_get_master_volume_gain_db() + ma_device_set_master_volume_db() + ma_device_get_master_volume_db() */ device_set_master_volume :: proc(pDevice: ^device, volume: f32) -> result --- @@ -1157,10 +1354,10 @@ foreign lib { Parameters ---------- pDevice (in) - A pointer to the device whose volume factor is being retrieved. + A pointer to the device whose volume factor is being retrieved. pVolume (in) - A pointer to the variable that will receive the volume factor. The returned value will be in the range of [0, 1]. + A pointer to the variable that will receive the volume factor. The returned value will be in the range of [0, 1]. Return Value @@ -1202,10 +1399,10 @@ foreign lib { Parameters ---------- pDevice (in) - A pointer to the device whose gain is being set. + A pointer to the device whose gain is being set. gainDB (in) - The new volume as gain in decibels. Must be less than or equal to 0, where 0 is full volume and anything less than 0 decreases the volume. + The new volume as gain in decibels. Must be less than or equal to 0, where 0 is full volume and anything less than 0 decreases the volume. Return Value @@ -1238,7 +1435,7 @@ foreign lib { ma_device_set_master_volume() ma_device_get_master_volume() */ - device_set_master_gain_db :: proc(pDevice: ^device, gainDB: f32) -> result --- + device_set_master_volume_db :: proc(pDevice: ^device, gainDB: f32) -> result --- /* Retrieves the master gain in decibels. @@ -1247,10 +1444,10 @@ foreign lib { Parameters ---------- pDevice (in) - A pointer to the device whose gain is being retrieved. + A pointer to the device whose gain is being retrieved. pGainDB (in) - A pointer to the variable that will receive the gain in decibels. The returned value will be <= 0. + A pointer to the variable that will receive the gain in decibels. The returned value will be <= 0. Return Value @@ -1277,11 +1474,11 @@ foreign lib { See Also -------- - ma_device_set_master_volume_gain_db() + ma_device_set_master_volume_db() ma_device_set_master_volume() ma_device_get_master_volume() */ - device_get_master_gain_db :: proc(pDevice: ^device, pGainDB: ^f32) -> result --- + device_get_master_volume_db :: proc(pDevice: ^device, pGainDB: ^f32) -> result --- /* @@ -1291,18 +1488,18 @@ foreign lib { Parameters ---------- pDevice (in) - A pointer to device whose processing the data callback. + A pointer to device whose processing the data callback. pOutput (out) - A pointer to the buffer that will receive the output PCM frame data. On a playback device this must not be NULL. On a duplex device - this can be NULL, in which case pInput must not be NULL. + A pointer to the buffer that will receive the output PCM frame data. On a playback device this must not be NULL. On a duplex device + this can be NULL, in which case pInput must not be NULL. pInput (in) - A pointer to the buffer containing input PCM frame data. On a capture device this must not be NULL. On a duplex device this can be - NULL, in which case `pOutput` must not be NULL. + A pointer to the buffer containing input PCM frame data. On a capture device this must not be NULL. On a duplex device this can be + NULL, in which case `pOutput` must not be NULL. frameCount (in) - The number of frames being processed. + The number of frames being processed. Return Value @@ -1329,7 +1526,7 @@ foreign lib { If you are implementing a custom backend, and that backend uses a callback for data delivery, you'll need to call this from inside that callback. */ - device_handle_backend_data_callback :: proc(pDevice: ^device, pOutput: rawptr, pInput: rawptr, frameCount: u32) -> result --- + device_handle_backend_data_callback :: proc(pDevice: ^device, pOutput, pInput: rawptr, frameCount: u32) -> result --- /* @@ -1346,18 +1543,18 @@ foreign lib { Parameters ---------- pDescriptor (in) - A pointer to device descriptor whose `periodSizeInFrames` and `periodSizeInMilliseconds` members - will be used for the calculation of the buffer size. + A pointer to device descriptor whose `periodSizeInFrames` and `periodSizeInMilliseconds` members + will be used for the calculation of the buffer size. nativeSampleRate (in) - The device's native sample rate. This is only ever used when the `periodSizeInFrames` member of - `pDescriptor` is zero. In this case, `periodSizeInMilliseconds` will be used instead, in which - case a sample rate is required to convert to a size in frames. + The device's native sample rate. This is only ever used when the `periodSizeInFrames` member of + `pDescriptor` is zero. In this case, `periodSizeInMilliseconds` will be used instead, in which + case a sample rate is required to convert to a size in frames. performanceProfile (in) - When both the `periodSizeInFrames` and `periodSizeInMilliseconds` members of `pDescriptor` are - zero, miniaudio will fall back to a buffer size based on the performance profile. The profile - to use for this calculation is determine by this parameter. + When both the `periodSizeInFrames` and `periodSizeInMilliseconds` members of `pDescriptor` are + zero, miniaudio will fall back to a buffer size based on the performance profile. The profile + to use for this calculation is determine by this parameter. Return Value @@ -1403,14 +1600,14 @@ foreign lib { Parameters ---------- pBackends (out, optional) - A pointer to the buffer that will receive the enabled backends. Set to NULL to retrieve the backend count. Setting - the capacity of the buffer to `MA_BUFFER_COUNT` will guarantee it's large enough for all backends. + A pointer to the buffer that will receive the enabled backends. Set to NULL to retrieve the backend count. Setting + the capacity of the buffer to `MA_BUFFER_COUNT` will guarantee it's large enough for all backends. backendCap (in) - The capacity of the `pBackends` buffer. + The capacity of the `pBackends` buffer. pBackendCount (out) - A pointer to the variable that will receive the enabled backend count. + A pointer to the variable that will receive the enabled backend count. Return Value @@ -1458,7 +1655,7 @@ foreign lib { result = ma_get_enabled_backends(enabledBackends, MA_BACKEND_COUNT, &enabledBackendCount); if (result != MA_SUCCESS) { - // Failed to retrieve enabled backends. Should never happen in this example since all inputs are valid. + // Failed to retrieve enabled backends. Should never happen in this example since all inputs are valid. } ``` diff --git a/vendor/miniaudio/device_io_types.odin b/vendor/miniaudio/device_io_types.odin index 8f43b8640..5a2c4bc73 100644 --- a/vendor/miniaudio/device_io_types.odin +++ b/vendor/miniaudio/device_io_types.odin @@ -2,28 +2,29 @@ package miniaudio import "core:c" -SUPPORT_WASAPI :: ODIN_OS == "windows" -SUPPORT_DSOUND :: ODIN_OS == "windows" -SUPPORT_WINMM :: ODIN_OS == "windows" -SUPPORT_COREAUDIO :: ODIN_OS == "darwin" -SUPPORT_SNDIO :: ODIN_OS == "openbsd" -SUPPORT_AUDIO4 :: ODIN_OS == "openbsd" || ODIN_OS == "netbsd" -SUPPORT_OSS :: ODIN_OS == "freebsd" -SUPPORT_PULSEAUDIO :: ODIN_OS == "linux" -SUPPORT_ALSA :: ODIN_OS == "linux" -SUPPORT_JACK :: ODIN_OS == "windows" -SUPPORT_AAUDIO :: ODIN_OS == "android" -SUPPORT_OPENSL :: ODIN_OS == "android" -SUPPORT_WEBAUDIO :: ODIN_OS == "emscripten" +SUPPORT_WASAPI :: ODIN_OS == .Windows +SUPPORT_DSOUND :: ODIN_OS == .Windows +SUPPORT_WINMM :: ODIN_OS == .Windows +SUPPORT_COREAUDIO :: ODIN_OS == .Darwin +SUPPORT_SNDIO :: ODIN_OS == .OpenBSD +SUPPORT_AUDIO4 :: false // ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD +SUPPORT_OSS :: ODIN_OS == .FreeBSD +SUPPORT_PULSEAUDIO :: ODIN_OS == .Linux +SUPPORT_ALSA :: ODIN_OS == .Linux +SUPPORT_JACK :: ODIN_OS == .Windows +SUPPORT_AAUDIO :: false // ODIN_OS == .Android +SUPPORT_OPENSL :: false // ODIN_OS == .Android +SUPPORT_WEBAUDIO :: false // ODIN_OS == .Emscripten SUPPORT_CUSTOM :: true -SUPPORT_NULL :: ODIN_OS != "emscripten" - -STATE_UNINITIALIZED :: 0 -STATE_STOPPED :: 1 /* The device's default state after initialization. */ -STATE_STARTED :: 2 /* The device is started and is requesting and/or delivering audio data. */ -STATE_STARTING :: 3 /* Transitioning from a stopped state to started. */ -STATE_STOPPING :: 4 /* Transitioning from a started state to stopped. */ +SUPPORT_NULL :: true // ODIN_OS != .Emscripten +device_state :: enum c.int { + uninitialized = 0, + stopped = 1, /* The device's default state after initialization. */ + started = 2, /* The device is started and is requesting and/or delivering audio data. */ + starting = 3, /* Transitioning from a stopped state to started. */ + stopping = 4, /* Transitioning from a started state to stopped. */ +} when SUPPORT_WASAPI { @@ -56,6 +57,96 @@ backend :: enum c.int { BACKEND_COUNT :: len(backend) +/* +Device job thread. This is used by backends that require asynchronous processing of certain +operations. It is not used by all backends. + +The device job thread is made up of a thread and a job queue. You can post a job to the thread with +ma_device_job_thread_post(). The thread will do the processing of the job. +*/ +device_job_thread_config :: struct { + noThread: b32, /* Set this to true if you want to process jobs yourself. */ + jobQueueCapacity: u32, + jobQueueFlags: u32, +} + +device_job_thread :: struct { + thread: thread, + jobQueue: job_queue, + _hasThread: b32, +} + + +/* Device notification types. */ +device_notification_type :: enum c.int { + started, + stopped, + rerouted, + interruption_began, + interruption_ended, +} + +device_notification :: struct { + pDevice: ^device, + type: device_notification_type, + data: struct #raw_union { + started: struct { + _unused: c.int, + }, + stopped: struct { + _unused: c.int, + }, + rerouted: struct { + _unused: c.int, + }, + interruption: struct { + _unused: c.int, + }, + }, +} + +/* +The notification callback for when the application should be notified of a change to the device. + +This callback is used for notifying the application of changes such as when the device has started, +stopped, rerouted or an interruption has occurred. Note that not all backends will post all +notification types. For example, some backends will perform automatic stream routing without any +kind of notification to the host program which means miniaudio will never know about it and will +never be able to fire the rerouted notification. You should keep this in mind when designing your +program. + +The stopped notification will *not* get fired when a device is rerouted. + + +Parameters +---------- +pNotification (in) + A pointer to a structure containing information about the event. Use the `pDevice` member of + this object to retrieve the relevant device. The `type` member can be used to discriminate + against each of the notification types. + + +Remarks +------- +Do not restart or uninitialize the device from the callback. + +Not all notifications will be triggered by all backends, however the started and stopped events +should be reliable for all backends. Some backends do not have a good way to detect device +stoppages due to unplugging the device which may result in the stopped callback not getting +fired. This has been observed with at least one BSD variant. + +The rerouted notification is fired *after* the reroute has occurred. The stopped notification will +*not* get fired when a device is rerouted. The following backends are known to do automatic stream +rerouting, but do not have a way to be notified of the change: + + * DirectSound + +The interruption notifications are used on mobile platforms for detecting when audio is interrupted +due to things like an incoming phone call. Currently this is only implemented on iOS. None of the +Android backends will report this notification. +*/ +device_notification_proc :: proc "c" (pNotification: ^device_notification) + /* The callback for processing audio data from the device. @@ -96,9 +187,11 @@ callback. The following APIs cannot be called from inside the callback: The proper way to stop the device is to call `ma_device_stop()` from a different thread, normally the main application thread. */ -device_callback_proc :: proc "c" (pDevice: ^device, pOutput: rawptr, pInput: rawptr, frameCount: u32) +device_data_proc :: proc "c" (pDevice: ^device, pOutput, pInput: rawptr, frameCount: u32) /* +DEPRECATED. Use ma_device_notification_proc instead. + The callback for when the device has been stopped. This will be called when the device is stopped explicitly with `ma_device_stop()` and also called implicitly when the device is stopped through external forces @@ -108,48 +201,15 @@ such as being unplugged or an internal error occuring. Parameters ---------- pDevice (in) - A pointer to the device that has just stopped. + A pointer to the device that has just stopped. Remarks ------- Do not restart or uninitialize the device from the callback. */ -stop_proc :: proc "c" (pDevice: ^device) +stop_proc :: proc "c" (pDevice: ^device) /* DEPRECATED. Use ma_device_notification_proc instead. */ -/* -The callback for handling log messages. - - -Parameters ----------- -pContext (in) - A pointer to the context the log message originated from. - -pDevice (in) - A pointer to the device the log message originate from, if any. This can be null, in which case the message came from the context. - -logLevel (in) - The log level. This can be one of the following: - - +----------------------+ - | Log Level | - +----------------------+ - | MA_LOG_LEVEL_DEBUG | - | MA_LOG_LEVEL_INFO | - | MA_LOG_LEVEL_WARNING | - | MA_LOG_LEVEL_ERROR | - +----------------------+ - -message (in) - The log message. - - -Remarks -------- -Do not modify the state of the device from inside the callback. -*/ -log_proc :: proc "c" (pContext: context_type, pDevice: ^device, logLevel: u32, message: cstring) device_type :: enum c.int { playback = 1, @@ -279,29 +339,14 @@ device_id :: struct #raw_union { DATA_FORMAT_FLAG_EXCLUSIVE_MODE :: 1 << 1 /* If set, this is supported in exclusive mode. Otherwise not natively supported by exclusive mode. */ +MAX_DEVICE_NAME_LENGTH :: 255 + device_info :: struct { /* Basic info. This is the only information guaranteed to be filled in during device enumeration. */ id: device_id, - name: [256]byte, + name: [MAX_DEVICE_NAME_LENGTH + 1]c.char, /* +1 for null terminator. */ isDefault: b32, - /* - Detailed info. As much of this is filled as possible with ma_context_get_device_info(). Note that you are allowed to initialize - a device with settings outside of this range, but it just means the data will be converted using miniaudio's data conversion - pipeline before sending the data to/from the device. Most programs will need to not worry about these values, but it's provided - here mainly for informational purposes or in the rare case that someone might find it useful. - - These will be set to 0 when returned by ma_context_enumerate_devices() or ma_context_get_devices(). - */ - formatCount: u32, - formats: [format]format, - minChannels: u32, - maxChannels: u32, - minSampleRate: u32, - maxSampleRate: u32, - - - /* Experimental. Don't use these right now. */ nativeDataFormatCount: u32, nativeDataFormats: [/*len(format_count) * standard_sample_rate.rate_count * MAX_CHANNELS*/ 64]struct { /* Not sure how big to make this. There can be *many* permutations for virtual devices which can support anything. */ format: format, /* Sample format. If set to ma_format_unknown, all sample formats are supported. */ @@ -312,31 +357,26 @@ device_info :: struct { } device_config :: struct { - deviceType: device_type, - sampleRate: u32, - periodSizeInFrames: u32, - periodSizeInMilliseconds: u32, - periods: u32, - performanceProfile: performance_profile, - noPreZeroedOutputBuffer: b8, /* When set to true, the contents of the output buffer passed into the data callback will be left undefined rather than initialized to zero. */ - noClip: b8, /* When set to true, the contents of the output buffer passed into the data callback will be clipped after returning. Only applies when the playback sample format is f32. */ - dataCallback: device_callback_proc, - stopCallback: stop_proc, - pUserData: rawptr, - resampling: struct { - algorithm: resample_algorithm, - linear: struct { - lpfOrder: u32, - }, - speex: struct { - quality: c.int, - }, - }, + deviceType: device_type, + sampleRate: u32, + periodSizeInFrames: u32, + periodSizeInMilliseconds: u32, + periods: u32, + performanceProfile: performance_profile, + noPreSilencedOutputBuffer: b8, /* When set to true, the contents of the output buffer passed into the data callback will be left undefined rather than initialized to zero. */ + noClip: b8, /* When set to true, the contents of the output buffer passed into the data callback will be clipped after returning. Only applies when the playback sample format is f32. */ + noDisableDenormals: b8, /* Do not disable denormals when firing the data callback. */ + noFixedSizedCallback: b8, /* Disables strict fixed-sized data callbacks. Setting this to true will result in the period size being treated only as a hint to the backend. This is an optimization for those who don't need fixed sized callbacks. */ + dataCallback: device_data_proc, + notificationCallback: device_notification_proc, + stopCallback: stop_proc, + pUserData: rawptr, + resampling: resampler_config, playback: struct { pDeviceID: ^device_id, format: format, channels: u32, - channelMap: [MAX_CHANNELS]channel, + channelMap: [^]channel, channelMixMode: channel_mix_mode, shareMode: share_mode, }, @@ -344,7 +384,7 @@ device_config :: struct { pDeviceID: ^device_id, format: format, channels: u32, - channelMap: [MAX_CHANNELS]channel, + channelMap: [^]channel, channelMixMode: channel_mix_mode, shareMode: share_mode, }, @@ -373,9 +413,10 @@ device_config :: struct { recordingPreset: opensl_recording_preset, }, aaudio: struct { - usage: aaudio_usage, - contentType: aaudio_content_type, - inputPreset: aaudio_input_preset, + usage: aaudio_usage, + contentType: aaudio_content_type, + inputPreset: aaudio_input_preset, + noAutoStartAfterReroute: b32, }, } @@ -425,14 +466,14 @@ to many devices. A device is created from a context. The general flow goes like this: 1) A context is created with `onContextInit()` - 1a) Available devices can be enumerated with `onContextEnumerateDevices()` if required. - 1b) Detailed information about a device can be queried with `onContextGetDeviceInfo()` if required. + 1a) Available devices can be enumerated with `onContextEnumerateDevices()` if required. + 1b) Detailed information about a device can be queried with `onContextGetDeviceInfo()` if required. 2) A device is created from the context that was created in the first step using `onDeviceInit()`, and optionally a device ID that was - selected from device enumeration via `onContextEnumerateDevices()`. + selected from device enumeration via `onContextEnumerateDevices()`. 3) A device is started or stopped with `onDeviceStart()` / `onDeviceStop()` 4) Data is delivered to and from the device by the backend. This is always done based on the native format returned by the prior call - to `onDeviceInit()`. Conversion between the device's native format and the format requested by the application will be handled by - miniaudio internally. + to `onDeviceInit()`. Conversion between the device's native format and the format requested by the application will be handled by + miniaudio internally. Initialization of the context is quite simple. You need to do any necessary initialization of internal objects and then output the callbacks defined in this structure. @@ -440,7 +481,7 @@ callbacks defined in this structure. Once the context has been initialized you can initialize a device. Before doing so, however, the application may want to know which physical devices are available. This is where `onContextEnumerateDevices()` comes in. This is fairly simple. For each device, fire the given callback with, at a minimum, the basic information filled out in `ma_device_info`. When the callback returns `MA_FALSE`, enumeration -needs to stop and the `onContextEnumerateDevices()` function return with a success code. +needs to stop and the `onContextEnumerateDevices()` function returns with a success code. Detailed device information can be retrieved from a device ID using `onContextGetDeviceInfo()`. This takes as input the device type and ID, and on output returns detailed information about the device in `ma_device_info`. The `onContextGetDeviceInfo()` callback must handle the @@ -455,7 +496,7 @@ internally by miniaudio. On input, if the sample format is set to `ma_format_unknown`, the backend is free to use whatever sample format it desires, so long as it's supported by miniaudio. When the channel count is set to 0, the backend should use the device's native channel count. The same applies for -sample rate. For the channel map, the default should be used when `ma_channel_map_blank()` returns true (all channels set to +sample rate. For the channel map, the default should be used when `ma_channel_map_is_blank()` returns true (all channels set to `MA_CHANNEL_NONE`). On input, the `periodSizeInFrames` or `periodSizeInMilliseconds` option should always be set. The backend should inspect both of these variables. If `periodSizeInFrames` is set, it should take priority, otherwise it needs to be derived from the period size in milliseconds (`periodSizeInMilliseconds`) and the sample rate, keeping in mind that the sample rate may be 0, in which case the @@ -474,14 +515,17 @@ This allows miniaudio to then process any necessary data conversion and then pas If the backend requires absolute flexibility with it's data delivery, it can optionally implement the `onDeviceDataLoop()` callback which will allow it to implement the logic that will run on the audio thread. This is much more advanced and is completely optional. -The audio thread should run data delivery logic in a loop while `ma_device_get_state() == MA_STATE_STARTED` and no errors have been +The audio thread should run data delivery logic in a loop while `ma_device_get_state() == ma_device_state_started` and no errors have been encounted. Do not start or stop the device here. That will be handled from outside the `onDeviceDataLoop()` callback. The invocation of the `onDeviceDataLoop()` callback will be handled by miniaudio. When you start the device, miniaudio will fire this -callback. When the device is stopped, the `ma_device_get_state() == MA_STATE_STARTED` condition will fail and the loop will be terminated +callback. When the device is stopped, the `ma_device_get_state() == ma_device_state_started` condition will fail and the loop will be terminated which will then fall through to the part that stops the device. For an example on how to implement the `onDeviceDataLoop()` callback, look at `ma_device_audio_thread__default_read_write()`. Implement the `onDeviceDataLoopWakeup()` callback if you need a mechanism to wake up the audio thread. + +If the backend supports an optimized retrieval of device information from an initialized `ma_device` object, it should implement the +`onDeviceGetInfo()` callback. This is optional, in which case it will fall back to `onContextGetDeviceInfo()` which is less efficient. */ backend_callbacks :: struct { onContextInit: proc "c" (pContext: ^context_type, pConfig: ^context_config, pCallbacks: ^backend_callbacks) -> result, @@ -496,10 +540,10 @@ backend_callbacks :: struct { onDeviceWrite: proc "c" (pDevice: ^device, pFrames: rawptr, frameCount: u32, pFramesWritten: ^u32) -> result, onDeviceDataLoop: proc "c" (pDevice: ^device) -> result, onDeviceDataLoopWakeup: proc "c" (pDevice: ^device) -> result, + onDeviceGetInfo: proc "c" (pDevice: ^device, type: device_type, pDeviceInfo: ^device_info) -> result, } context_config :: struct { - logCallback: log_proc, /* Legacy logging callback. Will be removed in version 0.11. */ pLog: ^log, threadPriority: thread_priority, threadStackSize: c.size_t, @@ -538,7 +582,7 @@ context_command__wasapi :: struct { deviceType: device_type, pAudioClient: rawptr, ppAudioClientService: ^rawptr, - pResult: ^rawptr, /* The result from creating the audio client service. */ + pResult: ^result, /* The result from creating the audio client service. */ }, releaseAudioClient: struct { pDevice: ^device, @@ -548,21 +592,20 @@ context_command__wasapi :: struct { } context_type :: struct { - callbacks: backend_callbacks, - backend: backend, /* DirectSound, ALSA, etc. */ - pLog: ^log, - log: log, /* Only used if the log is owned by the context. The pLog member will be set to &log in this case. */ - logCallback: log_proc, /* Legacy callback. Will be removed in version 0.11. */ - threadPriority: thread_priority, - threadStackSize: c.size_t, - pUserData: rawptr, - allocationCallbacks: allocation_callbacks, - deviceEnumLock: mutex, /* Used to make ma_context_get_devices() thread safe. */ - deviceInfoLock: mutex, /* Used to make ma_context_get_device_info() thread safe. */ - deviceInfoCapacity: u32, /* Total capacity of pDeviceInfos. */ + callbacks: backend_callbacks, + backend: backend, /* DirectSound, ALSA, etc. */ + pLog: ^log, + log: log, /* Only used if the log is owned by the context. The pLog member will be set to &log in this case. */ + threadPriority: thread_priority, + threadStackSize: c.size_t, + pUserData: rawptr, + allocationCallbacks: allocation_callbacks, + deviceEnumLock: mutex, /* Used to make ma_context_get_devices() thread safe. */ + deviceInfoLock: mutex, /* Used to make ma_context_get_device_info() thread safe. */ + deviceInfoCapacity: u32, /* Total capacity of pDeviceInfos. */ playbackDeviceInfoCount: u32, - captureDeviceInfoCount: u32, - pDeviceInfos: [^]device_info, /* Playback devices first, then capture. */ + captureDeviceInfoCount: u32, + pDeviceInfos: [^]device_info, /* Playback devices first, then capture. */ using _: struct #raw_union { wasapi: (struct { @@ -575,7 +618,7 @@ context_type :: struct { } when SUPPORT_WASAPI else struct {}), dsound: (struct { - DSoundDLL: handle, + hDSoundDLL: handle, DirectSoundCreate: proc "system" (), DirectSoundEnumerateA: proc "system" (), DirectSoundCaptureCreate: proc "system" (), @@ -739,8 +782,10 @@ context_type :: struct { pa_stream_writable_size: proc "system" (), pa_stream_readable_size: proc "system" (), - /*pa_mainloop**/ pMainLoop: ptr, - /*pa_context**/ pPulseContext: ptr, + /*pa_mainloop**/ pMainLoop: rawptr, + /*pa_context**/ pPulseContext: rawptr, + pApplicationName: cstring, /* Set when the context is initialized. Used by devices for their local pa_context objects. */ + pServerName: cstring, /* Set when the context is initialized. Used by devices for their local pa_context objects. */ } when SUPPORT_PULSEAUDIO else struct {}), jack: (struct { @@ -762,7 +807,7 @@ context_type :: struct { jack_port_get_buffer: proc "system" (), jack_free: proc "system" (), - pClientName: [^]c.char, + pClientName: cstring, tryStartServer: b32, } when SUPPORT_JACK else struct {}), @@ -791,7 +836,7 @@ context_type :: struct { AudioUnitInitialize: proc "system" (), AudioUnitRender: proc "system" (), - /*AudioComponent*/ component: ptr, + /*AudioComponent*/ component: rawptr, noAudioSessionDeactivate: b32, /* For tracking whether or not the iOS audio session should be explicitly deactivated. Set from the config in ma_context_init__coreaudio(). */ } when SUPPORT_COREAUDIO else struct {}), @@ -817,7 +862,7 @@ context_type :: struct { } when SUPPORT_SNDIO else struct {}), audio4: (struct { - _unused: cint, + _unused: c.int, } when SUPPORT_AUDIO4 else struct {}), oss: (struct { @@ -855,6 +900,7 @@ context_type :: struct { AAudioStream_getFramesPerBurst: proc "system" (), AAudioStream_requestStart: proc "system" (), AAudioStream_requestStop: proc "system" (), + jobThread: device_job_thread, /* For processing operations outside of the error callback, specifically device disconnections and rerouting. */ } when SUPPORT_AAUDIO else struct {}), opensl: (struct { @@ -895,7 +941,7 @@ context_type :: struct { RegOpenKeyExA: proc "system" (), RegCloseKey: proc "system" (), RegQueryValueExA: proc "system" (), - } when ODIN_OS == "windows" else struct {}), + } when ODIN_OS == .Windows else struct {}), posix: (struct { pthreadSO: handle, @@ -914,44 +960,47 @@ context_type :: struct { pthread_attr_setschedpolicy: proc "system" (), pthread_attr_getschedparam: proc "system" (), pthread_attr_setschedparam: proc "system" (), - } when ODIN_OS != "windows" else struct {}), + } when ODIN_OS != .Windows else struct {}), _unused: c.int, }, } device :: struct { - pContext: ^context_type, - type: device_type, - sampleRate: u32, - state: u32, /*atomic*/ /* The state of the device is variable and can change at any time on any thread. Must be used atomically. */ - onData: device_callback_proc, /* Set once at initialization time and should not be changed after. */ - onStop: stop_proc, /* Set once at initialization time and should not be changed after. */ - pUserData: rawptr, /* Application defined data. */ - startStopLock: mutex, - wakeupEvent: event, - startEvent: event, - stopEvent: event, - device_thread: thread, - workResult: result, /* This is set by the worker thread after it's finished doing a job. */ - isOwnerOfContext: b8, /* When set to true, uninitializing the device will also uninitialize the context. Set to true when NULL is passed into ma_device_init(). */ - noPreZeroedOutputBuffer: b8, - noClip: b8, - masterVolumeFactor: f32, /*atomic*/ /* Linear 0..1. Can be read and written simultaneously by different threads. Must be used atomically. */ - duplexRB: duplex_rb, /* Intermediary buffer for duplex device on asynchronous backends. */ + pContext: ^context_type, + type: device_type, + sampleRate: u32, + state: u32, /*atomic*/ /* The state of the device is variable and can change at any time on any thread. Must be used atomically. */ + onData: device_data_proc, /* Set once at initialization time and should not be changed after. */ + onNotification: device_notification_proc, /* Set once at initialization time and should not be changed after. */ + onStop: stop_proc, /* DEPRECATED. Use the notification callback instead. Set once at initialization time and should not be changed after. */ + pUserData: rawptr, /* Application defined data. */ + startStopLock: mutex, + wakeupEvent: event, + startEvent: event, + stopEvent: event, + device_thread: thread, + workResult: result, /* This is set by the worker thread after it's finished doing a job. */ + isOwnerOfContext: b8, /* When set to true, uninitializing the device will also uninitialize the context. Set to true when NULL is passed into ma_device_init(). */ + noPreSilencedOutputBuffer: b8, + noClip: b8, + noDisableDenormals: b8, + noFixedSizedCallback: b8, + masterVolumeFactor: f32, /*atomic*/ /* Linear 0..1. Can be read and written simultaneously by different threads. Must be used atomically. */ + duplexRB: duplex_rb, /* Intermediary buffer for duplex device on asynchronous backends. */ resampling: struct { - algorithm: resample_algorithm, + algorithm: resample_algorithm, + pBackendVTable: ^resampling_backend_vtable, + pBackendUserData: rawptr, linear: struct { lpfOrder: u32, }, - speex: struct { - quality: c.int, - }, }, playback: struct { - id: device_id, /* If using an explicit device, will be set to a copy of the ID used for initialization. Otherwise cleared to 0. */ - name: [256]byte, /* Maybe temporary. Likely to be replaced with a query API. */ - shareMode: share_mode, /* Set to whatever was passed in when the device was initialized. */ + pID: ^device_id, /* Set to NULL if using default ID, otherwise set to the address of "id". */ + id: device_id, /* If using an explicit device, will be set to a copy of the ID used for initialization. Otherwise cleared to 0. */ + name: [MAX_DEVICE_NAME_LENGTH + 1]c.char, /* Maybe temporary. Likely to be replaced with a query API. */ + shareMode: share_mode, /* Set to whatever was passed in when the device was initialized. */ playback_format: format, channels: u32, channelMap: [MAX_CHANNELS]channel, @@ -963,11 +1012,19 @@ device :: struct { internalPeriods: u32, channelMixMode: channel_mix_mode, converter: data_converter, + pIntermediaryBuffer: rawptr, /* For implementing fixed sized buffer callbacks. Will be null if using variable sized callbacks. */ + intermediaryBufferCap: u32, + intermediaryBufferLen: u32, /* How many valid frames are sitting in the intermediary buffer. */ + pInputCache: rawptr, /* In external format. Can be null. */ + inputCacheCap: u64, + inputCacheConsumed: u64, + inputCacheRemaining: u64, }, capture: struct { - id: device_id, /* If using an explicit device, will be set to a copy of the ID used for initialization. Otherwise cleared to 0. */ - name: [256]byte, /* Maybe temporary. Likely to be replaced with a query API. */ - shareMode: share_mode, /* Set to whatever was passed in when the device was initialized. */ + pID: ^device_id, /* Set to NULL if using default ID, otherwise set to the address of "id". */ + id: device_id, /* If using an explicit device, will be set to a copy of the ID used for initialization. Otherwise cleared to 0. */ + name: [MAX_DEVICE_NAME_LENGTH + 1]c.char, /* Maybe temporary. Likely to be replaced with a query API. */ + shareMode: share_mode, /* Set to whatever was passed in when the device was initialized. */ capture_format: format, channels: u32, channelMap: [MAX_CHANNELS]channel, @@ -979,6 +1036,9 @@ device :: struct { internalPeriods: u32, channelMixMode: channel_mix_mode, converter: data_converter, + pIntermediaryBuffer: rawptr, /* For implementing fixed sized buffer callbacks. Will be null if using variable sized callbacks. */ + intermediaryBufferCap: u32, + intermediaryBufferLen: u32, /* How many valid frames are sitting in the intermediary buffer. */ }, using _: struct #raw_union { @@ -991,7 +1051,7 @@ device :: struct { notificationClient: IMMNotificationClient, /*HANDLE*/ hEventPlayback: handle, /* Auto reset. Initialized to signaled. */ /*HANDLE*/ hEventCapture: handle, /* Auto reset. Initialized to unsignaled. */ - actualPeriodSizeInFramesPlayback: u32, /* Value from GetBufferSize(). internalPeriodSizeInFrames is not set to the _actual_ buffer size when low-latency shared mode is being used due to the way the IAudioClient3 API works. */ + actualPeriodSizeInFramesPlayback: u32, /* Value from GetBufferSize(). internalPeriodSizeInFrames is not set to the _actual_ buffer size when low-latency shared mode is being used due to the way the IAudioClient3 API works. */ actualPeriodSizeInFramesCapture: u32, originalPeriodSizeInFrames: u32, originalPeriodSizeInMilliseconds: u32, @@ -999,8 +1059,14 @@ device :: struct { originalPerformanceProfile: performance_profile, periodSizeInFramesPlayback: u32, periodSizeInFramesCapture: u32, - isStartedCapture: b32, /*atomic*/ /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ - isStartedPlayback: b32, /*atomic*/ /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ + pMappedBufferCapture: rawptr, + mappedBufferCaptureCap: u32, + mappedBufferCaptureLen: u32, + pMappedBufferPlayback: rawptr, + mappedBufferPlaybackCap: u32, + mappedBufferPlaybackLen: u32, + isStartedCapture: b32, /*atomic*/ /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ + isStartedPlayback: b32, /*atomic*/ /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ noAutoConvertSRC: b8, /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */ noDefaultQualitySRC: b8, /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */ noHardwareOffloading: b8, @@ -1049,14 +1115,16 @@ device :: struct { } when SUPPORT_ALSA else struct {}), pulse: (struct { + /*pa_mainloop**/ pMainLoop: rawptr, + /*pa_context**/ pPulseContext: rawptr, /*pa_stream**/ pStreamPlayback: rawptr, /*pa_stream**/ pStreamCapture: rawptr, } when SUPPORT_PULSEAUDIO else struct {}), jack: (struct { /*jack_client_t**/ pClient: rawptr, - /*jack_port_t**/ pPortsPlayback: [MAX_CHANNELS]rawptr, - /*jack_port_t**/ pPortsCapture: [MAX_CHANNELS]rawptr, + /*jack_port_t**/ pPortsPlayback: [^]rawptr, + /*jack_port_t**/ pPortsCapture: [^]rawptr, pIntermediaryBufferPlayback: [^]f32, /* Typed as a float because JACK is always floating point. */ pIntermediaryBufferCapture: [^]f32, } when SUPPORT_JACK else struct {}), @@ -1079,6 +1147,7 @@ device :: struct { isSwitchingCaptureDevice: b32, /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */ pRouteChangeHandler: rawptr, /* Only used on mobile platforms. Obj-C object for handling route changes. */ } when SUPPORT_COREAUDIO else struct {}), + sndio: (struct { handlePlayback: rawptr, handleCapture: rawptr, @@ -1099,6 +1168,10 @@ device :: struct { aaudio: (struct { /*AAudioStream**/ pStreamPlayback: rawptr, /*AAudioStream**/ pStreamCapture: rawptr, + usage: aaudio_usage, + contentType: aaudio_content_type, + inputPreset: aaudio_input_preset, + noAutoStartAfterReroute: b32, } when SUPPORT_AAUDIO else struct {}), opensl: (struct { diff --git a/vendor/miniaudio/doc.odin b/vendor/miniaudio/doc.odin index 887e5d149..c6de0ec61 100644 --- a/vendor/miniaudio/doc.odin +++ b/vendor/miniaudio/doc.odin @@ -2,7 +2,7 @@ package miniaudio /* Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. -miniaudio - v0.10.42 - 2021-08-22 +miniaudio - v0.11.9 - 2022-04-20 David Reid - mackron@gmail.com @@ -14,7 +14,8 @@ GitHub: https://github.com/mackron/miniaudio /* 1. Introduction =============== -miniaudio is a single file library for audio playback and capture. To use it, do the following in one .c file: +miniaudio is a single file library for audio playback and capture. To use it, do the following in +one .c file: ```c #define MINIAUDIO_IMPLEMENTATION @@ -23,16 +24,44 @@ miniaudio is a single file library for audio playback and capture. To use it, do You can do `#include "miniaudio.h"` in other parts of the program just like any other header. -miniaudio uses the concept of a "device" as the abstraction for physical devices. The idea is that you choose a physical device to emit or capture audio from, -and then move data to/from the device when miniaudio tells you to. Data is delivered to and from devices asynchronously via a callback which you specify when -initializing the device. +miniaudio includes both low level and high level APIs. The low level API is good for those who want +to do all of their mixing themselves and only require a light weight interface to the underlying +audio device. The high level API is good for those who have complex mixing and effect requirements. -When initializing the device you first need to configure it. The device configuration allows you to specify things like the format of the data delivered via -the callback, the size of the internal buffer and the ID of the device you want to emit or capture audio from. +In miniaudio, objects are transparent structures. Unlike many other libraries, there are no handles +to opaque objects which means you need to allocate memory for objects yourself. In the examples +presented in this documentation you will often see objects declared on the stack. You need to be +careful when translating these examples to your own code so that you don't accidentally declare +your objects on the stack and then cause them to become invalid once the function returns. In +addition, you must ensure the memory address of your objects remain the same throughout their +lifetime. You therefore cannot be making copies of your objects. -Once you have the device configuration set up you can initialize the device. When initializing a device you need to allocate memory for the device object -beforehand. This gives the application complete control over how the memory is allocated. In the example below we initialize a playback device on the stack, -but you could allocate it on the heap if that suits your situation better. +A config/init pattern is used throughout the entire library. The idea is that you set up a config +object and pass that into the initialization routine. The advantage to this system is that the +config object can be initialized with logical defaults and new properties added to it without +breaking the API. The config object can be allocated on the stack and does not need to be +maintained after initialization of the corresponding object. + + +1.1. Low Level API +------------------ +The low level API gives you access to the raw audio data of an audio device. It supports playback, +capture, full-duplex and loopback (WASAPI only). You can enumerate over devices to determine which +physical device(s) you want to connect to. + +The low level API uses the concept of a "device" as the abstraction for physical devices. The idea +is that you choose a physical device to emit or capture audio from, and then move data to/from the +device when miniaudio tells you to. Data is delivered to and from devices asynchronously via a +callback which you specify when initializing the device. + +When initializing the device you first need to configure it. The device configuration allows you to +specify things like the format of the data delivered via the callback, the size of the internal +buffer and the ID of the device you want to emit or capture audio from. + +Once you have the device configuration set up you can initialize the device. When initializing a +device you need to allocate memory for the device object beforehand. This gives the application +complete control over how the memory is allocated. In the example below we initialize a playback +device on the stack, but you could allocate it on the heap if that suits your situation better. ```c void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) @@ -65,20 +94,27 @@ but you could allocate it on the heap if that suits your situation better. } ``` -In the example above, `data_callback()` is where audio data is written and read from the device. The idea is in playback mode you cause sound to be emitted -from the speakers by writing audio data to the output buffer (`pOutput` in the example). In capture mode you read data from the input buffer (`pInput`) to -extract sound captured by the microphone. The `frameCount` parameter tells you how many frames can be written to the output buffer and read from the input -buffer. A "frame" is one sample for each channel. For example, in a stereo stream (2 channels), one frame is 2 samples: one for the left, one for the right. -The channel count is defined by the device config. The size in bytes of an individual sample is defined by the sample format which is also specified in the -device config. Multi-channel audio data is always interleaved, which means the samples for each frame are stored next to each other in memory. For example, in -a stereo stream the first pair of samples will be the left and right samples for the first frame, the second pair of samples will be the left and right samples -for the second frame, etc. +In the example above, `data_callback()` is where audio data is written and read from the device. +The idea is in playback mode you cause sound to be emitted from the speakers by writing audio data +to the output buffer (`pOutput` in the example). In capture mode you read data from the input +buffer (`pInput`) to extract sound captured by the microphone. The `frameCount` parameter tells you +how many frames can be written to the output buffer and read from the input buffer. A "frame" is +one sample for each channel. For example, in a stereo stream (2 channels), one frame is 2 +samples: one for the left, one for the right. The channel count is defined by the device config. +The size in bytes of an individual sample is defined by the sample format which is also specified +in the device config. Multi-channel audio data is always interleaved, which means the samples for +each frame are stored next to each other in memory. For example, in a stereo stream the first pair +of samples will be the left and right samples for the first frame, the second pair of samples will +be the left and right samples for the second frame, etc. -The configuration of the device is defined by the `ma_device_config` structure. The config object is always initialized with `ma_device_config_init()`. It's -important to always initialize the config with this function as it initializes it with logical defaults and ensures your program doesn't break when new members -are added to the `ma_device_config` structure. The example above uses a fairly simple and standard device configuration. The call to `ma_device_config_init()` -takes a single parameter, which is whether or not the device is a playback, capture, duplex or loopback device (loopback devices are not supported on all -backends). The `config.playback.format` member sets the sample format which can be one of the following (all formats are native-endian): +The configuration of the device is defined by the `ma_device_config` structure. The config object +is always initialized with `ma_device_config_init()`. It's important to always initialize the +config with this function as it initializes it with logical defaults and ensures your program +doesn't break when new members are added to the `ma_device_config` structure. The example above +uses a fairly simple and standard device configuration. The call to `ma_device_config_init()` takes +a single parameter, which is whether or not the device is a playback, capture, duplex or loopback +device (loopback devices are not supported on all backends). The `config.playback.format` member +sets the sample format which can be one of the following (all formats are native-endian): +---------------+----------------------------------------+---------------------------+ | Symbol | Description | Range | @@ -90,22 +126,30 @@ backends). The `config.playback.format` member sets the sample format which can | ma_format_u8 | 8-bit unsigned integer | [0, 255] | +---------------+----------------------------------------+---------------------------+ -The `config.playback.channels` member sets the number of channels to use with the device. The channel count cannot exceed MA_MAX_CHANNELS. The -`config.sampleRate` member sets the sample rate (which must be the same for both playback and capture in full-duplex configurations). This is usually set to -44100 or 48000, but can be set to anything. It's recommended to keep this between 8000 and 384000, however. +The `config.playback.channels` member sets the number of channels to use with the device. The +channel count cannot exceed MA_MAX_CHANNELS. The `config.sampleRate` member sets the sample rate +(which must be the same for both playback and capture in full-duplex configurations). This is +usually set to 44100 or 48000, but can be set to anything. It's recommended to keep this between +8000 and 384000, however. -Note that leaving the format, channel count and/or sample rate at their default values will result in the internal device's native configuration being used -which is useful if you want to avoid the overhead of miniaudio's automatic data conversion. +Note that leaving the format, channel count and/or sample rate at their default values will result +in the internal device's native configuration being used which is useful if you want to avoid the +overhead of miniaudio's automatic data conversion. -In addition to the sample format, channel count and sample rate, the data callback and user data pointer are also set via the config. The user data pointer is -not passed into the callback as a parameter, but is instead set to the `pUserData` member of `ma_device` which you can access directly since all miniaudio -structures are transparent. +In addition to the sample format, channel count and sample rate, the data callback and user data +pointer are also set via the config. The user data pointer is not passed into the callback as a +parameter, but is instead set to the `pUserData` member of `ma_device` which you can access +directly since all miniaudio structures are transparent. -Initializing the device is done with `ma_device_init()`. This will return a result code telling you what went wrong, if anything. On success it will return -`MA_SUCCESS`. After initialization is complete the device will be in a stopped state. To start it, use `ma_device_start()`. Uninitializing the device will stop -it, which is what the example above does, but you can also stop the device with `ma_device_stop()`. To resume the device simply call `ma_device_start()` again. -Note that it's important to never stop or start the device from inside the callback. This will result in a deadlock. Instead you set a variable or signal an -event indicating that the device needs to stop and handle it in a different thread. The following APIs must never be called inside the callback: +Initializing the device is done with `ma_device_init()`. This will return a result code telling you +what went wrong, if anything. On success it will return `MA_SUCCESS`. After initialization is +complete the device will be in a stopped state. To start it, use `ma_device_start()`. +Uninitializing the device will stop it, which is what the example above does, but you can also stop +the device with `ma_device_stop()`. To resume the device simply call `ma_device_start()` again. +Note that it's important to never stop or start the device from inside the callback. This will +result in a deadlock. Instead you set a variable or signal an event indicating that the device +needs to stop and handle it in a different thread. The following APIs must never be called inside +the callback: ```c ma_device_init() @@ -115,12 +159,14 @@ event indicating that the device needs to stop and handle it in a different thre ma_device_stop() ``` -You must never try uninitializing and reinitializing a device inside the callback. You must also never try to stop and start it from inside the callback. There -are a few other things you shouldn't do in the callback depending on your requirements, however this isn't so much a thread-safety thing, but rather a -real-time processing thing which is beyond the scope of this introduction. +You must never try uninitializing and reinitializing a device inside the callback. You must also +never try to stop and start it from inside the callback. There are a few other things you shouldn't +do in the callback depending on your requirements, however this isn't so much a thread-safety +thing, but rather a real-time processing thing which is beyond the scope of this introduction. -The example above demonstrates the initialization of a playback device, but it works exactly the same for capture. All you need to do is change the device type -from `ma_device_type_playback` to `ma_device_type_capture` when setting up the config, like so: +The example above demonstrates the initialization of a playback device, but it works exactly the +same for capture. All you need to do is change the device type from `ma_device_type_playback` to +`ma_device_type_capture` when setting up the config, like so: ```c ma_device_config config = ma_device_config_init(ma_device_type_capture); @@ -128,8 +174,9 @@ from `ma_device_type_playback` to `ma_device_type_capture` when setting up the c config.capture.channels = MY_CHANNEL_COUNT; ``` -In the data callback you just read from the input buffer (`pInput` in the example above) and leave the output buffer alone (it will be set to NULL when the -device type is set to `ma_device_type_capture`). +In the data callback you just read from the input buffer (`pInput` in the example above) and leave +the output buffer alone (it will be set to NULL when the device type is set to +`ma_device_type_capture`). These are the available device types and how you should handle the buffers in the callback: @@ -142,23 +189,29 @@ These are the available device types and how you should handle the buffers in th | ma_device_type_loopback | Read from input buffer, leave output buffer untouched. | +-------------------------+--------------------------------------------------------+ -You will notice in the example above that the sample format and channel count is specified separately for playback and capture. This is to support different -data formats between the playback and capture devices in a full-duplex system. An example may be that you want to capture audio data as a monaural stream (one -channel), but output sound to a stereo speaker system. Note that if you use different formats between playback and capture in a full-duplex configuration you -will need to convert the data yourself. There are functions available to help you do this which will be explained later. +You will notice in the example above that the sample format and channel count is specified +separately for playback and capture. This is to support different data formats between the playback +and capture devices in a full-duplex system. An example may be that you want to capture audio data +as a monaural stream (one channel), but output sound to a stereo speaker system. Note that if you +use different formats between playback and capture in a full-duplex configuration you will need to +convert the data yourself. There are functions available to help you do this which will be +explained later. -The example above did not specify a physical device to connect to which means it will use the operating system's default device. If you have multiple physical -devices connected and you want to use a specific one you will need to specify the device ID in the configuration, like so: +The example above did not specify a physical device to connect to which means it will use the +operating system's default device. If you have multiple physical devices connected and you want to +use a specific one you will need to specify the device ID in the configuration, like so: ```c config.playback.pDeviceID = pMyPlaybackDeviceID; // Only if requesting a playback or duplex device. config.capture.pDeviceID = pMyCaptureDeviceID; // Only if requesting a capture, duplex or loopback device. ``` -To retrieve the device ID you will need to perform device enumeration, however this requires the use of a new concept called the "context". Conceptually -speaking the context sits above the device. There is one context to many devices. The purpose of the context is to represent the backend at a more global level -and to perform operations outside the scope of an individual device. Mainly it is used for performing run-time linking against backend libraries, initializing -backends and enumerating devices. The example below shows how to enumerate devices. +To retrieve the device ID you will need to perform device enumeration, however this requires the +use of a new concept called the "context". Conceptually speaking the context sits above the device. +There is one context to many devices. The purpose of the context is to represent the backend at a +more global level and to perform operations outside the scope of an individual device. Mainly it is +used for performing run-time linking against backend libraries, initializing backends and +enumerating devices. The example below shows how to enumerate devices. ```c ma_context context; @@ -199,44 +252,236 @@ backends and enumerating devices. The example below shows how to enumerate devic ma_context_uninit(&context); ``` -The first thing we do in this example is initialize a `ma_context` object with `ma_context_init()`. The first parameter is a pointer to a list of `ma_backend` -values which are used to override the default backend priorities. When this is NULL, as in this example, miniaudio's default priorities are used. The second -parameter is the number of backends listed in the array pointed to by the first parameter. The third parameter is a pointer to a `ma_context_config` object -which can be NULL, in which case defaults are used. The context configuration is used for setting the logging callback, custom memory allocation callbacks, -user-defined data and some backend-specific configurations. +The first thing we do in this example is initialize a `ma_context` object with `ma_context_init()`. +The first parameter is a pointer to a list of `ma_backend` values which are used to override the +default backend priorities. When this is NULL, as in this example, miniaudio's default priorities +are used. The second parameter is the number of backends listed in the array pointed to by the +first parameter. The third parameter is a pointer to a `ma_context_config` object which can be +NULL, in which case defaults are used. The context configuration is used for setting the logging +callback, custom memory allocation callbacks, user-defined data and some backend-specific +configurations. -Once the context has been initialized you can enumerate devices. In the example above we use the simpler `ma_context_get_devices()`, however you can also use a -callback for handling devices by using `ma_context_enumerate_devices()`. When using `ma_context_get_devices()` you provide a pointer to a pointer that will, -upon output, be set to a pointer to a buffer containing a list of `ma_device_info` structures. You also provide a pointer to an unsigned integer that will -receive the number of items in the returned buffer. Do not free the returned buffers as their memory is managed internally by miniaudio. +Once the context has been initialized you can enumerate devices. In the example above we use the +simpler `ma_context_get_devices()`, however you can also use a callback for handling devices by +using `ma_context_enumerate_devices()`. When using `ma_context_get_devices()` you provide a pointer +to a pointer that will, upon output, be set to a pointer to a buffer containing a list of +`ma_device_info` structures. You also provide a pointer to an unsigned integer that will receive +the number of items in the returned buffer. Do not free the returned buffers as their memory is +managed internally by miniaudio. -The `ma_device_info` structure contains an `id` member which is the ID you pass to the device config. It also contains the name of the device which is useful -for presenting a list of devices to the user via the UI. +The `ma_device_info` structure contains an `id` member which is the ID you pass to the device +config. It also contains the name of the device which is useful for presenting a list of devices +to the user via the UI. -When creating your own context you will want to pass it to `ma_device_init()` when initializing the device. Passing in NULL, like we do in the first example, -will result in miniaudio creating the context for you, which you don't want to do since you've already created a context. Note that internally the context is -only tracked by it's pointer which means you must not change the location of the `ma_context` object. If this is an issue, consider using `malloc()` to -allocate memory for the context. +When creating your own context you will want to pass it to `ma_device_init()` when initializing the +device. Passing in NULL, like we do in the first example, will result in miniaudio creating the +context for you, which you don't want to do since you've already created a context. Note that +internally the context is only tracked by it's pointer which means you must not change the location +of the `ma_context` object. If this is an issue, consider using `malloc()` to allocate memory for +the context. + + +1.2. High Level API +------------------- +The high level API consists of three main parts: + + * Resource management for loading and streaming sounds. + * A node graph for advanced mixing and effect processing. + * A high level "engine" that wraps around the resource manager and node graph. + +The resource manager (`ma_resource_manager`) is used for loading sounds. It supports loading sounds +fully into memory and also streaming. It will also deal with reference counting for you which +avoids the same sound being loaded multiple times. + +The node graph is used for mixing and effect processing. The idea is that you connect a number of +nodes into the graph by connecting each node's outputs to another node's inputs. Each node can +implement it's own effect. By chaining nodes together, advanced mixing and effect processing can +be achieved. + +The engine encapsulates both the resource manager and the node graph to create a simple, easy to +use high level API. The resource manager and node graph APIs are covered in more later sections of +this manual. + +The code below shows how you can initialize an engine using it's default configuration. + + ```c + ma_result result; + ma_engine engine; + + result = ma_engine_init(NULL, &engine); + if (result != MA_SUCCESS) { + return result; // Failed to initialize the engine. + } + ``` + +This creates an engine instance which will initialize a device internally which you can access with +`ma_engine_get_device()`. It will also initialize a resource manager for you which can be accessed +with `ma_engine_get_resource_manager()`. The engine itself is a node graph (`ma_node_graph`) which +means you can pass a pointer to the engine object into any of the `ma_node_graph` APIs (with a +cast). Alternatively, you can use `ma_engine_get_node_graph()` instead of a cast. + +Note that all objects in miniaudio, including the `ma_engine` object in the example above, are +transparent structures. There are no handles to opaque structures in miniaudio which means you need +to be mindful of how you declare them. In the example above we are declaring it on the stack, but +this will result in the struct being invalidated once the function encapsulating it returns. If +allocating the engine on the heap is more appropriate, you can easily do so with a standard call +to `malloc()` or whatever heap allocation routine you like: + + ```c + ma_engine* pEngine = malloc(sizeof(*pEngine)); + ``` + +The `ma_engine` API uses the same config/init pattern used all throughout miniaudio. To configure +an engine, you can fill out a `ma_engine_config` object and pass it into the first parameter of +`ma_engine_init()`: + + ```c + ma_result result; + ma_engine engine; + ma_engine_config engineConfig; + + engineConfig = ma_engine_config_init(); + engineConfig.pResourceManager = &myCustomResourceManager; // <-- Initialized as some earlier stage. + + result = ma_engine_init(&engineConfig, &engine); + if (result != MA_SUCCESS) { + return result; + } + ``` + +This creates an engine instance using a custom config. In this particular example it's showing how +you can specify a custom resource manager rather than having the engine initialize one internally. +This is particularly useful if you want to have multiple engine's share the same resource manager. + +The engine must be uninitialized with `ma_engine_uninit()` when it's no longer needed. + +By default the engine will be started, but nothing will be playing because no sounds have been +initialized. The easiest but least flexible way of playing a sound is like so: + + ```c + ma_engine_play_sound(&engine, "my_sound.wav", NULL); + ``` + +This plays what miniaudio calls an "inline" sound. It plays the sound once, and then puts the +internal sound up for recycling. The last parameter is used to specify which sound group the sound +should be associated with which will be explained later. This particular way of playing a sound is +simple, but lacks flexibility and features. A more flexible way of playing a sound is to first +initialize a sound: + + ```c + ma_result result; + ma_sound sound; + + result = ma_sound_init_from_file(&engine, "my_sound.wav", 0, NULL, NULL, &sound); + if (result != MA_SUCCESS) { + return result; + } + + ma_sound_start(&sound); + ``` + +This returns a `ma_sound` object which represents a single instance of the specified sound file. If +you want to play the same file multiple times simultaneously, you need to create one sound for each +instance. + +Sounds should be uninitialized with `ma_sound_uninit()`. + +Sounds are not started by default. Start a sound with `ma_sound_start()` and stop it with +`ma_sound_stop()`. When a sound is stopped, it is not rewound to the start. Use +`ma_sound_seek_to_pcm_frames(&sound, 0)` to seek back to the start of a sound. By default, starting +and stopping sounds happens immediately, but sometimes it might be convenient to schedule the sound +the be started and/or stopped at a specific time. This can be done with the following functions: + + ```c + ma_sound_set_start_time_in_pcm_frames() + ma_sound_set_start_time_in_milliseconds() + ma_sound_set_stop_time_in_pcm_frames() + ma_sound_set_stop_time_in_milliseconds() + ``` + +The start/stop time needs to be specified based on the absolute timer which is controlled by the +engine. The current global time time in PCM frames can be retrieved with `ma_engine_get_time()`. +The engine's global time can be changed with `ma_engine_set_time()` for synchronization purposes if +required. Note that scheduling a start time still requires an explicit call to `ma_sound_start()` +before anything will play: + + ```c + ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time(&engine) + (ma_engine_get_sample_rate(&engine) * 2); + ma_sound_start(&sound); + ``` + +The third parameter of `ma_sound_init_from_file()` is a set of flags that control how the sound be +loaded and a few options on which features should be enabled for that sound. By default, the sound +is synchronously loaded fully into memory straight from the file system without any kind of +decoding. If you want to decode the sound before storing it in memory, you need to specify the +`MA_SOUND_FLAG_DECODE` flag. This is useful if you want to incur the cost of decoding at an earlier +stage, such as a loading stage. Without this option, decoding will happen dynamically at mixing +time which might be too expensive on the audio thread. + +If you want to load the sound asynchronously, you can specify the `MA_SOUND_FLAG_ASYNC` flag. This +will result in `ma_sound_init_from_file()` returning quickly, but the sound will not start playing +until the sound has had some audio decoded. + +The fourth parameter is a pointer to sound group. A sound group is used as a mechanism to organise +sounds into groups which have their own effect processing and volume control. An example is a game +which might have separate groups for sfx, voice and music. Each of these groups have their own +independent volume control. Use `ma_sound_group_init()` or `ma_sound_group_init_ex()` to initialize +a sound group. + +Sounds and sound groups are nodes in the engine's node graph and can be plugged into any `ma_node` +API. This makes it possible to connect sounds and sound groups to effect nodes to produce complex +effect chains. + +A sound can have it's volume changed with `ma_sound_set_volume()`. If you prefer decibel volume +control you can use `ma_volume_db_to_linear()` to convert from decibel representation to linear. + +Panning and pitching is supported with `ma_sound_set_pan()` and `ma_sound_set_pitch()`. If you know +a sound will never have it's pitch changed with `ma_sound_set_pitch()` or via the doppler effect, +you can specify the `MA_SOUND_FLAG_NO_PITCH` flag when initializing the sound for an optimization. + +By default, sounds and sound groups have spatialization enabled. If you don't ever want to +spatialize your sounds, initialize the sound with the `MA_SOUND_FLAG_NO_SPATIALIZATION` flag. The +spatialization model is fairly simple and is roughly on feature parity with OpenAL. HRTF and +environmental occlusion are not currently supported, but planned for the future. The supported +features include: + + * Sound and listener positioning and orientation with cones + * Attenuation models: none, inverse, linear and exponential + * Doppler effect + +Sounds can be faded in and out with `ma_sound_set_fade_in_pcm_frames()`. + +To check if a sound is currently playing, you can use `ma_sound_is_playing()`. To check if a sound +is at the end, use `ma_sound_at_end()`. Looping of a sound can be controlled with +`ma_sound_set_looping()`. Use `ma_sound_is_looping()` to check whether or not the sound is looping. 2. Building =========== -miniaudio should work cleanly out of the box without the need to download or install any dependencies. See below for platform-specific details. +miniaudio should work cleanly out of the box without the need to download or install any +dependencies. See below for platform-specific details. 2.1. Windows ------------ -The Windows build should compile cleanly on all popular compilers without the need to configure any include paths nor link to any libraries. +The Windows build should compile cleanly on all popular compilers without the need to configure any +include paths nor link to any libraries. + +The UWP build may require linking to mmdevapi.lib if you get errors about an unresolved external +symbol for `ActivateAudioInterfaceAsync()`. + 2.2. macOS and iOS ------------------ -The macOS build should compile cleanly without the need to download any dependencies nor link to any libraries or frameworks. The iOS build needs to be -compiled as Objective-C and will need to link the relevant frameworks but should compile cleanly out of the box with Xcode. Compiling through the command line -requires linking to `-lpthread` and `-lm`. +The macOS build should compile cleanly without the need to download any dependencies nor link to +any libraries or frameworks. The iOS build needs to be compiled as Objective-C and will need to +link the relevant frameworks but should compile cleanly out of the box with Xcode. Compiling +through the command line requires linking to `-lpthread` and `-lm`. -Due to the way miniaudio links to frameworks at runtime, your application may not pass Apple's notarization process. To fix this there are two options. The -first is to use the `MA_NO_RUNTIME_LINKING` option, like so: +Due to the way miniaudio links to frameworks at runtime, your application may not pass Apple's +notarization process. To fix this there are two options. The first is to use the +`MA_NO_RUNTIME_LINKING` option, like so: ```c #ifdef __APPLE__ @@ -246,8 +491,9 @@ first is to use the `MA_NO_RUNTIME_LINKING` option, like so: #include "miniaudio.h" ``` -This will require linking with `-framework CoreFoundation -framework CoreAudio -framework AudioUnit`. Alternatively, if you would rather keep using runtime -linking you can add the following to your entitlements.xcent file: +This will require linking with `-framework CoreFoundation -framework CoreAudio -framework AudioUnit`. +Alternatively, if you would rather keep using runtime linking you can add the following to your +entitlements.xcent file: ``` com.apple.security.cs.allow-dyld-environment-variables @@ -256,26 +502,37 @@ linking you can add the following to your entitlements.xcent file: ``` +See this discussion for more info: https://github.com/mackron/miniaudio/issues/203. + 2.3. Linux ---------- -The Linux build only requires linking to `-ldl`, `-lpthread` and `-lm`. You do not need any development packages. +The Linux build only requires linking to `-ldl`, `-lpthread` and `-lm`. You do not need any +development packages. You may need to link with `-latomic` if you're compiling for 32-bit ARM. + 2.4. BSD -------- -The BSD build only requires linking to `-lpthread` and `-lm`. NetBSD uses audio(4), OpenBSD uses sndio and FreeBSD uses OSS. +The BSD build only requires linking to `-lpthread` and `-lm`. NetBSD uses audio(4), OpenBSD uses +sndio and FreeBSD uses OSS. You may need to link with `-latomic` if you're compiling for 32-bit +ARM. + 2.5. Android ------------ -AAudio is the highest priority backend on Android. This should work out of the box without needing any kind of compiler configuration. Support for AAudio -starts with Android 8 which means older versions will fall back to OpenSL|ES which requires API level 16+. +AAudio is the highest priority backend on Android. This should work out of the box without needing +any kind of compiler configuration. Support for AAudio starts with Android 8 which means older +versions will fall back to OpenSL|ES which requires API level 16+. + +There have been reports that the OpenSL|ES backend fails to initialize on some Android based +devices due to `dlopen()` failing to open "libOpenSLES.so". If this happens on your platform +you'll need to disable run-time linking with `MA_NO_RUNTIME_LINKING` and link with -lOpenSLES. -There have been reports that the OpenSL|ES backend fails to initialize on some Android based devices due to `dlopen()` failing to open "libOpenSLES.so". If -this happens on your platform you'll need to disable run-time linking with `MA_NO_RUNTIME_LINKING` and link with -lOpenSLES. 2.6. Emscripten --------------- -The Emscripten build emits Web Audio JavaScript directly and should compile cleanly out of the box. You cannot use -std=c* compiler flags, nor -ansi. +The Emscripten build emits Web Audio JavaScript directly and should compile cleanly out of the box. +You cannot use `-std=c*` compiler flags, nor `-ansi`. 2.7. Build Options @@ -368,28 +625,26 @@ The Emscripten build emits Web Audio JavaScript directly and should compile clea +----------------------------------+--------------------------------------------------------------------+ | MA_NO_MP3 | Disables the built-in MP3 decoder. | +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_DEVICE_IO | Disables playback and recording. This will disable ma_context and | - | | ma_device APIs. This is useful if you only want to use miniaudio's | - | | data conversion and/or decoding APIs. | + | MA_NO_DEVICE_IO | Disables playback and recording. This will disable `ma_context` | + | | and `ma_device` APIs. This is useful if you only want to use | + | | miniaudio's data conversion and/or decoding APIs. | +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_THREADING | Disables the ma_thread, ma_mutex, ma_semaphore and ma_event APIs. | - | | This option is useful if you only need to use miniaudio for data | - | | conversion, decoding and/or encoding. Some families of APIs | - | | require threading which means the following options must also be | - | | set: | + | MA_NO_THREADING | Disables the `ma_thread`, `ma_mutex`, `ma_semaphore` and | + | | `ma_event` APIs. This option is useful if you only need to use | + | | miniaudio for data conversion, decoding and/or encoding. Some | + | | families of APIsrequire threading which means the following | + | | options must also be set: | | | | | | ``` | | | MA_NO_DEVICE_IO | | | ``` | +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_GENERATION | Disables generation APIs such a ma_waveform and ma_noise. | + | MA_NO_GENERATION | Disables generation APIs such a `ma_waveform` and `ma_noise`. | +----------------------------------+--------------------------------------------------------------------+ | MA_NO_SSE2 | Disables SSE2 optimizations. | +----------------------------------+--------------------------------------------------------------------+ | MA_NO_AVX2 | Disables AVX2 optimizations. | +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_AVX512 | Disables AVX-512 optimizations. | - +----------------------------------+--------------------------------------------------------------------+ | MA_NO_NEON | Disables NEON optimizations. | +----------------------------------+--------------------------------------------------------------------+ | MA_NO_RUNTIME_LINKING | Disables runtime linking. This is useful for passing Apple's | @@ -401,47 +656,47 @@ The Emscripten build emits Web Audio JavaScript directly and should compile clea | | You may need to enable this if your target platform does not allow | | | runtime linking via `dlopen()`. | +----------------------------------+--------------------------------------------------------------------+ - | MA_DEBUG_OUTPUT | Enable processing of MA_LOG_LEVEL_DEBUG messages and `printf()` | - | | output. | + | MA_DEBUG_OUTPUT | Enable `printf()` output of debug logs (`MA_LOG_LEVEL_DEBUG`). | +----------------------------------+--------------------------------------------------------------------+ | MA_COINIT_VALUE | Windows only. The value to pass to internal calls to | | | `CoInitializeEx()`. Defaults to `COINIT_MULTITHREADED`. | +----------------------------------+--------------------------------------------------------------------+ | MA_API | Controls how public APIs should be decorated. Default is `extern`. | +----------------------------------+--------------------------------------------------------------------+ - | MA_DLL | If set, configures MA_API to either import or export APIs | - | | depending on whether or not the implementation is being defined. | - | | If defining the implementation, MA_API will be configured to | - | | export. Otherwise it will be configured to import. This has no | - | | effect if MA_API is defined externally. | - +----------------------------------+--------------------------------------------------------------------+ 3. Definitions ============== -This section defines common terms used throughout miniaudio. Unfortunately there is often ambiguity in the use of terms throughout the audio space, so this -section is intended to clarify how miniaudio uses each term. +This section defines common terms used throughout miniaudio. Unfortunately there is often ambiguity +in the use of terms throughout the audio space, so this section is intended to clarify how miniaudio +uses each term. 3.1. Sample ----------- -A sample is a single unit of audio data. If the sample format is f32, then one sample is one 32-bit floating point number. +A sample is a single unit of audio data. If the sample format is f32, then one sample is one 32-bit +floating point number. 3.2. Frame / PCM Frame ---------------------- -A frame is a group of samples equal to the number of channels. For a stereo stream a frame is 2 samples, a mono frame is 1 sample, a 5.1 surround sound frame -is 6 samples, etc. The terms "frame" and "PCM frame" are the same thing in miniaudio. Note that this is different to a compressed frame. If ever miniaudio -needs to refer to a compressed frame, such as a FLAC frame, it will always clarify what it's referring to with something like "FLAC frame". +A frame is a group of samples equal to the number of channels. For a stereo stream a frame is 2 +samples, a mono frame is 1 sample, a 5.1 surround sound frame is 6 samples, etc. The terms "frame" +and "PCM frame" are the same thing in miniaudio. Note that this is different to a compressed frame. +If ever miniaudio needs to refer to a compressed frame, such as a FLAC frame, it will always +clarify what it's referring to with something like "FLAC frame". 3.3. Channel ------------ -A stream of monaural audio that is emitted from an individual speaker in a speaker system, or received from an individual microphone in a microphone system. A -stereo stream has two channels (a left channel, and a right channel), a 5.1 surround sound system has 6 channels, etc. Some audio systems refer to a channel as -a complex audio stream that's mixed with other channels to produce the final mix - this is completely different to miniaudio's use of the term "channel" and -should not be confused. +A stream of monaural audio that is emitted from an individual speaker in a speaker system, or +received from an individual microphone in a microphone system. A stereo stream has two channels (a +left channel, and a right channel), a 5.1 surround sound system has 6 channels, etc. Some audio +systems refer to a channel as a complex audio stream that's mixed with other channels to produce +the final mix - this is completely different to miniaudio's use of the term "channel" and should +not be confused. 3.4. Sample Rate ---------------- -The sample rate in miniaudio is always expressed in Hz, such as 44100, 48000, etc. It's the number of PCM frames that are processed per second. +The sample rate in miniaudio is always expressed in Hz, such as 44100, 48000, etc. It's the number +of PCM frames that are processed per second. 3.5. Formats ------------ @@ -461,10 +716,1685 @@ All formats are native-endian. -4. Decoding +4. Data Sources +=============== +The data source abstraction in miniaudio is used for retrieving audio data from some source. A few +examples include `ma_decoder`, `ma_noise` and `ma_waveform`. You will need to be familiar with data +sources in order to make sense of some of the higher level concepts in miniaudio. + +The `ma_data_source` API is a generic interface for reading from a data source. Any object that +implements the data source interface can be plugged into any `ma_data_source` function. + +To read data from a data source: + + ```c + ma_result result; + ma_uint64 framesRead; + + result = ma_data_source_read_pcm_frames(pDataSource, pFramesOut, frameCount, &framesRead, loop); + if (result != MA_SUCCESS) { + return result; // Failed to read data from the data source. + } + ``` + +If you don't need the number of frames that were successfully read you can pass in `NULL` to the +`pFramesRead` parameter. If this returns a value less than the number of frames requested it means +the end of the file has been reached. `MA_AT_END` will be returned only when the number of frames +read is 0. + +When calling any data source function, with the exception of `ma_data_source_init()` and +`ma_data_source_uninit()`, you can pass in any object that implements a data source. For example, +you could plug in a decoder like so: + + ```c + ma_result result; + ma_uint64 framesRead; + ma_decoder decoder; // <-- This would be initialized with `ma_decoder_init_*()`. + + result = ma_data_source_read_pcm_frames(&decoder, pFramesOut, frameCount, &framesRead, loop); + if (result != MA_SUCCESS) { + return result; // Failed to read data from the decoder. + } + ``` + +If you want to seek forward you can pass in `NULL` to the `pFramesOut` parameter. Alternatively you +can use `ma_data_source_seek_pcm_frames()`. + +To seek to a specific PCM frame: + + ```c + result = ma_data_source_seek_to_pcm_frame(pDataSource, frameIndex); + if (result != MA_SUCCESS) { + return result; // Failed to seek to PCM frame. + } + ``` + +You can retrieve the total length of a data source in PCM frames, but note that some data sources +may not have the notion of a length, such as noise and waveforms, and others may just not have a +way of determining the length such as some decoders. To retrieve the length: + + ```c + ma_uint64 length; + + result = ma_data_source_get_length_in_pcm_frames(pDataSource, &length); + if (result != MA_SUCCESS) { + return result; // Failed to retrieve the length. + } + ``` + +Care should be taken when retrieving the length of a data source where the underlying decoder is +pulling data from a data stream with an undefined length, such as internet radio or some kind of +broadcast. If you do this, `ma_data_source_get_length_in_pcm_frames()` may never return. + +The current position of the cursor in PCM frames can also be retrieved: + + ```c + ma_uint64 cursor; + + result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &cursor); + if (result != MA_SUCCESS) { + return result; // Failed to retrieve the cursor. + } + ``` + +You will often need to know the data format that will be returned after reading. This can be +retrieved like so: + + ```c + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_channel channelMap[MA_MAX_CHANNELS]; + + result = ma_data_source_get_data_format(pDataSource, &format, &channels, &sampleRate, channelMap, MA_MAX_CHANNELS); + if (result != MA_SUCCESS) { + return result; // Failed to retrieve data format. + } + ``` + +If you do not need a specific data format property, just pass in NULL to the respective parameter. + +There may be cases where you want to implement something like a sound bank where you only want to +read data within a certain range of the underlying data. To do this you can use a range: + + ```c + result = ma_data_source_set_range_in_pcm_frames(pDataSource, rangeBegInFrames, rangeEndInFrames); + if (result != MA_SUCCESS) { + return result; // Failed to set the range. + } + ``` + +This is useful if you have a sound bank where many sounds are stored in the same file and you want +the data source to only play one of those sub-sounds. + +Custom loop points can also be used with data sources. By default, data sources will loop after +they reach the end of the data source, but if you need to loop at a specific location, you can do +the following: + + ```c + result = ma_data_set_loop_point_in_pcm_frames(pDataSource, loopBegInFrames, loopEndInFrames); + if (result != MA_SUCCESS) { + return result; // Failed to set the loop point. + } + ``` + +The loop point is relative to the current range. + +It's sometimes useful to chain data sources together so that a seamless transition can be achieved. +To do this, you can use chaining: + + ```c + ma_decoder decoder1; + ma_decoder decoder2; + + // ... initialize decoders with ma_decoder_init_*() ... + + result = ma_data_source_set_next(&decoder1, &decoder2); + if (result != MA_SUCCESS) { + return result; // Failed to set the next data source. + } + + result = ma_data_source_read_pcm_frames(&decoder1, pFramesOut, frameCount, pFramesRead, MA_FALSE); + if (result != MA_SUCCESS) { + return result; // Failed to read from the decoder. + } + ``` + +In the example above we're using decoders. When reading from a chain, you always want to read from +the top level data source in the chain. In the example above, `decoder1` is the top level data +source in the chain. When `decoder1` reaches the end, `decoder2` will start seamlessly without any +gaps. + +Note that the `loop` parameter is set to false in the example above. When this is set to true, only +the current data source will be looped. You can loop the entire chain by linking in a loop like so: + + ```c + ma_data_source_set_next(&decoder1, &decoder2); // decoder1 -> decoder2 + ma_data_source_set_next(&decoder2, &decoder1); // decoder2 -> decoder1 (loop back to the start). + ``` + +Note that setting up chaining is not thread safe, so care needs to be taken if you're dynamically +changing links while the audio thread is in the middle of reading. + +Do not use `ma_decoder_seek_to_pcm_frame()` as a means to reuse a data source to play multiple +instances of the same sound simultaneously. Instead, initialize multiple data sources for each +instance. This can be extremely inefficient depending on the data source and can result in +glitching due to subtle changes to the state of internal filters. + + +4.1. Custom Data Sources +------------------------ +You can implement a custom data source by implementing the functions in `ma_data_source_vtable`. +Your custom object must have `ma_data_source_base` as it's first member: + + ```c + struct my_data_source + { + ma_data_source_base base; + ... + }; + ``` + +In your initialization routine, you need to call `ma_data_source_init()` in order to set up the +base object (`ma_data_source_base`): + + ```c + static ma_result my_data_source_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) + { + // Read data here. Output in the same format returned by my_data_source_get_data_format(). + } + + static ma_result my_data_source_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) + { + // Seek to a specific PCM frame here. Return MA_NOT_IMPLEMENTED if seeking is not supported. + } + + static ma_result my_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) + { + // Return the format of the data here. + } + + static ma_result my_data_source_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) + { + // Retrieve the current position of the cursor here. Return MA_NOT_IMPLEMENTED and set *pCursor to 0 if there is no notion of a cursor. + } + + static ma_result my_data_source_get_length(ma_data_source* pDataSource, ma_uint64* pLength) + { + // Retrieve the length in PCM frames here. Return MA_NOT_IMPLEMENTED and set *pLength to 0 if there is no notion of a length or if the length is unknown. + } + + static g_my_data_source_vtable = + { + my_data_source_read, + my_data_source_seek, + my_data_source_get_data_format, + my_data_source_get_cursor, + my_data_source_get_length + }; + + ma_result my_data_source_init(my_data_source* pMyDataSource) + { + ma_result result; + ma_data_source_config baseConfig; + + baseConfig = ma_data_source_config_init(); + baseConfig.vtable = &g_my_data_source_vtable; + + result = ma_data_source_init(&baseConfig, &pMyDataSource->base); + if (result != MA_SUCCESS) { + return result; + } + + // ... do the initialization of your custom data source here ... + + return MA_SUCCESS; + } + + void my_data_source_uninit(my_data_source* pMyDataSource) + { + // ... do the uninitialization of your custom data source here ... + + // You must uninitialize the base data source. + ma_data_source_uninit(&pMyDataSource->base); + } + ``` + +Note that `ma_data_source_init()` and `ma_data_source_uninit()` are never called directly outside +of the custom data source. It's up to the custom data source itself to call these within their own +init/uninit functions. + + + +5. Engine +========= +The `ma_engine` API is a high level API for managing and mixing sounds and effect processing. The +`ma_engine` object encapsulates a resource manager and a node graph, both of which will be +explained in more detail later. + +Sounds are called `ma_sound` and are created from an engine. Sounds can be associated with a mixing +group called `ma_sound_group` which are also created from the engine. Both `ma_sound` and +`ma_sound_group` objects are nodes within the engine's node graph. + +When the engine is initialized, it will normally create a device internally. If you would rather +manage the device yourself, you can do so and just pass a pointer to it via the engine config when +you initialize the engine. You can also just use the engine without a device, which again can be +configured via the engine config. + +The most basic way to initialize the engine is with a default config, like so: + + ```c + ma_result result; + ma_engine engine; + + result = ma_engine_init(NULL, &engine); + if (result != MA_SUCCESS) { + return result; // Failed to initialize the engine. + } + ``` + +This will result in the engine initializing a playback device using the operating system's default +device. This will be sufficient for many use cases, but if you need more flexibility you'll want to +configure the engine with an engine config: + + ```c + ma_result result; + ma_engine engine; + ma_engine_config engineConfig; + + engineConfig = ma_engine_config_init(); + engineConfig.pPlaybackDevice = &myDevice; + + result = ma_engine_init(&engineConfig, &engine); + if (result != MA_SUCCESS) { + return result; // Failed to initialize the engine. + } + ``` + +In the example above we're passing in a pre-initialized device. Since the caller is the one in +control of the device's data callback, it's their responsibility to manually call +`ma_engine_read_pcm_frames()` from inside their data callback: + + ```c + void playback_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) + { + ma_engine_read_pcm_frames(&g_Engine, pOutput, frameCount, NULL); + } + ``` + +You can also use the engine independent of a device entirely: + + ```c + ma_result result; + ma_engine engine; + ma_engine_config engineConfig; + + engineConfig = ma_engine_config_init(); + engineConfig.noDevice = MA_TRUE; + engineConfig.channels = 2; // Must be set when not using a device. + engineConfig.sampleRate = 48000; // Must be set when not using a device. + + result = ma_engine_init(&engineConfig, &engine); + if (result != MA_SUCCESS) { + return result; // Failed to initialize the engine. + } + ``` + +Note that when you're not using a device, you must set the channel count and sample rate in the +config or else miniaudio won't know what to use (miniaudio will use the device to determine this +normally). When not using a device, you need to use `ma_engine_read_pcm_frames()` to process audio +data from the engine. This kind of setup is useful if you want to do something like offline +processing. + +When a sound is loaded it goes through a resource manager. By default the engine will initialize a +resource manager internally, but you can also specify a pre-initialized resource manager: + + ```c + ma_result result; + ma_engine engine1; + ma_engine engine2; + ma_engine_config engineConfig; + + engineConfig = ma_engine_config_init(); + engineConfig.pResourceManager = &myResourceManager; + + ma_engine_init(&engineConfig, &engine1); + ma_engine_init(&engineConfig, &engine2); + ``` + +In this example we are initializing two engines, both of which are sharing the same resource +manager. This is especially useful for saving memory when loading the same file across multiple +engines. If you were not to use a shared resource manager, each engine instance would use their own +which would result in any sounds that are used between both engine's being loaded twice. By using +a shared resource manager, it would only be loaded once. Using multiple engine's is useful when you +need to output to multiple playback devices, such as in a local multiplayer game where each player +is using their own set of headphones. + +By default an engine will be in a started state. To make it so the engine is not automatically +started you can configure it as such: + + ```c + engineConfig.noAutoStart = MA_TRUE; + + // The engine will need to be started manually. + ma_engine_start(&engine); + + // Later on the engine can be stopped with ma_engine_stop(). + ma_engine_stop(&engine); + ``` + +The concept of starting or stopping an engine is only relevant when using the engine with a +device. Attempting to start or stop an engine that is not associated with a device will result in +`MA_INVALID_OPERATION`. + +The master volume of the engine can be controlled with `ma_engine_set_volume()` which takes a +linear scale, with 0 resulting in silence and anything above 1 resulting in amplification. If you +prefer decibel based volume control, use `ma_volume_db_to_linear()` to convert from dB to linear. + +When a sound is spatialized, it is done so relative to a listener. An engine can be configured to +have multiple listeners which can be configured via the config: + + ```c + engineConfig.listenerCount = 2; + ``` + +The maximum number of listeners is restricted to `MA_ENGINE_MAX_LISTENERS`. By default, when a +sound is spatialized, it will be done so relative to the closest listener. You can also pin a sound +to a specific listener which will be explained later. Listener's have a position, direction, cone, +and velocity (for doppler effect). A listener is referenced by an index, the meaning of which is up +to the caller (the index is 0 based and cannot go beyond the listener count, minus 1). The +position, direction and velocity are all specified in absolute terms: + + ```c + ma_engine_listener_set_position(&engine, listenerIndex, worldPosX, worldPosY, worldPosZ); + ``` + +The direction of the listener represents it's forward vector. The listener's up vector can also be +specified and defaults to +1 on the Y axis. + + ```c + ma_engine_listener_set_direction(&engine, listenerIndex, forwardX, forwardY, forwardZ); + ma_engine_listener_set_world_up(&engine, listenerIndex, 0, 1, 0); + ``` + +The engine supports directional attenuation. The listener can have a cone the controls how sound is +attenuated based on the listener's direction. When a sound is between the inner and outer cones, it +will be attenuated between 1 and the cone's outer gain: + + ```c + ma_engine_listener_set_cone(&engine, listenerIndex, innerAngleInRadians, outerAngleInRadians, outerGain); + ``` + +When a sound is inside the inner code, no directional attenuation is applied. When the sound is +outside of the outer cone, the attenuation will be set to `outerGain` in the example above. When +the sound is in between the inner and outer cones, the attenuation will be interpolated between 1 +and the outer gain. + +The engine's coordinate system follows the OpenGL coordinate system where positive X points right, +positive Y points up and negative Z points forward. + +The simplest and least flexible way to play a sound is like so: + + ```c + ma_engine_play_sound(&engine, "my_sound.wav", pGroup); + ``` + +This is a "fire and forget" style of function. The engine will manage the `ma_sound` object +internally. When the sound finishes playing, it'll be put up for recycling. For more flexibility +you'll want to initialize a sound object: + + ```c + ma_sound sound; + + result = ma_sound_init_from_file(&engine, "my_sound.wav", flags, pGroup, NULL, &sound); + if (result != MA_SUCCESS) { + return result; // Failed to load sound. + } + ``` + +Sounds need to be uninitialized with `ma_sound_uninit()`. + +The example above loads a sound from a file. If the resource manager has been disabled you will not +be able to use this function and instead you'll need to initialize a sound directly from a data +source: + + ```c + ma_sound sound; + + result = ma_sound_init_from_data_source(&engine, &dataSource, flags, pGroup, &sound); + if (result != MA_SUCCESS) { + return result; + } + ``` + +Each `ma_sound` object represents a single instance of the sound. If you want to play the same +sound multiple times at the same time, you need to initialize a separate `ma_sound` object. + +For the most flexibility when initializing sounds, use `ma_sound_init_ex()`. This uses miniaudio's +standard config/init pattern: + + ```c + ma_sound sound; + ma_sound_config soundConfig; + + soundConfig = ma_sound_config_init(); + soundConfig.pFilePath = NULL; // Set this to load from a file path. + soundConfig.pDataSource = NULL; // Set this to initialize from an existing data source. + soundConfig.pInitialAttachment = &someNodeInTheNodeGraph; + soundConfig.initialAttachmentInputBusIndex = 0; + soundConfig.channelsIn = 1; + soundConfig.channelsOut = 0; // Set to 0 to use the engine's native channel count. + + result = ma_sound_init_ex(&soundConfig, &sound); + if (result != MA_SUCCESS) { + return result; + } + ``` + +In the example above, the sound is being initialized without a file nor a data source. This is +valid, in which case the sound acts as a node in the middle of the node graph. This means you can +connect other sounds to this sound and allow it to act like a sound group. Indeed, this is exactly +what a `ma_sound_group` is. + +When loading a sound, you specify a set of flags that control how the sound is loaded and what +features are enabled for that sound. When no flags are set, the sound will be fully loaded into +memory in exactly the same format as how it's stored on the file system. The resource manager will +allocate a block of memory and then load the file directly into it. When reading audio data, it +will be decoded dynamically on the fly. In order to save processing time on the audio thread, it +might be beneficial to pre-decode the sound. You can do this with the `MA_SOUND_FLAG_DECODE` flag: + + ```c + ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_DECODE, pGroup, NULL, &sound); + ``` + +By default, sounds will be loaded synchronously, meaning `ma_sound_init_*()` will not return until +the sound has been fully loaded. If this is prohibitive you can instead load sounds asynchronously +by specificying the `MA_SOUND_FLAG_ASYNC` flag: + + ```c + ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, pGroup, NULL, &sound); + ``` + +This will result in `ma_sound_init_*()` returning quickly, but the sound won't yet have been fully +loaded. When you start the sound, it won't output anything until some sound is available. The sound +will start outputting audio before the sound has been fully decoded when the `MA_SOUND_FLAG_DECODE` +is specified. + +If you need to wait for an asynchronously loaded sound to be fully loaded, you can use a fence. A +fence in miniaudio is a simple synchronization mechanism which simply blocks until it's internal +counter hit's zero. You can specify a fence like so: + + ```c + ma_result result; + ma_fence fence; + ma_sound sounds[4]; + + result = ma_fence_init(&fence); + if (result != MA_SUCCES) { + return result; + } + + // Load some sounds asynchronously. + for (int iSound = 0; iSound < 4; iSound += 1) { + ma_sound_init_from_file(&engine, mySoundFilesPaths[iSound], MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, pGroup, &fence, &sounds[iSound]); + } + + // ... do some other stuff here in the mean time ... + + // Wait for all sounds to finish loading. + ma_fence_wait(&fence); + ``` + +If loading the entire sound into memory is prohibitive, you can also configure the engine to stream +the audio data: + + ```c + ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_STREAM, pGroup, NULL, &sound); + ``` + +When streaming sounds, 2 seconds worth of audio data is stored in memory. Although it should work +fine, it's inefficient to use streaming for short sounds. Streaming is useful for things like music +tracks in games. + +When you initialize a sound, if you specify a sound group the sound will be attached to that group +automatically. If you set it to NULL, it will be automatically attached to the engine's endpoint. +If you would instead rather leave the sound unattached by default, you can can specify the +`MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT` flag. This is useful if you want to set up a complex node +graph. + +Sounds are not started by default. To start a sound, use `ma_sound_start()`. Stop a sound with +`ma_sound_stop()`. + +Sounds can have their volume controlled with `ma_sound_set_volume()` in the same way as the +engine's master volume. + +Sounds support stereo panning and pitching. Set the pan with `ma_sound_set_pan()`. Setting the pan +to 0 will result in an unpanned sound. Setting it to -1 will shift everything to the left, whereas ++1 will shift it to the right. The pitch can be controlled with `ma_sound_set_pitch()`. A larger +value will result in a higher pitch. The pitch must be greater than 0. + +The engine supports 3D spatialization of sounds. By default sounds will have spatialization +enabled, but if a sound does not need to be spatialized it's best to disable it. There are two ways +to disable spatialization of a sound: + + ```c + // Disable spatialization at initialization time via a flag: + ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_NO_SPATIALIZATION, NULL, NULL, &sound); + + // Dynamically disable or enable spatialization post-initialization: + ma_sound_set_spatialization_enabled(&sound, isSpatializationEnabled); + ``` + +By default sounds will be spatialized based on the closest listener. If a sound should always be +spatialized relative to a specific listener it can be pinned to one: + + ```c + ma_sound_set_pinned_listener_index(&sound, listenerIndex); + ``` + +Like listeners, sounds have a position. By default, the position of a sound is in absolute space, +but it can be changed to be relative to a listener: + + ```c + ma_sound_set_positioning(&sound, ma_positioning_relative); + ``` + +Note that relative positioning of a sound only makes sense if there is either only one listener, or +the sound is pinned to a specific listener. To set the position of a sound: + + ```c + ma_sound_set_position(&sound, posX, posY, posZ); + ``` + +The direction works the same way as a listener and represents the sound's forward direction: + + ```c + ma_sound_set_direction(&sound, forwardX, forwardY, forwardZ); + ``` + +Sound's also have a cone for controlling directional attenuation. This works exactly the same as +listeners: + + ```c + ma_sound_set_cone(&sound, innerAngleInRadians, outerAngleInRadians, outerGain); + ``` + +The velocity of a sound is used for doppler effect and can be set as such: + + ```c + ma_sound_set_velocity(&sound, velocityX, velocityY, velocityZ); + ``` + +The engine supports different attenuation models which can be configured on a per-sound basis. By +default the attenuation model is set to `ma_attenuation_model_inverse` which is the equivalent to +OpenAL's `AL_INVERSE_DISTANCE_CLAMPED`. Configure the attenuation model like so: + + ```c + ma_sound_set_attenuation_model(&sound, ma_attenuation_model_inverse); + ``` + +The supported attenuation models include the following: + + +----------------------------------+----------------------------------------------+ + | ma_attenuation_model_none | No distance attenuation. | + +----------------------------------+----------------------------------------------+ + | ma_attenuation_model_inverse | Equivalent to `AL_INVERSE_DISTANCE_CLAMPED`. | + +----------------------------------+----------------------------------------------+ + | ma_attenuation_model_linear | Linear attenuation. | + +----------------------------------+----------------------------------------------+ + | ma_attenuation_model_exponential | Exponential attenuation. | + +----------------------------------+----------------------------------------------+ + +To control how quickly a sound rolls off as it moves away from the listener, you need to configure +the rolloff: + + ```c + ma_sound_set_rolloff(&sound, rolloff); + ``` + +You can control the minimum and maximum gain to apply from spatialization: + + ```c + ma_sound_set_min_gain(&sound, minGain); + ma_sound_set_max_gain(&sound, maxGain); + ``` + +Likewise, in the calculation of attenuation, you can control the minimum and maximum distances for +the attenuation calculation. This is useful if you want to ensure sounds don't drop below a certain +volume after the listener moves further away and to have sounds play a maximum volume when the +listener is within a certain distance: + + ```c + ma_sound_set_min_distance(&sound, minDistance); + ma_sound_set_max_distance(&sound, maxDistance); + ``` + +The engine's spatialization system supports doppler effect. The doppler factor can be configure on +a per-sound basis like so: + + ```c + ma_sound_set_doppler_factor(&sound, dopplerFactor); + ``` + +You can fade sounds in and out with `ma_sound_set_fade_in_pcm_frames()` and +`ma_sound_set_fade_in_milliseconds()`. Set the volume to -1 to use the current volume as the +starting volume: + + ```c + // Fade in over 1 second. + ma_sound_set_fade_in_milliseconds(&sound, 0, 1, 1000); + + // ... sometime later ... + + // Fade out over 1 second, starting from the current volume. + ma_sound_set_fade_in_milliseconds(&sound, -1, 0, 1000); + ``` + +By default sounds will start immediately, but sometimes for timing and synchronization purposes it +can be useful to schedule a sound to start or stop: + + ```c + // Start the sound in 1 second from now. + ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time(&engine) + (ma_engine_get_sample_rate(&engine) * 1)); + + // Stop the sound in 2 seconds from now. + ma_sound_set_stop_time_in_pcm_frames(&sound, ma_engine_get_time(&engine) + (ma_engine_get_sample_rate(&engine) * 2)); + ``` + +Note that scheduling a start time still requires an explicit call to `ma_sound_start()` before +anything will play. + +The time is specified in global time which is controlled by the engine. You can get the engine's +current time with `ma_engine_get_time()`. The engine's global time is incremented automatically as +audio data is read, but it can be reset with `ma_engine_set_time()` in case it needs to be +resynchronized for some reason. + +To determine whether or not a sound is currently playing, use `ma_sound_is_playing()`. This will +take the scheduled start and stop times into account. + +Whether or not a sound should loop can be controlled with `ma_sound_set_looping()`. Sounds will not +be looping by default. Use `ma_sound_is_looping()` to determine whether or not a sound is looping. + +Use `ma_sound_at_end()` to determine whether or not a sound is currently at the end. For a looping +sound this should never return true. + +Internally a sound wraps around a data source. Some APIs exist to control the underlying data +source, mainly for convenience: + + ```c + ma_sound_seek_to_pcm_frame(&sound, frameIndex); + ma_sound_get_data_format(&sound, &format, &channels, &sampleRate, pChannelMap, channelMapCapacity); + ma_sound_get_cursor_in_pcm_frames(&sound, &cursor); + ma_sound_get_length_in_pcm_frames(&sound, &length); + ``` + +Sound groups have the same API as sounds, only they are called `ma_sound_group`, and since they do +not have any notion of a data source, anything relating to a data source is unavailable. + +Internally, sound data is loaded via the `ma_decoder` API which means by default in only supports +file formats that have built-in support in miniaudio. You can extend this to support any kind of +file format through the use of custom decoders. To do this you'll need to use a self-managed +resource manager and configure it appropriately. See the "Resource Management" section below for +details on how to set this up. + + +6. Resource Management +====================== +Many programs will want to manage sound resources for things such as reference counting and +streaming. This is supported by miniaudio via the `ma_resource_manager` API. + +The resource manager is mainly responsible for the following: + + * Loading of sound files into memory with reference counting. + * Streaming of sound data + +When loading a sound file, the resource manager will give you back a `ma_data_source` compatible +object called `ma_resource_manager_data_source`. This object can be passed into any +`ma_data_source` API which is how you can read and seek audio data. When loading a sound file, you +specify whether or not you want the sound to be fully loaded into memory (and optionally +pre-decoded) or streamed. When loading into memory, you can also specify whether or not you want +the data to be loaded asynchronously. + +The example below is how you can initialize a resource manager using it's default configuration: + + ```c + ma_resource_manager_config config; + ma_resource_manager resourceManager; + + config = ma_resource_manager_config_init(); + result = ma_resource_manager_init(&config, &resourceManager); + if (result != MA_SUCCESS) { + ma_device_uninit(&device); + printf("Failed to initialize the resource manager."); + return -1; + } + ``` + +You can configure the format, channels and sample rate of the decoded audio data. By default it +will use the file's native data format, but you can configure it to use a consistent format. This +is useful for offloading the cost of data conversion to load time rather than dynamically +converting at mixing time. To do this, you configure the decoded format, channels and sample rate +like the code below: + + ```c + config = ma_resource_manager_config_init(); + config.decodedFormat = device.playback.format; + config.decodedChannels = device.playback.channels; + config.decodedSampleRate = device.sampleRate; + ``` + +In the code above, the resource manager will be configured so that any decoded audio data will be +pre-converted at load time to the device's native data format. If instead you used defaults and +the data format of the file did not match the device's data format, you would need to convert the +data at mixing time which may be prohibitive in high-performance and large scale scenarios like +games. + +Internally the resource manager uses the `ma_decoder` API to load sounds. This means by default it +only supports decoders that are built into miniaudio. It's possible to support additional encoding +formats through the use of custom decoders. To do so, pass in your `ma_decoding_backend_vtable` +vtables into the resource manager config: + + ```c + ma_decoding_backend_vtable* pCustomBackendVTables[] = + { + &g_ma_decoding_backend_vtable_libvorbis, + &g_ma_decoding_backend_vtable_libopus + }; + + ... + + resourceManagerConfig.ppCustomDecodingBackendVTables = pCustomBackendVTables; + resourceManagerConfig.customDecodingBackendCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]); + resourceManagerConfig.pCustomDecodingBackendUserData = NULL; + ``` + +This system can allow you to support any kind of file format. See the "Decoding" section for +details on how to implement custom decoders. The miniaudio repository includes examples for Opus +via libopus and libopusfile and Vorbis via libvorbis and libvorbisfile. + +Asynchronicity is achieved via a job system. When an operation needs to be performed, such as the +decoding of a page, a job will be posted to a queue which will then be processed by a job thread. +By default there will be only one job thread running, but this can be configured, like so: + + ```c + config = ma_resource_manager_config_init(); + config.jobThreadCount = MY_JOB_THREAD_COUNT; + ``` + +By default job threads are managed internally by the resource manager, however you can also self +manage your job threads if, for example, you want to integrate the job processing into your +existing job infrastructure, or if you simply don't like the way the resource manager does it. To +do this, just set the job thread count to 0 and process jobs manually. To process jobs, you first +need to retrieve a job using `ma_resource_manager_next_job()` and then process it using +`ma_job_process()`: + + ```c + config = ma_resource_manager_config_init(); + config.jobThreadCount = 0; // Don't manage any job threads internally. + config.flags = MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING; // Optional. Makes `ma_resource_manager_next_job()` non-blocking. + + // ... Initialize your custom job threads ... + + void my_custom_job_thread(...) + { + for (;;) { + ma_job job; + ma_result result = ma_resource_manager_next_job(pMyResourceManager, &job); + if (result != MA_SUCCESS) { + if (result == MA_NOT_DATA_AVAILABLE) { + // No jobs are available. Keep going. Will only get this if the resource manager was initialized + // with MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING. + continue; + } else if (result == MA_CANCELLED) { + // MA_JOB_TYPE_QUIT was posted. Exit. + break; + } else { + // Some other error occurred. + break; + } + } + + ma_job_process(&job); + } + } + ``` + +In the example above, the `MA_JOB_TYPE_QUIT` event is the used as the termination +indicator, but you can use whatever you would like to terminate the thread. The call to +`ma_resource_manager_next_job()` is blocking by default, but can be configured to be non-blocking +by initializing the resource manager with the `MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING` configuration +flag. Note that the `MA_JOB_TYPE_QUIT` will never be removed from the job queue. This +is to give every thread the opportunity to catch the event and terminate naturally. + +When loading a file, it's sometimes convenient to be able to customize how files are opened and +read instead of using standard `fopen()`, `fclose()`, etc. which is what miniaudio will use by +default. This can be done by setting `pVFS` member of the resource manager's config: + + ```c + // Initialize your custom VFS object. See documentation for VFS for information on how to do this. + my_custom_vfs vfs = my_custom_vfs_init(); + + config = ma_resource_manager_config_init(); + config.pVFS = &vfs; + ``` + +This is particularly useful in programs like games where you want to read straight from an archive +rather than the normal file system. If you do not specify a custom VFS, the resource manager will +use the operating system's normal file operations. This is default. + +To load a sound file and create a data source, call `ma_resource_manager_data_source_init()`. When +loading a sound you need to specify the file path and options for how the sounds should be loaded. +By default a sound will be loaded synchronously. The returned data source is owned by the caller +which means the caller is responsible for the allocation and freeing of the data source. Below is +an example for initializing a data source: + + ```c + ma_resource_manager_data_source dataSource; + ma_result result = ma_resource_manager_data_source_init(pResourceManager, pFilePath, flags, &dataSource); + if (result != MA_SUCCESS) { + // Error. + } + + // ... + + // A ma_resource_manager_data_source object is compatible with the `ma_data_source` API. To read data, just call + // the `ma_data_source_read_pcm_frames()` like you would with any normal data source. + result = ma_data_source_read_pcm_frames(&dataSource, pDecodedData, frameCount, &framesRead); + if (result != MA_SUCCESS) { + // Failed to read PCM frames. + } + + // ... + + ma_resource_manager_data_source_uninit(pResourceManager, &dataSource); + ``` + +The `flags` parameter specifies how you want to perform loading of the sound file. It can be a +combination of the following flags: + + ``` + MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM + MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE + MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC + MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT + ``` + +When no flags are specified (set to 0), the sound will be fully loaded into memory, but not +decoded, meaning the raw file data will be stored in memory, and then dynamically decoded when +`ma_data_source_read_pcm_frames()` is called. To instead decode the audio data before storing it in +memory, use the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` flag. By default, the sound file will +be loaded synchronously, meaning `ma_resource_manager_data_source_init()` will only return after +the entire file has been loaded. This is good for simplicity, but can be prohibitively slow. You +can instead load the sound asynchronously using the `MA_RESOURCE_MANAGER_DATA_SOURCE_ASYNC` flag. +This will result in `ma_resource_manager_data_source_init()` returning quickly, but no data will be +returned by `ma_data_source_read_pcm_frames()` until some data is available. When no data is +available because the asynchronous decoding hasn't caught up, `MA_BUSY` will be returned by +`ma_data_source_read_pcm_frames()`. + +For large sounds, it's often prohibitive to store the entire file in memory. To mitigate this, you +can instead stream audio data which you can do by specifying the +`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag. When streaming, data will be decoded in 1 +second pages. When a new page needs to be decoded, a job will be posted to the job queue and then +subsequently processed in a job thread. + +For in-memory sounds, reference counting is used to ensure the data is loaded only once. This means +multiple calls to `ma_resource_manager_data_source_init()` with the same file path will result in +the file data only being loaded once. Each call to `ma_resource_manager_data_source_init()` must be +matched up with a call to `ma_resource_manager_data_source_uninit()`. Sometimes it can be useful +for a program to register self-managed raw audio data and associate it with a file path. Use the +`ma_resource_manager_register_*()` and `ma_resource_manager_unregister_*()` APIs to do this. +`ma_resource_manager_register_decoded_data()` is used to associate a pointer to raw, self-managed +decoded audio data in the specified data format with the specified name. Likewise, +`ma_resource_manager_register_encoded_data()` is used to associate a pointer to raw self-managed +encoded audio data (the raw file data) with the specified name. Note that these names need not be +actual file paths. When `ma_resource_manager_data_source_init()` is called (without the +`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag), the resource manager will look for these +explicitly registered data buffers and, if found, will use it as the backing data for the data +source. Note that the resource manager does *not* make a copy of this data so it is up to the +caller to ensure the pointer stays valid for it's lifetime. Use +`ma_resource_manager_unregister_data()` to unregister the self-managed data. You can also use +`ma_resource_manager_register_file()` and `ma_resource_manager_unregister_file()` to register and +unregister a file. It does not make sense to use the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` +flag with a self-managed data pointer. + + +6.1. Asynchronous Loading and Synchronization +--------------------------------------------- +When loading asynchronously, it can be useful to poll whether or not loading has finished. Use +`ma_resource_manager_data_source_result()` to determine this. For in-memory sounds, this will +return `MA_SUCCESS` when the file has been *entirely* decoded. If the sound is still being decoded, +`MA_BUSY` will be returned. Otherwise, some other error code will be returned if the sound failed +to load. For streaming data sources, `MA_SUCCESS` will be returned when the first page has been +decoded and the sound is ready to be played. If the first page is still being decoded, `MA_BUSY` +will be returned. Otherwise, some other error code will be returned if the sound failed to load. + +In addition to polling, you can also use a simple synchronization object called a "fence" to wait +for asynchronously loaded sounds to finish. This is called `ma_fence`. The advantage to using a +fence is that it can be used to wait for a group of sounds to finish loading rather than waiting +for sounds on an individual basis. There are two stages to loading a sound: + + * Initialization of the internal decoder; and + * Completion of decoding of the file (the file is fully decoded) + +You can specify separate fences for each of the different stages. Waiting for the initialization +of the internal decoder is important for when you need to know the sample format, channels and +sample rate of the file. + +The example below shows how you could use a fence when loading a number of sounds: + + ```c + // This fence will be released when all sounds are finished loading entirely. + ma_fence fence; + ma_fence_init(&fence); + + // This will be passed into the initialization routine for each sound. + ma_resource_manager_pipeline_notifications notifications = ma_resource_manager_pipeline_notifications_init(); + notifications.done.pFence = &fence; + + // Now load a bunch of sounds: + for (iSound = 0; iSound < soundCount; iSound += 1) { + ma_resource_manager_data_source_init(pResourceManager, pSoundFilePaths[iSound], flags, ¬ifications, &pSoundSources[iSound]); + } + + // ... DO SOMETHING ELSE WHILE SOUNDS ARE LOADING ... + + // Wait for loading of sounds to finish. + ma_fence_wait(&fence); + ``` + +In the example above we used a fence for waiting until the entire file has been fully decoded. If +you only need to wait for the initialization of the internal decoder to complete, you can use the +`init` member of the `ma_resource_manager_pipeline_notifications` object: + + ```c + notifications.init.pFence = &fence; + ``` + +If a fence is not appropriate for your situation, you can instead use a callback that is fired on +an individual sound basis. This is done in a very similar way to fences: + + ```c + typedef struct + { + ma_async_notification_callbacks cb; + void* pMyData; + } my_notification; + + void my_notification_callback(ma_async_notification* pNotification) + { + my_notification* pMyNotification = (my_notification*)pNotification; + + // Do something in response to the sound finishing loading. + } + + ... + + my_notification myCallback; + myCallback.cb.onSignal = my_notification_callback; + myCallback.pMyData = pMyData; + + ma_resource_manager_pipeline_notifications notifications = ma_resource_manager_pipeline_notifications_init(); + notifications.done.pNotification = &myCallback; + + ma_resource_manager_data_source_init(pResourceManager, "my_sound.wav", flags, ¬ifications, &mySound); + ``` + +In the example above we just extend the `ma_async_notification_callbacks` object and pass an +instantiation into the `ma_resource_manager_pipeline_notifications` in the same way as we did with +the fence, only we set `pNotification` instead of `pFence`. You can set both of these at the same +time and they should both work as expected. If using the `pNotification` system, you need to ensure +your `ma_async_notification_callbacks` object stays valid. + + + +6.2. Resource Manager Implementation Details +-------------------------------------------- +Resources are managed in two main ways: + + * By storing the entire sound inside an in-memory buffer (referred to as a data buffer) + * By streaming audio data on the fly (referred to as a data stream) + +A resource managed data source (`ma_resource_manager_data_source`) encapsulates a data buffer or +data stream, depending on whether or not the data source was initialized with the +`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag. If so, it will make use of a +`ma_resource_manager_data_stream` object. Otherwise it will use a `ma_resource_manager_data_buffer` +object. Both of these objects are data sources which means they can be used with any +`ma_data_source_*()` API. + +Another major feature of the resource manager is the ability to asynchronously decode audio files. +This relieves the audio thread of time-consuming decoding which can negatively affect scalability +due to the audio thread needing to complete it's work extremely quickly to avoid glitching. +Asynchronous decoding is achieved through a job system. There is a central multi-producer, +multi-consumer, fixed-capacity job queue. When some asynchronous work needs to be done, a job is +posted to the queue which is then read by a job thread. The number of job threads can be +configured for improved scalability, and job threads can all run in parallel without needing to +worry about the order of execution (how this is achieved is explained below). + +When a sound is being loaded asynchronously, playback can begin before the sound has been fully +decoded. This enables the application to start playback of the sound quickly, while at the same +time allowing to resource manager to keep loading in the background. Since there may be less +threads than the number of sounds being loaded at a given time, a simple scheduling system is used +to keep decoding time balanced and fair. The resource manager solves this by splitting decoding +into chunks called pages. By default, each page is 1 second long. When a page has been decoded, a +new job will be posted to start decoding the next page. By dividing up decoding into pages, an +individual sound shouldn't ever delay every other sound from having their first page decoded. Of +course, when loading many sounds at the same time, there will always be an amount of time required +to process jobs in the queue so in heavy load situations there will still be some delay. To +determine if a data source is ready to have some frames read, use +`ma_resource_manager_data_source_get_available_frames()`. This will return the number of frames +available starting from the current position. + + +6.2.1. Job Queue +---------------- +The resource manager uses a job queue which is multi-producer, multi-consumer, and fixed-capacity. +This job queue is not currently lock-free, and instead uses a spinlock to achieve thread-safety. +Only a fixed number of jobs can be allocated and inserted into the queue which is done through a +lock-free data structure for allocating an index into a fixed sized array, with reference counting +for mitigation of the ABA problem. The reference count is 32-bit. + +For many types of jobs it's important that they execute in a specific order. In these cases, jobs +are executed serially. For the resource manager, serial execution of jobs is only required on a +per-object basis (per data buffer or per data stream). Each of these objects stores an execution +counter. When a job is posted it is associated with an execution counter. When the job is +processed, it checks if the execution counter of the job equals the execution counter of the +owning object and if so, processes the job. If the counters are not equal, the job will be posted +back onto the job queue for later processing. When the job finishes processing the execution order +of the main object is incremented. This system means the no matter how many job threads are +executing, decoding of an individual sound will always get processed serially. The advantage to +having multiple threads comes into play when loading multiple sounds at the same time. + +The resource manager's job queue is not 100% lock-free and will use a spinlock to achieve +thread-safety for a very small section of code. This is only relevant when the resource manager +uses more than one job thread. If only using a single job thread, which is the default, the +lock should never actually wait in practice. The amount of time spent locking should be quite +short, but it's something to be aware of for those who have pedantic lock-free requirements and +need to use more than one job thread. There are plans to remove this lock in a future version. + +In addition, posting a job will release a semaphore, which on Win32 is implemented with +`ReleaseSemaphore` and on POSIX platforms via a condition variable: + + ```c + pthread_mutex_lock(&pSemaphore->lock); + { + pSemaphore->value += 1; + pthread_cond_signal(&pSemaphore->cond); + } + pthread_mutex_unlock(&pSemaphore->lock); + ``` + +Again, this is relevant for those with strict lock-free requirements in the audio thread. To avoid +this, you can use non-blocking mode (via the `MA_JOB_QUEUE_FLAG_NON_BLOCKING` +flag) and implement your own job processing routine (see the "Resource Manager" section above for +details on how to do this). + + + +6.2.2. Data Buffers +------------------- +When the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag is excluded at initialization time, the +resource manager will try to load the data into an in-memory data buffer. Before doing so, however, +it will first check if the specified file is already loaded. If so, it will increment a reference +counter and just use the already loaded data. This saves both time and memory. When the data buffer +is uninitialized, the reference counter will be decremented. If the counter hits zero, the file +will be unloaded. This is a detail to keep in mind because it could result in excessive loading and +unloading of a sound. For example, the following sequence will result in a file be loaded twice, +once after the other: + + ```c + ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer0); // Refcount = 1. Initial load. + ma_resource_manager_data_source_uninit(pResourceManager, &myDataBuffer0); // Refcount = 0. Unloaded. + + ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer1); // Refcount = 1. Reloaded because previous uninit() unloaded it. + ma_resource_manager_data_source_uninit(pResourceManager, &myDataBuffer1); // Refcount = 0. Unloaded. + ``` + +A binary search tree (BST) is used for storing data buffers as it has good balance between +efficiency and simplicity. The key of the BST is a 64-bit hash of the file path that was passed +into `ma_resource_manager_data_source_init()`. The advantage of using a hash is that it saves +memory over storing the entire path, has faster comparisons, and results in a mostly balanced BST +due to the random nature of the hash. The disadvantage is that file names are case-sensitive. If +this is an issue, you should normalize your file names to upper- or lower-case before initializing +your data sources. + +When a sound file has not already been loaded and the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` +flag is excluded, the file will be decoded synchronously by the calling thread. There are two +options for controlling how the audio is stored in the data buffer - encoded or decoded. When the +`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` option is excluded, the raw file data will be stored +in memory. Otherwise the sound will be decoded before storing it in memory. Synchronous loading is +a very simple and standard process of simply adding an item to the BST, allocating a block of +memory and then decoding (if `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` is specified). + +When the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` flag is specified, loading of the data buffer +is done asynchronously. In this case, a job is posted to the queue to start loading and then the +function immediately returns, setting an internal result code to `MA_BUSY`. This result code is +returned when the program calls `ma_resource_manager_data_source_result()`. When decoding has fully +completed `MA_SUCCESS` will be returned. This can be used to know if loading has fully completed. + +When loading asynchronously, a single job is posted to the queue of the type +`MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE`. This involves making a copy of the file path and +associating it with job. When the job is processed by the job thread, it will first load the file +using the VFS associated with the resource manager. When using a custom VFS, it's important that it +be completely thread-safe because it will be used from one or more job threads at the same time. +Individual files should only ever be accessed by one thread at a time, however. After opening the +file via the VFS, the job will determine whether or not the file is being decoded. If not, it +simply allocates a block of memory and loads the raw file contents into it and returns. On the +other hand, when the file is being decoded, it will first allocate a decoder on the heap and +initialize it. Then it will check if the length of the file is known. If so it will allocate a +block of memory to store the decoded output and initialize it to silence. If the size is unknown, +it will allocate room for one page. After memory has been allocated, the first page will be +decoded. If the sound is shorter than a page, the result code will be set to `MA_SUCCESS` and the +completion event will be signalled and loading is now complete. If, however, there is more to +decode, a job with the code `MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE` is posted. This job +will decode the next page and perform the same process if it reaches the end. If there is more to +decode, the job will post another `MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE` job which will +keep on happening until the sound has been fully decoded. For sounds of an unknown length, each +page will be linked together as a linked list. Internally this is implemented via the +`ma_paged_audio_buffer` object. + + +6.2.3. Data Streams +------------------- +Data streams only ever store two pages worth of data for each instance. They are most useful for +large sounds like music tracks in games that would consume too much memory if fully decoded in +memory. After every frame from a page has been read, a job will be posted to load the next page +which is done from the VFS. + +For data streams, the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` flag will determine whether or +not initialization of the data source waits until the two pages have been decoded. When unset, +`ma_resource_manager_data_source_init()` will wait until the two pages have been loaded, otherwise +it will return immediately. + +When frames are read from a data stream using `ma_resource_manager_data_source_read_pcm_frames()`, +`MA_BUSY` will be returned if there are no frames available. If there are some frames available, +but less than the number requested, `MA_SUCCESS` will be returned, but the actual number of frames +read will be less than the number requested. Due to the asynchronous nature of data streams, +seeking is also asynchronous. If the data stream is in the middle of a seek, `MA_BUSY` will be +returned when trying to read frames. + +When `ma_resource_manager_data_source_read_pcm_frames()` results in a page getting fully consumed +a job is posted to load the next page. This will be posted from the same thread that called +`ma_resource_manager_data_source_read_pcm_frames()`. + +Data streams are uninitialized by posting a job to the queue, but the function won't return until +that job has been processed. The reason for this is that the caller owns the data stream object and +therefore miniaudio needs to ensure everything completes before handing back control to the caller. +Also, if the data stream is uninitialized while pages are in the middle of decoding, they must +complete before destroying any underlying object and the job system handles this cleanly. + +Note that when a new page needs to be loaded, a job will be posted to the resource manager's job +thread from the audio thread. You must keep in mind the details mentioned in the "Job Queue" +section above regarding locking when posting an event if you require a strictly lock-free audio +thread. + + + +7. Node Graph +============= +miniaudio's routing infrastructure follows a node graph paradigm. The idea is that you create a +node whose outputs are attached to inputs of another node, thereby creating a graph. There are +different types of nodes, with each node in the graph processing input data to produce output, +which is then fed through the chain. Each node in the graph can apply their own custom effects. At +the start of the graph will usually be one or more data source nodes which have no inputs, but +instead pull their data from a data source. At the end of the graph is an endpoint which represents +the end of the chain and is where the final output is ultimately extracted from. + +Each node has a number of input buses and a number of output buses. An output bus from a node is +attached to an input bus of another. Multiple nodes can connect their output buses to another +node's input bus, in which case their outputs will be mixed before processing by the node. Below is +a diagram that illustrates a hypothetical node graph setup: + + ``` + >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Data flows left to right >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + +---------------+ +-----------------+ + | Data Source 1 =----+ +----------+ +----= Low Pass Filter =----+ + +---------------+ | | =----+ +-----------------+ | +----------+ + +----= Splitter | +----= ENDPOINT | + +---------------+ | | =----+ +-----------------+ | +----------+ + | Data Source 2 =----+ +----------+ +----= Echo / Delay =----+ + +---------------+ +-----------------+ + ``` + +In the above graph, it starts with two data sources whose outputs are attached to the input of a +splitter node. It's at this point that the two data sources are mixed. After mixing, the splitter +performs it's processing routine and produces two outputs which is simply a duplication of the +input stream. One output is attached to a low pass filter, whereas the other output is attached to +a echo/delay. The outputs of the the low pass filter and the echo are attached to the endpoint, and +since they're both connected to the same input but, they'll be mixed. + +Each input bus must be configured to accept the same number of channels, but the number of channels +used by input buses can be different to the number of channels for output buses in which case +miniaudio will automatically convert the input data to the output channel count before processing. +The number of channels of an output bus of one node must match the channel count of the input bus +it's attached to. The channel counts cannot be changed after the node has been initialized. If you +attempt to attach an output bus to an input bus with a different channel count, attachment will +fail. + +To use a node graph, you first need to initialize a `ma_node_graph` object. This is essentially a +container around the entire graph. The `ma_node_graph` object is required for some thread-safety +issues which will be explained later. A `ma_node_graph` object is initialized using miniaudio's +standard config/init system: + + ```c + ma_node_graph_config nodeGraphConfig = ma_node_graph_config_init(myChannelCount); + + result = ma_node_graph_init(&nodeGraphConfig, NULL, &nodeGraph); // Second parameter is a pointer to allocation callbacks. + if (result != MA_SUCCESS) { + // Failed to initialize node graph. + } + ``` + +When you initialize the node graph, you're specifying the channel count of the endpoint. The +endpoint is a special node which has one input bus and one output bus, both of which have the +same channel count, which is specified in the config. Any nodes that connect directly to the +endpoint must be configured such that their output buses have the same channel count. When you read +audio data from the node graph, it'll have the channel count you specified in the config. To read +data from the graph: + + ```c + ma_uint32 framesRead; + result = ma_node_graph_read_pcm_frames(&nodeGraph, pFramesOut, frameCount, &framesRead); + if (result != MA_SUCCESS) { + // Failed to read data from the node graph. + } + ``` + +When you read audio data, miniaudio starts at the node graph's endpoint node which then pulls in +data from it's input attachments, which in turn recusively pull in data from their inputs, and so +on. At the start of the graph there will be some kind of data source node which will have zero +inputs and will instead read directly from a data source. The base nodes don't literally need to +read from a `ma_data_source` object, but they will always have some kind of underlying object that +sources some kind of audio. The `ma_data_source_node` node can be used to read from a +`ma_data_source`. Data is always in floating-point format and in the number of channels you +specified when the graph was initialized. The sample rate is defined by the underlying data sources. +It's up to you to ensure they use a consistent and appropraite sample rate. + +The `ma_node` API is designed to allow custom nodes to be implemented with relative ease, but +miniaudio includes a few stock nodes for common functionality. This is how you would initialize a +node which reads directly from a data source (`ma_data_source_node`) which is an example of one +of the stock nodes that comes with miniaudio: + + ```c + ma_data_source_node_config config = ma_data_source_node_config_init(pMyDataSource); + + ma_data_source_node dataSourceNode; + result = ma_data_source_node_init(&nodeGraph, &config, NULL, &dataSourceNode); + if (result != MA_SUCCESS) { + // Failed to create data source node. + } + ``` + +The data source node will use the output channel count to determine the channel count of the output +bus. There will be 1 output bus and 0 input buses (data will be drawn directly from the data +source). The data source must output to floating-point (`ma_format_f32`) or else an error will be +returned from `ma_data_source_node_init()`. + +By default the node will not be attached to the graph. To do so, use `ma_node_attach_output_bus()`: + + ```c + result = ma_node_attach_output_bus(&dataSourceNode, 0, ma_node_graph_get_endpoint(&nodeGraph), 0); + if (result != MA_SUCCESS) { + // Failed to attach node. + } + ``` + +The code above connects the data source node directly to the endpoint. Since the data source node +has only a single output bus, the index will always be 0. Likewise, the endpoint only has a single +input bus which means the input bus index will also always be 0. + +To detach a specific output bus, use `ma_node_detach_output_bus()`. To detach all output buses, use +`ma_node_detach_all_output_buses()`. If you want to just move the output bus from one attachment to +another, you do not need to detach first. You can just call `ma_node_attach_output_bus()` and it'll +deal with it for you. + +Less frequently you may want to create a specialized node. This will be a node where you implement +your own processing callback to apply a custom effect of some kind. This is similar to initalizing +one of the stock node types, only this time you need to specify a pointer to a vtable containing a +pointer to the processing function and the number of input and output buses. Example: + + ```c + static void my_custom_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) + { + // Do some processing of ppFramesIn (one stream of audio data per input bus) + const float* pFramesIn_0 = ppFramesIn[0]; // Input bus @ index 0. + const float* pFramesIn_1 = ppFramesIn[1]; // Input bus @ index 1. + float* pFramesOut_0 = ppFramesOut[0]; // Output bus @ index 0. + + // Do some processing. On input, `pFrameCountIn` will be the number of input frames in each + // buffer in `ppFramesIn` and `pFrameCountOut` will be the capacity of each of the buffers + // in `ppFramesOut`. On output, `pFrameCountIn` should be set to the number of input frames + // your node consumed and `pFrameCountOut` should be set the number of output frames that + // were produced. + // + // You should process as many frames as you can. If your effect consumes input frames at the + // same rate as output frames (always the case, unless you're doing resampling), you need + // only look at `ppFramesOut` and process that exact number of frames. If you're doing + // resampling, you'll need to be sure to set both `pFrameCountIn` and `pFrameCountOut` + // properly. + } + + static ma_node_vtable my_custom_node_vtable = + { + my_custom_node_process_pcm_frames, // The function that will be called process your custom node. This is where you'd implement your effect processing. + NULL, // Optional. A callback for calculating the number of input frames that are required to process a specified number of output frames. + 2, // 2 input buses. + 1, // 1 output bus. + 0 // Default flags. + }; + + ... + + // Each bus needs to have a channel count specified. To do this you need to specify the channel + // counts in an array and then pass that into the node config. + ma_uint32 inputChannels[2]; // Equal in size to the number of input channels specified in the vtable. + ma_uint32 outputChannels[1]; // Equal in size to the number of output channels specicied in the vtable. + + inputChannels[0] = channelsIn; + inputChannels[1] = channelsIn; + outputChannels[0] = channelsOut; + + ma_node_config nodeConfig = ma_node_config_init(); + nodeConfig.vtable = &my_custom_node_vtable; + nodeConfig.pInputChannels = inputChannels; + nodeConfig.pOutputChannels = outputChannels; + + ma_node_base node; + result = ma_node_init(&nodeGraph, &nodeConfig, NULL, &node); + if (result != MA_SUCCESS) { + // Failed to initialize node. + } + ``` + +When initializing a custom node, as in the code above, you'll normally just place your vtable in +static space. The number of input and output buses are specified as part of the vtable. If you need +a variable number of buses on a per-node bases, the vtable should have the relevant bus count set +to `MA_NODE_BUS_COUNT_UNKNOWN`. In this case, the bus count should be set in the node config: + + ```c + static ma_node_vtable my_custom_node_vtable = + { + my_custom_node_process_pcm_frames, // The function that will be called process your custom node. This is where you'd implement your effect processing. + NULL, // Optional. A callback for calculating the number of input frames that are required to process a specified number of output frames. + MA_NODE_BUS_COUNT_UNKNOWN, // The number of input buses is determined on a per-node basis. + 1, // 1 output bus. + 0 // Default flags. + }; + + ... + + ma_node_config nodeConfig = ma_node_config_init(); + nodeConfig.vtable = &my_custom_node_vtable; + nodeConfig.inputBusCount = myBusCount; // <-- Since the vtable specifies MA_NODE_BUS_COUNT_UNKNOWN, the input bus count should be set here. + nodeConfig.pInputChannels = inputChannels; // <-- Make sure there are nodeConfig.inputBusCount elements in this array. + nodeConfig.pOutputChannels = outputChannels; // <-- The vtable specifies 1 output bus, so there must be 1 element in this array. + ``` + +In the above example it's important to never set the `inputBusCount` and `outputBusCount` members +to anything other than their defaults if the vtable specifies an explicit count. They can only be +set if the vtable specifies MA_NODE_BUS_COUNT_UNKNOWN in the relevant bus count. + +Most often you'll want to create a structure to encapsulate your node with some extra data. You +need to make sure the `ma_node_base` object is your first member of the structure: + + ```c + typedef struct + { + ma_node_base base; // <-- Make sure this is always the first member. + float someCustomData; + } my_custom_node; + ``` + +By doing this, your object will be compatible with all `ma_node` APIs and you can attach it to the +graph just like any other node. + +In the custom processing callback (`my_custom_node_process_pcm_frames()` in the example above), the +number of channels for each bus is what was specified by the config when the node was initialized +with `ma_node_init()`. In addition, all attachments to each of the input buses will have been +pre-mixed by miniaudio. The config allows you to specify different channel counts for each +individual input and output bus. It's up to the effect to handle it appropriate, and if it can't, +return an error in it's initialization routine. + +Custom nodes can be assigned some flags to describe their behaviour. These are set via the vtable +and include the following: + + +-----------------------------------------+---------------------------------------------------+ + | Flag Name | Description | + +-----------------------------------------+---------------------------------------------------+ + | MA_NODE_FLAG_PASSTHROUGH | Useful for nodes that do not do any kind of audio | + | | processing, but are instead used for tracking | + | | time, handling events, etc. Also used by the | + | | internal endpoint node. It reads directly from | + | | the input bus to the output bus. Nodes with this | + | | flag must have exactly 1 input bus and 1 output | + | | bus, and both buses must have the same channel | + | | counts. | + +-----------------------------------------+---------------------------------------------------+ + | MA_NODE_FLAG_CONTINUOUS_PROCESSING | Causes the processing callback to be called even | + | | when no data is available to be read from input | + | | attachments. This is useful for effects like | + | | echos where there will be a tail of audio data | + | | that still needs to be processed even when the | + | | original data sources have reached their ends. | + +-----------------------------------------+---------------------------------------------------+ + | MA_NODE_FLAG_ALLOW_NULL_INPUT | Used in conjunction with | + | | `MA_NODE_FLAG_CONTINUOUS_PROCESSING`. When this | + | | is set, the `ppFramesIn` parameter of the | + | | processing callback will be set to NULL when | + | | there are no input frames are available. When | + | | this is unset, silence will be posted to the | + | | processing callback. | + +-----------------------------------------+---------------------------------------------------+ + | MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES | Used to tell miniaudio that input and output | + | | frames are processed at different rates. You | + | | should set this for any nodes that perform | + | | resampling. | + +-----------------------------------------+---------------------------------------------------+ + | MA_NODE_FLAG_SILENT_OUTPUT | Used to tell miniaudio that a node produces only | + | | silent output. This is useful for nodes where you | + | | don't want the output to contribute to the final | + | | mix. An example might be if you want split your | + | | stream and have one branch be output to a file. | + | | When using this flag, you should avoid writing to | + | | the output buffer of the node's processing | + | | callback because miniaudio will ignore it anyway. | + +-----------------------------------------+---------------------------------------------------+ + + +If you need to make a copy of an audio stream for effect processing you can use a splitter node +called `ma_splitter_node`. This takes has 1 input bus and splits the stream into 2 output buses. +You can use it like this: + + ```c + ma_splitter_node_config splitterNodeConfig = ma_splitter_node_config_init(channelsIn, channelsOut); + + ma_splitter_node splitterNode; + result = ma_splitter_node_init(&nodeGraph, &splitterNodeConfig, NULL, &splitterNode); + if (result != MA_SUCCESS) { + // Failed to create node. + } + + // Attach your output buses to two different input buses (can be on two different nodes). + ma_node_attach_output_bus(&splitterNode, 0, ma_node_graph_get_endpoint(&nodeGraph), 0); // Attach directly to the endpoint. + ma_node_attach_output_bus(&splitterNode, 1, &myEffectNode, 0); // Attach to input bus 0 of some effect node. + ``` + +The volume of an output bus can be configured on a per-bus basis: + + ```c + ma_node_set_output_bus_volume(&splitterNode, 0, 0.5f); + ma_node_set_output_bus_volume(&splitterNode, 1, 0.5f); + ``` + +In the code above we're using the splitter node from before and changing the volume of each of the +copied streams. + +You can start and stop a node with the following: + + ```c + ma_node_set_state(&splitterNode, ma_node_state_started); // The default state. + ma_node_set_state(&splitterNode, ma_node_state_stopped); + ``` + +By default the node is in a started state, but since it won't be connected to anything won't +actually be invoked by the node graph until it's connected. When you stop a node, data will not be +read from any of it's input connections. You can use this property to stop a group of sounds +atomically. + +You can configure the initial state of a node in it's config: + + ```c + nodeConfig.initialState = ma_node_state_stopped; + ``` + +Note that for the stock specialized nodes, all of their configs will have a `nodeConfig` member +which is the config to use with the base node. This is where the initial state can be configured +for specialized nodes: + + ```c + dataSourceNodeConfig.nodeConfig.initialState = ma_node_state_stopped; + ``` + +When using a specialized node like `ma_data_source_node` or `ma_splitter_node`, be sure to not +modify the `vtable` member of the `nodeConfig` object. + + +7.1. Timing +----------- +The node graph supports starting and stopping nodes at scheduled times. This is especially useful +for data source nodes where you want to get the node set up, but only start playback at a specific +time. There are two clocks: local and global. + +A local clock is per-node, whereas the global clock is per graph. Scheduling starts and stops can +only be done based on the global clock because the local clock will not be running while the node +is stopped. The global clocks advances whenever `ma_node_graph_read_pcm_frames()` is called. On the +other hand, the local clock only advances when the node's processing callback is fired, and is +advanced based on the output frame count. + +To retrieve the global time, use `ma_node_graph_get_time()`. The global time can be set with +`ma_node_graph_set_time()` which might be useful if you want to do seeking on a global timeline. +Getting and setting the local time is similar. Use `ma_node_get_time()` to retrieve the local time, +and `ma_node_set_time()` to set the local time. The global and local times will be advanced by the +audio thread, so care should be taken to avoid data races. Ideally you should avoid calling these +outside of the node processing callbacks which are always run on the audio thread. + +There is basic support for scheduling the starting and stopping of nodes. You can only schedule one +start and one stop at a time. This is mainly intended for putting nodes into a started or stopped +state in a frame-exact manner. Without this mechanism, starting and stopping of a node is limited +to the resolution of a call to `ma_node_graph_read_pcm_frames()` which would typically be in blocks +of several milliseconds. The following APIs can be used for scheduling node states: + + ```c + ma_node_set_state_time() + ma_node_get_state_time() + ``` + +The time is absolute and must be based on the global clock. An example is below: + + ```c + ma_node_set_state_time(&myNode, ma_node_state_started, sampleRate*1); // Delay starting to 1 second. + ma_node_set_state_time(&myNode, ma_node_state_stopped, sampleRate*5); // Delay stopping to 5 seconds. + ``` + +An example for changing the state using a relative time. + + ```c + ma_node_set_state_time(&myNode, ma_node_state_started, sampleRate*1 + ma_node_graph_get_time(&myNodeGraph)); + ma_node_set_state_time(&myNode, ma_node_state_stopped, sampleRate*5 + ma_node_graph_get_time(&myNodeGraph)); + ``` + +Note that due to the nature of multi-threading the times may not be 100% exact. If this is an +issue, consider scheduling state changes from within a processing callback. An idea might be to +have some kind of passthrough trigger node that is used specifically for tracking time and handling +events. + + + +7.2. Thread Safety and Locking +------------------------------ +When processing audio, it's ideal not to have any kind of locking in the audio thread. Since it's +expected that `ma_node_graph_read_pcm_frames()` would be run on the audio thread, it does so +without the use of any locks. This section discusses the implementation used by miniaudio and goes +over some of the compromises employed by miniaudio to achieve this goal. Note that the current +implementation may not be ideal - feedback and critiques are most welcome. + +The node graph API is not *entirely* lock-free. Only `ma_node_graph_read_pcm_frames()` is expected +to be lock-free. Attachment, detachment and uninitialization of nodes use locks to simplify the +implementation, but are crafted in a way such that such locking is not required when reading audio +data from the graph. Locking in these areas are achieved by means of spinlocks. + +The main complication with keeping `ma_node_graph_read_pcm_frames()` lock-free stems from the fact +that a node can be uninitialized, and it's memory potentially freed, while in the middle of being +processed on the audio thread. There are times when the audio thread will be referencing a node, +which means the uninitialization process of a node needs to make sure it delays returning until the +audio thread is finished so that control is not handed back to the caller thereby giving them a +chance to free the node's memory. + +When the audio thread is processing a node, it does so by reading from each of the output buses of +the node. In order for a node to process data for one of it's output buses, it needs to read from +each of it's input buses, and so on an so forth. It follows that once all output buses of a node +are detached, the node as a whole will be disconnected and no further processing will occur unless +it's output buses are reattached, which won't be happening when the node is being uninitialized. +By having `ma_node_detach_output_bus()` wait until the audio thread is finished with it, we can +simplify a few things, at the expense of making `ma_node_detach_output_bus()` a bit slower. By +doing this, the implementation of `ma_node_uninit()` becomes trivial - just detach all output +nodes, followed by each of the attachments to each of it's input nodes, and then do any final clean +up. + +With the above design, the worst-case scenario is `ma_node_detach_output_bus()` taking as long as +it takes to process the output bus being detached. This will happen if it's called at just the +wrong moment where the audio thread has just iterated it and has just started processing. The +caller of `ma_node_detach_output_bus()` will stall until the audio thread is finished, which +includes the cost of recursively processing it's inputs. This is the biggest compromise made with +the approach taken by miniaudio for it's lock-free processing system. The cost of detaching nodes +earlier in the pipeline (data sources, for example) will be cheaper than the cost of detaching +higher level nodes, such as some kind of final post-processing endpoint. If you need to do mass +detachments, detach starting from the lowest level nodes and work your way towards the final +endpoint node (but don't try detaching the node graph's endpoint). If the audio thread is not +running, detachment will be fast and detachment in any order will be the same. The reason nodes +need to wait for their input attachments to complete is due to the potential for desyncs between +data sources. If the node was to terminate processing mid way through processing it's inputs, +there's a chance that some of the underlying data sources will have been read, but then others not. +That will then result in a potential desynchronization when detaching and reattaching higher-level +nodes. A possible solution to this is to have an option when detaching to terminate processing +before processing all input attachments which should be fairly simple. + +Another compromise, albeit less significant, is locking when attaching and detaching nodes. This +locking is achieved by means of a spinlock in order to reduce memory overhead. A lock is present +for each input bus and output bus. When an output bus is connected to an input bus, both the output +bus and input bus is locked. This locking is specifically for attaching and detaching across +different threads and does not affect `ma_node_graph_read_pcm_frames()` in any way. The locking and +unlocking is mostly self-explanatory, but a slightly less intuitive aspect comes into it when +considering that iterating over attachments must not break as a result of attaching or detaching a +node while iteration is occuring. + +Attaching and detaching are both quite simple. When an output bus of a node is attached to an input +bus of another node, it's added to a linked list. Basically, an input bus is a linked list, where +each item in the list is and output bus. We have some intentional (and convenient) restrictions on +what can done with the linked list in order to simplify the implementation. First of all, whenever +something needs to iterate over the list, it must do so in a forward direction. Backwards iteration +is not supported. Also, items can only be added to the start of the list. + +The linked list is a doubly-linked list where each item in the list (an output bus) holds a pointer +to the next item in the list, and another to the previous item. A pointer to the previous item is +only required for fast detachment of the node - it is never used in iteration. This is an +important property because it means from the perspective of iteration, attaching and detaching of +an item can be done with a single atomic assignment. This is exploited by both the attachment and +detachment process. When attaching the node, the first thing that is done is the setting of the +local "next" and "previous" pointers of the node. After that, the item is "attached" to the list +by simply performing an atomic exchange with the head pointer. After that, the node is "attached" +to the list from the perspective of iteration. Even though the "previous" pointer of the next item +hasn't yet been set, from the perspective of iteration it's been attached because iteration will +only be happening in a forward direction which means the "previous" pointer won't actually ever get +used. The same general process applies to detachment. See `ma_node_attach_output_bus()` and +`ma_node_detach_output_bus()` for the implementation of this mechanism. + + + +8. Decoding =========== -The `ma_decoder` API is used for reading audio files. Decoders are completely decoupled from devices and can be used independently. The following formats are -supported: +The `ma_decoder` API is used for reading audio files. Decoders are completely decoupled from +devices and can be used independently. The following formats are supported: +---------+------------------+----------+ | Format | Decoding Backend | Built-In | @@ -475,7 +2405,8 @@ supported: | Vorbis | stb_vorbis | No | +---------+------------------+----------+ -Vorbis is supported via stb_vorbis which can be enabled by including the header section before the implementation of miniaudio, like the following: +Vorbis is supported via stb_vorbis which can be enabled by including the header section before the +implementation of miniaudio, like the following: ```c #define STB_VORBIS_HEADER_ONLY @@ -491,8 +2422,9 @@ Vorbis is supported via stb_vorbis which can be enabled by including the header A copy of stb_vorbis is included in the "extras" folder in the miniaudio repository (https://github.com/mackron/miniaudio). -Built-in decoders are amalgamated into the implementation section of miniaudio. You can disable the built-in decoders by specifying one or more of the -following options before the miniaudio implementation: +Built-in decoders are amalgamated into the implementation section of miniaudio. You can disable the +built-in decoders by specifying one or more of the following options before the miniaudio +implementation: ```c #define MA_NO_WAV @@ -500,10 +2432,12 @@ following options before the miniaudio implementation: #define MA_NO_FLAC ``` -Disabling built-in decoding libraries is useful if you use these libraries independantly of the `ma_decoder` API. +Disabling built-in decoding libraries is useful if you use these libraries independantly of the +`ma_decoder` API. -A decoder can be initialized from a file with `ma_decoder_init_file()`, a block of memory with `ma_decoder_init_memory()`, or from data delivered via callbacks -with `ma_decoder_init()`. Here is an example for loading a decoder from a file: +A decoder can be initialized from a file with `ma_decoder_init_file()`, a block of memory with +`ma_decoder_init_memory()`, or from data delivered via callbacks with `ma_decoder_init()`. Here is +an example for loading a decoder from a file: ```c ma_decoder decoder; @@ -517,20 +2451,23 @@ with `ma_decoder_init()`. Here is an example for loading a decoder from a file: ma_decoder_uninit(&decoder); ``` -When initializing a decoder, you can optionally pass in a pointer to a `ma_decoder_config` object (the `NULL` argument in the example above) which allows you -to configure the output format, channel count, sample rate and channel map: +When initializing a decoder, you can optionally pass in a pointer to a `ma_decoder_config` object +(the `NULL` argument in the example above) which allows you to configure the output format, channel +count, sample rate and channel map: ```c ma_decoder_config config = ma_decoder_config_init(ma_format_f32, 2, 48000); ``` -When passing in `NULL` for decoder config in `ma_decoder_init*()`, the output format will be the same as that defined by the decoding backend. +When passing in `NULL` for decoder config in `ma_decoder_init*()`, the output format will be the +same as that defined by the decoding backend. -Data is read from the decoder as PCM frames. This will return the number of PCM frames actually read. If the return value is less than the requested number of -PCM frames it means you've reached the end: +Data is read from the decoder as PCM frames. This will output the number of PCM frames actually +read. If this is less than the requested number of PCM frames it means you've reached the end. The +return value will be `MA_AT_END` if no samples have been read and the end has been reached. ```c - ma_uint64 framesRead = ma_decoder_read_pcm_frames(pDecoder, pFrames, framesToRead); + ma_result result = ma_decoder_read_pcm_frames(pDecoder, pFrames, framesToRead, &framesRead); if (framesRead < framesToRead) { // Reached the end. } @@ -551,8 +2488,10 @@ If you want to loop back to the start, you can simply seek back to the first PCM ma_decoder_seek_to_pcm_frame(pDecoder, 0); ``` -When loading a decoder, miniaudio uses a trial and error technique to find the appropriate decoding backend. This can be unnecessarily inefficient if the type -is already known. In this case you can use `encodingFormat` variable in the device config to specify a specific encoding format you want to decode: +When loading a decoder, miniaudio uses a trial and error technique to find the appropriate decoding +backend. This can be unnecessarily inefficient if the type is already known. In this case you can +use `encodingFormat` variable in the device config to specify a specific encoding format you want +to decode: ```c decoderConfig.encodingFormat = ma_encoding_format_wav; @@ -560,24 +2499,95 @@ is already known. In this case you can use `encodingFormat` variable in the devi See the `ma_encoding_format` enum for possible encoding formats. -The `ma_decoder_init_file()` API will try using the file extension to determine which decoding backend to prefer. +The `ma_decoder_init_file()` API will try using the file extension to determine which decoding +backend to prefer. + + +8.1. Custom Decoders +-------------------- +It's possible to implement a custom decoder and plug it into miniaudio. This is extremely useful +when you want to use the `ma_decoder` API, but need to support an encoding format that's not one of +the stock formats supported by miniaudio. This can be put to particularly good use when using the +`ma_engine` and/or `ma_resource_manager` APIs because they use `ma_decoder` internally. If, for +example, you wanted to support Opus, you can do so with a custom decoder (there if a reference +Opus decoder in the "extras" folder of the miniaudio repository which uses libopus + libopusfile). + +A custom decoder must implement a data source. A vtable called `ma_decoding_backend_vtable` needs +to be implemented which is then passed into the decoder config: + + ```c + ma_decoding_backend_vtable* pCustomBackendVTables[] = + { + &g_ma_decoding_backend_vtable_libvorbis, + &g_ma_decoding_backend_vtable_libopus + }; + + ... + + decoderConfig = ma_decoder_config_init_default(); + decoderConfig.pCustomBackendUserData = NULL; + decoderConfig.ppCustomBackendVTables = pCustomBackendVTables; + decoderConfig.customBackendCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]); + ``` + +The `ma_decoding_backend_vtable` vtable has the following functions: + + ``` + onInit + onInitFile + onInitFileW + onInitMemory + onUninit + ``` + +There are only two functions that must be implemented - `onInit` and `onUninit`. The other +functions can be implemented for a small optimization for loading from a file path or memory. If +these are not specified, miniaudio will deal with it for you via a generic implementation. + +When you initialize a custom data source (by implementing the `onInit` function in the vtable) you +will need to output a pointer to a `ma_data_source` which implements your custom decoder. See the +section about data sources for details on how to implemen this. Alternatively, see the +"custom_decoders" example in the miniaudio repository. + +The `onInit` function takes a pointer to some callbacks for the purpose of reading raw audio data +from some abitrary source. You'll use these functions to read from the raw data and perform the +decoding. When you call them, you will pass in the `pReadSeekTellUserData` pointer to the relevant +parameter. + +The `pConfig` parameter in `onInit` can be used to configure the backend if appropriate. It's only +used as a hint and can be ignored. However, if any of the properties are relevant to your decoder, +an optimal implementation will handle the relevant properties appropriately. + +If memory allocation is required, it should be done so via the specified allocation callbacks if +possible (the `pAllocationCallbacks` parameter). + +If an error occurs when initializing the decoder, you should leave `ppBackend` unset, or set to +NULL, and make sure everything is cleaned up appropriately and an appropriate result code returned. +When multiple custom backends are specified, miniaudio will cycle through the vtables in the order +they're listed in the array that's passed into the decoder config so it's important that your +initialization routine is clean. + +When a decoder is uninitialized, the `onUninit` callback will be fired which will give you an +opportunity to clean up and internal data. -5. Encoding +9. Encoding =========== -The `ma_encoding` API is used for writing audio files. The only supported output format is WAV which is achieved via dr_wav which is amalgamated into the -implementation section of miniaudio. This can be disabled by specifying the following option before the implementation of miniaudio: +The `ma_encoding` API is used for writing audio files. The only supported output format is WAV +which is achieved via dr_wav which is amalgamated into the implementation section of miniaudio. +This can be disabled by specifying the following option before the implementation of miniaudio: ```c #define MA_NO_WAV ``` -An encoder can be initialized to write to a file with `ma_encoder_init_file()` or from data delivered via callbacks with `ma_encoder_init()`. Below is an -example for initializing an encoder to output to a file. +An encoder can be initialized to write to a file with `ma_encoder_init_file()` or from data +delivered via callbacks with `ma_encoder_init()`. Below is an example for initializing an encoder +to output to a file. ```c - ma_encoder_config config = ma_encoder_config_init(ma_resource_format_wav, FORMAT, CHANNELS, SAMPLE_RATE); + ma_encoder_config config = ma_encoder_config_init(ma_encoding_format_wav, FORMAT, CHANNELS, SAMPLE_RATE); ma_encoder encoder; ma_result result = ma_encoder_init_file("my_file.wav", &config, &encoder); if (result != MA_SUCCESS) { @@ -589,17 +2599,20 @@ example for initializing an encoder to output to a file. ma_encoder_uninit(&encoder); ``` -When initializing an encoder you must specify a config which is initialized with `ma_encoder_config_init()`. Here you must specify the file type, the output -sample format, output channel count and output sample rate. The following file types are supported: +When initializing an encoder you must specify a config which is initialized with +`ma_encoder_config_init()`. Here you must specify the file type, the output sample format, output +channel count and output sample rate. The following file types are supported: +------------------------+-------------+ | Enum | Description | +------------------------+-------------+ - | ma_resource_format_wav | WAV | + | ma_encoding_format_wav | WAV | +------------------------+-------------+ -If the format, channel count or sample rate is not supported by the output file type an error will be returned. The encoder will not perform data conversion so -you will need to convert it before outputting any audio data. To output audio data, use `ma_encoder_write_pcm_frames()`, like in the example below: +If the format, channel count or sample rate is not supported by the output file type an error will +be returned. The encoder will not perform data conversion so you will need to convert it before +outputting any audio data. To output audio data, use `ma_encoder_write_pcm_frames()`, like in the +example below: ```c framesWritten = ma_encoder_write_pcm_frames(&encoder, pPCMFramesToWrite, framesToWrite); @@ -608,21 +2621,25 @@ you will need to convert it before outputting any audio data. To output audio da Encoders must be uninitialized with `ma_encoder_uninit()`. -6. Data Conversion -================== -A data conversion API is included with miniaudio which supports the majority of data conversion requirements. This supports conversion between sample formats, -channel counts (with channel mapping) and sample rates. + +10. Data Conversion +=================== +A data conversion API is included with miniaudio which supports the majority of data conversion +requirements. This supports conversion between sample formats, channel counts (with channel +mapping) and sample rates. -6.1. Sample Format Conversion ------------------------------ -Conversion between sample formats is achieved with the `ma_pcm_*_to_*()`, `ma_pcm_convert()` and `ma_convert_pcm_frames_format()` APIs. Use `ma_pcm_*_to_*()` -to convert between two specific formats. Use `ma_pcm_convert()` to convert based on a `ma_format` variable. Use `ma_convert_pcm_frames_format()` to convert -PCM frames where you want to specify the frame count and channel count as a variable instead of the total sample count. +10.1. Sample Format Conversion +------------------------------ +Conversion between sample formats is achieved with the `ma_pcm_*_to_*()`, `ma_pcm_convert()` and +`ma_convert_pcm_frames_format()` APIs. Use `ma_pcm_*_to_*()` to convert between two specific +formats. Use `ma_pcm_convert()` to convert based on a `ma_format` variable. Use +`ma_convert_pcm_frames_format()` to convert PCM frames where you want to specify the frame count +and channel count as a variable instead of the total sample count. -6.1.1. Dithering ----------------- +10.1.1. Dithering +----------------- Dithering can be set using the ditherMode parameter. The different dithering modes include the following, in order of efficiency: @@ -635,8 +2652,9 @@ The different dithering modes include the following, in order of efficiency: | Triangle | ma_dither_mode_triangle | +-----------+--------------------------+ -Note that even if the dither mode is set to something other than `ma_dither_mode_none`, it will be ignored for conversions where dithering is not needed. -Dithering is available for the following conversions: +Note that even if the dither mode is set to something other than `ma_dither_mode_none`, it will be +ignored for conversions where dithering is not needed. Dithering is available for the following +conversions: ``` s16 -> u8 @@ -648,14 +2666,16 @@ Dithering is available for the following conversions: f32 -> s16 ``` -Note that it is not an error to pass something other than ma_dither_mode_none for conversions where dither is not used. It will just be ignored. +Note that it is not an error to pass something other than ma_dither_mode_none for conversions where +dither is not used. It will just be ignored. -6.2. Channel Conversion ------------------------ -Channel conversion is used for channel rearrangement and conversion from one channel count to another. The `ma_channel_converter` API is used for channel -conversion. Below is an example of initializing a simple channel converter which converts from mono to stereo. +10.2. Channel Conversion +------------------------ +Channel conversion is used for channel rearrangement and conversion from one channel count to +another. The `ma_channel_converter` API is used for channel conversion. Below is an example of +initializing a simple channel converter which converts from mono to stereo. ```c ma_channel_converter_config config = ma_channel_converter_config_init( @@ -666,7 +2686,7 @@ conversion. Below is an example of initializing a simple channel converter which NULL, // Output channel map ma_channel_mix_mode_default); // The mixing algorithm to use when combining channels. - result = ma_channel_converter_init(&config, &converter); + result = ma_channel_converter_init(&config, NULL, &converter); if (result != MA_SUCCESS) { // Error. } @@ -681,34 +2701,43 @@ To perform the conversion simply call `ma_channel_converter_process_pcm_frames() } ``` -It is up to the caller to ensure the output buffer is large enough to accomodate the new PCM frames. +It is up to the caller to ensure the output buffer is large enough to accomodate the new PCM +frames. Input and output PCM frames are always interleaved. Deinterleaved layouts are not supported. -6.2.1. Channel Mapping ----------------------- -In addition to converting from one channel count to another, like the example above, the channel converter can also be used to rearrange channels. When -initializing the channel converter, you can optionally pass in channel maps for both the input and output frames. If the channel counts are the same, and each -channel map contains the same channel positions with the exception that they're in a different order, a simple shuffling of the channels will be performed. If, -however, there is not a 1:1 mapping of channel positions, or the channel counts differ, the input channels will be mixed based on a mixing mode which is -specified when initializing the `ma_channel_converter_config` object. +10.2.1. Channel Mapping +----------------------- +In addition to converting from one channel count to another, like the example above, the channel +converter can also be used to rearrange channels. When initializing the channel converter, you can +optionally pass in channel maps for both the input and output frames. If the channel counts are the +same, and each channel map contains the same channel positions with the exception that they're in +a different order, a simple shuffling of the channels will be performed. If, however, there is not +a 1:1 mapping of channel positions, or the channel counts differ, the input channels will be mixed +based on a mixing mode which is specified when initializing the `ma_channel_converter_config` +object. -When converting from mono to multi-channel, the mono channel is simply copied to each output channel. When going the other way around, the audio of each output -channel is simply averaged and copied to the mono channel. +When converting from mono to multi-channel, the mono channel is simply copied to each output +channel. When going the other way around, the audio of each output channel is simply averaged and +copied to the mono channel. -In more complicated cases blending is used. The `ma_channel_mix_mode_simple` mode will drop excess channels and silence extra channels. For example, converting -from 4 to 2 channels, the 3rd and 4th channels will be dropped, whereas converting from 2 to 4 channels will put silence into the 3rd and 4th channels. +In more complicated cases blending is used. The `ma_channel_mix_mode_simple` mode will drop excess +channels and silence extra channels. For example, converting from 4 to 2 channels, the 3rd and 4th +channels will be dropped, whereas converting from 2 to 4 channels will put silence into the 3rd and +4th channels. -The `ma_channel_mix_mode_rectangle` mode uses spacial locality based on a rectangle to compute a simple distribution between input and output. Imagine sitting -in the middle of a room, with speakers on the walls representing channel positions. The MA_CHANNEL_FRONT_LEFT position can be thought of as being in the corner -of the front and left walls. +The `ma_channel_mix_mode_rectangle` mode uses spacial locality based on a rectangle to compute a +simple distribution between input and output. Imagine sitting in the middle of a room, with +speakers on the walls representing channel positions. The `MA_CHANNEL_FRONT_LEFT` position can be +thought of as being in the corner of the front and left walls. -Finally, the `ma_channel_mix_mode_custom_weights` mode can be used to use custom user-defined weights. Custom weights can be passed in as the last parameter of +Finally, the `ma_channel_mix_mode_custom_weights` mode can be used to use custom user-defined +weights. Custom weights can be passed in as the last parameter of `ma_channel_converter_config_init()`. -Predefined channel maps can be retrieved with `ma_get_standard_channel_map()`. This takes a `ma_standard_channel_map` enum as it's first parameter, which can -be one of the following: +Predefined channel maps can be retrieved with `ma_channel_map_init_standard()`. This takes a +`ma_standard_channel_map` enum as it's first parameter, which can be one of the following: +-----------------------------------+-----------------------------------------------------------+ | Name | Description | @@ -780,9 +2809,10 @@ Below are the channel maps used by default in miniaudio (`ma_standard_channel_ma -6.3. Resampling ---------------- -Resampling is achieved with the `ma_resampler` object. To create a resampler object, do something like the following: +10.3. Resampling +---------------- +Resampling is achieved with the `ma_resampler` object. To create a resampler object, do something +like the following: ```c ma_resampler_config config = ma_resampler_config_init( @@ -819,104 +2849,128 @@ The following example shows how data can be processed // number of output frames written. ``` -To initialize the resampler you first need to set up a config (`ma_resampler_config`) with `ma_resampler_config_init()`. You need to specify the sample format -you want to use, the number of channels, the input and output sample rate, and the algorithm. +To initialize the resampler you first need to set up a config (`ma_resampler_config`) with +`ma_resampler_config_init()`. You need to specify the sample format you want to use, the number of +channels, the input and output sample rate, and the algorithm. -The sample format can be either `ma_format_s16` or `ma_format_f32`. If you need a different format you will need to perform pre- and post-conversions yourself -where necessary. Note that the format is the same for both input and output. The format cannot be changed after initialization. +The sample format can be either `ma_format_s16` or `ma_format_f32`. If you need a different format +you will need to perform pre- and post-conversions yourself where necessary. Note that the format +is the same for both input and output. The format cannot be changed after initialization. -The resampler supports multiple channels and is always interleaved (both input and output). The channel count cannot be changed after initialization. +The resampler supports multiple channels and is always interleaved (both input and output). The +channel count cannot be changed after initialization. -The sample rates can be anything other than zero, and are always specified in hertz. They should be set to something like 44100, etc. The sample rate is the -only configuration property that can be changed after initialization. +The sample rates can be anything other than zero, and are always specified in hertz. They should be +set to something like 44100, etc. The sample rate is the only configuration property that can be +changed after initialization. -The miniaudio resampler supports multiple algorithms: +The miniaudio resampler has built-in support for the following algorithms: +-----------+------------------------------+ | Algorithm | Enum Token | +-----------+------------------------------+ | Linear | ma_resample_algorithm_linear | - | Speex | ma_resample_algorithm_speex | + | Custom | ma_resample_algorithm_custom | +-----------+------------------------------+ -Because Speex is not public domain it is strictly opt-in and the code is stored in separate files. if you opt-in to the Speex backend you will need to consider -it's license, the text of which can be found in it's source files in "extras/speex_resampler". Details on how to opt-in to the Speex resampler is explained in -the Speex Resampler section below. - The algorithm cannot be changed after initialization. -Processing always happens on a per PCM frame basis and always assumes interleaved input and output. De-interleaved processing is not supported. To process -frames, use `ma_resampler_process_pcm_frames()`. On input, this function takes the number of output frames you can fit in the output buffer and the number of -input frames contained in the input buffer. On output these variables contain the number of output frames that were written to the output buffer and the -number of input frames that were consumed in the process. You can pass in NULL for the input buffer in which case it will be treated as an infinitely large -buffer of zeros. The output buffer can also be NULL, in which case the processing will be treated as seek. +Processing always happens on a per PCM frame basis and always assumes interleaved input and output. +De-interleaved processing is not supported. To process frames, use +`ma_resampler_process_pcm_frames()`. On input, this function takes the number of output frames you +can fit in the output buffer and the number of input frames contained in the input buffer. On +output these variables contain the number of output frames that were written to the output buffer +and the number of input frames that were consumed in the process. You can pass in NULL for the +input buffer in which case it will be treated as an infinitely large buffer of zeros. The output +buffer can also be NULL, in which case the processing will be treated as seek. -The sample rate can be changed dynamically on the fly. You can change this with explicit sample rates with `ma_resampler_set_rate()` and also with a decimal -ratio with `ma_resampler_set_rate_ratio()`. The ratio is in/out. +The sample rate can be changed dynamically on the fly. You can change this with explicit sample +rates with `ma_resampler_set_rate()` and also with a decimal ratio with +`ma_resampler_set_rate_ratio()`. The ratio is in/out. -Sometimes it's useful to know exactly how many input frames will be required to output a specific number of frames. You can calculate this with -`ma_resampler_get_required_input_frame_count()`. Likewise, it's sometimes useful to know exactly how many frames would be output given a certain number of -input frames. You can do this with `ma_resampler_get_expected_output_frame_count()`. +Sometimes it's useful to know exactly how many input frames will be required to output a specific +number of frames. You can calculate this with `ma_resampler_get_required_input_frame_count()`. +Likewise, it's sometimes useful to know exactly how many frames would be output given a certain +number of input frames. You can do this with `ma_resampler_get_expected_output_frame_count()`. -Due to the nature of how resampling works, the resampler introduces some latency. This can be retrieved in terms of both the input rate and the output rate -with `ma_resampler_get_input_latency()` and `ma_resampler_get_output_latency()`. +Due to the nature of how resampling works, the resampler introduces some latency. This can be +retrieved in terms of both the input rate and the output rate with +`ma_resampler_get_input_latency()` and `ma_resampler_get_output_latency()`. -6.3.1. Resampling Algorithms ----------------------------- -The choice of resampling algorithm depends on your situation and requirements. The linear resampler is the most efficient and has the least amount of latency, -but at the expense of poorer quality. The Speex resampler is higher quality, but slower with more latency. It also performs several heap allocations internally -for memory management. +10.3.1. Resampling Algorithms +----------------------------- +The choice of resampling algorithm depends on your situation and requirements. -6.3.1.1. Linear Resampling --------------------------- -The linear resampler is the fastest, but comes at the expense of poorer quality. There is, however, some control over the quality of the linear resampler which -may make it a suitable option depending on your requirements. +10.3.1.1. Linear Resampling +--------------------------- +The linear resampler is the fastest, but comes at the expense of poorer quality. There is, however, +some control over the quality of the linear resampler which may make it a suitable option depending +on your requirements. -The linear resampler performs low-pass filtering before or after downsampling or upsampling, depending on the sample rates you're converting between. When -decreasing the sample rate, the low-pass filter will be applied before downsampling. When increasing the rate it will be performed after upsampling. By default -a fourth order low-pass filter will be applied. This can be configured via the `lpfOrder` configuration variable. Setting this to 0 will disable filtering. +The linear resampler performs low-pass filtering before or after downsampling or upsampling, +depending on the sample rates you're converting between. When decreasing the sample rate, the +low-pass filter will be applied before downsampling. When increasing the rate it will be performed +after upsampling. By default a fourth order low-pass filter will be applied. This can be configured +via the `lpfOrder` configuration variable. Setting this to 0 will disable filtering. -The low-pass filter has a cutoff frequency which defaults to half the sample rate of the lowest of the input and output sample rates (Nyquist Frequency). This -can be controlled with the `lpfNyquistFactor` config variable. This defaults to 1, and should be in the range of 0..1, although a value of 0 does not make -sense and should be avoided. A value of 1 will use the Nyquist Frequency as the cutoff. A value of 0.5 will use half the Nyquist Frequency as the cutoff, etc. -Values less than 1 will result in more washed out sound due to more of the higher frequencies being removed. This config variable has no impact on performance -and is a purely perceptual configuration. +The low-pass filter has a cutoff frequency which defaults to half the sample rate of the lowest of +the input and output sample rates (Nyquist Frequency). -The API for the linear resampler is the same as the main resampler API, only it's called `ma_linear_resampler`. +The API for the linear resampler is the same as the main resampler API, only it's called +`ma_linear_resampler`. -6.3.1.2. Speex Resampling +10.3.2. Custom Resamplers ------------------------- -The Speex resampler is made up of third party code which is released under the BSD license. Because it is licensed differently to miniaudio, which is public -domain, it is strictly opt-in and all of it's code is stored in separate files. If you opt-in to the Speex resampler you must consider the license text in it's -source files. To opt-in, you must first `#include` the following file before the implementation of miniaudio.h: +You can implement a custom resampler by using the `ma_resample_algorithm_custom` resampling +algorithm and setting a vtable in the resampler config: ```c - #include "extras/speex_resampler/ma_speex_resampler.h" + ma_resampler_config config = ma_resampler_config_init(..., ma_resample_algorithm_custom); + config.pBackendVTable = &g_customResamplerVTable; ``` -Both the header and implementation is contained within the same file. The implementation can be included in your program like so: +Custom resamplers are useful if the stock algorithms are not appropriate for your use case. You +need to implement the required functions in `ma_resampling_backend_vtable`. Note that not all +functions in the vtable need to be implemented, but if it's possible to implement, they should be. - ```c - #define MINIAUDIO_SPEEX_RESAMPLER_IMPLEMENTATION - #include "extras/speex_resampler/ma_speex_resampler.h" - ``` +You can use the `ma_linear_resampler` object for an example on how to implement the vtable. The +`onGetHeapSize` callback is used to calculate the size of any internal heap allocation the custom +resampler will need to make given the supplied config. When you initialize the resampler via the +`onInit` callback, you'll be given a pointer to a heap allocation which is where you should store +the heap allocated data. You should not free this data in `onUninit` because miniaudio will manage +it for you. -Note that even if you opt-in to the Speex backend, miniaudio won't use it unless you explicitly ask for it in the respective config of the object you are -initializing. If you try to use the Speex resampler without opting in, initialization of the `ma_resampler` object will fail with `MA_NO_BACKEND`. +The `onProcess` callback is where the actual resampling takes place. On input, `pFrameCountIn` +points to a variable containing the number of frames in the `pFramesIn` buffer and +`pFrameCountOut` points to a variable containing the capacity in frames of the `pFramesOut` buffer. +On output, `pFrameCountIn` should be set to the number of input frames that were fully consumed, +whereas `pFrameCountOut` should be set to the number of frames that were written to `pFramesOut`. -The only configuration option to consider with the Speex resampler is the `speex.quality` config variable. This is a value between 0 and 10, with 0 being -the fastest with the poorest quality and 10 being the slowest with the highest quality. The default value is 3. +The `onSetRate` callback is optional and is used for dynamically changing the sample rate. If +dynamic rate changes are not supported, you can set this callback to NULL. + +The `onGetInputLatency` and `onGetOutputLatency` functions are used for retrieving the latency in +input and output rates respectively. These can be NULL in which case latency calculations will be +assumed to be NULL. + +The `onGetRequiredInputFrameCount` callback is used to give miniaudio a hint as to how many input +frames are required to be available to produce the given number of output frames. Likewise, the +`onGetExpectedOutputFrameCount` callback is used to determine how many output frames will be +produced given the specified number of input frames. miniaudio will use these as a hint, but they +are optional and can be set to NULL if you're unable to implement them. -6.4. General Data Conversion ----------------------------- -The `ma_data_converter` API can be used to wrap sample format conversion, channel conversion and resampling into one operation. This is what miniaudio uses -internally to convert between the format requested when the device was initialized and the format of the backend's native device. The API for general data -conversion is very similar to the resampling API. Create a `ma_data_converter` object like this: +10.4. General Data Conversion +----------------------------- +The `ma_data_converter` API can be used to wrap sample format conversion, channel conversion and +resampling into one operation. This is what miniaudio uses internally to convert between the format +requested when the device was initialized and the format of the backend's native device. The API +for general data conversion is very similar to the resampling API. Create a `ma_data_converter` +object like this: ```c ma_data_converter_config config = ma_data_converter_config_init( @@ -929,14 +2983,15 @@ conversion is very similar to the resampling API. Create a `ma_data_converter` o ); ma_data_converter converter; - ma_result result = ma_data_converter_init(&config, &converter); + ma_result result = ma_data_converter_init(&config, NULL, &converter); if (result != MA_SUCCESS) { // An error occurred... } ``` -In the example above we use `ma_data_converter_config_init()` to initialize the config, however there's many more properties that can be configured, such as -channel maps and resampling quality. Something like the following may be more suitable depending on your requirements: +In the example above we use `ma_data_converter_config_init()` to initialize the config, however +there's many more properties that can be configured, such as channel maps and resampling quality. +Something like the following may be more suitable depending on your requirements: ```c ma_data_converter_config config = ma_data_converter_config_init_default(); @@ -946,14 +3001,14 @@ channel maps and resampling quality. Something like the following may be more su config.channelsOut = outputChannels; config.sampleRateIn = inputSampleRate; config.sampleRateOut = outputSampleRate; - ma_get_standard_channel_map(ma_standard_channel_map_flac, config.channelCountIn, config.channelMapIn); + ma_channel_map_init_standard(ma_standard_channel_map_flac, config.channelMapIn, sizeof(config.channelMapIn)/sizeof(config.channelMapIn[0]), config.channelCountIn); config.resampling.linear.lpfOrder = MA_MAX_FILTER_ORDER; ``` Do the following to uninitialize the data converter: ```c - ma_data_converter_uninit(&converter); + ma_data_converter_uninit(&converter, NULL); ``` The following example shows how data can be processed @@ -970,33 +3025,42 @@ The following example shows how data can be processed // of output frames written. ``` -The data converter supports multiple channels and is always interleaved (both input and output). The channel count cannot be changed after initialization. +The data converter supports multiple channels and is always interleaved (both input and output). +The channel count cannot be changed after initialization. -Sample rates can be anything other than zero, and are always specified in hertz. They should be set to something like 44100, etc. The sample rate is the only -configuration property that can be changed after initialization, but only if the `resampling.allowDynamicSampleRate` member of `ma_data_converter_config` is -set to `MA_TRUE`. To change the sample rate, use `ma_data_converter_set_rate()` or `ma_data_converter_set_rate_ratio()`. The ratio must be in/out. The -resampling algorithm cannot be changed after initialization. +Sample rates can be anything other than zero, and are always specified in hertz. They should be set +to something like 44100, etc. The sample rate is the only configuration property that can be +changed after initialization, but only if the `resampling.allowDynamicSampleRate` member of +`ma_data_converter_config` is set to `MA_TRUE`. To change the sample rate, use +`ma_data_converter_set_rate()` or `ma_data_converter_set_rate_ratio()`. The ratio must be in/out. +The resampling algorithm cannot be changed after initialization. -Processing always happens on a per PCM frame basis and always assumes interleaved input and output. De-interleaved processing is not supported. To process -frames, use `ma_data_converter_process_pcm_frames()`. On input, this function takes the number of output frames you can fit in the output buffer and the number -of input frames contained in the input buffer. On output these variables contain the number of output frames that were written to the output buffer and the -number of input frames that were consumed in the process. You can pass in NULL for the input buffer in which case it will be treated as an infinitely large -buffer of zeros. The output buffer can also be NULL, in which case the processing will be treated as seek. +Processing always happens on a per PCM frame basis and always assumes interleaved input and output. +De-interleaved processing is not supported. To process frames, use +`ma_data_converter_process_pcm_frames()`. On input, this function takes the number of output frames +you can fit in the output buffer and the number of input frames contained in the input buffer. On +output these variables contain the number of output frames that were written to the output buffer +and the number of input frames that were consumed in the process. You can pass in NULL for the +input buffer in which case it will be treated as an infinitely large +buffer of zeros. The output buffer can also be NULL, in which case the processing will be treated +as seek. -Sometimes it's useful to know exactly how many input frames will be required to output a specific number of frames. You can calculate this with -`ma_data_converter_get_required_input_frame_count()`. Likewise, it's sometimes useful to know exactly how many frames would be output given a certain number of -input frames. You can do this with `ma_data_converter_get_expected_output_frame_count()`. +Sometimes it's useful to know exactly how many input frames will be required to output a specific +number of frames. You can calculate this with `ma_data_converter_get_required_input_frame_count()`. +Likewise, it's sometimes useful to know exactly how many frames would be output given a certain +number of input frames. You can do this with `ma_data_converter_get_expected_output_frame_count()`. -Due to the nature of how resampling works, the data converter introduces some latency if resampling is required. This can be retrieved in terms of both the -input rate and the output rate with `ma_data_converter_get_input_latency()` and `ma_data_converter_get_output_latency()`. +Due to the nature of how resampling works, the data converter introduces some latency if resampling +is required. This can be retrieved in terms of both the input rate and the output rate with +`ma_data_converter_get_input_latency()` and `ma_data_converter_get_output_latency()`. -7. Filtering -============ +11. Filtering +============= -7.1. Biquad Filtering ---------------------- +11.1. Biquad Filtering +---------------------- Biquad filtering is achieved with the `ma_biquad` API. Example: ```c @@ -1011,28 +3075,33 @@ Biquad filtering is achieved with the `ma_biquad` API. Example: ma_biquad_process_pcm_frames(&biquad, pFramesOut, pFramesIn, frameCount); ``` -Biquad filtering is implemented using transposed direct form 2. The numerator coefficients are b0, b1 and b2, and the denominator coefficients are a0, a1 and -a2. The a0 coefficient is required and coefficients must not be pre-normalized. +Biquad filtering is implemented using transposed direct form 2. The numerator coefficients are b0, +b1 and b2, and the denominator coefficients are a0, a1 and a2. The a0 coefficient is required and +coefficients must not be pre-normalized. -Supported formats are `ma_format_s16` and `ma_format_f32`. If you need to use a different format you need to convert it yourself beforehand. When using -`ma_format_s16` the biquad filter will use fixed point arithmetic. When using `ma_format_f32`, floating point arithmetic will be used. +Supported formats are `ma_format_s16` and `ma_format_f32`. If you need to use a different format +you need to convert it yourself beforehand. When using `ma_format_s16` the biquad filter will use +fixed point arithmetic. When using `ma_format_f32`, floating point arithmetic will be used. Input and output frames are always interleaved. -Filtering can be applied in-place by passing in the same pointer for both the input and output buffers, like so: +Filtering can be applied in-place by passing in the same pointer for both the input and output +buffers, like so: ```c ma_biquad_process_pcm_frames(&biquad, pMyData, pMyData, frameCount); ``` -If you need to change the values of the coefficients, but maintain the values in the registers you can do so with `ma_biquad_reinit()`. This is useful if you -need to change the properties of the filter while keeping the values of registers valid to avoid glitching. Do not use `ma_biquad_init()` for this as it will -do a full initialization which involves clearing the registers to 0. Note that changing the format or channel count after initialization is invalid and will -result in an error. +If you need to change the values of the coefficients, but maintain the values in the registers you +can do so with `ma_biquad_reinit()`. This is useful if you need to change the properties of the +filter while keeping the values of registers valid to avoid glitching. Do not use +`ma_biquad_init()` for this as it will do a full initialization which involves clearing the +registers to 0. Note that changing the format or channel count after initialization is invalid and +will result in an error. -7.2. Low-Pass Filtering ------------------------ +11.2. Low-Pass Filtering +------------------------ Low-pass filtering is achieved with the following APIs: +---------+------------------------------------------+ @@ -1057,16 +3126,18 @@ Low-pass filter example: ma_lpf_process_pcm_frames(&lpf, pFramesOut, pFramesIn, frameCount); ``` -Supported formats are `ma_format_s16` and` ma_format_f32`. If you need to use a different format you need to convert it yourself beforehand. Input and output -frames are always interleaved. +Supported formats are `ma_format_s16` and` ma_format_f32`. If you need to use a different format +you need to convert it yourself beforehand. Input and output frames are always interleaved. -Filtering can be applied in-place by passing in the same pointer for both the input and output buffers, like so: +Filtering can be applied in-place by passing in the same pointer for both the input and output +buffers, like so: ```c ma_lpf_process_pcm_frames(&lpf, pMyData, pMyData, frameCount); ``` -The maximum filter order is limited to `MA_MAX_FILTER_ORDER` which is set to 8. If you need more, you can chain first and second order filters together. +The maximum filter order is limited to `MA_MAX_FILTER_ORDER` which is set to 8. If you need more, +you can chain first and second order filters together. ```c for (iFilter = 0; iFilter < filterCount; iFilter += 1) { @@ -1074,19 +3145,22 @@ The maximum filter order is limited to `MA_MAX_FILTER_ORDER` which is set to 8. } ``` -If you need to change the configuration of the filter, but need to maintain the state of internal registers you can do so with `ma_lpf_reinit()`. This may be -useful if you need to change the sample rate and/or cutoff frequency dynamically while maintaing smooth transitions. Note that changing the format or channel -count after initialization is invalid and will result in an error. +If you need to change the configuration of the filter, but need to maintain the state of internal +registers you can do so with `ma_lpf_reinit()`. This may be useful if you need to change the sample +rate and/or cutoff frequency dynamically while maintaing smooth transitions. Note that changing the +format or channel count after initialization is invalid and will result in an error. -The `ma_lpf` object supports a configurable order, but if you only need a first order filter you may want to consider using `ma_lpf1`. Likewise, if you only -need a second order filter you can use `ma_lpf2`. The advantage of this is that they're lighter weight and a bit more efficient. +The `ma_lpf` object supports a configurable order, but if you only need a first order filter you +may want to consider using `ma_lpf1`. Likewise, if you only need a second order filter you can use +`ma_lpf2`. The advantage of this is that they're lighter weight and a bit more efficient. -If an even filter order is specified, a series of second order filters will be processed in a chain. If an odd filter order is specified, a first order filter -will be applied, followed by a series of second order filters in a chain. +If an even filter order is specified, a series of second order filters will be processed in a +chain. If an odd filter order is specified, a first order filter will be applied, followed by a +series of second order filters in a chain. -7.3. High-Pass Filtering ------------------------- +11.3. High-Pass Filtering +------------------------- High-pass filtering is achieved with the following APIs: +---------+-------------------------------------------+ @@ -1097,12 +3171,12 @@ High-pass filtering is achieved with the following APIs: | ma_hpf | High order high-pass filter (Butterworth) | +---------+-------------------------------------------+ -High-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_hpf1`, `ma_hpf2` and `ma_hpf`. See example code for low-pass filters -for example usage. +High-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_hpf1`, +`ma_hpf2` and `ma_hpf`. See example code for low-pass filters for example usage. -7.4. Band-Pass Filtering ------------------------- +11.4. Band-Pass Filtering +------------------------- Band-pass filtering is achieved with the following APIs: +---------+-------------------------------+ @@ -1112,13 +3186,14 @@ Band-pass filtering is achieved with the following APIs: | ma_bpf | High order band-pass filter | +---------+-------------------------------+ -Band-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_bpf2` and `ma_hpf`. See example code for low-pass filters for example -usage. Note that the order for band-pass filters must be an even number which means there is no first order band-pass filter, unlike low-pass and high-pass -filters. +Band-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_bpf2` and +`ma_hpf`. See example code for low-pass filters for example usage. Note that the order for +band-pass filters must be an even number which means there is no first order band-pass filter, +unlike low-pass and high-pass filters. -7.5. Notch Filtering --------------------- +11.5. Notch Filtering +--------------------- Notch filtering is achieved with the following APIs: +-----------+------------------------------------------+ @@ -1128,7 +3203,7 @@ Notch filtering is achieved with the following APIs: +-----------+------------------------------------------+ -7.6. Peaking EQ Filtering +11.6. Peaking EQ Filtering ------------------------- Peaking filtering is achieved with the following APIs: @@ -1139,8 +3214,8 @@ Peaking filtering is achieved with the following APIs: +----------+------------------------------------------+ -7.7. Low Shelf Filtering ------------------------- +11.7. Low Shelf Filtering +------------------------- Low shelf filtering is achieved with the following APIs: +-------------+------------------------------------------+ @@ -1149,11 +3224,12 @@ Low shelf filtering is achieved with the following APIs: | ma_loshelf2 | Second order low shelf filter | +-------------+------------------------------------------+ -Where a high-pass filter is used to eliminate lower frequencies, a low shelf filter can be used to just turn them down rather than eliminate them entirely. +Where a high-pass filter is used to eliminate lower frequencies, a low shelf filter can be used to +just turn them down rather than eliminate them entirely. -7.8. High Shelf Filtering -------------------------- +11.8. High Shelf Filtering +-------------------------- High shelf filtering is achieved with the following APIs: +-------------+------------------------------------------+ @@ -1162,18 +3238,20 @@ High shelf filtering is achieved with the following APIs: | ma_hishelf2 | Second order high shelf filter | +-------------+------------------------------------------+ -The high shelf filter has the same API as the low shelf filter, only you would use `ma_hishelf` instead of `ma_loshelf`. Where a low shelf filter is used to -adjust the volume of low frequencies, the high shelf filter does the same thing for high frequencies. +The high shelf filter has the same API as the low shelf filter, only you would use `ma_hishelf` +instead of `ma_loshelf`. Where a low shelf filter is used to adjust the volume of low frequencies, +the high shelf filter does the same thing for high frequencies. -8. Waveform and Noise Generation -================================ +12. Waveform and Noise Generation +================================= -8.1. Waveforms --------------- -miniaudio supports generation of sine, square, triangle and sawtooth waveforms. This is achieved with the `ma_waveform` API. Example: +12.1. Waveforms +--------------- +miniaudio supports generation of sine, square, triangle and sawtooth waveforms. This is achieved +with the `ma_waveform` API. Example: ```c ma_waveform_config config = ma_waveform_config_init( @@ -1195,11 +3273,12 @@ miniaudio supports generation of sine, square, triangle and sawtooth waveforms. ma_waveform_read_pcm_frames(&waveform, pOutput, frameCount); ``` -The amplitude, frequency, type, and sample rate can be changed dynamically with `ma_waveform_set_amplitude()`, `ma_waveform_set_frequency()`, -`ma_waveform_set_type()`, and `ma_waveform_set_sample_rate()` respectively. +The amplitude, frequency, type, and sample rate can be changed dynamically with +`ma_waveform_set_amplitude()`, `ma_waveform_set_frequency()`, `ma_waveform_set_type()`, and +`ma_waveform_set_sample_rate()` respectively. -You can invert the waveform by setting the amplitude to a negative value. You can use this to control whether or not a sawtooth has a positive or negative -ramp, for example. +You can invert the waveform by setting the amplitude to a negative value. You can use this to +control whether or not a sawtooth has a positive or negative ramp, for example. Below are the supported waveform types: @@ -1214,8 +3293,8 @@ Below are the supported waveform types: -8.2. Noise ----------- +12.2. Noise +----------- miniaudio supports generation of white, pink and Brownian noise via the `ma_noise` API. Example: ```c @@ -1237,13 +3316,16 @@ miniaudio supports generation of white, pink and Brownian noise via the `ma_nois ma_noise_read_pcm_frames(&noise, pOutput, frameCount); ``` -The noise API uses simple LCG random number generation. It supports a custom seed which is useful for things like automated testing requiring reproducibility. -Setting the seed to zero will default to `MA_DEFAULT_LCG_SEED`. +The noise API uses simple LCG random number generation. It supports a custom seed which is useful +for things like automated testing requiring reproducibility. Setting the seed to zero will default +to `MA_DEFAULT_LCG_SEED`. -The amplitude, seed, and type can be changed dynamically with `ma_noise_set_amplitude()`, `ma_noise_set_seed()`, and `ma_noise_set_type()` respectively. +The amplitude, seed, and type can be changed dynamically with `ma_noise_set_amplitude()`, +`ma_noise_set_seed()`, and `ma_noise_set_type()` respectively. -By default, the noise API will use different values for different channels. So, for example, the left side in a stereo stream will be different to the right -side. To instead have each channel use the same random value, set the `duplicateChannels` member of the noise config to true, like so: +By default, the noise API will use different values for different channels. So, for example, the +left side in a stereo stream will be different to the right side. To instead have each channel use +the same random value, set the `duplicateChannels` member of the noise config to true, like so: ```c config.duplicateChannels = MA_TRUE; @@ -1261,10 +3343,11 @@ Below are the supported noise types. -9. Audio Buffers -================ -miniaudio supports reading from a buffer of raw audio data via the `ma_audio_buffer` API. This can read from memory that's managed by the application, but -can also handle the memory management for you internally. Memory management is flexible and should support most use cases. +13. Audio Buffers +================= +miniaudio supports reading from a buffer of raw audio data via the `ma_audio_buffer` API. This can +read from memory that's managed by the application, but can also handle the memory management for +you internally. Memory management is flexible and should support most use cases. Audio buffers are initialised using the standard configuration system used everywhere in miniaudio: @@ -1287,11 +3370,14 @@ Audio buffers are initialised using the standard configuration system used every ma_audio_buffer_uninit(&buffer); ``` -In the example above, the memory pointed to by `pExistingData` will *not* be copied and is how an application can do self-managed memory allocation. If you -would rather make a copy of the data, use `ma_audio_buffer_init_copy()`. To uninitialize the buffer, use `ma_audio_buffer_uninit()`. +In the example above, the memory pointed to by `pExistingData` will *not* be copied and is how an +application can do self-managed memory allocation. If you would rather make a copy of the data, use +`ma_audio_buffer_init_copy()`. To uninitialize the buffer, use `ma_audio_buffer_uninit()`. -Sometimes it can be convenient to allocate the memory for the `ma_audio_buffer` structure and the raw audio data in a contiguous block of memory. That is, -the raw audio data will be located immediately after the `ma_audio_buffer` structure. To do this, use `ma_audio_buffer_alloc_and_init()`: +Sometimes it can be convenient to allocate the memory for the `ma_audio_buffer` structure and the +raw audio data in a contiguous block of memory. That is, the raw audio data will be located +immediately after the `ma_audio_buffer` structure. To do this, use +`ma_audio_buffer_alloc_and_init()`: ```c ma_audio_buffer_config config = ma_audio_buffer_config_init( @@ -1312,13 +3398,18 @@ the raw audio data will be located immediately after the `ma_audio_buffer` struc ma_audio_buffer_uninit_and_free(&buffer); ``` -If you initialize the buffer with `ma_audio_buffer_alloc_and_init()` you should uninitialize it with `ma_audio_buffer_uninit_and_free()`. In the example above, -the memory pointed to by `pExistingData` will be copied into the buffer, which is contrary to the behavior of `ma_audio_buffer_init()`. +If you initialize the buffer with `ma_audio_buffer_alloc_and_init()` you should uninitialize it +with `ma_audio_buffer_uninit_and_free()`. In the example above, the memory pointed to by +`pExistingData` will be copied into the buffer, which is contrary to the behavior of +`ma_audio_buffer_init()`. -An audio buffer has a playback cursor just like a decoder. As you read frames from the buffer, the cursor moves forward. The last parameter (`loop`) can be -used to determine if the buffer should loop. The return value is the number of frames actually read. If this is less than the number of frames requested it -means the end has been reached. This should never happen if the `loop` parameter is set to true. If you want to manually loop back to the start, you can do so -with with `ma_audio_buffer_seek_to_pcm_frame(pAudioBuffer, 0)`. Below is an example for reading data from an audio buffer. +An audio buffer has a playback cursor just like a decoder. As you read frames from the buffer, the +cursor moves forward. The last parameter (`loop`) can be used to determine if the buffer should +loop. The return value is the number of frames actually read. If this is less than the number of +frames requested it means the end has been reached. This should never happen if the `loop` +parameter is set to true. If you want to manually loop back to the start, you can do so with with +`ma_audio_buffer_seek_to_pcm_frame(pAudioBuffer, 0)`. Below is an example for reading data from an +audio buffer. ```c ma_uint64 framesRead = ma_audio_buffer_read_pcm_frames(pAudioBuffer, pFramesOut, desiredFrameCount, isLooping); @@ -1327,8 +3418,8 @@ with with `ma_audio_buffer_seek_to_pcm_frame(pAudioBuffer, 0)`. Below is an exam } ``` -Sometimes you may want to avoid the cost of data movement between the internal buffer and the output buffer. Instead you can use memory mapping to retrieve a -pointer to a segment of data: +Sometimes you may want to avoid the cost of data movement between the internal buffer and the +output buffer. Instead you can use memory mapping to retrieve a pointer to a segment of data: ```c void* pMappedFrames; @@ -1344,23 +3435,30 @@ pointer to a segment of data: } ``` -When you use memory mapping, the read cursor is increment by the frame count passed in to `ma_audio_buffer_unmap()`. If you decide not to process every frame -you can pass in a value smaller than the value returned by `ma_audio_buffer_map()`. The disadvantage to using memory mapping is that it does not handle looping -for you. You can determine if the buffer is at the end for the purpose of looping with `ma_audio_buffer_at_end()` or by inspecting the return value of -`ma_audio_buffer_unmap()` and checking if it equals `MA_AT_END`. You should not treat `MA_AT_END` as an error when returned by `ma_audio_buffer_unmap()`. +When you use memory mapping, the read cursor is increment by the frame count passed in to +`ma_audio_buffer_unmap()`. If you decide not to process every frame you can pass in a value smaller +than the value returned by `ma_audio_buffer_map()`. The disadvantage to using memory mapping is +that it does not handle looping for you. You can determine if the buffer is at the end for the +purpose of looping with `ma_audio_buffer_at_end()` or by inspecting the return value of +`ma_audio_buffer_unmap()` and checking if it equals `MA_AT_END`. You should not treat `MA_AT_END` +as an error when returned by `ma_audio_buffer_unmap()`. -10. Ring Buffers +14. Ring Buffers ================ -miniaudio supports lock free (single producer, single consumer) ring buffers which are exposed via the `ma_rb` and `ma_pcm_rb` APIs. The `ma_rb` API operates -on bytes, whereas the `ma_pcm_rb` operates on PCM frames. They are otherwise identical as `ma_pcm_rb` is just a wrapper around `ma_rb`. +miniaudio supports lock free (single producer, single consumer) ring buffers which are exposed via +the `ma_rb` and `ma_pcm_rb` APIs. The `ma_rb` API operates on bytes, whereas the `ma_pcm_rb` +operates on PCM frames. They are otherwise identical as `ma_pcm_rb` is just a wrapper around +`ma_rb`. -Unlike most other APIs in miniaudio, ring buffers support both interleaved and deinterleaved streams. The caller can also allocate their own backing memory for -the ring buffer to use internally for added flexibility. Otherwise the ring buffer will manage it's internal memory for you. +Unlike most other APIs in miniaudio, ring buffers support both interleaved and deinterleaved +streams. The caller can also allocate their own backing memory for the ring buffer to use +internally for added flexibility. Otherwise the ring buffer will manage it's internal memory for +you. -The examples below use the PCM frame variant of the ring buffer since that's most likely the one you will want to use. To initialize a ring buffer, do -something like the following: +The examples below use the PCM frame variant of the ring buffer since that's most likely the one +you will want to use. To initialize a ring buffer, do something like the following: ```c ma_pcm_rb rb; @@ -1370,39 +3468,53 @@ something like the following: } ``` -The `ma_pcm_rb_init()` function takes the sample format and channel count as parameters because it's the PCM varient of the ring buffer API. For the regular -ring buffer that operates on bytes you would call `ma_rb_init()` which leaves these out and just takes the size of the buffer in bytes instead of frames. The -fourth parameter is an optional pre-allocated buffer and the fifth parameter is a pointer to a `ma_allocation_callbacks` structure for custom memory allocation -routines. Passing in `NULL` for this results in `MA_MALLOC()` and `MA_FREE()` being used. +The `ma_pcm_rb_init()` function takes the sample format and channel count as parameters because +it's the PCM varient of the ring buffer API. For the regular ring buffer that operates on bytes you +would call `ma_rb_init()` which leaves these out and just takes the size of the buffer in bytes +instead of frames. The fourth parameter is an optional pre-allocated buffer and the fifth parameter +is a pointer to a `ma_allocation_callbacks` structure for custom memory allocation routines. +Passing in `NULL` for this results in `MA_MALLOC()` and `MA_FREE()` being used. -Use `ma_pcm_rb_init_ex()` if you need a deinterleaved buffer. The data for each sub-buffer is offset from each other based on the stride. To manage your -sub-buffers you can use `ma_pcm_rb_get_subbuffer_stride()`, `ma_pcm_rb_get_subbuffer_offset()` and `ma_pcm_rb_get_subbuffer_ptr()`. +Use `ma_pcm_rb_init_ex()` if you need a deinterleaved buffer. The data for each sub-buffer is +offset from each other based on the stride. To manage your sub-buffers you can use +`ma_pcm_rb_get_subbuffer_stride()`, `ma_pcm_rb_get_subbuffer_offset()` and +`ma_pcm_rb_get_subbuffer_ptr()`. -Use `ma_pcm_rb_acquire_read()` and `ma_pcm_rb_acquire_write()` to retrieve a pointer to a section of the ring buffer. You specify the number of frames you -need, and on output it will set to what was actually acquired. If the read or write pointer is positioned such that the number of frames requested will require -a loop, it will be clamped to the end of the buffer. Therefore, the number of frames you're given may be less than the number you requested. +Use `ma_pcm_rb_acquire_read()` and `ma_pcm_rb_acquire_write()` to retrieve a pointer to a section +of the ring buffer. You specify the number of frames you need, and on output it will set to what +was actually acquired. If the read or write pointer is positioned such that the number of frames +requested will require a loop, it will be clamped to the end of the buffer. Therefore, the number +of frames you're given may be less than the number you requested. -After calling `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()`, you do your work on the buffer and then "commit" it with `ma_pcm_rb_commit_read()` or -`ma_pcm_rb_commit_write()`. This is where the read/write pointers are updated. When you commit you need to pass in the buffer that was returned by the earlier -call to `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()` and is only used for validation. The number of frames passed to `ma_pcm_rb_commit_read()` and -`ma_pcm_rb_commit_write()` is what's used to increment the pointers, and can be less that what was originally requested. +After calling `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()`, you do your work on the +buffer and then "commit" it with `ma_pcm_rb_commit_read()` or `ma_pcm_rb_commit_write()`. This is +where the read/write pointers are updated. When you commit you need to pass in the buffer that was +returned by the earlier call to `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()` and is +only used for validation. The number of frames passed to `ma_pcm_rb_commit_read()` and +`ma_pcm_rb_commit_write()` is what's used to increment the pointers, and can be less that what was +originally requested. -If you want to correct for drift between the write pointer and the read pointer you can use a combination of `ma_pcm_rb_pointer_distance()`, -`ma_pcm_rb_seek_read()` and `ma_pcm_rb_seek_write()`. Note that you can only move the pointers forward, and you should only move the read pointer forward via -the consumer thread, and the write pointer forward by the producer thread. If there is too much space between the pointers, move the read pointer forward. If +If you want to correct for drift between the write pointer and the read pointer you can use a +combination of `ma_pcm_rb_pointer_distance()`, `ma_pcm_rb_seek_read()` and +`ma_pcm_rb_seek_write()`. Note that you can only move the pointers forward, and you should only +move the read pointer forward via the consumer thread, and the write pointer forward by the +producer thread. If there is too much space between the pointers, move the read pointer forward. If there is too little space between the pointers, move the write pointer forward. -You can use a ring buffer at the byte level instead of the PCM frame level by using the `ma_rb` API. This is exactly the same, only you will use the `ma_rb` -functions instead of `ma_pcm_rb` and instead of frame counts you will pass around byte counts. +You can use a ring buffer at the byte level instead of the PCM frame level by using the `ma_rb` +API. This is exactly the same, only you will use the `ma_rb` functions instead of `ma_pcm_rb` and +instead of frame counts you will pass around byte counts. -The maximum size of the buffer in bytes is `0x7FFFFFFF-(MA_SIMD_ALIGNMENT-1)` due to the most significant bit being used to encode a loop flag and the internally -managed buffers always being aligned to MA_SIMD_ALIGNMENT. +The maximum size of the buffer in bytes is `0x7FFFFFFF-(MA_SIMD_ALIGNMENT-1)` due to the most +significant bit being used to encode a loop flag and the internally managed buffers always being +aligned to `MA_SIMD_ALIGNMENT`. -Note that the ring buffer is only thread safe when used by a single consumer thread and single producer thread. +Note that the ring buffer is only thread safe when used by a single consumer thread and single +producer thread. -11. Backends +15. Backends ============ The following backends are supported by miniaudio. @@ -1428,28 +3540,36 @@ The following backends are supported by miniaudio. Some backends have some nuance details you may want to be aware of. -11.1. WASAPI +15.1. WASAPI ------------ -- Low-latency shared mode will be disabled when using an application-defined sample rate which is different to the device's native sample rate. To work around - this, set `wasapi.noAutoConvertSRC` to true in the device config. This is due to IAudioClient3_InitializeSharedAudioStream() failing when the - `AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM` flag is specified. Setting wasapi.noAutoConvertSRC will result in miniaudio's internal resampler being used instead - which will in turn enable the use of low-latency shared mode. +- Low-latency shared mode will be disabled when using an application-defined sample rate which is + different to the device's native sample rate. To work around this, set `wasapi.noAutoConvertSRC` + to true in the device config. This is due to IAudioClient3_InitializeSharedAudioStream() failing + when the `AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM` flag is specified. Setting wasapi.noAutoConvertSRC + will result in miniaudio's internal resampler being used instead which will in turn enable the + use of low-latency shared mode. -11.2. PulseAudio +15.2. PulseAudio ---------------- - If you experience bad glitching/noise on Arch Linux, consider this fix from the Arch wiki: - https://wiki.archlinux.org/index.php/PulseAudio/Troubleshooting#Glitches,_skips_or_crackling. Alternatively, consider using a different backend such as ALSA. + https://wiki.archlinux.org/index.php/PulseAudio/Troubleshooting#Glitches,_skips_or_crackling. + Alternatively, consider using a different backend such as ALSA. -11.3. Android +15.3. Android ------------- -- To capture audio on Android, remember to add the RECORD_AUDIO permission to your manifest: `` -- With OpenSL|ES, only a single ma_context can be active at any given time. This is due to a limitation with OpenSL|ES. -- With AAudio, only default devices are enumerated. This is due to AAudio not having an enumeration API (devices are enumerated through Java). You can however - perform your own device enumeration through Java and then set the ID in the ma_device_id structure (ma_device_id.aaudio) and pass it to ma_device_init(). -- The backend API will perform resampling where possible. The reason for this as opposed to using miniaudio's built-in resampler is to take advantage of any - potential device-specific optimizations the driver may implement. +- To capture audio on Android, remember to add the RECORD_AUDIO permission to your manifest: + `` +- With OpenSL|ES, only a single ma_context can be active at any given time. This is due to a + limitation with OpenSL|ES. +- With AAudio, only default devices are enumerated. This is due to AAudio not having an enumeration + API (devices are enumerated through Java). You can however perform your own device enumeration + through Java and then set the ID in the ma_device_id structure (ma_device_id.aaudio) and pass it + to ma_device_init(). +- The backend API will perform resampling where possible. The reason for this as opposed to using + miniaudio's built-in resampler is to take advantage of any potential device-specific + optimizations the driver may implement. -11.4. UWP +15.4. UWP --------- - UWP only supports default playback and capture devices. - UWP requires the Microphone capability to be enabled in the application's manifest (Package.appxmanifest): @@ -1463,28 +3583,49 @@ Some backends have some nuance details you may want to be aware of. ``` -11.5. Web Audio / Emscripten +15.5. Web Audio / Emscripten ---------------------------- - You cannot use `-std=c*` compiler flags, nor `-ansi`. This only applies to the Emscripten build. -- The first time a context is initialized it will create a global object called "miniaudio" whose primary purpose is to act as a factory for device objects. -- Currently the Web Audio backend uses ScriptProcessorNode's, but this may need to change later as they've been deprecated. -- Google has implemented a policy in their browsers that prevent automatic media output without first receiving some kind of user input. The following web page - has additional details: https://developers.google.com/web/updates/2017/09/autoplay-policy-changes. Starting the device may fail if you try to start playback - without first handling some kind of user input. +- The first time a context is initialized it will create a global object called "miniaudio" whose + primary purpose is to act as a factory for device objects. +- Currently the Web Audio backend uses ScriptProcessorNode's, but this may need to change later as + they've been deprecated. +- Google has implemented a policy in their browsers that prevent automatic media output without + first receiving some kind of user input. The following web page has additional details: + https://developers.google.com/web/updates/2017/09/autoplay-policy-changes. Starting the device + may fail if you try to start playback without first handling some kind of user input. -12. Miscellaneous Notes +16. Optimization Tips +===================== + +16.1. High Level API +-------------------- +- If a sound does not require doppler or pitch shifting, consider disabling pitching by + initializing the sound with the `MA_SOUND_FLAG_NO_PITCH` flag. +- If a sound does not require spatialization, disable it by initialzing the sound with the + `MA_SOUND_FLAG_NO_SPATIALIZATION` flag. It can be renabled again post-initialization with + `ma_sound_set_spatialization_enabled()`. + + + +17. Miscellaneous Notes ======================= -- Automatic stream routing is enabled on a per-backend basis. Support is explicitly enabled for WASAPI and Core Audio, however other backends such as - PulseAudio may naturally support it, though not all have been tested. -- The contents of the output buffer passed into the data callback will always be pre-initialized to silence unless the `noPreZeroedOutputBuffer` config variable - in `ma_device_config` is set to true, in which case it'll be undefined which will require you to write something to the entire buffer. -- By default miniaudio will automatically clip samples. This only applies when the playback sample format is configured as `ma_format_f32`. If you are doing - clipping yourself, you can disable this overhead by setting `noClip` to true in the device config. -- The sndio backend is currently only enabled on OpenBSD builds. -- The audio(4) backend is supported on OpenBSD, but you may need to disable sndiod before you can use it. +- Automatic stream routing is enabled on a per-backend basis. Support is explicitly enabled for + WASAPI and Core Audio, however other backends such as PulseAudio may naturally support it, though + not all have been tested. +- The contents of the output buffer passed into the data callback will always be pre-initialized to + silence unless the `noPreSilencedOutputBuffer` config variable in `ma_device_config` is set to + true, in which case it'll be undefined which will require you to write something to the entire + buffer. +- By default miniaudio will automatically clip samples. This only applies when the playback sample + format is configured as `ma_format_f32`. If you are doing clipping yourself, you can disable this + overhead by setting `noClip` to true in the device config. - Note that GCC and Clang requires `-msse2`, `-mavx2`, etc. for SIMD optimizations. -- When compiling with VC6 and earlier, decoding is restricted to files less than 2GB in size. This is due to 64-bit file APIs not being available. +- The sndio backend is currently only enabled on OpenBSD builds. +- The audio(4) backend is supported on OpenBSD, but you may need to disable sndiod before you can + use it. +- When compiling with VC6 and earlier, decoding is restricted to files less than 2GB in size. This + is due to 64-bit file APIs not being available. */ - diff --git a/vendor/miniaudio/effects.odin b/vendor/miniaudio/effects.odin new file mode 100644 index 000000000..e86d670d9 --- /dev/null +++ b/vendor/miniaudio/effects.odin @@ -0,0 +1,300 @@ +package miniaudio + +import c "core:c/libc" + +when ODIN_OS == .Windows { + foreign import lib "lib/miniaudio.lib" +} else when ODIN_OS == .Linux { + foreign import lib "lib/miniaudio.a" +} else { + foreign import lib "system:miniaudio" +} + +/* +Delay +*/ +delay_config :: struct { + channels: u32, + sampleRate: u32, + delayInFrames: u32, + delayStart: b32, /* Set to true to delay the start of the output; false otherwise. */ + wet: f32, /* 0..1. Default = 1. */ + dry: f32, /* 0..1. Default = 1. */ + decay: f32, /* 0..1. Default = 0 (no feedback). Feedback decay. Use this for echo. */ +} + +delay :: struct { + config: delay_config, + cursor: u32, /* Feedback is written to this cursor. Always equal or in front of the read cursor. */ + bufferSizeInFrames: u32, /* The maximum of config.startDelayInFrames and config.feedbackDelayInFrames. */ + pBuffer: [^]f32, +} + +@(default_calling_convention="c", link_prefix="ma_") +foreign lib { + delay_config_init :: proc(channels, sampleRate, delayInFrames: u32, decay: f32) -> delay_config --- + + delay_init :: proc(pConfig: ^delay_config, pAllocationCallbacks: ^allocation_callbacks, pDelay: ^delay) -> result --- + delay_uninit :: proc(pDelay: ^delay, pAllocationCallbacks: ^allocation_callbacks) --- + delay_process_pcm_frames :: proc(pDelay: ^delay, pFramesOut, pFramesIn: rawptr, frameCount: u32) -> result --- + delay_set_wet :: proc(pDelay: ^delay, value: f32) --- + delay_get_wet :: proc(pDelay: ^delay) -> f32 --- + delay_set_dry :: proc(pDelay: ^delay, value: f32) --- + delay_get_dry :: proc(pDelay: ^delay) -> f32 --- + delay_set_decay :: proc(pDelay: ^delay, value: f32) --- + delay_get_decay :: proc(pDelay: ^delay) -> f32 --- +} + + +/* Gainer for smooth volume changes. */ +gainer_config :: struct { + channels: u32, + smoothTimeInFrames: u32, +} + +gainer :: struct { + config: gainer_config, + t: u32, + pOldGains: [^]f32, + pNewGains: [^]f32, + + /* Memory management. */ + _pHeap: rawptr, + _ownsHeap: b32, +} + +@(default_calling_convention="c", link_prefix="ma_") +foreign lib { + gainer_config_init :: proc(channels, smoothTimeInFrames: u32) -> gainer_config --- + + gainer_get_heap_size :: proc(pConfig: ^gainer_config, pHeapSizeInBytes: ^c.size_t) -> result --- + gainer_init_preallocated :: proc(pConfig: ^gainer_config, pHeap: rawptr, pGainer: ^gainer) -> result --- + gainer_init :: proc(pConfig: ^gainer_config, pAllocationCallbacks: ^allocation_callbacks, pGainer: ^gainer) -> result --- + gainer_uninit :: proc(pGainer: ^gainer, pAllocationCallbacks: ^allocation_callbacks) --- + gainer_process_pcm_frames :: proc(pGainer: ^gainer, pFramesOut: rawptr, pFramesIn: rawptr, frameCount: u64) -> result --- + gainer_set_gain :: proc(pGainer: ^gainer, newGain: f32) -> result --- + gainer_set_gains :: proc(pGainer: ^gainer, pNewGains: [^]f32) -> result --- +} + + +/* Stereo panner. */ +pan_mode :: enum c.int { + balance = 0, /* Does not blend one side with the other. Technically just a balance. Compatible with other popular audio engines and therefore the default. */ + pan, /* A true pan. The sound from one side will "move" to the other side and blend with it. */ +} + +panner_config :: struct { + format: format, + channels: u32, + mode: pan_mode, + pan: f32, +} + +panner :: struct { + format: format, + channels: u32, + mode: pan_mode, + pan: f32, /* -1..1 where 0 is no pan, -1 is left side, +1 is right side. Defaults to 0. */ +} + +@(default_calling_convention="c", link_prefix="ma_") +foreign lib { + panner_config_init :: proc(format: format, channels: u32) -> panner_config --- + + panner_init :: proc(pConfig: ^panner_config, pPanner: ^panner) -> result --- + panner_process_pcm_frames :: proc(pPanner: ^panner, pFramesOut, pFramesIn: rawptr, frameCount: u64) -> result --- + panner_set_mode :: proc(pPanner: ^panner, mode: pan_mode) --- + panner_get_mode :: proc(pPanner: ^panner) -> pan_mode --- + panner_set_pan :: proc(pPanner: ^panner, pan: f32) --- + panner_get_pan :: proc(pPanner: ^panner) -> f32 --- +} + + +/* Fader. */ +fader_config :: struct { + format: format, + channels: u32, + sampleRate: u32, +} + +fader :: struct { + config: fader_config, + volumeBeg: f32, /* If volumeBeg and volumeEnd is equal to 1, no fading happens (ma_fader_process_pcm_frames() will run as a passthrough). */ + volumeEnd: f32, + lengthInFrames: u64, /* The total length of the fade. */ + cursorInFrames: u64, /* The current time in frames. Incremented by ma_fader_process_pcm_frames(). */ +} + +@(default_calling_convention="c", link_prefix="ma_") +foreign lib { + fader_config_init :: proc(format: format, channels, sampleRate: u32) -> fader_config --- + + fader_init :: proc(pConfig: ^fader_config, pFader: ^fader) -> result --- + fader_process_pcm_frames :: proc(pFader: ^fader, pFramesOut, pFramesIn: rawptr, frameCount: u64) -> result --- + fader_get_data_format :: proc(pFader: ^fader, pFormat: ^format, pChannels, pSampleRate: ^u32) --- + fader_set_fade :: proc(pFader: ^fader, volumeBeg, volumeEnd: f32, lengthInFrames: u64) --- + fader_get_current_volume :: proc(pFader: ^fader) -> f32 --- +} + + +/* Spatializer. */ +vec3f :: struct { + x: f32, + y: f32, + z: f32, +} + +attenuation_model :: enum c.int { + none, /* No distance attenuation and no spatialization. */ + inverse, /* Equivalent to OpenAL's AL_INVERSE_DISTANCE_CLAMPED. */ + linear, /* Linear attenuation. Equivalent to OpenAL's AL_LINEAR_DISTANCE_CLAMPED. */ + exponential, /* Exponential attenuation. Equivalent to OpenAL's AL_EXPONENT_DISTANCE_CLAMPED. */ +} + +positioning :: enum c.int { + absolute, + relative, +} + +handedness :: enum c.int { + right, + left, +} + +spatializer_listener_config :: struct { + channelsOut: u32, + pChannelMapOut: [^]channel, + handedness: handedness, /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */ + coneInnerAngleInRadians: f32, + coneOuterAngleInRadians: f32, + coneOuterGain: f32, + speedOfSound: f32, + worldUp: vec3f, +} + +spatializer_listener :: struct { + config: spatializer_listener_config, + position: vec3f, /* The absolute position of the listener. */ + direction: vec3f, /* The direction the listener is facing. The world up vector is config.worldUp. */ + velocity: vec3f, + isEnabled: b32, + + /* Memory management. */ + _ownsHeap: b32, + _pHeap: rawptr, +} + +@(default_calling_convention="c", link_prefix="ma_") +foreign lib { + spatializer_listener_config_init :: proc(channelsOut: u32) -> spatializer_listener_config --- + + spatializer_listener_get_heap_size :: proc(pConfig: ^spatializer_listener_config, pHeapSizeInBytes: ^c.size_t) -> result --- + spatializer_listener_init_preallocated :: proc(pConfig: ^spatializer_listener_config, pHeap: rawptr, pListener: ^spatializer_listener) -> result --- + spatializer_listener_init :: proc(pConfig: ^spatializer_listener_config, pAllocationCallbacks: ^allocation_callbacks, pListener: ^spatializer_listener) -> result --- + spatializer_listener_uninit :: proc(pListener: ^spatializer_listener, pAllocationCallbacks: ^allocation_callbacks) --- + spatializer_listener_get_channel_map :: proc(pListener: ^spatializer_listener) -> ^channel --- + spatializer_listener_set_cone :: proc(pListener: ^spatializer_listener, innerAngleInRadians, outerAngleInRadians, outerGain: f32) --- + spatializer_listener_get_cone :: proc(pListener: ^spatializer_listener, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain: ^f32) --- + spatializer_listener_set_position :: proc(pListener: ^spatializer_listener, x, y, z: f32) --- + spatializer_listener_get_position :: proc(pListener: ^spatializer_listener) -> vec3f --- + spatializer_listener_set_direction :: proc(pListener: ^spatializer_listener, x, y, z: f32) --- + spatializer_listener_get_direction :: proc(pListener: ^spatializer_listener) -> vec3f --- + spatializer_listener_set_velocity :: proc(pListener: ^spatializer_listener, x, y, z: f32) --- + spatializer_listener_get_velocity :: proc(pListener: ^spatializer_listener) -> vec3f --- + spatializer_listener_set_speed_of_sound :: proc(pListener: ^spatializer_listener, speedOfSound: f32) --- + spatializer_listener_get_speed_of_sound :: proc(pListener: ^spatializer_listener) -> f32 --- + spatializer_listener_set_world_up :: proc(pListener: ^spatializer_listener, x, y, z: f32) --- + spatializer_listener_get_world_up :: proc(pListener: ^spatializer_listener) -> vec3f --- + spatializer_listener_set_enabled :: proc(pListener: ^spatializer_listener, isEnabled: b32) --- + spatializer_listener_is_enabled :: proc(pListener: ^spatializer_listener) -> b32 --- +} + +spatializer_config :: struct { + channelsIn: u32, + channelsOut: u32, + pChannelMapIn: [^]channel, + attenuationModel: attenuation_model, + positioning: positioning, + handedness: handedness, /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */ + minGain: f32, + maxGain: f32, + minDistance: f32, + maxDistance: f32, + rolloff: f32, + coneInnerAngleInRadians: f32, + coneOuterAngleInRadians: f32, + coneOuterGain: f32, + dopplerFactor: f32, /* Set to 0 to disable doppler effect. */ + directionalAttenuationFactor: f32, /* Set to 0 to disable directional attenuation. */ + gainSmoothTimeInFrames: u32, /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */ +} + +spatializer :: struct { + channelsIn: u32, + channelsOut: u32, + pChannelMapIn: [^]channel, + attenuationModel: attenuation_model, + positioning: positioning, + handedness: handedness, /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */ + minGain: f32, + maxGain: f32, + minDistance: f32, + maxDistance: f32, + rolloff: f32, + coneInnerAngleInRadians: f32, + coneOuterAngleInRadians: f32, + coneOuterGain: f32, + dopplerFactor: f32, /* Set to 0 to disable doppler effect. */ + directionalAttenuationFactor: f32, /* Set to 0 to disable directional attenuation. */ + gainSmoothTimeInFrames: u32, /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */ + position: vec3f, + direction: vec3f, + velocity: vec3f, /* For doppler effect. */ + dopplerPitch: f32, /* Will be updated by ma_spatializer_process_pcm_frames() and can be used by higher level functions to apply a pitch shift for doppler effect. */ + gainer: gainer, /* For smooth gain transitions. */ + pNewChannelGainsOut: [^]f32, /* An offset of _pHeap. Used by ma_spatializer_process_pcm_frames() to store new channel gains. The number of elements in this array is equal to config.channelsOut. */ + + /* Memory management. */ + _pHeap: rawptr, + _ownsHeap: b32, +} + +@(default_calling_convention="c", link_prefix="ma_") +foreign lib { + spatializer_config_init :: proc(channelsIn, channelsOut: u32) -> spatializer_config --- + + spatializer_get_heap_size :: proc(pConfig: ^spatializer_config, pHeapSizeInBytes: ^c.size_t) -> result --- + spatializer_init_preallocated :: proc(pConfig: ^spatializer_config, pHeap: rawptr, pSpatializer: ^spatializer) -> result --- + spatializer_init :: proc(pConfig: ^spatializer_config, pAllocationCallbacks: ^allocation_callbacks, pSpatializer: ^spatializer) -> result --- + spatializer_uninit :: proc(pSpatializer: ^spatializer, pAllocationCallbacks: ^allocation_callbacks) --- + spatializer_process_pcm_frames :: proc(pSpatializer: ^spatializer, pListener: ^spatializer_listener, pFramesOut, pFramesIn: rawptr, frameCount: u64) -> result --- + spatializer_get_input_channels :: proc(pSpatializer: ^spatializer) -> u32 --- + spatializer_get_output_channels :: proc(pSpatializer: ^spatializer) -> u32 --- + spatializer_set_attenuation_model :: proc(pSpatializer: ^spatializer, attenuationModel: attenuation_model) --- + spatializer_get_attenuation_model :: proc(pSpatializer: ^spatializer) -> attenuation_model --- + spatializer_set_positioning :: proc(pSpatializer: ^spatializer, positioning: positioning) --- + spatializer_get_positioning :: proc(pSpatializer: ^spatializer) -> positioning --- + spatializer_set_rolloff :: proc(pSpatializer: ^spatializer, rolloff: f32) --- + spatializer_get_rolloff :: proc(pSpatializer: ^spatializer) -> f32 --- + spatializer_set_min_gain :: proc(pSpatializer: ^spatializer, minGain: f32) --- + spatializer_get_min_gain :: proc(pSpatializer: ^spatializer) -> f32 --- + spatializer_set_max_gain :: proc(pSpatializer: ^spatializer, maxGain: f32) --- + spatializer_get_max_gain :: proc(pSpatializer: ^spatializer) -> f32 --- + spatializer_set_min_distance :: proc(pSpatializer: ^spatializer, minDistance: f32) --- + spatializer_get_min_distance :: proc(pSpatializer: ^spatializer) -> f32 --- + spatializer_set_max_distance :: proc(pSpatializer: ^spatializer, maxDistance: f32) --- + spatializer_get_max_distance :: proc(pSpatializer: ^spatializer) -> f32 --- + spatializer_set_cone :: proc(pSpatializer: ^spatializer, innerAngleInRadians, outerAngleInRadians, outerGain: f32) --- + spatializer_get_cone :: proc(pSpatializer: ^spatializer, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain: ^f32) --- + spatializer_set_doppler_factor :: proc(pSpatializer: ^spatializer, dopplerFactor: f32) --- + spatializer_get_doppler_factor :: proc(pSpatializer: ^spatializer) -> f32 --- + spatializer_set_directional_attenuation_factor :: proc(pSpatializer: ^spatializer, directionalAttenuationFactor: f32) --- + spatializer_get_directional_attenuation_factor :: proc(pSpatializer: ^spatializer) -> f32 --- + spatializer_set_position :: proc(pSpatializer: ^spatializer, x, y, z: f32) --- + spatializer_get_position :: proc(pSpatializer: ^spatializer) -> vec3f --- + spatializer_set_direction :: proc(pSpatializer: ^spatializer, x, y, z: f32) --- + spatializer_get_direction :: proc(pSpatializer: ^spatializer) -> vec3f --- + spatializer_set_velocity :: proc(pSpatializer: ^spatializer, x, y, z: f32) --- + spatializer_get_velocity :: proc(pSpatializer: ^spatializer) -> vec3f --- + spatializer_get_relative_position_and_direction :: proc(pSpatializer: ^spatializer, pListener: ^spatializer_listener, pRelativePos, pRelativeDir: ^vec3f) --- +} diff --git a/vendor/miniaudio/encoding.odin b/vendor/miniaudio/encoding.odin index 866c19010..ee396466a 100644 --- a/vendor/miniaudio/encoding.odin +++ b/vendor/miniaudio/encoding.odin @@ -2,8 +2,13 @@ package miniaudio import "core:c" -when ODIN_OS == "windows" { foreign import lib "lib/miniaudio.lib" } -when ODIN_OS == "linux" { foreign import lib "lib/miniaudio.a" } +when ODIN_OS == .Windows { + foreign import lib "lib/miniaudio.lib" +} else when ODIN_OS == .Linux { + foreign import lib "lib/miniaudio.a" +} else { + foreign import lib "system:miniaudio" +} /************************************************************************************************************************************************************ @@ -14,14 +19,14 @@ Encoders do not perform any format conversion for you. If your target format doe ************************************************************************************************************************************************************/ -encoder_write_proc :: proc "c" (pEncoder: ^encoder, pBufferIn: rawptr, bytesToWrite: c.size_t) -> c.size_t /* Returns the number of bytes written. */ -encoder_seek_proc :: proc "c" (pEncoder: ^encoder, byteOffset: c.int, origin: seek_origin) -> b32 +encoder_write_proc :: proc "c" (pEncoder: ^encoder, pBufferIn: rawptr, bytesToWrite: c.size_t, pBytesWritten: ^c.size_t) -> result +encoder_seek_proc :: proc "c" (pEncoder: ^encoder, offset: i64, origin: seek_origin) -> result encoder_init_proc :: proc "c" (pEncoder: ^encoder) -> result -encoder_uninit_proc :: proc "c" (pEncoder: ^encoder) -encoder_write_pcm_frames_proc :: proc "c" (pEncoder: ^encoder, pFramesIn: rawptr, frameCount: u64) -> u64 +encoder_uninit_proc :: proc "c" (pEncoder: ^encoder) +encoder_write_pcm_frames_proc :: proc "c" (pEncoder: ^encoder, pFramesIn: rawptr, frameCount: u64, pFramesWritten: ^u64) -> result encoder_config :: struct { - resourceFormat: resource_format, + encodingFormat: encoding_format, format: format, channels: u32, sampleRate: u32, @@ -37,16 +42,23 @@ encoder :: struct { onWritePCMFrames: encoder_write_pcm_frames_proc, pUserData: rawptr, pInternalEncoder: rawptr, /* <-- The drwav/drflac/stb_vorbis/etc. objects. */ - pFile: rawptr, /* FILE*. Only used when initialized with ma_encoder_init_file(). */ + data: struct #raw_union { + vfs: struct { + pVFS: ^vfs, + file: vfs_file, + }, + }, } @(default_calling_convention="c", link_prefix="ma_") foreign lib { - encoder_config_init :: proc(resourceFormat: resource_format, format: format, channels: u32, sampleRate: u32) -> encoder_config --- + encoder_config_init :: proc(encodingFormat: encoding_format, format: format, channels: u32, sampleRate: u32) -> encoder_config --- encoder_init :: proc(onWrite: encoder_write_proc, onSeek: encoder_seek_proc, pUserData: rawptr, pConfig: ^encoder_config, pEncoder: ^encoder) -> result --- + encoder_init_vfs :: proc(pVFS: ^vfs, pFilePath: cstring, pConfig: ^encoder_config, pEncoder: ^encoder) -> result --- + encoder_init_vfs_w :: proc(pVFS: ^vfs, pFilePath: [^]c.wchar_t, pConfig: ^encoder_config, pEncoder: ^encoder) -> result --- encoder_init_file :: proc(pFilePath: cstring, pConfig: ^encoder_config, pEncoder: ^encoder) -> result --- encoder_init_file_w :: proc(pFilePath: [^]c.wchar_t, pConfig: ^encoder_config, pEncoder: ^encoder) -> result --- encoder_uninit :: proc(pEncoder: ^encoder) --- - encoder_write_pcm_frames :: proc(pEncoder: ^encoder, FramesIn: rawptr, frameCount: u64) -> u64 --- -} \ No newline at end of file + encoder_write_pcm_frames :: proc(pEncoder: ^encoder, FramesIn: rawptr, frameCount: u64, pFramesWritten: ^u64) -> result --- +} diff --git a/vendor/miniaudio/engine.odin b/vendor/miniaudio/engine.odin new file mode 100644 index 000000000..935d54744 --- /dev/null +++ b/vendor/miniaudio/engine.odin @@ -0,0 +1,341 @@ +package miniaudio + +import "core:c" + +when ODIN_OS == .Windows { + foreign import lib "lib/miniaudio.lib" +} else when ODIN_OS == .Linux { + foreign import lib "lib/miniaudio.a" +} else { + foreign import lib "system:miniaudio" +} + +/************************************************************************************************************************************************************ + +Engine + +************************************************************************************************************************************************************/ + +/* Sound flags. */ +sound_flags :: enum c.int { + STREAM = 0x00000001, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM */ + DECODE = 0x00000002, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE */ + ASYNC = 0x00000004, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC */ + WAIT_INIT = 0x00000008, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT */ + NO_DEFAULT_ATTACHMENT = 0x00000010, /* Do not attach to the endpoint by default. Useful for when setting up nodes in a complex graph system. */ + NO_PITCH = 0x00000020, /* Disable pitch shifting with ma_sound_set_pitch() and ma_sound_group_set_pitch(). This is an optimization. */ + NO_SPATIALIZATION = 0x00000040, /* Disable spatialization. */ +} + +ENGINE_MAX_LISTENERS :: 4 + +LISTENER_INDEX_CLOSEST :: 255 + +engine_node_type :: enum c.int { + sound, + group, +} + +engine_node_config :: struct { + pEngine: ^engine, + type: engine_node_type, + channelsIn: u32, + channelsOut: u32, + sampleRate: u32, /* Only used when the type is set to ma_engine_node_type_sound. */ + isPitchDisabled: b8, /* Pitching can be explicitly disable with MA_SOUND_FLAG_NO_PITCH to optimize processing. */ + isSpatializationDisabled: b8, /* Spatialization can be explicitly disabled with MA_SOUND_FLAG_NO_SPATIALIZATION. */ + pinnedListenerIndex: u8, /* The index of the listener this node should always use for spatialization. If set to MA_LISTENER_INDEX_CLOSEST the engine will use the closest listener. */ +} + +/* Base node object for both ma_sound and ma_sound_group. */ +engine_node :: struct { + baseNode: node_base, /* Must be the first member for compatiblity with the ma_node API. */ + pEngine: ^engine, /* A pointer to the engine. Set based on the value from the config. */ + sampleRate: u32, /* The sample rate of the input data. For sounds backed by a data source, this will be the data source's sample rate. Otherwise it'll be the engine's sample rate. */ + fader: fader, + resampler: linear_resampler, /* For pitch shift. */ + spatializer: spatializer, + panner: panner, + pitch: f32, /*atomic*/ + oldPitch: f32, /* For determining whether or not the resampler needs to be updated to reflect the new pitch. The resampler will be updated on the mixing thread. */ + oldDopplerPitch: f32, /* For determining whether or not the resampler needs to be updated to take a new doppler pitch into account. */ + isPitchDisabled: b32, /*atomic*/ /* When set to true, pitching will be disabled which will allow the resampler to be bypassed to save some computation. */ + isSpatializationDisabled: b32, /*atomic*/ /* Set to false by default. When set to false, will not have spatialisation applied. */ + pinnedListenerIndex: u32, /*atomic*/ /* The index of the listener this node should always use for spatialization. If set to MA_LISTENER_INDEX_CLOSEST the engine will use the closest listener. */ + + /* Memory management. */ + _ownsHeap: b8, + _pHeap: rawptr, +} + +@(default_calling_convention="c", link_prefix="ma_") +foreign lib { + engine_node_config_init :: proc(pEngine: ^engine, type: engine_node_type, flags: u32) -> engine_node_config --- + + engine_node_get_heap_size :: proc(pConfig: ^engine_node_config, pHeapSizeInBytes: ^c.size_t) -> result --- + engine_node_init_preallocated :: proc(pConfig: ^engine_node_config, pHeap: rawptr, pEngineNode: ^engine_node) -> result --- + engine_node_init :: proc(pConfig: ^engine_node_config, pAllocationCallbacks: ^allocation_callbacks, pEngineNode: ^engine_node) -> result --- + engine_node_uninit :: proc(pEngineNode: ^engine_node, pAllocationCallbacks: ^allocation_callbacks) --- +} + + +SOUND_SOURCE_CHANNEL_COUNT :: 0xFFFFFFFF + +sound_config :: struct { + pFilePath: cstring, /* Set this to load from the resource manager. */ + pFilePathW: [^]c.wchar_t, /* Set this to load from the resource manager. */ + pDataSource: ^data_source, /* Set this to load from an existing data source. */ + pInitialAttachment: ^node, /* If set, the sound will be attached to an input of this node. This can be set to a ma_sound. If set to NULL, the sound will be attached directly to the endpoint unless MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT is set in `flags`. */ + initialAttachmentInputBusIndex: u32, /* The index of the input bus of pInitialAttachment to attach the sound to. */ + channelsIn: u32, /* Ignored if using a data source as input (the data source's channel count will be used always). Otherwise, setting to 0 will cause the engine's channel count to be used. */ + channelsOut: u32, /* Set this to 0 (default) to use the engine's channel count. Set to MA_SOUND_SOURCE_CHANNEL_COUNT to use the data source's channel count (only used if using a data source as input). */ + flags: u32, /* A combination of MA_SOUND_FLAG_* flags. */ + initialSeekPointInPCMFrames: u64, /* Initializes the sound such that it's seeked to this location by default. */ + rangeBegInPCMFrames: u64, + rangeEndInPCMFrames: u64, + loopPointBegInPCMFrames: u64, + loopPointEndInPCMFrames: u64, + isLooping: b32, + pDoneFence: ^fence, /* Released when the resource manager has finished decoding the entire sound. Not used with streams. */ +} + +sound :: struct { + engineNode: engine_node, /* Must be the first member for compatibility with the ma_node API. */ + pDataSource: ^data_source, + seekTarget: u64, /*atomic*/ /* The PCM frame index to seek to in the mixing thread. Set to (~(ma_uint64)0) to not perform any seeking. */ + atEnd: b32, /*atomic*/ + ownsDataSource: b8, + + /* + We're declaring a resource manager data source object here to save us a malloc when loading a + sound via the resource manager, which I *think* will be the most common scenario. + */ + pResourceManagerDataSource: ^resource_manager_data_source, +} + +/* Structure specifically for sounds played with ma_engine_play_sound(). Making this a separate structure to reduce overhead. */ +sound_inlined :: struct { + sound: sound, + pNext: ^sound_inlined, + pPrev: ^sound_inlined, +} + +@(default_calling_convention="c", link_prefix="ma_") +foreign lib { + sound_config_init :: proc() -> sound_config --- + + sound_init_from_file :: proc(pEngine: ^engine, pFilePath: cstring, flags: u32, pGroup: ^sound_group, pDoneFence: ^fence, pSound: ^sound) -> result --- + sound_init_from_file_w :: proc(pEngine: ^engine, pFilePath: [^]c.wchar_t, flags: u32, pGroup: ^sound_group, pDoneFence: ^fence, pSound: ^sound) -> result --- + sound_init_copy :: proc(pEngine: ^engine, pExistingSound: ^sound, flags: u32, pGroup: ^sound_group, pSound: ^sound) -> result --- + sound_init_from_data_source :: proc(pEngine: ^engine, pDataSource: ^data_source, flags: u32, pGroup: ^sound_group, pSound: ^sound) -> result --- + sound_init_ex :: proc(pEngine: ^engine, pConfig: ^sound_config, pSound: ^sound) -> result --- + sound_uninit :: proc(pSound: ^sound) --- + sound_get_engine :: proc(pSound: ^sound) -> ^engine --- + sound_get_data_source :: proc(pSound: ^sound) -> ^data_source --- + sound_start :: proc(pSound: ^sound) -> result --- + sound_stop :: proc(pSound: ^sound) -> result --- + sound_set_volume :: proc(pSound: ^sound, volume: f32) --- + sound_get_volume :: proc(pSound: ^sound) -> f32 --- + sound_set_pan :: proc(pSound: ^sound, pan: f32) --- + sound_get_pan :: proc(pSound: ^sound) -> f32 --- + sound_set_pan_mode :: proc(pSound: ^sound, panMode: pan_mode) --- + sound_get_pan_mode :: proc(pSound: ^sound) -> pan_mode --- + sound_set_pitch :: proc(pSound: ^sound, pitch: f32) --- + sound_get_pitch :: proc(pSound: ^sound) -> f32 --- + sound_set_spatialization_enabled :: proc(pSound: ^sound, enabled: b32) --- + sound_is_spatialization_enabled :: proc(pSound: ^sound) -> b32 --- + sound_set_pinned_listener_index :: proc(pSound: ^sound, listenerIndex: u32) --- + sound_get_pinned_listener_index :: proc(pSound: ^sound) -> u32 --- + sound_get_listener_index :: proc(pSound: ^sound) -> u32 --- + sound_get_direction_to_listener :: proc(pSound: ^sound) -> vec3f --- + sound_set_position :: proc(pSound: ^sound, x, y, z: f32) --- + sound_get_position :: proc(pSound: ^sound) -> vec3f --- + sound_set_direction :: proc(pSound: ^sound, x, y, z: f32) --- + sound_get_direction :: proc(pSound: ^sound) -> vec3f --- + sound_set_velocity :: proc(pSound: ^sound, x, y, z: f32) --- + sound_get_velocity :: proc(pSound: ^sound) -> vec3f --- + sound_set_attenuation_model :: proc(pSound: ^sound, attenuationModel: attenuation_model) --- + sound_get_attenuation_model :: proc(pSound: ^sound) -> attenuation_model --- + sound_set_positioning :: proc(pSound: ^sound, positioning: positioning) --- + sound_get_positioning :: proc(pSound: ^sound) -> positioning --- + sound_set_rolloff :: proc(pSound: ^sound, rolloff: f32) --- + sound_get_rolloff :: proc(pSound: ^sound) -> f32 --- + sound_set_min_gain :: proc(pSound: ^sound, minGain: f32) --- + sound_get_min_gain :: proc(pSound: ^sound) -> f32 --- + sound_set_max_gain :: proc(pSound: ^sound, maxGain: f32) --- + sound_get_max_gain :: proc(pSound: ^sound) -> f32 --- + sound_set_min_distance :: proc(pSound: ^sound, minDistance: f32) --- + sound_get_min_distance :: proc(pSound: ^sound) -> f32 --- + sound_set_max_distance :: proc(pSound: ^sound, maxDistance: f32) --- + sound_get_max_distance :: proc(pSound: ^sound) -> f32 --- + sound_set_cone :: proc(pSound: ^sound, innerAngleInRadians, outerAngleInRadians, outerGain: f32) --- + sound_get_cone :: proc(pSound: ^sound, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain: ^f32) --- + sound_set_doppler_factor :: proc(pSound: ^sound, dopplerFactor: f32) --- + sound_get_doppler_factor :: proc(pSound: ^sound) -> f32 --- + sound_set_directional_attenuation_factor :: proc(pSound: ^sound, directionalAttenuationFactor: f32) --- + sound_get_directional_attenuation_factor :: proc(pSound: ^sound) -> f32 --- + sound_set_fade_in_pcm_frames :: proc(pSound: ^sound, volumeBeg, volumeEnd: f32, fadeLengthInFrames: u64) --- + sound_set_fade_in_milliseconds :: proc(pSound: ^sound, volumeBeg, volumeEnd: f32, fadeLengthInMilliseconds: u64) --- + sound_get_current_fade_volume :: proc(pSound: ^sound) -> f32 --- + sound_set_start_time_in_pcm_frames :: proc(pSound: ^sound, absoluteGlobalTimeInFrames: u64) --- + sound_set_start_time_in_milliseconds :: proc(pSound: ^sound, absoluteGlobalTimeInMilliseconds: u64) --- + sound_set_stop_time_in_pcm_frames :: proc(pSound: ^sound, absoluteGlobalTimeInFrames: u64) --- + sound_set_stop_time_in_milliseconds :: proc(pSound: ^sound, absoluteGlobalTimeInMilliseconds: u64) --- + sound_is_playing :: proc(pSound: ^sound) -> b32 --- + sound_get_time_in_pcm_frames :: proc(pSound: ^sound) -> u64 --- + sound_set_looping :: proc(pSound: ^sound, isLooping: b32) --- + sound_is_looping :: proc(pSound: ^sound) -> b32 --- + sound_at_end :: proc(pSound: ^sound) -> b32 --- + sound_seek_to_pcm_frame :: proc(pSound: ^sound, frameIndex: u64) -> result --- /* Just a wrapper around ma_data_source_seek_to_pcm_frame(). */ + sound_get_data_format :: proc(pSound: ^sound, pFormat: ^format, pChannels, pSampleRate: ^u32, pChannelMap: ^channel, channelMapCap: c.size_t) -> result --- + sound_get_cursor_in_pcm_frames :: proc(pSound: ^sound, pCursor: ^u64) -> result --- + sound_get_length_in_pcm_frames :: proc(pSound: ^sound, pLength: ^u64) -> result --- + sound_get_cursor_in_seconds :: proc(pSound: ^sound, pCursor: ^f32) -> result --- + sound_get_length_in_seconds :: proc(pSound: ^sound, pLength: ^f32) -> result --- +} + + +/* A sound group is just a sound. */ +sound_group_config :: distinct sound_config +sound_group :: distinct sound + +@(default_calling_convention="c", link_prefix="ma_") +foreign lib { + sound_group_config_init :: proc() -> sound_group_config --- + + sound_group_init :: proc(pEngine: ^engine, flags: u32, pParentGroup, pGroup: ^sound_group) -> result --- + sound_group_init_ex :: proc(pEngine: ^engine, pConfig: ^sound_group_config, pGroup: ^sound_group) -> result --- + sound_group_uninit :: proc(pGroup: ^sound_group) --- + sound_group_get_engine :: proc(pGroup: ^sound_group) -> ^engine --- + sound_group_start :: proc(pGroup: ^sound_group) -> result --- + sound_group_stop :: proc(pGroup: ^sound_group) -> result --- + sound_group_set_volume :: proc(pGroup: ^sound_group, volume: f32) --- + sound_group_get_volume :: proc(pGroup: ^sound_group) -> f32 --- + sound_group_set_pan :: proc(pGroup: ^sound_group, pan: f32) --- + sound_group_get_pan :: proc(pGroup: ^sound_group) -> f32 --- + sound_group_set_pan_mode :: proc(pGroup: ^sound_group, panMode: pan_mode) --- + sound_group_get_pan_mode :: proc(pGroup: ^sound_group) -> pan_mode --- + sound_group_set_pitch :: proc(pGroup: ^sound_group, pitch: f32) --- + sound_group_get_pitch :: proc(pGroup: ^sound_group) -> f32 --- + sound_group_set_spatialization_enabled :: proc(pGroup: ^sound_group, enabled: b32) --- + sound_group_is_spatialization_enabled :: proc(pGroup: ^sound_group) -> b32 --- + sound_group_set_pinned_listener_index :: proc(pGroup: ^sound_group, listenerIndex: u32) --- + sound_group_get_pinned_listener_index :: proc(pGroup: ^sound_group) -> u32 --- + sound_group_get_listener_index :: proc(pGroup: ^sound_group) -> u32 --- + sound_group_get_direction_to_listener :: proc(pGroup: ^sound_group) -> vec3f --- + sound_group_set_position :: proc(pGroup: ^sound_group, x, y, z: f32) --- + sound_group_get_position :: proc(pGroup: ^sound_group) -> vec3f --- + sound_group_set_direction :: proc(pGroup: ^sound_group, x, y, z: f32) --- + sound_group_get_direction :: proc(pGroup: ^sound_group) -> vec3f --- + sound_group_set_velocity :: proc(pGroup: ^sound_group, x, y, z: f32) --- + sound_group_get_velocity :: proc(pGroup: ^sound_group) -> vec3f --- + sound_group_set_attenuation_model :: proc(pGroup: ^sound_group, attenuationModel: attenuation_model) --- + sound_group_get_attenuation_model :: proc(pGroup: ^sound_group) -> attenuation_model --- + sound_group_set_positioning :: proc(pGroup: ^sound_group, positioning: positioning) --- + sound_group_get_positioning :: proc(pGroup: ^sound_group) -> positioning --- + sound_group_set_rolloff :: proc(pGroup: ^sound_group, rolloff: f32) --- + sound_group_get_rolloff :: proc(pGroup: ^sound_group) -> f32 --- + sound_group_set_min_gain :: proc(pGroup: ^sound_group, minGain: f32) --- + sound_group_get_min_gain :: proc(pGroup: ^sound_group) -> f32 --- + sound_group_set_max_gain :: proc(pGroup: ^sound_group, maxGain: f32) --- + sound_group_get_max_gain :: proc(pGroup: ^sound_group) -> f32 --- + sound_group_set_min_distance :: proc(pGroup: ^sound_group, minDistance: f32) --- + sound_group_get_min_distance :: proc(pGroup: ^sound_group) -> f32 --- + sound_group_set_max_distance :: proc(pGroup: ^sound_group, maxDistance: f32) --- + sound_group_get_max_distance :: proc(pGroup: ^sound_group) -> f32 --- + sound_group_set_cone :: proc(pGroup: ^sound_group, innerAngleInRadians, outerAngleInRadians, outerGain: f32) --- + sound_group_get_cone :: proc(pGroup: ^sound_group, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain: ^f32) --- + sound_group_set_doppler_factor :: proc(pGroup: ^sound_group, dopplerFactor: f32) --- + sound_group_get_doppler_factor :: proc(pGroup: ^sound_group) -> f32 --- + sound_group_set_directional_attenuation_factor :: proc(pGroup: ^sound_group, directionalAttenuationFactor: f32) --- + sound_group_get_directional_attenuation_factor :: proc(pGroup: ^sound_group) -> f32 --- + sound_group_set_fade_in_pcm_frames :: proc(pGroup: ^sound_group, volumeBeg, volumeEnd: f32, fadeLengthInFrames: u64) --- + sound_group_set_fade_in_milliseconds :: proc(pGroup: ^sound_group, volumeBeg, volumeEnd: f32, fadeLengthInMilliseconds: u64) --- + sound_group_get_current_fade_volume :: proc(pGroup: ^sound_group) -> f32 --- + sound_group_set_start_time_in_pcm_frames :: proc(pGroup: ^sound_group, absoluteGlobalTimeInFrames: u64) --- + sound_group_set_start_time_in_milliseconds :: proc(pGroup: ^sound_group, absoluteGlobalTimeInMilliseconds: u64) --- + sound_group_set_stop_time_in_pcm_frames :: proc(pGroup: ^sound_group, absoluteGlobalTimeInFrames: u64) --- + sound_group_set_stop_time_in_milliseconds :: proc(pGroup: ^sound_group, absoluteGlobalTimeInMilliseconds: u64) --- + sound_group_is_playing :: proc(pGroup: ^sound_group) -> b32 --- + sound_group_get_time_in_pcm_frames :: proc(pGroup: ^sound_group) -> u64 --- +} + + +engine_config :: struct { + pResourceManager: ^resource_manager, /* Can be null in which case a resource manager will be created for you. */ + pContext: ^context_type, + pDevice: ^device, /* If set, the caller is responsible for calling ma_engine_data_callback() in the device's data callback. */ + pPlaybackDeviceID: ^device_id, /* The ID of the playback device to use with the default listener. */ + pLog: ^log, /* When set to NULL, will use the context's log. */ + listenerCount: u32, /* Must be between 1 and MA_ENGINE_MAX_LISTENERS. */ + channels: u32, /* The number of channels to use when mixing and spatializing. When set to 0, will use the native channel count of the device. */ + sampleRate: u32, /* The sample rate. When set to 0 will use the native channel count of the device. */ + periodSizeInFrames: u32, /* If set to something other than 0, updates will always be exactly this size. The underlying device may be a different size, but from the perspective of the mixer that won't matter.*/ + periodSizeInMilliseconds: u32, /* Used if periodSizeInFrames is unset. */ + gainSmoothTimeInFrames: u32, /* The number of frames to interpolate the gain of spatialized sounds across. If set to 0, will use gainSmoothTimeInMilliseconds. */ + gainSmoothTimeInMilliseconds: u32, /* When set to 0, gainSmoothTimeInFrames will be used. If both are set to 0, a default value will be used. */ + allocationCallbacks: allocation_callbacks, + noAutoStart: b32, /* When set to true, requires an explicit call to ma_engine_start(). This is false by default, meaning the engine will be started automatically in ma_engine_init(). */ + noDevice: b32, /* When set to true, don't create a default device. ma_engine_read_pcm_frames() can be called manually to read data. */ + monoExpansionMode: mono_expansion_mode, /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */ + pResourceManagerVFS: ^vfs, /* A pointer to a pre-allocated VFS object to use with the resource manager. This is ignored if pResourceManager is not NULL. */ +} + +engine :: struct { + nodeGraph: node_graph, /* An engine is a node graph. It should be able to be plugged into any ma_node_graph API (with a cast) which means this must be the first member of this struct. */ + pResourceManager: ^resource_manager, + pDevice: ^device, /* Optionally set via the config, otherwise allocated by the engine in ma_engine_init(). */ + pLog: ^log, + sampleRate: u32, + listenerCount: u32, + listeners: [ENGINE_MAX_LISTENERS]spatializer_listener, + allocationCallbacks: allocation_callbacks, + ownsResourceManager: b8, + ownsDevice: b8, + inlinedSoundLock: spinlock, /* For synchronizing access so the inlined sound list. */ + pInlinedSoundHead: ^sound_inlined, /* The first inlined sound. Inlined sounds are tracked in a linked list. */ + inlinedSoundCount: u32, /*atomic*/ /* The total number of allocated inlined sound objects. Used for debugging. */ + gainSmoothTimeInFrames: u32, /* The number of frames to interpolate the gain of spatialized sounds across. */ + monoExpansionMode: mono_expansion_mode, +} + +@(default_calling_convention="c", link_prefix="ma_") +foreign lib { + engine_config_init :: proc() -> engine_config --- + + engine_init :: proc(pConfig: ^engine_config, pEngine: ^engine) -> result --- + engine_uninit :: proc(pEngine: ^engine) --- + engine_read_pcm_frames :: proc(pEngine: ^engine, pFramesOut: rawptr, frameCount: u64, pFramesRead: ^u64) -> result --- + engine_get_node_graph :: proc(pEngine: ^engine) -> ^node_graph --- + engine_get_resource_manager :: proc(pEngine: ^engine) -> ^resource_manager --- + engine_get_device :: proc(pEngine: ^engine) -> ^device --- + engine_get_log :: proc(pEngine: ^engine) -> ^log --- + engine_get_endpoint :: proc(pEngine: ^engine) -> ^node --- + engine_get_time :: proc(pEngine: ^engine) -> u64 --- + engine_set_time :: proc(pEngine: ^engine, globalTime: u64) -> result --- + engine_get_channels :: proc(pEngine: ^engine) -> u32 --- + engine_get_sample_rate :: proc(pEngine: ^engine) -> u32 --- + + engine_start :: proc(pEngine: ^engine) -> result --- + engine_stop :: proc(pEngine: ^engine) -> result --- + engine_set_volume :: proc(pEngine: ^engine, volume: f32) -> result --- + engine_set_gain_db :: proc(pEngine: ^engine, gainDB: f32) -> result --- + + engine_get_listener_count :: proc(pEngine: ^engine) -> u32 --- + engine_find_closest_listener :: proc(pEngine: ^engine, absolutePosX, absolutePosY, absolutePosZ: f32) -> u32 --- + engine_listener_set_position :: proc(pEngine: ^engine, listenerIndex: u32, x, y, z: f32) --- + engine_listener_get_position :: proc(pEngine: ^engine, listenerIndex: u32) -> vec3f --- + engine_listener_set_direction :: proc(pEngine: ^engine, listenerIndex: u32, x, y, z: f32) --- + engine_listener_get_direction :: proc(pEngine: ^engine, listenerIndex: u32) -> vec3f --- + engine_listener_set_velocity :: proc(pEngine: ^engine, listenerIndex: u32, x, y, z: f32) --- + engine_listener_get_velocity :: proc(pEngine: ^engine, listenerIndex: u32) -> vec3f --- + engine_listener_set_cone :: proc(pEngine: ^engine, listenerIndex: u32, innerAngleInRadians, outerAngleInRadians, outerGain: f32) --- + engine_listener_get_cone :: proc(pEngine: ^engine, listenerIndex: u32, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain: ^f32) --- + engine_listener_set_world_up :: proc(pEngine: ^engine, listenerIndex: u32, x, y, z: f32) --- + engine_listener_get_world_up :: proc(pEngine: ^engine, listenerIndex: u32) -> vec3f --- + engine_listener_set_enabled :: proc(pEngine: ^engine, listenerIndex: u32, isEnabled: b32) --- + engine_listener_is_enabled :: proc(pEngine: ^engine, listenerIndex: u32) -> b32 --- + + engine_play_sound_ex :: proc(pEngine: ^engine, pFilePath: cstring, pNode: ^node, nodeInputBusIndex: u32) -> result --- + engine_play_sound :: proc(pEngine: ^engine, pFilePath: cstring, pGroup: ^sound_group) -> result --- /* Fire and forget. */ +} diff --git a/vendor/miniaudio/filtering.odin b/vendor/miniaudio/filtering.odin index fec21f33d..b8175c372 100644 --- a/vendor/miniaudio/filtering.odin +++ b/vendor/miniaudio/filtering.odin @@ -1,7 +1,14 @@ package miniaudio -when ODIN_OS == "windows" { foreign import lib "lib/miniaudio.lib" } -when ODIN_OS == "linux" { foreign import lib "lib/miniaudio.a" } +import c "core:c/libc" + +when ODIN_OS == .Windows { + foreign import lib "lib/miniaudio.lib" +} else when ODIN_OS == .Linux { + foreign import lib "lib/miniaudio.a" +} else { + foreign import lib "system:miniaudio" +} /************************************************************************************************************************************************************** @@ -14,14 +21,14 @@ biquad_coefficient :: struct #raw_union { } biquad_config :: struct { - format: format, + format: format, channels: u32, - b0: f64, - b1: f64, - b2: f64, - a0: f64, - a1: f64, - a2: f64, + b0: f64, + b1: f64, + b2: f64, + a0: f64, + a1: f64, + a2: f64, } biquad :: struct { @@ -32,17 +39,25 @@ biquad :: struct { b2: biquad_coefficient, a1: biquad_coefficient, a2: biquad_coefficient, - r1: [MAX_CHANNELS]biquad_coefficient, - r2: [MAX_CHANNELS]biquad_coefficient, + pR1: ^biquad_coefficient, + pR2: ^biquad_coefficient, + + /* Memory management. */ + _pHeap: rawptr, + _ownsHeap: b32, } @(default_calling_convention="c", link_prefix="ma_") foreign lib { biquad_config_init :: proc(format: format, channels: u32, b0, b1, b2, a0, a1, a2: f64) -> biquad_config --- - biquad_init :: proc(pConfig: ^biquad_config, pBQ: ^biquad) -> result --- + biquad_get_heap_size :: proc(pConfig: ^biquad_config, pHeapSizeInBytes: ^c.size_t) -> result --- + biquad_init_preallocated :: proc(pConfig: ^biquad_config, pHeap: rawptr, pBQ: ^biquad) -> result --- + biquad_init :: proc(pConfig: ^biquad_config, pAllocationCallbacks: ^allocation_callbacks, pBQ: ^biquad) -> result --- + biquad_uninit :: proc(pBQ: ^biquad, pAllocationCallbacks: ^allocation_callbacks) --- biquad_reinit :: proc(pConfig: ^biquad_config, pBQ: ^biquad) -> result --- - biquad_process_pcm_frames :: proc(pBQ: ^biquad, pFramesOut: rawptr, pFramesIn: rawptr, frameCount: u64) -> result --- + biquad_clear_cache :: proc(pBQ: ^biquad) -> result --- + biquad_process_pcm_frames :: proc(pBQ: ^biquad, pFramesOut, pFramesIn: rawptr, frameCount: u64) -> result --- biquad_get_latency :: proc(pBQ: ^biquad) -> u32 --- } @@ -65,7 +80,11 @@ lpf1 :: struct { format: format, channels: u32, a: biquad_coefficient, - r1: [MAX_CHANNELS]biquad_coefficient, + pR1: ^biquad_coefficient, + + /* Memory management. */ + _pHeap: rawptr, + _ownsHeap: b32, } lpf2 :: struct { @@ -86,8 +105,12 @@ lpf :: struct { sampleRate: u32, lpf1Count: u32, lpf2Count: u32, - lpf1: [1]lpf1, - lpf2: [MAX_FILTER_ORDER/2]lpf2, + pLPF1: ^lpf1, + pLPF2: ^lpf2, + + /* Memory management. */ + _pHeap: rawptr, + _ownsHeap: b32, } @@ -96,20 +119,32 @@ foreign lib { lpf1_config_init :: proc(format: format, channels: u32, sampleRate: u32, cutoffFrequency: f64) -> lpf1_config --- lpf2_config_init :: proc(format: format, channels: u32, sampleRate: u32, cutoffFrequency, q: f64) -> lpf2_config --- - lpf1_init :: proc(pConfig: ^lpf1_config, pLPF: ^lpf1) -> result --- + lpf1_get_heap_size :: proc(pConfig: ^lpf1_config, pHeapSizeInBytes: ^c.size_t) -> result --- + lpf1_init_preallocated :: proc(pConfig: ^lpf1_config, pHeap: rawptr, pLPF: ^lpf1) -> result --- + lpf1_init :: proc(pConfig: ^lpf1_config, pAllocationCallbacks: ^allocation_callbacks, pLPF: ^lpf1) -> result --- + lpf1_uninit :: proc(pLPF: ^lpf1, pAllocationCallbacks: ^allocation_callbacks) --- lpf1_reinit :: proc(pConfig: ^lpf1_config, pLPF: ^lpf1) -> result --- + lpf1_clear_cache :: proc(pLPF: ^lpf1) -> result --- lpf1_process_pcm_frames :: proc(pLPF: ^lpf1, pFramesOut: rawptr, pFramesIn: rawptr, frameCount: u64) -> result --- lpf1_get_latency :: proc(pLPF: ^lpf1) -> u32 --- - lpf2_init :: proc(pConfig: ^lpf2_config, pLPF: ^lpf2) -> result --- + lpf2_get_heap_size :: proc(pConfig: ^lpf2_config, pHeapSizeInBytes: ^c.size_t) -> result --- + lpf2_init_preallocated :: proc(pConfig: ^lpf2_config, pHeap: rawptr, pHPF: ^lpf2) -> result --- + lpf2_init :: proc(pConfig: ^lpf2_config, pAllocationCallbacks: ^allocation_callbacks, pLPF: ^lpf2) -> result --- + lpf2_uninit :: proc(pLPF: ^lpf2, pAllocationCallbacks: ^allocation_callbacks) --- lpf2_reinit :: proc(pConfig: ^lpf2_config, pLPF: ^lpf2) -> result --- + lpf2_clear_cache :: proc(pLPF: ^lpf2) -> result --- lpf2_process_pcm_frames :: proc(pLPF: ^lpf2, pFramesOut: rawptr, pFramesIn: rawptr, frameCount: u64) -> result --- lpf2_get_latency :: proc(pLPF: ^lpf2) -> u32 --- lpf_config_init :: proc(format: format, channels: u32, sampleRate: u32, cutoffFrequency: f64, order: u32) -> lpf_config --- - lpf_init :: proc(pConfig: ^lpf_config, pLPF: ^lpf) -> result --- + lpf_get_heap_size :: proc(pConfig: ^lpf_config, pHeapSizeInBytes: ^c.size_t) -> result --- + lpf_init_preallocated :: proc(pConfig: ^lpf_config, pHeap: rawptr, pLPF: ^lpf) -> result --- + lpf_init :: proc(pConfig: ^lpf_config, pAllocationCallbacks: ^allocation_callbacks, pLPF: ^lpf) -> result --- + lpf_uninit :: proc(pLPF: ^lpf, pAllocationCallbacks: ^allocation_callbacks) --- lpf_reinit :: proc(pConfig: ^lpf_config, pLPF: ^lpf) -> result --- + lpf_clear_cache :: proc(pLPF: ^lpf) -> result --- lpf_process_pcm_frames :: proc(pLPF: ^lpf, pFramesOut: rawptr, pFramesIn: rawptr, frameCount: u64) -> result --- lpf_get_latency :: proc(pLPF: ^lpf) -> u32 --- } @@ -133,7 +168,11 @@ hpf1 :: struct { format: format, channels: u32, a: biquad_coefficient, - r1: [MAX_CHANNELS]biquad_coefficient, + pR1: ^biquad_coefficient, + + /* Memory management. */ + _pHeap: rawptr, + _ownsHeap: b32, } hpf2 :: struct { @@ -154,8 +193,12 @@ hpf :: struct { sampleRate: u32, hpf1Count: u32, hpf2Count: u32, - hpf1: [1]hpf1, - hpf2: [MAX_FILTER_ORDER/2]hpf2, + pHPF1: ^hpf1, + pHPF2: ^hpf2, + + /* Memory management. */ + _pHeap: rawptr, + _ownsHeap: b32, } @@ -164,19 +207,28 @@ foreign lib { hpf1_config_init :: proc(format: format, channels: u32, sampleRate: u32, cutoffFrequency: f64) -> hpf1_config --- hpf2_config_init :: proc(format: format, channels: u32, sampleRate: u32, cutoffFrequency, q: f64) -> hpf2_config --- - hpf1_init :: proc(pConfig: ^hpf1_config, pHPF: ^hpf1) -> result --- + hpf1_get_heap_size :: proc(pConfig: ^hpf1_config, pHeapSizeInBytes: ^c.size_t) -> result --- + hpf1_init_preallocated :: proc(pConfig: ^hpf1_config, pHeap: rawptr, pLPF: ^hpf1) -> result --- + hpf1_init :: proc(pConfig: ^hpf1_config, pAllocationCallbacks: ^allocation_callbacks, pHPF: ^hpf1) -> result --- + hpf1_uninit :: proc(pHPF: ^hpf1, pAllocationCallbacks: ^allocation_callbacks) --- hpf1_reinit :: proc(pConfig: ^hpf1_config, pHPF: ^hpf1) -> result --- hpf1_process_pcm_frames :: proc(pHPF: ^hpf1, pFramesOut: rawptr, pFramesIn: rawptr, frameCount: u64) -> result --- hpf1_get_latency :: proc(pHPF: ^hpf1) -> u32 --- - hpf2_init :: proc(pConfig: ^hpf2_config, pHPF: ^hpf2) -> result --- + hpf2_get_heap_size :: proc(pConfig: ^hpf2_config, pHeapSizeInBytes: ^c.size_t) -> result --- + hpf2_init_preallocated :: proc(pConfig: ^hpf2_config, pHeap: rawptr, pHPF: ^hpf2) -> result --- + hpf2_init :: proc(pConfig: ^hpf2_config, pAllocationCallbacks: ^allocation_callbacks, pHPF: ^hpf2) -> result --- + hpf2_uninit :: proc(pHPF: ^hpf2, pAllocationCallbacks: ^allocation_callbacks) --- hpf2_reinit :: proc(pConfig: ^hpf2_config, pHPF: ^hpf2) -> result --- hpf2_process_pcm_frames :: proc(pHPF: ^hpf2, pFramesOut: rawptr, pFramesIn: rawptr, frameCount: u64) -> result --- hpf2_get_latency :: proc(pHPF: ^hpf2) -> u32 --- hpf_config_init :: proc(format: format, channels: u32, sampleRate: u32, cutoffFrequency: f64, order: u32) -> hpf_config --- - hpf_init :: proc(pConfig: ^hpf_config, pHPF: ^hpf) -> result --- + hpf_get_heap_size :: proc(pConfig: ^hpf_config, pHeapSizeInBytes: ^c.size_t) -> result --- + hpf_init_preallocated :: proc(pConfig: ^hpf_config, pHeap: rawptr, pLPF: ^hpf) -> result --- + hpf_init :: proc(pConfig: ^hpf_config, pAllocationCallbacks: ^allocation_callbacks, pHPF: ^hpf) -> result --- + hpf_uninit :: proc(pHPF: ^hpf, pAllocationCallbacks: ^allocation_callbacks) --- hpf_reinit :: proc(pConfig: ^hpf_config, pHPF: ^hpf) -> result --- hpf_process_pcm_frames :: proc(pHPF: ^hpf, pFramesOut: rawptr, pFramesIn: rawptr, frameCount: u64) -> result --- hpf_get_latency :: proc(pHPF: ^hpf) -> u32 --- @@ -212,21 +264,31 @@ bpf :: struct { format: format, channels: u32, bpf2Count: u32, - bpf2: [MAX_FILTER_ORDER/2]bpf2, + pBPF2: ^bpf2, + + /* Memory management. */ + _pHeap: rawptr, + _ownsHeap: b32, } @(default_calling_convention="c", link_prefix="ma_") foreign lib { bpf2_config_init :: proc(format: format, channels: u32, sampleRate: u32, cutoffFrequency: f64, q: f64) -> bpf2_config --- - bpf2_init :: proc(pConfig: ^bpf2_config, pBPF: ^bpf2) -> result --- + bpf2_get_heap_size :: proc(pConfig: ^bpf2_config, pHeapSizeInBytes: ^c.size_t) -> result --- + bpf2_init_preallocated :: proc(pConfig: ^bpf2_config, pHeap: rawptr, pBPF: ^bpf2) -> result --- + bpf2_init :: proc(pConfig: ^bpf2_config, pAllocationCallbacks: ^allocation_callbacks, pBPF: ^bpf2) -> result --- + bpf2_uninit :: proc(pBPF: ^bpf2, pAllocationCallbacks: ^allocation_callbacks) --- bpf2_reinit :: proc(pConfig: ^bpf2_config, pBPF: ^bpf2) -> result --- bpf2_process_pcm_frames :: proc(pBPF: ^bpf2, pFramesOut: rawptr, pFramesIn: rawptr, frameCount: u64) -> result --- bpf2_get_latency :: proc(pBPF: ^bpf2) -> u32 --- bpf_config_init :: proc(format: format, channels: u32, sampleRate: u32, cutoffFrequency: f64, order: u32) -> bpf_config --- - bpf_init :: proc(pConfig: ^bpf_config, pBPF: ^bpf) -> result --- + bpf_get_heap_size :: proc(pConfig: ^bpf_config, pHeapSizeInBytes: ^c.size_t) -> result --- + bpf_init_preallocated :: proc(pConfig: ^bpf_config, pHeap: rawptr, pBPF: ^bpf) -> result --- + bpf_init :: proc(pConfig: ^bpf_config, pAllocationCallbacks: ^allocation_callbacks, pBPF: ^bpf) -> result --- + bpf_uninit :: proc(pBPF: ^bpf, pAllocationCallbacks: ^allocation_callbacks) --- bpf_reinit :: proc(pConfig: ^bpf_config, pBPF: ^bpf) -> result --- bpf_process_pcm_frames :: proc(pBPF: ^bpf, pFramesOut: rawptr, pFramesIn: rawptr, frameCount: u64) -> result --- bpf_get_latency :: proc(pBPF: ^bpf) -> u32 --- @@ -255,7 +317,10 @@ notch2 :: struct { foreign lib { notch2_config_init :: proc(format: format, channels: u32, sampleRate: u32, q: f64, frequency: f64) -> notch2_config --- - notch2_init :: proc(pConfig: ^notch2_config, pFilter: ^notch2) -> result --- + notch2_get_heap_size :: proc(pConfig: ^notch2_config, pHeapSizeInBytes: ^c.size_t) -> result --- + notch2_init_preallocated :: proc(pConfig: ^notch2_config, pHeap: rawptr, pFilter: ^notch2) -> result --- + notch2_init :: proc(pConfig: ^notch2_config, pAllocationCallbacks: ^allocation_callbacks, pFilter: ^notch2) -> result --- + notch2_uninit :: proc(pFilter: ^notch2, pAllocationCallbacks: ^allocation_callbacks) --- notch2_reinit :: proc(pConfig: ^notch2_config, pFilter: ^notch2) -> result --- notch2_process_pcm_frames :: proc(pFilter: ^notch2, pFramesOut: rawptr, pFramesIn: rawptr, frameCount: u64) -> result --- notch2_get_latency :: proc(pFilter: ^notch2) -> u32 --- @@ -285,7 +350,10 @@ peak2 :: struct { foreign lib { peak2_config_init :: proc(format: format, channels: u32, sampleRate: u32, gainDB, q, frequency: f64) -> peak2_config --- - peak2_init :: proc(pConfig: ^peak2_config, pFilter: ^peak2) -> result --- + peak2_get_heap_size :: proc(pConfig: ^peak2_config, pHeapSizeInBytes: ^c.size_t) -> result --- + peak2_init_preallocated :: proc(pConfig: ^peak2_config, pHeap: rawptr, pFilter: ^peak2) -> result --- + peak2_init :: proc(pConfig: ^peak2_config, pAllocationCallbacks: ^allocation_callbacks, pFilter: ^peak2) -> result --- + peak2_uninit :: proc(pFilter: ^peak2, pAllocationCallbacks: ^allocation_callbacks) --- peak2_reinit :: proc(pConfig: ^peak2_config, pFilter: ^peak2) -> result --- peak2_process_pcm_frames :: proc(pFilter: ^peak2, pFramesOut: rawptr, pFramesIn: rawptr, frameCount: u64) -> result --- peak2_get_latency :: proc(pFilter: ^peak2) -> u32 --- @@ -315,7 +383,10 @@ loshelf2 :: struct { foreign lib { loshelf2_config_init :: proc(format: format, channels: u32, sampleRate: u32, gainDB, shelfSlope, frequency: f64) -> loshelf2_config --- - loshelf2_init :: proc(pConfig: ^loshelf2_config, pFilter: ^loshelf2) -> result --- + loshelf2_get_heap_size :: proc(pConfig: ^loshelf2_config, pHeapSizeInBytes: ^c.size_t) -> result --- + loshelf2_init_preallocated :: proc(pConfig: ^loshelf2_config, pHeap: rawptr, pFilter: ^loshelf2) -> result --- + loshelf2_init :: proc(pConfig: ^loshelf2_config, pAllocationCallbacks: ^allocation_callbacks, pFilter: ^loshelf2) -> result --- + loshelf2_uninit :: proc(pFilter: ^loshelf2, pAllocationCallbacks: ^allocation_callbacks) --- loshelf2_reinit :: proc(pConfig: ^loshelf2_config, pFilter: ^loshelf2) -> result --- loshelf2_process_pcm_frames :: proc(pFilter: ^loshelf2, pFramesOut: rawptr, pFramesIn: rawptr, frameCount: u64) -> result --- loshelf2_get_latency :: proc(pFilter: ^loshelf2) -> u32 --- @@ -345,7 +416,10 @@ hishelf2 :: struct { foreign lib { hishelf2_config_init :: proc(format: format, channels: u32, sampleRate: u32, gainDB, shelfSlope, frequency: f64) -> hishelf2_config --- - hishelf2_init :: proc(pConfig: ^hishelf2_config, pFilter: ^hishelf2) -> result --- + hishelf2_get_heap_size :: proc(pConfig: ^hishelf2_config, pHeapSizeInBytes: ^c.size_t) -> result --- + hishelf2_init_preallocated :: proc(pConfig: ^hishelf2_config, pHeap: rawptr, pFilter: ^hishelf2) -> result --- + hishelf2_init :: proc(pConfig: ^hishelf2_config, pAllocationCallbacks: ^allocation_callbacks, pFilter: ^hishelf2) -> result --- + hishelf2_uninit :: proc(pFilter: ^hishelf2, pAllocationCallbacks: ^allocation_callbacks) --- hishelf2_reinit :: proc(pConfig: ^hishelf2_config, pFilter: ^hishelf2) -> result --- hishelf2_process_pcm_frames :: proc(pFilter: ^hishelf2, pFramesOut: rawptr, pFramesIn: rawptr, frameCount: u64) -> result --- hishelf2_get_latency :: proc(pFilter: ^hishelf2) -> u32 --- diff --git a/vendor/miniaudio/generation.odin b/vendor/miniaudio/generation.odin index c2009967c..305090c7d 100644 --- a/vendor/miniaudio/generation.odin +++ b/vendor/miniaudio/generation.odin @@ -2,8 +2,13 @@ package miniaudio import "core:c" -when ODIN_OS == "windows" { foreign import lib "lib/miniaudio.lib" } -when ODIN_OS == "linux" { foreign import lib "lib/miniaudio.a" } +when ODIN_OS == .Windows { + foreign import lib "lib/miniaudio.lib" +} else when ODIN_OS == .Linux { + foreign import lib "lib/miniaudio.a" +} else { + foreign import lib "system:miniaudio" +} waveform_type :: enum c.int { sine, @@ -51,14 +56,18 @@ noise :: struct { lcg: lcg, state: struct #raw_union { pink: struct { - bin: [MAX_CHANNELS][16]f64, - accumulation: [MAX_CHANNELS]f64, - counter: [MAX_CHANNELS]u32, + bin: ^[^]f64, + accumulation: [^]f64, + counter: [^]u32, }, brownian: struct { - accumulation: [MAX_CHANNELS]f64, + accumulation: [^]f64, }, }, + + /* Memory management. */ + _pHeap: rawptr, + _ownsHeap: b32, } @(default_calling_convention="c", link_prefix="ma_") @@ -67,7 +76,7 @@ foreign lib { waveform_init :: proc(pConfig: ^waveform_config, pWaveform: ^waveform) -> result --- waveform_uninit :: proc(pWaveform: ^waveform) --- - waveform_read_pcm_frames :: proc(pWaveform: ^waveform, pFramesOut: rawptr, frameCount: u64) -> u64 --- + waveform_read_pcm_frames :: proc(pWaveform: ^waveform, pFramesOut: rawptr, frameCount: u64, pFramesRead: ^u64) -> result --- waveform_seek_to_pcm_frame :: proc(pWaveform: ^waveform, frameIndex: u64) -> result --- waveform_set_amplitude :: proc(pWaveform: ^waveform, amplitude: f64) -> result --- waveform_set_frequency :: proc(pWaveform: ^waveform, frequency: f64) -> result --- @@ -76,10 +85,12 @@ foreign lib { noise_config_init :: proc(format: format, channels: u32, type: noise_type, seed: i32, amplitude: f64) -> noise_config --- - noise_init :: proc(pConfig: ^noise_config, pNoise: ^noise) -> result --- - noise_uninit :: proc(pNoise: ^noise) --- - noise_read_pcm_frames :: proc(pNoise: ^noise, pFramesOut: rawptr, frameCount: u64) -> u64 --- - noise_set_amplitude :: proc(pNoise: ^noise, amplitude: f64) -> result --- - noise_set_seed :: proc(pNoise: ^noise, seed: i32) -> result --- - noise_set_type :: proc(pNoise: ^noise, type: noise_type) -> result --- -} \ No newline at end of file + noise_get_heap_size :: proc(pConfig: ^noise_config, pHeapSizeInBytes: ^c.size_t) -> result --- + noise_init_preallocated :: proc(pConfig: ^noise_config, pHeap: rawptr, pNoise: ^noise) -> result --- + noise_init :: proc(pConfig: ^noise_config, pAllocationCallbacks: ^allocation_callbacks, pNoise: ^noise) -> result --- + noise_uninit :: proc(pNoise: ^noise, pAllocationCallbacks: ^allocation_callbacks) --- + noise_read_pcm_frames :: proc(pNoise: ^noise, pFramesOut: rawptr, frameCount: u64, pFramesRead: ^u64) -> result --- + noise_set_amplitude :: proc(pNoise: ^noise, amplitude: f64) -> result --- + noise_set_seed :: proc(pNoise: ^noise, seed: i32) -> result --- + noise_set_type :: proc(pNoise: ^noise, type: noise_type) -> result --- +} diff --git a/vendor/miniaudio/job_queue.odin b/vendor/miniaudio/job_queue.odin new file mode 100644 index 000000000..99899fdbd --- /dev/null +++ b/vendor/miniaudio/job_queue.odin @@ -0,0 +1,239 @@ +package miniaudio + +import c "core:c/libc" + +when ODIN_OS == .Windows { + foreign import lib "lib/miniaudio.lib" +} else when ODIN_OS == .Linux { + foreign import lib "lib/miniaudio.a" +} else { + foreign import lib "system:miniaudio" +} + +/* +Slot Allocator +-------------- +The idea of the slot allocator is for it to be used in conjunction with a fixed sized buffer. You use the slot allocator to allocator an index that can be used +as the insertion point for an object. + +Slots are reference counted to help mitigate the ABA problem in the lock-free queue we use for tracking jobs. + +The slot index is stored in the low 32 bits. The reference counter is stored in the high 32 bits: + + +-----------------+-----------------+ + | 32 Bits | 32 Bits | + +-----------------+-----------------+ + | Reference Count | Slot Index | + +-----------------+-----------------+ +*/ +slot_allocator_config :: struct { + capacity: u32, /* The number of slots to make available. */ +} + +slot_allocator_group :: struct { + bitfield: u32, /*atomic*/ /* Must be used atomically because the allocation and freeing routines need to make copies of this which must never be optimized away by the compiler. */ +} + +slot_allocator :: struct { + pGroups: [^]slot_allocator_group, /* Slots are grouped in chunks of 32. */ + pSlots: [^]u32, /* 32 bits for reference counting for ABA mitigation. */ + count: u32, /* Allocation count. */ + capacity: u32, + + /* Memory management. */ + _ownsHeap: b32, + _pHeap: rawptr, +} + +@(default_calling_convention="c", link_prefix="ma_") +foreign lib { + slot_allocator_config_init :: proc(capacity: u32) -> slot_allocator_config --- + + slot_allocator_get_heap_size :: proc(pConfig: ^slot_allocator_config, pHeapSizeInBytes: ^c.size_t) -> result --- + slot_allocator_init_preallocated :: proc(pConfig: ^slot_allocator_config, pHeap: rawptr, pAllocator: ^slot_allocator) -> result --- + slot_allocator_init :: proc(pConfig: ^slot_allocator_config, pAllocationCallbacks: ^allocation_callbacks, pAllocator: ^slot_allocator) -> result --- + slot_allocator_uninit :: proc(pAllocator: ^slot_allocator, pAllocationCallbacks: ^allocation_callbacks) --- + slot_allocator_alloc :: proc(pAllocator: ^slot_allocator, pSlot: ^u64) -> result --- + slot_allocator_free :: proc(pAllocator: ^slot_allocator, slot: u64) -> result --- +} + +/* +Callback for processing a job. Each job type will have their own processing callback which will be +called by ma_job_process(). +*/ +job_proc :: proc "c" (pJob: ^job) + +/* When a job type is added here an callback needs to be added go "g_jobVTable" in the implementation section. */ +job_type :: enum c.int { + /* Miscellaneous. */ + QUIT = 0, + CUSTOM, + + /* Resource Manager. */ + RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE, + RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE, + RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE, + RESOURCE_MANAGER_LOAD_DATA_BUFFER, + RESOURCE_MANAGER_FREE_DATA_BUFFER, + RESOURCE_MANAGER_LOAD_DATA_STREAM, + RESOURCE_MANAGER_FREE_DATA_STREAM, + RESOURCE_MANAGER_PAGE_DATA_STREAM, + RESOURCE_MANAGER_SEEK_DATA_STREAM, + + /* Device. */ + DEVICE_AAUDIO_REROUTE, + + /* Count. Must always be last. */ + COUNT, +} + +job :: struct { + toc: struct #raw_union { /* 8 bytes. We encode the job code into the slot allocation data to save space. */ + breakup: struct { + code: u16, /* Job type. */ + slot: u16, /* Index into a ma_slot_allocator. */ + refcount: u32, + }, + allocation: u64, + }, + next: u64, /*atomic*/ /* refcount + slot for the next item. Does not include the job code. */ + order: u32, /* Execution order. Used to create a data dependency and ensure a job is executed in order. Usage is contextual depending on the job type. */ + + data: struct #raw_union { + /* Miscellaneous. */ + custom: struct { + proc_: job_proc, + data0: uintptr, + data1: uintptr, + }, + + /* Resource Manager */ + resourceManager: struct #raw_union { + loadDataBufferNode: struct { + pResourceManager: rawptr /*ma_resource_manager**/, + pDataBufferNode: rawptr /*ma_resource_manager_data_buffer_node**/, + pFilePath: cstring, + pFilePathW: [^]c.wchar_t, + flags: u32, /* Resource manager data source flags that were used when initializing the data buffer. */ + pInitNotification: ^async_notification, /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */ + pDoneNotification: ^async_notification, /* Signalled when the data buffer has been fully decoded. Will be passed through to MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE when decoding. */ + pInitFence: ^fence, /* Released when initialization of the decoder is complete. */ + pDoneFence: ^fence, /* Released if initialization of the decoder fails. Passed through to PAGE_DATA_BUFFER_NODE untouched if init is successful. */ + }, + freeDataBufferNode: struct { + pResourceManager: rawptr /*ma_resource_manager**/, + pDataBufferNode: rawptr /*ma_resource_manager_data_buffer_node**/, + pDoneNotification: ^async_notification, + pDoneFence: ^fence, + }, + pageDataBufferNode: struct { + pResourceManager: rawptr /*ma_resource_manager**/, + pDataBufferNode: rawptr /*ma_resource_manager_data_buffer_node**/, + pDecoder: rawptr /*ma_decoder**/, + pDoneNotification: ^async_notification, /* Signalled when the data buffer has been fully decoded. */ + pDoneFence: ^fence, /* Passed through from LOAD_DATA_BUFFER_NODE and released when the data buffer completes decoding or an error occurs. */ + }, + + loadDataBuffer: struct { + pDataBuffer: rawptr /*ma_resource_manager_data_buffer**/, + pInitNotification: ^async_notification, /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */ + pDoneNotification: ^async_notification, /* Signalled when the data buffer has been fully decoded. */ + pInitFence: ^fence, /* Released when the data buffer has been initialized and the format/channels/rate can be retrieved. */ + pDoneFence: ^fence, /* Released when the data buffer has been fully decoded. */ + rangeBegInPCMFrames: u64, + rangeEndInPCMFrames: u64, + loopPointBegInPCMFrames: u64, + loopPointEndInPCMFrames: u64, + isLooping: u32, + }, + freeDataBuffer: struct { + pDataBuffer: rawptr /*ma_resource_manager_data_buffer**/, + pDoneNotification: ^async_notification, + pDoneFence: ^fence, + }, + + loadDataStream: struct { + pDataStream: rawptr /*ma_resource_manager_data_stream**/, + pFilePath: cstring, /* Allocated when the job is posted, freed by the job thread after loading. */ + pFilePathW: [^]c.wchar_t, /* ^ As above ^. Only used if pFilePath is NULL. */ + initialSeekPoint: u64, + pInitNotification: ^async_notification, /* Signalled after the first two pages have been decoded and frames can be read from the stream. */ + pInitFence: ^fence, + }, + freeDataStream: struct { + pDataStream: rawptr /*ma_resource_manager_data_stream**/, + pDoneNotification: ^async_notification, + pDoneFence: ^fence, + }, + pageDataStream: struct { + pDataStream: rawptr /*ma_resource_manager_data_stream**/, + pageIndex: u32, /* The index of the page to decode into. */ + }, + seekDataStream: struct { + pDataStream: rawptr /*ma_resource_manager_data_stream**/, + frameIndex: u64, + }, + }, + + /* Device. */ + device: struct #raw_union { + aaudio: struct #raw_union { + reroute: struct { + pDevice: rawptr /*ma_device**/, + deviceType: u32 /*ma_device_type*/, + }, + }, + }, + }, +} + +@(default_calling_convention="c", link_prefix="ma_") +foreign lib { + job_init :: proc(code: u16) -> job --- + job_process :: proc(pJob: ^job) -> result --- +} + + +/* +When set, ma_job_queue_next() will not wait and no semaphore will be signaled in +ma_job_queue_post(). ma_job_queue_next() will return MA_NO_DATA_AVAILABLE if nothing is available. + +This flag should always be used for platforms that do not support multithreading. +*/ +job_queue_flags :: enum c.int { + NON_BLOCKING = 0x00000001, +} + +job_queue_config :: struct { + flags: u32, + capacity: u32, /* The maximum number of jobs that can fit in the queue at a time. */ +} + +USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE :: false + +job_queue :: struct { + flags: u32, /* Flags passed in at initialization time. */ + capacity: u32, /* The maximum number of jobs that can fit in the queue at a time. Set by the config. */ + head: u64, /*atomic*/ /* The first item in the list. Required for removing from the top of the list. */ + tail: u64, /*atomic*/ /* The last item in the list. Required for appending to the end of the list. */ + sem: (struct {} when NO_THREADING else semaphore), /* Only used when MA_JOB_QUEUE_FLAG_NON_BLOCKING is unset. */ + allocator: slot_allocator, + pJobs: [^]job, + lock: (struct {} when USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE else spinlock), + + /* Memory management. */ + _pHeap: rawptr, + _ownsHeap: b32, +} + +@(default_calling_convention="c", link_prefix="ma_") +foreign lib { + job_queue_config_init :: proc(flags, capacity: u32) -> job_queue_config --- + + job_queue_get_heap_size :: proc(pConfig: ^job_queue_config, pHeapSizeInBytes: ^c.size_t) -> result --- + job_queue_init_preallocated :: proc(pConfig: ^job_queue_config, pHeap: rawptr, pQueue: ^job_queue) -> result --- + job_queue_init :: proc(pConfig: ^job_queue_config, pAllocationCallbacks: ^allocation_callbacks, pQueue: ^job_queue) -> result --- + job_queue_uninit :: proc(pQueue: ^job_queue, pAllocationCallbacks: ^allocation_callbacks) --- + job_queue_post :: proc(pQueue: ^job_queue, pJob: ^job) -> result --- + job_queue_next :: proc(pQueue: ^job_queue, pJob: ^job) -> result --- /* Returns MA_CANCELLED if the next job is a quit job. */ +} diff --git a/vendor/miniaudio/lib/miniaudio.lib b/vendor/miniaudio/lib/miniaudio.lib index 3d7c8327f..400cb9608 100644 Binary files a/vendor/miniaudio/lib/miniaudio.lib and b/vendor/miniaudio/lib/miniaudio.lib differ diff --git a/vendor/miniaudio/logging.odin b/vendor/miniaudio/logging.odin index 54792bff9..b03778079 100644 --- a/vendor/miniaudio/logging.odin +++ b/vendor/miniaudio/logging.odin @@ -2,11 +2,46 @@ package miniaudio import c "core:c/libc" -when ODIN_OS == "windows" { foreign import lib "lib/miniaudio.lib" } -when ODIN_OS == "linux" { foreign import lib "lib/miniaudio.a" } +when ODIN_OS == .Windows { + foreign import lib "lib/miniaudio.lib" +} else when ODIN_OS == .Linux { + foreign import lib "lib/miniaudio.a" +} else { + foreign import lib "system:miniaudio" +} MAX_LOG_CALLBACKS :: 4 + +/* +The callback for handling log messages. + + +Parameters +---------- +pUserData (in) + The user data pointer that was passed into ma_log_register_callback(). + +logLevel (in) + The log level. This can be one of the following: + + +----------------------+ + | Log Level | + +----------------------+ + | MA_LOG_LEVEL_DEBUG | + | MA_LOG_LEVEL_INFO | + | MA_LOG_LEVEL_WARNING | + | MA_LOG_LEVEL_ERROR | + +----------------------+ + +pMessage (in) + The log message. + + +Remarks +------- +Do not modify the state of the device from inside the callback. +*/ log_callback_proc :: proc "c" (pUserData: rawptr, level: u32, pMessage: cstring) log_callback :: struct { @@ -32,4 +67,4 @@ foreign lib { log_post :: proc(pLog: ^log, level: u32, pMessage: cstring) -> result --- log_postv :: proc(pLog: ^log, level: u32, pFormat: cstring, args: c.va_list) -> result --- log_postf :: proc(pLog: ^log, level: u32, pFormat: cstring, #c_vararg args: ..any) -> result --- -} \ No newline at end of file +} diff --git a/vendor/miniaudio/node_graph.odin b/vendor/miniaudio/node_graph.odin new file mode 100644 index 000000000..c0df39c0f --- /dev/null +++ b/vendor/miniaudio/node_graph.odin @@ -0,0 +1,469 @@ +package miniaudio + +import "core:c" + +when ODIN_OS == .Windows { + foreign import lib "lib/miniaudio.lib" +} else when ODIN_OS == .Linux { + foreign import lib "lib/miniaudio.a" +} else { + foreign import lib "system:miniaudio" +} + +/************************************************************************************************************************************************************ + +Node Graph + +************************************************************************************************************************************************************/ + +/* Must never exceed 254. */ +MAX_NODE_BUS_COUNT :: 254 + +/* Used internally by miniaudio for memory management. Must never exceed MA_MAX_NODE_BUS_COUNT. */ +MAX_NODE_LOCAL_BUS_COUNT :: 2 + +/* Use this when the bus count is determined by the node instance rather than the vtable. */ +NODE_BUS_COUNT_UNKNOWN :: 255 + +node :: struct {} + +/* Node flags. */ +node_flags :: enum c.int { + PASSTHROUGH = 0x00000001, + CONTINUOUS_PROCESSING = 0x00000002, + ALLOW_NULL_INPUT = 0x00000004, + DIFFERENT_PROCESSING_RATES = 0x00000008, + SILENT_OUTPUT = 0x00000010, +} + +/* The playback state of a node. Either started or stopped. */ +node_state :: enum c.int { + started = 0, + stopped = 1, +} + +node_vtable :: struct { + /* + Extended processing callback. This callback is used for effects that process input and output + at different rates (i.e. they perform resampling). This is similar to the simple version, only + they take two seperate frame counts: one for input, and one for output. + + On input, `pFrameCountOut` is equal to the capacity of the output buffer for each bus, whereas + `pFrameCountIn` will be equal to the number of PCM frames in each of the buffers in `ppFramesIn`. + + On output, set `pFrameCountOut` to the number of PCM frames that were actually output and set + `pFrameCountIn` to the number of input frames that were consumed. + */ + onProcess: proc "c" (pNode: ^node, ppFramesIn: ^[^]f32, pFrameCountIn: ^u32, ppFramesOut: ^[^]f32, pFrameCountOut: ^u32), + + /* + A callback for retrieving the number of a input frames that are required to output the + specified number of output frames. You would only want to implement this when the node performs + resampling. This is optional, even for nodes that perform resampling, but it does offer a + small reduction in latency as it allows miniaudio to calculate the exact number of input frames + to read at a time instead of having to estimate. + */ + onGetRequiredInputFrameCount: proc "c" (pNode: ^node, outputFrameCount: u32, pInputFrameCount: ^u32) -> result, + + /* + The number of input buses. This is how many sub-buffers will be contained in the `ppFramesIn` + parameters of the callbacks above. + */ + inputBusCount: u8, + + /* + The number of output buses. This is how many sub-buffers will be contained in the `ppFramesOut` + parameters of the callbacks above. + */ + outputBusCount: u8, + + /* + Flags describing characteristics of the node. This is currently just a placeholder for some + ideas for later on. + */ + flags: u32, +} + +node_config :: struct { + vtable: ^node_vtable, /* Should never be null. Initialization of the node will fail if so. */ + initialState: node_state, /* Defaults to ma_node_state_started. */ + inputBusCount: u32, /* Only used if the vtable specifies an input bus count of `MA_NODE_BUS_COUNT_UNKNOWN`, otherwise must be set to `MA_NODE_BUS_COUNT_UNKNOWN` (default). */ + outputBusCount: u32, /* Only used if the vtable specifies an output bus count of `MA_NODE_BUS_COUNT_UNKNOWN`, otherwise be set to `MA_NODE_BUS_COUNT_UNKNOWN` (default). */ + pInputChannels: ^u32, /* The number of elements are determined by the input bus count as determined by the vtable, or `inputBusCount` if the vtable specifies `MA_NODE_BUS_COUNT_UNKNOWN`. */ + pOutputChannels: ^u32, /* The number of elements are determined by the output bus count as determined by the vtable, or `outputBusCount` if the vtable specifies `MA_NODE_BUS_COUNT_UNKNOWN`. */ +} + +/* +A node has multiple output buses. An output bus is attached to an input bus as an item in a linked +list. Think of the input bus as a linked list, with the output bus being an item in that list. +*/ +node_output_bus :: struct { + /* Immutable. */ + pNode: ^node, /* The node that owns this output bus. The input node. Will be null for dummy head and tail nodes. */ + outputBusIndex: u8, /* The index of the output bus on pNode that this output bus represents. */ + channels: u8, /* The number of channels in the audio stream for this bus. */ + + /* Mutable via multiple threads. Must be used atomically. The weird ordering here is for packing reasons. */ + inputNodeInputBusIndex: u8, /*atomic*/ /* The index of the input bus on the input. Required for detaching. */ + flags: u32, /*atomic*/ /* Some state flags for tracking the read state of the output buffer. A combination of MA_NODE_OUTPUT_BUS_FLAG_*. */ + refCount: u32, /*atomic*/ /* Reference count for some thread-safety when detaching. */ + isAttached: b32, /*atomic*/ /* This is used to prevent iteration of nodes that are in the middle of being detached. Used for thread safety. */ + lock: spinlock, /*atomic*/ /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */ + volume: f32, /*atomic*/ /* Linear. */ + pNext: ^node_output_bus, /*atomic*/ /* If null, it's the tail node or detached. */ + pPrev: ^node_output_bus, /*atomic*/ /* If null, it's the head node or detached. */ + pInputNode: ^node, /*atomic*/ /* The node that this output bus is attached to. Required for detaching. */ +} + +/* +A node has multiple input buses. The output buses of a node are connecting to the input busses of +another. An input bus is essentially just a linked list of output buses. +*/ +node_input_bus :: struct { + /* Mutable via multiple threads. */ + head: node_output_bus, /* Dummy head node for simplifying some lock-free thread-safety stuff. */ + nextCounter: u32, /*atomic*/ /* This is used to determine whether or not the input bus is finding the next node in the list. Used for thread safety when detaching output buses. */ + lock: spinlock, /*atomic*/ /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */ + + /* Set once at startup. */ + channels: u8, /* The number of channels in the audio stream for this bus. */ +} + + +node_base :: struct { + /* These variables are set once at startup. */ + pNodeGraph: ^node_graph, /* The graph this node belongs to. */ + vtable: ^node_vtable, + pCachedData: [^]f32, /* Allocated on the heap. Fixed size. Needs to be stored on the heap because reading from output buses is done in separate function calls. */ + cachedDataCapInFramesPerBus: u16, /* The capacity of the input data cache in frames, per bus. */ + + /* These variables are read and written only from the audio thread. */ + cachedFrameCountOut: u16, + cachedFrameCountIn: u16, + consumedFrameCountIn: u16, + + /* These variables are read and written between different threads. */ + state: node_state, /*atomic*/ /* When set to stopped, nothing will be read, regardless of the times in stateTimes. */ + stateTimes: [2]u64, /*atomic*/ /* Indexed by ma_node_state. Specifies the time based on the global clock that a node should be considered to be in the relevant state. */ + localTime: u64, /*atomic*/ /* The node's local clock. This is just a running sum of the number of output frames that have been processed. Can be modified by any thread with `ma_node_set_time()`. */ + inputBusCount: u32, + outputBusCount: u32, + pInputBuses: [^]node_input_bus, + pOutputBuses: [^]node_output_bus, + + /* Memory management. */ + _inputBuses: [MAX_NODE_LOCAL_BUS_COUNT]node_input_bus, + _outputBuses: [MAX_NODE_LOCAL_BUS_COUNT]node_output_bus, + _pHeap: rawptr, /* A heap allocation for internal use only. pInputBuses and/or pOutputBuses will point to this if the bus count exceeds MA_MAX_NODE_LOCAL_BUS_COUNT. */ + _ownsHeap: b32, /* If set to true, the node owns the heap allocation and _pHeap will be freed in ma_node_uninit(). */ +} + +@(default_calling_convention="c", link_prefix="ma_") +foreign lib { + node_config_init :: proc() -> node_config --- + + node_get_heap_size :: proc(pNodeGraph: ^node_graph, pConfig: ^node_config, pHeapSizeInBytes: ^c.size_t) -> result --- + node_init_preallocated :: proc(pNodeGraph: ^node_graph, pConfig: ^node_config, pHeap: rawptr, pNode: ^node) -> result --- + node_init :: proc(pNodeGraph: ^node_graph, pConfig: ^node_config, pAllocationCallbacks: ^allocation_callbacks, pNode: ^node) -> result --- + node_uninit :: proc(pNode: ^node, pAllocationCallbacks: ^allocation_callbacks) --- + node_get_node_graph :: proc(pNode: ^node) -> ^node_graph --- + node_get_input_bus_count :: proc(pNode: ^node) -> u32 --- + node_get_output_bus_count :: proc(pNode: ^node) -> u32 --- + node_get_input_channels :: proc(pNode: ^node, inputBusIndex: u32) -> u32 --- + node_get_output_channels :: proc(pNode: ^node, outputBusIndex: u32) -> u32 --- + node_attach_output_bus :: proc(pNode: ^node, outputBusIndex: u32, pOtherNode: ^node, otherNodeInputBusIndex: u32) -> result --- + node_detach_output_bus :: proc(pNode: ^node, outputBusIndex: u32) -> result --- + node_detach_all_output_buses :: proc(pNode: ^node) -> result --- + node_set_output_bus_volume :: proc(pNode: ^node, outputBusIndex: u32, volume: f32) -> result --- + node_get_output_bus_volume :: proc(pNode: ^node, outputBusIndex: u32) -> f32 --- + node_set_state :: proc(pNode: ^node, state: node_state) -> result --- + node_get_state :: proc(pNode: ^node) -> node_state --- + node_set_state_time :: proc(pNode: ^node, state: node_state, globalTime: u64) -> result --- + node_get_state_time :: proc(pNode: ^node, state: node_state) -> u64 --- + node_get_state_by_time :: proc(pNode: ^node, globalTime: u64) -> node_state --- + node_get_state_by_time_range :: proc(pNode: ^node, globalTimeBeg: u64, globalTimeEnd: u64) -> node_state --- + node_get_time :: proc(pNode: ^node) -> u64 --- + node_set_time :: proc(pNode: ^node, localTime: u64) -> result --- +} + +node_graph_config :: struct { + channels: u32, + nodeCacheCapInFrames: u16, +} + +node_graph :: struct { + /* Immutable. */ + base: node_base, /* The node graph itself is a node so it can be connected as an input to different node graph. This has zero inputs and calls ma_node_graph_read_pcm_frames() to generate it's output. */ + endpoint: node_base, /* Special node that all nodes eventually connect to. Data is read from this node in ma_node_graph_read_pcm_frames(). */ + nodeCacheCapInFrames: u16, + + /* Read and written by multiple threads. */ + isReading: b32, /*atomic*/ +} + +@(default_calling_convention="c", link_prefix="ma_") +foreign lib { + node_graph_config_init :: proc(channels: u32) -> node_graph_config --- + + node_graph_init :: proc(pConfig: ^node_graph_config, pAllocationCallbacks: ^allocation_callbacks, pNodeGraph: ^node_graph) -> result --- + node_graph_uninit :: proc(pNodeGraph: ^node_graph, pAllocationCallbacks: ^allocation_callbacks) --- + node_graph_get_endpoint :: proc(pNodeGraph: ^node_graph) -> ^node --- + node_graph_read_pcm_frames :: proc(pNodeGraph: ^node_graph, pFramesOut: rawptr, frameCount: u64, pFramesRead: ^u64) -> result --- + node_graph_get_channels :: proc(pNodeGraph: ^node_graph) -> u32 --- + node_graph_get_time :: proc(pNodeGraph: ^node_graph) -> u64 --- + node_graph_set_time :: proc(pNodeGraph: ^node_graph, globalTime: u64) -> result --- +} + + + +/* Data source node. 0 input buses, 1 output bus. Used for reading from a data source. */ +data_source_node_config :: struct { + nodeConfig: node_config, + pDataSource: ^data_source, +} + +data_source_node :: struct { + base: node_base, + pDataSource: ^data_source, +} + +@(default_calling_convention="c", link_prefix="ma_") +foreign lib { + data_source_node_config_init :: proc(pDataSource: ^data_source) -> data_source_node_config --- + + data_source_node_init :: proc(pNodeGraph: ^node_graph, pConfig: ^data_source_node_config, pAllocationCallbacks: ^allocation_callbacks, pDataSourceNode: ^data_source_node) -> result --- + data_source_node_uninit :: proc(pDataSourceNode: ^data_source_node, pAllocationCallbacks: ^allocation_callbacks) --- + data_source_node_set_looping :: proc(pDataSourceNode: ^data_source_node, isLooping: b32) -> result --- + data_source_node_is_looping :: proc(pDataSourceNode: ^data_source_node) -> b32 --- +} + + +/* Splitter Node. 1 input, 2 outputs. Used for splitting/copying a stream so it can be as input into two separate output nodes. */ +splitter_node_config :: struct { + nodeConfig: node_config, + channels: u32, +} + +splitter_node :: struct { + base: node_base, +} + +@(default_calling_convention="c", link_prefix="ma_") +foreign lib { + splitter_node_config_init :: proc(channels: u32) -> splitter_node_config --- + + splitter_node_init :: proc(pNodeGraph: ^node_graph, pConfig: ^splitter_node_config, pAllocationCallbacks: ^allocation_callbacks, pSplitterNode: ^splitter_node) -> result --- + splitter_node_uninit :: proc(pSplitterNode: ^splitter_node, pAllocationCallbacks: ^allocation_callbacks) --- +} + + +/* +Biquad Node +*/ +biquad_node_config :: struct { + nodeConfig: node_config, + biquad: biquad_config, +} + +biquad_node :: struct { + baseNode: node_base, + biquad: biquad, +} + +@(default_calling_convention="c", link_prefix="ma_") +foreign lib { + biquad_node_config_init :: proc(channels: u32, b0, b1, b2, a0, a1, a2: f32) -> biquad_node_config --- + + biquad_node_init :: proc(pNodeGraph: ^node_graph, pConfig: ^biquad_node_config, pAllocationCallbacks: ^allocation_callbacks, pNode: ^biquad_node) -> result --- + biquad_node_reinit :: proc(pConfig: ^biquad_config, pNode: ^biquad_node) -> result --- + biquad_node_uninit :: proc(pNode: ^biquad_node, pAllocationCallbacks: ^allocation_callbacks) --- +} + + +/* +Low Pass Filter Node +*/ +lpf_node_config :: struct { + nodeConfig: node_config, + lpf: lpf_config, +} + +lpf_node :: struct { + baseNode: node_base, + lpf: lpf, +} + +@(default_calling_convention="c", link_prefix="ma_") +foreign lib { + lpf_node_config_init :: proc(channels, sampleRate: u32, cutoffFrequency: f64, order: u32) -> lpf_node_config --- + + lpf_node_init :: proc(pNodeGraph: ^node_graph, pConfig: ^lpf_node_config, pAllocationCallbacks: ^allocation_callbacks, pNode: ^lpf_node) -> result --- + lpf_node_reinit :: proc(pConfig: ^lpf_config, pNode: ^lpf_node) -> result --- + lpf_node_uninit :: proc(pNode: ^lpf_node, pAllocationCallbacks: ^allocation_callbacks) --- +} + + +/* +High Pass Filter Node +*/ +hpf_node_config :: struct { + nodeConfig: node_config, + hpf: hpf_config, +} + +hpf_node :: struct { + baseNode: node_base, + hpf: hpf, +} + +@(default_calling_convention="c", link_prefix="ma_") +foreign lib { + hpf_node_config_init :: proc(channels, sampleRate: u32, cutoffFrequency: f64, order: u32) -> hpf_node_config --- + + hpf_node_init :: proc(pNodeGraph: ^node_graph, pConfig: ^hpf_node_config, pAllocationCallbacks: ^allocation_callbacks, pNode: ^hpf_node) -> result --- + hpf_node_reinit :: proc(pConfig: ^hpf_config, pNode: ^hpf_node) -> result --- + hpf_node_uninit :: proc(pNode: ^hpf_node, pAllocationCallbacks: ^allocation_callbacks) --- +} + + +/* +Band Pass Filter Node +*/ +bpf_node_config :: struct { + nodeConfig: node_config, + bpf: bpf_config, +} + +bpf_node :: struct { + baseNode: node_base, + bpf: bpf, +} + +@(default_calling_convention="c", link_prefix="ma_") +foreign lib { + bpf_node_config_init :: proc(channels, sampleRate: u32, cutoffFrequency: f64, order: u32) -> bpf_node_config --- + + bpf_node_init :: proc(pNodeGraph: ^node_graph, pConfig: ^bpf_node_config, pAllocationCallbacks: ^allocation_callbacks, pNode: ^bpf_node) -> result --- + bpf_node_reinit :: proc(pConfig: ^bpf_config, pNode: ^bpf_node) -> result --- + bpf_node_uninit :: proc(pNode: ^bpf_node, pAllocationCallbacks: ^allocation_callbacks) --- +} + + +/* +Notching Filter Node +*/ +notch_node_config :: struct { + nodeConfig: node_config, + notch: notch_config, +} + +notch_node :: struct { + baseNode: node_base, + notch: notch2, +} + +@(default_calling_convention="c", link_prefix="ma_") +foreign lib { + notch_node_config_init :: proc(channels, sampleRate: u32, q, frequency: f64) -> notch_node_config --- + + notch_node_init :: proc(pNodeGraph: ^node_graph, pConfig: ^notch_node_config, pAllocationCallbacks: ^allocation_callbacks, pNode: ^notch_node) -> result --- + notch_node_reinit :: proc(pConfig: ^notch_config, pNode: ^notch_node) -> result --- + notch_node_uninit :: proc(pNode: ^notch_node, pAllocationCallbacks: ^allocation_callbacks) --- +} + + +/* +Peaking Filter Node +*/ +peak_node_config :: struct { + nodeConfig: node_config, + peak: peak_config, +} + +peak_node :: struct { + baseNode: node_base, + peak: peak2, +} + +@(default_calling_convention="c", link_prefix="ma_") +foreign lib { + peak_node_config_init :: proc(channels, sampleRate: u32, gainDB, q, frequency: f64) -> peak_node_config --- + + peak_node_init :: proc(pNodeGraph: ^node_graph, pConfig: ^peak_node_config, pAllocationCallbacks: ^allocation_callbacks, pNode: ^peak_node) -> result --- + peak_node_reinit :: proc(pConfig: ^peak_config, pNode: ^peak_node) -> result --- + peak_node_uninit :: proc(pNode: ^peak_node, pAllocationCallbacks: ^allocation_callbacks) --- +} + + +/* +Low Shelf Filter Node +*/ +loshelf_node_config :: struct { + nodeConfig: node_config, + loshelf: loshelf_config, +} + +loshelf_node :: struct { + baseNode: node_base, + loshelf: loshelf2, +} + +@(default_calling_convention="c", link_prefix="ma_") +foreign lib { + loshelf_node_config_init :: proc(channels, sampleRate: u32, gainDB, q, frequency: f64) -> loshelf_node_config --- + + loshelf_node_init :: proc(pNodeGraph: ^node_graph, pConfig: ^loshelf_node_config, pAllocationCallbacks: ^allocation_callbacks, pNode: ^loshelf_node) -> result --- + loshelf_node_reinit :: proc(pConfig: ^loshelf_config, pNode: ^loshelf_node) -> result --- + loshelf_node_uninit :: proc(pNode: ^loshelf_node, pAllocationCallbacks: ^allocation_callbacks) --- +} + + +/* +High Shelf Filter Node +*/ +hishelf_node_config :: struct { + nodeConfig: node_config, + hishelf: hishelf_config, +} + +hishelf_node :: struct { + baseNode: node_base, + hishelf: hishelf2, +} + +@(default_calling_convention="c", link_prefix="ma_") +foreign lib { + hishelf_node_config_init :: proc(channels, sampleRate: u32, gainDB, q, frequency: f64) -> hishelf_node_config --- + + hishelf_node_init :: proc(pNodeGraph: ^node_graph, pConfig: ^hishelf_node_config, pAllocationCallbacks: ^allocation_callbacks, pNode: ^hishelf_node) -> result --- + hishelf_node_reinit :: proc(pConfig: ^hishelf_config, pNode: ^hishelf_node) -> result --- + hishelf_node_uninit :: proc(pNode: ^hishelf_node, pAllocationCallbacks: ^allocation_callbacks) --- +} + + +/* +Delay Filter Node +*/ +delay_node_config :: struct { + nodeConfig: node_config, + delay: delay_config, +} + +delay_node :: struct { + baseNode: node_base, + delay: delay, +} + +@(default_calling_convention="c", link_prefix="ma_") +foreign lib { + delay_node_config_init :: proc(channels, sampleRate, delayInFrames: u32, decay: f32) -> delay_node_config --- + + delay_node_init :: proc(pNodeGraph: ^node_graph, pConfig: ^delay_node_config, pAllocationCallbacks: ^allocation_callbacks, pDelayNode: ^delay_node) -> result --- + delay_node_uninit :: proc(pDelayNode: ^delay_node, pAllocationCallbacks: ^allocation_callbacks) --- + delay_node_set_wet :: proc(pDelayNode: ^delay_node, value: f32) --- + delay_node_get_wet :: proc(pDelayNode: ^delay_node) -> f32 --- + delay_node_set_dry :: proc(pDelayNode: ^delay_node, value: f32) --- + delay_node_get_dry :: proc(pDelayNode: ^delay_node) -> f32 --- + delay_node_set_decay :: proc(pDelayNode: ^delay_node, value: f32) --- + delay_node_get_decay :: proc(pDelayNode: ^delay_node) -> f32 --- +} diff --git a/vendor/miniaudio/resource_manager.odin b/vendor/miniaudio/resource_manager.odin new file mode 100644 index 000000000..e67d4a475 --- /dev/null +++ b/vendor/miniaudio/resource_manager.odin @@ -0,0 +1,288 @@ +package miniaudio + +import "core:c" + +when ODIN_OS == .Windows { + foreign import lib "lib/miniaudio.lib" +} else when ODIN_OS == .Linux { + foreign import lib "lib/miniaudio.a" +} else { + foreign import lib "system:miniaudio" +} + +/************************************************************************************************************************************************************ + +Resource Manager + +************************************************************************************************************************************************************/ + +resource_manager_data_source_flags :: enum c.int { + STREAM = 0x00000001, /* When set, does not load the entire data source in memory. Disk I/O will happen on job threads. */ + DECODE = 0x00000002, /* Decode data before storing in memory. When set, decoding is done at the resource manager level rather than the mixing thread. Results in faster mixing, but higher memory usage. */ + ASYNC = 0x00000004, /* When set, the resource manager will load the data source asynchronously. */ + WAIT_INIT = 0x00000008, /* When set, waits for initialization of the underlying data source before returning from ma_resource_manager_data_source_init(). */ + UNKNOWN_LENGTH = 0x00000010, /* Gives the resource manager a hint that the length of the data source is unknown and calling `ma_data_source_get_length_in_pcm_frames()` should be avoided. */ +} + +/* +Pipeline notifications used by the resource manager. Made up of both an async notification and a fence, both of which are optional. +*/ +resource_manager_pipeline_stage_notification :: struct { + pNotification: ^async_notification, + pFence: ^fence, +} + +resource_manager_pipeline_notifications :: struct { + init: resource_manager_pipeline_stage_notification, /* Initialization of the decoder. */ + done: resource_manager_pipeline_stage_notification, /* Decoding fully completed. */ +} + +@(default_calling_convention="c", link_prefix="ma_") +foreign lib { + resource_manager_pipeline_notifications_init :: proc() -> resource_manager_pipeline_notifications --- +} + + +/* BEGIN BACKWARDS COMPATIBILITY */ +/* TODO: Remove this block in version 0.12. */ +resource_manager_job :: job +resource_manager_job_init :: job_init +JOB_TYPE_RESOURCE_MANAGER_QUEUE_FLAG_NON_BLOCKING :: job_queue_flags.NON_BLOCKING +resource_manager_job_queue_config :: job_queue_config +resource_manager_job_queue_config_init :: job_queue_config_init +resource_manager_job_queue :: job_queue +resource_manager_job_queue_get_heap_size :: job_queue_get_heap_size +resource_manager_job_queue_init_preallocated :: job_queue_init_preallocated +resource_manager_job_queue_init :: job_queue_init +resource_manager_job_queue_uninit :: job_queue_uninit +resource_manager_job_queue_post :: job_queue_post +resource_manager_job_queue_next :: job_queue_next +/* END BACKWARDS COMPATIBILITY */ + + + +/* Maximum job thread count will be restricted to this, but this may be removed later and replaced with a heap allocation thereby removing any limitation. */ +RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT :: 64 + +resource_manager_flags :: enum c.int { + /* Indicates ma_resource_manager_next_job() should not block. Only valid when the job thread count is 0. */ + NON_BLOCKING = 0x00000001, + + /* Disables any kind of multithreading. Implicitly enables MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING. */ + NO_THREADING = 0x00000002, +} + +resource_manager_data_source_config :: struct { + pFilePath: cstring, + pFilePathW: [^]c.wchar_t, + pNotifications: ^resource_manager_pipeline_notifications, + initialSeekPointInPCMFrames: u64, + rangeBegInPCMFrames: u64, + rangeEndInPCMFrames: u64, + loopPointBegInPCMFrames: u64, + loopPointEndInPCMFrames: u64, + isLooping: b32, + flags: u32, +} + +resource_manager_data_supply_type :: enum c.int { + unknown = 0, /* Used for determining whether or the data supply has been initialized. */ + encoded, /* Data supply is an encoded buffer. Connector is ma_decoder. */ + decoded, /* Data supply is a decoded buffer. Connector is ma_audio_buffer. */ + decoded_paged, /* Data supply is a linked list of decoded buffers. Connector is ma_paged_audio_buffer. */ +} + +resource_manager_data_supply :: struct { + type: resource_manager_data_supply_type, /*atomic*/ /* Read and written from different threads so needs to be accessed atomically. */ + backend: struct #raw_union { + encoded: struct { + pData: rawptr, + sizeInBytes: c.size_t, + }, + decoded: struct { + pData: rawptr, + totalFrameCount: u64, + decodedFrameCount: u64, + format: format, + channels: u32, + sampleRate: u32, + }, + decodedPaged: struct { + data: paged_audio_buffer_data, + decodedFrameCount: u64, + sampleRate: u32, + }, + }, +} + +resource_manager_data_buffer_node :: struct { + hashedName32: u32, /* The hashed name. This is the key. */ + refCount: u32, + result: result, /*atomic*/ /* Result from asynchronous loading. When loading set to MA_BUSY. When fully loaded set to MA_SUCCESS. When deleting set to MA_UNAVAILABLE. */ + executionCounter: u32, /*atomic*/ /* For allocating execution orders for jobs. */ + executionPointer: u32, /*atomic*/ /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ + isDataOwnedByResourceManager: b32, /* Set to true when the underlying data buffer was allocated the resource manager. Set to false if it is owned by the application (via ma_resource_manager_register_*()). */ + data: resource_manager_data_supply, + pParent: ^resource_manager_data_buffer_node, + pChildLo: ^resource_manager_data_buffer_node, + pChildHi: ^resource_manager_data_buffer_node, +} + +resource_manager_data_buffer :: struct { + ds: data_source_base, /* Base data source. A data buffer is a data source. */ + pResourceManager: ^resource_manager, /* A pointer to the resource manager that owns this buffer. */ + pNode: ^resource_manager_data_buffer_node, /* The data node. This is reference counted and is what supplies the data. */ + flags: u32, /* The flags that were passed used to initialize the buffer. */ + executionCounter: u32, /*atomic*/ /* For allocating execution orders for jobs. */ + executionPointer: u32, /*atomic*/ /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ + seekTargetInPCMFrames: u64, /* Only updated by the public API. Never written nor read from the job thread. */ + seekToCursorOnNextRead: b32, /* On the next read we need to seek to the frame cursor. */ + result: result, /*atomic*/ /* Keeps track of a result of decoding. Set to MA_BUSY while the buffer is still loading. Set to MA_SUCCESS when loading is finished successfully. Otherwise set to some other code. */ + isLooping: b32, /*atomic*/ /* Can be read and written by different threads at the same time. Must be used atomically. */ + isConnectorInitialized: b32, /* Used for asynchronous loading to ensure we don't try to initialize the connector multiple times while waiting for the node to fully load. */ + connector: struct #raw_union { + decoder: decoder, /* Supply type is ma_resource_manager_data_supply_type_encoded */ + buffer: audio_buffer, /* Supply type is ma_resource_manager_data_supply_type_decoded */ + pagedBuffer: paged_audio_buffer, /* Supply type is ma_resource_manager_data_supply_type_decoded_paged */ + }, /* Connects this object to the node's data supply. */ +} + +resource_manager_data_stream :: struct { + ds: data_source_base, /* Base data source. A data stream is a data source. */ + pResourceManager: ^resource_manager, /* A pointer to the resource manager that owns this data stream. */ + flags: u32, /* The flags that were passed used to initialize the stream. */ + decoder: decoder, /* Used for filling pages with data. This is only ever accessed by the job thread. The public API should never touch this. */ + isDecoderInitialized: b32, /* Required for determining whether or not the decoder should be uninitialized in MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM. */ + totalLengthInPCMFrames: u64, /* This is calculated when first loaded by the MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM. */ + relativeCursor: u32, /* The playback cursor, relative to the current page. Only ever accessed by the public API. Never accessed by the job thread. */ + absoluteCursor: u64, /*atomic*/ /* The playback cursor, in absolute position starting from the start of the file. */ + currentPageIndex: u32, /* Toggles between 0 and 1. Index 0 is the first half of pPageData. Index 1 is the second half. Only ever accessed by the public API. Never accessed by the job thread. */ + executionCounter: u32, /*atomic*/ /* For allocating execution orders for jobs. */ + executionPointer: u32, /*atomic*/ /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ + + /* Written by the public API, read by the job thread. */ + isLooping: b32, /*atomic*/ /* Whether or not the stream is looping. It's important to set the looping flag at the data stream level for smooth loop transitions. */ + + /* Written by the job thread, read by the public API. */ + pPageData: rawptr, /* Buffer containing the decoded data of each page. Allocated once at initialization time. */ + pageFrameCount: [2]u32, /*atomic*/ /* The number of valid PCM frames in each page. Used to determine the last valid frame. */ + + /* Written and read by both the public API and the job thread. These must be atomic. */ + result: result, /*atomic*/ /* Result from asynchronous loading. When loading set to MA_BUSY. When initialized set to MA_SUCCESS. When deleting set to MA_UNAVAILABLE. If an error occurs when loading, set to an error code. */ + isDecoderAtEnd: b32, /*atomic*/ /* Whether or not the decoder has reached the end. */ + isPageValid: [2]b32, /*atomic*/ /* Booleans to indicate whether or not a page is valid. Set to false by the public API, set to true by the job thread. Set to false as the pages are consumed, true when they are filled. */ + seekCounter: b32, /*atomic*/ /* When 0, no seeking is being performed. When > 0, a seek is being performed and reading should be delayed with MA_BUSY. */ +} + +resource_manager_data_source :: struct { + backend: struct #raw_union { + buffer: resource_manager_data_buffer, + stream: resource_manager_data_stream, + }, /* Must be the first item because we need the first item to be the data source callbacks for the buffer or stream. */ + + flags: u32, /* The flags that were passed in to ma_resource_manager_data_source_init(). */ + executionCounter: u32, /*atomic*/ /* For allocating execution orders for jobs. */ + executionPointer: u32, /*atomic*/ /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ +} + +resource_manager_config :: struct { + allocationCallbacks: allocation_callbacks, + pLog: ^log, + decodedFormat: format, /* The decoded format to use. Set to ma_format_unknown (default) to use the file's native format. */ + decodedChannels: u32, /* The decoded channel count to use. Set to 0 (default) to use the file's native channel count. */ + decodedSampleRate: u32, /* the decoded sample rate to use. Set to 0 (default) to use the file's native sample rate. */ + jobThreadCount: u32, /* Set to 0 if you want to self-manage your job threads. Defaults to 1. */ + jobQueueCapacity: u32, /* The maximum number of jobs that can fit in the queue at a time. Defaults to MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY. Cannot be zero. */ + flags: u32, + pVFS: ^vfs, /* Can be NULL in which case defaults will be used. */ + ppCustomDecodingBackendVTables: ^[^]decoding_backend_vtable, + customDecodingBackendCount: u32, + pCustomDecodingBackendUserData: rawptr, +} + +resource_manager :: struct { + config: resource_manager_config, + pRootDataBufferNode: ^resource_manager_data_buffer_node, /* The root buffer in the binary tree. */ + dataBufferBSTLock: (struct {} when NO_THREADING else mutex), /* For synchronizing access to the data buffer binary tree. */ + jobThreads: (struct {} when NO_THREADING else [RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT]thread), /* The threads for executing jobs. */ + jobQueue: job_queue, /* Multi-consumer, multi-producer job queue for managing jobs for asynchronous decoding and streaming. */ + defaultVFS: default_vfs, /* Only used if a custom VFS is not specified. */ + log: log, /* Only used if no log was specified in the config. */ +} + +@(default_calling_convention="c", link_prefix="ma_") +foreign lib { + resource_manager_data_source_config_init :: proc() -> resource_manager_data_source_config --- + resource_manager_config_init :: proc() -> resource_manager_config --- + + /* Init. */ + resource_manager_init :: proc(pConfig: ^resource_manager_config, pResourceManager: ^resource_manager) -> result --- + resource_manager_uninit :: proc(pResourceManager: ^resource_manager) --- + resource_manager_get_log :: proc(pResourceManager: ^resource_manager) -> ^log --- + + /* Registration. */ + resource_manager_register_file :: proc(pResourceManager: ^resource_manager, pFilePath: cstring, flags: u32) -> result --- + resource_manager_register_file_w :: proc(pResourceManager: ^resource_manager, pFilePath: [^]c.wchar_t, flags: u32) -> result --- + resource_manager_register_decoded_data :: proc(pResourceManager: ^resource_manager, pName: cstring, pData: rawptr, frameCount: u64, format: format, channels: u32, sampleRate: u32) -> result --- /* Does not copy. Increments the reference count if already exists and returns MA_SUCCESS. */ + resource_manager_register_decoded_data_w :: proc(pResourceManager: ^resource_manager, pName: [^]c.wchar_t, pData: rawptr, frameCount: u64, format: format, channels: u32, sampleRate: u32) -> result --- + resource_manager_register_encoded_data :: proc(pResourceManager: ^resource_manager, pName: cstring, pData: rawptr, sizeInBytes: c.size_t) -> result --- /* Does not copy. Increments the reference count if already exists and returns MA_SUCCESS. */ + resource_manager_register_encoded_data_w :: proc(pResourceManager: ^resource_manager, pName: [^]c.wchar_t, pData: rawptr, sizeInBytes: c.size_t) -> result --- + resource_manager_unregister_file :: proc(pResourceManager: ^resource_manager, pFilePath: cstring) -> result --- + resource_manager_unregister_file_w :: proc(pResourceManager: ^resource_manager, pFilePath: [^]c.wchar_t) -> result --- + resource_manager_unregister_data :: proc(pResourceManager: ^resource_manager, pName: cstring) -> result --- + resource_manager_unregister_data_w :: proc(pResourceManager: ^resource_manager, pName: [^]c.wchar_t) -> result --- + + /* Data Buffers. */ + resource_manager_data_buffer_init_ex :: proc(pResourceManager: ^resource_manager, pConfig: ^resource_manager_data_source_config, pDataBuffer: ^resource_manager_data_buffer) -> result --- + resource_manager_data_buffer_init :: proc(pResourceManager: ^resource_manager, pFilePath: cstring, flags: u32, pNotifications: ^resource_manager_pipeline_notifications, pDataBuffer: ^resource_manager_data_buffer) -> result --- + resource_manager_data_buffer_init_w :: proc(pResourceManager: ^resource_manager, pFilePath: [^]c.wchar_t, flags: u32, pNotifications: ^resource_manager_pipeline_notifications, pDataBuffer: ^resource_manager_data_buffer) -> result --- + resource_manager_data_buffer_init_copy :: proc(pResourceManager: ^resource_manager, pExistingDataBuffer, pDataBuffer: ^resource_manager_data_buffer) -> result --- + resource_manager_data_buffer_uninit :: proc(pDataBuffer: ^resource_manager_data_buffer) -> result --- + resource_manager_data_buffer_read_pcm_frames :: proc(pDataBuffer: ^resource_manager_data_buffer, pFramesOut: rawptr, frameCount: u64, pFramesRead: ^u64) -> result --- + resource_manager_data_buffer_seek_to_pcm_frame :: proc(pDataBuffer: ^resource_manager_data_buffer, frameIndex: u64) -> result --- + resource_manager_data_buffer_get_data_format :: proc(pDataBuffer: ^resource_manager_data_buffer, pFormat: ^format, pChannels: ^u32, pSampleRate: ^u32, pChannelMap: [^]channel, channelMapCap: c.size_t) -> result --- + resource_manager_data_buffer_get_cursor_in_pcm_frames :: proc(pDataBuffer: ^resource_manager_data_buffer, pCursor: ^u64) -> result --- + resource_manager_data_buffer_get_length_in_pcm_frames :: proc(pDataBuffer: ^resource_manager_data_buffer, pLength: ^u64) -> result --- + resource_manager_data_buffer_result :: proc(pDataBuffer: ^resource_manager_data_buffer) -> result --- + resource_manager_data_buffer_set_looping :: proc(pDataBuffer: ^resource_manager_data_buffer, isLooping: b32) -> result --- + resource_manager_data_buffer_is_looping :: proc(pDataBuffer: ^resource_manager_data_buffer) -> b32 --- + resource_manager_data_buffer_get_available_frames :: proc(pDataBuffer: ^resource_manager_data_buffer, pAvailableFrames: ^u64) -> result --- + + /* Data Streams. */ + resource_manager_data_stream_init_ex :: proc(pResourceManager: ^resource_manager, pConfig: ^resource_manager_data_source_config, pDataStream: ^resource_manager_data_stream) -> result --- + resource_manager_data_stream_init :: proc(pResourceManager: ^resource_manager, pFilePath: cstring, flags: u32, pNotifications: ^resource_manager_pipeline_notifications, pDataStream: ^resource_manager_data_stream) -> result --- + resource_manager_data_stream_init_w :: proc(pResourceManager: ^resource_manager, pFilePath: [^]c.wchar_t, flags: u32, pNotifications: ^resource_manager_pipeline_notifications, pDataStream: ^resource_manager_data_stream) -> result --- + resource_manager_data_stream_uninit :: proc(pDataStream: ^resource_manager_data_stream) -> result --- + resource_manager_data_stream_read_pcm_frames :: proc(pDataStream: ^resource_manager_data_stream, pFramesOut: rawptr, frameCount: u64, pFramesRead: ^u64) -> result --- + resource_manager_data_stream_seek_to_pcm_frame :: proc(pDataStream: ^resource_manager_data_stream, frameIndex: u64) -> result --- + resource_manager_data_stream_get_data_format :: proc(pDataStream: ^resource_manager_data_stream, pFormat: ^format, pChannels, pSampleRate: ^u32, pChannelMap: [^]channel, channelMapCap: c.size_t) -> result --- + resource_manager_data_stream_get_cursor_in_pcm_frames :: proc(pDataStream: ^resource_manager_data_stream, pCursor: ^u64) -> result --- + resource_manager_data_stream_get_length_in_pcm_frames :: proc(pDataStream: ^resource_manager_data_stream, pLength: ^u64) -> result --- + resource_manager_data_stream_result :: proc(pDataStream: ^resource_manager_data_stream) -> result --- + resource_manager_data_stream_set_looping :: proc(pDataStream: ^resource_manager_data_stream, isLooping: b32) -> result --- + resource_manager_data_stream_is_looping :: proc(pDataStream: ^resource_manager_data_stream) -> b32 --- + resource_manager_data_stream_get_available_frames :: proc(pDataStream: ^resource_manager_data_stream, pAvailableFrames: ^u64) -> result --- + + /* Data Sources. */ + resource_manager_data_source_init_ex :: proc(pResourceManager: ^resource_manager, pConfig: ^resource_manager_data_source_config, pDataSource: ^resource_manager_data_source) -> result --- + resource_manager_data_source_init :: proc(pResourceManager: ^resource_manager, pName: cstring, flags: u32, pNotifications: ^resource_manager_pipeline_notifications, pDataSource: ^resource_manager_data_source) -> result --- + resource_manager_data_source_init_w :: proc(pResourceManager: ^resource_manager, pName: [^]c.wchar_t, flags: u32, pNotifications: ^resource_manager_pipeline_notifications, pDataSource: ^resource_manager_data_source) -> result --- + resource_manager_data_source_init_copy :: proc(pResourceManager: ^resource_manager, pExistingDataSource, pDataSource: ^resource_manager_data_source) -> result --- + resource_manager_data_source_uninit :: proc(pDataSource: ^resource_manager_data_source) -> result --- + resource_manager_data_source_read_pcm_frames :: proc(pDataSource: ^resource_manager_data_source, pFramesOut: rawptr, frameCount: u64, pFramesRead: ^u64) -> result --- + resource_manager_data_source_seek_to_pcm_frame :: proc(pDataSource: ^resource_manager_data_source, frameIndex: u64) -> result --- + resource_manager_data_source_get_data_format :: proc(pDataSource: ^resource_manager_data_source, pFormat: ^format, pChannels, pSampleRate: ^u32, pChannelMap: [^]channel, channelMapCap: c.size_t) -> result --- + resource_manager_data_source_get_cursor_in_pcm_frames :: proc(pDataSource: ^resource_manager_data_source, pCursor: ^u64) -> result --- + resource_manager_data_source_get_length_in_pcm_frames :: proc(pDataSource: ^resource_manager_data_source, pLength: ^u64) -> result --- + resource_manager_data_source_result :: proc(pDataSource: ^resource_manager_data_source) -> result --- + resource_manager_data_source_set_looping :: proc(pDataSource: ^resource_manager_data_source, isLooping: b32) -> result --- + resource_manager_data_source_is_looping :: proc(pDataSource: ^resource_manager_data_source) -> b32 --- + resource_manager_data_source_get_available_frames :: proc(pDataSource: ^resource_manager_data_source, pAvailableFrames: ^u64) -> result --- + + /* Job management. */ + resource_manager_post_job :: proc(pResourceManager: ^resource_manager, pJob: ^job) -> result --- + resource_manager_post_job_quit :: proc(pResourceManager: ^resource_manager) -> result --- /* Helper for posting a quit job. */ + resource_manager_next_job :: proc(pResourceManager: ^resource_manager, pJob: ^job) -> result --- + resource_manager_process_job :: proc(pResourceManager: ^resource_manager, pJob: ^job) -> result --- /* DEPRECATED. Use ma_job_process(). Will be removed in version 0.12. */ + resource_manager_process_next_job :: proc(pResourceManager: ^resource_manager) -> result --- /* Returns MA_CANCELLED if a MA_JOB_TYPE_QUIT job is found. In non-blocking mode, returns MA_NO_DATA_AVAILABLE if no jobs are available. */ +} diff --git a/vendor/miniaudio/src/Makefile b/vendor/miniaudio/src/Makefile index 7ff72ebdc..3c61b5e2f 100644 --- a/vendor/miniaudio/src/Makefile +++ b/vendor/miniaudio/src/Makefile @@ -1,6 +1,6 @@ all: mkdir -p ../lib - gcc -c -O2 -Os -fPIC miniaudio.c - ar rcs ../lib/miniaudio.a miniaudio.o - #gcc -fPIC -shared -Wl,-soname=miniaudio.so -o ../lib/miniaudio.so miniaudio.o + $(CC) -c -O2 -Os -fPIC miniaudio.c + $(AR) rcs ../lib/miniaudio.a miniaudio.o + #$(CC) -fPIC -shared -Wl,-soname=miniaudio.so -o ../lib/miniaudio.so miniaudio.o rm *.o diff --git a/vendor/miniaudio/src/miniaudio.h b/vendor/miniaudio/src/miniaudio.h index 5793f20d6..f774f0d5f 100644 --- a/vendor/miniaudio/src/miniaudio.h +++ b/vendor/miniaudio/src/miniaudio.h @@ -1,6 +1,6 @@ /* Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. -miniaudio - v0.10.42 - 2021-08-22 +miniaudio - v0.11.9 - 2022-04-20 David Reid - mackron@gmail.com @@ -12,7 +12,8 @@ GitHub: https://github.com/mackron/miniaudio /* 1. Introduction =============== -miniaudio is a single file library for audio playback and capture. To use it, do the following in one .c file: +miniaudio is a single file library for audio playback and capture. To use it, do the following in +one .c file: ```c #define MINIAUDIO_IMPLEMENTATION @@ -21,16 +22,44 @@ miniaudio is a single file library for audio playback and capture. To use it, do You can do `#include "miniaudio.h"` in other parts of the program just like any other header. -miniaudio uses the concept of a "device" as the abstraction for physical devices. The idea is that you choose a physical device to emit or capture audio from, -and then move data to/from the device when miniaudio tells you to. Data is delivered to and from devices asynchronously via a callback which you specify when -initializing the device. +miniaudio includes both low level and high level APIs. The low level API is good for those who want +to do all of their mixing themselves and only require a light weight interface to the underlying +audio device. The high level API is good for those who have complex mixing and effect requirements. -When initializing the device you first need to configure it. The device configuration allows you to specify things like the format of the data delivered via -the callback, the size of the internal buffer and the ID of the device you want to emit or capture audio from. +In miniaudio, objects are transparent structures. Unlike many other libraries, there are no handles +to opaque objects which means you need to allocate memory for objects yourself. In the examples +presented in this documentation you will often see objects declared on the stack. You need to be +careful when translating these examples to your own code so that you don't accidentally declare +your objects on the stack and then cause them to become invalid once the function returns. In +addition, you must ensure the memory address of your objects remain the same throughout their +lifetime. You therefore cannot be making copies of your objects. -Once you have the device configuration set up you can initialize the device. When initializing a device you need to allocate memory for the device object -beforehand. This gives the application complete control over how the memory is allocated. In the example below we initialize a playback device on the stack, -but you could allocate it on the heap if that suits your situation better. +A config/init pattern is used throughout the entire library. The idea is that you set up a config +object and pass that into the initialization routine. The advantage to this system is that the +config object can be initialized with logical defaults and new properties added to it without +breaking the API. The config object can be allocated on the stack and does not need to be +maintained after initialization of the corresponding object. + + +1.1. Low Level API +------------------ +The low level API gives you access to the raw audio data of an audio device. It supports playback, +capture, full-duplex and loopback (WASAPI only). You can enumerate over devices to determine which +physical device(s) you want to connect to. + +The low level API uses the concept of a "device" as the abstraction for physical devices. The idea +is that you choose a physical device to emit or capture audio from, and then move data to/from the +device when miniaudio tells you to. Data is delivered to and from devices asynchronously via a +callback which you specify when initializing the device. + +When initializing the device you first need to configure it. The device configuration allows you to +specify things like the format of the data delivered via the callback, the size of the internal +buffer and the ID of the device you want to emit or capture audio from. + +Once you have the device configuration set up you can initialize the device. When initializing a +device you need to allocate memory for the device object beforehand. This gives the application +complete control over how the memory is allocated. In the example below we initialize a playback +device on the stack, but you could allocate it on the heap if that suits your situation better. ```c void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) @@ -63,20 +92,27 @@ but you could allocate it on the heap if that suits your situation better. } ``` -In the example above, `data_callback()` is where audio data is written and read from the device. The idea is in playback mode you cause sound to be emitted -from the speakers by writing audio data to the output buffer (`pOutput` in the example). In capture mode you read data from the input buffer (`pInput`) to -extract sound captured by the microphone. The `frameCount` parameter tells you how many frames can be written to the output buffer and read from the input -buffer. A "frame" is one sample for each channel. For example, in a stereo stream (2 channels), one frame is 2 samples: one for the left, one for the right. -The channel count is defined by the device config. The size in bytes of an individual sample is defined by the sample format which is also specified in the -device config. Multi-channel audio data is always interleaved, which means the samples for each frame are stored next to each other in memory. For example, in -a stereo stream the first pair of samples will be the left and right samples for the first frame, the second pair of samples will be the left and right samples -for the second frame, etc. +In the example above, `data_callback()` is where audio data is written and read from the device. +The idea is in playback mode you cause sound to be emitted from the speakers by writing audio data +to the output buffer (`pOutput` in the example). In capture mode you read data from the input +buffer (`pInput`) to extract sound captured by the microphone. The `frameCount` parameter tells you +how many frames can be written to the output buffer and read from the input buffer. A "frame" is +one sample for each channel. For example, in a stereo stream (2 channels), one frame is 2 +samples: one for the left, one for the right. The channel count is defined by the device config. +The size in bytes of an individual sample is defined by the sample format which is also specified +in the device config. Multi-channel audio data is always interleaved, which means the samples for +each frame are stored next to each other in memory. For example, in a stereo stream the first pair +of samples will be the left and right samples for the first frame, the second pair of samples will +be the left and right samples for the second frame, etc. -The configuration of the device is defined by the `ma_device_config` structure. The config object is always initialized with `ma_device_config_init()`. It's -important to always initialize the config with this function as it initializes it with logical defaults and ensures your program doesn't break when new members -are added to the `ma_device_config` structure. The example above uses a fairly simple and standard device configuration. The call to `ma_device_config_init()` -takes a single parameter, which is whether or not the device is a playback, capture, duplex or loopback device (loopback devices are not supported on all -backends). The `config.playback.format` member sets the sample format which can be one of the following (all formats are native-endian): +The configuration of the device is defined by the `ma_device_config` structure. The config object +is always initialized with `ma_device_config_init()`. It's important to always initialize the +config with this function as it initializes it with logical defaults and ensures your program +doesn't break when new members are added to the `ma_device_config` structure. The example above +uses a fairly simple and standard device configuration. The call to `ma_device_config_init()` takes +a single parameter, which is whether or not the device is a playback, capture, duplex or loopback +device (loopback devices are not supported on all backends). The `config.playback.format` member +sets the sample format which can be one of the following (all formats are native-endian): +---------------+----------------------------------------+---------------------------+ | Symbol | Description | Range | @@ -88,22 +124,30 @@ backends). The `config.playback.format` member sets the sample format which can | ma_format_u8 | 8-bit unsigned integer | [0, 255] | +---------------+----------------------------------------+---------------------------+ -The `config.playback.channels` member sets the number of channels to use with the device. The channel count cannot exceed MA_MAX_CHANNELS. The -`config.sampleRate` member sets the sample rate (which must be the same for both playback and capture in full-duplex configurations). This is usually set to -44100 or 48000, but can be set to anything. It's recommended to keep this between 8000 and 384000, however. +The `config.playback.channels` member sets the number of channels to use with the device. The +channel count cannot exceed MA_MAX_CHANNELS. The `config.sampleRate` member sets the sample rate +(which must be the same for both playback and capture in full-duplex configurations). This is +usually set to 44100 or 48000, but can be set to anything. It's recommended to keep this between +8000 and 384000, however. -Note that leaving the format, channel count and/or sample rate at their default values will result in the internal device's native configuration being used -which is useful if you want to avoid the overhead of miniaudio's automatic data conversion. +Note that leaving the format, channel count and/or sample rate at their default values will result +in the internal device's native configuration being used which is useful if you want to avoid the +overhead of miniaudio's automatic data conversion. -In addition to the sample format, channel count and sample rate, the data callback and user data pointer are also set via the config. The user data pointer is -not passed into the callback as a parameter, but is instead set to the `pUserData` member of `ma_device` which you can access directly since all miniaudio -structures are transparent. +In addition to the sample format, channel count and sample rate, the data callback and user data +pointer are also set via the config. The user data pointer is not passed into the callback as a +parameter, but is instead set to the `pUserData` member of `ma_device` which you can access +directly since all miniaudio structures are transparent. -Initializing the device is done with `ma_device_init()`. This will return a result code telling you what went wrong, if anything. On success it will return -`MA_SUCCESS`. After initialization is complete the device will be in a stopped state. To start it, use `ma_device_start()`. Uninitializing the device will stop -it, which is what the example above does, but you can also stop the device with `ma_device_stop()`. To resume the device simply call `ma_device_start()` again. -Note that it's important to never stop or start the device from inside the callback. This will result in a deadlock. Instead you set a variable or signal an -event indicating that the device needs to stop and handle it in a different thread. The following APIs must never be called inside the callback: +Initializing the device is done with `ma_device_init()`. This will return a result code telling you +what went wrong, if anything. On success it will return `MA_SUCCESS`. After initialization is +complete the device will be in a stopped state. To start it, use `ma_device_start()`. +Uninitializing the device will stop it, which is what the example above does, but you can also stop +the device with `ma_device_stop()`. To resume the device simply call `ma_device_start()` again. +Note that it's important to never stop or start the device from inside the callback. This will +result in a deadlock. Instead you set a variable or signal an event indicating that the device +needs to stop and handle it in a different thread. The following APIs must never be called inside +the callback: ```c ma_device_init() @@ -113,12 +157,14 @@ event indicating that the device needs to stop and handle it in a different thre ma_device_stop() ``` -You must never try uninitializing and reinitializing a device inside the callback. You must also never try to stop and start it from inside the callback. There -are a few other things you shouldn't do in the callback depending on your requirements, however this isn't so much a thread-safety thing, but rather a -real-time processing thing which is beyond the scope of this introduction. +You must never try uninitializing and reinitializing a device inside the callback. You must also +never try to stop and start it from inside the callback. There are a few other things you shouldn't +do in the callback depending on your requirements, however this isn't so much a thread-safety +thing, but rather a real-time processing thing which is beyond the scope of this introduction. -The example above demonstrates the initialization of a playback device, but it works exactly the same for capture. All you need to do is change the device type -from `ma_device_type_playback` to `ma_device_type_capture` when setting up the config, like so: +The example above demonstrates the initialization of a playback device, but it works exactly the +same for capture. All you need to do is change the device type from `ma_device_type_playback` to +`ma_device_type_capture` when setting up the config, like so: ```c ma_device_config config = ma_device_config_init(ma_device_type_capture); @@ -126,8 +172,9 @@ from `ma_device_type_playback` to `ma_device_type_capture` when setting up the c config.capture.channels = MY_CHANNEL_COUNT; ``` -In the data callback you just read from the input buffer (`pInput` in the example above) and leave the output buffer alone (it will be set to NULL when the -device type is set to `ma_device_type_capture`). +In the data callback you just read from the input buffer (`pInput` in the example above) and leave +the output buffer alone (it will be set to NULL when the device type is set to +`ma_device_type_capture`). These are the available device types and how you should handle the buffers in the callback: @@ -140,23 +187,29 @@ These are the available device types and how you should handle the buffers in th | ma_device_type_loopback | Read from input buffer, leave output buffer untouched. | +-------------------------+--------------------------------------------------------+ -You will notice in the example above that the sample format and channel count is specified separately for playback and capture. This is to support different -data formats between the playback and capture devices in a full-duplex system. An example may be that you want to capture audio data as a monaural stream (one -channel), but output sound to a stereo speaker system. Note that if you use different formats between playback and capture in a full-duplex configuration you -will need to convert the data yourself. There are functions available to help you do this which will be explained later. +You will notice in the example above that the sample format and channel count is specified +separately for playback and capture. This is to support different data formats between the playback +and capture devices in a full-duplex system. An example may be that you want to capture audio data +as a monaural stream (one channel), but output sound to a stereo speaker system. Note that if you +use different formats between playback and capture in a full-duplex configuration you will need to +convert the data yourself. There are functions available to help you do this which will be +explained later. -The example above did not specify a physical device to connect to which means it will use the operating system's default device. If you have multiple physical -devices connected and you want to use a specific one you will need to specify the device ID in the configuration, like so: +The example above did not specify a physical device to connect to which means it will use the +operating system's default device. If you have multiple physical devices connected and you want to +use a specific one you will need to specify the device ID in the configuration, like so: ```c config.playback.pDeviceID = pMyPlaybackDeviceID; // Only if requesting a playback or duplex device. config.capture.pDeviceID = pMyCaptureDeviceID; // Only if requesting a capture, duplex or loopback device. ``` -To retrieve the device ID you will need to perform device enumeration, however this requires the use of a new concept called the "context". Conceptually -speaking the context sits above the device. There is one context to many devices. The purpose of the context is to represent the backend at a more global level -and to perform operations outside the scope of an individual device. Mainly it is used for performing run-time linking against backend libraries, initializing -backends and enumerating devices. The example below shows how to enumerate devices. +To retrieve the device ID you will need to perform device enumeration, however this requires the +use of a new concept called the "context". Conceptually speaking the context sits above the device. +There is one context to many devices. The purpose of the context is to represent the backend at a +more global level and to perform operations outside the scope of an individual device. Mainly it is +used for performing run-time linking against backend libraries, initializing backends and +enumerating devices. The example below shows how to enumerate devices. ```c ma_context context; @@ -197,44 +250,236 @@ backends and enumerating devices. The example below shows how to enumerate devic ma_context_uninit(&context); ``` -The first thing we do in this example is initialize a `ma_context` object with `ma_context_init()`. The first parameter is a pointer to a list of `ma_backend` -values which are used to override the default backend priorities. When this is NULL, as in this example, miniaudio's default priorities are used. The second -parameter is the number of backends listed in the array pointed to by the first parameter. The third parameter is a pointer to a `ma_context_config` object -which can be NULL, in which case defaults are used. The context configuration is used for setting the logging callback, custom memory allocation callbacks, -user-defined data and some backend-specific configurations. +The first thing we do in this example is initialize a `ma_context` object with `ma_context_init()`. +The first parameter is a pointer to a list of `ma_backend` values which are used to override the +default backend priorities. When this is NULL, as in this example, miniaudio's default priorities +are used. The second parameter is the number of backends listed in the array pointed to by the +first parameter. The third parameter is a pointer to a `ma_context_config` object which can be +NULL, in which case defaults are used. The context configuration is used for setting the logging +callback, custom memory allocation callbacks, user-defined data and some backend-specific +configurations. -Once the context has been initialized you can enumerate devices. In the example above we use the simpler `ma_context_get_devices()`, however you can also use a -callback for handling devices by using `ma_context_enumerate_devices()`. When using `ma_context_get_devices()` you provide a pointer to a pointer that will, -upon output, be set to a pointer to a buffer containing a list of `ma_device_info` structures. You also provide a pointer to an unsigned integer that will -receive the number of items in the returned buffer. Do not free the returned buffers as their memory is managed internally by miniaudio. +Once the context has been initialized you can enumerate devices. In the example above we use the +simpler `ma_context_get_devices()`, however you can also use a callback for handling devices by +using `ma_context_enumerate_devices()`. When using `ma_context_get_devices()` you provide a pointer +to a pointer that will, upon output, be set to a pointer to a buffer containing a list of +`ma_device_info` structures. You also provide a pointer to an unsigned integer that will receive +the number of items in the returned buffer. Do not free the returned buffers as their memory is +managed internally by miniaudio. -The `ma_device_info` structure contains an `id` member which is the ID you pass to the device config. It also contains the name of the device which is useful -for presenting a list of devices to the user via the UI. +The `ma_device_info` structure contains an `id` member which is the ID you pass to the device +config. It also contains the name of the device which is useful for presenting a list of devices +to the user via the UI. -When creating your own context you will want to pass it to `ma_device_init()` when initializing the device. Passing in NULL, like we do in the first example, -will result in miniaudio creating the context for you, which you don't want to do since you've already created a context. Note that internally the context is -only tracked by it's pointer which means you must not change the location of the `ma_context` object. If this is an issue, consider using `malloc()` to -allocate memory for the context. +When creating your own context you will want to pass it to `ma_device_init()` when initializing the +device. Passing in NULL, like we do in the first example, will result in miniaudio creating the +context for you, which you don't want to do since you've already created a context. Note that +internally the context is only tracked by it's pointer which means you must not change the location +of the `ma_context` object. If this is an issue, consider using `malloc()` to allocate memory for +the context. + + +1.2. High Level API +------------------- +The high level API consists of three main parts: + + * Resource management for loading and streaming sounds. + * A node graph for advanced mixing and effect processing. + * A high level "engine" that wraps around the resource manager and node graph. + +The resource manager (`ma_resource_manager`) is used for loading sounds. It supports loading sounds +fully into memory and also streaming. It will also deal with reference counting for you which +avoids the same sound being loaded multiple times. + +The node graph is used for mixing and effect processing. The idea is that you connect a number of +nodes into the graph by connecting each node's outputs to another node's inputs. Each node can +implement it's own effect. By chaining nodes together, advanced mixing and effect processing can +be achieved. + +The engine encapsulates both the resource manager and the node graph to create a simple, easy to +use high level API. The resource manager and node graph APIs are covered in more later sections of +this manual. + +The code below shows how you can initialize an engine using it's default configuration. + + ```c + ma_result result; + ma_engine engine; + + result = ma_engine_init(NULL, &engine); + if (result != MA_SUCCESS) { + return result; // Failed to initialize the engine. + } + ``` + +This creates an engine instance which will initialize a device internally which you can access with +`ma_engine_get_device()`. It will also initialize a resource manager for you which can be accessed +with `ma_engine_get_resource_manager()`. The engine itself is a node graph (`ma_node_graph`) which +means you can pass a pointer to the engine object into any of the `ma_node_graph` APIs (with a +cast). Alternatively, you can use `ma_engine_get_node_graph()` instead of a cast. + +Note that all objects in miniaudio, including the `ma_engine` object in the example above, are +transparent structures. There are no handles to opaque structures in miniaudio which means you need +to be mindful of how you declare them. In the example above we are declaring it on the stack, but +this will result in the struct being invalidated once the function encapsulating it returns. If +allocating the engine on the heap is more appropriate, you can easily do so with a standard call +to `malloc()` or whatever heap allocation routine you like: + + ```c + ma_engine* pEngine = malloc(sizeof(*pEngine)); + ``` + +The `ma_engine` API uses the same config/init pattern used all throughout miniaudio. To configure +an engine, you can fill out a `ma_engine_config` object and pass it into the first parameter of +`ma_engine_init()`: + + ```c + ma_result result; + ma_engine engine; + ma_engine_config engineConfig; + + engineConfig = ma_engine_config_init(); + engineConfig.pResourceManager = &myCustomResourceManager; // <-- Initialized as some earlier stage. + + result = ma_engine_init(&engineConfig, &engine); + if (result != MA_SUCCESS) { + return result; + } + ``` + +This creates an engine instance using a custom config. In this particular example it's showing how +you can specify a custom resource manager rather than having the engine initialize one internally. +This is particularly useful if you want to have multiple engine's share the same resource manager. + +The engine must be uninitialized with `ma_engine_uninit()` when it's no longer needed. + +By default the engine will be started, but nothing will be playing because no sounds have been +initialized. The easiest but least flexible way of playing a sound is like so: + + ```c + ma_engine_play_sound(&engine, "my_sound.wav", NULL); + ``` + +This plays what miniaudio calls an "inline" sound. It plays the sound once, and then puts the +internal sound up for recycling. The last parameter is used to specify which sound group the sound +should be associated with which will be explained later. This particular way of playing a sound is +simple, but lacks flexibility and features. A more flexible way of playing a sound is to first +initialize a sound: + + ```c + ma_result result; + ma_sound sound; + + result = ma_sound_init_from_file(&engine, "my_sound.wav", 0, NULL, NULL, &sound); + if (result != MA_SUCCESS) { + return result; + } + + ma_sound_start(&sound); + ``` + +This returns a `ma_sound` object which represents a single instance of the specified sound file. If +you want to play the same file multiple times simultaneously, you need to create one sound for each +instance. + +Sounds should be uninitialized with `ma_sound_uninit()`. + +Sounds are not started by default. Start a sound with `ma_sound_start()` and stop it with +`ma_sound_stop()`. When a sound is stopped, it is not rewound to the start. Use +`ma_sound_seek_to_pcm_frames(&sound, 0)` to seek back to the start of a sound. By default, starting +and stopping sounds happens immediately, but sometimes it might be convenient to schedule the sound +the be started and/or stopped at a specific time. This can be done with the following functions: + + ```c + ma_sound_set_start_time_in_pcm_frames() + ma_sound_set_start_time_in_milliseconds() + ma_sound_set_stop_time_in_pcm_frames() + ma_sound_set_stop_time_in_milliseconds() + ``` + +The start/stop time needs to be specified based on the absolute timer which is controlled by the +engine. The current global time time in PCM frames can be retrieved with `ma_engine_get_time()`. +The engine's global time can be changed with `ma_engine_set_time()` for synchronization purposes if +required. Note that scheduling a start time still requires an explicit call to `ma_sound_start()` +before anything will play: + + ```c + ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time(&engine) + (ma_engine_get_sample_rate(&engine) * 2); + ma_sound_start(&sound); + ``` + +The third parameter of `ma_sound_init_from_file()` is a set of flags that control how the sound be +loaded and a few options on which features should be enabled for that sound. By default, the sound +is synchronously loaded fully into memory straight from the file system without any kind of +decoding. If you want to decode the sound before storing it in memory, you need to specify the +`MA_SOUND_FLAG_DECODE` flag. This is useful if you want to incur the cost of decoding at an earlier +stage, such as a loading stage. Without this option, decoding will happen dynamically at mixing +time which might be too expensive on the audio thread. + +If you want to load the sound asynchronously, you can specify the `MA_SOUND_FLAG_ASYNC` flag. This +will result in `ma_sound_init_from_file()` returning quickly, but the sound will not start playing +until the sound has had some audio decoded. + +The fourth parameter is a pointer to sound group. A sound group is used as a mechanism to organise +sounds into groups which have their own effect processing and volume control. An example is a game +which might have separate groups for sfx, voice and music. Each of these groups have their own +independent volume control. Use `ma_sound_group_init()` or `ma_sound_group_init_ex()` to initialize +a sound group. + +Sounds and sound groups are nodes in the engine's node graph and can be plugged into any `ma_node` +API. This makes it possible to connect sounds and sound groups to effect nodes to produce complex +effect chains. + +A sound can have it's volume changed with `ma_sound_set_volume()`. If you prefer decibel volume +control you can use `ma_volume_db_to_linear()` to convert from decibel representation to linear. + +Panning and pitching is supported with `ma_sound_set_pan()` and `ma_sound_set_pitch()`. If you know +a sound will never have it's pitch changed with `ma_sound_set_pitch()` or via the doppler effect, +you can specify the `MA_SOUND_FLAG_NO_PITCH` flag when initializing the sound for an optimization. + +By default, sounds and sound groups have spatialization enabled. If you don't ever want to +spatialize your sounds, initialize the sound with the `MA_SOUND_FLAG_NO_SPATIALIZATION` flag. The +spatialization model is fairly simple and is roughly on feature parity with OpenAL. HRTF and +environmental occlusion are not currently supported, but planned for the future. The supported +features include: + + * Sound and listener positioning and orientation with cones + * Attenuation models: none, inverse, linear and exponential + * Doppler effect + +Sounds can be faded in and out with `ma_sound_set_fade_in_pcm_frames()`. + +To check if a sound is currently playing, you can use `ma_sound_is_playing()`. To check if a sound +is at the end, use `ma_sound_at_end()`. Looping of a sound can be controlled with +`ma_sound_set_looping()`. Use `ma_sound_is_looping()` to check whether or not the sound is looping. 2. Building =========== -miniaudio should work cleanly out of the box without the need to download or install any dependencies. See below for platform-specific details. +miniaudio should work cleanly out of the box without the need to download or install any +dependencies. See below for platform-specific details. 2.1. Windows ------------ -The Windows build should compile cleanly on all popular compilers without the need to configure any include paths nor link to any libraries. +The Windows build should compile cleanly on all popular compilers without the need to configure any +include paths nor link to any libraries. + +The UWP build may require linking to mmdevapi.lib if you get errors about an unresolved external +symbol for `ActivateAudioInterfaceAsync()`. + 2.2. macOS and iOS ------------------ -The macOS build should compile cleanly without the need to download any dependencies nor link to any libraries or frameworks. The iOS build needs to be -compiled as Objective-C and will need to link the relevant frameworks but should compile cleanly out of the box with Xcode. Compiling through the command line -requires linking to `-lpthread` and `-lm`. +The macOS build should compile cleanly without the need to download any dependencies nor link to +any libraries or frameworks. The iOS build needs to be compiled as Objective-C and will need to +link the relevant frameworks but should compile cleanly out of the box with Xcode. Compiling +through the command line requires linking to `-lpthread` and `-lm`. -Due to the way miniaudio links to frameworks at runtime, your application may not pass Apple's notarization process. To fix this there are two options. The -first is to use the `MA_NO_RUNTIME_LINKING` option, like so: +Due to the way miniaudio links to frameworks at runtime, your application may not pass Apple's +notarization process. To fix this there are two options. The first is to use the +`MA_NO_RUNTIME_LINKING` option, like so: ```c #ifdef __APPLE__ @@ -244,8 +489,9 @@ first is to use the `MA_NO_RUNTIME_LINKING` option, like so: #include "miniaudio.h" ``` -This will require linking with `-framework CoreFoundation -framework CoreAudio -framework AudioUnit`. Alternatively, if you would rather keep using runtime -linking you can add the following to your entitlements.xcent file: +This will require linking with `-framework CoreFoundation -framework CoreAudio -framework AudioUnit`. +Alternatively, if you would rather keep using runtime linking you can add the following to your +entitlements.xcent file: ``` com.apple.security.cs.allow-dyld-environment-variables @@ -254,26 +500,37 @@ linking you can add the following to your entitlements.xcent file: ``` +See this discussion for more info: https://github.com/mackron/miniaudio/issues/203. + 2.3. Linux ---------- -The Linux build only requires linking to `-ldl`, `-lpthread` and `-lm`. You do not need any development packages. +The Linux build only requires linking to `-ldl`, `-lpthread` and `-lm`. You do not need any +development packages. You may need to link with `-latomic` if you're compiling for 32-bit ARM. + 2.4. BSD -------- -The BSD build only requires linking to `-lpthread` and `-lm`. NetBSD uses audio(4), OpenBSD uses sndio and FreeBSD uses OSS. +The BSD build only requires linking to `-lpthread` and `-lm`. NetBSD uses audio(4), OpenBSD uses +sndio and FreeBSD uses OSS. You may need to link with `-latomic` if you're compiling for 32-bit +ARM. + 2.5. Android ------------ -AAudio is the highest priority backend on Android. This should work out of the box without needing any kind of compiler configuration. Support for AAudio -starts with Android 8 which means older versions will fall back to OpenSL|ES which requires API level 16+. +AAudio is the highest priority backend on Android. This should work out of the box without needing +any kind of compiler configuration. Support for AAudio starts with Android 8 which means older +versions will fall back to OpenSL|ES which requires API level 16+. + +There have been reports that the OpenSL|ES backend fails to initialize on some Android based +devices due to `dlopen()` failing to open "libOpenSLES.so". If this happens on your platform +you'll need to disable run-time linking with `MA_NO_RUNTIME_LINKING` and link with -lOpenSLES. -There have been reports that the OpenSL|ES backend fails to initialize on some Android based devices due to `dlopen()` failing to open "libOpenSLES.so". If -this happens on your platform you'll need to disable run-time linking with `MA_NO_RUNTIME_LINKING` and link with -lOpenSLES. 2.6. Emscripten --------------- -The Emscripten build emits Web Audio JavaScript directly and should compile cleanly out of the box. You cannot use -std=c* compiler flags, nor -ansi. +The Emscripten build emits Web Audio JavaScript directly and should compile cleanly out of the box. +You cannot use `-std=c*` compiler flags, nor `-ansi`. 2.7. Build Options @@ -366,28 +623,26 @@ The Emscripten build emits Web Audio JavaScript directly and should compile clea +----------------------------------+--------------------------------------------------------------------+ | MA_NO_MP3 | Disables the built-in MP3 decoder. | +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_DEVICE_IO | Disables playback and recording. This will disable ma_context and | - | | ma_device APIs. This is useful if you only want to use miniaudio's | - | | data conversion and/or decoding APIs. | + | MA_NO_DEVICE_IO | Disables playback and recording. This will disable `ma_context` | + | | and `ma_device` APIs. This is useful if you only want to use | + | | miniaudio's data conversion and/or decoding APIs. | +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_THREADING | Disables the ma_thread, ma_mutex, ma_semaphore and ma_event APIs. | - | | This option is useful if you only need to use miniaudio for data | - | | conversion, decoding and/or encoding. Some families of APIs | - | | require threading which means the following options must also be | - | | set: | + | MA_NO_THREADING | Disables the `ma_thread`, `ma_mutex`, `ma_semaphore` and | + | | `ma_event` APIs. This option is useful if you only need to use | + | | miniaudio for data conversion, decoding and/or encoding. Some | + | | families of APIsrequire threading which means the following | + | | options must also be set: | | | | | | ``` | | | MA_NO_DEVICE_IO | | | ``` | +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_GENERATION | Disables generation APIs such a ma_waveform and ma_noise. | + | MA_NO_GENERATION | Disables generation APIs such a `ma_waveform` and `ma_noise`. | +----------------------------------+--------------------------------------------------------------------+ | MA_NO_SSE2 | Disables SSE2 optimizations. | +----------------------------------+--------------------------------------------------------------------+ | MA_NO_AVX2 | Disables AVX2 optimizations. | +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_AVX512 | Disables AVX-512 optimizations. | - +----------------------------------+--------------------------------------------------------------------+ | MA_NO_NEON | Disables NEON optimizations. | +----------------------------------+--------------------------------------------------------------------+ | MA_NO_RUNTIME_LINKING | Disables runtime linking. This is useful for passing Apple's | @@ -399,47 +654,47 @@ The Emscripten build emits Web Audio JavaScript directly and should compile clea | | You may need to enable this if your target platform does not allow | | | runtime linking via `dlopen()`. | +----------------------------------+--------------------------------------------------------------------+ - | MA_DEBUG_OUTPUT | Enable processing of MA_LOG_LEVEL_DEBUG messages and `printf()` | - | | output. | + | MA_DEBUG_OUTPUT | Enable `printf()` output of debug logs (`MA_LOG_LEVEL_DEBUG`). | +----------------------------------+--------------------------------------------------------------------+ | MA_COINIT_VALUE | Windows only. The value to pass to internal calls to | | | `CoInitializeEx()`. Defaults to `COINIT_MULTITHREADED`. | +----------------------------------+--------------------------------------------------------------------+ | MA_API | Controls how public APIs should be decorated. Default is `extern`. | +----------------------------------+--------------------------------------------------------------------+ - | MA_DLL | If set, configures MA_API to either import or export APIs | - | | depending on whether or not the implementation is being defined. | - | | If defining the implementation, MA_API will be configured to | - | | export. Otherwise it will be configured to import. This has no | - | | effect if MA_API is defined externally. | - +----------------------------------+--------------------------------------------------------------------+ 3. Definitions ============== -This section defines common terms used throughout miniaudio. Unfortunately there is often ambiguity in the use of terms throughout the audio space, so this -section is intended to clarify how miniaudio uses each term. +This section defines common terms used throughout miniaudio. Unfortunately there is often ambiguity +in the use of terms throughout the audio space, so this section is intended to clarify how miniaudio +uses each term. 3.1. Sample ----------- -A sample is a single unit of audio data. If the sample format is f32, then one sample is one 32-bit floating point number. +A sample is a single unit of audio data. If the sample format is f32, then one sample is one 32-bit +floating point number. 3.2. Frame / PCM Frame ---------------------- -A frame is a group of samples equal to the number of channels. For a stereo stream a frame is 2 samples, a mono frame is 1 sample, a 5.1 surround sound frame -is 6 samples, etc. The terms "frame" and "PCM frame" are the same thing in miniaudio. Note that this is different to a compressed frame. If ever miniaudio -needs to refer to a compressed frame, such as a FLAC frame, it will always clarify what it's referring to with something like "FLAC frame". +A frame is a group of samples equal to the number of channels. For a stereo stream a frame is 2 +samples, a mono frame is 1 sample, a 5.1 surround sound frame is 6 samples, etc. The terms "frame" +and "PCM frame" are the same thing in miniaudio. Note that this is different to a compressed frame. +If ever miniaudio needs to refer to a compressed frame, such as a FLAC frame, it will always +clarify what it's referring to with something like "FLAC frame". 3.3. Channel ------------ -A stream of monaural audio that is emitted from an individual speaker in a speaker system, or received from an individual microphone in a microphone system. A -stereo stream has two channels (a left channel, and a right channel), a 5.1 surround sound system has 6 channels, etc. Some audio systems refer to a channel as -a complex audio stream that's mixed with other channels to produce the final mix - this is completely different to miniaudio's use of the term "channel" and -should not be confused. +A stream of monaural audio that is emitted from an individual speaker in a speaker system, or +received from an individual microphone in a microphone system. A stereo stream has two channels (a +left channel, and a right channel), a 5.1 surround sound system has 6 channels, etc. Some audio +systems refer to a channel as a complex audio stream that's mixed with other channels to produce +the final mix - this is completely different to miniaudio's use of the term "channel" and should +not be confused. 3.4. Sample Rate ---------------- -The sample rate in miniaudio is always expressed in Hz, such as 44100, 48000, etc. It's the number of PCM frames that are processed per second. +The sample rate in miniaudio is always expressed in Hz, such as 44100, 48000, etc. It's the number +of PCM frames that are processed per second. 3.5. Formats ------------ @@ -459,10 +714,1685 @@ All formats are native-endian. -4. Decoding +4. Data Sources +=============== +The data source abstraction in miniaudio is used for retrieving audio data from some source. A few +examples include `ma_decoder`, `ma_noise` and `ma_waveform`. You will need to be familiar with data +sources in order to make sense of some of the higher level concepts in miniaudio. + +The `ma_data_source` API is a generic interface for reading from a data source. Any object that +implements the data source interface can be plugged into any `ma_data_source` function. + +To read data from a data source: + + ```c + ma_result result; + ma_uint64 framesRead; + + result = ma_data_source_read_pcm_frames(pDataSource, pFramesOut, frameCount, &framesRead, loop); + if (result != MA_SUCCESS) { + return result; // Failed to read data from the data source. + } + ``` + +If you don't need the number of frames that were successfully read you can pass in `NULL` to the +`pFramesRead` parameter. If this returns a value less than the number of frames requested it means +the end of the file has been reached. `MA_AT_END` will be returned only when the number of frames +read is 0. + +When calling any data source function, with the exception of `ma_data_source_init()` and +`ma_data_source_uninit()`, you can pass in any object that implements a data source. For example, +you could plug in a decoder like so: + + ```c + ma_result result; + ma_uint64 framesRead; + ma_decoder decoder; // <-- This would be initialized with `ma_decoder_init_*()`. + + result = ma_data_source_read_pcm_frames(&decoder, pFramesOut, frameCount, &framesRead, loop); + if (result != MA_SUCCESS) { + return result; // Failed to read data from the decoder. + } + ``` + +If you want to seek forward you can pass in `NULL` to the `pFramesOut` parameter. Alternatively you +can use `ma_data_source_seek_pcm_frames()`. + +To seek to a specific PCM frame: + + ```c + result = ma_data_source_seek_to_pcm_frame(pDataSource, frameIndex); + if (result != MA_SUCCESS) { + return result; // Failed to seek to PCM frame. + } + ``` + +You can retrieve the total length of a data source in PCM frames, but note that some data sources +may not have the notion of a length, such as noise and waveforms, and others may just not have a +way of determining the length such as some decoders. To retrieve the length: + + ```c + ma_uint64 length; + + result = ma_data_source_get_length_in_pcm_frames(pDataSource, &length); + if (result != MA_SUCCESS) { + return result; // Failed to retrieve the length. + } + ``` + +Care should be taken when retrieving the length of a data source where the underlying decoder is +pulling data from a data stream with an undefined length, such as internet radio or some kind of +broadcast. If you do this, `ma_data_source_get_length_in_pcm_frames()` may never return. + +The current position of the cursor in PCM frames can also be retrieved: + + ```c + ma_uint64 cursor; + + result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &cursor); + if (result != MA_SUCCESS) { + return result; // Failed to retrieve the cursor. + } + ``` + +You will often need to know the data format that will be returned after reading. This can be +retrieved like so: + + ```c + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_channel channelMap[MA_MAX_CHANNELS]; + + result = ma_data_source_get_data_format(pDataSource, &format, &channels, &sampleRate, channelMap, MA_MAX_CHANNELS); + if (result != MA_SUCCESS) { + return result; // Failed to retrieve data format. + } + ``` + +If you do not need a specific data format property, just pass in NULL to the respective parameter. + +There may be cases where you want to implement something like a sound bank where you only want to +read data within a certain range of the underlying data. To do this you can use a range: + + ```c + result = ma_data_source_set_range_in_pcm_frames(pDataSource, rangeBegInFrames, rangeEndInFrames); + if (result != MA_SUCCESS) { + return result; // Failed to set the range. + } + ``` + +This is useful if you have a sound bank where many sounds are stored in the same file and you want +the data source to only play one of those sub-sounds. + +Custom loop points can also be used with data sources. By default, data sources will loop after +they reach the end of the data source, but if you need to loop at a specific location, you can do +the following: + + ```c + result = ma_data_set_loop_point_in_pcm_frames(pDataSource, loopBegInFrames, loopEndInFrames); + if (result != MA_SUCCESS) { + return result; // Failed to set the loop point. + } + ``` + +The loop point is relative to the current range. + +It's sometimes useful to chain data sources together so that a seamless transition can be achieved. +To do this, you can use chaining: + + ```c + ma_decoder decoder1; + ma_decoder decoder2; + + // ... initialize decoders with ma_decoder_init_*() ... + + result = ma_data_source_set_next(&decoder1, &decoder2); + if (result != MA_SUCCESS) { + return result; // Failed to set the next data source. + } + + result = ma_data_source_read_pcm_frames(&decoder1, pFramesOut, frameCount, pFramesRead, MA_FALSE); + if (result != MA_SUCCESS) { + return result; // Failed to read from the decoder. + } + ``` + +In the example above we're using decoders. When reading from a chain, you always want to read from +the top level data source in the chain. In the example above, `decoder1` is the top level data +source in the chain. When `decoder1` reaches the end, `decoder2` will start seamlessly without any +gaps. + +Note that the `loop` parameter is set to false in the example above. When this is set to true, only +the current data source will be looped. You can loop the entire chain by linking in a loop like so: + + ```c + ma_data_source_set_next(&decoder1, &decoder2); // decoder1 -> decoder2 + ma_data_source_set_next(&decoder2, &decoder1); // decoder2 -> decoder1 (loop back to the start). + ``` + +Note that setting up chaining is not thread safe, so care needs to be taken if you're dynamically +changing links while the audio thread is in the middle of reading. + +Do not use `ma_decoder_seek_to_pcm_frame()` as a means to reuse a data source to play multiple +instances of the same sound simultaneously. Instead, initialize multiple data sources for each +instance. This can be extremely inefficient depending on the data source and can result in +glitching due to subtle changes to the state of internal filters. + + +4.1. Custom Data Sources +------------------------ +You can implement a custom data source by implementing the functions in `ma_data_source_vtable`. +Your custom object must have `ma_data_source_base` as it's first member: + + ```c + struct my_data_source + { + ma_data_source_base base; + ... + }; + ``` + +In your initialization routine, you need to call `ma_data_source_init()` in order to set up the +base object (`ma_data_source_base`): + + ```c + static ma_result my_data_source_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) + { + // Read data here. Output in the same format returned by my_data_source_get_data_format(). + } + + static ma_result my_data_source_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) + { + // Seek to a specific PCM frame here. Return MA_NOT_IMPLEMENTED if seeking is not supported. + } + + static ma_result my_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) + { + // Return the format of the data here. + } + + static ma_result my_data_source_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) + { + // Retrieve the current position of the cursor here. Return MA_NOT_IMPLEMENTED and set *pCursor to 0 if there is no notion of a cursor. + } + + static ma_result my_data_source_get_length(ma_data_source* pDataSource, ma_uint64* pLength) + { + // Retrieve the length in PCM frames here. Return MA_NOT_IMPLEMENTED and set *pLength to 0 if there is no notion of a length or if the length is unknown. + } + + static g_my_data_source_vtable = + { + my_data_source_read, + my_data_source_seek, + my_data_source_get_data_format, + my_data_source_get_cursor, + my_data_source_get_length + }; + + ma_result my_data_source_init(my_data_source* pMyDataSource) + { + ma_result result; + ma_data_source_config baseConfig; + + baseConfig = ma_data_source_config_init(); + baseConfig.vtable = &g_my_data_source_vtable; + + result = ma_data_source_init(&baseConfig, &pMyDataSource->base); + if (result != MA_SUCCESS) { + return result; + } + + // ... do the initialization of your custom data source here ... + + return MA_SUCCESS; + } + + void my_data_source_uninit(my_data_source* pMyDataSource) + { + // ... do the uninitialization of your custom data source here ... + + // You must uninitialize the base data source. + ma_data_source_uninit(&pMyDataSource->base); + } + ``` + +Note that `ma_data_source_init()` and `ma_data_source_uninit()` are never called directly outside +of the custom data source. It's up to the custom data source itself to call these within their own +init/uninit functions. + + + +5. Engine +========= +The `ma_engine` API is a high level API for managing and mixing sounds and effect processing. The +`ma_engine` object encapsulates a resource manager and a node graph, both of which will be +explained in more detail later. + +Sounds are called `ma_sound` and are created from an engine. Sounds can be associated with a mixing +group called `ma_sound_group` which are also created from the engine. Both `ma_sound` and +`ma_sound_group` objects are nodes within the engine's node graph. + +When the engine is initialized, it will normally create a device internally. If you would rather +manage the device yourself, you can do so and just pass a pointer to it via the engine config when +you initialize the engine. You can also just use the engine without a device, which again can be +configured via the engine config. + +The most basic way to initialize the engine is with a default config, like so: + + ```c + ma_result result; + ma_engine engine; + + result = ma_engine_init(NULL, &engine); + if (result != MA_SUCCESS) { + return result; // Failed to initialize the engine. + } + ``` + +This will result in the engine initializing a playback device using the operating system's default +device. This will be sufficient for many use cases, but if you need more flexibility you'll want to +configure the engine with an engine config: + + ```c + ma_result result; + ma_engine engine; + ma_engine_config engineConfig; + + engineConfig = ma_engine_config_init(); + engineConfig.pPlaybackDevice = &myDevice; + + result = ma_engine_init(&engineConfig, &engine); + if (result != MA_SUCCESS) { + return result; // Failed to initialize the engine. + } + ``` + +In the example above we're passing in a pre-initialized device. Since the caller is the one in +control of the device's data callback, it's their responsibility to manually call +`ma_engine_read_pcm_frames()` from inside their data callback: + + ```c + void playback_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) + { + ma_engine_read_pcm_frames(&g_Engine, pOutput, frameCount, NULL); + } + ``` + +You can also use the engine independent of a device entirely: + + ```c + ma_result result; + ma_engine engine; + ma_engine_config engineConfig; + + engineConfig = ma_engine_config_init(); + engineConfig.noDevice = MA_TRUE; + engineConfig.channels = 2; // Must be set when not using a device. + engineConfig.sampleRate = 48000; // Must be set when not using a device. + + result = ma_engine_init(&engineConfig, &engine); + if (result != MA_SUCCESS) { + return result; // Failed to initialize the engine. + } + ``` + +Note that when you're not using a device, you must set the channel count and sample rate in the +config or else miniaudio won't know what to use (miniaudio will use the device to determine this +normally). When not using a device, you need to use `ma_engine_read_pcm_frames()` to process audio +data from the engine. This kind of setup is useful if you want to do something like offline +processing. + +When a sound is loaded it goes through a resource manager. By default the engine will initialize a +resource manager internally, but you can also specify a pre-initialized resource manager: + + ```c + ma_result result; + ma_engine engine1; + ma_engine engine2; + ma_engine_config engineConfig; + + engineConfig = ma_engine_config_init(); + engineConfig.pResourceManager = &myResourceManager; + + ma_engine_init(&engineConfig, &engine1); + ma_engine_init(&engineConfig, &engine2); + ``` + +In this example we are initializing two engines, both of which are sharing the same resource +manager. This is especially useful for saving memory when loading the same file across multiple +engines. If you were not to use a shared resource manager, each engine instance would use their own +which would result in any sounds that are used between both engine's being loaded twice. By using +a shared resource manager, it would only be loaded once. Using multiple engine's is useful when you +need to output to multiple playback devices, such as in a local multiplayer game where each player +is using their own set of headphones. + +By default an engine will be in a started state. To make it so the engine is not automatically +started you can configure it as such: + + ```c + engineConfig.noAutoStart = MA_TRUE; + + // The engine will need to be started manually. + ma_engine_start(&engine); + + // Later on the engine can be stopped with ma_engine_stop(). + ma_engine_stop(&engine); + ``` + +The concept of starting or stopping an engine is only relevant when using the engine with a +device. Attempting to start or stop an engine that is not associated with a device will result in +`MA_INVALID_OPERATION`. + +The master volume of the engine can be controlled with `ma_engine_set_volume()` which takes a +linear scale, with 0 resulting in silence and anything above 1 resulting in amplification. If you +prefer decibel based volume control, use `ma_volume_db_to_linear()` to convert from dB to linear. + +When a sound is spatialized, it is done so relative to a listener. An engine can be configured to +have multiple listeners which can be configured via the config: + + ```c + engineConfig.listenerCount = 2; + ``` + +The maximum number of listeners is restricted to `MA_ENGINE_MAX_LISTENERS`. By default, when a +sound is spatialized, it will be done so relative to the closest listener. You can also pin a sound +to a specific listener which will be explained later. Listener's have a position, direction, cone, +and velocity (for doppler effect). A listener is referenced by an index, the meaning of which is up +to the caller (the index is 0 based and cannot go beyond the listener count, minus 1). The +position, direction and velocity are all specified in absolute terms: + + ```c + ma_engine_listener_set_position(&engine, listenerIndex, worldPosX, worldPosY, worldPosZ); + ``` + +The direction of the listener represents it's forward vector. The listener's up vector can also be +specified and defaults to +1 on the Y axis. + + ```c + ma_engine_listener_set_direction(&engine, listenerIndex, forwardX, forwardY, forwardZ); + ma_engine_listener_set_world_up(&engine, listenerIndex, 0, 1, 0); + ``` + +The engine supports directional attenuation. The listener can have a cone the controls how sound is +attenuated based on the listener's direction. When a sound is between the inner and outer cones, it +will be attenuated between 1 and the cone's outer gain: + + ```c + ma_engine_listener_set_cone(&engine, listenerIndex, innerAngleInRadians, outerAngleInRadians, outerGain); + ``` + +When a sound is inside the inner code, no directional attenuation is applied. When the sound is +outside of the outer cone, the attenuation will be set to `outerGain` in the example above. When +the sound is in between the inner and outer cones, the attenuation will be interpolated between 1 +and the outer gain. + +The engine's coordinate system follows the OpenGL coordinate system where positive X points right, +positive Y points up and negative Z points forward. + +The simplest and least flexible way to play a sound is like so: + + ```c + ma_engine_play_sound(&engine, "my_sound.wav", pGroup); + ``` + +This is a "fire and forget" style of function. The engine will manage the `ma_sound` object +internally. When the sound finishes playing, it'll be put up for recycling. For more flexibility +you'll want to initialize a sound object: + + ```c + ma_sound sound; + + result = ma_sound_init_from_file(&engine, "my_sound.wav", flags, pGroup, NULL, &sound); + if (result != MA_SUCCESS) { + return result; // Failed to load sound. + } + ``` + +Sounds need to be uninitialized with `ma_sound_uninit()`. + +The example above loads a sound from a file. If the resource manager has been disabled you will not +be able to use this function and instead you'll need to initialize a sound directly from a data +source: + + ```c + ma_sound sound; + + result = ma_sound_init_from_data_source(&engine, &dataSource, flags, pGroup, &sound); + if (result != MA_SUCCESS) { + return result; + } + ``` + +Each `ma_sound` object represents a single instance of the sound. If you want to play the same +sound multiple times at the same time, you need to initialize a separate `ma_sound` object. + +For the most flexibility when initializing sounds, use `ma_sound_init_ex()`. This uses miniaudio's +standard config/init pattern: + + ```c + ma_sound sound; + ma_sound_config soundConfig; + + soundConfig = ma_sound_config_init(); + soundConfig.pFilePath = NULL; // Set this to load from a file path. + soundConfig.pDataSource = NULL; // Set this to initialize from an existing data source. + soundConfig.pInitialAttachment = &someNodeInTheNodeGraph; + soundConfig.initialAttachmentInputBusIndex = 0; + soundConfig.channelsIn = 1; + soundConfig.channelsOut = 0; // Set to 0 to use the engine's native channel count. + + result = ma_sound_init_ex(&soundConfig, &sound); + if (result != MA_SUCCESS) { + return result; + } + ``` + +In the example above, the sound is being initialized without a file nor a data source. This is +valid, in which case the sound acts as a node in the middle of the node graph. This means you can +connect other sounds to this sound and allow it to act like a sound group. Indeed, this is exactly +what a `ma_sound_group` is. + +When loading a sound, you specify a set of flags that control how the sound is loaded and what +features are enabled for that sound. When no flags are set, the sound will be fully loaded into +memory in exactly the same format as how it's stored on the file system. The resource manager will +allocate a block of memory and then load the file directly into it. When reading audio data, it +will be decoded dynamically on the fly. In order to save processing time on the audio thread, it +might be beneficial to pre-decode the sound. You can do this with the `MA_SOUND_FLAG_DECODE` flag: + + ```c + ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_DECODE, pGroup, NULL, &sound); + ``` + +By default, sounds will be loaded synchronously, meaning `ma_sound_init_*()` will not return until +the sound has been fully loaded. If this is prohibitive you can instead load sounds asynchronously +by specificying the `MA_SOUND_FLAG_ASYNC` flag: + + ```c + ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, pGroup, NULL, &sound); + ``` + +This will result in `ma_sound_init_*()` returning quickly, but the sound won't yet have been fully +loaded. When you start the sound, it won't output anything until some sound is available. The sound +will start outputting audio before the sound has been fully decoded when the `MA_SOUND_FLAG_DECODE` +is specified. + +If you need to wait for an asynchronously loaded sound to be fully loaded, you can use a fence. A +fence in miniaudio is a simple synchronization mechanism which simply blocks until it's internal +counter hit's zero. You can specify a fence like so: + + ```c + ma_result result; + ma_fence fence; + ma_sound sounds[4]; + + result = ma_fence_init(&fence); + if (result != MA_SUCCES) { + return result; + } + + // Load some sounds asynchronously. + for (int iSound = 0; iSound < 4; iSound += 1) { + ma_sound_init_from_file(&engine, mySoundFilesPaths[iSound], MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, pGroup, &fence, &sounds[iSound]); + } + + // ... do some other stuff here in the mean time ... + + // Wait for all sounds to finish loading. + ma_fence_wait(&fence); + ``` + +If loading the entire sound into memory is prohibitive, you can also configure the engine to stream +the audio data: + + ```c + ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_STREAM, pGroup, NULL, &sound); + ``` + +When streaming sounds, 2 seconds worth of audio data is stored in memory. Although it should work +fine, it's inefficient to use streaming for short sounds. Streaming is useful for things like music +tracks in games. + +When you initialize a sound, if you specify a sound group the sound will be attached to that group +automatically. If you set it to NULL, it will be automatically attached to the engine's endpoint. +If you would instead rather leave the sound unattached by default, you can can specify the +`MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT` flag. This is useful if you want to set up a complex node +graph. + +Sounds are not started by default. To start a sound, use `ma_sound_start()`. Stop a sound with +`ma_sound_stop()`. + +Sounds can have their volume controlled with `ma_sound_set_volume()` in the same way as the +engine's master volume. + +Sounds support stereo panning and pitching. Set the pan with `ma_sound_set_pan()`. Setting the pan +to 0 will result in an unpanned sound. Setting it to -1 will shift everything to the left, whereas ++1 will shift it to the right. The pitch can be controlled with `ma_sound_set_pitch()`. A larger +value will result in a higher pitch. The pitch must be greater than 0. + +The engine supports 3D spatialization of sounds. By default sounds will have spatialization +enabled, but if a sound does not need to be spatialized it's best to disable it. There are two ways +to disable spatialization of a sound: + + ```c + // Disable spatialization at initialization time via a flag: + ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_NO_SPATIALIZATION, NULL, NULL, &sound); + + // Dynamically disable or enable spatialization post-initialization: + ma_sound_set_spatialization_enabled(&sound, isSpatializationEnabled); + ``` + +By default sounds will be spatialized based on the closest listener. If a sound should always be +spatialized relative to a specific listener it can be pinned to one: + + ```c + ma_sound_set_pinned_listener_index(&sound, listenerIndex); + ``` + +Like listeners, sounds have a position. By default, the position of a sound is in absolute space, +but it can be changed to be relative to a listener: + + ```c + ma_sound_set_positioning(&sound, ma_positioning_relative); + ``` + +Note that relative positioning of a sound only makes sense if there is either only one listener, or +the sound is pinned to a specific listener. To set the position of a sound: + + ```c + ma_sound_set_position(&sound, posX, posY, posZ); + ``` + +The direction works the same way as a listener and represents the sound's forward direction: + + ```c + ma_sound_set_direction(&sound, forwardX, forwardY, forwardZ); + ``` + +Sound's also have a cone for controlling directional attenuation. This works exactly the same as +listeners: + + ```c + ma_sound_set_cone(&sound, innerAngleInRadians, outerAngleInRadians, outerGain); + ``` + +The velocity of a sound is used for doppler effect and can be set as such: + + ```c + ma_sound_set_velocity(&sound, velocityX, velocityY, velocityZ); + ``` + +The engine supports different attenuation models which can be configured on a per-sound basis. By +default the attenuation model is set to `ma_attenuation_model_inverse` which is the equivalent to +OpenAL's `AL_INVERSE_DISTANCE_CLAMPED`. Configure the attenuation model like so: + + ```c + ma_sound_set_attenuation_model(&sound, ma_attenuation_model_inverse); + ``` + +The supported attenuation models include the following: + + +----------------------------------+----------------------------------------------+ + | ma_attenuation_model_none | No distance attenuation. | + +----------------------------------+----------------------------------------------+ + | ma_attenuation_model_inverse | Equivalent to `AL_INVERSE_DISTANCE_CLAMPED`. | + +----------------------------------+----------------------------------------------+ + | ma_attenuation_model_linear | Linear attenuation. | + +----------------------------------+----------------------------------------------+ + | ma_attenuation_model_exponential | Exponential attenuation. | + +----------------------------------+----------------------------------------------+ + +To control how quickly a sound rolls off as it moves away from the listener, you need to configure +the rolloff: + + ```c + ma_sound_set_rolloff(&sound, rolloff); + ``` + +You can control the minimum and maximum gain to apply from spatialization: + + ```c + ma_sound_set_min_gain(&sound, minGain); + ma_sound_set_max_gain(&sound, maxGain); + ``` + +Likewise, in the calculation of attenuation, you can control the minimum and maximum distances for +the attenuation calculation. This is useful if you want to ensure sounds don't drop below a certain +volume after the listener moves further away and to have sounds play a maximum volume when the +listener is within a certain distance: + + ```c + ma_sound_set_min_distance(&sound, minDistance); + ma_sound_set_max_distance(&sound, maxDistance); + ``` + +The engine's spatialization system supports doppler effect. The doppler factor can be configure on +a per-sound basis like so: + + ```c + ma_sound_set_doppler_factor(&sound, dopplerFactor); + ``` + +You can fade sounds in and out with `ma_sound_set_fade_in_pcm_frames()` and +`ma_sound_set_fade_in_milliseconds()`. Set the volume to -1 to use the current volume as the +starting volume: + + ```c + // Fade in over 1 second. + ma_sound_set_fade_in_milliseconds(&sound, 0, 1, 1000); + + // ... sometime later ... + + // Fade out over 1 second, starting from the current volume. + ma_sound_set_fade_in_milliseconds(&sound, -1, 0, 1000); + ``` + +By default sounds will start immediately, but sometimes for timing and synchronization purposes it +can be useful to schedule a sound to start or stop: + + ```c + // Start the sound in 1 second from now. + ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time(&engine) + (ma_engine_get_sample_rate(&engine) * 1)); + + // Stop the sound in 2 seconds from now. + ma_sound_set_stop_time_in_pcm_frames(&sound, ma_engine_get_time(&engine) + (ma_engine_get_sample_rate(&engine) * 2)); + ``` + +Note that scheduling a start time still requires an explicit call to `ma_sound_start()` before +anything will play. + +The time is specified in global time which is controlled by the engine. You can get the engine's +current time with `ma_engine_get_time()`. The engine's global time is incremented automatically as +audio data is read, but it can be reset with `ma_engine_set_time()` in case it needs to be +resynchronized for some reason. + +To determine whether or not a sound is currently playing, use `ma_sound_is_playing()`. This will +take the scheduled start and stop times into account. + +Whether or not a sound should loop can be controlled with `ma_sound_set_looping()`. Sounds will not +be looping by default. Use `ma_sound_is_looping()` to determine whether or not a sound is looping. + +Use `ma_sound_at_end()` to determine whether or not a sound is currently at the end. For a looping +sound this should never return true. + +Internally a sound wraps around a data source. Some APIs exist to control the underlying data +source, mainly for convenience: + + ```c + ma_sound_seek_to_pcm_frame(&sound, frameIndex); + ma_sound_get_data_format(&sound, &format, &channels, &sampleRate, pChannelMap, channelMapCapacity); + ma_sound_get_cursor_in_pcm_frames(&sound, &cursor); + ma_sound_get_length_in_pcm_frames(&sound, &length); + ``` + +Sound groups have the same API as sounds, only they are called `ma_sound_group`, and since they do +not have any notion of a data source, anything relating to a data source is unavailable. + +Internally, sound data is loaded via the `ma_decoder` API which means by default in only supports +file formats that have built-in support in miniaudio. You can extend this to support any kind of +file format through the use of custom decoders. To do this you'll need to use a self-managed +resource manager and configure it appropriately. See the "Resource Management" section below for +details on how to set this up. + + +6. Resource Management +====================== +Many programs will want to manage sound resources for things such as reference counting and +streaming. This is supported by miniaudio via the `ma_resource_manager` API. + +The resource manager is mainly responsible for the following: + + * Loading of sound files into memory with reference counting. + * Streaming of sound data + +When loading a sound file, the resource manager will give you back a `ma_data_source` compatible +object called `ma_resource_manager_data_source`. This object can be passed into any +`ma_data_source` API which is how you can read and seek audio data. When loading a sound file, you +specify whether or not you want the sound to be fully loaded into memory (and optionally +pre-decoded) or streamed. When loading into memory, you can also specify whether or not you want +the data to be loaded asynchronously. + +The example below is how you can initialize a resource manager using it's default configuration: + + ```c + ma_resource_manager_config config; + ma_resource_manager resourceManager; + + config = ma_resource_manager_config_init(); + result = ma_resource_manager_init(&config, &resourceManager); + if (result != MA_SUCCESS) { + ma_device_uninit(&device); + printf("Failed to initialize the resource manager."); + return -1; + } + ``` + +You can configure the format, channels and sample rate of the decoded audio data. By default it +will use the file's native data format, but you can configure it to use a consistent format. This +is useful for offloading the cost of data conversion to load time rather than dynamically +converting at mixing time. To do this, you configure the decoded format, channels and sample rate +like the code below: + + ```c + config = ma_resource_manager_config_init(); + config.decodedFormat = device.playback.format; + config.decodedChannels = device.playback.channels; + config.decodedSampleRate = device.sampleRate; + ``` + +In the code above, the resource manager will be configured so that any decoded audio data will be +pre-converted at load time to the device's native data format. If instead you used defaults and +the data format of the file did not match the device's data format, you would need to convert the +data at mixing time which may be prohibitive in high-performance and large scale scenarios like +games. + +Internally the resource manager uses the `ma_decoder` API to load sounds. This means by default it +only supports decoders that are built into miniaudio. It's possible to support additional encoding +formats through the use of custom decoders. To do so, pass in your `ma_decoding_backend_vtable` +vtables into the resource manager config: + + ```c + ma_decoding_backend_vtable* pCustomBackendVTables[] = + { + &g_ma_decoding_backend_vtable_libvorbis, + &g_ma_decoding_backend_vtable_libopus + }; + + ... + + resourceManagerConfig.ppCustomDecodingBackendVTables = pCustomBackendVTables; + resourceManagerConfig.customDecodingBackendCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]); + resourceManagerConfig.pCustomDecodingBackendUserData = NULL; + ``` + +This system can allow you to support any kind of file format. See the "Decoding" section for +details on how to implement custom decoders. The miniaudio repository includes examples for Opus +via libopus and libopusfile and Vorbis via libvorbis and libvorbisfile. + +Asynchronicity is achieved via a job system. When an operation needs to be performed, such as the +decoding of a page, a job will be posted to a queue which will then be processed by a job thread. +By default there will be only one job thread running, but this can be configured, like so: + + ```c + config = ma_resource_manager_config_init(); + config.jobThreadCount = MY_JOB_THREAD_COUNT; + ``` + +By default job threads are managed internally by the resource manager, however you can also self +manage your job threads if, for example, you want to integrate the job processing into your +existing job infrastructure, or if you simply don't like the way the resource manager does it. To +do this, just set the job thread count to 0 and process jobs manually. To process jobs, you first +need to retrieve a job using `ma_resource_manager_next_job()` and then process it using +`ma_job_process()`: + + ```c + config = ma_resource_manager_config_init(); + config.jobThreadCount = 0; // Don't manage any job threads internally. + config.flags = MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING; // Optional. Makes `ma_resource_manager_next_job()` non-blocking. + + // ... Initialize your custom job threads ... + + void my_custom_job_thread(...) + { + for (;;) { + ma_job job; + ma_result result = ma_resource_manager_next_job(pMyResourceManager, &job); + if (result != MA_SUCCESS) { + if (result == MA_NOT_DATA_AVAILABLE) { + // No jobs are available. Keep going. Will only get this if the resource manager was initialized + // with MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING. + continue; + } else if (result == MA_CANCELLED) { + // MA_JOB_TYPE_QUIT was posted. Exit. + break; + } else { + // Some other error occurred. + break; + } + } + + ma_job_process(&job); + } + } + ``` + +In the example above, the `MA_JOB_TYPE_QUIT` event is the used as the termination +indicator, but you can use whatever you would like to terminate the thread. The call to +`ma_resource_manager_next_job()` is blocking by default, but can be configured to be non-blocking +by initializing the resource manager with the `MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING` configuration +flag. Note that the `MA_JOB_TYPE_QUIT` will never be removed from the job queue. This +is to give every thread the opportunity to catch the event and terminate naturally. + +When loading a file, it's sometimes convenient to be able to customize how files are opened and +read instead of using standard `fopen()`, `fclose()`, etc. which is what miniaudio will use by +default. This can be done by setting `pVFS` member of the resource manager's config: + + ```c + // Initialize your custom VFS object. See documentation for VFS for information on how to do this. + my_custom_vfs vfs = my_custom_vfs_init(); + + config = ma_resource_manager_config_init(); + config.pVFS = &vfs; + ``` + +This is particularly useful in programs like games where you want to read straight from an archive +rather than the normal file system. If you do not specify a custom VFS, the resource manager will +use the operating system's normal file operations. This is default. + +To load a sound file and create a data source, call `ma_resource_manager_data_source_init()`. When +loading a sound you need to specify the file path and options for how the sounds should be loaded. +By default a sound will be loaded synchronously. The returned data source is owned by the caller +which means the caller is responsible for the allocation and freeing of the data source. Below is +an example for initializing a data source: + + ```c + ma_resource_manager_data_source dataSource; + ma_result result = ma_resource_manager_data_source_init(pResourceManager, pFilePath, flags, &dataSource); + if (result != MA_SUCCESS) { + // Error. + } + + // ... + + // A ma_resource_manager_data_source object is compatible with the `ma_data_source` API. To read data, just call + // the `ma_data_source_read_pcm_frames()` like you would with any normal data source. + result = ma_data_source_read_pcm_frames(&dataSource, pDecodedData, frameCount, &framesRead); + if (result != MA_SUCCESS) { + // Failed to read PCM frames. + } + + // ... + + ma_resource_manager_data_source_uninit(pResourceManager, &dataSource); + ``` + +The `flags` parameter specifies how you want to perform loading of the sound file. It can be a +combination of the following flags: + + ``` + MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM + MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE + MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC + MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT + ``` + +When no flags are specified (set to 0), the sound will be fully loaded into memory, but not +decoded, meaning the raw file data will be stored in memory, and then dynamically decoded when +`ma_data_source_read_pcm_frames()` is called. To instead decode the audio data before storing it in +memory, use the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` flag. By default, the sound file will +be loaded synchronously, meaning `ma_resource_manager_data_source_init()` will only return after +the entire file has been loaded. This is good for simplicity, but can be prohibitively slow. You +can instead load the sound asynchronously using the `MA_RESOURCE_MANAGER_DATA_SOURCE_ASYNC` flag. +This will result in `ma_resource_manager_data_source_init()` returning quickly, but no data will be +returned by `ma_data_source_read_pcm_frames()` until some data is available. When no data is +available because the asynchronous decoding hasn't caught up, `MA_BUSY` will be returned by +`ma_data_source_read_pcm_frames()`. + +For large sounds, it's often prohibitive to store the entire file in memory. To mitigate this, you +can instead stream audio data which you can do by specifying the +`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag. When streaming, data will be decoded in 1 +second pages. When a new page needs to be decoded, a job will be posted to the job queue and then +subsequently processed in a job thread. + +For in-memory sounds, reference counting is used to ensure the data is loaded only once. This means +multiple calls to `ma_resource_manager_data_source_init()` with the same file path will result in +the file data only being loaded once. Each call to `ma_resource_manager_data_source_init()` must be +matched up with a call to `ma_resource_manager_data_source_uninit()`. Sometimes it can be useful +for a program to register self-managed raw audio data and associate it with a file path. Use the +`ma_resource_manager_register_*()` and `ma_resource_manager_unregister_*()` APIs to do this. +`ma_resource_manager_register_decoded_data()` is used to associate a pointer to raw, self-managed +decoded audio data in the specified data format with the specified name. Likewise, +`ma_resource_manager_register_encoded_data()` is used to associate a pointer to raw self-managed +encoded audio data (the raw file data) with the specified name. Note that these names need not be +actual file paths. When `ma_resource_manager_data_source_init()` is called (without the +`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag), the resource manager will look for these +explicitly registered data buffers and, if found, will use it as the backing data for the data +source. Note that the resource manager does *not* make a copy of this data so it is up to the +caller to ensure the pointer stays valid for it's lifetime. Use +`ma_resource_manager_unregister_data()` to unregister the self-managed data. You can also use +`ma_resource_manager_register_file()` and `ma_resource_manager_unregister_file()` to register and +unregister a file. It does not make sense to use the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` +flag with a self-managed data pointer. + + +6.1. Asynchronous Loading and Synchronization +--------------------------------------------- +When loading asynchronously, it can be useful to poll whether or not loading has finished. Use +`ma_resource_manager_data_source_result()` to determine this. For in-memory sounds, this will +return `MA_SUCCESS` when the file has been *entirely* decoded. If the sound is still being decoded, +`MA_BUSY` will be returned. Otherwise, some other error code will be returned if the sound failed +to load. For streaming data sources, `MA_SUCCESS` will be returned when the first page has been +decoded and the sound is ready to be played. If the first page is still being decoded, `MA_BUSY` +will be returned. Otherwise, some other error code will be returned if the sound failed to load. + +In addition to polling, you can also use a simple synchronization object called a "fence" to wait +for asynchronously loaded sounds to finish. This is called `ma_fence`. The advantage to using a +fence is that it can be used to wait for a group of sounds to finish loading rather than waiting +for sounds on an individual basis. There are two stages to loading a sound: + + * Initialization of the internal decoder; and + * Completion of decoding of the file (the file is fully decoded) + +You can specify separate fences for each of the different stages. Waiting for the initialization +of the internal decoder is important for when you need to know the sample format, channels and +sample rate of the file. + +The example below shows how you could use a fence when loading a number of sounds: + + ```c + // This fence will be released when all sounds are finished loading entirely. + ma_fence fence; + ma_fence_init(&fence); + + // This will be passed into the initialization routine for each sound. + ma_resource_manager_pipeline_notifications notifications = ma_resource_manager_pipeline_notifications_init(); + notifications.done.pFence = &fence; + + // Now load a bunch of sounds: + for (iSound = 0; iSound < soundCount; iSound += 1) { + ma_resource_manager_data_source_init(pResourceManager, pSoundFilePaths[iSound], flags, ¬ifications, &pSoundSources[iSound]); + } + + // ... DO SOMETHING ELSE WHILE SOUNDS ARE LOADING ... + + // Wait for loading of sounds to finish. + ma_fence_wait(&fence); + ``` + +In the example above we used a fence for waiting until the entire file has been fully decoded. If +you only need to wait for the initialization of the internal decoder to complete, you can use the +`init` member of the `ma_resource_manager_pipeline_notifications` object: + + ```c + notifications.init.pFence = &fence; + ``` + +If a fence is not appropriate for your situation, you can instead use a callback that is fired on +an individual sound basis. This is done in a very similar way to fences: + + ```c + typedef struct + { + ma_async_notification_callbacks cb; + void* pMyData; + } my_notification; + + void my_notification_callback(ma_async_notification* pNotification) + { + my_notification* pMyNotification = (my_notification*)pNotification; + + // Do something in response to the sound finishing loading. + } + + ... + + my_notification myCallback; + myCallback.cb.onSignal = my_notification_callback; + myCallback.pMyData = pMyData; + + ma_resource_manager_pipeline_notifications notifications = ma_resource_manager_pipeline_notifications_init(); + notifications.done.pNotification = &myCallback; + + ma_resource_manager_data_source_init(pResourceManager, "my_sound.wav", flags, ¬ifications, &mySound); + ``` + +In the example above we just extend the `ma_async_notification_callbacks` object and pass an +instantiation into the `ma_resource_manager_pipeline_notifications` in the same way as we did with +the fence, only we set `pNotification` instead of `pFence`. You can set both of these at the same +time and they should both work as expected. If using the `pNotification` system, you need to ensure +your `ma_async_notification_callbacks` object stays valid. + + + +6.2. Resource Manager Implementation Details +-------------------------------------------- +Resources are managed in two main ways: + + * By storing the entire sound inside an in-memory buffer (referred to as a data buffer) + * By streaming audio data on the fly (referred to as a data stream) + +A resource managed data source (`ma_resource_manager_data_source`) encapsulates a data buffer or +data stream, depending on whether or not the data source was initialized with the +`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag. If so, it will make use of a +`ma_resource_manager_data_stream` object. Otherwise it will use a `ma_resource_manager_data_buffer` +object. Both of these objects are data sources which means they can be used with any +`ma_data_source_*()` API. + +Another major feature of the resource manager is the ability to asynchronously decode audio files. +This relieves the audio thread of time-consuming decoding which can negatively affect scalability +due to the audio thread needing to complete it's work extremely quickly to avoid glitching. +Asynchronous decoding is achieved through a job system. There is a central multi-producer, +multi-consumer, fixed-capacity job queue. When some asynchronous work needs to be done, a job is +posted to the queue which is then read by a job thread. The number of job threads can be +configured for improved scalability, and job threads can all run in parallel without needing to +worry about the order of execution (how this is achieved is explained below). + +When a sound is being loaded asynchronously, playback can begin before the sound has been fully +decoded. This enables the application to start playback of the sound quickly, while at the same +time allowing to resource manager to keep loading in the background. Since there may be less +threads than the number of sounds being loaded at a given time, a simple scheduling system is used +to keep decoding time balanced and fair. The resource manager solves this by splitting decoding +into chunks called pages. By default, each page is 1 second long. When a page has been decoded, a +new job will be posted to start decoding the next page. By dividing up decoding into pages, an +individual sound shouldn't ever delay every other sound from having their first page decoded. Of +course, when loading many sounds at the same time, there will always be an amount of time required +to process jobs in the queue so in heavy load situations there will still be some delay. To +determine if a data source is ready to have some frames read, use +`ma_resource_manager_data_source_get_available_frames()`. This will return the number of frames +available starting from the current position. + + +6.2.1. Job Queue +---------------- +The resource manager uses a job queue which is multi-producer, multi-consumer, and fixed-capacity. +This job queue is not currently lock-free, and instead uses a spinlock to achieve thread-safety. +Only a fixed number of jobs can be allocated and inserted into the queue which is done through a +lock-free data structure for allocating an index into a fixed sized array, with reference counting +for mitigation of the ABA problem. The reference count is 32-bit. + +For many types of jobs it's important that they execute in a specific order. In these cases, jobs +are executed serially. For the resource manager, serial execution of jobs is only required on a +per-object basis (per data buffer or per data stream). Each of these objects stores an execution +counter. When a job is posted it is associated with an execution counter. When the job is +processed, it checks if the execution counter of the job equals the execution counter of the +owning object and if so, processes the job. If the counters are not equal, the job will be posted +back onto the job queue for later processing. When the job finishes processing the execution order +of the main object is incremented. This system means the no matter how many job threads are +executing, decoding of an individual sound will always get processed serially. The advantage to +having multiple threads comes into play when loading multiple sounds at the same time. + +The resource manager's job queue is not 100% lock-free and will use a spinlock to achieve +thread-safety for a very small section of code. This is only relevant when the resource manager +uses more than one job thread. If only using a single job thread, which is the default, the +lock should never actually wait in practice. The amount of time spent locking should be quite +short, but it's something to be aware of for those who have pedantic lock-free requirements and +need to use more than one job thread. There are plans to remove this lock in a future version. + +In addition, posting a job will release a semaphore, which on Win32 is implemented with +`ReleaseSemaphore` and on POSIX platforms via a condition variable: + + ```c + pthread_mutex_lock(&pSemaphore->lock); + { + pSemaphore->value += 1; + pthread_cond_signal(&pSemaphore->cond); + } + pthread_mutex_unlock(&pSemaphore->lock); + ``` + +Again, this is relevant for those with strict lock-free requirements in the audio thread. To avoid +this, you can use non-blocking mode (via the `MA_JOB_QUEUE_FLAG_NON_BLOCKING` +flag) and implement your own job processing routine (see the "Resource Manager" section above for +details on how to do this). + + + +6.2.2. Data Buffers +------------------- +When the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag is excluded at initialization time, the +resource manager will try to load the data into an in-memory data buffer. Before doing so, however, +it will first check if the specified file is already loaded. If so, it will increment a reference +counter and just use the already loaded data. This saves both time and memory. When the data buffer +is uninitialized, the reference counter will be decremented. If the counter hits zero, the file +will be unloaded. This is a detail to keep in mind because it could result in excessive loading and +unloading of a sound. For example, the following sequence will result in a file be loaded twice, +once after the other: + + ```c + ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer0); // Refcount = 1. Initial load. + ma_resource_manager_data_source_uninit(pResourceManager, &myDataBuffer0); // Refcount = 0. Unloaded. + + ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer1); // Refcount = 1. Reloaded because previous uninit() unloaded it. + ma_resource_manager_data_source_uninit(pResourceManager, &myDataBuffer1); // Refcount = 0. Unloaded. + ``` + +A binary search tree (BST) is used for storing data buffers as it has good balance between +efficiency and simplicity. The key of the BST is a 64-bit hash of the file path that was passed +into `ma_resource_manager_data_source_init()`. The advantage of using a hash is that it saves +memory over storing the entire path, has faster comparisons, and results in a mostly balanced BST +due to the random nature of the hash. The disadvantage is that file names are case-sensitive. If +this is an issue, you should normalize your file names to upper- or lower-case before initializing +your data sources. + +When a sound file has not already been loaded and the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` +flag is excluded, the file will be decoded synchronously by the calling thread. There are two +options for controlling how the audio is stored in the data buffer - encoded or decoded. When the +`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` option is excluded, the raw file data will be stored +in memory. Otherwise the sound will be decoded before storing it in memory. Synchronous loading is +a very simple and standard process of simply adding an item to the BST, allocating a block of +memory and then decoding (if `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` is specified). + +When the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` flag is specified, loading of the data buffer +is done asynchronously. In this case, a job is posted to the queue to start loading and then the +function immediately returns, setting an internal result code to `MA_BUSY`. This result code is +returned when the program calls `ma_resource_manager_data_source_result()`. When decoding has fully +completed `MA_SUCCESS` will be returned. This can be used to know if loading has fully completed. + +When loading asynchronously, a single job is posted to the queue of the type +`MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE`. This involves making a copy of the file path and +associating it with job. When the job is processed by the job thread, it will first load the file +using the VFS associated with the resource manager. When using a custom VFS, it's important that it +be completely thread-safe because it will be used from one or more job threads at the same time. +Individual files should only ever be accessed by one thread at a time, however. After opening the +file via the VFS, the job will determine whether or not the file is being decoded. If not, it +simply allocates a block of memory and loads the raw file contents into it and returns. On the +other hand, when the file is being decoded, it will first allocate a decoder on the heap and +initialize it. Then it will check if the length of the file is known. If so it will allocate a +block of memory to store the decoded output and initialize it to silence. If the size is unknown, +it will allocate room for one page. After memory has been allocated, the first page will be +decoded. If the sound is shorter than a page, the result code will be set to `MA_SUCCESS` and the +completion event will be signalled and loading is now complete. If, however, there is more to +decode, a job with the code `MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE` is posted. This job +will decode the next page and perform the same process if it reaches the end. If there is more to +decode, the job will post another `MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE` job which will +keep on happening until the sound has been fully decoded. For sounds of an unknown length, each +page will be linked together as a linked list. Internally this is implemented via the +`ma_paged_audio_buffer` object. + + +6.2.3. Data Streams +------------------- +Data streams only ever store two pages worth of data for each instance. They are most useful for +large sounds like music tracks in games that would consume too much memory if fully decoded in +memory. After every frame from a page has been read, a job will be posted to load the next page +which is done from the VFS. + +For data streams, the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` flag will determine whether or +not initialization of the data source waits until the two pages have been decoded. When unset, +`ma_resource_manager_data_source_init()` will wait until the two pages have been loaded, otherwise +it will return immediately. + +When frames are read from a data stream using `ma_resource_manager_data_source_read_pcm_frames()`, +`MA_BUSY` will be returned if there are no frames available. If there are some frames available, +but less than the number requested, `MA_SUCCESS` will be returned, but the actual number of frames +read will be less than the number requested. Due to the asynchronous nature of data streams, +seeking is also asynchronous. If the data stream is in the middle of a seek, `MA_BUSY` will be +returned when trying to read frames. + +When `ma_resource_manager_data_source_read_pcm_frames()` results in a page getting fully consumed +a job is posted to load the next page. This will be posted from the same thread that called +`ma_resource_manager_data_source_read_pcm_frames()`. + +Data streams are uninitialized by posting a job to the queue, but the function won't return until +that job has been processed. The reason for this is that the caller owns the data stream object and +therefore miniaudio needs to ensure everything completes before handing back control to the caller. +Also, if the data stream is uninitialized while pages are in the middle of decoding, they must +complete before destroying any underlying object and the job system handles this cleanly. + +Note that when a new page needs to be loaded, a job will be posted to the resource manager's job +thread from the audio thread. You must keep in mind the details mentioned in the "Job Queue" +section above regarding locking when posting an event if you require a strictly lock-free audio +thread. + + + +7. Node Graph +============= +miniaudio's routing infrastructure follows a node graph paradigm. The idea is that you create a +node whose outputs are attached to inputs of another node, thereby creating a graph. There are +different types of nodes, with each node in the graph processing input data to produce output, +which is then fed through the chain. Each node in the graph can apply their own custom effects. At +the start of the graph will usually be one or more data source nodes which have no inputs, but +instead pull their data from a data source. At the end of the graph is an endpoint which represents +the end of the chain and is where the final output is ultimately extracted from. + +Each node has a number of input buses and a number of output buses. An output bus from a node is +attached to an input bus of another. Multiple nodes can connect their output buses to another +node's input bus, in which case their outputs will be mixed before processing by the node. Below is +a diagram that illustrates a hypothetical node graph setup: + + ``` + >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Data flows left to right >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + +---------------+ +-----------------+ + | Data Source 1 =----+ +----------+ +----= Low Pass Filter =----+ + +---------------+ | | =----+ +-----------------+ | +----------+ + +----= Splitter | +----= ENDPOINT | + +---------------+ | | =----+ +-----------------+ | +----------+ + | Data Source 2 =----+ +----------+ +----= Echo / Delay =----+ + +---------------+ +-----------------+ + ``` + +In the above graph, it starts with two data sources whose outputs are attached to the input of a +splitter node. It's at this point that the two data sources are mixed. After mixing, the splitter +performs it's processing routine and produces two outputs which is simply a duplication of the +input stream. One output is attached to a low pass filter, whereas the other output is attached to +a echo/delay. The outputs of the the low pass filter and the echo are attached to the endpoint, and +since they're both connected to the same input but, they'll be mixed. + +Each input bus must be configured to accept the same number of channels, but the number of channels +used by input buses can be different to the number of channels for output buses in which case +miniaudio will automatically convert the input data to the output channel count before processing. +The number of channels of an output bus of one node must match the channel count of the input bus +it's attached to. The channel counts cannot be changed after the node has been initialized. If you +attempt to attach an output bus to an input bus with a different channel count, attachment will +fail. + +To use a node graph, you first need to initialize a `ma_node_graph` object. This is essentially a +container around the entire graph. The `ma_node_graph` object is required for some thread-safety +issues which will be explained later. A `ma_node_graph` object is initialized using miniaudio's +standard config/init system: + + ```c + ma_node_graph_config nodeGraphConfig = ma_node_graph_config_init(myChannelCount); + + result = ma_node_graph_init(&nodeGraphConfig, NULL, &nodeGraph); // Second parameter is a pointer to allocation callbacks. + if (result != MA_SUCCESS) { + // Failed to initialize node graph. + } + ``` + +When you initialize the node graph, you're specifying the channel count of the endpoint. The +endpoint is a special node which has one input bus and one output bus, both of which have the +same channel count, which is specified in the config. Any nodes that connect directly to the +endpoint must be configured such that their output buses have the same channel count. When you read +audio data from the node graph, it'll have the channel count you specified in the config. To read +data from the graph: + + ```c + ma_uint32 framesRead; + result = ma_node_graph_read_pcm_frames(&nodeGraph, pFramesOut, frameCount, &framesRead); + if (result != MA_SUCCESS) { + // Failed to read data from the node graph. + } + ``` + +When you read audio data, miniaudio starts at the node graph's endpoint node which then pulls in +data from it's input attachments, which in turn recusively pull in data from their inputs, and so +on. At the start of the graph there will be some kind of data source node which will have zero +inputs and will instead read directly from a data source. The base nodes don't literally need to +read from a `ma_data_source` object, but they will always have some kind of underlying object that +sources some kind of audio. The `ma_data_source_node` node can be used to read from a +`ma_data_source`. Data is always in floating-point format and in the number of channels you +specified when the graph was initialized. The sample rate is defined by the underlying data sources. +It's up to you to ensure they use a consistent and appropraite sample rate. + +The `ma_node` API is designed to allow custom nodes to be implemented with relative ease, but +miniaudio includes a few stock nodes for common functionality. This is how you would initialize a +node which reads directly from a data source (`ma_data_source_node`) which is an example of one +of the stock nodes that comes with miniaudio: + + ```c + ma_data_source_node_config config = ma_data_source_node_config_init(pMyDataSource); + + ma_data_source_node dataSourceNode; + result = ma_data_source_node_init(&nodeGraph, &config, NULL, &dataSourceNode); + if (result != MA_SUCCESS) { + // Failed to create data source node. + } + ``` + +The data source node will use the output channel count to determine the channel count of the output +bus. There will be 1 output bus and 0 input buses (data will be drawn directly from the data +source). The data source must output to floating-point (`ma_format_f32`) or else an error will be +returned from `ma_data_source_node_init()`. + +By default the node will not be attached to the graph. To do so, use `ma_node_attach_output_bus()`: + + ```c + result = ma_node_attach_output_bus(&dataSourceNode, 0, ma_node_graph_get_endpoint(&nodeGraph), 0); + if (result != MA_SUCCESS) { + // Failed to attach node. + } + ``` + +The code above connects the data source node directly to the endpoint. Since the data source node +has only a single output bus, the index will always be 0. Likewise, the endpoint only has a single +input bus which means the input bus index will also always be 0. + +To detach a specific output bus, use `ma_node_detach_output_bus()`. To detach all output buses, use +`ma_node_detach_all_output_buses()`. If you want to just move the output bus from one attachment to +another, you do not need to detach first. You can just call `ma_node_attach_output_bus()` and it'll +deal with it for you. + +Less frequently you may want to create a specialized node. This will be a node where you implement +your own processing callback to apply a custom effect of some kind. This is similar to initalizing +one of the stock node types, only this time you need to specify a pointer to a vtable containing a +pointer to the processing function and the number of input and output buses. Example: + + ```c + static void my_custom_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) + { + // Do some processing of ppFramesIn (one stream of audio data per input bus) + const float* pFramesIn_0 = ppFramesIn[0]; // Input bus @ index 0. + const float* pFramesIn_1 = ppFramesIn[1]; // Input bus @ index 1. + float* pFramesOut_0 = ppFramesOut[0]; // Output bus @ index 0. + + // Do some processing. On input, `pFrameCountIn` will be the number of input frames in each + // buffer in `ppFramesIn` and `pFrameCountOut` will be the capacity of each of the buffers + // in `ppFramesOut`. On output, `pFrameCountIn` should be set to the number of input frames + // your node consumed and `pFrameCountOut` should be set the number of output frames that + // were produced. + // + // You should process as many frames as you can. If your effect consumes input frames at the + // same rate as output frames (always the case, unless you're doing resampling), you need + // only look at `ppFramesOut` and process that exact number of frames. If you're doing + // resampling, you'll need to be sure to set both `pFrameCountIn` and `pFrameCountOut` + // properly. + } + + static ma_node_vtable my_custom_node_vtable = + { + my_custom_node_process_pcm_frames, // The function that will be called process your custom node. This is where you'd implement your effect processing. + NULL, // Optional. A callback for calculating the number of input frames that are required to process a specified number of output frames. + 2, // 2 input buses. + 1, // 1 output bus. + 0 // Default flags. + }; + + ... + + // Each bus needs to have a channel count specified. To do this you need to specify the channel + // counts in an array and then pass that into the node config. + ma_uint32 inputChannels[2]; // Equal in size to the number of input channels specified in the vtable. + ma_uint32 outputChannels[1]; // Equal in size to the number of output channels specicied in the vtable. + + inputChannels[0] = channelsIn; + inputChannels[1] = channelsIn; + outputChannels[0] = channelsOut; + + ma_node_config nodeConfig = ma_node_config_init(); + nodeConfig.vtable = &my_custom_node_vtable; + nodeConfig.pInputChannels = inputChannels; + nodeConfig.pOutputChannels = outputChannels; + + ma_node_base node; + result = ma_node_init(&nodeGraph, &nodeConfig, NULL, &node); + if (result != MA_SUCCESS) { + // Failed to initialize node. + } + ``` + +When initializing a custom node, as in the code above, you'll normally just place your vtable in +static space. The number of input and output buses are specified as part of the vtable. If you need +a variable number of buses on a per-node bases, the vtable should have the relevant bus count set +to `MA_NODE_BUS_COUNT_UNKNOWN`. In this case, the bus count should be set in the node config: + + ```c + static ma_node_vtable my_custom_node_vtable = + { + my_custom_node_process_pcm_frames, // The function that will be called process your custom node. This is where you'd implement your effect processing. + NULL, // Optional. A callback for calculating the number of input frames that are required to process a specified number of output frames. + MA_NODE_BUS_COUNT_UNKNOWN, // The number of input buses is determined on a per-node basis. + 1, // 1 output bus. + 0 // Default flags. + }; + + ... + + ma_node_config nodeConfig = ma_node_config_init(); + nodeConfig.vtable = &my_custom_node_vtable; + nodeConfig.inputBusCount = myBusCount; // <-- Since the vtable specifies MA_NODE_BUS_COUNT_UNKNOWN, the input bus count should be set here. + nodeConfig.pInputChannels = inputChannels; // <-- Make sure there are nodeConfig.inputBusCount elements in this array. + nodeConfig.pOutputChannels = outputChannels; // <-- The vtable specifies 1 output bus, so there must be 1 element in this array. + ``` + +In the above example it's important to never set the `inputBusCount` and `outputBusCount` members +to anything other than their defaults if the vtable specifies an explicit count. They can only be +set if the vtable specifies MA_NODE_BUS_COUNT_UNKNOWN in the relevant bus count. + +Most often you'll want to create a structure to encapsulate your node with some extra data. You +need to make sure the `ma_node_base` object is your first member of the structure: + + ```c + typedef struct + { + ma_node_base base; // <-- Make sure this is always the first member. + float someCustomData; + } my_custom_node; + ``` + +By doing this, your object will be compatible with all `ma_node` APIs and you can attach it to the +graph just like any other node. + +In the custom processing callback (`my_custom_node_process_pcm_frames()` in the example above), the +number of channels for each bus is what was specified by the config when the node was initialized +with `ma_node_init()`. In addition, all attachments to each of the input buses will have been +pre-mixed by miniaudio. The config allows you to specify different channel counts for each +individual input and output bus. It's up to the effect to handle it appropriate, and if it can't, +return an error in it's initialization routine. + +Custom nodes can be assigned some flags to describe their behaviour. These are set via the vtable +and include the following: + + +-----------------------------------------+---------------------------------------------------+ + | Flag Name | Description | + +-----------------------------------------+---------------------------------------------------+ + | MA_NODE_FLAG_PASSTHROUGH | Useful for nodes that do not do any kind of audio | + | | processing, but are instead used for tracking | + | | time, handling events, etc. Also used by the | + | | internal endpoint node. It reads directly from | + | | the input bus to the output bus. Nodes with this | + | | flag must have exactly 1 input bus and 1 output | + | | bus, and both buses must have the same channel | + | | counts. | + +-----------------------------------------+---------------------------------------------------+ + | MA_NODE_FLAG_CONTINUOUS_PROCESSING | Causes the processing callback to be called even | + | | when no data is available to be read from input | + | | attachments. This is useful for effects like | + | | echos where there will be a tail of audio data | + | | that still needs to be processed even when the | + | | original data sources have reached their ends. | + +-----------------------------------------+---------------------------------------------------+ + | MA_NODE_FLAG_ALLOW_NULL_INPUT | Used in conjunction with | + | | `MA_NODE_FLAG_CONTINUOUS_PROCESSING`. When this | + | | is set, the `ppFramesIn` parameter of the | + | | processing callback will be set to NULL when | + | | there are no input frames are available. When | + | | this is unset, silence will be posted to the | + | | processing callback. | + +-----------------------------------------+---------------------------------------------------+ + | MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES | Used to tell miniaudio that input and output | + | | frames are processed at different rates. You | + | | should set this for any nodes that perform | + | | resampling. | + +-----------------------------------------+---------------------------------------------------+ + | MA_NODE_FLAG_SILENT_OUTPUT | Used to tell miniaudio that a node produces only | + | | silent output. This is useful for nodes where you | + | | don't want the output to contribute to the final | + | | mix. An example might be if you want split your | + | | stream and have one branch be output to a file. | + | | When using this flag, you should avoid writing to | + | | the output buffer of the node's processing | + | | callback because miniaudio will ignore it anyway. | + +-----------------------------------------+---------------------------------------------------+ + + +If you need to make a copy of an audio stream for effect processing you can use a splitter node +called `ma_splitter_node`. This takes has 1 input bus and splits the stream into 2 output buses. +You can use it like this: + + ```c + ma_splitter_node_config splitterNodeConfig = ma_splitter_node_config_init(channelsIn, channelsOut); + + ma_splitter_node splitterNode; + result = ma_splitter_node_init(&nodeGraph, &splitterNodeConfig, NULL, &splitterNode); + if (result != MA_SUCCESS) { + // Failed to create node. + } + + // Attach your output buses to two different input buses (can be on two different nodes). + ma_node_attach_output_bus(&splitterNode, 0, ma_node_graph_get_endpoint(&nodeGraph), 0); // Attach directly to the endpoint. + ma_node_attach_output_bus(&splitterNode, 1, &myEffectNode, 0); // Attach to input bus 0 of some effect node. + ``` + +The volume of an output bus can be configured on a per-bus basis: + + ```c + ma_node_set_output_bus_volume(&splitterNode, 0, 0.5f); + ma_node_set_output_bus_volume(&splitterNode, 1, 0.5f); + ``` + +In the code above we're using the splitter node from before and changing the volume of each of the +copied streams. + +You can start and stop a node with the following: + + ```c + ma_node_set_state(&splitterNode, ma_node_state_started); // The default state. + ma_node_set_state(&splitterNode, ma_node_state_stopped); + ``` + +By default the node is in a started state, but since it won't be connected to anything won't +actually be invoked by the node graph until it's connected. When you stop a node, data will not be +read from any of it's input connections. You can use this property to stop a group of sounds +atomically. + +You can configure the initial state of a node in it's config: + + ```c + nodeConfig.initialState = ma_node_state_stopped; + ``` + +Note that for the stock specialized nodes, all of their configs will have a `nodeConfig` member +which is the config to use with the base node. This is where the initial state can be configured +for specialized nodes: + + ```c + dataSourceNodeConfig.nodeConfig.initialState = ma_node_state_stopped; + ``` + +When using a specialized node like `ma_data_source_node` or `ma_splitter_node`, be sure to not +modify the `vtable` member of the `nodeConfig` object. + + +7.1. Timing +----------- +The node graph supports starting and stopping nodes at scheduled times. This is especially useful +for data source nodes where you want to get the node set up, but only start playback at a specific +time. There are two clocks: local and global. + +A local clock is per-node, whereas the global clock is per graph. Scheduling starts and stops can +only be done based on the global clock because the local clock will not be running while the node +is stopped. The global clocks advances whenever `ma_node_graph_read_pcm_frames()` is called. On the +other hand, the local clock only advances when the node's processing callback is fired, and is +advanced based on the output frame count. + +To retrieve the global time, use `ma_node_graph_get_time()`. The global time can be set with +`ma_node_graph_set_time()` which might be useful if you want to do seeking on a global timeline. +Getting and setting the local time is similar. Use `ma_node_get_time()` to retrieve the local time, +and `ma_node_set_time()` to set the local time. The global and local times will be advanced by the +audio thread, so care should be taken to avoid data races. Ideally you should avoid calling these +outside of the node processing callbacks which are always run on the audio thread. + +There is basic support for scheduling the starting and stopping of nodes. You can only schedule one +start and one stop at a time. This is mainly intended for putting nodes into a started or stopped +state in a frame-exact manner. Without this mechanism, starting and stopping of a node is limited +to the resolution of a call to `ma_node_graph_read_pcm_frames()` which would typically be in blocks +of several milliseconds. The following APIs can be used for scheduling node states: + + ```c + ma_node_set_state_time() + ma_node_get_state_time() + ``` + +The time is absolute and must be based on the global clock. An example is below: + + ```c + ma_node_set_state_time(&myNode, ma_node_state_started, sampleRate*1); // Delay starting to 1 second. + ma_node_set_state_time(&myNode, ma_node_state_stopped, sampleRate*5); // Delay stopping to 5 seconds. + ``` + +An example for changing the state using a relative time. + + ```c + ma_node_set_state_time(&myNode, ma_node_state_started, sampleRate*1 + ma_node_graph_get_time(&myNodeGraph)); + ma_node_set_state_time(&myNode, ma_node_state_stopped, sampleRate*5 + ma_node_graph_get_time(&myNodeGraph)); + ``` + +Note that due to the nature of multi-threading the times may not be 100% exact. If this is an +issue, consider scheduling state changes from within a processing callback. An idea might be to +have some kind of passthrough trigger node that is used specifically for tracking time and handling +events. + + + +7.2. Thread Safety and Locking +------------------------------ +When processing audio, it's ideal not to have any kind of locking in the audio thread. Since it's +expected that `ma_node_graph_read_pcm_frames()` would be run on the audio thread, it does so +without the use of any locks. This section discusses the implementation used by miniaudio and goes +over some of the compromises employed by miniaudio to achieve this goal. Note that the current +implementation may not be ideal - feedback and critiques are most welcome. + +The node graph API is not *entirely* lock-free. Only `ma_node_graph_read_pcm_frames()` is expected +to be lock-free. Attachment, detachment and uninitialization of nodes use locks to simplify the +implementation, but are crafted in a way such that such locking is not required when reading audio +data from the graph. Locking in these areas are achieved by means of spinlocks. + +The main complication with keeping `ma_node_graph_read_pcm_frames()` lock-free stems from the fact +that a node can be uninitialized, and it's memory potentially freed, while in the middle of being +processed on the audio thread. There are times when the audio thread will be referencing a node, +which means the uninitialization process of a node needs to make sure it delays returning until the +audio thread is finished so that control is not handed back to the caller thereby giving them a +chance to free the node's memory. + +When the audio thread is processing a node, it does so by reading from each of the output buses of +the node. In order for a node to process data for one of it's output buses, it needs to read from +each of it's input buses, and so on an so forth. It follows that once all output buses of a node +are detached, the node as a whole will be disconnected and no further processing will occur unless +it's output buses are reattached, which won't be happening when the node is being uninitialized. +By having `ma_node_detach_output_bus()` wait until the audio thread is finished with it, we can +simplify a few things, at the expense of making `ma_node_detach_output_bus()` a bit slower. By +doing this, the implementation of `ma_node_uninit()` becomes trivial - just detach all output +nodes, followed by each of the attachments to each of it's input nodes, and then do any final clean +up. + +With the above design, the worst-case scenario is `ma_node_detach_output_bus()` taking as long as +it takes to process the output bus being detached. This will happen if it's called at just the +wrong moment where the audio thread has just iterated it and has just started processing. The +caller of `ma_node_detach_output_bus()` will stall until the audio thread is finished, which +includes the cost of recursively processing it's inputs. This is the biggest compromise made with +the approach taken by miniaudio for it's lock-free processing system. The cost of detaching nodes +earlier in the pipeline (data sources, for example) will be cheaper than the cost of detaching +higher level nodes, such as some kind of final post-processing endpoint. If you need to do mass +detachments, detach starting from the lowest level nodes and work your way towards the final +endpoint node (but don't try detaching the node graph's endpoint). If the audio thread is not +running, detachment will be fast and detachment in any order will be the same. The reason nodes +need to wait for their input attachments to complete is due to the potential for desyncs between +data sources. If the node was to terminate processing mid way through processing it's inputs, +there's a chance that some of the underlying data sources will have been read, but then others not. +That will then result in a potential desynchronization when detaching and reattaching higher-level +nodes. A possible solution to this is to have an option when detaching to terminate processing +before processing all input attachments which should be fairly simple. + +Another compromise, albeit less significant, is locking when attaching and detaching nodes. This +locking is achieved by means of a spinlock in order to reduce memory overhead. A lock is present +for each input bus and output bus. When an output bus is connected to an input bus, both the output +bus and input bus is locked. This locking is specifically for attaching and detaching across +different threads and does not affect `ma_node_graph_read_pcm_frames()` in any way. The locking and +unlocking is mostly self-explanatory, but a slightly less intuitive aspect comes into it when +considering that iterating over attachments must not break as a result of attaching or detaching a +node while iteration is occuring. + +Attaching and detaching are both quite simple. When an output bus of a node is attached to an input +bus of another node, it's added to a linked list. Basically, an input bus is a linked list, where +each item in the list is and output bus. We have some intentional (and convenient) restrictions on +what can done with the linked list in order to simplify the implementation. First of all, whenever +something needs to iterate over the list, it must do so in a forward direction. Backwards iteration +is not supported. Also, items can only be added to the start of the list. + +The linked list is a doubly-linked list where each item in the list (an output bus) holds a pointer +to the next item in the list, and another to the previous item. A pointer to the previous item is +only required for fast detachment of the node - it is never used in iteration. This is an +important property because it means from the perspective of iteration, attaching and detaching of +an item can be done with a single atomic assignment. This is exploited by both the attachment and +detachment process. When attaching the node, the first thing that is done is the setting of the +local "next" and "previous" pointers of the node. After that, the item is "attached" to the list +by simply performing an atomic exchange with the head pointer. After that, the node is "attached" +to the list from the perspective of iteration. Even though the "previous" pointer of the next item +hasn't yet been set, from the perspective of iteration it's been attached because iteration will +only be happening in a forward direction which means the "previous" pointer won't actually ever get +used. The same general process applies to detachment. See `ma_node_attach_output_bus()` and +`ma_node_detach_output_bus()` for the implementation of this mechanism. + + + +8. Decoding =========== -The `ma_decoder` API is used for reading audio files. Decoders are completely decoupled from devices and can be used independently. The following formats are -supported: +The `ma_decoder` API is used for reading audio files. Decoders are completely decoupled from +devices and can be used independently. The following formats are supported: +---------+------------------+----------+ | Format | Decoding Backend | Built-In | @@ -473,7 +2403,8 @@ supported: | Vorbis | stb_vorbis | No | +---------+------------------+----------+ -Vorbis is supported via stb_vorbis which can be enabled by including the header section before the implementation of miniaudio, like the following: +Vorbis is supported via stb_vorbis which can be enabled by including the header section before the +implementation of miniaudio, like the following: ```c #define STB_VORBIS_HEADER_ONLY @@ -489,8 +2420,9 @@ Vorbis is supported via stb_vorbis which can be enabled by including the header A copy of stb_vorbis is included in the "extras" folder in the miniaudio repository (https://github.com/mackron/miniaudio). -Built-in decoders are amalgamated into the implementation section of miniaudio. You can disable the built-in decoders by specifying one or more of the -following options before the miniaudio implementation: +Built-in decoders are amalgamated into the implementation section of miniaudio. You can disable the +built-in decoders by specifying one or more of the following options before the miniaudio +implementation: ```c #define MA_NO_WAV @@ -498,10 +2430,12 @@ following options before the miniaudio implementation: #define MA_NO_FLAC ``` -Disabling built-in decoding libraries is useful if you use these libraries independantly of the `ma_decoder` API. +Disabling built-in decoding libraries is useful if you use these libraries independantly of the +`ma_decoder` API. -A decoder can be initialized from a file with `ma_decoder_init_file()`, a block of memory with `ma_decoder_init_memory()`, or from data delivered via callbacks -with `ma_decoder_init()`. Here is an example for loading a decoder from a file: +A decoder can be initialized from a file with `ma_decoder_init_file()`, a block of memory with +`ma_decoder_init_memory()`, or from data delivered via callbacks with `ma_decoder_init()`. Here is +an example for loading a decoder from a file: ```c ma_decoder decoder; @@ -515,20 +2449,23 @@ with `ma_decoder_init()`. Here is an example for loading a decoder from a file: ma_decoder_uninit(&decoder); ``` -When initializing a decoder, you can optionally pass in a pointer to a `ma_decoder_config` object (the `NULL` argument in the example above) which allows you -to configure the output format, channel count, sample rate and channel map: +When initializing a decoder, you can optionally pass in a pointer to a `ma_decoder_config` object +(the `NULL` argument in the example above) which allows you to configure the output format, channel +count, sample rate and channel map: ```c ma_decoder_config config = ma_decoder_config_init(ma_format_f32, 2, 48000); ``` -When passing in `NULL` for decoder config in `ma_decoder_init*()`, the output format will be the same as that defined by the decoding backend. +When passing in `NULL` for decoder config in `ma_decoder_init*()`, the output format will be the +same as that defined by the decoding backend. -Data is read from the decoder as PCM frames. This will return the number of PCM frames actually read. If the return value is less than the requested number of -PCM frames it means you've reached the end: +Data is read from the decoder as PCM frames. This will output the number of PCM frames actually +read. If this is less than the requested number of PCM frames it means you've reached the end. The +return value will be `MA_AT_END` if no samples have been read and the end has been reached. ```c - ma_uint64 framesRead = ma_decoder_read_pcm_frames(pDecoder, pFrames, framesToRead); + ma_result result = ma_decoder_read_pcm_frames(pDecoder, pFrames, framesToRead, &framesRead); if (framesRead < framesToRead) { // Reached the end. } @@ -549,8 +2486,10 @@ If you want to loop back to the start, you can simply seek back to the first PCM ma_decoder_seek_to_pcm_frame(pDecoder, 0); ``` -When loading a decoder, miniaudio uses a trial and error technique to find the appropriate decoding backend. This can be unnecessarily inefficient if the type -is already known. In this case you can use `encodingFormat` variable in the device config to specify a specific encoding format you want to decode: +When loading a decoder, miniaudio uses a trial and error technique to find the appropriate decoding +backend. This can be unnecessarily inefficient if the type is already known. In this case you can +use `encodingFormat` variable in the device config to specify a specific encoding format you want +to decode: ```c decoderConfig.encodingFormat = ma_encoding_format_wav; @@ -558,24 +2497,95 @@ is already known. In this case you can use `encodingFormat` variable in the devi See the `ma_encoding_format` enum for possible encoding formats. -The `ma_decoder_init_file()` API will try using the file extension to determine which decoding backend to prefer. +The `ma_decoder_init_file()` API will try using the file extension to determine which decoding +backend to prefer. + + +8.1. Custom Decoders +-------------------- +It's possible to implement a custom decoder and plug it into miniaudio. This is extremely useful +when you want to use the `ma_decoder` API, but need to support an encoding format that's not one of +the stock formats supported by miniaudio. This can be put to particularly good use when using the +`ma_engine` and/or `ma_resource_manager` APIs because they use `ma_decoder` internally. If, for +example, you wanted to support Opus, you can do so with a custom decoder (there if a reference +Opus decoder in the "extras" folder of the miniaudio repository which uses libopus + libopusfile). + +A custom decoder must implement a data source. A vtable called `ma_decoding_backend_vtable` needs +to be implemented which is then passed into the decoder config: + + ```c + ma_decoding_backend_vtable* pCustomBackendVTables[] = + { + &g_ma_decoding_backend_vtable_libvorbis, + &g_ma_decoding_backend_vtable_libopus + }; + + ... + + decoderConfig = ma_decoder_config_init_default(); + decoderConfig.pCustomBackendUserData = NULL; + decoderConfig.ppCustomBackendVTables = pCustomBackendVTables; + decoderConfig.customBackendCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]); + ``` + +The `ma_decoding_backend_vtable` vtable has the following functions: + + ``` + onInit + onInitFile + onInitFileW + onInitMemory + onUninit + ``` + +There are only two functions that must be implemented - `onInit` and `onUninit`. The other +functions can be implemented for a small optimization for loading from a file path or memory. If +these are not specified, miniaudio will deal with it for you via a generic implementation. + +When you initialize a custom data source (by implementing the `onInit` function in the vtable) you +will need to output a pointer to a `ma_data_source` which implements your custom decoder. See the +section about data sources for details on how to implemen this. Alternatively, see the +"custom_decoders" example in the miniaudio repository. + +The `onInit` function takes a pointer to some callbacks for the purpose of reading raw audio data +from some abitrary source. You'll use these functions to read from the raw data and perform the +decoding. When you call them, you will pass in the `pReadSeekTellUserData` pointer to the relevant +parameter. + +The `pConfig` parameter in `onInit` can be used to configure the backend if appropriate. It's only +used as a hint and can be ignored. However, if any of the properties are relevant to your decoder, +an optimal implementation will handle the relevant properties appropriately. + +If memory allocation is required, it should be done so via the specified allocation callbacks if +possible (the `pAllocationCallbacks` parameter). + +If an error occurs when initializing the decoder, you should leave `ppBackend` unset, or set to +NULL, and make sure everything is cleaned up appropriately and an appropriate result code returned. +When multiple custom backends are specified, miniaudio will cycle through the vtables in the order +they're listed in the array that's passed into the decoder config so it's important that your +initialization routine is clean. + +When a decoder is uninitialized, the `onUninit` callback will be fired which will give you an +opportunity to clean up and internal data. -5. Encoding +9. Encoding =========== -The `ma_encoding` API is used for writing audio files. The only supported output format is WAV which is achieved via dr_wav which is amalgamated into the -implementation section of miniaudio. This can be disabled by specifying the following option before the implementation of miniaudio: +The `ma_encoding` API is used for writing audio files. The only supported output format is WAV +which is achieved via dr_wav which is amalgamated into the implementation section of miniaudio. +This can be disabled by specifying the following option before the implementation of miniaudio: ```c #define MA_NO_WAV ``` -An encoder can be initialized to write to a file with `ma_encoder_init_file()` or from data delivered via callbacks with `ma_encoder_init()`. Below is an -example for initializing an encoder to output to a file. +An encoder can be initialized to write to a file with `ma_encoder_init_file()` or from data +delivered via callbacks with `ma_encoder_init()`. Below is an example for initializing an encoder +to output to a file. ```c - ma_encoder_config config = ma_encoder_config_init(ma_resource_format_wav, FORMAT, CHANNELS, SAMPLE_RATE); + ma_encoder_config config = ma_encoder_config_init(ma_encoding_format_wav, FORMAT, CHANNELS, SAMPLE_RATE); ma_encoder encoder; ma_result result = ma_encoder_init_file("my_file.wav", &config, &encoder); if (result != MA_SUCCESS) { @@ -587,17 +2597,20 @@ example for initializing an encoder to output to a file. ma_encoder_uninit(&encoder); ``` -When initializing an encoder you must specify a config which is initialized with `ma_encoder_config_init()`. Here you must specify the file type, the output -sample format, output channel count and output sample rate. The following file types are supported: +When initializing an encoder you must specify a config which is initialized with +`ma_encoder_config_init()`. Here you must specify the file type, the output sample format, output +channel count and output sample rate. The following file types are supported: +------------------------+-------------+ | Enum | Description | +------------------------+-------------+ - | ma_resource_format_wav | WAV | + | ma_encoding_format_wav | WAV | +------------------------+-------------+ -If the format, channel count or sample rate is not supported by the output file type an error will be returned. The encoder will not perform data conversion so -you will need to convert it before outputting any audio data. To output audio data, use `ma_encoder_write_pcm_frames()`, like in the example below: +If the format, channel count or sample rate is not supported by the output file type an error will +be returned. The encoder will not perform data conversion so you will need to convert it before +outputting any audio data. To output audio data, use `ma_encoder_write_pcm_frames()`, like in the +example below: ```c framesWritten = ma_encoder_write_pcm_frames(&encoder, pPCMFramesToWrite, framesToWrite); @@ -606,21 +2619,25 @@ you will need to convert it before outputting any audio data. To output audio da Encoders must be uninitialized with `ma_encoder_uninit()`. -6. Data Conversion -================== -A data conversion API is included with miniaudio which supports the majority of data conversion requirements. This supports conversion between sample formats, -channel counts (with channel mapping) and sample rates. + +10. Data Conversion +=================== +A data conversion API is included with miniaudio which supports the majority of data conversion +requirements. This supports conversion between sample formats, channel counts (with channel +mapping) and sample rates. -6.1. Sample Format Conversion ------------------------------ -Conversion between sample formats is achieved with the `ma_pcm_*_to_*()`, `ma_pcm_convert()` and `ma_convert_pcm_frames_format()` APIs. Use `ma_pcm_*_to_*()` -to convert between two specific formats. Use `ma_pcm_convert()` to convert based on a `ma_format` variable. Use `ma_convert_pcm_frames_format()` to convert -PCM frames where you want to specify the frame count and channel count as a variable instead of the total sample count. +10.1. Sample Format Conversion +------------------------------ +Conversion between sample formats is achieved with the `ma_pcm_*_to_*()`, `ma_pcm_convert()` and +`ma_convert_pcm_frames_format()` APIs. Use `ma_pcm_*_to_*()` to convert between two specific +formats. Use `ma_pcm_convert()` to convert based on a `ma_format` variable. Use +`ma_convert_pcm_frames_format()` to convert PCM frames where you want to specify the frame count +and channel count as a variable instead of the total sample count. -6.1.1. Dithering ----------------- +10.1.1. Dithering +----------------- Dithering can be set using the ditherMode parameter. The different dithering modes include the following, in order of efficiency: @@ -633,8 +2650,9 @@ The different dithering modes include the following, in order of efficiency: | Triangle | ma_dither_mode_triangle | +-----------+--------------------------+ -Note that even if the dither mode is set to something other than `ma_dither_mode_none`, it will be ignored for conversions where dithering is not needed. -Dithering is available for the following conversions: +Note that even if the dither mode is set to something other than `ma_dither_mode_none`, it will be +ignored for conversions where dithering is not needed. Dithering is available for the following +conversions: ``` s16 -> u8 @@ -646,14 +2664,16 @@ Dithering is available for the following conversions: f32 -> s16 ``` -Note that it is not an error to pass something other than ma_dither_mode_none for conversions where dither is not used. It will just be ignored. +Note that it is not an error to pass something other than ma_dither_mode_none for conversions where +dither is not used. It will just be ignored. -6.2. Channel Conversion ------------------------ -Channel conversion is used for channel rearrangement and conversion from one channel count to another. The `ma_channel_converter` API is used for channel -conversion. Below is an example of initializing a simple channel converter which converts from mono to stereo. +10.2. Channel Conversion +------------------------ +Channel conversion is used for channel rearrangement and conversion from one channel count to +another. The `ma_channel_converter` API is used for channel conversion. Below is an example of +initializing a simple channel converter which converts from mono to stereo. ```c ma_channel_converter_config config = ma_channel_converter_config_init( @@ -664,7 +2684,7 @@ conversion. Below is an example of initializing a simple channel converter which NULL, // Output channel map ma_channel_mix_mode_default); // The mixing algorithm to use when combining channels. - result = ma_channel_converter_init(&config, &converter); + result = ma_channel_converter_init(&config, NULL, &converter); if (result != MA_SUCCESS) { // Error. } @@ -679,34 +2699,43 @@ To perform the conversion simply call `ma_channel_converter_process_pcm_frames() } ``` -It is up to the caller to ensure the output buffer is large enough to accomodate the new PCM frames. +It is up to the caller to ensure the output buffer is large enough to accomodate the new PCM +frames. Input and output PCM frames are always interleaved. Deinterleaved layouts are not supported. -6.2.1. Channel Mapping ----------------------- -In addition to converting from one channel count to another, like the example above, the channel converter can also be used to rearrange channels. When -initializing the channel converter, you can optionally pass in channel maps for both the input and output frames. If the channel counts are the same, and each -channel map contains the same channel positions with the exception that they're in a different order, a simple shuffling of the channels will be performed. If, -however, there is not a 1:1 mapping of channel positions, or the channel counts differ, the input channels will be mixed based on a mixing mode which is -specified when initializing the `ma_channel_converter_config` object. +10.2.1. Channel Mapping +----------------------- +In addition to converting from one channel count to another, like the example above, the channel +converter can also be used to rearrange channels. When initializing the channel converter, you can +optionally pass in channel maps for both the input and output frames. If the channel counts are the +same, and each channel map contains the same channel positions with the exception that they're in +a different order, a simple shuffling of the channels will be performed. If, however, there is not +a 1:1 mapping of channel positions, or the channel counts differ, the input channels will be mixed +based on a mixing mode which is specified when initializing the `ma_channel_converter_config` +object. -When converting from mono to multi-channel, the mono channel is simply copied to each output channel. When going the other way around, the audio of each output -channel is simply averaged and copied to the mono channel. +When converting from mono to multi-channel, the mono channel is simply copied to each output +channel. When going the other way around, the audio of each output channel is simply averaged and +copied to the mono channel. -In more complicated cases blending is used. The `ma_channel_mix_mode_simple` mode will drop excess channels and silence extra channels. For example, converting -from 4 to 2 channels, the 3rd and 4th channels will be dropped, whereas converting from 2 to 4 channels will put silence into the 3rd and 4th channels. +In more complicated cases blending is used. The `ma_channel_mix_mode_simple` mode will drop excess +channels and silence extra channels. For example, converting from 4 to 2 channels, the 3rd and 4th +channels will be dropped, whereas converting from 2 to 4 channels will put silence into the 3rd and +4th channels. -The `ma_channel_mix_mode_rectangle` mode uses spacial locality based on a rectangle to compute a simple distribution between input and output. Imagine sitting -in the middle of a room, with speakers on the walls representing channel positions. The MA_CHANNEL_FRONT_LEFT position can be thought of as being in the corner -of the front and left walls. +The `ma_channel_mix_mode_rectangle` mode uses spacial locality based on a rectangle to compute a +simple distribution between input and output. Imagine sitting in the middle of a room, with +speakers on the walls representing channel positions. The `MA_CHANNEL_FRONT_LEFT` position can be +thought of as being in the corner of the front and left walls. -Finally, the `ma_channel_mix_mode_custom_weights` mode can be used to use custom user-defined weights. Custom weights can be passed in as the last parameter of +Finally, the `ma_channel_mix_mode_custom_weights` mode can be used to use custom user-defined +weights. Custom weights can be passed in as the last parameter of `ma_channel_converter_config_init()`. -Predefined channel maps can be retrieved with `ma_get_standard_channel_map()`. This takes a `ma_standard_channel_map` enum as it's first parameter, which can -be one of the following: +Predefined channel maps can be retrieved with `ma_channel_map_init_standard()`. This takes a +`ma_standard_channel_map` enum as it's first parameter, which can be one of the following: +-----------------------------------+-----------------------------------------------------------+ | Name | Description | @@ -778,9 +2807,10 @@ Below are the channel maps used by default in miniaudio (`ma_standard_channel_ma -6.3. Resampling ---------------- -Resampling is achieved with the `ma_resampler` object. To create a resampler object, do something like the following: +10.3. Resampling +---------------- +Resampling is achieved with the `ma_resampler` object. To create a resampler object, do something +like the following: ```c ma_resampler_config config = ma_resampler_config_init( @@ -817,104 +2847,128 @@ The following example shows how data can be processed // number of output frames written. ``` -To initialize the resampler you first need to set up a config (`ma_resampler_config`) with `ma_resampler_config_init()`. You need to specify the sample format -you want to use, the number of channels, the input and output sample rate, and the algorithm. +To initialize the resampler you first need to set up a config (`ma_resampler_config`) with +`ma_resampler_config_init()`. You need to specify the sample format you want to use, the number of +channels, the input and output sample rate, and the algorithm. -The sample format can be either `ma_format_s16` or `ma_format_f32`. If you need a different format you will need to perform pre- and post-conversions yourself -where necessary. Note that the format is the same for both input and output. The format cannot be changed after initialization. +The sample format can be either `ma_format_s16` or `ma_format_f32`. If you need a different format +you will need to perform pre- and post-conversions yourself where necessary. Note that the format +is the same for both input and output. The format cannot be changed after initialization. -The resampler supports multiple channels and is always interleaved (both input and output). The channel count cannot be changed after initialization. +The resampler supports multiple channels and is always interleaved (both input and output). The +channel count cannot be changed after initialization. -The sample rates can be anything other than zero, and are always specified in hertz. They should be set to something like 44100, etc. The sample rate is the -only configuration property that can be changed after initialization. +The sample rates can be anything other than zero, and are always specified in hertz. They should be +set to something like 44100, etc. The sample rate is the only configuration property that can be +changed after initialization. -The miniaudio resampler supports multiple algorithms: +The miniaudio resampler has built-in support for the following algorithms: +-----------+------------------------------+ | Algorithm | Enum Token | +-----------+------------------------------+ | Linear | ma_resample_algorithm_linear | - | Speex | ma_resample_algorithm_speex | + | Custom | ma_resample_algorithm_custom | +-----------+------------------------------+ -Because Speex is not public domain it is strictly opt-in and the code is stored in separate files. if you opt-in to the Speex backend you will need to consider -it's license, the text of which can be found in it's source files in "extras/speex_resampler". Details on how to opt-in to the Speex resampler is explained in -the Speex Resampler section below. - The algorithm cannot be changed after initialization. -Processing always happens on a per PCM frame basis and always assumes interleaved input and output. De-interleaved processing is not supported. To process -frames, use `ma_resampler_process_pcm_frames()`. On input, this function takes the number of output frames you can fit in the output buffer and the number of -input frames contained in the input buffer. On output these variables contain the number of output frames that were written to the output buffer and the -number of input frames that were consumed in the process. You can pass in NULL for the input buffer in which case it will be treated as an infinitely large -buffer of zeros. The output buffer can also be NULL, in which case the processing will be treated as seek. +Processing always happens on a per PCM frame basis and always assumes interleaved input and output. +De-interleaved processing is not supported. To process frames, use +`ma_resampler_process_pcm_frames()`. On input, this function takes the number of output frames you +can fit in the output buffer and the number of input frames contained in the input buffer. On +output these variables contain the number of output frames that were written to the output buffer +and the number of input frames that were consumed in the process. You can pass in NULL for the +input buffer in which case it will be treated as an infinitely large buffer of zeros. The output +buffer can also be NULL, in which case the processing will be treated as seek. -The sample rate can be changed dynamically on the fly. You can change this with explicit sample rates with `ma_resampler_set_rate()` and also with a decimal -ratio with `ma_resampler_set_rate_ratio()`. The ratio is in/out. +The sample rate can be changed dynamically on the fly. You can change this with explicit sample +rates with `ma_resampler_set_rate()` and also with a decimal ratio with +`ma_resampler_set_rate_ratio()`. The ratio is in/out. -Sometimes it's useful to know exactly how many input frames will be required to output a specific number of frames. You can calculate this with -`ma_resampler_get_required_input_frame_count()`. Likewise, it's sometimes useful to know exactly how many frames would be output given a certain number of -input frames. You can do this with `ma_resampler_get_expected_output_frame_count()`. +Sometimes it's useful to know exactly how many input frames will be required to output a specific +number of frames. You can calculate this with `ma_resampler_get_required_input_frame_count()`. +Likewise, it's sometimes useful to know exactly how many frames would be output given a certain +number of input frames. You can do this with `ma_resampler_get_expected_output_frame_count()`. -Due to the nature of how resampling works, the resampler introduces some latency. This can be retrieved in terms of both the input rate and the output rate -with `ma_resampler_get_input_latency()` and `ma_resampler_get_output_latency()`. +Due to the nature of how resampling works, the resampler introduces some latency. This can be +retrieved in terms of both the input rate and the output rate with +`ma_resampler_get_input_latency()` and `ma_resampler_get_output_latency()`. -6.3.1. Resampling Algorithms ----------------------------- -The choice of resampling algorithm depends on your situation and requirements. The linear resampler is the most efficient and has the least amount of latency, -but at the expense of poorer quality. The Speex resampler is higher quality, but slower with more latency. It also performs several heap allocations internally -for memory management. +10.3.1. Resampling Algorithms +----------------------------- +The choice of resampling algorithm depends on your situation and requirements. -6.3.1.1. Linear Resampling --------------------------- -The linear resampler is the fastest, but comes at the expense of poorer quality. There is, however, some control over the quality of the linear resampler which -may make it a suitable option depending on your requirements. +10.3.1.1. Linear Resampling +--------------------------- +The linear resampler is the fastest, but comes at the expense of poorer quality. There is, however, +some control over the quality of the linear resampler which may make it a suitable option depending +on your requirements. -The linear resampler performs low-pass filtering before or after downsampling or upsampling, depending on the sample rates you're converting between. When -decreasing the sample rate, the low-pass filter will be applied before downsampling. When increasing the rate it will be performed after upsampling. By default -a fourth order low-pass filter will be applied. This can be configured via the `lpfOrder` configuration variable. Setting this to 0 will disable filtering. +The linear resampler performs low-pass filtering before or after downsampling or upsampling, +depending on the sample rates you're converting between. When decreasing the sample rate, the +low-pass filter will be applied before downsampling. When increasing the rate it will be performed +after upsampling. By default a fourth order low-pass filter will be applied. This can be configured +via the `lpfOrder` configuration variable. Setting this to 0 will disable filtering. -The low-pass filter has a cutoff frequency which defaults to half the sample rate of the lowest of the input and output sample rates (Nyquist Frequency). This -can be controlled with the `lpfNyquistFactor` config variable. This defaults to 1, and should be in the range of 0..1, although a value of 0 does not make -sense and should be avoided. A value of 1 will use the Nyquist Frequency as the cutoff. A value of 0.5 will use half the Nyquist Frequency as the cutoff, etc. -Values less than 1 will result in more washed out sound due to more of the higher frequencies being removed. This config variable has no impact on performance -and is a purely perceptual configuration. +The low-pass filter has a cutoff frequency which defaults to half the sample rate of the lowest of +the input and output sample rates (Nyquist Frequency). -The API for the linear resampler is the same as the main resampler API, only it's called `ma_linear_resampler`. +The API for the linear resampler is the same as the main resampler API, only it's called +`ma_linear_resampler`. -6.3.1.2. Speex Resampling +10.3.2. Custom Resamplers ------------------------- -The Speex resampler is made up of third party code which is released under the BSD license. Because it is licensed differently to miniaudio, which is public -domain, it is strictly opt-in and all of it's code is stored in separate files. If you opt-in to the Speex resampler you must consider the license text in it's -source files. To opt-in, you must first `#include` the following file before the implementation of miniaudio.h: +You can implement a custom resampler by using the `ma_resample_algorithm_custom` resampling +algorithm and setting a vtable in the resampler config: ```c - #include "extras/speex_resampler/ma_speex_resampler.h" + ma_resampler_config config = ma_resampler_config_init(..., ma_resample_algorithm_custom); + config.pBackendVTable = &g_customResamplerVTable; ``` -Both the header and implementation is contained within the same file. The implementation can be included in your program like so: +Custom resamplers are useful if the stock algorithms are not appropriate for your use case. You +need to implement the required functions in `ma_resampling_backend_vtable`. Note that not all +functions in the vtable need to be implemented, but if it's possible to implement, they should be. - ```c - #define MINIAUDIO_SPEEX_RESAMPLER_IMPLEMENTATION - #include "extras/speex_resampler/ma_speex_resampler.h" - ``` +You can use the `ma_linear_resampler` object for an example on how to implement the vtable. The +`onGetHeapSize` callback is used to calculate the size of any internal heap allocation the custom +resampler will need to make given the supplied config. When you initialize the resampler via the +`onInit` callback, you'll be given a pointer to a heap allocation which is where you should store +the heap allocated data. You should not free this data in `onUninit` because miniaudio will manage +it for you. -Note that even if you opt-in to the Speex backend, miniaudio won't use it unless you explicitly ask for it in the respective config of the object you are -initializing. If you try to use the Speex resampler without opting in, initialization of the `ma_resampler` object will fail with `MA_NO_BACKEND`. +The `onProcess` callback is where the actual resampling takes place. On input, `pFrameCountIn` +points to a variable containing the number of frames in the `pFramesIn` buffer and +`pFrameCountOut` points to a variable containing the capacity in frames of the `pFramesOut` buffer. +On output, `pFrameCountIn` should be set to the number of input frames that were fully consumed, +whereas `pFrameCountOut` should be set to the number of frames that were written to `pFramesOut`. -The only configuration option to consider with the Speex resampler is the `speex.quality` config variable. This is a value between 0 and 10, with 0 being -the fastest with the poorest quality and 10 being the slowest with the highest quality. The default value is 3. +The `onSetRate` callback is optional and is used for dynamically changing the sample rate. If +dynamic rate changes are not supported, you can set this callback to NULL. + +The `onGetInputLatency` and `onGetOutputLatency` functions are used for retrieving the latency in +input and output rates respectively. These can be NULL in which case latency calculations will be +assumed to be NULL. + +The `onGetRequiredInputFrameCount` callback is used to give miniaudio a hint as to how many input +frames are required to be available to produce the given number of output frames. Likewise, the +`onGetExpectedOutputFrameCount` callback is used to determine how many output frames will be +produced given the specified number of input frames. miniaudio will use these as a hint, but they +are optional and can be set to NULL if you're unable to implement them. -6.4. General Data Conversion ----------------------------- -The `ma_data_converter` API can be used to wrap sample format conversion, channel conversion and resampling into one operation. This is what miniaudio uses -internally to convert between the format requested when the device was initialized and the format of the backend's native device. The API for general data -conversion is very similar to the resampling API. Create a `ma_data_converter` object like this: +10.4. General Data Conversion +----------------------------- +The `ma_data_converter` API can be used to wrap sample format conversion, channel conversion and +resampling into one operation. This is what miniaudio uses internally to convert between the format +requested when the device was initialized and the format of the backend's native device. The API +for general data conversion is very similar to the resampling API. Create a `ma_data_converter` +object like this: ```c ma_data_converter_config config = ma_data_converter_config_init( @@ -927,14 +2981,15 @@ conversion is very similar to the resampling API. Create a `ma_data_converter` o ); ma_data_converter converter; - ma_result result = ma_data_converter_init(&config, &converter); + ma_result result = ma_data_converter_init(&config, NULL, &converter); if (result != MA_SUCCESS) { // An error occurred... } ``` -In the example above we use `ma_data_converter_config_init()` to initialize the config, however there's many more properties that can be configured, such as -channel maps and resampling quality. Something like the following may be more suitable depending on your requirements: +In the example above we use `ma_data_converter_config_init()` to initialize the config, however +there's many more properties that can be configured, such as channel maps and resampling quality. +Something like the following may be more suitable depending on your requirements: ```c ma_data_converter_config config = ma_data_converter_config_init_default(); @@ -944,14 +2999,14 @@ channel maps and resampling quality. Something like the following may be more su config.channelsOut = outputChannels; config.sampleRateIn = inputSampleRate; config.sampleRateOut = outputSampleRate; - ma_get_standard_channel_map(ma_standard_channel_map_flac, config.channelCountIn, config.channelMapIn); + ma_channel_map_init_standard(ma_standard_channel_map_flac, config.channelMapIn, sizeof(config.channelMapIn)/sizeof(config.channelMapIn[0]), config.channelCountIn); config.resampling.linear.lpfOrder = MA_MAX_FILTER_ORDER; ``` Do the following to uninitialize the data converter: ```c - ma_data_converter_uninit(&converter); + ma_data_converter_uninit(&converter, NULL); ``` The following example shows how data can be processed @@ -968,33 +3023,42 @@ The following example shows how data can be processed // of output frames written. ``` -The data converter supports multiple channels and is always interleaved (both input and output). The channel count cannot be changed after initialization. +The data converter supports multiple channels and is always interleaved (both input and output). +The channel count cannot be changed after initialization. -Sample rates can be anything other than zero, and are always specified in hertz. They should be set to something like 44100, etc. The sample rate is the only -configuration property that can be changed after initialization, but only if the `resampling.allowDynamicSampleRate` member of `ma_data_converter_config` is -set to `MA_TRUE`. To change the sample rate, use `ma_data_converter_set_rate()` or `ma_data_converter_set_rate_ratio()`. The ratio must be in/out. The -resampling algorithm cannot be changed after initialization. +Sample rates can be anything other than zero, and are always specified in hertz. They should be set +to something like 44100, etc. The sample rate is the only configuration property that can be +changed after initialization, but only if the `resampling.allowDynamicSampleRate` member of +`ma_data_converter_config` is set to `MA_TRUE`. To change the sample rate, use +`ma_data_converter_set_rate()` or `ma_data_converter_set_rate_ratio()`. The ratio must be in/out. +The resampling algorithm cannot be changed after initialization. -Processing always happens on a per PCM frame basis and always assumes interleaved input and output. De-interleaved processing is not supported. To process -frames, use `ma_data_converter_process_pcm_frames()`. On input, this function takes the number of output frames you can fit in the output buffer and the number -of input frames contained in the input buffer. On output these variables contain the number of output frames that were written to the output buffer and the -number of input frames that were consumed in the process. You can pass in NULL for the input buffer in which case it will be treated as an infinitely large -buffer of zeros. The output buffer can also be NULL, in which case the processing will be treated as seek. +Processing always happens on a per PCM frame basis and always assumes interleaved input and output. +De-interleaved processing is not supported. To process frames, use +`ma_data_converter_process_pcm_frames()`. On input, this function takes the number of output frames +you can fit in the output buffer and the number of input frames contained in the input buffer. On +output these variables contain the number of output frames that were written to the output buffer +and the number of input frames that were consumed in the process. You can pass in NULL for the +input buffer in which case it will be treated as an infinitely large +buffer of zeros. The output buffer can also be NULL, in which case the processing will be treated +as seek. -Sometimes it's useful to know exactly how many input frames will be required to output a specific number of frames. You can calculate this with -`ma_data_converter_get_required_input_frame_count()`. Likewise, it's sometimes useful to know exactly how many frames would be output given a certain number of -input frames. You can do this with `ma_data_converter_get_expected_output_frame_count()`. +Sometimes it's useful to know exactly how many input frames will be required to output a specific +number of frames. You can calculate this with `ma_data_converter_get_required_input_frame_count()`. +Likewise, it's sometimes useful to know exactly how many frames would be output given a certain +number of input frames. You can do this with `ma_data_converter_get_expected_output_frame_count()`. -Due to the nature of how resampling works, the data converter introduces some latency if resampling is required. This can be retrieved in terms of both the -input rate and the output rate with `ma_data_converter_get_input_latency()` and `ma_data_converter_get_output_latency()`. +Due to the nature of how resampling works, the data converter introduces some latency if resampling +is required. This can be retrieved in terms of both the input rate and the output rate with +`ma_data_converter_get_input_latency()` and `ma_data_converter_get_output_latency()`. -7. Filtering -============ +11. Filtering +============= -7.1. Biquad Filtering ---------------------- +11.1. Biquad Filtering +---------------------- Biquad filtering is achieved with the `ma_biquad` API. Example: ```c @@ -1009,28 +3073,33 @@ Biquad filtering is achieved with the `ma_biquad` API. Example: ma_biquad_process_pcm_frames(&biquad, pFramesOut, pFramesIn, frameCount); ``` -Biquad filtering is implemented using transposed direct form 2. The numerator coefficients are b0, b1 and b2, and the denominator coefficients are a0, a1 and -a2. The a0 coefficient is required and coefficients must not be pre-normalized. +Biquad filtering is implemented using transposed direct form 2. The numerator coefficients are b0, +b1 and b2, and the denominator coefficients are a0, a1 and a2. The a0 coefficient is required and +coefficients must not be pre-normalized. -Supported formats are `ma_format_s16` and `ma_format_f32`. If you need to use a different format you need to convert it yourself beforehand. When using -`ma_format_s16` the biquad filter will use fixed point arithmetic. When using `ma_format_f32`, floating point arithmetic will be used. +Supported formats are `ma_format_s16` and `ma_format_f32`. If you need to use a different format +you need to convert it yourself beforehand. When using `ma_format_s16` the biquad filter will use +fixed point arithmetic. When using `ma_format_f32`, floating point arithmetic will be used. Input and output frames are always interleaved. -Filtering can be applied in-place by passing in the same pointer for both the input and output buffers, like so: +Filtering can be applied in-place by passing in the same pointer for both the input and output +buffers, like so: ```c ma_biquad_process_pcm_frames(&biquad, pMyData, pMyData, frameCount); ``` -If you need to change the values of the coefficients, but maintain the values in the registers you can do so with `ma_biquad_reinit()`. This is useful if you -need to change the properties of the filter while keeping the values of registers valid to avoid glitching. Do not use `ma_biquad_init()` for this as it will -do a full initialization which involves clearing the registers to 0. Note that changing the format or channel count after initialization is invalid and will -result in an error. +If you need to change the values of the coefficients, but maintain the values in the registers you +can do so with `ma_biquad_reinit()`. This is useful if you need to change the properties of the +filter while keeping the values of registers valid to avoid glitching. Do not use +`ma_biquad_init()` for this as it will do a full initialization which involves clearing the +registers to 0. Note that changing the format or channel count after initialization is invalid and +will result in an error. -7.2. Low-Pass Filtering ------------------------ +11.2. Low-Pass Filtering +------------------------ Low-pass filtering is achieved with the following APIs: +---------+------------------------------------------+ @@ -1055,16 +3124,18 @@ Low-pass filter example: ma_lpf_process_pcm_frames(&lpf, pFramesOut, pFramesIn, frameCount); ``` -Supported formats are `ma_format_s16` and` ma_format_f32`. If you need to use a different format you need to convert it yourself beforehand. Input and output -frames are always interleaved. +Supported formats are `ma_format_s16` and` ma_format_f32`. If you need to use a different format +you need to convert it yourself beforehand. Input and output frames are always interleaved. -Filtering can be applied in-place by passing in the same pointer for both the input and output buffers, like so: +Filtering can be applied in-place by passing in the same pointer for both the input and output +buffers, like so: ```c ma_lpf_process_pcm_frames(&lpf, pMyData, pMyData, frameCount); ``` -The maximum filter order is limited to `MA_MAX_FILTER_ORDER` which is set to 8. If you need more, you can chain first and second order filters together. +The maximum filter order is limited to `MA_MAX_FILTER_ORDER` which is set to 8. If you need more, +you can chain first and second order filters together. ```c for (iFilter = 0; iFilter < filterCount; iFilter += 1) { @@ -1072,19 +3143,22 @@ The maximum filter order is limited to `MA_MAX_FILTER_ORDER` which is set to 8. } ``` -If you need to change the configuration of the filter, but need to maintain the state of internal registers you can do so with `ma_lpf_reinit()`. This may be -useful if you need to change the sample rate and/or cutoff frequency dynamically while maintaing smooth transitions. Note that changing the format or channel -count after initialization is invalid and will result in an error. +If you need to change the configuration of the filter, but need to maintain the state of internal +registers you can do so with `ma_lpf_reinit()`. This may be useful if you need to change the sample +rate and/or cutoff frequency dynamically while maintaing smooth transitions. Note that changing the +format or channel count after initialization is invalid and will result in an error. -The `ma_lpf` object supports a configurable order, but if you only need a first order filter you may want to consider using `ma_lpf1`. Likewise, if you only -need a second order filter you can use `ma_lpf2`. The advantage of this is that they're lighter weight and a bit more efficient. +The `ma_lpf` object supports a configurable order, but if you only need a first order filter you +may want to consider using `ma_lpf1`. Likewise, if you only need a second order filter you can use +`ma_lpf2`. The advantage of this is that they're lighter weight and a bit more efficient. -If an even filter order is specified, a series of second order filters will be processed in a chain. If an odd filter order is specified, a first order filter -will be applied, followed by a series of second order filters in a chain. +If an even filter order is specified, a series of second order filters will be processed in a +chain. If an odd filter order is specified, a first order filter will be applied, followed by a +series of second order filters in a chain. -7.3. High-Pass Filtering ------------------------- +11.3. High-Pass Filtering +------------------------- High-pass filtering is achieved with the following APIs: +---------+-------------------------------------------+ @@ -1095,12 +3169,12 @@ High-pass filtering is achieved with the following APIs: | ma_hpf | High order high-pass filter (Butterworth) | +---------+-------------------------------------------+ -High-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_hpf1`, `ma_hpf2` and `ma_hpf`. See example code for low-pass filters -for example usage. +High-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_hpf1`, +`ma_hpf2` and `ma_hpf`. See example code for low-pass filters for example usage. -7.4. Band-Pass Filtering ------------------------- +11.4. Band-Pass Filtering +------------------------- Band-pass filtering is achieved with the following APIs: +---------+-------------------------------+ @@ -1110,13 +3184,14 @@ Band-pass filtering is achieved with the following APIs: | ma_bpf | High order band-pass filter | +---------+-------------------------------+ -Band-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_bpf2` and `ma_hpf`. See example code for low-pass filters for example -usage. Note that the order for band-pass filters must be an even number which means there is no first order band-pass filter, unlike low-pass and high-pass -filters. +Band-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_bpf2` and +`ma_hpf`. See example code for low-pass filters for example usage. Note that the order for +band-pass filters must be an even number which means there is no first order band-pass filter, +unlike low-pass and high-pass filters. -7.5. Notch Filtering --------------------- +11.5. Notch Filtering +--------------------- Notch filtering is achieved with the following APIs: +-----------+------------------------------------------+ @@ -1126,7 +3201,7 @@ Notch filtering is achieved with the following APIs: +-----------+------------------------------------------+ -7.6. Peaking EQ Filtering +11.6. Peaking EQ Filtering ------------------------- Peaking filtering is achieved with the following APIs: @@ -1137,8 +3212,8 @@ Peaking filtering is achieved with the following APIs: +----------+------------------------------------------+ -7.7. Low Shelf Filtering ------------------------- +11.7. Low Shelf Filtering +------------------------- Low shelf filtering is achieved with the following APIs: +-------------+------------------------------------------+ @@ -1147,11 +3222,12 @@ Low shelf filtering is achieved with the following APIs: | ma_loshelf2 | Second order low shelf filter | +-------------+------------------------------------------+ -Where a high-pass filter is used to eliminate lower frequencies, a low shelf filter can be used to just turn them down rather than eliminate them entirely. +Where a high-pass filter is used to eliminate lower frequencies, a low shelf filter can be used to +just turn them down rather than eliminate them entirely. -7.8. High Shelf Filtering -------------------------- +11.8. High Shelf Filtering +-------------------------- High shelf filtering is achieved with the following APIs: +-------------+------------------------------------------+ @@ -1160,18 +3236,20 @@ High shelf filtering is achieved with the following APIs: | ma_hishelf2 | Second order high shelf filter | +-------------+------------------------------------------+ -The high shelf filter has the same API as the low shelf filter, only you would use `ma_hishelf` instead of `ma_loshelf`. Where a low shelf filter is used to -adjust the volume of low frequencies, the high shelf filter does the same thing for high frequencies. +The high shelf filter has the same API as the low shelf filter, only you would use `ma_hishelf` +instead of `ma_loshelf`. Where a low shelf filter is used to adjust the volume of low frequencies, +the high shelf filter does the same thing for high frequencies. -8. Waveform and Noise Generation -================================ +12. Waveform and Noise Generation +================================= -8.1. Waveforms --------------- -miniaudio supports generation of sine, square, triangle and sawtooth waveforms. This is achieved with the `ma_waveform` API. Example: +12.1. Waveforms +--------------- +miniaudio supports generation of sine, square, triangle and sawtooth waveforms. This is achieved +with the `ma_waveform` API. Example: ```c ma_waveform_config config = ma_waveform_config_init( @@ -1193,11 +3271,12 @@ miniaudio supports generation of sine, square, triangle and sawtooth waveforms. ma_waveform_read_pcm_frames(&waveform, pOutput, frameCount); ``` -The amplitude, frequency, type, and sample rate can be changed dynamically with `ma_waveform_set_amplitude()`, `ma_waveform_set_frequency()`, -`ma_waveform_set_type()`, and `ma_waveform_set_sample_rate()` respectively. +The amplitude, frequency, type, and sample rate can be changed dynamically with +`ma_waveform_set_amplitude()`, `ma_waveform_set_frequency()`, `ma_waveform_set_type()`, and +`ma_waveform_set_sample_rate()` respectively. -You can invert the waveform by setting the amplitude to a negative value. You can use this to control whether or not a sawtooth has a positive or negative -ramp, for example. +You can invert the waveform by setting the amplitude to a negative value. You can use this to +control whether or not a sawtooth has a positive or negative ramp, for example. Below are the supported waveform types: @@ -1212,8 +3291,8 @@ Below are the supported waveform types: -8.2. Noise ----------- +12.2. Noise +----------- miniaudio supports generation of white, pink and Brownian noise via the `ma_noise` API. Example: ```c @@ -1235,13 +3314,16 @@ miniaudio supports generation of white, pink and Brownian noise via the `ma_nois ma_noise_read_pcm_frames(&noise, pOutput, frameCount); ``` -The noise API uses simple LCG random number generation. It supports a custom seed which is useful for things like automated testing requiring reproducibility. -Setting the seed to zero will default to `MA_DEFAULT_LCG_SEED`. +The noise API uses simple LCG random number generation. It supports a custom seed which is useful +for things like automated testing requiring reproducibility. Setting the seed to zero will default +to `MA_DEFAULT_LCG_SEED`. -The amplitude, seed, and type can be changed dynamically with `ma_noise_set_amplitude()`, `ma_noise_set_seed()`, and `ma_noise_set_type()` respectively. +The amplitude, seed, and type can be changed dynamically with `ma_noise_set_amplitude()`, +`ma_noise_set_seed()`, and `ma_noise_set_type()` respectively. -By default, the noise API will use different values for different channels. So, for example, the left side in a stereo stream will be different to the right -side. To instead have each channel use the same random value, set the `duplicateChannels` member of the noise config to true, like so: +By default, the noise API will use different values for different channels. So, for example, the +left side in a stereo stream will be different to the right side. To instead have each channel use +the same random value, set the `duplicateChannels` member of the noise config to true, like so: ```c config.duplicateChannels = MA_TRUE; @@ -1259,10 +3341,11 @@ Below are the supported noise types. -9. Audio Buffers -================ -miniaudio supports reading from a buffer of raw audio data via the `ma_audio_buffer` API. This can read from memory that's managed by the application, but -can also handle the memory management for you internally. Memory management is flexible and should support most use cases. +13. Audio Buffers +================= +miniaudio supports reading from a buffer of raw audio data via the `ma_audio_buffer` API. This can +read from memory that's managed by the application, but can also handle the memory management for +you internally. Memory management is flexible and should support most use cases. Audio buffers are initialised using the standard configuration system used everywhere in miniaudio: @@ -1285,11 +3368,14 @@ Audio buffers are initialised using the standard configuration system used every ma_audio_buffer_uninit(&buffer); ``` -In the example above, the memory pointed to by `pExistingData` will *not* be copied and is how an application can do self-managed memory allocation. If you -would rather make a copy of the data, use `ma_audio_buffer_init_copy()`. To uninitialize the buffer, use `ma_audio_buffer_uninit()`. +In the example above, the memory pointed to by `pExistingData` will *not* be copied and is how an +application can do self-managed memory allocation. If you would rather make a copy of the data, use +`ma_audio_buffer_init_copy()`. To uninitialize the buffer, use `ma_audio_buffer_uninit()`. -Sometimes it can be convenient to allocate the memory for the `ma_audio_buffer` structure and the raw audio data in a contiguous block of memory. That is, -the raw audio data will be located immediately after the `ma_audio_buffer` structure. To do this, use `ma_audio_buffer_alloc_and_init()`: +Sometimes it can be convenient to allocate the memory for the `ma_audio_buffer` structure and the +raw audio data in a contiguous block of memory. That is, the raw audio data will be located +immediately after the `ma_audio_buffer` structure. To do this, use +`ma_audio_buffer_alloc_and_init()`: ```c ma_audio_buffer_config config = ma_audio_buffer_config_init( @@ -1310,13 +3396,18 @@ the raw audio data will be located immediately after the `ma_audio_buffer` struc ma_audio_buffer_uninit_and_free(&buffer); ``` -If you initialize the buffer with `ma_audio_buffer_alloc_and_init()` you should uninitialize it with `ma_audio_buffer_uninit_and_free()`. In the example above, -the memory pointed to by `pExistingData` will be copied into the buffer, which is contrary to the behavior of `ma_audio_buffer_init()`. +If you initialize the buffer with `ma_audio_buffer_alloc_and_init()` you should uninitialize it +with `ma_audio_buffer_uninit_and_free()`. In the example above, the memory pointed to by +`pExistingData` will be copied into the buffer, which is contrary to the behavior of +`ma_audio_buffer_init()`. -An audio buffer has a playback cursor just like a decoder. As you read frames from the buffer, the cursor moves forward. The last parameter (`loop`) can be -used to determine if the buffer should loop. The return value is the number of frames actually read. If this is less than the number of frames requested it -means the end has been reached. This should never happen if the `loop` parameter is set to true. If you want to manually loop back to the start, you can do so -with with `ma_audio_buffer_seek_to_pcm_frame(pAudioBuffer, 0)`. Below is an example for reading data from an audio buffer. +An audio buffer has a playback cursor just like a decoder. As you read frames from the buffer, the +cursor moves forward. The last parameter (`loop`) can be used to determine if the buffer should +loop. The return value is the number of frames actually read. If this is less than the number of +frames requested it means the end has been reached. This should never happen if the `loop` +parameter is set to true. If you want to manually loop back to the start, you can do so with with +`ma_audio_buffer_seek_to_pcm_frame(pAudioBuffer, 0)`. Below is an example for reading data from an +audio buffer. ```c ma_uint64 framesRead = ma_audio_buffer_read_pcm_frames(pAudioBuffer, pFramesOut, desiredFrameCount, isLooping); @@ -1325,8 +3416,8 @@ with with `ma_audio_buffer_seek_to_pcm_frame(pAudioBuffer, 0)`. Below is an exam } ``` -Sometimes you may want to avoid the cost of data movement between the internal buffer and the output buffer. Instead you can use memory mapping to retrieve a -pointer to a segment of data: +Sometimes you may want to avoid the cost of data movement between the internal buffer and the +output buffer. Instead you can use memory mapping to retrieve a pointer to a segment of data: ```c void* pMappedFrames; @@ -1342,23 +3433,30 @@ pointer to a segment of data: } ``` -When you use memory mapping, the read cursor is increment by the frame count passed in to `ma_audio_buffer_unmap()`. If you decide not to process every frame -you can pass in a value smaller than the value returned by `ma_audio_buffer_map()`. The disadvantage to using memory mapping is that it does not handle looping -for you. You can determine if the buffer is at the end for the purpose of looping with `ma_audio_buffer_at_end()` or by inspecting the return value of -`ma_audio_buffer_unmap()` and checking if it equals `MA_AT_END`. You should not treat `MA_AT_END` as an error when returned by `ma_audio_buffer_unmap()`. +When you use memory mapping, the read cursor is increment by the frame count passed in to +`ma_audio_buffer_unmap()`. If you decide not to process every frame you can pass in a value smaller +than the value returned by `ma_audio_buffer_map()`. The disadvantage to using memory mapping is +that it does not handle looping for you. You can determine if the buffer is at the end for the +purpose of looping with `ma_audio_buffer_at_end()` or by inspecting the return value of +`ma_audio_buffer_unmap()` and checking if it equals `MA_AT_END`. You should not treat `MA_AT_END` +as an error when returned by `ma_audio_buffer_unmap()`. -10. Ring Buffers +14. Ring Buffers ================ -miniaudio supports lock free (single producer, single consumer) ring buffers which are exposed via the `ma_rb` and `ma_pcm_rb` APIs. The `ma_rb` API operates -on bytes, whereas the `ma_pcm_rb` operates on PCM frames. They are otherwise identical as `ma_pcm_rb` is just a wrapper around `ma_rb`. +miniaudio supports lock free (single producer, single consumer) ring buffers which are exposed via +the `ma_rb` and `ma_pcm_rb` APIs. The `ma_rb` API operates on bytes, whereas the `ma_pcm_rb` +operates on PCM frames. They are otherwise identical as `ma_pcm_rb` is just a wrapper around +`ma_rb`. -Unlike most other APIs in miniaudio, ring buffers support both interleaved and deinterleaved streams. The caller can also allocate their own backing memory for -the ring buffer to use internally for added flexibility. Otherwise the ring buffer will manage it's internal memory for you. +Unlike most other APIs in miniaudio, ring buffers support both interleaved and deinterleaved +streams. The caller can also allocate their own backing memory for the ring buffer to use +internally for added flexibility. Otherwise the ring buffer will manage it's internal memory for +you. -The examples below use the PCM frame variant of the ring buffer since that's most likely the one you will want to use. To initialize a ring buffer, do -something like the following: +The examples below use the PCM frame variant of the ring buffer since that's most likely the one +you will want to use. To initialize a ring buffer, do something like the following: ```c ma_pcm_rb rb; @@ -1368,39 +3466,53 @@ something like the following: } ``` -The `ma_pcm_rb_init()` function takes the sample format and channel count as parameters because it's the PCM varient of the ring buffer API. For the regular -ring buffer that operates on bytes you would call `ma_rb_init()` which leaves these out and just takes the size of the buffer in bytes instead of frames. The -fourth parameter is an optional pre-allocated buffer and the fifth parameter is a pointer to a `ma_allocation_callbacks` structure for custom memory allocation -routines. Passing in `NULL` for this results in `MA_MALLOC()` and `MA_FREE()` being used. +The `ma_pcm_rb_init()` function takes the sample format and channel count as parameters because +it's the PCM varient of the ring buffer API. For the regular ring buffer that operates on bytes you +would call `ma_rb_init()` which leaves these out and just takes the size of the buffer in bytes +instead of frames. The fourth parameter is an optional pre-allocated buffer and the fifth parameter +is a pointer to a `ma_allocation_callbacks` structure for custom memory allocation routines. +Passing in `NULL` for this results in `MA_MALLOC()` and `MA_FREE()` being used. -Use `ma_pcm_rb_init_ex()` if you need a deinterleaved buffer. The data for each sub-buffer is offset from each other based on the stride. To manage your -sub-buffers you can use `ma_pcm_rb_get_subbuffer_stride()`, `ma_pcm_rb_get_subbuffer_offset()` and `ma_pcm_rb_get_subbuffer_ptr()`. +Use `ma_pcm_rb_init_ex()` if you need a deinterleaved buffer. The data for each sub-buffer is +offset from each other based on the stride. To manage your sub-buffers you can use +`ma_pcm_rb_get_subbuffer_stride()`, `ma_pcm_rb_get_subbuffer_offset()` and +`ma_pcm_rb_get_subbuffer_ptr()`. -Use `ma_pcm_rb_acquire_read()` and `ma_pcm_rb_acquire_write()` to retrieve a pointer to a section of the ring buffer. You specify the number of frames you -need, and on output it will set to what was actually acquired. If the read or write pointer is positioned such that the number of frames requested will require -a loop, it will be clamped to the end of the buffer. Therefore, the number of frames you're given may be less than the number you requested. +Use `ma_pcm_rb_acquire_read()` and `ma_pcm_rb_acquire_write()` to retrieve a pointer to a section +of the ring buffer. You specify the number of frames you need, and on output it will set to what +was actually acquired. If the read or write pointer is positioned such that the number of frames +requested will require a loop, it will be clamped to the end of the buffer. Therefore, the number +of frames you're given may be less than the number you requested. -After calling `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()`, you do your work on the buffer and then "commit" it with `ma_pcm_rb_commit_read()` or -`ma_pcm_rb_commit_write()`. This is where the read/write pointers are updated. When you commit you need to pass in the buffer that was returned by the earlier -call to `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()` and is only used for validation. The number of frames passed to `ma_pcm_rb_commit_read()` and -`ma_pcm_rb_commit_write()` is what's used to increment the pointers, and can be less that what was originally requested. +After calling `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()`, you do your work on the +buffer and then "commit" it with `ma_pcm_rb_commit_read()` or `ma_pcm_rb_commit_write()`. This is +where the read/write pointers are updated. When you commit you need to pass in the buffer that was +returned by the earlier call to `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()` and is +only used for validation. The number of frames passed to `ma_pcm_rb_commit_read()` and +`ma_pcm_rb_commit_write()` is what's used to increment the pointers, and can be less that what was +originally requested. -If you want to correct for drift between the write pointer and the read pointer you can use a combination of `ma_pcm_rb_pointer_distance()`, -`ma_pcm_rb_seek_read()` and `ma_pcm_rb_seek_write()`. Note that you can only move the pointers forward, and you should only move the read pointer forward via -the consumer thread, and the write pointer forward by the producer thread. If there is too much space between the pointers, move the read pointer forward. If +If you want to correct for drift between the write pointer and the read pointer you can use a +combination of `ma_pcm_rb_pointer_distance()`, `ma_pcm_rb_seek_read()` and +`ma_pcm_rb_seek_write()`. Note that you can only move the pointers forward, and you should only +move the read pointer forward via the consumer thread, and the write pointer forward by the +producer thread. If there is too much space between the pointers, move the read pointer forward. If there is too little space between the pointers, move the write pointer forward. -You can use a ring buffer at the byte level instead of the PCM frame level by using the `ma_rb` API. This is exactly the same, only you will use the `ma_rb` -functions instead of `ma_pcm_rb` and instead of frame counts you will pass around byte counts. +You can use a ring buffer at the byte level instead of the PCM frame level by using the `ma_rb` +API. This is exactly the same, only you will use the `ma_rb` functions instead of `ma_pcm_rb` and +instead of frame counts you will pass around byte counts. -The maximum size of the buffer in bytes is `0x7FFFFFFF-(MA_SIMD_ALIGNMENT-1)` due to the most significant bit being used to encode a loop flag and the internally -managed buffers always being aligned to MA_SIMD_ALIGNMENT. +The maximum size of the buffer in bytes is `0x7FFFFFFF-(MA_SIMD_ALIGNMENT-1)` due to the most +significant bit being used to encode a loop flag and the internally managed buffers always being +aligned to `MA_SIMD_ALIGNMENT`. -Note that the ring buffer is only thread safe when used by a single consumer thread and single producer thread. +Note that the ring buffer is only thread safe when used by a single consumer thread and single +producer thread. -11. Backends +15. Backends ============ The following backends are supported by miniaudio. @@ -1426,28 +3538,36 @@ The following backends are supported by miniaudio. Some backends have some nuance details you may want to be aware of. -11.1. WASAPI +15.1. WASAPI ------------ -- Low-latency shared mode will be disabled when using an application-defined sample rate which is different to the device's native sample rate. To work around - this, set `wasapi.noAutoConvertSRC` to true in the device config. This is due to IAudioClient3_InitializeSharedAudioStream() failing when the - `AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM` flag is specified. Setting wasapi.noAutoConvertSRC will result in miniaudio's internal resampler being used instead - which will in turn enable the use of low-latency shared mode. +- Low-latency shared mode will be disabled when using an application-defined sample rate which is + different to the device's native sample rate. To work around this, set `wasapi.noAutoConvertSRC` + to true in the device config. This is due to IAudioClient3_InitializeSharedAudioStream() failing + when the `AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM` flag is specified. Setting wasapi.noAutoConvertSRC + will result in miniaudio's internal resampler being used instead which will in turn enable the + use of low-latency shared mode. -11.2. PulseAudio +15.2. PulseAudio ---------------- - If you experience bad glitching/noise on Arch Linux, consider this fix from the Arch wiki: - https://wiki.archlinux.org/index.php/PulseAudio/Troubleshooting#Glitches,_skips_or_crackling. Alternatively, consider using a different backend such as ALSA. + https://wiki.archlinux.org/index.php/PulseAudio/Troubleshooting#Glitches,_skips_or_crackling. + Alternatively, consider using a different backend such as ALSA. -11.3. Android +15.3. Android ------------- -- To capture audio on Android, remember to add the RECORD_AUDIO permission to your manifest: `` -- With OpenSL|ES, only a single ma_context can be active at any given time. This is due to a limitation with OpenSL|ES. -- With AAudio, only default devices are enumerated. This is due to AAudio not having an enumeration API (devices are enumerated through Java). You can however - perform your own device enumeration through Java and then set the ID in the ma_device_id structure (ma_device_id.aaudio) and pass it to ma_device_init(). -- The backend API will perform resampling where possible. The reason for this as opposed to using miniaudio's built-in resampler is to take advantage of any - potential device-specific optimizations the driver may implement. +- To capture audio on Android, remember to add the RECORD_AUDIO permission to your manifest: + `` +- With OpenSL|ES, only a single ma_context can be active at any given time. This is due to a + limitation with OpenSL|ES. +- With AAudio, only default devices are enumerated. This is due to AAudio not having an enumeration + API (devices are enumerated through Java). You can however perform your own device enumeration + through Java and then set the ID in the ma_device_id structure (ma_device_id.aaudio) and pass it + to ma_device_init(). +- The backend API will perform resampling where possible. The reason for this as opposed to using + miniaudio's built-in resampler is to take advantage of any potential device-specific + optimizations the driver may implement. -11.4. UWP +15.4. UWP --------- - UWP only supports default playback and capture devices. - UWP requires the Microphone capability to be enabled in the application's manifest (Package.appxmanifest): @@ -1461,29 +3581,51 @@ Some backends have some nuance details you may want to be aware of. ``` -11.5. Web Audio / Emscripten +15.5. Web Audio / Emscripten ---------------------------- - You cannot use `-std=c*` compiler flags, nor `-ansi`. This only applies to the Emscripten build. -- The first time a context is initialized it will create a global object called "miniaudio" whose primary purpose is to act as a factory for device objects. -- Currently the Web Audio backend uses ScriptProcessorNode's, but this may need to change later as they've been deprecated. -- Google has implemented a policy in their browsers that prevent automatic media output without first receiving some kind of user input. The following web page - has additional details: https://developers.google.com/web/updates/2017/09/autoplay-policy-changes. Starting the device may fail if you try to start playback - without first handling some kind of user input. +- The first time a context is initialized it will create a global object called "miniaudio" whose + primary purpose is to act as a factory for device objects. +- Currently the Web Audio backend uses ScriptProcessorNode's, but this may need to change later as + they've been deprecated. +- Google has implemented a policy in their browsers that prevent automatic media output without + first receiving some kind of user input. The following web page has additional details: + https://developers.google.com/web/updates/2017/09/autoplay-policy-changes. Starting the device + may fail if you try to start playback without first handling some kind of user input. -12. Miscellaneous Notes +16. Optimization Tips +===================== + +16.1. High Level API +-------------------- +- If a sound does not require doppler or pitch shifting, consider disabling pitching by + initializing the sound with the `MA_SOUND_FLAG_NO_PITCH` flag. +- If a sound does not require spatialization, disable it by initialzing the sound with the + `MA_SOUND_FLAG_NO_SPATIALIZATION` flag. It can be renabled again post-initialization with + `ma_sound_set_spatialization_enabled()`. + + + +17. Miscellaneous Notes ======================= -- Automatic stream routing is enabled on a per-backend basis. Support is explicitly enabled for WASAPI and Core Audio, however other backends such as - PulseAudio may naturally support it, though not all have been tested. -- The contents of the output buffer passed into the data callback will always be pre-initialized to silence unless the `noPreZeroedOutputBuffer` config variable - in `ma_device_config` is set to true, in which case it'll be undefined which will require you to write something to the entire buffer. -- By default miniaudio will automatically clip samples. This only applies when the playback sample format is configured as `ma_format_f32`. If you are doing - clipping yourself, you can disable this overhead by setting `noClip` to true in the device config. -- The sndio backend is currently only enabled on OpenBSD builds. -- The audio(4) backend is supported on OpenBSD, but you may need to disable sndiod before you can use it. +- Automatic stream routing is enabled on a per-backend basis. Support is explicitly enabled for + WASAPI and Core Audio, however other backends such as PulseAudio may naturally support it, though + not all have been tested. +- The contents of the output buffer passed into the data callback will always be pre-initialized to + silence unless the `noPreSilencedOutputBuffer` config variable in `ma_device_config` is set to + true, in which case it'll be undefined which will require you to write something to the entire + buffer. +- By default miniaudio will automatically clip samples. This only applies when the playback sample + format is configured as `ma_format_f32`. If you are doing clipping yourself, you can disable this + overhead by setting `noClip` to true in the device config. - Note that GCC and Clang requires `-msse2`, `-mavx2`, etc. for SIMD optimizations. -- When compiling with VC6 and earlier, decoding is restricted to files less than 2GB in size. This is due to 64-bit file APIs not being available. +- The sndio backend is currently only enabled on OpenBSD builds. +- The audio(4) backend is supported on OpenBSD, but you may need to disable sndiod before you can + use it. +- When compiling with VC6 and earlier, decoding is restricted to files less than 2GB in size. This + is due to 64-bit file APIs not being available. */ #ifndef miniaudio_h @@ -1497,8 +3639,8 @@ extern "C" { #define MA_XSTRINGIFY(x) MA_STRINGIFY(x) #define MA_VERSION_MAJOR 0 -#define MA_VERSION_MINOR 10 -#define MA_VERSION_REVISION 42 +#define MA_VERSION_MINOR 11 +#define MA_VERSION_REVISION 9 #define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION) #if defined(_MSC_VER) && !defined(__clang__) @@ -1513,66 +3655,55 @@ extern "C" { #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */ #endif #endif + -/* Platform/backend detection. */ -#ifdef _WIN32 - #define MA_WIN32 - #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PC_APP || WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) - #define MA_WIN32_UWP - #else - #define MA_WIN32_DESKTOP - #endif + +#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined(_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) + #define MA_SIZEOF_PTR 8 #else - #define MA_POSIX - #include /* Unfortunate #include, but needed for pthread_t, pthread_mutex_t and pthread_cond_t types. */ - - #ifdef __unix__ - #define MA_UNIX - #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) - #define MA_BSD - #endif - #endif - #ifdef __linux__ - #define MA_LINUX - #endif - #ifdef __APPLE__ - #define MA_APPLE - #endif - #ifdef __ANDROID__ - #define MA_ANDROID - #endif - #ifdef __EMSCRIPTEN__ - #define MA_EMSCRIPTEN - #endif + #define MA_SIZEOF_PTR 4 #endif #include /* For size_t. */ /* Sized types. */ -typedef signed char ma_int8; -typedef unsigned char ma_uint8; -typedef signed short ma_int16; -typedef unsigned short ma_uint16; -typedef signed int ma_int32; -typedef unsigned int ma_uint32; -#if defined(_MSC_VER) - typedef signed __int64 ma_int64; - typedef unsigned __int64 ma_uint64; +#if defined(MA_USE_STDINT) + #include + typedef int8_t ma_int8; + typedef uint8_t ma_uint8; + typedef int16_t ma_int16; + typedef uint16_t ma_uint16; + typedef int32_t ma_int32; + typedef uint32_t ma_uint32; + typedef int64_t ma_int64; + typedef uint64_t ma_uint64; #else - #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wlong-long" - #if defined(__clang__) - #pragma GCC diagnostic ignored "-Wc++11-long-long" + typedef signed char ma_int8; + typedef unsigned char ma_uint8; + typedef signed short ma_int16; + typedef unsigned short ma_uint16; + typedef signed int ma_int32; + typedef unsigned int ma_uint32; + #if defined(_MSC_VER) && !defined(__clang__) + typedef signed __int64 ma_int64; + typedef unsigned __int64 ma_uint64; + #else + #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wlong-long" + #if defined(__clang__) + #pragma GCC diagnostic ignored "-Wc++11-long-long" + #endif + #endif + typedef signed long long ma_int64; + typedef unsigned long long ma_uint64; + #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic pop #endif #endif - typedef signed long long ma_int64; - typedef unsigned long long ma_uint64; - #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic pop - #endif -#endif -#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) +#endif /* MA_USE_STDINT */ + +#if MA_SIZEOF_PTR == 8 typedef ma_uint64 ma_uintptr; #else typedef ma_uint32 ma_uintptr; @@ -1603,6 +3734,58 @@ typedef ma_uint16 wchar_t; #endif +/* Platform/backend detection. */ +#ifdef _WIN32 + #define MA_WIN32 + #if defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_PC_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PC_APP) || (defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)) + #define MA_WIN32_UWP + #elif defined(WINAPI_FAMILY) && (defined(WINAPI_FAMILY_GAMES) && WINAPI_FAMILY == WINAPI_FAMILY_GAMES) + #define MA_WIN32_GDK + #else + #define MA_WIN32_DESKTOP + #endif +#else + #define MA_POSIX + + /* + Use the MA_NO_PTHREAD_IN_HEADER option at your own risk. This is intentionally undocumented. + You can use this to avoid including pthread.h in the header section. The downside is that it + results in some fixed sized structures being declared for the various types that are used in + miniaudio. The risk here is that these types might be too small for a given platform. This + risk is yours to take and no support will be offered if you enable this option. + */ + #ifndef MA_NO_PTHREAD_IN_HEADER + #include /* Unfortunate #include, but needed for pthread_t, pthread_mutex_t and pthread_cond_t types. */ + typedef pthread_t ma_pthread_t; + typedef pthread_mutex_t ma_pthread_mutex_t; + typedef pthread_cond_t ma_pthread_cond_t; + #else + typedef ma_uintptr ma_pthread_t; + typedef union ma_pthread_mutex_t { char __data[40]; ma_uint64 __alignment; } ma_pthread_mutex_t; + typedef union ma_pthread_cond_t { char __data[48]; ma_uint64 __alignment; } ma_pthread_cond_t; + #endif + + #ifdef __unix__ + #define MA_UNIX + #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) + #define MA_BSD + #endif + #endif + #ifdef __linux__ + #define MA_LINUX + #endif + #ifdef __APPLE__ + #define MA_APPLE + #endif + #ifdef __ANDROID__ + #define MA_ANDROID + #endif + #ifdef __EMSCRIPTEN__ + #define MA_EMSCRIPTEN + #endif +#endif + + #ifdef _MSC_VER #define MA_INLINE __forceinline #elif defined(__GNUC__) @@ -1614,9 +3797,15 @@ typedef ma_uint16 wchar_t; I am using "__inline__" only when we're compiling in strict ANSI mode. */ #if defined(__STRICT_ANSI__) - #define MA_INLINE __inline__ __attribute__((always_inline)) + #define MA_GNUC_INLINE_HINT __inline__ #else - #define MA_INLINE inline __attribute__((always_inline)) + #define MA_GNUC_INLINE_HINT inline + #endif + + #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) + #define MA_INLINE MA_GNUC_INLINE_HINT __attribute__((always_inline)) + #else + #define MA_INLINE MA_GNUC_INLINE_HINT #endif #elif defined(__WATCOMC__) #define MA_INLINE __inline @@ -1654,201 +3843,219 @@ typedef ma_uint16 wchar_t; #endif #endif -/* SIMD alignment in bytes. Currently set to 64 bytes in preparation for future AVX-512 optimizations. */ -#define MA_SIMD_ALIGNMENT 64 +/* SIMD alignment in bytes. Currently set to 32 bytes in preparation for future AVX optimizations. */ +#define MA_SIMD_ALIGNMENT 32 /* Logging Levels ============== Log levels are only used to give logging callbacks some context as to the severity of a log message -so they can do filtering. All log levels will be posted to registered logging callbacks, except for -MA_LOG_LEVEL_DEBUG which will only get processed if MA_DEBUG_OUTPUT is enabled. +so they can do filtering. All log levels will be posted to registered logging callbacks. If you +don't want to output a certain log level you can discriminate against the log level in the callback. MA_LOG_LEVEL_DEBUG - Used for debugging. These log messages are only posted when `MA_DEBUG_OUTPUT` is enabled. + Used for debugging. Useful for debug and test builds, but should be disabled in release builds. MA_LOG_LEVEL_INFO - Informational logging. Useful for debugging. This will also enable warning and error logs. This - will never be called from within the data callback. + Informational logging. Useful for debugging. This will never be called from within the data + callback. MA_LOG_LEVEL_WARNING - Warnings. You should enable this in you development builds and action them when encounted. This - will also enable error logs. These logs usually indicate a potential problem or - misconfiguration, but still allow you to keep running. This will never be called from within - the data callback. + Warnings. You should enable this in you development builds and action them when encounted. These + logs usually indicate a potential problem or misconfiguration, but still allow you to keep + running. This will never be called from within the data callback. MA_LOG_LEVEL_ERROR Error logging. This will be fired when an operation fails and is subsequently aborted. This can be fired from within the data callback, in which case the device will be stopped. You should always have this log level enabled. */ -#define MA_LOG_LEVEL_DEBUG 4 -#define MA_LOG_LEVEL_INFO 3 -#define MA_LOG_LEVEL_WARNING 2 -#define MA_LOG_LEVEL_ERROR 1 - -/* Deprecated. */ -#define MA_LOG_LEVEL_VERBOSE MA_LOG_LEVEL_DEBUG - -/* Deprecated. */ -#ifndef MA_LOG_LEVEL -#define MA_LOG_LEVEL MA_LOG_LEVEL_ERROR -#endif +typedef enum +{ + MA_LOG_LEVEL_DEBUG = 4, + MA_LOG_LEVEL_INFO = 3, + MA_LOG_LEVEL_WARNING = 2, + MA_LOG_LEVEL_ERROR = 1 +} ma_log_level; /* -An annotation for variables which must be used atomically. This doesn't actually do anything - it's -just used as a way for humans to identify variables that should be used atomically. +Variables needing to be accessed atomically should be declared with this macro for two reasons: + + 1) It allows people who read the code to identify a variable as such; and + 2) It forces alignment on platforms where it's required or optimal. + +Note that for x86/64, alignment is not strictly necessary, but does have some performance +implications. Where supported by the compiler, alignment will be used, but otherwise if the CPU +architecture does not require it, it will simply leave it unaligned. This is the case with old +versions of Visual Studio, which I've confirmed with at least VC6. */ -#define MA_ATOMIC +#if defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) + #include + #define MA_ATOMIC(alignment, type) alignas(alignment) type +#else + #if defined(__GNUC__) + /* GCC-style compilers. */ + #define MA_ATOMIC(alignment, type) type __attribute__((aligned(alignment))) + #elif defined(_MSC_VER) && _MSC_VER > 1200 /* 1200 = VC6. Alignment not supported, but not necessary because x86 is the only supported target. */ + /* MSVC. */ + #define MA_ATOMIC(alignment, type) __declspec(align(alignment)) type + #else + /* Other compilers. */ + #define MA_ATOMIC(alignment, type) type + #endif +#endif typedef struct ma_context ma_context; typedef struct ma_device ma_device; typedef ma_uint8 ma_channel; -#define MA_CHANNEL_NONE 0 -#define MA_CHANNEL_MONO 1 -#define MA_CHANNEL_FRONT_LEFT 2 -#define MA_CHANNEL_FRONT_RIGHT 3 -#define MA_CHANNEL_FRONT_CENTER 4 -#define MA_CHANNEL_LFE 5 -#define MA_CHANNEL_BACK_LEFT 6 -#define MA_CHANNEL_BACK_RIGHT 7 -#define MA_CHANNEL_FRONT_LEFT_CENTER 8 -#define MA_CHANNEL_FRONT_RIGHT_CENTER 9 -#define MA_CHANNEL_BACK_CENTER 10 -#define MA_CHANNEL_SIDE_LEFT 11 -#define MA_CHANNEL_SIDE_RIGHT 12 -#define MA_CHANNEL_TOP_CENTER 13 -#define MA_CHANNEL_TOP_FRONT_LEFT 14 -#define MA_CHANNEL_TOP_FRONT_CENTER 15 -#define MA_CHANNEL_TOP_FRONT_RIGHT 16 -#define MA_CHANNEL_TOP_BACK_LEFT 17 -#define MA_CHANNEL_TOP_BACK_CENTER 18 -#define MA_CHANNEL_TOP_BACK_RIGHT 19 -#define MA_CHANNEL_AUX_0 20 -#define MA_CHANNEL_AUX_1 21 -#define MA_CHANNEL_AUX_2 22 -#define MA_CHANNEL_AUX_3 23 -#define MA_CHANNEL_AUX_4 24 -#define MA_CHANNEL_AUX_5 25 -#define MA_CHANNEL_AUX_6 26 -#define MA_CHANNEL_AUX_7 27 -#define MA_CHANNEL_AUX_8 28 -#define MA_CHANNEL_AUX_9 29 -#define MA_CHANNEL_AUX_10 30 -#define MA_CHANNEL_AUX_11 31 -#define MA_CHANNEL_AUX_12 32 -#define MA_CHANNEL_AUX_13 33 -#define MA_CHANNEL_AUX_14 34 -#define MA_CHANNEL_AUX_15 35 -#define MA_CHANNEL_AUX_16 36 -#define MA_CHANNEL_AUX_17 37 -#define MA_CHANNEL_AUX_18 38 -#define MA_CHANNEL_AUX_19 39 -#define MA_CHANNEL_AUX_20 40 -#define MA_CHANNEL_AUX_21 41 -#define MA_CHANNEL_AUX_22 42 -#define MA_CHANNEL_AUX_23 43 -#define MA_CHANNEL_AUX_24 44 -#define MA_CHANNEL_AUX_25 45 -#define MA_CHANNEL_AUX_26 46 -#define MA_CHANNEL_AUX_27 47 -#define MA_CHANNEL_AUX_28 48 -#define MA_CHANNEL_AUX_29 49 -#define MA_CHANNEL_AUX_30 50 -#define MA_CHANNEL_AUX_31 51 -#define MA_CHANNEL_LEFT MA_CHANNEL_FRONT_LEFT -#define MA_CHANNEL_RIGHT MA_CHANNEL_FRONT_RIGHT -#define MA_CHANNEL_POSITION_COUNT (MA_CHANNEL_AUX_31 + 1) +typedef enum +{ + MA_CHANNEL_NONE = 0, + MA_CHANNEL_MONO = 1, + MA_CHANNEL_FRONT_LEFT = 2, + MA_CHANNEL_FRONT_RIGHT = 3, + MA_CHANNEL_FRONT_CENTER = 4, + MA_CHANNEL_LFE = 5, + MA_CHANNEL_BACK_LEFT = 6, + MA_CHANNEL_BACK_RIGHT = 7, + MA_CHANNEL_FRONT_LEFT_CENTER = 8, + MA_CHANNEL_FRONT_RIGHT_CENTER = 9, + MA_CHANNEL_BACK_CENTER = 10, + MA_CHANNEL_SIDE_LEFT = 11, + MA_CHANNEL_SIDE_RIGHT = 12, + MA_CHANNEL_TOP_CENTER = 13, + MA_CHANNEL_TOP_FRONT_LEFT = 14, + MA_CHANNEL_TOP_FRONT_CENTER = 15, + MA_CHANNEL_TOP_FRONT_RIGHT = 16, + MA_CHANNEL_TOP_BACK_LEFT = 17, + MA_CHANNEL_TOP_BACK_CENTER = 18, + MA_CHANNEL_TOP_BACK_RIGHT = 19, + MA_CHANNEL_AUX_0 = 20, + MA_CHANNEL_AUX_1 = 21, + MA_CHANNEL_AUX_2 = 22, + MA_CHANNEL_AUX_3 = 23, + MA_CHANNEL_AUX_4 = 24, + MA_CHANNEL_AUX_5 = 25, + MA_CHANNEL_AUX_6 = 26, + MA_CHANNEL_AUX_7 = 27, + MA_CHANNEL_AUX_8 = 28, + MA_CHANNEL_AUX_9 = 29, + MA_CHANNEL_AUX_10 = 30, + MA_CHANNEL_AUX_11 = 31, + MA_CHANNEL_AUX_12 = 32, + MA_CHANNEL_AUX_13 = 33, + MA_CHANNEL_AUX_14 = 34, + MA_CHANNEL_AUX_15 = 35, + MA_CHANNEL_AUX_16 = 36, + MA_CHANNEL_AUX_17 = 37, + MA_CHANNEL_AUX_18 = 38, + MA_CHANNEL_AUX_19 = 39, + MA_CHANNEL_AUX_20 = 40, + MA_CHANNEL_AUX_21 = 41, + MA_CHANNEL_AUX_22 = 42, + MA_CHANNEL_AUX_23 = 43, + MA_CHANNEL_AUX_24 = 44, + MA_CHANNEL_AUX_25 = 45, + MA_CHANNEL_AUX_26 = 46, + MA_CHANNEL_AUX_27 = 47, + MA_CHANNEL_AUX_28 = 48, + MA_CHANNEL_AUX_29 = 49, + MA_CHANNEL_AUX_30 = 50, + MA_CHANNEL_AUX_31 = 51, + MA_CHANNEL_LEFT = MA_CHANNEL_FRONT_LEFT, + MA_CHANNEL_RIGHT = MA_CHANNEL_FRONT_RIGHT, + MA_CHANNEL_POSITION_COUNT = (MA_CHANNEL_AUX_31 + 1) +} _ma_channel_position; /* Do not use `_ma_channel_position` directly. Use `ma_channel` instead. */ + +typedef enum +{ + MA_SUCCESS = 0, + MA_ERROR = -1, /* A generic error. */ + MA_INVALID_ARGS = -2, + MA_INVALID_OPERATION = -3, + MA_OUT_OF_MEMORY = -4, + MA_OUT_OF_RANGE = -5, + MA_ACCESS_DENIED = -6, + MA_DOES_NOT_EXIST = -7, + MA_ALREADY_EXISTS = -8, + MA_TOO_MANY_OPEN_FILES = -9, + MA_INVALID_FILE = -10, + MA_TOO_BIG = -11, + MA_PATH_TOO_LONG = -12, + MA_NAME_TOO_LONG = -13, + MA_NOT_DIRECTORY = -14, + MA_IS_DIRECTORY = -15, + MA_DIRECTORY_NOT_EMPTY = -16, + MA_AT_END = -17, + MA_NO_SPACE = -18, + MA_BUSY = -19, + MA_IO_ERROR = -20, + MA_INTERRUPT = -21, + MA_UNAVAILABLE = -22, + MA_ALREADY_IN_USE = -23, + MA_BAD_ADDRESS = -24, + MA_BAD_SEEK = -25, + MA_BAD_PIPE = -26, + MA_DEADLOCK = -27, + MA_TOO_MANY_LINKS = -28, + MA_NOT_IMPLEMENTED = -29, + MA_NO_MESSAGE = -30, + MA_BAD_MESSAGE = -31, + MA_NO_DATA_AVAILABLE = -32, + MA_INVALID_DATA = -33, + MA_TIMEOUT = -34, + MA_NO_NETWORK = -35, + MA_NOT_UNIQUE = -36, + MA_NOT_SOCKET = -37, + MA_NO_ADDRESS = -38, + MA_BAD_PROTOCOL = -39, + MA_PROTOCOL_UNAVAILABLE = -40, + MA_PROTOCOL_NOT_SUPPORTED = -41, + MA_PROTOCOL_FAMILY_NOT_SUPPORTED = -42, + MA_ADDRESS_FAMILY_NOT_SUPPORTED = -43, + MA_SOCKET_NOT_SUPPORTED = -44, + MA_CONNECTION_RESET = -45, + MA_ALREADY_CONNECTED = -46, + MA_NOT_CONNECTED = -47, + MA_CONNECTION_REFUSED = -48, + MA_NO_HOST = -49, + MA_IN_PROGRESS = -50, + MA_CANCELLED = -51, + MA_MEMORY_ALREADY_MAPPED = -52, + + /* General miniaudio-specific errors. */ + MA_FORMAT_NOT_SUPPORTED = -100, + MA_DEVICE_TYPE_NOT_SUPPORTED = -101, + MA_SHARE_MODE_NOT_SUPPORTED = -102, + MA_NO_BACKEND = -103, + MA_NO_DEVICE = -104, + MA_API_NOT_FOUND = -105, + MA_INVALID_DEVICE_CONFIG = -106, + MA_LOOP = -107, + + /* State errors. */ + MA_DEVICE_NOT_INITIALIZED = -200, + MA_DEVICE_ALREADY_INITIALIZED = -201, + MA_DEVICE_NOT_STARTED = -202, + MA_DEVICE_NOT_STOPPED = -203, + + /* Operation errors. */ + MA_FAILED_TO_INIT_BACKEND = -300, + MA_FAILED_TO_OPEN_BACKEND_DEVICE = -301, + MA_FAILED_TO_START_BACKEND_DEVICE = -302, + MA_FAILED_TO_STOP_BACKEND_DEVICE = -303 +} ma_result; -typedef int ma_result; -#define MA_SUCCESS 0 -#define MA_ERROR -1 /* A generic error. */ -#define MA_INVALID_ARGS -2 -#define MA_INVALID_OPERATION -3 -#define MA_OUT_OF_MEMORY -4 -#define MA_OUT_OF_RANGE -5 -#define MA_ACCESS_DENIED -6 -#define MA_DOES_NOT_EXIST -7 -#define MA_ALREADY_EXISTS -8 -#define MA_TOO_MANY_OPEN_FILES -9 -#define MA_INVALID_FILE -10 -#define MA_TOO_BIG -11 -#define MA_PATH_TOO_LONG -12 -#define MA_NAME_TOO_LONG -13 -#define MA_NOT_DIRECTORY -14 -#define MA_IS_DIRECTORY -15 -#define MA_DIRECTORY_NOT_EMPTY -16 -#define MA_AT_END -17 -#define MA_NO_SPACE -18 -#define MA_BUSY -19 -#define MA_IO_ERROR -20 -#define MA_INTERRUPT -21 -#define MA_UNAVAILABLE -22 -#define MA_ALREADY_IN_USE -23 -#define MA_BAD_ADDRESS -24 -#define MA_BAD_SEEK -25 -#define MA_BAD_PIPE -26 -#define MA_DEADLOCK -27 -#define MA_TOO_MANY_LINKS -28 -#define MA_NOT_IMPLEMENTED -29 -#define MA_NO_MESSAGE -30 -#define MA_BAD_MESSAGE -31 -#define MA_NO_DATA_AVAILABLE -32 -#define MA_INVALID_DATA -33 -#define MA_TIMEOUT -34 -#define MA_NO_NETWORK -35 -#define MA_NOT_UNIQUE -36 -#define MA_NOT_SOCKET -37 -#define MA_NO_ADDRESS -38 -#define MA_BAD_PROTOCOL -39 -#define MA_PROTOCOL_UNAVAILABLE -40 -#define MA_PROTOCOL_NOT_SUPPORTED -41 -#define MA_PROTOCOL_FAMILY_NOT_SUPPORTED -42 -#define MA_ADDRESS_FAMILY_NOT_SUPPORTED -43 -#define MA_SOCKET_NOT_SUPPORTED -44 -#define MA_CONNECTION_RESET -45 -#define MA_ALREADY_CONNECTED -46 -#define MA_NOT_CONNECTED -47 -#define MA_CONNECTION_REFUSED -48 -#define MA_NO_HOST -49 -#define MA_IN_PROGRESS -50 -#define MA_CANCELLED -51 -#define MA_MEMORY_ALREADY_MAPPED -52 - -/* General miniaudio-specific errors. */ -#define MA_FORMAT_NOT_SUPPORTED -100 -#define MA_DEVICE_TYPE_NOT_SUPPORTED -101 -#define MA_SHARE_MODE_NOT_SUPPORTED -102 -#define MA_NO_BACKEND -103 -#define MA_NO_DEVICE -104 -#define MA_API_NOT_FOUND -105 -#define MA_INVALID_DEVICE_CONFIG -106 -#define MA_LOOP -107 - -/* State errors. */ -#define MA_DEVICE_NOT_INITIALIZED -200 -#define MA_DEVICE_ALREADY_INITIALIZED -201 -#define MA_DEVICE_NOT_STARTED -202 -#define MA_DEVICE_NOT_STOPPED -203 - -/* Operation errors. */ -#define MA_FAILED_TO_INIT_BACKEND -300 -#define MA_FAILED_TO_OPEN_BACKEND_DEVICE -301 -#define MA_FAILED_TO_START_BACKEND_DEVICE -302 -#define MA_FAILED_TO_STOP_BACKEND_DEVICE -303 - - -#define MA_MIN_CHANNELS 1 -#ifndef MA_MAX_CHANNELS -#define MA_MAX_CHANNELS 32 +#define MA_MIN_CHANNELS 1 +#ifndef MA_MAX_CHANNELS +#define MA_MAX_CHANNELS 254 #endif - #ifndef MA_MAX_FILTER_ORDER -#define MA_MAX_FILTER_ORDER 8 +#define MA_MAX_FILTER_ORDER 8 #endif typedef enum @@ -1911,17 +4118,12 @@ typedef enum ma_standard_sample_rate_count = 14 /* Need to maintain the count manually. Make sure this is updated if items are added to enum. */ } ma_standard_sample_rate; -/* These are deprecated. Use ma_standard_sample_rate_min and ma_standard_sample_rate_max. */ -#define MA_MIN_SAMPLE_RATE (ma_uint32)ma_standard_sample_rate_min -#define MA_MAX_SAMPLE_RATE (ma_uint32)ma_standard_sample_rate_max - typedef enum { ma_channel_mix_mode_rectangular = 0, /* Simple averaging based on the plane(s) the channel is sitting on. */ ma_channel_mix_mode_simple, /* Drop excess channels; zeroed out extra channels. */ ma_channel_mix_mode_custom_weights, /* Use custom weights specified in ma_channel_router_config. */ - ma_channel_mix_mode_planar_blend = ma_channel_mix_mode_rectangular, ma_channel_mix_mode_default = ma_channel_mix_mode_rectangular } ma_channel_mix_mode; @@ -1959,6 +4161,9 @@ typedef struct } ma_lcg; +/* Spinlocks are 32-bit for compatibility reasons. */ +typedef ma_uint32 ma_spinlock; + #ifndef MA_NO_THREADING /* Thread priorities should be ordered such that the default priority of the worker thread is 0. */ typedef enum @@ -1973,21 +4178,18 @@ typedef enum ma_thread_priority_default = 0 } ma_thread_priority; -/* Spinlocks are 32-bit for compatibility reasons. */ -typedef ma_uint32 ma_spinlock; - #if defined(MA_WIN32) typedef ma_handle ma_thread; #endif #if defined(MA_POSIX) -typedef pthread_t ma_thread; +typedef ma_pthread_t ma_thread; #endif #if defined(MA_WIN32) typedef ma_handle ma_mutex; #endif #if defined(MA_POSIX) -typedef pthread_mutex_t ma_mutex; +typedef ma_pthread_mutex_t ma_mutex; #endif #if defined(MA_WIN32) @@ -1997,8 +4199,8 @@ typedef ma_handle ma_event; typedef struct { ma_uint32 value; - pthread_mutex_t lock; - pthread_cond_t cond; + ma_pthread_mutex_t lock; + ma_pthread_cond_t cond; } ma_event; #endif /* MA_POSIX */ @@ -2009,8 +4211,8 @@ typedef ma_handle ma_semaphore; typedef struct { int value; - pthread_mutex_t lock; - pthread_cond_t cond; + ma_pthread_mutex_t lock; + ma_pthread_cond_t cond; } ma_semaphore; #endif /* MA_POSIX */ #else @@ -2052,6 +4254,36 @@ Logging #define MA_MAX_LOG_CALLBACKS 4 #endif + +/* +The callback for handling log messages. + + +Parameters +---------- +pUserData (in) + The user data pointer that was passed into ma_log_register_callback(). + +logLevel (in) + The log level. This can be one of the following: + + +----------------------+ + | Log Level | + +----------------------+ + | MA_LOG_LEVEL_DEBUG | + | MA_LOG_LEVEL_INFO | + | MA_LOG_LEVEL_WARNING | + | MA_LOG_LEVEL_ERROR | + +----------------------+ + +pMessage (in) + The log message. + + +Remarks +------- +Do not modify the state of the device from inside the callback. +*/ typedef void (* ma_log_callback_proc)(void* pUserData, ma_uint32 level, const char* pMessage); typedef struct @@ -2116,12 +4348,20 @@ typedef struct ma_biquad_coefficient b2; ma_biquad_coefficient a1; ma_biquad_coefficient a2; - ma_biquad_coefficient r1[MA_MAX_CHANNELS]; - ma_biquad_coefficient r2[MA_MAX_CHANNELS]; + ma_biquad_coefficient* pR1; + ma_biquad_coefficient* pR2; + + /* Memory management. */ + void* _pHeap; + ma_bool32 _ownsHeap; } ma_biquad; -MA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, ma_biquad* pBQ); +MA_API ma_result ma_biquad_get_heap_size(const ma_biquad_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_biquad_init_preallocated(const ma_biquad_config* pConfig, void* pHeap, ma_biquad* pBQ); +MA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad* pBQ); +MA_API void ma_biquad_uninit(ma_biquad* pBQ, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ); +MA_API ma_result ma_biquad_clear_cache(ma_biquad* pBQ); MA_API ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); MA_API ma_uint32 ma_biquad_get_latency(const ma_biquad* pBQ); @@ -2148,11 +4388,19 @@ typedef struct ma_format format; ma_uint32 channels; ma_biquad_coefficient a; - ma_biquad_coefficient r1[MA_MAX_CHANNELS]; + ma_biquad_coefficient* pR1; + + /* Memory management. */ + void* _pHeap; + ma_bool32 _ownsHeap; } ma_lpf1; -MA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, ma_lpf1* pLPF); +MA_API ma_result ma_lpf1_get_heap_size(const ma_lpf1_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_lpf1_init_preallocated(const ma_lpf1_config* pConfig, void* pHeap, ma_lpf1* pLPF); +MA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf1* pLPF); +MA_API void ma_lpf1_uninit(ma_lpf1* pLPF, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_lpf1_reinit(const ma_lpf1_config* pConfig, ma_lpf1* pLPF); +MA_API ma_result ma_lpf1_clear_cache(ma_lpf1* pLPF); MA_API ma_result ma_lpf1_process_pcm_frames(ma_lpf1* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); MA_API ma_uint32 ma_lpf1_get_latency(const ma_lpf1* pLPF); @@ -2161,8 +4409,12 @@ typedef struct ma_biquad bq; /* The second order low-pass filter is implemented as a biquad filter. */ } ma_lpf2; -MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, ma_lpf2* pLPF); +MA_API ma_result ma_lpf2_get_heap_size(const ma_lpf2_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_lpf2_init_preallocated(const ma_lpf2_config* pConfig, void* pHeap, ma_lpf2* pHPF); +MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf2* pLPF); +MA_API void ma_lpf2_uninit(ma_lpf2* pLPF, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_lpf2_reinit(const ma_lpf2_config* pConfig, ma_lpf2* pLPF); +MA_API ma_result ma_lpf2_clear_cache(ma_lpf2* pLPF); MA_API ma_result ma_lpf2_process_pcm_frames(ma_lpf2* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); MA_API ma_uint32 ma_lpf2_get_latency(const ma_lpf2* pLPF); @@ -2185,12 +4437,20 @@ typedef struct ma_uint32 sampleRate; ma_uint32 lpf1Count; ma_uint32 lpf2Count; - ma_lpf1 lpf1[1]; - ma_lpf2 lpf2[MA_MAX_FILTER_ORDER/2]; + ma_lpf1* pLPF1; + ma_lpf2* pLPF2; + + /* Memory management. */ + void* _pHeap; + ma_bool32 _ownsHeap; } ma_lpf; -MA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, ma_lpf* pLPF); +MA_API ma_result ma_lpf_get_heap_size(const ma_lpf_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_lpf_init_preallocated(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF); +MA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf* pLPF); +MA_API void ma_lpf_uninit(ma_lpf* pLPF, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF); +MA_API ma_result ma_lpf_clear_cache(ma_lpf* pLPF); MA_API ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); MA_API ma_uint32 ma_lpf_get_latency(const ma_lpf* pLPF); @@ -2217,10 +4477,17 @@ typedef struct ma_format format; ma_uint32 channels; ma_biquad_coefficient a; - ma_biquad_coefficient r1[MA_MAX_CHANNELS]; + ma_biquad_coefficient* pR1; + + /* Memory management. */ + void* _pHeap; + ma_bool32 _ownsHeap; } ma_hpf1; -MA_API ma_result ma_hpf1_init(const ma_hpf1_config* pConfig, ma_hpf1* pHPF); +MA_API ma_result ma_hpf1_get_heap_size(const ma_hpf1_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_hpf1_init_preallocated(const ma_hpf1_config* pConfig, void* pHeap, ma_hpf1* pLPF); +MA_API ma_result ma_hpf1_init(const ma_hpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf1* pHPF); +MA_API void ma_hpf1_uninit(ma_hpf1* pHPF, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_hpf1_reinit(const ma_hpf1_config* pConfig, ma_hpf1* pHPF); MA_API ma_result ma_hpf1_process_pcm_frames(ma_hpf1* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); MA_API ma_uint32 ma_hpf1_get_latency(const ma_hpf1* pHPF); @@ -2230,7 +4497,10 @@ typedef struct ma_biquad bq; /* The second order high-pass filter is implemented as a biquad filter. */ } ma_hpf2; -MA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, ma_hpf2* pHPF); +MA_API ma_result ma_hpf2_get_heap_size(const ma_hpf2_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_hpf2_init_preallocated(const ma_hpf2_config* pConfig, void* pHeap, ma_hpf2* pHPF); +MA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf2* pHPF); +MA_API void ma_hpf2_uninit(ma_hpf2* pHPF, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_hpf2_reinit(const ma_hpf2_config* pConfig, ma_hpf2* pHPF); MA_API ma_result ma_hpf2_process_pcm_frames(ma_hpf2* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); MA_API ma_uint32 ma_hpf2_get_latency(const ma_hpf2* pHPF); @@ -2254,11 +4524,18 @@ typedef struct ma_uint32 sampleRate; ma_uint32 hpf1Count; ma_uint32 hpf2Count; - ma_hpf1 hpf1[1]; - ma_hpf2 hpf2[MA_MAX_FILTER_ORDER/2]; + ma_hpf1* pHPF1; + ma_hpf2* pHPF2; + + /* Memory management. */ + void* _pHeap; + ma_bool32 _ownsHeap; } ma_hpf; -MA_API ma_result ma_hpf_init(const ma_hpf_config* pConfig, ma_hpf* pHPF); +MA_API ma_result ma_hpf_get_heap_size(const ma_hpf_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_hpf_init_preallocated(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pLPF); +MA_API ma_result ma_hpf_init(const ma_hpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf* pHPF); +MA_API void ma_hpf_uninit(ma_hpf* pHPF, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_hpf_reinit(const ma_hpf_config* pConfig, ma_hpf* pHPF); MA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); MA_API ma_uint32 ma_hpf_get_latency(const ma_hpf* pHPF); @@ -2285,7 +4562,10 @@ typedef struct ma_biquad bq; /* The second order band-pass filter is implemented as a biquad filter. */ } ma_bpf2; -MA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, ma_bpf2* pBPF); +MA_API ma_result ma_bpf2_get_heap_size(const ma_bpf2_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_bpf2_init_preallocated(const ma_bpf2_config* pConfig, void* pHeap, ma_bpf2* pBPF); +MA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf2* pBPF); +MA_API void ma_bpf2_uninit(ma_bpf2* pBPF, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_bpf2_reinit(const ma_bpf2_config* pConfig, ma_bpf2* pBPF); MA_API ma_result ma_bpf2_process_pcm_frames(ma_bpf2* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); MA_API ma_uint32 ma_bpf2_get_latency(const ma_bpf2* pBPF); @@ -2307,10 +4587,17 @@ typedef struct ma_format format; ma_uint32 channels; ma_uint32 bpf2Count; - ma_bpf2 bpf2[MA_MAX_FILTER_ORDER/2]; + ma_bpf2* pBPF2; + + /* Memory management. */ + void* _pHeap; + ma_bool32 _ownsHeap; } ma_bpf; -MA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, ma_bpf* pBPF); +MA_API ma_result ma_bpf_get_heap_size(const ma_bpf_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_bpf_init_preallocated(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF); +MA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf* pBPF); +MA_API void ma_bpf_uninit(ma_bpf* pBPF, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_bpf_reinit(const ma_bpf_config* pConfig, ma_bpf* pBPF); MA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); MA_API ma_uint32 ma_bpf_get_latency(const ma_bpf* pBPF); @@ -2337,7 +4624,10 @@ typedef struct ma_biquad bq; } ma_notch2; -MA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, ma_notch2* pFilter); +MA_API ma_result ma_notch2_get_heap_size(const ma_notch2_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_notch2_init_preallocated(const ma_notch2_config* pConfig, void* pHeap, ma_notch2* pFilter); +MA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch2* pFilter); +MA_API void ma_notch2_uninit(ma_notch2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_notch2_reinit(const ma_notch2_config* pConfig, ma_notch2* pFilter); MA_API ma_result ma_notch2_process_pcm_frames(ma_notch2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); MA_API ma_uint32 ma_notch2_get_latency(const ma_notch2* pFilter); @@ -2365,7 +4655,10 @@ typedef struct ma_biquad bq; } ma_peak2; -MA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, ma_peak2* pFilter); +MA_API ma_result ma_peak2_get_heap_size(const ma_peak2_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_peak2_init_preallocated(const ma_peak2_config* pConfig, void* pHeap, ma_peak2* pFilter); +MA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak2* pFilter); +MA_API void ma_peak2_uninit(ma_peak2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_peak2_reinit(const ma_peak2_config* pConfig, ma_peak2* pFilter); MA_API ma_result ma_peak2_process_pcm_frames(ma_peak2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); MA_API ma_uint32 ma_peak2_get_latency(const ma_peak2* pFilter); @@ -2393,7 +4686,10 @@ typedef struct ma_biquad bq; } ma_loshelf2; -MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter); +MA_API ma_result ma_loshelf2_get_heap_size(const ma_loshelf2_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_loshelf2_init_preallocated(const ma_loshelf2_config* pConfig, void* pHeap, ma_loshelf2* pFilter); +MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf2* pFilter); +MA_API void ma_loshelf2_uninit(ma_loshelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_loshelf2_reinit(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter); MA_API ma_result ma_loshelf2_process_pcm_frames(ma_loshelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); MA_API ma_uint32 ma_loshelf2_get_latency(const ma_loshelf2* pFilter); @@ -2421,13 +4717,316 @@ typedef struct ma_biquad bq; } ma_hishelf2; -MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter); +MA_API ma_result ma_hishelf2_get_heap_size(const ma_hishelf2_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_hishelf2_init_preallocated(const ma_hishelf2_config* pConfig, void* pHeap, ma_hishelf2* pFilter); +MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf2* pFilter); +MA_API void ma_hishelf2_uninit(ma_hishelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_hishelf2_reinit(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter); MA_API ma_result ma_hishelf2_process_pcm_frames(ma_hishelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); MA_API ma_uint32 ma_hishelf2_get_latency(const ma_hishelf2* pFilter); +/* +Delay +*/ +typedef struct +{ + ma_uint32 channels; + ma_uint32 sampleRate; + ma_uint32 delayInFrames; + ma_bool32 delayStart; /* Set to true to delay the start of the output; false otherwise. */ + float wet; /* 0..1. Default = 1. */ + float dry; /* 0..1. Default = 1. */ + float decay; /* 0..1. Default = 0 (no feedback). Feedback decay. Use this for echo. */ +} ma_delay_config; + +MA_API ma_delay_config ma_delay_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay); + + +typedef struct +{ + ma_delay_config config; + ma_uint32 cursor; /* Feedback is written to this cursor. Always equal or in front of the read cursor. */ + ma_uint32 bufferSizeInFrames; /* The maximum of config.startDelayInFrames and config.feedbackDelayInFrames. */ + float* pBuffer; +} ma_delay; + +MA_API ma_result ma_delay_init(const ma_delay_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay* pDelay); +MA_API void ma_delay_uninit(ma_delay* pDelay, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_delay_process_pcm_frames(ma_delay* pDelay, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount); +MA_API void ma_delay_set_wet(ma_delay* pDelay, float value); +MA_API float ma_delay_get_wet(const ma_delay* pDelay); +MA_API void ma_delay_set_dry(ma_delay* pDelay, float value); +MA_API float ma_delay_get_dry(const ma_delay* pDelay); +MA_API void ma_delay_set_decay(ma_delay* pDelay, float value); +MA_API float ma_delay_get_decay(const ma_delay* pDelay); + + +/* Gainer for smooth volume changes. */ +typedef struct +{ + ma_uint32 channels; + ma_uint32 smoothTimeInFrames; +} ma_gainer_config; + +MA_API ma_gainer_config ma_gainer_config_init(ma_uint32 channels, ma_uint32 smoothTimeInFrames); + + +typedef struct +{ + ma_gainer_config config; + ma_uint32 t; + float* pOldGains; + float* pNewGains; + + /* Memory management. */ + void* _pHeap; + ma_bool32 _ownsHeap; +} ma_gainer; + +MA_API ma_result ma_gainer_get_heap_size(const ma_gainer_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_gainer_init_preallocated(const ma_gainer_config* pConfig, void* pHeap, ma_gainer* pGainer); +MA_API ma_result ma_gainer_init(const ma_gainer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_gainer* pGainer); +MA_API void ma_gainer_uninit(ma_gainer* pGainer, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_gainer_process_pcm_frames(ma_gainer* pGainer, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API ma_result ma_gainer_set_gain(ma_gainer* pGainer, float newGain); +MA_API ma_result ma_gainer_set_gains(ma_gainer* pGainer, float* pNewGains); + + + +/* Stereo panner. */ +typedef enum +{ + ma_pan_mode_balance = 0, /* Does not blend one side with the other. Technically just a balance. Compatible with other popular audio engines and therefore the default. */ + ma_pan_mode_pan /* A true pan. The sound from one side will "move" to the other side and blend with it. */ +} ma_pan_mode; + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_pan_mode mode; + float pan; +} ma_panner_config; + +MA_API ma_panner_config ma_panner_config_init(ma_format format, ma_uint32 channels); + + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_pan_mode mode; + float pan; /* -1..1 where 0 is no pan, -1 is left side, +1 is right side. Defaults to 0. */ +} ma_panner; + +MA_API ma_result ma_panner_init(const ma_panner_config* pConfig, ma_panner* pPanner); +MA_API ma_result ma_panner_process_pcm_frames(ma_panner* pPanner, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API void ma_panner_set_mode(ma_panner* pPanner, ma_pan_mode mode); +MA_API ma_pan_mode ma_panner_get_mode(const ma_panner* pPanner); +MA_API void ma_panner_set_pan(ma_panner* pPanner, float pan); +MA_API float ma_panner_get_pan(const ma_panner* pPanner); + + + +/* Fader. */ +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; +} ma_fader_config; + +MA_API ma_fader_config ma_fader_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate); + +typedef struct +{ + ma_fader_config config; + float volumeBeg; /* If volumeBeg and volumeEnd is equal to 1, no fading happens (ma_fader_process_pcm_frames() will run as a passthrough). */ + float volumeEnd; + ma_uint64 lengthInFrames; /* The total length of the fade. */ + ma_uint64 cursorInFrames; /* The current time in frames. Incremented by ma_fader_process_pcm_frames(). */ +} ma_fader; + +MA_API ma_result ma_fader_init(const ma_fader_config* pConfig, ma_fader* pFader); +MA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate); +MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames); +MA_API float ma_fader_get_current_volume(ma_fader* pFader); + + + +/* Spatializer. */ +typedef struct +{ + float x; + float y; + float z; +} ma_vec3f; + +typedef enum +{ + ma_attenuation_model_none, /* No distance attenuation and no spatialization. */ + ma_attenuation_model_inverse, /* Equivalent to OpenAL's AL_INVERSE_DISTANCE_CLAMPED. */ + ma_attenuation_model_linear, /* Linear attenuation. Equivalent to OpenAL's AL_LINEAR_DISTANCE_CLAMPED. */ + ma_attenuation_model_exponential /* Exponential attenuation. Equivalent to OpenAL's AL_EXPONENT_DISTANCE_CLAMPED. */ +} ma_attenuation_model; + +typedef enum +{ + ma_positioning_absolute, + ma_positioning_relative +} ma_positioning; + +typedef enum +{ + ma_handedness_right, + ma_handedness_left +} ma_handedness; + + +typedef struct +{ + ma_uint32 channelsOut; + ma_channel* pChannelMapOut; + ma_handedness handedness; /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */ + float coneInnerAngleInRadians; + float coneOuterAngleInRadians; + float coneOuterGain; + float speedOfSound; + ma_vec3f worldUp; +} ma_spatializer_listener_config; + +MA_API ma_spatializer_listener_config ma_spatializer_listener_config_init(ma_uint32 channelsOut); + + +typedef struct +{ + ma_spatializer_listener_config config; + ma_vec3f position; /* The absolute position of the listener. */ + ma_vec3f direction; /* The direction the listener is facing. The world up vector is config.worldUp. */ + ma_vec3f velocity; + ma_bool32 isEnabled; + + /* Memory management. */ + ma_bool32 _ownsHeap; + void* _pHeap; +} ma_spatializer_listener; + +MA_API ma_result ma_spatializer_listener_get_heap_size(const ma_spatializer_listener_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_spatializer_listener_init_preallocated(const ma_spatializer_listener_config* pConfig, void* pHeap, ma_spatializer_listener* pListener); +MA_API ma_result ma_spatializer_listener_init(const ma_spatializer_listener_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer_listener* pListener); +MA_API void ma_spatializer_listener_uninit(ma_spatializer_listener* pListener, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_channel* ma_spatializer_listener_get_channel_map(ma_spatializer_listener* pListener); +MA_API void ma_spatializer_listener_set_cone(ma_spatializer_listener* pListener, float innerAngleInRadians, float outerAngleInRadians, float outerGain); +MA_API void ma_spatializer_listener_get_cone(const ma_spatializer_listener* pListener, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); +MA_API void ma_spatializer_listener_set_position(ma_spatializer_listener* pListener, float x, float y, float z); +MA_API ma_vec3f ma_spatializer_listener_get_position(const ma_spatializer_listener* pListener); +MA_API void ma_spatializer_listener_set_direction(ma_spatializer_listener* pListener, float x, float y, float z); +MA_API ma_vec3f ma_spatializer_listener_get_direction(const ma_spatializer_listener* pListener); +MA_API void ma_spatializer_listener_set_velocity(ma_spatializer_listener* pListener, float x, float y, float z); +MA_API ma_vec3f ma_spatializer_listener_get_velocity(const ma_spatializer_listener* pListener); +MA_API void ma_spatializer_listener_set_speed_of_sound(ma_spatializer_listener* pListener, float speedOfSound); +MA_API float ma_spatializer_listener_get_speed_of_sound(const ma_spatializer_listener* pListener); +MA_API void ma_spatializer_listener_set_world_up(ma_spatializer_listener* pListener, float x, float y, float z); +MA_API ma_vec3f ma_spatializer_listener_get_world_up(const ma_spatializer_listener* pListener); +MA_API void ma_spatializer_listener_set_enabled(ma_spatializer_listener* pListener, ma_bool32 isEnabled); +MA_API ma_bool32 ma_spatializer_listener_is_enabled(const ma_spatializer_listener* pListener); + + +typedef struct +{ + ma_uint32 channelsIn; + ma_uint32 channelsOut; + ma_channel* pChannelMapIn; + ma_attenuation_model attenuationModel; + ma_positioning positioning; + ma_handedness handedness; /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */ + float minGain; + float maxGain; + float minDistance; + float maxDistance; + float rolloff; + float coneInnerAngleInRadians; + float coneOuterAngleInRadians; + float coneOuterGain; + float dopplerFactor; /* Set to 0 to disable doppler effect. */ + float directionalAttenuationFactor; /* Set to 0 to disable directional attenuation. */ + ma_uint32 gainSmoothTimeInFrames; /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */ +} ma_spatializer_config; + +MA_API ma_spatializer_config ma_spatializer_config_init(ma_uint32 channelsIn, ma_uint32 channelsOut); + + +typedef struct +{ + ma_uint32 channelsIn; + ma_uint32 channelsOut; + ma_channel* pChannelMapIn; + ma_attenuation_model attenuationModel; + ma_positioning positioning; + ma_handedness handedness; /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */ + float minGain; + float maxGain; + float minDistance; + float maxDistance; + float rolloff; + float coneInnerAngleInRadians; + float coneOuterAngleInRadians; + float coneOuterGain; + float dopplerFactor; /* Set to 0 to disable doppler effect. */ + float directionalAttenuationFactor; /* Set to 0 to disable directional attenuation. */ + ma_uint32 gainSmoothTimeInFrames; /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */ + ma_vec3f position; + ma_vec3f direction; + ma_vec3f velocity; /* For doppler effect. */ + float dopplerPitch; /* Will be updated by ma_spatializer_process_pcm_frames() and can be used by higher level functions to apply a pitch shift for doppler effect. */ + ma_gainer gainer; /* For smooth gain transitions. */ + float* pNewChannelGainsOut; /* An offset of _pHeap. Used by ma_spatializer_process_pcm_frames() to store new channel gains. The number of elements in this array is equal to config.channelsOut. */ + + /* Memory management. */ + void* _pHeap; + ma_bool32 _ownsHeap; +} ma_spatializer; + +MA_API ma_result ma_spatializer_get_heap_size(const ma_spatializer_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_spatializer_init_preallocated(const ma_spatializer_config* pConfig, void* pHeap, ma_spatializer* pSpatializer); +MA_API ma_result ma_spatializer_init(const ma_spatializer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer* pSpatializer); +MA_API void ma_spatializer_uninit(ma_spatializer* pSpatializer, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, ma_spatializer_listener* pListener, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API ma_uint32 ma_spatializer_get_input_channels(const ma_spatializer* pSpatializer); +MA_API ma_uint32 ma_spatializer_get_output_channels(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_set_attenuation_model(ma_spatializer* pSpatializer, ma_attenuation_model attenuationModel); +MA_API ma_attenuation_model ma_spatializer_get_attenuation_model(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_set_positioning(ma_spatializer* pSpatializer, ma_positioning positioning); +MA_API ma_positioning ma_spatializer_get_positioning(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rolloff); +MA_API float ma_spatializer_get_rolloff(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minGain); +MA_API float ma_spatializer_get_min_gain(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxGain); +MA_API float ma_spatializer_get_max_gain(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float minDistance); +MA_API float ma_spatializer_get_min_distance(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float maxDistance); +MA_API float ma_spatializer_get_max_distance(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAngleInRadians, float outerAngleInRadians, float outerGain); +MA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); +MA_API void ma_spatializer_set_doppler_factor(ma_spatializer* pSpatializer, float dopplerFactor); +MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pSpatializer, float directionalAttenuationFactor); +MA_API float ma_spatializer_get_directional_attenuation_factor(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, float y, float z); +MA_API ma_vec3f ma_spatializer_get_position(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_set_direction(ma_spatializer* pSpatializer, float x, float y, float z); +MA_API ma_vec3f ma_spatializer_get_direction(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_set_velocity(ma_spatializer* pSpatializer, float x, float y, float z); +MA_API ma_vec3f ma_spatializer_get_velocity(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_get_relative_position_and_direction(const ma_spatializer* pSpatializer, const ma_spatializer_listener* pListener, ma_vec3f* pRelativePos, ma_vec3f* pRelativeDir); + + + /************************************************************************************************************************************************************ ************************************************************************************************************************************************************* @@ -2465,75 +5064,106 @@ typedef struct ma_uint32 inTimeFrac; union { - float f32[MA_MAX_CHANNELS]; - ma_int16 s16[MA_MAX_CHANNELS]; + float* f32; + ma_int16* s16; } x0; /* The previous input frame. */ union { - float f32[MA_MAX_CHANNELS]; - ma_int16 s16[MA_MAX_CHANNELS]; + float* f32; + ma_int16* s16; } x1; /* The next input frame. */ ma_lpf lpf; + + /* Memory management. */ + void* _pHeap; + ma_bool32 _ownsHeap; } ma_linear_resampler; -MA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, ma_linear_resampler* pResampler); -MA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler); +MA_API ma_result ma_linear_resampler_get_heap_size(const ma_linear_resampler_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_linear_resampler_init_preallocated(const ma_linear_resampler_config* pConfig, void* pHeap, ma_linear_resampler* pResampler); +MA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_linear_resampler* pResampler); +MA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_linear_resampler_process_pcm_frames(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); MA_API ma_result ma_linear_resampler_set_rate(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResampler, float ratioInOut); -MA_API ma_uint64 ma_linear_resampler_get_required_input_frame_count(const ma_linear_resampler* pResampler, ma_uint64 outputFrameCount); -MA_API ma_uint64 ma_linear_resampler_get_expected_output_frame_count(const ma_linear_resampler* pResampler, ma_uint64 inputFrameCount); MA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler* pResampler); MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resampler* pResampler); +MA_API ma_result ma_linear_resampler_get_required_input_frame_count(const ma_linear_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); +MA_API ma_result ma_linear_resampler_get_expected_output_frame_count(const ma_linear_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); +MA_API ma_result ma_linear_resampler_reset(ma_linear_resampler* pResampler); + + +typedef struct ma_resampler_config ma_resampler_config; + +typedef void ma_resampling_backend; +typedef struct +{ + ma_result (* onGetHeapSize )(void* pUserData, const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes); + ma_result (* onInit )(void* pUserData, const ma_resampler_config* pConfig, void* pHeap, ma_resampling_backend** ppBackend); + void (* onUninit )(void* pUserData, ma_resampling_backend* pBackend, const ma_allocation_callbacks* pAllocationCallbacks); + ma_result (* onProcess )(void* pUserData, ma_resampling_backend* pBackend, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); + ma_result (* onSetRate )(void* pUserData, ma_resampling_backend* pBackend, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); /* Optional. Rate changes will be disabled. */ + ma_uint64 (* onGetInputLatency )(void* pUserData, const ma_resampling_backend* pBackend); /* Optional. Latency will be reported as 0. */ + ma_uint64 (* onGetOutputLatency )(void* pUserData, const ma_resampling_backend* pBackend); /* Optional. Latency will be reported as 0. */ + ma_result (* onGetRequiredInputFrameCount )(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); /* Optional. Latency mitigation will be disabled. */ + ma_result (* onGetExpectedOutputFrameCount)(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); /* Optional. Latency mitigation will be disabled. */ + ma_result (* onReset )(void* pUserData, ma_resampling_backend* pBackend); +} ma_resampling_backend_vtable; typedef enum { - ma_resample_algorithm_linear = 0, /* Fastest, lowest quality. Optional low-pass filtering. Default. */ - ma_resample_algorithm_speex + ma_resample_algorithm_linear = 0, /* Fastest, lowest quality. Optional low-pass filtering. Default. */ + ma_resample_algorithm_custom, } ma_resample_algorithm; -typedef struct +struct ma_resampler_config { ma_format format; /* Must be either ma_format_f32 or ma_format_s16. */ ma_uint32 channels; ma_uint32 sampleRateIn; ma_uint32 sampleRateOut; - ma_resample_algorithm algorithm; + ma_resample_algorithm algorithm; /* When set to ma_resample_algorithm_custom, pBackendVTable will be used. */ + ma_resampling_backend_vtable* pBackendVTable; + void* pBackendUserData; struct { ma_uint32 lpfOrder; - double lpfNyquistFactor; } linear; - struct - { - int quality; /* 0 to 10. Defaults to 3. */ - } speex; -} ma_resampler_config; +}; MA_API ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_resample_algorithm algorithm); typedef struct { - ma_resampler_config config; + ma_resampling_backend* pBackend; + ma_resampling_backend_vtable* pBackendVTable; + void* pBackendUserData; + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRateIn; + ma_uint32 sampleRateOut; union { ma_linear_resampler linear; - struct - { - void* pSpeexResamplerState; /* SpeexResamplerState* */ - } speex; - } state; + } state; /* State for stock resamplers so we can avoid a malloc. For stock resamplers, pBackend will point here. */ + + /* Memory management. */ + void* _pHeap; + ma_bool32 _ownsHeap; } ma_resampler; +MA_API ma_result ma_resampler_get_heap_size(const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_resampler_init_preallocated(const ma_resampler_config* pConfig, void* pHeap, ma_resampler* pResampler); + /* Initializes a new resampler object from a config. */ -MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, ma_resampler* pResampler); +MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_resampler* pResampler); /* Uninitializes a resampler. */ -MA_API void ma_resampler_uninit(ma_resampler* pResampler); +MA_API void ma_resampler_uninit(ma_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks); /* Converts the given input data. @@ -2572,23 +5202,6 @@ The ration is in/out. */ MA_API ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, float ratio); - -/* -Calculates the number of whole input frames that would need to be read from the client in order to output the specified -number of output frames. - -The returned value does not include cached input frames. It only returns the number of extra frames that would need to be -read from the input buffer in order to output the specified number of output frames. -*/ -MA_API ma_uint64 ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount); - -/* -Calculates the number of whole output frames that would be output after fully reading and consuming the specified number of -input frames. -*/ -MA_API ma_uint64 ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount); - - /* Retrieves the latency introduced by the resampler in input frames. */ @@ -2599,6 +5212,25 @@ Retrieves the latency introduced by the resampler in output frames. */ MA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler); +/* +Calculates the number of whole input frames that would need to be read from the client in order to output the specified +number of output frames. + +The returned value does not include cached input frames. It only returns the number of extra frames that would need to be +read from the input buffer in order to output the specified number of output frames. +*/ +MA_API ma_result ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); + +/* +Calculates the number of whole output frames that would be output after fully reading and consuming the specified number of +input frames. +*/ +MA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); + +/* +Resets the resampler's timer and clears it's internal cache. +*/ +MA_API ma_result ma_resampler_reset(ma_resampler* pResampler); /************************************************************************************************************************************************************** @@ -2606,15 +5238,33 @@ MA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler) Channel Conversion **************************************************************************************************************************************************************/ +typedef enum +{ + ma_channel_conversion_path_unknown, + ma_channel_conversion_path_passthrough, + ma_channel_conversion_path_mono_out, /* Converting to mono. */ + ma_channel_conversion_path_mono_in, /* Converting from mono. */ + ma_channel_conversion_path_shuffle, /* Simple shuffle. Will use this when all channels are present in both input and output channel maps, but just in a different order. */ + ma_channel_conversion_path_weights /* Blended based on weights. */ +} ma_channel_conversion_path; + +typedef enum +{ + ma_mono_expansion_mode_duplicate = 0, /* The default. */ + ma_mono_expansion_mode_average, /* Average the mono channel across all channels. */ + ma_mono_expansion_mode_stereo_only, /* Duplicate to the left and right channels only and ignore the others. */ + ma_mono_expansion_mode_default = ma_mono_expansion_mode_duplicate +} ma_mono_expansion_mode; + typedef struct { ma_format format; ma_uint32 channelsIn; ma_uint32 channelsOut; - ma_channel channelMapIn[MA_MAX_CHANNELS]; - ma_channel channelMapOut[MA_MAX_CHANNELS]; + const ma_channel* pChannelMapIn; + const ma_channel* pChannelMapOut; ma_channel_mix_mode mixingMode; - float weights[MA_MAX_CHANNELS][MA_MAX_CHANNELS]; /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */ + float** ppWeights; /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */ } ma_channel_converter_config; MA_API ma_channel_converter_config ma_channel_converter_config_init(ma_format format, ma_uint32 channelsIn, const ma_channel* pChannelMapIn, ma_uint32 channelsOut, const ma_channel* pChannelMapOut, ma_channel_mix_mode mixingMode); @@ -2624,24 +5274,29 @@ typedef struct ma_format format; ma_uint32 channelsIn; ma_uint32 channelsOut; - ma_channel channelMapIn[MA_MAX_CHANNELS]; - ma_channel channelMapOut[MA_MAX_CHANNELS]; ma_channel_mix_mode mixingMode; + ma_channel_conversion_path conversionPath; + ma_channel* pChannelMapIn; + ma_channel* pChannelMapOut; + ma_uint8* pShuffleTable; /* Indexed by output channel index. */ union { - float f32[MA_MAX_CHANNELS][MA_MAX_CHANNELS]; - ma_int32 s16[MA_MAX_CHANNELS][MA_MAX_CHANNELS]; - } weights; - ma_bool8 isPassthrough; - ma_bool8 isSimpleShuffle; - ma_bool8 isSimpleMonoExpansion; - ma_bool8 isStereoToMono; - ma_uint8 shuffleTable[MA_MAX_CHANNELS]; + float** f32; + ma_int32** s16; + } weights; /* [in][out] */ + + /* Memory management. */ + void* _pHeap; + ma_bool32 _ownsHeap; } ma_channel_converter; -MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, ma_channel_converter* pConverter); -MA_API void ma_channel_converter_uninit(ma_channel_converter* pConverter); +MA_API ma_result ma_channel_converter_get_heap_size(const ma_channel_converter_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_converter_config* pConfig, void* pHeap, ma_channel_converter* pConverter); +MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_channel_converter* pConverter); +MA_API void ma_channel_converter_uninit(ma_channel_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API ma_result ma_channel_converter_get_input_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); +MA_API ma_result ma_channel_converter_get_output_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); /************************************************************************************************************************************************************** @@ -2657,33 +5312,39 @@ typedef struct ma_uint32 channelsOut; ma_uint32 sampleRateIn; ma_uint32 sampleRateOut; - ma_channel channelMapIn[MA_MAX_CHANNELS]; - ma_channel channelMapOut[MA_MAX_CHANNELS]; + ma_channel* pChannelMapIn; + ma_channel* pChannelMapOut; ma_dither_mode ditherMode; ma_channel_mix_mode channelMixMode; - float channelWeights[MA_MAX_CHANNELS][MA_MAX_CHANNELS]; /* [in][out]. Only used when channelMixMode is set to ma_channel_mix_mode_custom_weights. */ - struct - { - ma_resample_algorithm algorithm; - ma_bool32 allowDynamicSampleRate; - struct - { - ma_uint32 lpfOrder; - double lpfNyquistFactor; - } linear; - struct - { - int quality; - } speex; - } resampling; + float** ppChannelWeights; /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */ + ma_bool32 allowDynamicSampleRate; + ma_resampler_config resampling; } ma_data_converter_config; MA_API ma_data_converter_config ma_data_converter_config_init_default(void); MA_API ma_data_converter_config ma_data_converter_config_init(ma_format formatIn, ma_format formatOut, ma_uint32 channelsIn, ma_uint32 channelsOut, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); + +typedef enum +{ + ma_data_converter_execution_path_passthrough, /* No conversion. */ + ma_data_converter_execution_path_format_only, /* Only format conversion. */ + ma_data_converter_execution_path_channels_only, /* Only channel conversion. */ + ma_data_converter_execution_path_resample_only, /* Only resampling. */ + ma_data_converter_execution_path_resample_first, /* All conversions, but resample as the first step. */ + ma_data_converter_execution_path_channels_first /* All conversions, but channels as the first step. */ +} ma_data_converter_execution_path; + typedef struct { - ma_data_converter_config config; + ma_format formatIn; + ma_format formatOut; + ma_uint32 channelsIn; + ma_uint32 channelsOut; + ma_uint32 sampleRateIn; + ma_uint32 sampleRateOut; + ma_dither_mode ditherMode; + ma_data_converter_execution_path executionPath; /* The execution path the data converter will follow when processing. */ ma_channel_converter channelConverter; ma_resampler resampler; ma_bool8 hasPreFormatConversion; @@ -2691,17 +5352,26 @@ typedef struct ma_bool8 hasChannelConverter; ma_bool8 hasResampler; ma_bool8 isPassthrough; + + /* Memory management. */ + ma_bool8 _ownsHeap; + void* _pHeap; } ma_data_converter; -MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, ma_data_converter* pConverter); -MA_API void ma_data_converter_uninit(ma_data_converter* pConverter); +MA_API ma_result ma_data_converter_get_heap_size(const ma_data_converter_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_data_converter_init_preallocated(const ma_data_converter_config* pConfig, void* pHeap, ma_data_converter* pConverter); +MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_converter* pConverter); +MA_API void ma_data_converter_uninit(ma_data_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_data_converter_process_pcm_frames(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); MA_API ma_result ma_data_converter_set_rate(ma_data_converter* pConverter, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); MA_API ma_result ma_data_converter_set_rate_ratio(ma_data_converter* pConverter, float ratioInOut); -MA_API ma_uint64 ma_data_converter_get_required_input_frame_count(const ma_data_converter* pConverter, ma_uint64 outputFrameCount); -MA_API ma_uint64 ma_data_converter_get_expected_output_frame_count(const ma_data_converter* pConverter, ma_uint64 inputFrameCount); MA_API ma_uint64 ma_data_converter_get_input_latency(const ma_data_converter* pConverter); MA_API ma_uint64 ma_data_converter_get_output_latency(const ma_data_converter* pConverter); +MA_API ma_result ma_data_converter_get_required_input_frame_count(const ma_data_converter* pConverter, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); +MA_API ma_result ma_data_converter_get_expected_output_frame_count(const ma_data_converter* pConverter, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); +MA_API ma_result ma_data_converter_get_input_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); +MA_API ma_result ma_data_converter_get_output_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); +MA_API ma_result ma_data_converter_reset(ma_data_converter* pConverter); /************************************************************************************************************************************************************ @@ -2753,9 +5423,6 @@ This is used in the shuffle table to indicate that the channel index is undefine */ #define MA_CHANNEL_INDEX_NULL 255 -/* Retrieves the channel position of the specified channel based on miniaudio's default channel map. */ -MA_API ma_channel ma_channel_map_get_default_channel(ma_uint32 channelCount, ma_uint32 channelIndex); - /* Retrieves the channel position of the specified channel in the given channel map. @@ -2768,14 +5435,14 @@ Initializes a blank channel map. When a blank channel map is specified anywhere it indicates that the native channel map should be used. */ -MA_API void ma_channel_map_init_blank(ma_uint32 channels, ma_channel* pChannelMap); +MA_API void ma_channel_map_init_blank(ma_channel* pChannelMap, ma_uint32 channels); /* Helper for retrieving a standard channel map. -The output channel map buffer must have a capacity of at least `channels`. +The output channel map buffer must have a capacity of at least `channelMapCap`. */ -MA_API void ma_get_standard_channel_map(ma_standard_channel_map standardChannelMap, ma_uint32 channels, ma_channel* pChannelMap); +MA_API void ma_channel_map_init_standard(ma_standard_channel_map standardChannelMap, ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channels); /* Copies a channel map. @@ -2789,7 +5456,7 @@ Copies a channel map if one is specified, otherwise copies the default channel m The output buffer must have a capacity of at least `channels`. If not NULL, the input channel map must also have a capacity of at least `channels`. */ -MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels); +MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, size_t channelMapCapOut, const ma_channel* pIn, ma_uint32 channels); /* @@ -2804,7 +5471,7 @@ Invalid channel maps: The channel map buffer must have a capacity of at least `channels`. */ -MA_API ma_bool32 ma_channel_map_valid(ma_uint32 channels, const ma_channel* pChannelMap); +MA_API ma_bool32 ma_channel_map_is_valid(const ma_channel* pChannelMap, ma_uint32 channels); /* Helper for comparing two channel maps for equality. @@ -2813,14 +5480,14 @@ This assumes the channel count is the same between the two. Both channels map buffers must have a capacity of at least `channels`. */ -MA_API ma_bool32 ma_channel_map_equal(ma_uint32 channels, const ma_channel* pChannelMapA, const ma_channel* pChannelMapB); +MA_API ma_bool32 ma_channel_map_is_equal(const ma_channel* pChannelMapA, const ma_channel* pChannelMapB, ma_uint32 channels); /* Helper for determining if a channel map is blank (all channels set to MA_CHANNEL_NONE). The channel map buffer must have a capacity of at least `channels`. */ -MA_API ma_bool32 ma_channel_map_blank(ma_uint32 channels, const ma_channel* pChannelMap); +MA_API ma_bool32 ma_channel_map_is_blank(const ma_channel* pChannelMap, ma_uint32 channels); /* Helper for determining whether or not a channel is present in the given channel map. @@ -2860,10 +5527,10 @@ typedef struct ma_uint32 subbufferSizeInBytes; ma_uint32 subbufferCount; ma_uint32 subbufferStrideInBytes; - MA_ATOMIC ma_uint32 encodedReadOffset; /* Most significant bit is the loop flag. Lower 31 bits contains the actual offset in bytes. Must be used atomically. */ - MA_ATOMIC ma_uint32 encodedWriteOffset; /* Most significant bit is the loop flag. Lower 31 bits contains the actual offset in bytes. Must be used atomically. */ - ma_bool8 ownsBuffer; /* Used to know whether or not miniaudio is responsible for free()-ing the buffer. */ - ma_bool8 clearOnWriteAcquire; /* When set, clears the acquired write buffer before returning from ma_rb_acquire_write(). */ + MA_ATOMIC(4, ma_uint32) encodedReadOffset; /* Most significant bit is the loop flag. Lower 31 bits contains the actual offset in bytes. Must be used atomically. */ + MA_ATOMIC(4, ma_uint32) encodedWriteOffset; /* Most significant bit is the loop flag. Lower 31 bits contains the actual offset in bytes. Must be used atomically. */ + ma_bool8 ownsBuffer; /* Used to know whether or not miniaudio is responsible for free()-ing the buffer. */ + ma_bool8 clearOnWriteAcquire; /* When set, clears the acquired write buffer before returning from ma_rb_acquire_write(). */ ma_allocation_callbacks allocationCallbacks; } ma_rb; @@ -2872,9 +5539,9 @@ MA_API ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocate MA_API void ma_rb_uninit(ma_rb* pRB); MA_API void ma_rb_reset(ma_rb* pRB); MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut); -MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes, void* pBufferOut); +MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes); MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut); -MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes, void* pBufferOut); +MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes); MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes); MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes); MA_API ma_int32 ma_rb_pointer_distance(ma_rb* pRB); /* Returns the distance between the write pointer and the read pointer. Should never be negative for a correct program. Will return the number of bytes that can be read before the read pointer hits the write pointer. */ @@ -2898,9 +5565,9 @@ MA_API ma_result ma_pcm_rb_init(ma_format format, ma_uint32 channels, ma_uint32 MA_API void ma_pcm_rb_uninit(ma_pcm_rb* pRB); MA_API void ma_pcm_rb_reset(ma_pcm_rb* pRB); MA_API ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut); -MA_API ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames, void* pBufferOut); +MA_API ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames); MA_API ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut); -MA_API ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames, void* pBufferOut); +MA_API ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames); MA_API ma_result ma_pcm_rb_seek_read(ma_pcm_rb* pRB, ma_uint32 offsetInFrames); MA_API ma_result ma_pcm_rb_seek_write(ma_pcm_rb* pRB, ma_uint32 offsetInFrames); MA_API ma_int32 ma_pcm_rb_pointer_distance(ma_pcm_rb* pRB); /* Return value is in frames. */ @@ -2942,17 +5609,22 @@ Retrieves a human readable description of the given result code. MA_API const char* ma_result_description(ma_result result); /* -malloc(). Calls MA_MALLOC(). +malloc() */ MA_API void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); /* -realloc(). Calls MA_REALLOC(). +calloc() +*/ +MA_API void* ma_calloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); + +/* +realloc() */ MA_API void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); /* -free(). Calls MA_FREE(). +free() */ MA_API void ma_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); @@ -2994,6 +5666,413 @@ MA_API const char* ma_log_level_to_string(ma_uint32 logLevel); + +/************************************************************************************************************************************************************ + +Synchronization + +************************************************************************************************************************************************************/ +/* +Locks a spinlock. +*/ +MA_API ma_result ma_spinlock_lock(volatile ma_spinlock* pSpinlock); + +/* +Locks a spinlock, but does not yield() when looping. +*/ +MA_API ma_result ma_spinlock_lock_noyield(volatile ma_spinlock* pSpinlock); + +/* +Unlocks a spinlock. +*/ +MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock); + + +#ifndef MA_NO_THREADING + +/* +Creates a mutex. + +A mutex must be created from a valid context. A mutex is initially unlocked. +*/ +MA_API ma_result ma_mutex_init(ma_mutex* pMutex); + +/* +Deletes a mutex. +*/ +MA_API void ma_mutex_uninit(ma_mutex* pMutex); + +/* +Locks a mutex with an infinite timeout. +*/ +MA_API void ma_mutex_lock(ma_mutex* pMutex); + +/* +Unlocks a mutex. +*/ +MA_API void ma_mutex_unlock(ma_mutex* pMutex); + + +/* +Initializes an auto-reset event. +*/ +MA_API ma_result ma_event_init(ma_event* pEvent); + +/* +Uninitializes an auto-reset event. +*/ +MA_API void ma_event_uninit(ma_event* pEvent); + +/* +Waits for the specified auto-reset event to become signalled. +*/ +MA_API ma_result ma_event_wait(ma_event* pEvent); + +/* +Signals the specified auto-reset event. +*/ +MA_API ma_result ma_event_signal(ma_event* pEvent); +#endif /* MA_NO_THREADING */ + + +/* +Fence +===== +This locks while the counter is larger than 0. Counter can be incremented and decremented by any +thread, but care needs to be taken when waiting. It is possible for one thread to acquire the +fence just as another thread returns from ma_fence_wait(). + +The idea behind a fence is to allow you to wait for a group of operations to complete. When an +operation starts, the counter is incremented which locks the fence. When the operation completes, +the fence will be released which decrements the counter. ma_fence_wait() will block until the +counter hits zero. + +If threading is disabled, ma_fence_wait() will spin on the counter. +*/ +typedef struct +{ +#ifndef MA_NO_THREADING + ma_event e; +#endif + ma_uint32 counter; +} ma_fence; + +MA_API ma_result ma_fence_init(ma_fence* pFence); +MA_API void ma_fence_uninit(ma_fence* pFence); +MA_API ma_result ma_fence_acquire(ma_fence* pFence); /* Increment counter. */ +MA_API ma_result ma_fence_release(ma_fence* pFence); /* Decrement counter. */ +MA_API ma_result ma_fence_wait(ma_fence* pFence); /* Wait for counter to reach 0. */ + + + +/* +Notification callback for asynchronous operations. +*/ +typedef void ma_async_notification; + +typedef struct +{ + void (* onSignal)(ma_async_notification* pNotification); +} ma_async_notification_callbacks; + +MA_API ma_result ma_async_notification_signal(ma_async_notification* pNotification); + + +/* +Simple polling notification. + +This just sets a variable when the notification has been signalled which is then polled with ma_async_notification_poll_is_signalled() +*/ +typedef struct +{ + ma_async_notification_callbacks cb; + ma_bool32 signalled; +} ma_async_notification_poll; + +MA_API ma_result ma_async_notification_poll_init(ma_async_notification_poll* pNotificationPoll); +MA_API ma_bool32 ma_async_notification_poll_is_signalled(const ma_async_notification_poll* pNotificationPoll); + + +/* +Event Notification + +This uses an ma_event. If threading is disabled (MA_NO_THREADING), initialization will fail. +*/ +typedef struct +{ + ma_async_notification_callbacks cb; +#ifndef MA_NO_THREADING + ma_event e; +#endif +} ma_async_notification_event; + +MA_API ma_result ma_async_notification_event_init(ma_async_notification_event* pNotificationEvent); +MA_API ma_result ma_async_notification_event_uninit(ma_async_notification_event* pNotificationEvent); +MA_API ma_result ma_async_notification_event_wait(ma_async_notification_event* pNotificationEvent); +MA_API ma_result ma_async_notification_event_signal(ma_async_notification_event* pNotificationEvent); + + + + +/************************************************************************************************************************************************************ + +Job Queue + +************************************************************************************************************************************************************/ + +/* +Slot Allocator +-------------- +The idea of the slot allocator is for it to be used in conjunction with a fixed sized buffer. You use the slot allocator to allocator an index that can be used +as the insertion point for an object. + +Slots are reference counted to help mitigate the ABA problem in the lock-free queue we use for tracking jobs. + +The slot index is stored in the low 32 bits. The reference counter is stored in the high 32 bits: + + +-----------------+-----------------+ + | 32 Bits | 32 Bits | + +-----------------+-----------------+ + | Reference Count | Slot Index | + +-----------------+-----------------+ +*/ +typedef struct +{ + ma_uint32 capacity; /* The number of slots to make available. */ +} ma_slot_allocator_config; + +MA_API ma_slot_allocator_config ma_slot_allocator_config_init(ma_uint32 capacity); + + +typedef struct +{ + MA_ATOMIC(4, ma_uint32) bitfield; /* Must be used atomically because the allocation and freeing routines need to make copies of this which must never be optimized away by the compiler. */ +} ma_slot_allocator_group; + +typedef struct +{ + ma_slot_allocator_group* pGroups; /* Slots are grouped in chunks of 32. */ + ma_uint32* pSlots; /* 32 bits for reference counting for ABA mitigation. */ + ma_uint32 count; /* Allocation count. */ + ma_uint32 capacity; + + /* Memory management. */ + ma_bool32 _ownsHeap; + void* _pHeap; +} ma_slot_allocator; + +MA_API ma_result ma_slot_allocator_get_heap_size(const ma_slot_allocator_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_slot_allocator_init_preallocated(const ma_slot_allocator_config* pConfig, void* pHeap, ma_slot_allocator* pAllocator); +MA_API ma_result ma_slot_allocator_init(const ma_slot_allocator_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_slot_allocator* pAllocator); +MA_API void ma_slot_allocator_uninit(ma_slot_allocator* pAllocator, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_slot_allocator_alloc(ma_slot_allocator* pAllocator, ma_uint64* pSlot); +MA_API ma_result ma_slot_allocator_free(ma_slot_allocator* pAllocator, ma_uint64 slot); + + +typedef struct ma_job ma_job; + +/* +Callback for processing a job. Each job type will have their own processing callback which will be +called by ma_job_process(). +*/ +typedef ma_result (* ma_job_proc)(ma_job* pJob); + +/* When a job type is added here an callback needs to be added go "g_jobVTable" in the implementation section. */ +typedef enum +{ + /* Miscellaneous. */ + MA_JOB_TYPE_QUIT = 0, + MA_JOB_TYPE_CUSTOM, + + /* Resource Manager. */ + MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE, + MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE, + MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE, + MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER, + MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER, + MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM, + MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM, + MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM, + MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM, + + /* Device. */ + MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE, + + /* Count. Must always be last. */ + MA_JOB_TYPE_COUNT +} ma_job_type; + +struct ma_job +{ + union + { + struct + { + ma_uint16 code; /* Job type. */ + ma_uint16 slot; /* Index into a ma_slot_allocator. */ + ma_uint32 refcount; + } breakup; + ma_uint64 allocation; + } toc; /* 8 bytes. We encode the job code into the slot allocation data to save space. */ + MA_ATOMIC(8, ma_uint64) next; /* refcount + slot for the next item. Does not include the job code. */ + ma_uint32 order; /* Execution order. Used to create a data dependency and ensure a job is executed in order. Usage is contextual depending on the job type. */ + + union + { + /* Miscellaneous. */ + struct + { + ma_job_proc proc; + ma_uintptr data0; + ma_uintptr data1; + } custom; + + /* Resource Manager */ + union + { + struct + { + /*ma_resource_manager**/ void* pResourceManager; + /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode; + char* pFilePath; + wchar_t* pFilePathW; + ma_uint32 flags; /* Resource manager data source flags that were used when initializing the data buffer. */ + ma_async_notification* pInitNotification; /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */ + ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. Will be passed through to MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE when decoding. */ + ma_fence* pInitFence; /* Released when initialization of the decoder is complete. */ + ma_fence* pDoneFence; /* Released if initialization of the decoder fails. Passed through to PAGE_DATA_BUFFER_NODE untouched if init is successful. */ + } loadDataBufferNode; + struct + { + /*ma_resource_manager**/ void* pResourceManager; + /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode; + ma_async_notification* pDoneNotification; + ma_fence* pDoneFence; + } freeDataBufferNode; + struct + { + /*ma_resource_manager**/ void* pResourceManager; + /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode; + /*ma_decoder**/ void* pDecoder; + ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. */ + ma_fence* pDoneFence; /* Passed through from LOAD_DATA_BUFFER_NODE and released when the data buffer completes decoding or an error occurs. */ + } pageDataBufferNode; + + struct + { + /*ma_resource_manager_data_buffer**/ void* pDataBuffer; + ma_async_notification* pInitNotification; /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */ + ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. */ + ma_fence* pInitFence; /* Released when the data buffer has been initialized and the format/channels/rate can be retrieved. */ + ma_fence* pDoneFence; /* Released when the data buffer has been fully decoded. */ + ma_uint64 rangeBegInPCMFrames; + ma_uint64 rangeEndInPCMFrames; + ma_uint64 loopPointBegInPCMFrames; + ma_uint64 loopPointEndInPCMFrames; + ma_uint32 isLooping; + } loadDataBuffer; + struct + { + /*ma_resource_manager_data_buffer**/ void* pDataBuffer; + ma_async_notification* pDoneNotification; + ma_fence* pDoneFence; + } freeDataBuffer; + + struct + { + /*ma_resource_manager_data_stream**/ void* pDataStream; + char* pFilePath; /* Allocated when the job is posted, freed by the job thread after loading. */ + wchar_t* pFilePathW; /* ^ As above ^. Only used if pFilePath is NULL. */ + ma_uint64 initialSeekPoint; + ma_async_notification* pInitNotification; /* Signalled after the first two pages have been decoded and frames can be read from the stream. */ + ma_fence* pInitFence; + } loadDataStream; + struct + { + /*ma_resource_manager_data_stream**/ void* pDataStream; + ma_async_notification* pDoneNotification; + ma_fence* pDoneFence; + } freeDataStream; + struct + { + /*ma_resource_manager_data_stream**/ void* pDataStream; + ma_uint32 pageIndex; /* The index of the page to decode into. */ + } pageDataStream; + struct + { + /*ma_resource_manager_data_stream**/ void* pDataStream; + ma_uint64 frameIndex; + } seekDataStream; + } resourceManager; + + /* Device. */ + union + { + union + { + struct + { + /*ma_device**/ void* pDevice; + /*ma_device_type*/ ma_uint32 deviceType; + } reroute; + } aaudio; + } device; + } data; +}; + +MA_API ma_job ma_job_init(ma_uint16 code); +MA_API ma_result ma_job_process(ma_job* pJob); + + +/* +When set, ma_job_queue_next() will not wait and no semaphore will be signaled in +ma_job_queue_post(). ma_job_queue_next() will return MA_NO_DATA_AVAILABLE if nothing is available. + +This flag should always be used for platforms that do not support multithreading. +*/ +typedef enum +{ + MA_JOB_QUEUE_FLAG_NON_BLOCKING = 0x00000001 +} ma_job_queue_flags; + +typedef struct +{ + ma_uint32 flags; + ma_uint32 capacity; /* The maximum number of jobs that can fit in the queue at a time. */ +} ma_job_queue_config; + +MA_API ma_job_queue_config ma_job_queue_config_init(ma_uint32 flags, ma_uint32 capacity); + + +typedef struct +{ + ma_uint32 flags; /* Flags passed in at initialization time. */ + ma_uint32 capacity; /* The maximum number of jobs that can fit in the queue at a time. Set by the config. */ + MA_ATOMIC(8, ma_uint64) head; /* The first item in the list. Required for removing from the top of the list. */ + MA_ATOMIC(8, ma_uint64) tail; /* The last item in the list. Required for appending to the end of the list. */ +#ifndef MA_NO_THREADING + ma_semaphore sem; /* Only used when MA_JOB_QUEUE_FLAG_NON_BLOCKING is unset. */ +#endif + ma_slot_allocator allocator; + ma_job* pJobs; +#ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE + ma_spinlock lock; +#endif + + /* Memory management. */ + void* _pHeap; + ma_bool32 _ownsHeap; +} ma_job_queue; + +MA_API ma_result ma_job_queue_get_heap_size(const ma_job_queue_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_job_queue_init_preallocated(const ma_job_queue_config* pConfig, void* pHeap, ma_job_queue* pQueue); +MA_API ma_result ma_job_queue_init(const ma_job_queue_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_job_queue* pQueue); +MA_API void ma_job_queue_uninit(ma_job_queue* pQueue, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_job_queue_post(ma_job_queue* pQueue, const ma_job* pJob); +MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob); /* Returns MA_CANCELLED if the next job is a quit job. */ + + + /************************************************************************************************************************************************************ ************************************************************************************************************************************************************* @@ -3100,11 +6179,14 @@ This section contains the APIs for device playback and capture. Here is where yo #define MA_HAS_NULL #endif -#define MA_STATE_UNINITIALIZED 0 -#define MA_STATE_STOPPED 1 /* The device's default state after initialization. */ -#define MA_STATE_STARTED 2 /* The device is started and is requesting and/or delivering audio data. */ -#define MA_STATE_STARTING 3 /* Transitioning from a stopped state to started. */ -#define MA_STATE_STOPPING 4 /* Transitioning from a started state to stopped. */ +typedef enum +{ + ma_device_state_uninitialized = 0, + ma_device_state_stopped = 1, /* The device's default state after initialization. */ + ma_device_state_started = 2, /* The device is started and is requesting and/or delivering audio data. */ + ma_device_state_starting = 3, /* Transitioning from a stopped state to started. */ + ma_device_state_stopping = 4 /* Transitioning from a started state to stopped. */ +} ma_device_state; #ifdef MA_SUPPORT_WASAPI /* We need a IMMNotificationClient object for WASAPI. */ @@ -3139,6 +6221,114 @@ typedef enum #define MA_BACKEND_COUNT (ma_backend_null+1) +/* +Device job thread. This is used by backends that require asynchronous processing of certain +operations. It is not used by all backends. + +The device job thread is made up of a thread and a job queue. You can post a job to the thread with +ma_device_job_thread_post(). The thread will do the processing of the job. +*/ +typedef struct +{ + ma_bool32 noThread; /* Set this to true if you want to process jobs yourself. */ + ma_uint32 jobQueueCapacity; + ma_uint32 jobQueueFlags; +} ma_device_job_thread_config; + +MA_API ma_device_job_thread_config ma_device_job_thread_config_init(void); + +typedef struct +{ + ma_thread thread; + ma_job_queue jobQueue; + ma_bool32 _hasThread; +} ma_device_job_thread; + +MA_API ma_result ma_device_job_thread_init(const ma_device_job_thread_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_device_job_thread* pJobThread); +MA_API void ma_device_job_thread_uninit(ma_device_job_thread* pJobThread, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_device_job_thread_post(ma_device_job_thread* pJobThread, const ma_job* pJob); +MA_API ma_result ma_device_job_thread_next(ma_device_job_thread* pJobThread, ma_job* pJob); + + + +/* Device notification types. */ +typedef enum +{ + ma_device_notification_type_started, + ma_device_notification_type_stopped, + ma_device_notification_type_rerouted, + ma_device_notification_type_interruption_began, + ma_device_notification_type_interruption_ended +} ma_device_notification_type; + +typedef struct +{ + ma_device* pDevice; + ma_device_notification_type type; + union + { + struct + { + int _unused; + } started; + struct + { + int _unused; + } stopped; + struct + { + int _unused; + } rerouted; + struct + { + int _unused; + } interruption; + } data; +} ma_device_notification; + +/* +The notification callback for when the application should be notified of a change to the device. + +This callback is used for notifying the application of changes such as when the device has started, +stopped, rerouted or an interruption has occurred. Note that not all backends will post all +notification types. For example, some backends will perform automatic stream routing without any +kind of notification to the host program which means miniaudio will never know about it and will +never be able to fire the rerouted notification. You should keep this in mind when designing your +program. + +The stopped notification will *not* get fired when a device is rerouted. + + +Parameters +---------- +pNotification (in) + A pointer to a structure containing information about the event. Use the `pDevice` member of + this object to retrieve the relevant device. The `type` member can be used to discriminate + against each of the notification types. + + +Remarks +------- +Do not restart or uninitialize the device from the callback. + +Not all notifications will be triggered by all backends, however the started and stopped events +should be reliable for all backends. Some backends do not have a good way to detect device +stoppages due to unplugging the device which may result in the stopped callback not getting +fired. This has been observed with at least one BSD variant. + +The rerouted notification is fired *after* the reroute has occurred. The stopped notification will +*not* get fired when a device is rerouted. The following backends are known to do automatic stream +rerouting, but do not have a way to be notified of the change: + + * DirectSound + +The interruption notifications are used on mobile platforms for detecting when audio is interrupted +due to things like an incoming phone call. Currently this is only implemented on iOS. None of the +Android backends will report this notification. +*/ +typedef void (* ma_device_notification_proc)(const ma_device_notification* pNotification); + + /* The callback for processing audio data from the device. @@ -3179,9 +6369,14 @@ callback. The following APIs cannot be called from inside the callback: The proper way to stop the device is to call `ma_device_stop()` from a different thread, normally the main application thread. */ -typedef void (* ma_device_callback_proc)(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); +typedef void (* ma_device_data_proc)(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); + + + /* +DEPRECATED. Use ma_device_notification_proc instead. + The callback for when the device has been stopped. This will be called when the device is stopped explicitly with `ma_device_stop()` and also called implicitly when the device is stopped through external forces @@ -3198,41 +6393,7 @@ Remarks ------- Do not restart or uninitialize the device from the callback. */ -typedef void (* ma_stop_proc)(ma_device* pDevice); - -/* -The callback for handling log messages. - - -Parameters ----------- -pContext (in) - A pointer to the context the log message originated from. - -pDevice (in) - A pointer to the device the log message originate from, if any. This can be null, in which case the message came from the context. - -logLevel (in) - The log level. This can be one of the following: - - +----------------------+ - | Log Level | - +----------------------+ - | MA_LOG_LEVEL_DEBUG | - | MA_LOG_LEVEL_INFO | - | MA_LOG_LEVEL_WARNING | - | MA_LOG_LEVEL_ERROR | - +----------------------+ - -message (in) - The log message. - - -Remarks -------- -Do not modify the state of the device from inside the callback. -*/ -typedef void (* ma_log_proc)(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message); +typedef void (* ma_stop_proc)(ma_device* pDevice); /* DEPRECATED. Use ma_device_notification_proc instead. */ typedef enum { @@ -3378,30 +6539,17 @@ typedef struct ma_backend_callbacks ma_backend_callbacks; #define MA_DATA_FORMAT_FLAG_EXCLUSIVE_MODE (1U << 1) /* If set, this is supported in exclusive mode. Otherwise not natively supported by exclusive mode. */ +#ifndef MA_MAX_DEVICE_NAME_LENGTH +#define MA_MAX_DEVICE_NAME_LENGTH 255 +#endif + typedef struct { /* Basic info. This is the only information guaranteed to be filled in during device enumeration. */ ma_device_id id; - char name[256]; + char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* +1 for null terminator. */ ma_bool32 isDefault; - /* - Detailed info. As much of this is filled as possible with ma_context_get_device_info(). Note that you are allowed to initialize - a device with settings outside of this range, but it just means the data will be converted using miniaudio's data conversion - pipeline before sending the data to/from the device. Most programs will need to not worry about these values, but it's provided - here mainly for informational purposes or in the rare case that someone might find it useful. - - These will be set to 0 when returned by ma_context_enumerate_devices() or ma_context_get_devices(). - */ - ma_uint32 formatCount; - ma_format formats[ma_format_count]; - ma_uint32 minChannels; - ma_uint32 maxChannels; - ma_uint32 minSampleRate; - ma_uint32 maxSampleRate; - - - /* Experimental. Don't use these right now. */ ma_uint32 nativeDataFormatCount; struct { @@ -3420,29 +6568,21 @@ struct ma_device_config ma_uint32 periodSizeInMilliseconds; ma_uint32 periods; ma_performance_profile performanceProfile; - ma_bool8 noPreZeroedOutputBuffer; /* When set to true, the contents of the output buffer passed into the data callback will be left undefined rather than initialized to zero. */ + ma_bool8 noPreSilencedOutputBuffer; /* When set to true, the contents of the output buffer passed into the data callback will be left undefined rather than initialized to silence. */ ma_bool8 noClip; /* When set to true, the contents of the output buffer passed into the data callback will be clipped after returning. Only applies when the playback sample format is f32. */ - ma_device_callback_proc dataCallback; + ma_bool8 noDisableDenormals; /* Do not disable denormals when firing the data callback. */ + ma_bool8 noFixedSizedCallback; /* Disables strict fixed-sized data callbacks. Setting this to true will result in the period size being treated only as a hint to the backend. This is an optimization for those who don't need fixed sized callbacks. */ + ma_device_data_proc dataCallback; + ma_device_notification_proc notificationCallback; ma_stop_proc stopCallback; void* pUserData; - struct - { - ma_resample_algorithm algorithm; - struct - { - ma_uint32 lpfOrder; - } linear; - struct - { - int quality; - } speex; - } resampling; + ma_resampler_config resampling; struct { const ma_device_id* pDeviceID; ma_format format; ma_uint32 channels; - ma_channel channelMap[MA_MAX_CHANNELS]; + ma_channel* pChannelMap; ma_channel_mix_mode channelMixMode; ma_share_mode shareMode; } playback; @@ -3451,7 +6591,7 @@ struct ma_device_config const ma_device_id* pDeviceID; ma_format format; ma_uint32 channels; - ma_channel channelMap[MA_MAX_CHANNELS]; + ma_channel* pChannelMap; ma_channel_mix_mode channelMixMode; ma_share_mode shareMode; } capture; @@ -3489,6 +6629,7 @@ struct ma_device_config ma_aaudio_usage usage; ma_aaudio_content_type contentType; ma_aaudio_input_preset inputPreset; + ma_bool32 noAutoStartAfterReroute; } aaudio; }; @@ -3554,7 +6695,7 @@ callbacks defined in this structure. Once the context has been initialized you can initialize a device. Before doing so, however, the application may want to know which physical devices are available. This is where `onContextEnumerateDevices()` comes in. This is fairly simple. For each device, fire the given callback with, at a minimum, the basic information filled out in `ma_device_info`. When the callback returns `MA_FALSE`, enumeration -needs to stop and the `onContextEnumerateDevices()` function return with a success code. +needs to stop and the `onContextEnumerateDevices()` function returns with a success code. Detailed device information can be retrieved from a device ID using `onContextGetDeviceInfo()`. This takes as input the device type and ID, and on output returns detailed information about the device in `ma_device_info`. The `onContextGetDeviceInfo()` callback must handle the @@ -3569,7 +6710,7 @@ internally by miniaudio. On input, if the sample format is set to `ma_format_unknown`, the backend is free to use whatever sample format it desires, so long as it's supported by miniaudio. When the channel count is set to 0, the backend should use the device's native channel count. The same applies for -sample rate. For the channel map, the default should be used when `ma_channel_map_blank()` returns true (all channels set to +sample rate. For the channel map, the default should be used when `ma_channel_map_is_blank()` returns true (all channels set to `MA_CHANNEL_NONE`). On input, the `periodSizeInFrames` or `periodSizeInMilliseconds` option should always be set. The backend should inspect both of these variables. If `periodSizeInFrames` is set, it should take priority, otherwise it needs to be derived from the period size in milliseconds (`periodSizeInMilliseconds`) and the sample rate, keeping in mind that the sample rate may be 0, in which case the @@ -3588,14 +6729,17 @@ This allows miniaudio to then process any necessary data conversion and then pas If the backend requires absolute flexibility with it's data delivery, it can optionally implement the `onDeviceDataLoop()` callback which will allow it to implement the logic that will run on the audio thread. This is much more advanced and is completely optional. -The audio thread should run data delivery logic in a loop while `ma_device_get_state() == MA_STATE_STARTED` and no errors have been +The audio thread should run data delivery logic in a loop while `ma_device_get_state() == ma_device_state_started` and no errors have been encounted. Do not start or stop the device here. That will be handled from outside the `onDeviceDataLoop()` callback. The invocation of the `onDeviceDataLoop()` callback will be handled by miniaudio. When you start the device, miniaudio will fire this -callback. When the device is stopped, the `ma_device_get_state() == MA_STATE_STARTED` condition will fail and the loop will be terminated +callback. When the device is stopped, the `ma_device_get_state() == ma_device_state_started` condition will fail and the loop will be terminated which will then fall through to the part that stops the device. For an example on how to implement the `onDeviceDataLoop()` callback, look at `ma_device_audio_thread__default_read_write()`. Implement the `onDeviceDataLoopWakeup()` callback if you need a mechanism to wake up the audio thread. + +If the backend supports an optimized retrieval of device information from an initialized `ma_device` object, it should implement the +`onDeviceGetInfo()` callback. This is optional, in which case it will fall back to `onContextGetDeviceInfo()` which is less efficient. */ struct ma_backend_callbacks { @@ -3611,11 +6755,11 @@ struct ma_backend_callbacks ma_result (* onDeviceWrite)(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten); ma_result (* onDeviceDataLoop)(ma_device* pDevice); ma_result (* onDeviceDataLoopWakeup)(ma_device* pDevice); + ma_result (* onDeviceGetInfo)(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo); }; struct ma_context_config { - ma_log_proc logCallback; /* Legacy logging callback. Will be removed in version 0.11. */ ma_log* pLog; ma_thread_priority threadPriority; size_t threadStackSize; @@ -3678,7 +6822,6 @@ struct ma_context ma_backend backend; /* DirectSound, ALSA, etc. */ ma_log* pLog; ma_log log; /* Only used if the log is owned by the context. The pLog member will be set to &log in this case. */ - ma_log_proc logCallback; /* Legacy callback. Will be removed in version 0.11. */ ma_thread_priority threadPriority; size_t threadStackSize; void* pUserData; @@ -3863,6 +7006,7 @@ struct ma_context ma_proc pa_stream_set_write_callback; ma_proc pa_stream_set_read_callback; ma_proc pa_stream_set_suspended_callback; + ma_proc pa_stream_set_moved_callback; ma_proc pa_stream_is_suspended; ma_proc pa_stream_flush; ma_proc pa_stream_drain; @@ -3878,6 +7022,8 @@ struct ma_context /*pa_mainloop**/ ma_ptr pMainLoop; /*pa_context**/ ma_ptr pPulseContext; + char* pApplicationName; /* Set when the context is initialized. Used by devices for their local pa_context objects. */ + char* pServerName; /* Set when the context is initialized. Used by devices for their local pa_context objects. */ } pulse; #endif #ifdef MA_SUPPORT_JACK @@ -4004,6 +7150,7 @@ struct ma_context ma_proc AAudioStream_getFramesPerBurst; ma_proc AAudioStream_requestStart; ma_proc AAudioStream_requestStop; + ma_device_job_thread jobThread; /* For processing operations outside of the error callback, specifically device disconnections and rerouting. */ } aaudio; #endif #ifdef MA_SUPPORT_OPENSL @@ -4087,37 +7234,39 @@ struct ma_device ma_context* pContext; ma_device_type type; ma_uint32 sampleRate; - MA_ATOMIC ma_uint32 state; /* The state of the device is variable and can change at any time on any thread. Must be used atomically. */ - ma_device_callback_proc onData; /* Set once at initialization time and should not be changed after. */ - ma_stop_proc onStop; /* Set once at initialization time and should not be changed after. */ - void* pUserData; /* Application defined data. */ + MA_ATOMIC(4, ma_device_state) state; /* The state of the device is variable and can change at any time on any thread. Must be used atomically. */ + ma_device_data_proc onData; /* Set once at initialization time and should not be changed after. */ + ma_device_notification_proc onNotification; /* Set once at initialization time and should not be changed after. */ + ma_stop_proc onStop; /* DEPRECATED. Use the notification callback instead. Set once at initialization time and should not be changed after. */ + void* pUserData; /* Application defined data. */ ma_mutex startStopLock; ma_event wakeupEvent; ma_event startEvent; ma_event stopEvent; ma_thread thread; - ma_result workResult; /* This is set by the worker thread after it's finished doing a job. */ - ma_bool8 isOwnerOfContext; /* When set to true, uninitializing the device will also uninitialize the context. Set to true when NULL is passed into ma_device_init(). */ - ma_bool8 noPreZeroedOutputBuffer; + ma_result workResult; /* This is set by the worker thread after it's finished doing a job. */ + ma_bool8 isOwnerOfContext; /* When set to true, uninitializing the device will also uninitialize the context. Set to true when NULL is passed into ma_device_init(). */ + ma_bool8 noPreSilencedOutputBuffer; ma_bool8 noClip; - MA_ATOMIC float masterVolumeFactor; /* Linear 0..1. Can be read and written simultaneously by different threads. Must be used atomically. */ - ma_duplex_rb duplexRB; /* Intermediary buffer for duplex device on asynchronous backends. */ + ma_bool8 noDisableDenormals; + ma_bool8 noFixedSizedCallback; + MA_ATOMIC(4, float) masterVolumeFactor; /* Linear 0..1. Can be read and written simultaneously by different threads. Must be used atomically. */ + ma_duplex_rb duplexRB; /* Intermediary buffer for duplex device on asynchronous backends. */ struct { ma_resample_algorithm algorithm; + ma_resampling_backend_vtable* pBackendVTable; + void* pBackendUserData; struct { ma_uint32 lpfOrder; } linear; - struct - { - int quality; - } speex; } resampling; struct { + ma_device_id* pID; /* Set to NULL if using default ID, otherwise set to the address of "id". */ ma_device_id id; /* If using an explicit device, will be set to a copy of the ID used for initialization. Otherwise cleared to 0. */ - char name[256]; /* Maybe temporary. Likely to be replaced with a query API. */ + char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* Maybe temporary. Likely to be replaced with a query API. */ ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */ ma_format format; ma_uint32 channels; @@ -4130,11 +7279,19 @@ struct ma_device ma_uint32 internalPeriods; ma_channel_mix_mode channelMixMode; ma_data_converter converter; + void* pIntermediaryBuffer; /* For implementing fixed sized buffer callbacks. Will be null if using variable sized callbacks. */ + ma_uint32 intermediaryBufferCap; + ma_uint32 intermediaryBufferLen; /* How many valid frames are sitting in the intermediary buffer. */ + void* pInputCache; /* In external format. Can be null. */ + ma_uint64 inputCacheCap; + ma_uint64 inputCacheConsumed; + ma_uint64 inputCacheRemaining; } playback; struct { + ma_device_id* pID; /* Set to NULL if using default ID, otherwise set to the address of "id". */ ma_device_id id; /* If using an explicit device, will be set to a copy of the ID used for initialization. Otherwise cleared to 0. */ - char name[256]; /* Maybe temporary. Likely to be replaced with a query API. */ + char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* Maybe temporary. Likely to be replaced with a query API. */ ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */ ma_format format; ma_uint32 channels; @@ -4147,6 +7304,9 @@ struct ma_device ma_uint32 internalPeriods; ma_channel_mix_mode channelMixMode; ma_data_converter converter; + void* pIntermediaryBuffer; /* For implementing fixed sized buffer callbacks. Will be null if using variable sized callbacks. */ + ma_uint32 intermediaryBufferCap; + ma_uint32 intermediaryBufferLen; /* How many valid frames are sitting in the intermediary buffer. */ } capture; union @@ -4162,16 +7322,22 @@ struct ma_device ma_IMMNotificationClient notificationClient; /*HANDLE*/ ma_handle hEventPlayback; /* Auto reset. Initialized to signaled. */ /*HANDLE*/ ma_handle hEventCapture; /* Auto reset. Initialized to unsignaled. */ - ma_uint32 actualPeriodSizeInFramesPlayback; /* Value from GetBufferSize(). internalPeriodSizeInFrames is not set to the _actual_ buffer size when low-latency shared mode is being used due to the way the IAudioClient3 API works. */ - ma_uint32 actualPeriodSizeInFramesCapture; + ma_uint32 actualBufferSizeInFramesPlayback; /* Value from GetBufferSize(). internalPeriodSizeInFrames is not set to the _actual_ buffer size when low-latency shared mode is being used due to the way the IAudioClient3 API works. */ + ma_uint32 actualBufferSizeInFramesCapture; ma_uint32 originalPeriodSizeInFrames; ma_uint32 originalPeriodSizeInMilliseconds; ma_uint32 originalPeriods; ma_performance_profile originalPerformanceProfile; ma_uint32 periodSizeInFramesPlayback; ma_uint32 periodSizeInFramesCapture; - MA_ATOMIC ma_bool32 isStartedCapture; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ - MA_ATOMIC ma_bool32 isStartedPlayback; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ + void* pMappedBufferCapture; + ma_uint32 mappedBufferCaptureCap; + ma_uint32 mappedBufferCaptureLen; + void* pMappedBufferPlayback; + ma_uint32 mappedBufferPlaybackCap; + ma_uint32 mappedBufferPlaybackLen; + MA_ATOMIC(4, ma_bool32) isStartedCapture; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ + MA_ATOMIC(4, ma_bool32) isStartedPlayback; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */ ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */ ma_bool8 noHardwareOffloading; @@ -4228,6 +7394,8 @@ struct ma_device #ifdef MA_SUPPORT_PULSEAUDIO struct { + /*pa_mainloop**/ ma_ptr pMainLoop; + /*pa_context**/ ma_ptr pPulseContext; /*pa_stream**/ ma_ptr pStreamPlayback; /*pa_stream**/ ma_ptr pStreamCapture; } pulse; @@ -4236,8 +7404,8 @@ struct ma_device struct { /*jack_client_t**/ ma_ptr pClient; - /*jack_port_t**/ ma_ptr pPortsPlayback[MA_MAX_CHANNELS]; - /*jack_port_t**/ ma_ptr pPortsCapture[MA_MAX_CHANNELS]; + /*jack_port_t**/ ma_ptr* ppPortsPlayback; + /*jack_port_t**/ ma_ptr* ppPortsCapture; float* pIntermediaryBufferPlayback; /* Typed as a float because JACK is always floating point. */ float* pIntermediaryBufferCapture; } jack; @@ -4260,7 +7428,7 @@ struct ma_device ma_bool32 isDefaultCaptureDevice; ma_bool32 isSwitchingPlaybackDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */ ma_bool32 isSwitchingCaptureDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */ - void* pRouteChangeHandler; /* Only used on mobile platforms. Obj-C object for handling route changes. */ + void* pNotificationHandler; /* Only used on mobile platforms. Obj-C object for handling route changes. */ } coreaudio; #endif #ifdef MA_SUPPORT_SNDIO @@ -4291,6 +7459,10 @@ struct ma_device { /*AAudioStream**/ ma_ptr pStreamPlayback; /*AAudioStream**/ ma_ptr pStreamCapture; + ma_aaudio_usage usage; + ma_aaudio_content_type contentType; + ma_aaudio_input_preset inputPreset; + ma_bool32 noAutoStartAfterReroute; } aaudio; #endif #ifdef MA_SUPPORT_OPENSL @@ -4334,7 +7506,7 @@ struct ma_device ma_uint32 currentPeriodFramesRemainingCapture; ma_uint64 lastProcessedFramePlayback; ma_uint64 lastProcessedFrameCapture; - MA_ATOMIC ma_bool32 isStarted; /* Read and written by multiple threads. Must be used atomically, and must be 32-bit for compiler compatibility. */ + MA_ATOMIC(4, ma_bool32) isStarted; /* Read and written by multiple threads. Must be used atomically, and must be 32-bit for compiler compatibility. */ } null_device; #endif }; @@ -4446,6 +7618,9 @@ can then be set directly on the structure. Below are the members of the `ma_cont | ma_thread_priority_default | |--------------------------------------| + threadStackSize + The desired size of the stack for the audio thread. Defaults to the operating system's default. + pUserData A pointer to application-defined data. This can be accessed from the context object directly such as `context.pUserData`. @@ -4500,6 +7675,12 @@ can then be set directly on the structure. Below are the members of the `ma_cont | ma_ios_session_category_option_allow_air_play | AVAudioSessionCategoryOptionAllowAirPlay | |---------------------------------------------------------------------------|------------------------------------------------------------------| + coreaudio.noAudioSessionActivate + iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:true] on initialization. + + coreaudio.noAudioSessionDeactivate + iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:false] on uninitialization. + jack.pClientName The name of the client to pass to `jack_client_open()`. @@ -4543,9 +7724,12 @@ ma_backend backends[] = { ma_backend_dsound }; +ma_log log; +ma_log_init(&log); +ma_log_register_callback(&log, ma_log_callback_init(my_log_callbac, pMyLogUserData)); + ma_context_config config = ma_context_config_init(); -config.logCallback = my_log_callback; -config.pUserData = pMyUserData; +config.pLog = &log; // Specify a custom log object in the config so any logs that are posted from ma_context_init() are captured. ma_context context; ma_result result = ma_context_init(backends, sizeof(backends)/sizeof(backends[0]), &config, &context); @@ -4555,6 +7739,9 @@ if (result != MA_SUCCESS) { // Couldn't find an appropriate backend. } } + +// You could also attach a log callback post-initialization: +ma_log_register_callback(ma_context_get_log(&context), ma_log_callback_init(my_log_callback, pMyLogUserData)); ``` @@ -4606,6 +7793,8 @@ Remarks Pass the returned pointer to `ma_log_post()`, `ma_log_postv()` or `ma_log_postf()` to post a log message. +You can attach your own logging callback to the log with `ma_log_register_callback()` + Return Value ------------ @@ -4747,10 +7936,6 @@ deviceType (in) pDeviceID (in) The ID of the device being queried. -shareMode (in) - The share mode to query for device capabilities. This should be set to whatever you're intending on using when initializing the device. If you're unsure, - set this to `ma_share_mode_shared`. - pDeviceInfo (out) A pointer to the `ma_device_info` structure that will receive the device information. @@ -4776,7 +7961,7 @@ the requested share mode is unsupported. This leaves pDeviceInfo unmodified in the result of an error. */ -MA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo); +MA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo); /* Determines if the given context supports loopback mode. @@ -4947,7 +8132,7 @@ then be set directly on the structure. Below are the members of the `ma_device_c A hint to miniaudio as to the performance requirements of your program. Can be either `ma_performance_profile_low_latency` (default) or `ma_performance_profile_conservative`. This mainly affects the size of default buffers and can usually be left at it's default value. - noPreZeroedOutputBuffer + noPreSilencedOutputBuffer When set to true, the contents of the output buffer passed into the data callback will be left undefined. When set to false (default), the contents of the output buffer will be cleared the zero. You can use this to avoid the overhead of zeroing out the buffer if you can guarantee that your data callback will write to every sample in the output buffer, or if you are doing your own clearing. @@ -4957,12 +8142,19 @@ then be set directly on the structure. Below are the members of the `ma_device_c contents of the output buffer are left alone after returning and it will be left up to the backend itself to decide whether or not the clip. This only applies when the playback sample format is f32. + noDisableDenormals + By default, miniaudio will disable denormals when the data callback is called. Setting this to true will prevent the disabling of denormals. + + noFixedSizedCallback + Allows miniaudio to fire the data callback with any frame count. When this is set to true, the data callback will be fired with a consistent frame + count as specified by `periodSizeInFrames` or `periodSizeInMilliseconds`. When set to false, miniaudio will fire the callback with whatever the + backend requests, which could be anything. + dataCallback The callback to fire whenever data is ready to be delivered to or from the device. - stopCallback - The callback to fire whenever the device has stopped, either explicitly via `ma_device_stop()`, or implicitly due to things like the device being - disconnected. + notificationCallback + The callback to fire when something has changed with the device, such as whether or not it has been started or stopped. pUserData The user data pointer to use with the device. You can access this directly from the device object like `device.pUserData`. @@ -4971,6 +8163,12 @@ then be set directly on the structure. Below are the members of the `ma_device_c The resampling algorithm to use when miniaudio needs to perform resampling between the rate specified by `sampleRate` and the device's native rate. The default value is `ma_resample_algorithm_linear`, and the quality can be configured with `resampling.linear.lpfOrder`. + resampling.pBackendVTable + A pointer to an optional vtable that can be used for plugging in a custom resampler. + + resampling.pBackendUserData + A pointer that will passed to callbacks in pBackendVTable. + resampling.linear.lpfOrder The linear resampler applies a low-pass filter as part of it's procesing for anti-aliasing. This setting controls the order of the filter. The higher the value, the better the quality, in general. Setting this to 0 will disable low-pass filtering altogether. The maximum value is @@ -4988,9 +8186,9 @@ then be set directly on the structure. Below are the members of the `ma_device_c The number of channels to use for playback. When set to 0 the device's native channel count will be used. This can be retrieved after initialization from the device object directly with `device.playback.channels`. - playback.channelMap + playback.pChannelMap The channel map to use for playback. When left empty, the device's native channel map will be used. This can be retrieved after initialization from the - device object direct with `device.playback.channelMap`. + device object direct with `device.playback.pChannelMap`. When set, the buffer should contain `channels` items. playback.shareMode The preferred share mode to use for playback. Can be either `ma_share_mode_shared` (default) or `ma_share_mode_exclusive`. Note that if you specify @@ -5009,9 +8207,9 @@ then be set directly on the structure. Below are the members of the `ma_device_c The number of channels to use for capture. When set to 0 the device's native channel count will be used. This can be retrieved after initialization from the device object directly with `device.capture.channels`. - capture.channelMap + capture.pChannelMap The channel map to use for capture. When left empty, the device's native channel map will be used. This can be retrieved after initialization from the - device object direct with `device.capture.channelMap`. + device object direct with `device.capture.pChannelMap`. When set, the buffer should contain `channels` items. capture.shareMode The preferred share mode to use for capture. Can be either `ma_share_mode_shared` (default) or `ma_share_mode_exclusive`. Note that if you specify @@ -5056,6 +8254,30 @@ then be set directly on the structure. Below are the members of the `ma_device_c find the closest match between the sample rate requested in the device config and the sample rates natively supported by the hardware. When set to false, the sample rate currently set by the operating system will always be used. + opensl.streamType + OpenSL only. Explicitly sets the stream type. If left unset (`ma_opensl_stream_type_default`), the + stream type will be left unset. Think of this as the type of audio you're playing. + + opensl.recordingPreset + OpenSL only. Explicitly sets the type of recording your program will be doing. When left + unset, the recording preset will be left unchanged. + + aaudio.usage + AAudio only. Explicitly sets the nature of the audio the program will be consuming. When + left unset, the usage will be left unchanged. + + aaudio.contentType + AAudio only. Sets the content type. When left unset, the content type will be left unchanged. + + aaudio.inputPreset + AAudio only. Explicitly sets the type of recording your program will be doing. When left + unset, the input preset will be left unchanged. + + aaudio.noAutoStartAfterReroute + AAudio only. Controls whether or not the device should be automatically restarted after a + stream reroute. When set to false (default) the device will be restarted automatically; + otherwise the device will be stopped. + Once initialized, the device's config is immutable. If you need to change the config you will need to initialize a new device. @@ -5259,6 +8481,95 @@ Helper function for retrieving the log object associated with the context that o MA_API ma_log* ma_device_get_log(ma_device* pDevice); +/* +Retrieves information about the device. + + +Parameters +---------- +pDevice (in) + A pointer to the device whose information is being retrieved. + +type (in) + The device type. This parameter is required for duplex devices. When retrieving device + information, you are doing so for an individual playback or capture device. + +pDeviceInfo (out) + A pointer to the `ma_device_info` that will receive the device information. + + +Return Value +------------ +MA_SUCCESS if successful; any other error code otherwise. + + +Thread Safety +------------- +Unsafe. This should be considered unsafe because it may be calling into the backend which may or +may not be safe. + + +Callback Safety +--------------- +Unsafe. You should avoid calling this in the data callback because it may call into the backend +which may or may not be safe. +*/ +MA_API ma_result ma_device_get_info(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo); + + +/* +Retrieves the name of the device. + + +Parameters +---------- +pDevice (in) + A pointer to the device whose information is being retrieved. + +type (in) + The device type. This parameter is required for duplex devices. When retrieving device + information, you are doing so for an individual playback or capture device. + +pName (out) + A pointer to the buffer that will receive the name. + +nameCap (in) + The capacity of the output buffer, including space for the null terminator. + +pLengthNotIncludingNullTerminator (out, optional) + A pointer to the variable that will receive the length of the name, not including the null + terminator. + + +Return Value +------------ +MA_SUCCESS if successful; any other error code otherwise. + + +Thread Safety +------------- +Unsafe. This should be considered unsafe because it may be calling into the backend which may or +may not be safe. + + +Callback Safety +--------------- +Unsafe. You should avoid calling this in the data callback because it may call into the backend +which may or may not be safe. + + +Remarks +------- +If the name does not fully fit into the output buffer, it'll be truncated. You can pass in NULL to +`pName` if you want to first get the length of the name for the purpose of memory allocation of the +output buffer. Allocating a buffer of size `MA_MAX_DEVICE_NAME_LENGTH + 1` should be enough for +most cases and will avoid the need for the inefficiency of calling this function twice. + +This is implemented in terms of `ma_device_get_info()`. +*/ +MA_API ma_result ma_device_get_name(ma_device* pDevice, ma_device_type type, char* pName, size_t nameCap, size_t* pLengthNotIncludingNullTerminator); + + /* Starts the device. For playback devices this begins playback. For capture devices it begins recording. @@ -5398,17 +8709,17 @@ Return Value ------------ The current state of the device. The return value will be one of the following: - +------------------------+------------------------------------------------------------------------------+ - | MA_STATE_UNINITIALIZED | Will only be returned if the device is in the middle of initialization. | - +------------------------+------------------------------------------------------------------------------+ - | MA_STATE_STOPPED | The device is stopped. The initial state of the device after initialization. | - +------------------------+------------------------------------------------------------------------------+ - | MA_STATE_STARTED | The device started and requesting and/or delivering audio data. | - +------------------------+------------------------------------------------------------------------------+ - | MA_STATE_STARTING | The device is in the process of starting. | - +------------------------+------------------------------------------------------------------------------+ - | MA_STATE_STOPPING | The device is in the process of stopping. | - +------------------------+------------------------------------------------------------------------------+ + +-------------------------------+------------------------------------------------------------------------------+ + | ma_device_state_uninitialized | Will only be returned if the device is in the middle of initialization. | + +-------------------------------+------------------------------------------------------------------------------+ + | ma_device_state_stopped | The device is stopped. The initial state of the device after initialization. | + +-------------------------------+------------------------------------------------------------------------------+ + | ma_device_state_started | The device started and requesting and/or delivering audio data. | + +-------------------------------+------------------------------------------------------------------------------+ + | ma_device_state_starting | The device is in the process of starting. | + +-------------------------------+------------------------------------------------------------------------------+ + | ma_device_state_stopping | The device is in the process of stopping. | + +-------------------------------+------------------------------------------------------------------------------+ Thread Safety @@ -5427,22 +8738,71 @@ Remarks The general flow of a devices state goes like this: ``` - ma_device_init() -> MA_STATE_UNINITIALIZED -> MA_STATE_STOPPED - ma_device_start() -> MA_STATE_STARTING -> MA_STATE_STARTED - ma_device_stop() -> MA_STATE_STOPPING -> MA_STATE_STOPPED + ma_device_init() -> ma_device_state_uninitialized -> ma_device_state_stopped + ma_device_start() -> ma_device_state_starting -> ma_device_state_started + ma_device_stop() -> ma_device_state_stopping -> ma_device_state_stopped ``` When the state of the device is changed with `ma_device_start()` or `ma_device_stop()` at this same time as this function is called, the value returned by this function could potentially be out of sync. If this is significant to your program you need to implement your own synchronization. */ -MA_API ma_uint32 ma_device_get_state(const ma_device* pDevice); +MA_API ma_device_state ma_device_get_state(const ma_device* pDevice); + + +/* +Performs post backend initialization routines for setting up internal data conversion. + +This should be called whenever the backend is initialized. The only time this should be called from +outside of miniaudio is if you're implementing a custom backend, and you would only do it if you +are reinitializing the backend due to rerouting or reinitializing for some reason. + + +Parameters +---------- +pDevice [in] + A pointer to the device. + +deviceType [in] + The type of the device that was just reinitialized. + +pPlaybackDescriptor [in] + The descriptor of the playback device containing the internal data format and buffer sizes. + +pPlaybackDescriptor [in] + The descriptor of the capture device containing the internal data format and buffer sizes. + + +Return Value +------------ +MA_SUCCESS if successful; any other error otherwise. + + +Thread Safety +------------- +Unsafe. This will be reinitializing internal data converters which may be in use by another thread. + + +Callback Safety +--------------- +Unsafe. This will be reinitializing internal data converters which may be in use by the callback. + + +Remarks +------- +For a duplex device, you can call this for only one side of the system. This is why the deviceType +is specified as a parameter rather than deriving it from the device. + +You do not need to call this manually unless you are doing a custom backend, in which case you need +only do it if you're manually performing rerouting or reinitialization. +*/ +MA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceType, const ma_device_descriptor* pPlaybackDescriptor, const ma_device_descriptor* pCaptureDescriptor); /* Sets the master volume factor for the device. -The volume factor must be between 0 (silence) and 1 (full volume). Use `ma_device_set_master_gain_db()` to use decibel notation, where 0 is full volume and +The volume factor must be between 0 (silence) and 1 (full volume). Use `ma_device_set_master_volume_db()` to use decibel notation, where 0 is full volume and values less than 0 decreases the volume. @@ -5452,14 +8812,14 @@ pDevice (in) A pointer to the device whose volume is being set. volume (in) - The new volume factor. Must be within the range of [0, 1]. + The new volume factor. Must be >= 0. Return Value ------------ MA_SUCCESS if the volume was set successfully. MA_INVALID_ARGS if pDevice is NULL. -MA_INVALID_ARGS if the volume factor is not within the range of [0, 1]. +MA_INVALID_ARGS if volume is negative. Thread Safety @@ -5482,8 +8842,8 @@ This does not change the operating system's volume. It only affects the volume f See Also -------- ma_device_get_master_volume() -ma_device_set_master_volume_gain_db() -ma_device_get_master_volume_gain_db() +ma_device_set_master_volume_db() +ma_device_get_master_volume_db() */ MA_API ma_result ma_device_set_master_volume(ma_device* pDevice, float volume); @@ -5575,7 +8935,7 @@ ma_device_get_master_volume_gain_db() ma_device_set_master_volume() ma_device_get_master_volume() */ -MA_API ma_result ma_device_set_master_gain_db(ma_device* pDevice, float gainDB); +MA_API ma_result ma_device_set_master_volume_db(ma_device* pDevice, float gainDB); /* Retrieves the master gain in decibels. @@ -5614,11 +8974,11 @@ If an error occurs, `*pGainDB` will be set to 0. See Also -------- -ma_device_set_master_volume_gain_db() +ma_device_set_master_volume_db() ma_device_set_master_volume() ma_device_get_master_volume() */ -MA_API ma_result ma_device_get_master_gain_db(ma_device* pDevice, float* pGainDB); +MA_API ma_result ma_device_get_master_volume_db(ma_device* pDevice, float* pGainDB); /* @@ -5814,68 +9174,6 @@ MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend); #endif /* MA_NO_DEVICE_IO */ -#ifndef MA_NO_THREADING - -/* -Locks a spinlock. -*/ -MA_API ma_result ma_spinlock_lock(volatile ma_spinlock* pSpinlock); - -/* -Locks a spinlock, but does not yield() when looping. -*/ -MA_API ma_result ma_spinlock_lock_noyield(volatile ma_spinlock* pSpinlock); - -/* -Unlocks a spinlock. -*/ -MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock); - - -/* -Creates a mutex. - -A mutex must be created from a valid context. A mutex is initially unlocked. -*/ -MA_API ma_result ma_mutex_init(ma_mutex* pMutex); - -/* -Deletes a mutex. -*/ -MA_API void ma_mutex_uninit(ma_mutex* pMutex); - -/* -Locks a mutex with an infinite timeout. -*/ -MA_API void ma_mutex_lock(ma_mutex* pMutex); - -/* -Unlocks a mutex. -*/ -MA_API void ma_mutex_unlock(ma_mutex* pMutex); - - -/* -Initializes an auto-reset event. -*/ -MA_API ma_result ma_event_init(ma_event* pEvent); - -/* -Uninitializes an auto-reset event. -*/ -MA_API void ma_event_uninit(ma_event* pEvent); - -/* -Waits for the specified auto-reset event to become signalled. -*/ -MA_API ma_result ma_event_wait(ma_event* pEvent); - -/* -Signals the specified auto-reset event. -*/ -MA_API ma_result ma_event_signal(ma_event* pEvent); -#endif /* MA_NO_THREADING */ - /************************************************************************************************************************************************************ @@ -5883,13 +9181,6 @@ Utiltities ************************************************************************************************************************************************************/ -/* -Adjust buffer size based on a scaling factor. - -This just multiplies the base size by the scaling factor, making sure it's a size of at least 1. -*/ -MA_API ma_uint32 ma_scale_buffer_size(ma_uint32 baseBufferSize, float scale); - /* Calculates a buffer size in milliseconds from the specified number of frames and sample rate. */ @@ -5914,7 +9205,6 @@ For all formats except `ma_format_u8`, the output buffer will be filled with 0. makes more sense for the purpose of mixing to initialize it to the center point. */ MA_API void ma_silence_pcm_frames(void* p, ma_uint64 frameCount, ma_format format, ma_uint32 channels); -static MA_INLINE void ma_zero_pcm_frames(void* p, ma_uint64 frameCount, ma_format format, ma_uint32 channels) { ma_silence_pcm_frames(p, frameCount, format, channels); } /* @@ -5927,10 +9217,14 @@ static MA_INLINE const float* ma_offset_pcm_frames_const_ptr_f32(const float* p, /* -Clips f32 samples. +Clips samples. */ -MA_API void ma_clip_samples_f32(float* p, ma_uint64 sampleCount); -static MA_INLINE void ma_clip_pcm_frames_f32(float* p, ma_uint64 frameCount, ma_uint32 channels) { ma_clip_samples_f32(p, frameCount*channels); } +MA_API void ma_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count); +MA_API void ma_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count); +MA_API void ma_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count); +MA_API void ma_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count); +MA_API void ma_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count); +MA_API void ma_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels); /* Helper for applying a volume factor to samples. @@ -5949,11 +9243,11 @@ MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint64 sampleCount, fl MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint64 sampleCount, float factor); MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint64 sampleCount, float factor); -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pPCMFramesOut, const ma_uint8* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pPCMFramesOut, const ma_int16* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pPCMFramesOut, const void* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pPCMFramesOut, const ma_int32* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pPCMFramesOut, const float* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pFramesOut, const ma_uint8* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pFramesOut, const ma_int16* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pFramesOut, const ma_int32* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); MA_API void ma_copy_and_apply_volume_factor_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor); MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); @@ -5963,36 +9257,55 @@ MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint64 f MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); MA_API void ma_apply_volume_factor_pcm_frames(void* pFrames, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor); +MA_API void ma_copy_and_apply_volume_factor_per_channel_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float* pChannelGains); + + +MA_API void ma_copy_and_apply_volume_and_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count, float volume); +MA_API void ma_copy_and_apply_volume_and_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count, float volume); +MA_API void ma_copy_and_apply_volume_and_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count, float volume); +MA_API void ma_copy_and_apply_volume_and_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count, float volume); +MA_API void ma_copy_and_apply_volume_and_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count, float volume); +MA_API void ma_copy_and_apply_volume_and_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float volume); + /* Helper for converting a linear factor to gain in decibels. */ -MA_API float ma_factor_to_gain_db(float factor); +MA_API float ma_volume_linear_to_db(float factor); /* Helper for converting gain in decibels to a linear factor. */ -MA_API float ma_gain_db_to_factor(float gain); +MA_API float ma_volume_db_to_linear(float gain); + + +/************************************************************************************************** + +Data Source + +**************************************************************************************************/ typedef void ma_data_source; +#define MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT 0x00000001 + typedef struct { ma_result (* onRead)(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); ma_result (* onSeek)(ma_data_source* pDataSource, ma_uint64 frameIndex); - ma_result (* onMap)(ma_data_source* pDataSource, void** ppFramesOut, ma_uint64* pFrameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */ - ma_result (* onUnmap)(ma_data_source* pDataSource, ma_uint64 frameCount); - ma_result (* onGetDataFormat)(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate); + ma_result (* onGetDataFormat)(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); ma_result (* onGetCursor)(ma_data_source* pDataSource, ma_uint64* pCursor); ma_result (* onGetLength)(ma_data_source* pDataSource, ma_uint64* pLength); -} ma_data_source_vtable, ma_data_source_callbacks; /* TODO: Remove ma_data_source_callbacks in version 0.11. */ + ma_result (* onSetLooping)(ma_data_source* pDataSource, ma_bool32 isLooping); + ma_uint32 flags; +} ma_data_source_vtable; typedef ma_data_source* (* ma_data_source_get_next_proc)(ma_data_source* pDataSource); typedef struct { - const ma_data_source_vtable* vtable; /* Can be null, which is useful for proxies. */ + const ma_data_source_vtable* vtable; } ma_data_source_config; MA_API ma_data_source_config ma_data_source_config_init(void); @@ -6000,9 +9313,6 @@ MA_API ma_data_source_config ma_data_source_config_init(void); typedef struct { - ma_data_source_callbacks cb; /* TODO: Remove this. */ - - /* Variables below are placeholder and not yet used. */ const ma_data_source_vtable* vtable; ma_uint64 rangeBegInFrames; ma_uint64 rangeEndInFrames; /* Set to -1 for unranged (default). */ @@ -6011,30 +9321,31 @@ typedef struct ma_data_source* pCurrent; /* When non-NULL, the data source being initialized will act as a proxy and will route all operations to pCurrent. Used in conjunction with pNext/onGetNext for seamless chaining. */ ma_data_source* pNext; /* When set to NULL, onGetNext will be used. */ ma_data_source_get_next_proc onGetNext; /* Will be used when pNext is NULL. If both are NULL, no next will be used. */ + MA_ATOMIC(4, ma_bool32) isLooping; } ma_data_source_base; MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_data_source* pDataSource); MA_API void ma_data_source_uninit(ma_data_source* pDataSource); -MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead, ma_bool32 loop); /* Must support pFramesOut = NULL in which case a forward seek should be performed. */ -MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked, ma_bool32 loop); /* Can only seek forward. Equivalent to ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount); */ +MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Must support pFramesOut = NULL in which case a forward seek should be performed. */ +MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked); /* Can only seek forward. Equivalent to ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, &framesRead); */ MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex); -MA_API ma_result ma_data_source_map(ma_data_source* pDataSource, void** ppFramesOut, ma_uint64* pFrameCount); /* Returns MA_NOT_IMPLEMENTED if mapping is not supported. */ -MA_API ma_result ma_data_source_unmap(ma_data_source* pDataSource, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. */ -MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate); +MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor); MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength); /* Returns MA_NOT_IMPLEMENTED if the length is unknown or cannot be determined. Decoders can return this. */ -#if defined(MA_EXPERIMENTAL__DATA_LOOPING_AND_CHAINING) +MA_API ma_result ma_data_source_get_cursor_in_seconds(ma_data_source* pDataSource, float* pCursor); +MA_API ma_result ma_data_source_get_length_in_seconds(ma_data_source* pDataSource, float* pLength); +MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool32 isLooping); +MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource); MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames); -MA_API void ma_data_source_get_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames); +MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames); MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames); -MA_API void ma_data_source_get_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames); +MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames); MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource); -MA_API ma_data_source* ma_data_source_get_current(ma_data_source* pDataSource); +MA_API ma_data_source* ma_data_source_get_current(const ma_data_source* pDataSource); MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource); -MA_API ma_data_source* ma_data_source_get_next(ma_data_source* pDataSource); +MA_API ma_data_source* ma_data_source_get_next(const ma_data_source* pDataSource); MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext); -MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(ma_data_source* pDataSource); -#endif +MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(const ma_data_source* pDataSource); typedef struct @@ -6042,6 +9353,7 @@ typedef struct ma_data_source_base ds; ma_format format; ma_uint32 channels; + ma_uint32 sampleRate; ma_uint64 cursor; ma_uint64 sizeInFrames; const void* pData; @@ -6065,6 +9377,7 @@ typedef struct { ma_format format; ma_uint32 channels; + ma_uint32 sampleRate; ma_uint64 sizeInFrames; const void* pData; /* If set to NULL, will allocate a block of memory for you. */ ma_allocation_callbacks allocationCallbacks; @@ -6095,6 +9408,69 @@ MA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer* MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames); +/* +Paged Audio Buffer +================== +A paged audio buffer is made up of a linked list of pages. It's expandable, but not shrinkable. It +can be used for cases where audio data is streamed in asynchronously while allowing data to be read +at the same time. + +This is lock-free, but not 100% thread safe. You can append a page and read from the buffer across +simultaneously across different threads, however only one thread at a time can append, and only one +thread at a time can read and seek. +*/ +typedef struct ma_paged_audio_buffer_page ma_paged_audio_buffer_page; +struct ma_paged_audio_buffer_page +{ + MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pNext; + ma_uint64 sizeInFrames; + ma_uint8 pAudioData[1]; +}; + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_paged_audio_buffer_page head; /* Dummy head for the lock-free algorithm. Always has a size of 0. */ + MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pTail; /* Never null. Initially set to &head. */ +} ma_paged_audio_buffer_data; + +MA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data* pData); +MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data* pData); +MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data* pData); +MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data* pData, ma_uint64* pLength); +MA_API ma_result ma_paged_audio_buffer_data_allocate_page(ma_paged_audio_buffer_data* pData, ma_uint64 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage); +MA_API ma_result ma_paged_audio_buffer_data_free_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage); +MA_API ma_result ma_paged_audio_buffer_data_allocate_and_append_page(ma_paged_audio_buffer_data* pData, ma_uint32 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks); + + +typedef struct +{ + ma_paged_audio_buffer_data* pData; /* Must not be null. */ +} ma_paged_audio_buffer_config; + +MA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data* pData); + + +typedef struct +{ + ma_data_source_base ds; + ma_paged_audio_buffer_data* pData; /* Audio data is read from here. Cannot be null. */ + ma_paged_audio_buffer_page* pCurrent; + ma_uint64 relativeCursor; /* Relative to the current page. */ + ma_uint64 absoluteCursor; +} ma_paged_audio_buffer; + +MA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config* pConfig, ma_paged_audio_buffer* pPagedAudioBuffer); +MA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer* pPagedAudioBuffer); +MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Returns MA_AT_END if no more pages available. */ +MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64 frameIndex); +MA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pCursor); +MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pLength); + + /************************************************************************************************************************************************************ @@ -6108,8 +9484,11 @@ appropriate for a given situation. typedef void ma_vfs; typedef ma_handle ma_vfs_file; -#define MA_OPEN_MODE_READ 0x00000001 -#define MA_OPEN_MODE_WRITE 0x00000002 +typedef enum +{ + MA_OPEN_MODE_READ = 0x00000001, + MA_OPEN_MODE_WRITE = 0x00000002 +} ma_open_mode_flags; typedef enum { @@ -6162,11 +9541,6 @@ typedef ma_result (* ma_tell_proc)(void* pUserData, ma_int64* pCursor); #if !defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING) -typedef enum -{ - ma_resource_format_wav -} ma_resource_format; - typedef enum { ma_encoding_format_unknown = 0, @@ -6193,25 +9567,24 @@ typedef struct ma_decoder ma_decoder; typedef struct { ma_format preferredFormat; + ma_uint32 seekPointCount; /* Set to > 0 to generate a seektable if the decoding backend supports it. */ } ma_decoding_backend_config; -MA_API ma_decoding_backend_config ma_decoding_backend_config_init(ma_format preferredFormat); +MA_API ma_decoding_backend_config ma_decoding_backend_config_init(ma_format preferredFormat, ma_uint32 seekPointCount); typedef struct { - ma_result (* onInit )(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); - ma_result (* onInitFile )(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */ - ma_result (* onInitFileW )(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */ - ma_result (* onInitMemory )(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */ - void (* onUninit )(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks); - ma_result (* onGetChannelMap)(void* pUserData, ma_data_source* pBackend, ma_channel* pChannelMap, size_t channelMapCap); + ma_result (* onInit )(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); + ma_result (* onInitFile )(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */ + ma_result (* onInitFileW )(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */ + ma_result (* onInitMemory)(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */ + void (* onUninit )(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks); } ma_decoding_backend_vtable; -/* TODO: Convert read and seek to be consistent with the VFS API (ma_result return value, bytes read moved to an output parameter). */ -typedef size_t (* ma_decoder_read_proc)(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead); /* Returns the number of bytes read. */ -typedef ma_bool32 (* ma_decoder_seek_proc)(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin); +typedef ma_result (* ma_decoder_read_proc)(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead); /* Returns the number of bytes read. */ +typedef ma_result (* ma_decoder_seek_proc)(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin); typedef ma_result (* ma_decoder_tell_proc)(ma_decoder* pDecoder, ma_int64* pCursor); typedef struct @@ -6219,23 +9592,13 @@ typedef struct ma_format format; /* Set to 0 or ma_format_unknown to use the stream's internal format. */ ma_uint32 channels; /* Set to 0 to use the stream's internal channels. */ ma_uint32 sampleRate; /* Set to 0 to use the stream's internal sample rate. */ - ma_channel channelMap[MA_MAX_CHANNELS]; + ma_channel* pChannelMap; ma_channel_mix_mode channelMixMode; ma_dither_mode ditherMode; - struct - { - ma_resample_algorithm algorithm; - struct - { - ma_uint32 lpfOrder; - } linear; - struct - { - int quality; - } speex; - } resampling; + ma_resampler_config resampling; ma_allocation_callbacks allocationCallbacks; ma_encoding_format encodingFormat; + ma_uint32 seekPointCount; /* When set to > 0, specifies the number of seek points to use for the generation of a seek table. Not all decoding backends support this. */ ma_decoding_backend_vtable** ppCustomBackendVTables; ma_uint32 customBackendCount; void* pCustomBackendUserData; @@ -6255,8 +9618,11 @@ struct ma_decoder ma_format outputFormat; ma_uint32 outputChannels; ma_uint32 outputSampleRate; - ma_channel outputChannelMap[MA_MAX_CHANNELS]; - ma_data_converter converter; /* <-- Data conversion is achieved by running frames through this. */ + ma_data_converter converter; /* Data conversion is achieved by running frames through this. */ + void* pInputCache; /* In input format. Can be null if it's not needed. */ + ma_uint64 inputCacheCap; /* The capacity of the input cache. */ + ma_uint64 inputCacheConsumed; /* The number of frames that have been consumed in the cache. Used for determining the next valid frame. */ + ma_uint64 inputCacheRemaining; /* The number of valid frames remaining in the cahce. */ ma_allocation_callbacks allocationCallbacks; union { @@ -6289,6 +9655,25 @@ Uninitializes a decoder. */ MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder); +/* +Reads PCM frames from the given decoder. + +This is not thread safe without your own synchronization. +*/ +MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); + +/* +Seeks to a PCM frame based on it's absolute index. + +This is not thread safe without your own synchronization. +*/ +MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex); + +/* +Retrieves the decoder's output data format. +*/ +MA_API ma_result ma_decoder_get_data_format(ma_decoder* pDecoder, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); + /* Retrieves the current position of the read cursor in PCM frames. */ @@ -6308,21 +9693,7 @@ For MP3's, this will decode the entire file. Do not call this in time critical s This function is not thread safe without your own synchronization. */ -MA_API ma_uint64 ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder); - -/* -Reads PCM frames from the given decoder. - -This is not thread safe without your own synchronization. -*/ -MA_API ma_uint64 ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount); - -/* -Seeks to a PCM frame based on it's absolute index. - -This is not thread safe without your own synchronization. -*/ -MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex); +MA_API ma_result ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pLength); /* Retrieves the number of frames that can be read before reaching the end. @@ -6343,43 +9714,6 @@ MA_API ma_result ma_decode_from_vfs(ma_vfs* pVFS, const char* pFilePath, ma_deco MA_API ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut); MA_API ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut); - - - -/* -DEPRECATED - -Set the "encodingFormat" variable in the decoder config instead: - - decoderConfig.encodingFormat = ma_encoding_format_wav; - -These functions will be removed in version 0.11. -*/ -MA_API ma_result ma_decoder_init_wav(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_flac(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_mp3(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_vorbis(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_memory_wav(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_memory_flac(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_memory_mp3(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_memory_vorbis(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_vfs_wav(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_vfs_flac(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_vfs_mp3(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_vfs_vorbis(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_vfs_wav_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_vfs_flac_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_vfs_mp3_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_vfs_vorbis_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_file_wav(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_file_flac(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_file_mp3(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_file_vorbis(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_file_wav_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_file_flac_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_file_mp3_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_file_vorbis_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); - #endif /* MA_NO_DECODING */ @@ -6394,22 +9728,22 @@ Encoders do not perform any format conversion for you. If your target format doe #ifndef MA_NO_ENCODING typedef struct ma_encoder ma_encoder; -typedef size_t (* ma_encoder_write_proc) (ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite); /* Returns the number of bytes written. */ -typedef ma_bool32 (* ma_encoder_seek_proc) (ma_encoder* pEncoder, int byteOffset, ma_seek_origin origin); +typedef ma_result (* ma_encoder_write_proc) (ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite, size_t* pBytesWritten); +typedef ma_result (* ma_encoder_seek_proc) (ma_encoder* pEncoder, ma_int64 offset, ma_seek_origin origin); typedef ma_result (* ma_encoder_init_proc) (ma_encoder* pEncoder); typedef void (* ma_encoder_uninit_proc) (ma_encoder* pEncoder); -typedef ma_uint64 (* ma_encoder_write_pcm_frames_proc)(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount); +typedef ma_result (* ma_encoder_write_pcm_frames_proc)(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten); typedef struct { - ma_resource_format resourceFormat; + ma_encoding_format encodingFormat; ma_format format; ma_uint32 channels; ma_uint32 sampleRate; ma_allocation_callbacks allocationCallbacks; } ma_encoder_config; -MA_API ma_encoder_config ma_encoder_config_init(ma_resource_format resourceFormat, ma_format format, ma_uint32 channels, ma_uint32 sampleRate); +MA_API ma_encoder_config ma_encoder_config_init(ma_encoding_format encodingFormat, ma_format format, ma_uint32 channels, ma_uint32 sampleRate); struct ma_encoder { @@ -6421,14 +9755,23 @@ struct ma_encoder ma_encoder_write_pcm_frames_proc onWritePCMFrames; void* pUserData; void* pInternalEncoder; /* <-- The drwav/drflac/stb_vorbis/etc. objects. */ - void* pFile; /* FILE*. Only used when initialized with ma_encoder_init_file(). */ + union + { + struct + { + ma_vfs* pVFS; + ma_vfs_file file; + } vfs; + } data; }; MA_API ma_result ma_encoder_init(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, const ma_encoder_config* pConfig, ma_encoder* pEncoder); +MA_API ma_result ma_encoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); +MA_API ma_result ma_encoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); MA_API ma_result ma_encoder_init_file(const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); MA_API ma_result ma_encoder_init_file_w(const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); MA_API void ma_encoder_uninit(ma_encoder* pEncoder); -MA_API ma_uint64 ma_encoder_write_pcm_frames(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount); +MA_API ma_result ma_encoder_write_pcm_frames(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten); #endif /* MA_NO_ENCODING */ @@ -6469,7 +9812,7 @@ typedef struct MA_API ma_result ma_waveform_init(const ma_waveform_config* pConfig, ma_waveform* pWaveform); MA_API void ma_waveform_uninit(ma_waveform* pWaveform); -MA_API ma_uint64 ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount); +MA_API ma_result ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); MA_API ma_result ma_waveform_seek_to_pcm_frame(ma_waveform* pWaveform, ma_uint64 frameIndex); MA_API ma_result ma_waveform_set_amplitude(ma_waveform* pWaveform, double amplitude); MA_API ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double frequency); @@ -6483,6 +9826,7 @@ typedef enum ma_noise_type_brownian } ma_noise_type; + typedef struct { ma_format format; @@ -6504,32 +9848,1187 @@ typedef struct { struct { - double bin[MA_MAX_CHANNELS][16]; - double accumulation[MA_MAX_CHANNELS]; - ma_uint32 counter[MA_MAX_CHANNELS]; + double** bin; + double* accumulation; + ma_uint32* counter; } pink; struct { - double accumulation[MA_MAX_CHANNELS]; + double* accumulation; } brownian; } state; + + /* Memory management. */ + void* _pHeap; + ma_bool32 _ownsHeap; } ma_noise; -MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, ma_noise* pNoise); -MA_API void ma_noise_uninit(ma_noise* pNoise); -MA_API ma_uint64 ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount); +MA_API ma_result ma_noise_get_heap_size(const ma_noise_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_noise_init_preallocated(const ma_noise_config* pConfig, void* pHeap, ma_noise* pNoise); +MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_noise* pNoise); +MA_API void ma_noise_uninit(ma_noise* pNoise, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); MA_API ma_result ma_noise_set_amplitude(ma_noise* pNoise, double amplitude); MA_API ma_result ma_noise_set_seed(ma_noise* pNoise, ma_int32 seed); MA_API ma_result ma_noise_set_type(ma_noise* pNoise, ma_noise_type type); #endif /* MA_NO_GENERATION */ + + +/************************************************************************************************************************************************************ + +Resource Manager + +************************************************************************************************************************************************************/ +/* The resource manager cannot be enabled if there is no decoder. */ +#if !defined(MA_NO_RESOURCE_MANAGER) && defined(MA_NO_DECODING) +#define MA_NO_RESOURCE_MANAGER +#endif + +#ifndef MA_NO_RESOURCE_MANAGER +typedef struct ma_resource_manager ma_resource_manager; +typedef struct ma_resource_manager_data_buffer_node ma_resource_manager_data_buffer_node; +typedef struct ma_resource_manager_data_buffer ma_resource_manager_data_buffer; +typedef struct ma_resource_manager_data_stream ma_resource_manager_data_stream; +typedef struct ma_resource_manager_data_source ma_resource_manager_data_source; + +typedef enum +{ + MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM = 0x00000001, /* When set, does not load the entire data source in memory. Disk I/O will happen on job threads. */ + MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE = 0x00000002, /* Decode data before storing in memory. When set, decoding is done at the resource manager level rather than the mixing thread. Results in faster mixing, but higher memory usage. */ + MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC = 0x00000004, /* When set, the resource manager will load the data source asynchronously. */ + MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT = 0x00000008, /* When set, waits for initialization of the underlying data source before returning from ma_resource_manager_data_source_init(). */ + MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH = 0x00000010 /* Gives the resource manager a hint that the length of the data source is unknown and calling `ma_data_source_get_length_in_pcm_frames()` should be avoided. */ +} ma_resource_manager_data_source_flags; + + +/* +Pipeline notifications used by the resource manager. Made up of both an async notification and a fence, both of which are optional. +*/ +typedef struct +{ + ma_async_notification* pNotification; + ma_fence* pFence; +} ma_resource_manager_pipeline_stage_notification; + +typedef struct +{ + ma_resource_manager_pipeline_stage_notification init; /* Initialization of the decoder. */ + ma_resource_manager_pipeline_stage_notification done; /* Decoding fully completed. */ +} ma_resource_manager_pipeline_notifications; + +MA_API ma_resource_manager_pipeline_notifications ma_resource_manager_pipeline_notifications_init(void); + + + +/* BEGIN BACKWARDS COMPATIBILITY */ +/* TODO: Remove this block in version 0.12. */ +#if 1 +#define ma_resource_manager_job ma_job +#define ma_resource_manager_job_init ma_job_init +#define MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_FLAG_NON_BLOCKING MA_JOB_QUEUE_FLAG_NON_BLOCKING +#define ma_resource_manager_job_queue_config ma_job_queue_config +#define ma_resource_manager_job_queue_config_init ma_job_queue_config_init +#define ma_resource_manager_job_queue ma_job_queue +#define ma_resource_manager_job_queue_get_heap_size ma_job_queue_get_heap_size +#define ma_resource_manager_job_queue_init_preallocated ma_job_queue_init_preallocated +#define ma_resource_manager_job_queue_init ma_job_queue_init +#define ma_resource_manager_job_queue_uninit ma_job_queue_uninit +#define ma_resource_manager_job_queue_post ma_job_queue_post +#define ma_resource_manager_job_queue_next ma_job_queue_next +#endif +/* END BACKWARDS COMPATIBILITY */ + + + + +/* Maximum job thread count will be restricted to this, but this may be removed later and replaced with a heap allocation thereby removing any limitation. */ +#ifndef MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT +#define MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT 64 +#endif + +typedef enum +{ + /* Indicates ma_resource_manager_next_job() should not block. Only valid when the job thread count is 0. */ + MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING = 0x00000001, + + /* Disables any kind of multithreading. Implicitly enables MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING. */ + MA_RESOURCE_MANAGER_FLAG_NO_THREADING = 0x00000002 +} ma_resource_manager_flags; + +typedef struct +{ + const char* pFilePath; + const wchar_t* pFilePathW; + const ma_resource_manager_pipeline_notifications* pNotifications; + ma_uint64 initialSeekPointInPCMFrames; + ma_uint64 rangeBegInPCMFrames; + ma_uint64 rangeEndInPCMFrames; + ma_uint64 loopPointBegInPCMFrames; + ma_uint64 loopPointEndInPCMFrames; + ma_bool32 isLooping; + ma_uint32 flags; +} ma_resource_manager_data_source_config; + +MA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_config_init(void); + + +typedef enum +{ + ma_resource_manager_data_supply_type_unknown = 0, /* Used for determining whether or the data supply has been initialized. */ + ma_resource_manager_data_supply_type_encoded, /* Data supply is an encoded buffer. Connector is ma_decoder. */ + ma_resource_manager_data_supply_type_decoded, /* Data supply is a decoded buffer. Connector is ma_audio_buffer. */ + ma_resource_manager_data_supply_type_decoded_paged /* Data supply is a linked list of decoded buffers. Connector is ma_paged_audio_buffer. */ +} ma_resource_manager_data_supply_type; + +typedef struct +{ + MA_ATOMIC(4, ma_resource_manager_data_supply_type) type; /* Read and written from different threads so needs to be accessed atomically. */ + union + { + struct + { + const void* pData; + size_t sizeInBytes; + } encoded; + struct + { + const void* pData; + ma_uint64 totalFrameCount; + ma_uint64 decodedFrameCount; + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + } decoded; + struct + { + ma_paged_audio_buffer_data data; + ma_uint64 decodedFrameCount; + ma_uint32 sampleRate; + } decodedPaged; + } backend; +} ma_resource_manager_data_supply; + +struct ma_resource_manager_data_buffer_node +{ + ma_uint32 hashedName32; /* The hashed name. This is the key. */ + ma_uint32 refCount; + MA_ATOMIC(4, ma_result) result; /* Result from asynchronous loading. When loading set to MA_BUSY. When fully loaded set to MA_SUCCESS. When deleting set to MA_UNAVAILABLE. */ + MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ + MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ + ma_bool32 isDataOwnedByResourceManager; /* Set to true when the underlying data buffer was allocated the resource manager. Set to false if it is owned by the application (via ma_resource_manager_register_*()). */ + ma_resource_manager_data_supply data; + ma_resource_manager_data_buffer_node* pParent; + ma_resource_manager_data_buffer_node* pChildLo; + ma_resource_manager_data_buffer_node* pChildHi; +}; + +struct ma_resource_manager_data_buffer +{ + ma_data_source_base ds; /* Base data source. A data buffer is a data source. */ + ma_resource_manager* pResourceManager; /* A pointer to the resource manager that owns this buffer. */ + ma_resource_manager_data_buffer_node* pNode; /* The data node. This is reference counted and is what supplies the data. */ + ma_uint32 flags; /* The flags that were passed used to initialize the buffer. */ + MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ + MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ + ma_uint64 seekTargetInPCMFrames; /* Only updated by the public API. Never written nor read from the job thread. */ + ma_bool32 seekToCursorOnNextRead; /* On the next read we need to seek to the frame cursor. */ + MA_ATOMIC(4, ma_result) result; /* Keeps track of a result of decoding. Set to MA_BUSY while the buffer is still loading. Set to MA_SUCCESS when loading is finished successfully. Otherwise set to some other code. */ + MA_ATOMIC(4, ma_bool32) isLooping; /* Can be read and written by different threads at the same time. Must be used atomically. */ + ma_bool32 isConnectorInitialized; /* Used for asynchronous loading to ensure we don't try to initialize the connector multiple times while waiting for the node to fully load. */ + union + { + ma_decoder decoder; /* Supply type is ma_resource_manager_data_supply_type_encoded */ + ma_audio_buffer buffer; /* Supply type is ma_resource_manager_data_supply_type_decoded */ + ma_paged_audio_buffer pagedBuffer; /* Supply type is ma_resource_manager_data_supply_type_decoded_paged */ + } connector; /* Connects this object to the node's data supply. */ +}; + +struct ma_resource_manager_data_stream +{ + ma_data_source_base ds; /* Base data source. A data stream is a data source. */ + ma_resource_manager* pResourceManager; /* A pointer to the resource manager that owns this data stream. */ + ma_uint32 flags; /* The flags that were passed used to initialize the stream. */ + ma_decoder decoder; /* Used for filling pages with data. This is only ever accessed by the job thread. The public API should never touch this. */ + ma_bool32 isDecoderInitialized; /* Required for determining whether or not the decoder should be uninitialized in MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM. */ + ma_uint64 totalLengthInPCMFrames; /* This is calculated when first loaded by the MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM. */ + ma_uint32 relativeCursor; /* The playback cursor, relative to the current page. Only ever accessed by the public API. Never accessed by the job thread. */ + MA_ATOMIC(8, ma_uint64) absoluteCursor; /* The playback cursor, in absolute position starting from the start of the file. */ + ma_uint32 currentPageIndex; /* Toggles between 0 and 1. Index 0 is the first half of pPageData. Index 1 is the second half. Only ever accessed by the public API. Never accessed by the job thread. */ + MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ + MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ + + /* Written by the public API, read by the job thread. */ + MA_ATOMIC(4, ma_bool32) isLooping; /* Whether or not the stream is looping. It's important to set the looping flag at the data stream level for smooth loop transitions. */ + + /* Written by the job thread, read by the public API. */ + void* pPageData; /* Buffer containing the decoded data of each page. Allocated once at initialization time. */ + MA_ATOMIC(4, ma_uint32) pageFrameCount[2]; /* The number of valid PCM frames in each page. Used to determine the last valid frame. */ + + /* Written and read by both the public API and the job thread. These must be atomic. */ + MA_ATOMIC(4, ma_result) result; /* Result from asynchronous loading. When loading set to MA_BUSY. When initialized set to MA_SUCCESS. When deleting set to MA_UNAVAILABLE. If an error occurs when loading, set to an error code. */ + MA_ATOMIC(4, ma_bool32) isDecoderAtEnd; /* Whether or not the decoder has reached the end. */ + MA_ATOMIC(4, ma_bool32) isPageValid[2]; /* Booleans to indicate whether or not a page is valid. Set to false by the public API, set to true by the job thread. Set to false as the pages are consumed, true when they are filled. */ + MA_ATOMIC(4, ma_bool32) seekCounter; /* When 0, no seeking is being performed. When > 0, a seek is being performed and reading should be delayed with MA_BUSY. */ +}; + +struct ma_resource_manager_data_source +{ + union + { + ma_resource_manager_data_buffer buffer; + ma_resource_manager_data_stream stream; + } backend; /* Must be the first item because we need the first item to be the data source callbacks for the buffer or stream. */ + + ma_uint32 flags; /* The flags that were passed in to ma_resource_manager_data_source_init(). */ + MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ + MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ +}; + +typedef struct +{ + ma_allocation_callbacks allocationCallbacks; + ma_log* pLog; + ma_format decodedFormat; /* The decoded format to use. Set to ma_format_unknown (default) to use the file's native format. */ + ma_uint32 decodedChannels; /* The decoded channel count to use. Set to 0 (default) to use the file's native channel count. */ + ma_uint32 decodedSampleRate; /* the decoded sample rate to use. Set to 0 (default) to use the file's native sample rate. */ + ma_uint32 jobThreadCount; /* Set to 0 if you want to self-manage your job threads. Defaults to 1. */ + ma_uint32 jobQueueCapacity; /* The maximum number of jobs that can fit in the queue at a time. Defaults to MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY. Cannot be zero. */ + ma_uint32 flags; + ma_vfs* pVFS; /* Can be NULL in which case defaults will be used. */ + ma_decoding_backend_vtable** ppCustomDecodingBackendVTables; + ma_uint32 customDecodingBackendCount; + void* pCustomDecodingBackendUserData; +} ma_resource_manager_config; + +MA_API ma_resource_manager_config ma_resource_manager_config_init(void); + +struct ma_resource_manager +{ + ma_resource_manager_config config; + ma_resource_manager_data_buffer_node* pRootDataBufferNode; /* The root buffer in the binary tree. */ +#ifndef MA_NO_THREADING + ma_mutex dataBufferBSTLock; /* For synchronizing access to the data buffer binary tree. */ + ma_thread jobThreads[MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT]; /* The threads for executing jobs. */ +#endif + ma_job_queue jobQueue; /* Multi-consumer, multi-producer job queue for managing jobs for asynchronous decoding and streaming. */ + ma_default_vfs defaultVFS; /* Only used if a custom VFS is not specified. */ + ma_log log; /* Only used if no log was specified in the config. */ +}; + +/* Init. */ +MA_API ma_result ma_resource_manager_init(const ma_resource_manager_config* pConfig, ma_resource_manager* pResourceManager); +MA_API void ma_resource_manager_uninit(ma_resource_manager* pResourceManager); +MA_API ma_log* ma_resource_manager_get_log(ma_resource_manager* pResourceManager); + +/* Registration. */ +MA_API ma_result ma_resource_manager_register_file(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags); +MA_API ma_result ma_resource_manager_register_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags); +MA_API ma_result ma_resource_manager_register_decoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate); /* Does not copy. Increments the reference count if already exists and returns MA_SUCCESS. */ +MA_API ma_result ma_resource_manager_register_decoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate); +MA_API ma_result ma_resource_manager_register_encoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, size_t sizeInBytes); /* Does not copy. Increments the reference count if already exists and returns MA_SUCCESS. */ +MA_API ma_result ma_resource_manager_register_encoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, size_t sizeInBytes); +MA_API ma_result ma_resource_manager_unregister_file(ma_resource_manager* pResourceManager, const char* pFilePath); +MA_API ma_result ma_resource_manager_unregister_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath); +MA_API ma_result ma_resource_manager_unregister_data(ma_resource_manager* pResourceManager, const char* pName); +MA_API ma_result ma_resource_manager_unregister_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName); + +/* Data Buffers. */ +MA_API ma_result ma_resource_manager_data_buffer_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_buffer* pDataBuffer); +MA_API ma_result ma_resource_manager_data_buffer_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer); +MA_API ma_result ma_resource_manager_data_buffer_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer); +MA_API ma_result ma_resource_manager_data_buffer_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_buffer* pExistingDataBuffer, ma_resource_manager_data_buffer* pDataBuffer); +MA_API ma_result ma_resource_manager_data_buffer_uninit(ma_resource_manager_data_buffer* pDataBuffer); +MA_API ma_result ma_resource_manager_data_buffer_read_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); +MA_API ma_result ma_resource_manager_data_buffer_seek_to_pcm_frame(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64 frameIndex); +MA_API ma_result ma_resource_manager_data_buffer_get_data_format(ma_resource_manager_data_buffer* pDataBuffer, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); +MA_API ma_result ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pCursor); +MA_API ma_result ma_resource_manager_data_buffer_get_length_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pLength); +MA_API ma_result ma_resource_manager_data_buffer_result(const ma_resource_manager_data_buffer* pDataBuffer); +MA_API ma_result ma_resource_manager_data_buffer_set_looping(ma_resource_manager_data_buffer* pDataBuffer, ma_bool32 isLooping); +MA_API ma_bool32 ma_resource_manager_data_buffer_is_looping(const ma_resource_manager_data_buffer* pDataBuffer); +MA_API ma_result ma_resource_manager_data_buffer_get_available_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pAvailableFrames); + +/* Data Streams. */ +MA_API ma_result ma_resource_manager_data_stream_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_stream* pDataStream); +MA_API ma_result ma_resource_manager_data_stream_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream); +MA_API ma_result ma_resource_manager_data_stream_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream); +MA_API ma_result ma_resource_manager_data_stream_uninit(ma_resource_manager_data_stream* pDataStream); +MA_API ma_result ma_resource_manager_data_stream_read_pcm_frames(ma_resource_manager_data_stream* pDataStream, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); +MA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameIndex); +MA_API ma_result ma_resource_manager_data_stream_get_data_format(ma_resource_manager_data_stream* pDataStream, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); +MA_API ma_result ma_resource_manager_data_stream_get_cursor_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pCursor); +MA_API ma_result ma_resource_manager_data_stream_get_length_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pLength); +MA_API ma_result ma_resource_manager_data_stream_result(const ma_resource_manager_data_stream* pDataStream); +MA_API ma_result ma_resource_manager_data_stream_set_looping(ma_resource_manager_data_stream* pDataStream, ma_bool32 isLooping); +MA_API ma_bool32 ma_resource_manager_data_stream_is_looping(const ma_resource_manager_data_stream* pDataStream); +MA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pAvailableFrames); + +/* Data Sources. */ +MA_API ma_result ma_resource_manager_data_source_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource); +MA_API ma_result ma_resource_manager_data_source_init(ma_resource_manager* pResourceManager, const char* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource); +MA_API ma_result ma_resource_manager_data_source_init_w(ma_resource_manager* pResourceManager, const wchar_t* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource); +MA_API ma_result ma_resource_manager_data_source_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source* pExistingDataSource, ma_resource_manager_data_source* pDataSource); +MA_API ma_result ma_resource_manager_data_source_uninit(ma_resource_manager_data_source* pDataSource); +MA_API ma_result ma_resource_manager_data_source_read_pcm_frames(ma_resource_manager_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); +MA_API ma_result ma_resource_manager_data_source_seek_to_pcm_frame(ma_resource_manager_data_source* pDataSource, ma_uint64 frameIndex); +MA_API ma_result ma_resource_manager_data_source_get_data_format(ma_resource_manager_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); +MA_API ma_result ma_resource_manager_data_source_get_cursor_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pCursor); +MA_API ma_result ma_resource_manager_data_source_get_length_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pLength); +MA_API ma_result ma_resource_manager_data_source_result(const ma_resource_manager_data_source* pDataSource); +MA_API ma_result ma_resource_manager_data_source_set_looping(ma_resource_manager_data_source* pDataSource, ma_bool32 isLooping); +MA_API ma_bool32 ma_resource_manager_data_source_is_looping(const ma_resource_manager_data_source* pDataSource); +MA_API ma_result ma_resource_manager_data_source_get_available_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pAvailableFrames); + +/* Job management. */ +MA_API ma_result ma_resource_manager_post_job(ma_resource_manager* pResourceManager, const ma_job* pJob); +MA_API ma_result ma_resource_manager_post_job_quit(ma_resource_manager* pResourceManager); /* Helper for posting a quit job. */ +MA_API ma_result ma_resource_manager_next_job(ma_resource_manager* pResourceManager, ma_job* pJob); +MA_API ma_result ma_resource_manager_process_job(ma_resource_manager* pResourceManager, ma_job* pJob); /* DEPRECATED. Use ma_job_process(). Will be removed in version 0.12. */ +MA_API ma_result ma_resource_manager_process_next_job(ma_resource_manager* pResourceManager); /* Returns MA_CANCELLED if a MA_JOB_TYPE_QUIT job is found. In non-blocking mode, returns MA_NO_DATA_AVAILABLE if no jobs are available. */ +#endif /* MA_NO_RESOURCE_MANAGER */ + + + +/************************************************************************************************************************************************************ + +Node Graph + +************************************************************************************************************************************************************/ +#ifndef MA_NO_NODE_GRAPH +/* Must never exceed 254. */ +#ifndef MA_MAX_NODE_BUS_COUNT +#define MA_MAX_NODE_BUS_COUNT 254 +#endif + +/* Used internally by miniaudio for memory management. Must never exceed MA_MAX_NODE_BUS_COUNT. */ +#ifndef MA_MAX_NODE_LOCAL_BUS_COUNT +#define MA_MAX_NODE_LOCAL_BUS_COUNT 2 +#endif + +/* Use this when the bus count is determined by the node instance rather than the vtable. */ +#define MA_NODE_BUS_COUNT_UNKNOWN 255 + +typedef struct ma_node_graph ma_node_graph; +typedef void ma_node; + + +/* Node flags. */ +typedef enum +{ + MA_NODE_FLAG_PASSTHROUGH = 0x00000001, + MA_NODE_FLAG_CONTINUOUS_PROCESSING = 0x00000002, + MA_NODE_FLAG_ALLOW_NULL_INPUT = 0x00000004, + MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES = 0x00000008, + MA_NODE_FLAG_SILENT_OUTPUT = 0x00000010 +} ma_node_flags; + + +/* The playback state of a node. Either started or stopped. */ +typedef enum +{ + ma_node_state_started = 0, + ma_node_state_stopped = 1 +} ma_node_state; + + +typedef struct +{ + /* + Extended processing callback. This callback is used for effects that process input and output + at different rates (i.e. they perform resampling). This is similar to the simple version, only + they take two seperate frame counts: one for input, and one for output. + + On input, `pFrameCountOut` is equal to the capacity of the output buffer for each bus, whereas + `pFrameCountIn` will be equal to the number of PCM frames in each of the buffers in `ppFramesIn`. + + On output, set `pFrameCountOut` to the number of PCM frames that were actually output and set + `pFrameCountIn` to the number of input frames that were consumed. + */ + void (* onProcess)(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut); + + /* + A callback for retrieving the number of a input frames that are required to output the + specified number of output frames. You would only want to implement this when the node performs + resampling. This is optional, even for nodes that perform resampling, but it does offer a + small reduction in latency as it allows miniaudio to calculate the exact number of input frames + to read at a time instead of having to estimate. + */ + ma_result (* onGetRequiredInputFrameCount)(ma_node* pNode, ma_uint32 outputFrameCount, ma_uint32* pInputFrameCount); + + /* + The number of input buses. This is how many sub-buffers will be contained in the `ppFramesIn` + parameters of the callbacks above. + */ + ma_uint8 inputBusCount; + + /* + The number of output buses. This is how many sub-buffers will be contained in the `ppFramesOut` + parameters of the callbacks above. + */ + ma_uint8 outputBusCount; + + /* + Flags describing characteristics of the node. This is currently just a placeholder for some + ideas for later on. + */ + ma_uint32 flags; +} ma_node_vtable; + +typedef struct +{ + const ma_node_vtable* vtable; /* Should never be null. Initialization of the node will fail if so. */ + ma_node_state initialState; /* Defaults to ma_node_state_started. */ + ma_uint32 inputBusCount; /* Only used if the vtable specifies an input bus count of `MA_NODE_BUS_COUNT_UNKNOWN`, otherwise must be set to `MA_NODE_BUS_COUNT_UNKNOWN` (default). */ + ma_uint32 outputBusCount; /* Only used if the vtable specifies an output bus count of `MA_NODE_BUS_COUNT_UNKNOWN`, otherwise be set to `MA_NODE_BUS_COUNT_UNKNOWN` (default). */ + const ma_uint32* pInputChannels; /* The number of elements are determined by the input bus count as determined by the vtable, or `inputBusCount` if the vtable specifies `MA_NODE_BUS_COUNT_UNKNOWN`. */ + const ma_uint32* pOutputChannels; /* The number of elements are determined by the output bus count as determined by the vtable, or `outputBusCount` if the vtable specifies `MA_NODE_BUS_COUNT_UNKNOWN`. */ +} ma_node_config; + +MA_API ma_node_config ma_node_config_init(void); + + +/* +A node has multiple output buses. An output bus is attached to an input bus as an item in a linked +list. Think of the input bus as a linked list, with the output bus being an item in that list. +*/ +typedef struct ma_node_output_bus ma_node_output_bus; +struct ma_node_output_bus +{ + /* Immutable. */ + ma_node* pNode; /* The node that owns this output bus. The input node. Will be null for dummy head and tail nodes. */ + ma_uint8 outputBusIndex; /* The index of the output bus on pNode that this output bus represents. */ + ma_uint8 channels; /* The number of channels in the audio stream for this bus. */ + + /* Mutable via multiple threads. Must be used atomically. The weird ordering here is for packing reasons. */ + MA_ATOMIC(1, ma_uint8) inputNodeInputBusIndex; /* The index of the input bus on the input. Required for detaching. */ + MA_ATOMIC(4, ma_uint32) flags; /* Some state flags for tracking the read state of the output buffer. A combination of MA_NODE_OUTPUT_BUS_FLAG_*. */ + MA_ATOMIC(4, ma_uint32) refCount; /* Reference count for some thread-safety when detaching. */ + MA_ATOMIC(4, ma_bool32) isAttached; /* This is used to prevent iteration of nodes that are in the middle of being detached. Used for thread safety. */ + MA_ATOMIC(4, ma_spinlock) lock; /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */ + MA_ATOMIC(4, float) volume; /* Linear. */ + MA_ATOMIC(MA_SIZEOF_PTR, ma_node_output_bus*) pNext; /* If null, it's the tail node or detached. */ + MA_ATOMIC(MA_SIZEOF_PTR, ma_node_output_bus*) pPrev; /* If null, it's the head node or detached. */ + MA_ATOMIC(MA_SIZEOF_PTR, ma_node*) pInputNode; /* The node that this output bus is attached to. Required for detaching. */ +}; + +/* +A node has multiple input buses. The output buses of a node are connecting to the input busses of +another. An input bus is essentially just a linked list of output buses. +*/ +typedef struct ma_node_input_bus ma_node_input_bus; +struct ma_node_input_bus +{ + /* Mutable via multiple threads. */ + ma_node_output_bus head; /* Dummy head node for simplifying some lock-free thread-safety stuff. */ + MA_ATOMIC(4, ma_uint32) nextCounter; /* This is used to determine whether or not the input bus is finding the next node in the list. Used for thread safety when detaching output buses. */ + MA_ATOMIC(4, ma_spinlock) lock; /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */ + + /* Set once at startup. */ + ma_uint8 channels; /* The number of channels in the audio stream for this bus. */ +}; + + +typedef struct ma_node_base ma_node_base; +struct ma_node_base +{ + /* These variables are set once at startup. */ + ma_node_graph* pNodeGraph; /* The graph this node belongs to. */ + const ma_node_vtable* vtable; + float* pCachedData; /* Allocated on the heap. Fixed size. Needs to be stored on the heap because reading from output buses is done in separate function calls. */ + ma_uint16 cachedDataCapInFramesPerBus; /* The capacity of the input data cache in frames, per bus. */ + + /* These variables are read and written only from the audio thread. */ + ma_uint16 cachedFrameCountOut; + ma_uint16 cachedFrameCountIn; + ma_uint16 consumedFrameCountIn; + + /* These variables are read and written between different threads. */ + MA_ATOMIC(4, ma_node_state) state; /* When set to stopped, nothing will be read, regardless of the times in stateTimes. */ + MA_ATOMIC(8, ma_uint64) stateTimes[2]; /* Indexed by ma_node_state. Specifies the time based on the global clock that a node should be considered to be in the relevant state. */ + MA_ATOMIC(8, ma_uint64) localTime; /* The node's local clock. This is just a running sum of the number of output frames that have been processed. Can be modified by any thread with `ma_node_set_time()`. */ + ma_uint32 inputBusCount; + ma_uint32 outputBusCount; + ma_node_input_bus* pInputBuses; + ma_node_output_bus* pOutputBuses; + + /* Memory management. */ + ma_node_input_bus _inputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT]; + ma_node_output_bus _outputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT]; + void* _pHeap; /* A heap allocation for internal use only. pInputBuses and/or pOutputBuses will point to this if the bus count exceeds MA_MAX_NODE_LOCAL_BUS_COUNT. */ + ma_bool32 _ownsHeap; /* If set to true, the node owns the heap allocation and _pHeap will be freed in ma_node_uninit(). */ +}; + +MA_API ma_result ma_node_get_heap_size(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, void* pHeap, ma_node* pNode); +MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode); +MA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_node_graph* ma_node_get_node_graph(const ma_node* pNode); +MA_API ma_uint32 ma_node_get_input_bus_count(const ma_node* pNode); +MA_API ma_uint32 ma_node_get_output_bus_count(const ma_node* pNode); +MA_API ma_uint32 ma_node_get_input_channels(const ma_node* pNode, ma_uint32 inputBusIndex); +MA_API ma_uint32 ma_node_get_output_channels(const ma_node* pNode, ma_uint32 outputBusIndex); +MA_API ma_result ma_node_attach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex, ma_node* pOtherNode, ma_uint32 otherNodeInputBusIndex); +MA_API ma_result ma_node_detach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex); +MA_API ma_result ma_node_detach_all_output_buses(ma_node* pNode); +MA_API ma_result ma_node_set_output_bus_volume(ma_node* pNode, ma_uint32 outputBusIndex, float volume); +MA_API float ma_node_get_output_bus_volume(const ma_node* pNode, ma_uint32 outputBusIndex); +MA_API ma_result ma_node_set_state(ma_node* pNode, ma_node_state state); +MA_API ma_node_state ma_node_get_state(const ma_node* pNode); +MA_API ma_result ma_node_set_state_time(ma_node* pNode, ma_node_state state, ma_uint64 globalTime); +MA_API ma_uint64 ma_node_get_state_time(const ma_node* pNode, ma_node_state state); +MA_API ma_node_state ma_node_get_state_by_time(const ma_node* pNode, ma_uint64 globalTime); +MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_uint64 globalTimeBeg, ma_uint64 globalTimeEnd); +MA_API ma_uint64 ma_node_get_time(const ma_node* pNode); +MA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime); + + +typedef struct +{ + ma_uint32 channels; + ma_uint16 nodeCacheCapInFrames; +} ma_node_graph_config; + +MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels); + + +struct ma_node_graph +{ + /* Immutable. */ + ma_node_base base; /* The node graph itself is a node so it can be connected as an input to different node graph. This has zero inputs and calls ma_node_graph_read_pcm_frames() to generate it's output. */ + ma_node_base endpoint; /* Special node that all nodes eventually connect to. Data is read from this node in ma_node_graph_read_pcm_frames(). */ + ma_uint16 nodeCacheCapInFrames; + + /* Read and written by multiple threads. */ + MA_ATOMIC(4, ma_bool32) isReading; +}; + +MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node_graph* pNodeGraph); +MA_API void ma_node_graph_uninit(ma_node_graph* pNodeGraph, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_node* ma_node_graph_get_endpoint(ma_node_graph* pNodeGraph); +MA_API ma_result ma_node_graph_read_pcm_frames(ma_node_graph* pNodeGraph, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); +MA_API ma_uint32 ma_node_graph_get_channels(const ma_node_graph* pNodeGraph); +MA_API ma_uint64 ma_node_graph_get_time(const ma_node_graph* pNodeGraph); +MA_API ma_result ma_node_graph_set_time(ma_node_graph* pNodeGraph, ma_uint64 globalTime); + + + +/* Data source node. 0 input buses, 1 output bus. Used for reading from a data source. */ +typedef struct +{ + ma_node_config nodeConfig; + ma_data_source* pDataSource; +} ma_data_source_node_config; + +MA_API ma_data_source_node_config ma_data_source_node_config_init(ma_data_source* pDataSource); + + +typedef struct +{ + ma_node_base base; + ma_data_source* pDataSource; +} ma_data_source_node; + +MA_API ma_result ma_data_source_node_init(ma_node_graph* pNodeGraph, const ma_data_source_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source_node* pDataSourceNode); +MA_API void ma_data_source_node_uninit(ma_data_source_node* pDataSourceNode, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_data_source_node_set_looping(ma_data_source_node* pDataSourceNode, ma_bool32 isLooping); +MA_API ma_bool32 ma_data_source_node_is_looping(ma_data_source_node* pDataSourceNode); + + +/* Splitter Node. 1 input, 2 outputs. Used for splitting/copying a stream so it can be as input into two separate output nodes. */ +typedef struct +{ + ma_node_config nodeConfig; + ma_uint32 channels; +} ma_splitter_node_config; + +MA_API ma_splitter_node_config ma_splitter_node_config_init(ma_uint32 channels); + + +typedef struct +{ + ma_node_base base; +} ma_splitter_node; + +MA_API ma_result ma_splitter_node_init(ma_node_graph* pNodeGraph, const ma_splitter_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_splitter_node* pSplitterNode); +MA_API void ma_splitter_node_uninit(ma_splitter_node* pSplitterNode, const ma_allocation_callbacks* pAllocationCallbacks); + + +/* +Biquad Node +*/ +typedef struct +{ + ma_node_config nodeConfig; + ma_biquad_config biquad; +} ma_biquad_node_config; + +MA_API ma_biquad_node_config ma_biquad_node_config_init(ma_uint32 channels, float b0, float b1, float b2, float a0, float a1, float a2); + + +typedef struct +{ + ma_node_base baseNode; + ma_biquad biquad; +} ma_biquad_node; + +MA_API ma_result ma_biquad_node_init(ma_node_graph* pNodeGraph, const ma_biquad_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad_node* pNode); +MA_API ma_result ma_biquad_node_reinit(const ma_biquad_config* pConfig, ma_biquad_node* pNode); +MA_API void ma_biquad_node_uninit(ma_biquad_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); + + +/* +Low Pass Filter Node +*/ +typedef struct +{ + ma_node_config nodeConfig; + ma_lpf_config lpf; +} ma_lpf_node_config; + +MA_API ma_lpf_node_config ma_lpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); + + +typedef struct +{ + ma_node_base baseNode; + ma_lpf lpf; +} ma_lpf_node; + +MA_API ma_result ma_lpf_node_init(ma_node_graph* pNodeGraph, const ma_lpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf_node* pNode); +MA_API ma_result ma_lpf_node_reinit(const ma_lpf_config* pConfig, ma_lpf_node* pNode); +MA_API void ma_lpf_node_uninit(ma_lpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); + + +/* +High Pass Filter Node +*/ +typedef struct +{ + ma_node_config nodeConfig; + ma_hpf_config hpf; +} ma_hpf_node_config; + +MA_API ma_hpf_node_config ma_hpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); + + +typedef struct +{ + ma_node_base baseNode; + ma_hpf hpf; +} ma_hpf_node; + +MA_API ma_result ma_hpf_node_init(ma_node_graph* pNodeGraph, const ma_hpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf_node* pNode); +MA_API ma_result ma_hpf_node_reinit(const ma_hpf_config* pConfig, ma_hpf_node* pNode); +MA_API void ma_hpf_node_uninit(ma_hpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); + + +/* +Band Pass Filter Node +*/ +typedef struct +{ + ma_node_config nodeConfig; + ma_bpf_config bpf; +} ma_bpf_node_config; + +MA_API ma_bpf_node_config ma_bpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); + + +typedef struct +{ + ma_node_base baseNode; + ma_bpf bpf; +} ma_bpf_node; + +MA_API ma_result ma_bpf_node_init(ma_node_graph* pNodeGraph, const ma_bpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf_node* pNode); +MA_API ma_result ma_bpf_node_reinit(const ma_bpf_config* pConfig, ma_bpf_node* pNode); +MA_API void ma_bpf_node_uninit(ma_bpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); + + +/* +Notching Filter Node +*/ +typedef struct +{ + ma_node_config nodeConfig; + ma_notch_config notch; +} ma_notch_node_config; + +MA_API ma_notch_node_config ma_notch_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency); + + +typedef struct +{ + ma_node_base baseNode; + ma_notch2 notch; +} ma_notch_node; + +MA_API ma_result ma_notch_node_init(ma_node_graph* pNodeGraph, const ma_notch_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch_node* pNode); +MA_API ma_result ma_notch_node_reinit(const ma_notch_config* pConfig, ma_notch_node* pNode); +MA_API void ma_notch_node_uninit(ma_notch_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); + + +/* +Peaking Filter Node +*/ +typedef struct +{ + ma_node_config nodeConfig; + ma_peak_config peak; +} ma_peak_node_config; + +MA_API ma_peak_node_config ma_peak_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency); + + +typedef struct +{ + ma_node_base baseNode; + ma_peak2 peak; +} ma_peak_node; + +MA_API ma_result ma_peak_node_init(ma_node_graph* pNodeGraph, const ma_peak_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak_node* pNode); +MA_API ma_result ma_peak_node_reinit(const ma_peak_config* pConfig, ma_peak_node* pNode); +MA_API void ma_peak_node_uninit(ma_peak_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); + + +/* +Low Shelf Filter Node +*/ +typedef struct +{ + ma_node_config nodeConfig; + ma_loshelf_config loshelf; +} ma_loshelf_node_config; + +MA_API ma_loshelf_node_config ma_loshelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency); + + +typedef struct +{ + ma_node_base baseNode; + ma_loshelf2 loshelf; +} ma_loshelf_node; + +MA_API ma_result ma_loshelf_node_init(ma_node_graph* pNodeGraph, const ma_loshelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf_node* pNode); +MA_API ma_result ma_loshelf_node_reinit(const ma_loshelf_config* pConfig, ma_loshelf_node* pNode); +MA_API void ma_loshelf_node_uninit(ma_loshelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); + + +/* +High Shelf Filter Node +*/ +typedef struct +{ + ma_node_config nodeConfig; + ma_hishelf_config hishelf; +} ma_hishelf_node_config; + +MA_API ma_hishelf_node_config ma_hishelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency); + + +typedef struct +{ + ma_node_base baseNode; + ma_hishelf2 hishelf; +} ma_hishelf_node; + +MA_API ma_result ma_hishelf_node_init(ma_node_graph* pNodeGraph, const ma_hishelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf_node* pNode); +MA_API ma_result ma_hishelf_node_reinit(const ma_hishelf_config* pConfig, ma_hishelf_node* pNode); +MA_API void ma_hishelf_node_uninit(ma_hishelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); + + +typedef struct +{ + ma_node_config nodeConfig; + ma_delay_config delay; +} ma_delay_node_config; + +MA_API ma_delay_node_config ma_delay_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay); + + +typedef struct +{ + ma_node_base baseNode; + ma_delay delay; +} ma_delay_node; + +MA_API ma_result ma_delay_node_init(ma_node_graph* pNodeGraph, const ma_delay_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay_node* pDelayNode); +MA_API void ma_delay_node_uninit(ma_delay_node* pDelayNode, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API void ma_delay_node_set_wet(ma_delay_node* pDelayNode, float value); +MA_API float ma_delay_node_get_wet(const ma_delay_node* pDelayNode); +MA_API void ma_delay_node_set_dry(ma_delay_node* pDelayNode, float value); +MA_API float ma_delay_node_get_dry(const ma_delay_node* pDelayNode); +MA_API void ma_delay_node_set_decay(ma_delay_node* pDelayNode, float value); +MA_API float ma_delay_node_get_decay(const ma_delay_node* pDelayNode); +#endif /* MA_NO_NODE_GRAPH */ + + +/************************************************************************************************************************************************************ + +Engine + +************************************************************************************************************************************************************/ +#if !defined(MA_NO_ENGINE) && !defined(MA_NO_NODE_GRAPH) +typedef struct ma_engine ma_engine; +typedef struct ma_sound ma_sound; + + +/* Sound flags. */ +typedef enum +{ + MA_SOUND_FLAG_STREAM = 0x00000001, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM */ + MA_SOUND_FLAG_DECODE = 0x00000002, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE */ + MA_SOUND_FLAG_ASYNC = 0x00000004, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC */ + MA_SOUND_FLAG_WAIT_INIT = 0x00000008, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT */ + MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT = 0x00000010, /* Do not attach to the endpoint by default. Useful for when setting up nodes in a complex graph system. */ + MA_SOUND_FLAG_NO_PITCH = 0x00000020, /* Disable pitch shifting with ma_sound_set_pitch() and ma_sound_group_set_pitch(). This is an optimization. */ + MA_SOUND_FLAG_NO_SPATIALIZATION = 0x00000040 /* Disable spatialization. */ +} ma_sound_flags; + +#ifndef MA_ENGINE_MAX_LISTENERS +#define MA_ENGINE_MAX_LISTENERS 4 +#endif + +#define MA_LISTENER_INDEX_CLOSEST ((ma_uint8)-1) + +typedef enum +{ + ma_engine_node_type_sound, + ma_engine_node_type_group +} ma_engine_node_type; + +typedef struct +{ + ma_engine* pEngine; + ma_engine_node_type type; + ma_uint32 channelsIn; + ma_uint32 channelsOut; + ma_uint32 sampleRate; /* Only used when the type is set to ma_engine_node_type_sound. */ + ma_bool8 isPitchDisabled; /* Pitching can be explicitly disable with MA_SOUND_FLAG_NO_PITCH to optimize processing. */ + ma_bool8 isSpatializationDisabled; /* Spatialization can be explicitly disabled with MA_SOUND_FLAG_NO_SPATIALIZATION. */ + ma_uint8 pinnedListenerIndex; /* The index of the listener this node should always use for spatialization. If set to MA_LISTENER_INDEX_CLOSEST the engine will use the closest listener. */ +} ma_engine_node_config; + +MA_API ma_engine_node_config ma_engine_node_config_init(ma_engine* pEngine, ma_engine_node_type type, ma_uint32 flags); + + +/* Base node object for both ma_sound and ma_sound_group. */ +typedef struct +{ + ma_node_base baseNode; /* Must be the first member for compatiblity with the ma_node API. */ + ma_engine* pEngine; /* A pointer to the engine. Set based on the value from the config. */ + ma_uint32 sampleRate; /* The sample rate of the input data. For sounds backed by a data source, this will be the data source's sample rate. Otherwise it'll be the engine's sample rate. */ + ma_fader fader; + ma_linear_resampler resampler; /* For pitch shift. */ + ma_spatializer spatializer; + ma_panner panner; + MA_ATOMIC(4, float) pitch; + float oldPitch; /* For determining whether or not the resampler needs to be updated to reflect the new pitch. The resampler will be updated on the mixing thread. */ + float oldDopplerPitch; /* For determining whether or not the resampler needs to be updated to take a new doppler pitch into account. */ + MA_ATOMIC(4, ma_bool32) isPitchDisabled; /* When set to true, pitching will be disabled which will allow the resampler to be bypassed to save some computation. */ + MA_ATOMIC(4, ma_bool32) isSpatializationDisabled; /* Set to false by default. When set to false, will not have spatialisation applied. */ + MA_ATOMIC(4, ma_uint32) pinnedListenerIndex; /* The index of the listener this node should always use for spatialization. If set to MA_LISTENER_INDEX_CLOSEST the engine will use the closest listener. */ + + /* Memory management. */ + ma_bool8 _ownsHeap; + void* _pHeap; +} ma_engine_node; + +MA_API ma_result ma_engine_node_get_heap_size(const ma_engine_node_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* pConfig, void* pHeap, ma_engine_node* pEngineNode); +MA_API ma_result ma_engine_node_init(const ma_engine_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_engine_node* pEngineNode); +MA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocation_callbacks* pAllocationCallbacks); + + +#define MA_SOUND_SOURCE_CHANNEL_COUNT 0xFFFFFFFF + +typedef struct +{ + const char* pFilePath; /* Set this to load from the resource manager. */ + const wchar_t* pFilePathW; /* Set this to load from the resource manager. */ + ma_data_source* pDataSource; /* Set this to load from an existing data source. */ + ma_node* pInitialAttachment; /* If set, the sound will be attached to an input of this node. This can be set to a ma_sound. If set to NULL, the sound will be attached directly to the endpoint unless MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT is set in `flags`. */ + ma_uint32 initialAttachmentInputBusIndex; /* The index of the input bus of pInitialAttachment to attach the sound to. */ + ma_uint32 channelsIn; /* Ignored if using a data source as input (the data source's channel count will be used always). Otherwise, setting to 0 will cause the engine's channel count to be used. */ + ma_uint32 channelsOut; /* Set this to 0 (default) to use the engine's channel count. Set to MA_SOUND_SOURCE_CHANNEL_COUNT to use the data source's channel count (only used if using a data source as input). */ + ma_uint32 flags; /* A combination of MA_SOUND_FLAG_* flags. */ + ma_uint64 initialSeekPointInPCMFrames; /* Initializes the sound such that it's seeked to this location by default. */ + ma_uint64 rangeBegInPCMFrames; + ma_uint64 rangeEndInPCMFrames; + ma_uint64 loopPointBegInPCMFrames; + ma_uint64 loopPointEndInPCMFrames; + ma_bool32 isLooping; + ma_fence* pDoneFence; /* Released when the resource manager has finished decoding the entire sound. Not used with streams. */ +} ma_sound_config; + +MA_API ma_sound_config ma_sound_config_init(void); + +struct ma_sound +{ + ma_engine_node engineNode; /* Must be the first member for compatibility with the ma_node API. */ + ma_data_source* pDataSource; + MA_ATOMIC(8, ma_uint64) seekTarget; /* The PCM frame index to seek to in the mixing thread. Set to (~(ma_uint64)0) to not perform any seeking. */ + MA_ATOMIC(4, ma_bool32) atEnd; + ma_bool8 ownsDataSource; + + /* + We're declaring a resource manager data source object here to save us a malloc when loading a + sound via the resource manager, which I *think* will be the most common scenario. + */ +#ifndef MA_NO_RESOURCE_MANAGER + ma_resource_manager_data_source* pResourceManagerDataSource; +#endif +}; + +/* Structure specifically for sounds played with ma_engine_play_sound(). Making this a separate structure to reduce overhead. */ +typedef struct ma_sound_inlined ma_sound_inlined; +struct ma_sound_inlined +{ + ma_sound sound; + ma_sound_inlined* pNext; + ma_sound_inlined* pPrev; +}; + +/* A sound group is just a sound. */ +typedef ma_sound_config ma_sound_group_config; +typedef ma_sound ma_sound_group; + +MA_API ma_sound_group_config ma_sound_group_config_init(void); + + +typedef struct +{ +#if !defined(MA_NO_RESOURCE_MANAGER) + ma_resource_manager* pResourceManager; /* Can be null in which case a resource manager will be created for you. */ +#endif +#if !defined(MA_NO_DEVICE_IO) + ma_context* pContext; + ma_device* pDevice; /* If set, the caller is responsible for calling ma_engine_data_callback() in the device's data callback. */ + ma_device_id* pPlaybackDeviceID; /* The ID of the playback device to use with the default listener. */ +#endif + ma_log* pLog; /* When set to NULL, will use the context's log. */ + ma_uint32 listenerCount; /* Must be between 1 and MA_ENGINE_MAX_LISTENERS. */ + ma_uint32 channels; /* The number of channels to use when mixing and spatializing. When set to 0, will use the native channel count of the device. */ + ma_uint32 sampleRate; /* The sample rate. When set to 0 will use the native channel count of the device. */ + ma_uint32 periodSizeInFrames; /* If set to something other than 0, updates will always be exactly this size. The underlying device may be a different size, but from the perspective of the mixer that won't matter.*/ + ma_uint32 periodSizeInMilliseconds; /* Used if periodSizeInFrames is unset. */ + ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. If set to 0, will use gainSmoothTimeInMilliseconds. */ + ma_uint32 gainSmoothTimeInMilliseconds; /* When set to 0, gainSmoothTimeInFrames will be used. If both are set to 0, a default value will be used. */ + ma_allocation_callbacks allocationCallbacks; + ma_bool32 noAutoStart; /* When set to true, requires an explicit call to ma_engine_start(). This is false by default, meaning the engine will be started automatically in ma_engine_init(). */ + ma_bool32 noDevice; /* When set to true, don't create a default device. ma_engine_read_pcm_frames() can be called manually to read data. */ + ma_mono_expansion_mode monoExpansionMode; /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */ + ma_vfs* pResourceManagerVFS; /* A pointer to a pre-allocated VFS object to use with the resource manager. This is ignored if pResourceManager is not NULL. */ +} ma_engine_config; + +MA_API ma_engine_config ma_engine_config_init(void); + + +struct ma_engine +{ + ma_node_graph nodeGraph; /* An engine is a node graph. It should be able to be plugged into any ma_node_graph API (with a cast) which means this must be the first member of this struct. */ +#if !defined(MA_NO_RESOURCE_MANAGER) + ma_resource_manager* pResourceManager; +#endif +#if !defined(MA_NO_DEVICE_IO) + ma_device* pDevice; /* Optionally set via the config, otherwise allocated by the engine in ma_engine_init(). */ +#endif + ma_log* pLog; + ma_uint32 sampleRate; + ma_uint32 listenerCount; + ma_spatializer_listener listeners[MA_ENGINE_MAX_LISTENERS]; + ma_allocation_callbacks allocationCallbacks; + ma_bool8 ownsResourceManager; + ma_bool8 ownsDevice; + ma_spinlock inlinedSoundLock; /* For synchronizing access so the inlined sound list. */ + ma_sound_inlined* pInlinedSoundHead; /* The first inlined sound. Inlined sounds are tracked in a linked list. */ + MA_ATOMIC(4, ma_uint32) inlinedSoundCount; /* The total number of allocated inlined sound objects. Used for debugging. */ + ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. */ + ma_mono_expansion_mode monoExpansionMode; +}; + +MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEngine); +MA_API void ma_engine_uninit(ma_engine* pEngine); +MA_API ma_result ma_engine_read_pcm_frames(ma_engine* pEngine, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); +MA_API ma_node_graph* ma_engine_get_node_graph(ma_engine* pEngine); +#if !defined(MA_NO_RESOURCE_MANAGER) +MA_API ma_resource_manager* ma_engine_get_resource_manager(ma_engine* pEngine); +#endif +MA_API ma_device* ma_engine_get_device(ma_engine* pEngine); +MA_API ma_log* ma_engine_get_log(ma_engine* pEngine); +MA_API ma_node* ma_engine_get_endpoint(ma_engine* pEngine); +MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine); +MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime); +MA_API ma_uint32 ma_engine_get_channels(const ma_engine* pEngine); +MA_API ma_uint32 ma_engine_get_sample_rate(const ma_engine* pEngine); + +MA_API ma_result ma_engine_start(ma_engine* pEngine); +MA_API ma_result ma_engine_stop(ma_engine* pEngine); +MA_API ma_result ma_engine_set_volume(ma_engine* pEngine, float volume); +MA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB); + +MA_API ma_uint32 ma_engine_get_listener_count(const ma_engine* pEngine); +MA_API ma_uint32 ma_engine_find_closest_listener(const ma_engine* pEngine, float absolutePosX, float absolutePosY, float absolutePosZ); +MA_API void ma_engine_listener_set_position(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); +MA_API ma_vec3f ma_engine_listener_get_position(const ma_engine* pEngine, ma_uint32 listenerIndex); +MA_API void ma_engine_listener_set_direction(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); +MA_API ma_vec3f ma_engine_listener_get_direction(const ma_engine* pEngine, ma_uint32 listenerIndex); +MA_API void ma_engine_listener_set_velocity(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); +MA_API ma_vec3f ma_engine_listener_get_velocity(const ma_engine* pEngine, ma_uint32 listenerIndex); +MA_API void ma_engine_listener_set_cone(ma_engine* pEngine, ma_uint32 listenerIndex, float innerAngleInRadians, float outerAngleInRadians, float outerGain); +MA_API void ma_engine_listener_get_cone(const ma_engine* pEngine, ma_uint32 listenerIndex, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); +MA_API void ma_engine_listener_set_world_up(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); +MA_API ma_vec3f ma_engine_listener_get_world_up(const ma_engine* pEngine, ma_uint32 listenerIndex); +MA_API void ma_engine_listener_set_enabled(ma_engine* pEngine, ma_uint32 listenerIndex, ma_bool32 isEnabled); +MA_API ma_bool32 ma_engine_listener_is_enabled(const ma_engine* pEngine, ma_uint32 listenerIndex); + +#ifndef MA_NO_RESOURCE_MANAGER +MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePath, ma_node* pNode, ma_uint32 nodeInputBusIndex); +MA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ma_sound_group* pGroup); /* Fire and forget. */ +#endif + +#ifndef MA_NO_RESOURCE_MANAGER +MA_API ma_result ma_sound_init_from_file(ma_engine* pEngine, const char* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound); +MA_API ma_result ma_sound_init_from_file_w(ma_engine* pEngine, const wchar_t* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound); +MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistingSound, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound); +#endif +MA_API ma_result ma_sound_init_from_data_source(ma_engine* pEngine, ma_data_source* pDataSource, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound); +MA_API ma_result ma_sound_init_ex(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound); +MA_API void ma_sound_uninit(ma_sound* pSound); +MA_API ma_engine* ma_sound_get_engine(const ma_sound* pSound); +MA_API ma_data_source* ma_sound_get_data_source(const ma_sound* pSound); +MA_API ma_result ma_sound_start(ma_sound* pSound); +MA_API ma_result ma_sound_stop(ma_sound* pSound); +MA_API void ma_sound_set_volume(ma_sound* pSound, float volume); +MA_API float ma_sound_get_volume(const ma_sound* pSound); +MA_API void ma_sound_set_pan(ma_sound* pSound, float pan); +MA_API float ma_sound_get_pan(const ma_sound* pSound); +MA_API void ma_sound_set_pan_mode(ma_sound* pSound, ma_pan_mode panMode); +MA_API ma_pan_mode ma_sound_get_pan_mode(const ma_sound* pSound); +MA_API void ma_sound_set_pitch(ma_sound* pSound, float pitch); +MA_API float ma_sound_get_pitch(const ma_sound* pSound); +MA_API void ma_sound_set_spatialization_enabled(ma_sound* pSound, ma_bool32 enabled); +MA_API ma_bool32 ma_sound_is_spatialization_enabled(const ma_sound* pSound); +MA_API void ma_sound_set_pinned_listener_index(ma_sound* pSound, ma_uint32 listenerIndex); +MA_API ma_uint32 ma_sound_get_pinned_listener_index(const ma_sound* pSound); +MA_API ma_uint32 ma_sound_get_listener_index(const ma_sound* pSound); +MA_API ma_vec3f ma_sound_get_direction_to_listener(const ma_sound* pSound); +MA_API void ma_sound_set_position(ma_sound* pSound, float x, float y, float z); +MA_API ma_vec3f ma_sound_get_position(const ma_sound* pSound); +MA_API void ma_sound_set_direction(ma_sound* pSound, float x, float y, float z); +MA_API ma_vec3f ma_sound_get_direction(const ma_sound* pSound); +MA_API void ma_sound_set_velocity(ma_sound* pSound, float x, float y, float z); +MA_API ma_vec3f ma_sound_get_velocity(const ma_sound* pSound); +MA_API void ma_sound_set_attenuation_model(ma_sound* pSound, ma_attenuation_model attenuationModel); +MA_API ma_attenuation_model ma_sound_get_attenuation_model(const ma_sound* pSound); +MA_API void ma_sound_set_positioning(ma_sound* pSound, ma_positioning positioning); +MA_API ma_positioning ma_sound_get_positioning(const ma_sound* pSound); +MA_API void ma_sound_set_rolloff(ma_sound* pSound, float rolloff); +MA_API float ma_sound_get_rolloff(const ma_sound* pSound); +MA_API void ma_sound_set_min_gain(ma_sound* pSound, float minGain); +MA_API float ma_sound_get_min_gain(const ma_sound* pSound); +MA_API void ma_sound_set_max_gain(ma_sound* pSound, float maxGain); +MA_API float ma_sound_get_max_gain(const ma_sound* pSound); +MA_API void ma_sound_set_min_distance(ma_sound* pSound, float minDistance); +MA_API float ma_sound_get_min_distance(const ma_sound* pSound); +MA_API void ma_sound_set_max_distance(ma_sound* pSound, float maxDistance); +MA_API float ma_sound_get_max_distance(const ma_sound* pSound); +MA_API void ma_sound_set_cone(ma_sound* pSound, float innerAngleInRadians, float outerAngleInRadians, float outerGain); +MA_API void ma_sound_get_cone(const ma_sound* pSound, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); +MA_API void ma_sound_set_doppler_factor(ma_sound* pSound, float dopplerFactor); +MA_API float ma_sound_get_doppler_factor(const ma_sound* pSound); +MA_API void ma_sound_set_directional_attenuation_factor(ma_sound* pSound, float directionalAttenuationFactor); +MA_API float ma_sound_get_directional_attenuation_factor(const ma_sound* pSound); +MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames); +MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds); +MA_API float ma_sound_get_current_fade_volume(ma_sound* pSound); +MA_API void ma_sound_set_start_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames); +MA_API void ma_sound_set_start_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds); +MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames); +MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds); +MA_API ma_bool32 ma_sound_is_playing(const ma_sound* pSound); +MA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound* pSound); +MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping); +MA_API ma_bool32 ma_sound_is_looping(const ma_sound* pSound); +MA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound); +MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameIndex); /* Just a wrapper around ma_data_source_seek_to_pcm_frame(). */ +MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); +MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor); +MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength); +MA_API ma_result ma_sound_get_cursor_in_seconds(ma_sound* pSound, float* pCursor); +MA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength); + +MA_API ma_result ma_sound_group_init(ma_engine* pEngine, ma_uint32 flags, ma_sound_group* pParentGroup, ma_sound_group* pGroup); +MA_API ma_result ma_sound_group_init_ex(ma_engine* pEngine, const ma_sound_group_config* pConfig, ma_sound_group* pGroup); +MA_API void ma_sound_group_uninit(ma_sound_group* pGroup); +MA_API ma_engine* ma_sound_group_get_engine(const ma_sound_group* pGroup); +MA_API ma_result ma_sound_group_start(ma_sound_group* pGroup); +MA_API ma_result ma_sound_group_stop(ma_sound_group* pGroup); +MA_API void ma_sound_group_set_volume(ma_sound_group* pGroup, float volume); +MA_API float ma_sound_group_get_volume(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_pan(ma_sound_group* pGroup, float pan); +MA_API float ma_sound_group_get_pan(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_pan_mode(ma_sound_group* pGroup, ma_pan_mode panMode); +MA_API ma_pan_mode ma_sound_group_get_pan_mode(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_pitch(ma_sound_group* pGroup, float pitch); +MA_API float ma_sound_group_get_pitch(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_spatialization_enabled(ma_sound_group* pGroup, ma_bool32 enabled); +MA_API ma_bool32 ma_sound_group_is_spatialization_enabled(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_pinned_listener_index(ma_sound_group* pGroup, ma_uint32 listenerIndex); +MA_API ma_uint32 ma_sound_group_get_pinned_listener_index(const ma_sound_group* pGroup); +MA_API ma_uint32 ma_sound_group_get_listener_index(const ma_sound_group* pGroup); +MA_API ma_vec3f ma_sound_group_get_direction_to_listener(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_position(ma_sound_group* pGroup, float x, float y, float z); +MA_API ma_vec3f ma_sound_group_get_position(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_direction(ma_sound_group* pGroup, float x, float y, float z); +MA_API ma_vec3f ma_sound_group_get_direction(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_velocity(ma_sound_group* pGroup, float x, float y, float z); +MA_API ma_vec3f ma_sound_group_get_velocity(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_attenuation_model(ma_sound_group* pGroup, ma_attenuation_model attenuationModel); +MA_API ma_attenuation_model ma_sound_group_get_attenuation_model(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_positioning(ma_sound_group* pGroup, ma_positioning positioning); +MA_API ma_positioning ma_sound_group_get_positioning(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_rolloff(ma_sound_group* pGroup, float rolloff); +MA_API float ma_sound_group_get_rolloff(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_min_gain(ma_sound_group* pGroup, float minGain); +MA_API float ma_sound_group_get_min_gain(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_max_gain(ma_sound_group* pGroup, float maxGain); +MA_API float ma_sound_group_get_max_gain(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_min_distance(ma_sound_group* pGroup, float minDistance); +MA_API float ma_sound_group_get_min_distance(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_max_distance(ma_sound_group* pGroup, float maxDistance); +MA_API float ma_sound_group_get_max_distance(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_cone(ma_sound_group* pGroup, float innerAngleInRadians, float outerAngleInRadians, float outerGain); +MA_API void ma_sound_group_get_cone(const ma_sound_group* pGroup, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); +MA_API void ma_sound_group_set_doppler_factor(ma_sound_group* pGroup, float dopplerFactor); +MA_API float ma_sound_group_get_doppler_factor(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_directional_attenuation_factor(ma_sound_group* pGroup, float directionalAttenuationFactor); +MA_API float ma_sound_group_get_directional_attenuation_factor(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_fade_in_pcm_frames(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames); +MA_API void ma_sound_group_set_fade_in_milliseconds(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds); +MA_API float ma_sound_group_get_current_fade_volume(ma_sound_group* pGroup); +MA_API void ma_sound_group_set_start_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames); +MA_API void ma_sound_group_set_start_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds); +MA_API void ma_sound_group_set_stop_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames); +MA_API void ma_sound_group_set_stop_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds); +MA_API ma_bool32 ma_sound_group_is_playing(const ma_sound_group* pGroup); +MA_API ma_uint64 ma_sound_group_get_time_in_pcm_frames(const ma_sound_group* pGroup); +#endif /* MA_NO_ENGINE */ + #ifdef __cplusplus } #endif #endif /* miniaudio_h */ +/* +This is for preventing greying out of the implementation section. +*/ +#if defined(Q_CREATOR_RUN) || defined(__INTELLISENSE__) || defined(__CDT_PARSER__) +#define MINIAUDIO_IMPLEMENTATION +#endif /************************************************************************************************************************************************************ ************************************************************************************************************************************************************* @@ -6552,6 +11051,9 @@ IMPLEMENTATION #include /* For strcasecmp(). */ #include /* For wcslen(), wcsrtombs() */ #endif +#ifdef _MSC_VER + #include /* For _controlfp_s constants */ +#endif #ifdef MA_WIN32 #include @@ -6560,6 +11062,7 @@ IMPLEMENTATION #include /* For memset() */ #include #include /* select() (used for ma_sleep()). */ +#include #endif #include /* For fstat(), etc. */ @@ -6602,15 +11105,10 @@ IMPLEMENTATION #define MA_X64 #elif defined(__i386) || defined(_M_IX86) #define MA_X86 -#elif defined(__arm__) || defined(_M_ARM) +#elif defined(__arm__) || defined(_M_ARM) || defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) #define MA_ARM #endif -/* Cannot currently support AVX-512 if AVX is disabled. */ -#if !defined(MA_NO_AVX512) && defined(MA_NO_AVX2) -#define MA_NO_AVX512 -#endif - /* Intrinsics Support */ #if defined(MA_X64) || defined(MA_X86) #if defined(_MSC_VER) && !defined(__clang__) @@ -6624,9 +11122,6 @@ IMPLEMENTATION #if _MSC_VER >= 1700 && !defined(MA_NO_AVX2) /* 2012 */ #define MA_SUPPORT_AVX2 #endif - #if _MSC_VER >= 1910 && !defined(MA_NO_AVX512) /* 2017 */ - #define MA_SUPPORT_AVX512 - #endif #else /* Assume GNUC-style. */ #if defined(__SSE2__) && !defined(MA_NO_SSE2) @@ -6638,9 +11133,6 @@ IMPLEMENTATION #if defined(__AVX2__) && !defined(MA_NO_AVX2) #define MA_SUPPORT_AVX2 #endif - #if defined(__AVX512F__) && !defined(MA_NO_AVX512) - #define MA_SUPPORT_AVX512 - #endif #endif /* If at this point we still haven't determined compiler support for the intrinsics just fall back to __has_include. */ @@ -6654,14 +11146,9 @@ IMPLEMENTATION #if !defined(MA_SUPPORT_AVX2) && !defined(MA_NO_AVX2) && __has_include() #define MA_SUPPORT_AVX2 #endif - #if !defined(MA_SUPPORT_AVX512) && !defined(MA_NO_AVX512) && __has_include() - #define MA_SUPPORT_AVX512 - #endif #endif - #if defined(MA_SUPPORT_AVX512) - #include /* Not a mistake. Intentionally including instead of because otherwise the compiler will complain. */ - #elif defined(MA_SUPPORT_AVX2) || defined(MA_SUPPORT_AVX) + #if defined(MA_SUPPORT_AVX2) || defined(MA_SUPPORT_AVX) #include #elif defined(MA_SUPPORT_SSE2) #include @@ -6671,16 +11158,6 @@ IMPLEMENTATION #if defined(MA_ARM) #if !defined(MA_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) #define MA_SUPPORT_NEON - #endif - - /* Fall back to looking for the #include file. */ - #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include) - #if !defined(MA_SUPPORT_NEON) && !defined(MA_NO_NEON) && __has_include() - #define MA_SUPPORT_NEON - #endif - #endif - - #if defined(MA_SUPPORT_NEON) #include #endif #endif @@ -6689,6 +11166,7 @@ IMPLEMENTATION #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable:4752) /* found Intel(R) Advanced Vector Extensions; consider using /arch:AVX */ + #pragma warning(disable:4049) /* compiler limit : terminating line number emission */ #endif #if defined(MA_X64) || defined(MA_X86) @@ -6850,41 +11328,6 @@ static MA_INLINE ma_bool32 ma_has_avx2(void) #endif } -static MA_INLINE ma_bool32 ma_has_avx512f(void) -{ -#if defined(MA_SUPPORT_AVX512) - #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX512) - #if defined(__AVX512F__) - return MA_TRUE; /* If the compiler is allowed to freely generate AVX-512F code we can assume support. */ - #else - /* AVX-512 requires both CPU and OS support. */ - #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV) - return MA_FALSE; - #else - int info1[4]; - int info7[4]; - ma_cpuid(info1, 1); - ma_cpuid(info7, 7); - if (((info1[2] & (1 << 27)) != 0) && ((info7[1] & (1 << 16)) != 0)) { - ma_uint64 xrc = ma_xgetbv(0); - if ((xrc & 0xE6) == 0xE6) { - return MA_TRUE; - } else { - return MA_FALSE; - } - } else { - return MA_FALSE; - } - #endif - #endif - #else - return MA_FALSE; /* AVX-512F is only supported on x86 and x64 architectures. */ - #endif -#else - return MA_FALSE; /* No compiler support. */ -#endif -} - static MA_INLINE ma_bool32 ma_has_neon(void) { #if defined(MA_SUPPORT_NEON) @@ -6934,7 +11377,7 @@ static MA_INLINE ma_bool32 ma_has_neon(void) #elif defined(_MSC_VER) #define MA_ASSUME(x) __assume(x) #else - #define MA_ASSUME(x) while(0) + #define MA_ASSUME(x) (void)(x) #endif #endif @@ -7033,7 +11476,7 @@ static void ma_sleep__posix(ma_uint32 milliseconds) (void)milliseconds; MA_ASSERT(MA_FALSE); /* The Emscripten build should never sleep. */ #else - #if _POSIX_C_SOURCE >= 199309L + #if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L struct timespec ts; ts.tv_sec = milliseconds / 1000; ts.tv_nsec = milliseconds % 1000 * 1000000; @@ -7048,7 +11491,7 @@ static void ma_sleep__posix(ma_uint32 milliseconds) } #endif -static void ma_sleep(ma_uint32 milliseconds) +static MA_INLINE void ma_sleep(ma_uint32 milliseconds) { #ifdef MA_WIN32 ma_sleep__win32(milliseconds); @@ -7077,7 +11520,7 @@ static MA_INLINE void ma_yield() #else __asm__ __volatile__ ("pause"); #endif -#elif (defined(__arm__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7) || (defined(_M_ARM) && _M_ARM >= 7) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6T2__) +#elif (defined(__arm__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7) || defined(_M_ARM64) || (defined(_M_ARM) && _M_ARM >= 7) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6T2__) /* ARM */ #if defined(_MSC_VER) /* Apparently there is a __yield() intrinsic that's compatible with ARM, but I cannot find documentation for it nor can I find where it's declared. */ @@ -7091,6 +11534,96 @@ static MA_INLINE void ma_yield() } +#define MA_MM_DENORMALS_ZERO_MASK 0x0040 +#define MA_MM_FLUSH_ZERO_MASK 0x8000 + +static MA_INLINE unsigned int ma_disable_denormals() +{ + unsigned int prevState; + + #if defined(_MSC_VER) + { + /* + Older versions of Visual Studio don't support the "safe" versions of _controlfp_s(). I don't + know which version of Visual Studio first added support for _controlfp_s(), but I do know + that VC6 lacks support. _MSC_VER = 1200 is VC6, but if you get compilation errors on older + versions of Visual Studio, let me know and I'll make the necessary adjustment. + */ + #if _MSC_VER <= 1200 + { + prevState = _statusfp(); + _controlfp(prevState | _DN_FLUSH, _MCW_DN); + } + #else + { + unsigned int unused; + _controlfp_s(&prevState, 0, 0); + _controlfp_s(&unused, prevState | _DN_FLUSH, _MCW_DN); + } + #endif + } + #elif defined(MA_X86) || defined(MA_X64) + { + #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */ + { + prevState = _mm_getcsr(); + _mm_setcsr(prevState | MA_MM_DENORMALS_ZERO_MASK | MA_MM_FLUSH_ZERO_MASK); + } + #else + { + /* x88/64, but no support for _mm_getcsr()/_mm_setcsr(). May need to fall back to inlined assembly here. */ + prevState = 0; + } + #endif + } + #else + { + /* Unknown or unsupported architecture. No-op. */ + prevState = 0; + } + #endif + + return prevState; +} + +static MA_INLINE void ma_restore_denormals(unsigned int prevState) +{ + #if defined(_MSC_VER) + { + /* Older versions of Visual Studio do not support _controlfp_s(). See ma_disable_denormals(). */ + #if _MSC_VER <= 1200 + { + _controlfp(prevState, _MCW_DN); + } + #else + { + unsigned int unused; + _controlfp_s(&unused, prevState, _MCW_DN); + } + #endif + } + #elif defined(MA_X86) || defined(MA_X64) + { + #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */ + { + _mm_setcsr(prevState); + } + #else + { + /* x88/64, but no support for _mm_getcsr()/_mm_setcsr(). May need to fall back to inlined assembly here. */ + (void)prevState; + } + #endif + } + #else + { + /* Unknown or unsupported architecture. No-op. */ + (void)prevState; + } + #endif +} + + #ifndef MA_COINIT_VALUE #define MA_COINIT_VALUE 0 /* 0 = COINIT_MULTITHREADED */ @@ -7344,11 +11877,21 @@ static MA_INLINE double ma_sqrtd(double x) } +static MA_INLINE float ma_sinf(float x) +{ + return (float)ma_sind((float)x); +} + static MA_INLINE double ma_cosd(double x) { return ma_sind((MA_PI_D*0.5) - x); } +static MA_INLINE float ma_cosf(float x) +{ + return (float)ma_cosd((float)x); +} + static MA_INLINE double ma_log10d(double x) { return ma_logd(x) * 0.43429448190325182765; @@ -7684,6 +12227,10 @@ MA_API int ma_strappend(char* dst, size_t dstSize, const char* srcA, const char* MA_API char* ma_copy_string(const char* src, const ma_allocation_callbacks* pAllocationCallbacks) { + if (src == NULL) { + return NULL; + } + size_t sz = strlen(src)+1; char* dst = (char*)ma_malloc(sz, pAllocationCallbacks); if (dst == NULL) { @@ -8367,76 +12914,6 @@ static void ma__free_default(void* p, void* pUserData) MA_FREE(p); } - -static void* ma__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks == NULL) { - return NULL; - } - - if (pAllocationCallbacks->onMalloc != NULL) { - return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); - } - - /* Try using realloc(). */ - if (pAllocationCallbacks->onRealloc != NULL) { - return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData); - } - - return NULL; -} - -static void* ma__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks == NULL) { - return NULL; - } - - if (pAllocationCallbacks->onRealloc != NULL) { - return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData); - } - - /* Try emulating realloc() in terms of malloc()/free(). */ - if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) { - void* p2; - - p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData); - if (p2 == NULL) { - return NULL; - } - - if (p != NULL) { - MA_COPY_MEMORY(p2, p, szOld); - pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); - } - - return p2; - } - - return NULL; -} - -static MA_INLINE void* ma__calloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) -{ - void* p = ma__malloc_from_callbacks(sz, pAllocationCallbacks); - if (p != NULL) { - MA_ZERO_MEMORY(p, sz); - } - - return p; -} - -static void ma__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (p == NULL || pAllocationCallbacks == NULL) { - return; - } - - if (pAllocationCallbacks->onFree != NULL) { - pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); - } -} - static ma_allocation_callbacks ma_allocation_callbacks_init_default(void) { ma_allocation_callbacks callbacks; @@ -8547,7 +13024,7 @@ MA_API ma_result ma_log_init(const ma_allocation_callbacks* pAllocationCallbacks } } #endif - + /* If we're using debug output, enable it. */ #if defined(MA_DEBUG_OUTPUT) { @@ -8644,15 +13121,6 @@ MA_API ma_result ma_log_post(ma_log* pLog, ma_uint32 level, const char* pMessage return MA_INVALID_ARGS; } - /* If it's a debug log, ignore it unless MA_DEBUG_OUTPUT is enabled. */ - #if !defined(MA_DEBUG_OUTPUT) - { - if (level == MA_LOG_LEVEL_DEBUG) { - return MA_INVALID_ARGS; /* Don't post debug messages if debug output is disabled. */ - } - } - #endif - ma_log_lock(pLog); { ma_uint32 iLog; @@ -8719,18 +13187,6 @@ MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat return MA_INVALID_ARGS; } - /* - If it's a debug log, ignore it unless MA_DEBUG_OUTPUT is enabled. Do this before generating the - formatted message string so that we don't waste time only to have ma_log_post() reject it. - */ - #if !defined(MA_DEBUG_OUTPUT) - { - if (level == MA_LOG_LEVEL_DEBUG) { - return MA_INVALID_ARGS; /* Don't post debug messages if debug output is disabled. */ - } - } - #endif - #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || ((!defined(_MSC_VER) || _MSC_VER >= 1900) && !defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) { ma_result result; @@ -8840,18 +13296,6 @@ MA_API ma_result ma_log_postf(ma_log* pLog, ma_uint32 level, const char* pFormat return MA_INVALID_ARGS; } - /* - If it's a debug log, ignore it unless MA_DEBUG_OUTPUT is enabled. Do this before generating the - formatted message string so that we don't waste time only to have ma_log_post() reject it. - */ - #if !defined(MA_DEBUG_OUTPUT) - { - if (level == MA_LOG_LEVEL_DEBUG) { - return MA_INVALID_ARGS; /* Don't post debug messages if debug output is disabled. */ - } - } - #endif - va_start(args, pFormat); { result = ma_log_postv(pLog, level, pFormat, args); @@ -8863,8 +13307,32 @@ MA_API ma_result ma_log_postf(ma_log* pLog, ma_uint32 level, const char* pFormat +static MA_INLINE ma_uint8 ma_clip_u8(ma_int32 x) +{ + return (ma_uint8)(ma_clamp(x, -128, 127) + 128); +} + +static MA_INLINE ma_int16 ma_clip_s16(ma_int32 x) +{ + return (ma_int16)ma_clamp(x, -32768, 32767); +} + +static MA_INLINE ma_int64 ma_clip_s24(ma_int64 x) +{ + return (ma_int64)ma_clamp(x, -8388608, 8388607); +} + +static MA_INLINE ma_int32 ma_clip_s32(ma_int64 x) +{ + /* This dance is to silence warnings with -std=c89. A good compiler should be able to optimize this away. */ + ma_int64 clipMin; + ma_int64 clipMax; + clipMin = -((ma_int64)2147483647 + 1); + clipMax = (ma_int64)2147483647; + + return (ma_int32)ma_clamp(x, clipMin, clipMax); +} -/* Clamps an f32 sample to -1..1 */ static MA_INLINE float ma_clip_f32(float x) { if (x < -1) return -1; @@ -8872,6 +13340,7 @@ static MA_INLINE float ma_clip_f32(float x) return x; } + static MA_INLINE float ma_mix_f32(float x, float y, float a) { return x*(1-a) + y*a; @@ -8896,12 +13365,6 @@ static MA_INLINE __m256 ma_mix_f32_fast__avx2(__m256 x, __m256 y, __m256 a) return _mm256_add_ps(x, _mm256_mul_ps(_mm256_sub_ps(y, x), a)); } #endif -#if defined(MA_SUPPORT_AVX512) -static MA_INLINE __m512 ma_mix_f32_fast__avx512(__m512 x, __m512 y, __m512 a) -{ - return _mm512_add_ps(x, _mm512_mul_ps(_mm512_sub_ps(y, x), a)); -} -#endif #if defined(MA_SUPPORT_NEON) static MA_INLINE float32x4_t ma_mix_f32_fast__neon(float32x4_t x, float32x4_t y, float32x4_t a) { @@ -8944,6 +13407,27 @@ static MA_INLINE ma_uint32 ma_gcf_u32(ma_uint32 a, ma_uint32 b) } +static ma_uint32 ma_ffs_32(ma_uint32 x) +{ + ma_uint32 i; + + /* Just a naive implementation just to get things working for now. Will optimize this later. */ + for (i = 0; i < 32; i += 1) { + if ((x & (1 << i)) != 0) { + return i; + } + } + + return i; +} + +static MA_INLINE ma_int16 ma_float_to_fixed_16(float x) +{ + return (ma_int16)(x * (1 << 8)); +} + + + /* Random Number Generation @@ -9104,7 +13588,7 @@ typedef signed short c89atomic_int16; typedef unsigned short c89atomic_uint16; typedef signed int c89atomic_int32; typedef unsigned int c89atomic_uint32; -#if defined(_MSC_VER) +#if defined(_MSC_VER) && !defined(__clang__) typedef signed __int64 c89atomic_int64; typedef unsigned __int64 c89atomic_uint64; #else @@ -9153,7 +13637,7 @@ typedef unsigned char c89atomic_bool; #define C89ATOMIC_X64 #elif defined(__i386) || defined(_M_IX86) #define C89ATOMIC_X86 -#elif defined(__arm__) || defined(_M_ARM) +#elif defined(__arm__) || defined(_M_ARM) || defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) #define C89ATOMIC_ARM #endif #if defined(_MSC_VER) @@ -9262,7 +13746,7 @@ typedef unsigned char c89atomic_bool; #define c89atomic_compare_and_swap_32(dst, expected, desired) (c89atomic_uint32)_InterlockedCompareExchange((volatile long*)dst, (long)desired, (long)expected) #endif #if defined(C89ATOMIC_HAS_64) - #define c89atomic_compare_and_swap_64(dst, expected, desired) (c89atomic_uint64)_InterlockedCompareExchange64((volatile long long*)dst, (long long)desired, (long long)expected) + #define c89atomic_compare_and_swap_64(dst, expected, desired) (c89atomic_uint64)_InterlockedCompareExchange64((volatile c89atomic_int64*)dst, (c89atomic_int64)desired, (c89atomic_int64)expected) #endif #endif #if defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY) @@ -10449,11 +14933,11 @@ typedef unsigned char c89atomic_bool; { return (void*)c89atomic_exchange_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64)src, order); } - static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, volatile void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) + static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { return c89atomic_compare_exchange_strong_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder); } - static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, volatile void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) + static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { return c89atomic_compare_exchange_weak_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder); } @@ -10482,7 +14966,7 @@ typedef unsigned char c89atomic_bool; { return c89atomic_compare_exchange_strong_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder); } - static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, volatile void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) + static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { return c89atomic_compare_exchange_weak_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder); } @@ -10498,8 +14982,8 @@ typedef unsigned char c89atomic_bool; #define c89atomic_store_ptr(dst, src) c89atomic_store_explicit_ptr((volatile void**)dst, (void*)src, c89atomic_memory_order_seq_cst) #define c89atomic_load_ptr(ptr) c89atomic_load_explicit_ptr((volatile void**)ptr, c89atomic_memory_order_seq_cst) #define c89atomic_exchange_ptr(dst, src) c89atomic_exchange_explicit_ptr((volatile void**)dst, (void*)src, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_strong_ptr(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_ptr((volatile void**)dst, (volatile void**)expected, (void*)desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_weak_ptr(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_ptr((volatile void**)dst, (volatile void**)expected, (void*)desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_strong_ptr(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_weak_ptr(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) #define c89atomic_test_and_set_8( ptr) c89atomic_test_and_set_explicit_8( ptr, c89atomic_memory_order_seq_cst) #define c89atomic_test_and_set_16(ptr) c89atomic_test_and_set_explicit_16(ptr, c89atomic_memory_order_seq_cst) #define c89atomic_test_and_set_32(ptr) c89atomic_test_and_set_explicit_32(ptr, c89atomic_memory_order_seq_cst) @@ -10672,16 +15156,16 @@ static C89ATOMIC_INLINE void c89atomic_store_explicit_f64(volatile double* dst, x.f = src; c89atomic_store_explicit_64((volatile c89atomic_uint64*)dst, x.i, order); } -static C89ATOMIC_INLINE float c89atomic_load_explicit_f32(volatile float* ptr, c89atomic_memory_order order) +static C89ATOMIC_INLINE float c89atomic_load_explicit_f32(volatile const float* ptr, c89atomic_memory_order order) { c89atomic_if32 r; - r.i = c89atomic_load_explicit_32((volatile c89atomic_uint32*)ptr, order); + r.i = c89atomic_load_explicit_32((volatile const c89atomic_uint32*)ptr, order); return r.f; } -static C89ATOMIC_INLINE double c89atomic_load_explicit_f64(volatile double* ptr, c89atomic_memory_order order) +static C89ATOMIC_INLINE double c89atomic_load_explicit_f64(volatile const double* ptr, c89atomic_memory_order order) { c89atomic_if64 r; - r.i = c89atomic_load_explicit_64((volatile c89atomic_uint64*)ptr, order); + r.i = c89atomic_load_explicit_64((volatile const c89atomic_uint64*)ptr, order); return r.f; } static C89ATOMIC_INLINE float c89atomic_exchange_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order) @@ -10733,26 +15217,29 @@ static C89ATOMIC_INLINE void c89atomic_spinlock_unlock(volatile c89atomic_spinlo MA_API ma_uint64 ma_calculate_frame_count_after_resampling(ma_uint32 sampleRateOut, ma_uint32 sampleRateIn, ma_uint64 frameCountIn) { - /* For robustness we're going to use a resampler object to calculate this since that already has a way of calculating this. */ - ma_result result; - ma_uint64 frameCountOut; - ma_resampler_config config; - ma_resampler resampler; + /* This is based on the calculation in ma_linear_resampler_get_expected_output_frame_count(). */ + ma_uint64 outputFrameCount; + ma_uint64 preliminaryInputFrameCountFromFrac; + ma_uint64 preliminaryInputFrameCount; + + if (sampleRateIn == 0 || sampleRateOut == 0 || frameCountIn == 0) { + return 0; + } if (sampleRateOut == sampleRateIn) { return frameCountIn; } - config = ma_resampler_config_init(ma_format_s16, 1, sampleRateIn, sampleRateOut, ma_resample_algorithm_linear); - result = ma_resampler_init(&config, &resampler); - if (result != MA_SUCCESS) { - return 0; + outputFrameCount = (frameCountIn * sampleRateOut) / sampleRateIn; + + preliminaryInputFrameCountFromFrac = (outputFrameCount * (sampleRateIn / sampleRateOut)) / sampleRateOut; + preliminaryInputFrameCount = (outputFrameCount * (sampleRateIn % sampleRateOut)) + preliminaryInputFrameCountFromFrac; + + if (preliminaryInputFrameCount <= frameCountIn) { + outputFrameCount += 1; } - frameCountOut = ma_resampler_get_expected_output_frame_count(&resampler, frameCountIn); - - ma_resampler_uninit(&resampler); - return frameCountOut; + return outputFrameCount; } #ifndef MA_DATA_CONVERTER_STACK_BUFFER_SIZE @@ -10790,16 +15277,6 @@ static ma_result ma_result_from_GetLastError(DWORD error) Threading *******************************************************************************/ -#ifndef MA_NO_THREADING -#ifdef MA_WIN32 - #define MA_THREADCALL WINAPI - typedef unsigned long ma_thread_result; -#else - #define MA_THREADCALL - typedef void* ma_thread_result; -#endif -typedef ma_thread_result (MA_THREADCALL * ma_thread_entry_proc)(void* pData); - static MA_INLINE ma_result ma_spinlock_lock_ex(volatile ma_spinlock* pSpinlock, ma_bool32 yield) { if (pSpinlock == NULL) { @@ -10841,6 +15318,17 @@ MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock) return MA_SUCCESS; } + +#ifndef MA_NO_THREADING +#ifdef MA_WIN32 + #define MA_THREADCALL WINAPI + typedef unsigned long ma_thread_result; +#else + #define MA_THREADCALL + typedef void* ma_thread_result; +#endif +typedef ma_thread_result (MA_THREADCALL * ma_thread_entry_proc)(void* pData); + #ifdef MA_WIN32 static int ma_thread_priority_to_win32(ma_thread_priority priority) { @@ -11048,7 +15536,7 @@ static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority (void)stackSize; #endif - result = pthread_create(pThread, pAttr, entryProc, pData); + result = pthread_create((pthread_t*)pThread, pAttr, entryProc, pData); /* The thread attributes object is no longer required. */ if (pAttr != NULL) { @@ -11064,8 +15552,7 @@ static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority static void ma_thread_wait__posix(ma_thread* pThread) { - pthread_join(*pThread, NULL); - pthread_detach(*pThread); + pthread_join((pthread_t)*pThread, NULL); } @@ -11099,14 +15586,14 @@ static ma_result ma_event_init__posix(ma_event* pEvent) { int result; - result = pthread_mutex_init(&pEvent->lock, NULL); + result = pthread_mutex_init((pthread_mutex_t*)&pEvent->lock, NULL); if (result != 0) { return ma_result_from_errno(result); } - result = pthread_cond_init(&pEvent->cond, NULL); + result = pthread_cond_init((pthread_cond_t*)&pEvent->cond, NULL); if (result != 0) { - pthread_mutex_destroy(&pEvent->lock); + pthread_mutex_destroy((pthread_mutex_t*)&pEvent->lock); return ma_result_from_errno(result); } @@ -11116,32 +15603,32 @@ static ma_result ma_event_init__posix(ma_event* pEvent) static void ma_event_uninit__posix(ma_event* pEvent) { - pthread_cond_destroy(&pEvent->cond); - pthread_mutex_destroy(&pEvent->lock); + pthread_cond_destroy((pthread_cond_t*)&pEvent->cond); + pthread_mutex_destroy((pthread_mutex_t*)&pEvent->lock); } static ma_result ma_event_wait__posix(ma_event* pEvent) { - pthread_mutex_lock(&pEvent->lock); + pthread_mutex_lock((pthread_mutex_t*)&pEvent->lock); { while (pEvent->value == 0) { - pthread_cond_wait(&pEvent->cond, &pEvent->lock); + pthread_cond_wait((pthread_cond_t*)&pEvent->cond, (pthread_mutex_t*)&pEvent->lock); } pEvent->value = 0; /* Auto-reset. */ } - pthread_mutex_unlock(&pEvent->lock); + pthread_mutex_unlock((pthread_mutex_t*)&pEvent->lock); return MA_SUCCESS; } static ma_result ma_event_signal__posix(ma_event* pEvent) { - pthread_mutex_lock(&pEvent->lock); + pthread_mutex_lock((pthread_mutex_t*)&pEvent->lock); { pEvent->value = 1; - pthread_cond_signal(&pEvent->cond); + pthread_cond_signal((pthread_cond_t*)&pEvent->cond); } - pthread_mutex_unlock(&pEvent->lock); + pthread_mutex_unlock((pthread_mutex_t*)&pEvent->lock); return MA_SUCCESS; } @@ -11157,14 +15644,14 @@ static ma_result ma_semaphore_init__posix(int initialValue, ma_semaphore* pSemap pSemaphore->value = initialValue; - result = pthread_mutex_init(&pSemaphore->lock, NULL); + result = pthread_mutex_init((pthread_mutex_t*)&pSemaphore->lock, NULL); if (result != 0) { return ma_result_from_errno(result); /* Failed to create mutex. */ } - result = pthread_cond_init(&pSemaphore->cond, NULL); + result = pthread_cond_init((pthread_cond_t*)&pSemaphore->cond, NULL); if (result != 0) { - pthread_mutex_destroy(&pSemaphore->lock); + pthread_mutex_destroy((pthread_mutex_t*)&pSemaphore->lock); return ma_result_from_errno(result); /* Failed to create condition variable. */ } @@ -11177,8 +15664,8 @@ static void ma_semaphore_uninit__posix(ma_semaphore* pSemaphore) return; } - pthread_cond_destroy(&pSemaphore->cond); - pthread_mutex_destroy(&pSemaphore->lock); + pthread_cond_destroy((pthread_cond_t*)&pSemaphore->cond); + pthread_mutex_destroy((pthread_mutex_t*)&pSemaphore->lock); } static ma_result ma_semaphore_wait__posix(ma_semaphore* pSemaphore) @@ -11187,16 +15674,16 @@ static ma_result ma_semaphore_wait__posix(ma_semaphore* pSemaphore) return MA_INVALID_ARGS; } - pthread_mutex_lock(&pSemaphore->lock); + pthread_mutex_lock((pthread_mutex_t*)&pSemaphore->lock); { /* We need to wait on a condition variable before escaping. We can't return from this function until the semaphore has been signaled. */ while (pSemaphore->value == 0) { - pthread_cond_wait(&pSemaphore->cond, &pSemaphore->lock); + pthread_cond_wait((pthread_cond_t*)&pSemaphore->cond, (pthread_mutex_t*)&pSemaphore->lock); } pSemaphore->value -= 1; } - pthread_mutex_unlock(&pSemaphore->lock); + pthread_mutex_unlock((pthread_mutex_t*)&pSemaphore->lock); return MA_SUCCESS; } @@ -11207,12 +15694,12 @@ static ma_result ma_semaphore_release__posix(ma_semaphore* pSemaphore) return MA_INVALID_ARGS; } - pthread_mutex_lock(&pSemaphore->lock); + pthread_mutex_lock((pthread_mutex_t*)&pSemaphore->lock); { pSemaphore->value += 1; - pthread_cond_signal(&pSemaphore->cond); + pthread_cond_signal((pthread_cond_t*)&pSemaphore->cond); } - pthread_mutex_unlock(&pSemaphore->lock); + pthread_mutex_unlock((pthread_mutex_t*)&pSemaphore->lock); return MA_SUCCESS; } @@ -11257,7 +15744,7 @@ static ma_result ma_thread_create(ma_thread* pThread, ma_thread_priority priorit ma_thread_proxy_data* pProxyData; if (pThread == NULL || entryProc == NULL) { - return MA_FALSE; + return MA_INVALID_ARGS; } pProxyData = (ma_thread_proxy_data*)ma_malloc(sizeof(*pProxyData), pAllocationCallbacks); /* Will be freed by the proxy entry proc. */ @@ -11386,14 +15873,14 @@ static ma_result ma_event_alloc_and_init(ma_event** ppEvent, ma_allocation_callb *ppEvent = NULL; - pEvent = ma_malloc(sizeof(*pEvent), pAllocationCallbacks/*, MA_ALLOCATION_TYPE_EVENT*/); + pEvent = ma_malloc(sizeof(*pEvent), pAllocationCallbacks); if (pEvent == NULL) { return MA_OUT_OF_MEMORY; } result = ma_event_init(pEvent); if (result != MA_SUCCESS) { - ma_free(pEvent, pAllocationCallbacks/*, MA_ALLOCATION_TYPE_EVENT*/); + ma_free(pEvent, pAllocationCallbacks); return result; } @@ -11424,7 +15911,7 @@ static void ma_event_uninit_and_free(ma_event* pEvent, ma_allocation_callbacks* } ma_event_uninit(pEvent); - ma_free(pEvent, pAllocationCallbacks/*, MA_ALLOCATION_TYPE_EVENT*/); + ma_free(pEvent, pAllocationCallbacks); } #endif @@ -11527,6 +16014,1025 @@ MA_API ma_result ma_semaphore_release(ma_semaphore* pSemaphore) +#define MA_FENCE_COUNTER_MAX 0x7FFFFFFF + +MA_API ma_result ma_fence_init(ma_fence* pFence) +{ + if (pFence == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pFence); + pFence->counter = 0; + + #ifndef MA_NO_THREADING + { + ma_result result; + + result = ma_event_init(&pFence->e); + if (result != MA_SUCCESS) { + return result; + } + } + #endif + + return MA_SUCCESS; +} + +MA_API void ma_fence_uninit(ma_fence* pFence) +{ + if (pFence == NULL) { + return; + } + + #ifndef MA_NO_THREADING + { + ma_event_uninit(&pFence->e); + } + #endif + + MA_ZERO_OBJECT(pFence); +} + +MA_API ma_result ma_fence_acquire(ma_fence* pFence) +{ + if (pFence == NULL) { + return MA_INVALID_ARGS; + } + + for (;;) { + ma_uint32 oldCounter = c89atomic_load_32(&pFence->counter); + ma_uint32 newCounter = oldCounter + 1; + + /* Make sure we're not about to exceed our maximum value. */ + if (newCounter > MA_FENCE_COUNTER_MAX) { + MA_ASSERT(MA_FALSE); + return MA_OUT_OF_RANGE; + } + + if (c89atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) { + return MA_SUCCESS; + } else { + if (oldCounter == MA_FENCE_COUNTER_MAX) { + MA_ASSERT(MA_FALSE); + return MA_OUT_OF_RANGE; /* The other thread took the last available slot. Abort. */ + } + } + } + + /* Should never get here. */ + /*return MA_SUCCESS;*/ +} + +MA_API ma_result ma_fence_release(ma_fence* pFence) +{ + if (pFence == NULL) { + return MA_INVALID_ARGS; + } + + for (;;) { + ma_uint32 oldCounter = c89atomic_load_32(&pFence->counter); + ma_uint32 newCounter = oldCounter - 1; + + if (oldCounter == 0) { + MA_ASSERT(MA_FALSE); + return MA_INVALID_OPERATION; /* Acquire/release mismatch. */ + } + + if (c89atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) { + #ifndef MA_NO_THREADING + { + if (newCounter == 0) { + ma_event_signal(&pFence->e); /* <-- ma_fence_wait() will be waiting on this. */ + } + } + #endif + + return MA_SUCCESS; + } else { + if (oldCounter == 0) { + MA_ASSERT(MA_FALSE); + return MA_INVALID_OPERATION; /* Another thread has taken the 0 slot. Acquire/release mismatch. */ + } + } + } + + /* Should never get here. */ + /*return MA_SUCCESS;*/ +} + +MA_API ma_result ma_fence_wait(ma_fence* pFence) +{ + if (pFence == NULL) { + return MA_INVALID_ARGS; + } + + for (;;) { + ma_uint32 counter; + + counter = c89atomic_load_32(&pFence->counter); + if (counter == 0) { + /* + Counter has hit zero. By the time we get here some other thread may have acquired the + fence again, but that is where the caller needs to take care with how they se the fence. + */ + return MA_SUCCESS; + } + + /* Getting here means the counter is > 0. We'll need to wait for something to happen. */ + #ifndef MA_NO_THREADING + { + ma_result result; + + result = ma_event_wait(&pFence->e); + if (result != MA_SUCCESS) { + return result; + } + } + #endif + } + + /* Should never get here. */ + /*return MA_INVALID_OPERATION;*/ +} + + +MA_API ma_result ma_async_notification_signal(ma_async_notification* pNotification) +{ + ma_async_notification_callbacks* pNotificationCallbacks = (ma_async_notification_callbacks*)pNotification; + + if (pNotification == NULL) { + return MA_INVALID_ARGS; + } + + if (pNotificationCallbacks->onSignal == NULL) { + return MA_NOT_IMPLEMENTED; + } + + pNotificationCallbacks->onSignal(pNotification); + return MA_INVALID_ARGS; +} + + +static void ma_async_notification_poll__on_signal(ma_async_notification* pNotification) +{ + ((ma_async_notification_poll*)pNotification)->signalled = MA_TRUE; +} + +MA_API ma_result ma_async_notification_poll_init(ma_async_notification_poll* pNotificationPoll) +{ + if (pNotificationPoll == NULL) { + return MA_INVALID_ARGS; + } + + pNotificationPoll->cb.onSignal = ma_async_notification_poll__on_signal; + pNotificationPoll->signalled = MA_FALSE; + + return MA_SUCCESS; +} + +MA_API ma_bool32 ma_async_notification_poll_is_signalled(const ma_async_notification_poll* pNotificationPoll) +{ + if (pNotificationPoll == NULL) { + return MA_FALSE; + } + + return pNotificationPoll->signalled; +} + + +static void ma_async_notification_event__on_signal(ma_async_notification* pNotification) +{ + ma_async_notification_event_signal((ma_async_notification_event*)pNotification); +} + +MA_API ma_result ma_async_notification_event_init(ma_async_notification_event* pNotificationEvent) +{ + if (pNotificationEvent == NULL) { + return MA_INVALID_ARGS; + } + + pNotificationEvent->cb.onSignal = ma_async_notification_event__on_signal; + + #ifndef MA_NO_THREADING + { + ma_result result; + + result = ma_event_init(&pNotificationEvent->e); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; + } + #else + { + return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ + } + #endif +} + +MA_API ma_result ma_async_notification_event_uninit(ma_async_notification_event* pNotificationEvent) +{ + if (pNotificationEvent == NULL) { + return MA_INVALID_ARGS; + } + + #ifndef MA_NO_THREADING + { + ma_event_uninit(&pNotificationEvent->e); + return MA_SUCCESS; + } + #else + { + return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ + } + #endif +} + +MA_API ma_result ma_async_notification_event_wait(ma_async_notification_event* pNotificationEvent) +{ + if (pNotificationEvent == NULL) { + return MA_INVALID_ARGS; + } + + #ifndef MA_NO_THREADING + { + return ma_event_wait(&pNotificationEvent->e); + } + #else + { + return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ + } + #endif +} + +MA_API ma_result ma_async_notification_event_signal(ma_async_notification_event* pNotificationEvent) +{ + if (pNotificationEvent == NULL) { + return MA_INVALID_ARGS; + } + + #ifndef MA_NO_THREADING + { + return ma_event_signal(&pNotificationEvent->e); + } + #else + { + return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ + } + #endif +} + + + +/************************************************************************************************************************************************************ + +Job Queue + +************************************************************************************************************************************************************/ +MA_API ma_slot_allocator_config ma_slot_allocator_config_init(ma_uint32 capacity) +{ + ma_slot_allocator_config config; + + MA_ZERO_OBJECT(&config); + config.capacity = capacity; + + return config; +} + + +static MA_INLINE ma_uint32 ma_slot_allocator_calculate_group_capacity(ma_uint32 slotCapacity) +{ + ma_uint32 cap = slotCapacity / 32; + if ((slotCapacity % 32) != 0) { + cap += 1; + } + + return cap; +} + +static MA_INLINE ma_uint32 ma_slot_allocator_group_capacity(const ma_slot_allocator* pAllocator) +{ + return ma_slot_allocator_calculate_group_capacity(pAllocator->capacity); +} + + +typedef struct +{ + size_t sizeInBytes; + size_t groupsOffset; + size_t slotsOffset; +} ma_slot_allocator_heap_layout; + +static ma_result ma_slot_allocator_get_heap_layout(const ma_slot_allocator_config* pConfig, ma_slot_allocator_heap_layout* pHeapLayout) +{ + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->capacity == 0) { + return MA_INVALID_ARGS; + } + + pHeapLayout->sizeInBytes = 0; + + /* Groups. */ + pHeapLayout->groupsOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(ma_slot_allocator_calculate_group_capacity(pConfig->capacity) * sizeof(ma_slot_allocator_group)); + + /* Slots. */ + pHeapLayout->slotsOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(pConfig->capacity * sizeof(ma_uint32)); + + return MA_SUCCESS; +} + +MA_API ma_result ma_slot_allocator_get_heap_size(const ma_slot_allocator_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_slot_allocator_heap_layout layout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_slot_allocator_get_heap_layout(pConfig, &layout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = layout.sizeInBytes; + + return result; +} + +MA_API ma_result ma_slot_allocator_init_preallocated(const ma_slot_allocator_config* pConfig, void* pHeap, ma_slot_allocator* pAllocator) +{ + ma_result result; + ma_slot_allocator_heap_layout heapLayout; + + if (pAllocator == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pAllocator); + + if (pHeap == NULL) { + return MA_INVALID_ARGS; + } + + result = ma_slot_allocator_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pAllocator->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pAllocator->pGroups = (ma_slot_allocator_group*)ma_offset_ptr(pHeap, heapLayout.groupsOffset); + pAllocator->pSlots = (ma_uint32*)ma_offset_ptr(pHeap, heapLayout.slotsOffset); + pAllocator->capacity = pConfig->capacity; + + return MA_SUCCESS; +} + +MA_API ma_result ma_slot_allocator_init(const ma_slot_allocator_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_slot_allocator* pAllocator) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_slot_allocator_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; /* Failed to retrieve the size of the heap allocation. */ + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_slot_allocator_init_preallocated(pConfig, pHeap, pAllocator); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pAllocator->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_slot_allocator_uninit(ma_slot_allocator* pAllocator, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocator == NULL) { + return; + } + + if (pAllocator->_ownsHeap) { + ma_free(pAllocator->_pHeap, pAllocationCallbacks); + } +} + +MA_API ma_result ma_slot_allocator_alloc(ma_slot_allocator* pAllocator, ma_uint64* pSlot) +{ + ma_uint32 iAttempt; + const ma_uint32 maxAttempts = 2; /* The number of iterations to perform until returning MA_OUT_OF_MEMORY if no slots can be found. */ + + if (pAllocator == NULL || pSlot == NULL) { + return MA_INVALID_ARGS; + } + + for (iAttempt = 0; iAttempt < maxAttempts; iAttempt += 1) { + /* We need to acquire a suitable bitfield first. This is a bitfield that's got an available slot within it. */ + ma_uint32 iGroup; + for (iGroup = 0; iGroup < ma_slot_allocator_group_capacity(pAllocator); iGroup += 1) { + /* CAS */ + for (;;) { + ma_uint32 oldBitfield; + ma_uint32 newBitfield; + ma_uint32 bitOffset; + + oldBitfield = c89atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */ + + /* Fast check to see if anything is available. */ + if (oldBitfield == 0xFFFFFFFF) { + break; /* No available bits in this bitfield. */ + } + + bitOffset = ma_ffs_32(~oldBitfield); + MA_ASSERT(bitOffset < 32); + + newBitfield = oldBitfield | (1 << bitOffset); + + if (c89atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) { + ma_uint32 slotIndex; + + /* Increment the counter as soon as possible to have other threads report out-of-memory sooner than later. */ + c89atomic_fetch_add_32(&pAllocator->count, 1); + + /* The slot index is required for constructing the output value. */ + slotIndex = (iGroup << 5) + bitOffset; /* iGroup << 5 = iGroup * 32 */ + if (slotIndex >= pAllocator->capacity) { + return MA_OUT_OF_MEMORY; + } + + /* Increment the reference count before constructing the output value. */ + pAllocator->pSlots[slotIndex] += 1; + + /* Construct the output value. */ + *pSlot = (((ma_uint64)pAllocator->pSlots[slotIndex] << 32) | slotIndex); + + return MA_SUCCESS; + } + } + } + + /* We weren't able to find a slot. If it's because we've reached our capacity we need to return MA_OUT_OF_MEMORY. Otherwise we need to do another iteration and try again. */ + if (pAllocator->count < pAllocator->capacity) { + ma_yield(); + } else { + return MA_OUT_OF_MEMORY; + } + } + + /* We couldn't find a slot within the maximum number of attempts. */ + return MA_OUT_OF_MEMORY; +} + +MA_API ma_result ma_slot_allocator_free(ma_slot_allocator* pAllocator, ma_uint64 slot) +{ + ma_uint32 iGroup; + ma_uint32 iBit; + + if (pAllocator == NULL) { + return MA_INVALID_ARGS; + } + + iGroup = (ma_uint32)((slot & 0xFFFFFFFF) >> 5); /* slot / 32 */ + iBit = (ma_uint32)((slot & 0xFFFFFFFF) & 31); /* slot % 32 */ + + if (iGroup >= ma_slot_allocator_group_capacity(pAllocator)) { + return MA_INVALID_ARGS; + } + + MA_ASSERT(iBit < 32); /* This must be true due to the logic we used to actually calculate it. */ + + while (c89atomic_load_32(&pAllocator->count) > 0) { + /* CAS */ + ma_uint32 oldBitfield; + ma_uint32 newBitfield; + + oldBitfield = c89atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */ + newBitfield = oldBitfield & ~(1 << iBit); + + /* Debugging for checking for double-frees. */ + #if defined(MA_DEBUG_OUTPUT) + { + if ((oldBitfield & (1 << iBit)) == 0) { + MA_ASSERT(MA_FALSE); /* Double free detected.*/ + } + } + #endif + + if (c89atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) { + c89atomic_fetch_sub_32(&pAllocator->count, 1); + return MA_SUCCESS; + } + } + + /* Getting here means there are no allocations available for freeing. */ + return MA_INVALID_OPERATION; +} + + +#define MA_JOB_ID_NONE ~((ma_uint64)0) +#define MA_JOB_SLOT_NONE (ma_uint16)(~0) + +static MA_INLINE ma_uint32 ma_job_extract_refcount(ma_uint64 toc) +{ + return (ma_uint32)(toc >> 32); +} + +static MA_INLINE ma_uint16 ma_job_extract_slot(ma_uint64 toc) +{ + return (ma_uint16)(toc & 0x0000FFFF); +} + +static MA_INLINE ma_uint16 ma_job_extract_code(ma_uint64 toc) +{ + return (ma_uint16)((toc & 0xFFFF0000) >> 16); +} + +static MA_INLINE ma_uint64 ma_job_toc_to_allocation(ma_uint64 toc) +{ + return ((ma_uint64)ma_job_extract_refcount(toc) << 32) | (ma_uint64)ma_job_extract_slot(toc); +} + +static MA_INLINE ma_uint64 ma_job_set_refcount(ma_uint64 toc, ma_uint32 refcount) +{ + /* Clear the reference count first. */ + toc = toc & ~((ma_uint64)0xFFFFFFFF << 32); + toc = toc | ((ma_uint64)refcount << 32); + + return toc; +} + + +MA_API ma_job ma_job_init(ma_uint16 code) +{ + ma_job job; + + MA_ZERO_OBJECT(&job); + job.toc.breakup.code = code; + job.toc.breakup.slot = MA_JOB_SLOT_NONE; /* Temp value. Will be allocated when posted to a queue. */ + job.next = MA_JOB_ID_NONE; + + return job; +} + + +static ma_result ma_job_process__noop(ma_job* pJob); +static ma_result ma_job_process__quit(ma_job* pJob); +static ma_result ma_job_process__custom(ma_job* pJob); +static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob); +static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob); +static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob); +static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob); +static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob); +static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob); +static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob); +static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob); +static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob); + +#if !defined(MA_NO_DEVICE_IO) +static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob); +#endif + +static ma_job_proc g_jobVTable[MA_JOB_TYPE_COUNT] = +{ + /* Miscellaneous. */ + ma_job_process__quit, /* MA_JOB_TYPE_QUIT */ + ma_job_process__custom, /* MA_JOB_TYPE_CUSTOM */ + + /* Resource Manager. */ + ma_job_process__resource_manager__load_data_buffer_node, /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE */ + ma_job_process__resource_manager__free_data_buffer_node, /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE */ + ma_job_process__resource_manager__page_data_buffer_node, /* MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE */ + ma_job_process__resource_manager__load_data_buffer, /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER */ + ma_job_process__resource_manager__free_data_buffer, /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER */ + ma_job_process__resource_manager__load_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM */ + ma_job_process__resource_manager__free_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM */ + ma_job_process__resource_manager__page_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM */ + ma_job_process__resource_manager__seek_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM */ + + /* Device. */ +#if !defined(MA_NO_DEVICE_IO) + ma_job_process__device__aaudio_reroute /*MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE*/ +#endif +}; + +MA_API ma_result ma_job_process(ma_job* pJob) +{ + if (pJob == NULL) { + return MA_INVALID_ARGS; + } + + if (pJob->toc.breakup.code > MA_JOB_TYPE_COUNT) { + return MA_INVALID_OPERATION; + } + + return g_jobVTable[pJob->toc.breakup.code](pJob); +} + +static ma_result ma_job_process__noop(ma_job* pJob) +{ + MA_ASSERT(pJob != NULL); + + /* No-op. */ + (void)pJob; + + return MA_SUCCESS; +} + +static ma_result ma_job_process__quit(ma_job* pJob) +{ + return ma_job_process__noop(pJob); +} + +static ma_result ma_job_process__custom(ma_job* pJob) +{ + MA_ASSERT(pJob != NULL); + + /* No-op if there's no callback. */ + if (pJob->data.custom.proc == NULL) { + return MA_SUCCESS; + } + + return pJob->data.custom.proc(pJob); +} + + + +MA_API ma_job_queue_config ma_job_queue_config_init(ma_uint32 flags, ma_uint32 capacity) +{ + ma_job_queue_config config; + + config.flags = flags; + config.capacity = capacity; + + return config; +} + + +typedef struct +{ + size_t sizeInBytes; + size_t allocatorOffset; + size_t jobsOffset; +} ma_job_queue_heap_layout; + +static ma_result ma_job_queue_get_heap_layout(const ma_job_queue_config* pConfig, ma_job_queue_heap_layout* pHeapLayout) +{ + ma_result result; + + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->capacity == 0) { + return MA_INVALID_ARGS; + } + + pHeapLayout->sizeInBytes = 0; + + /* Allocator. */ + { + ma_slot_allocator_config allocatorConfig; + size_t allocatorHeapSizeInBytes; + + allocatorConfig = ma_slot_allocator_config_init(pConfig->capacity); + result = ma_slot_allocator_get_heap_size(&allocatorConfig, &allocatorHeapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->allocatorOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += allocatorHeapSizeInBytes; + } + + /* Jobs. */ + pHeapLayout->jobsOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(pConfig->capacity * sizeof(ma_job)); + + return MA_SUCCESS; +} + +MA_API ma_result ma_job_queue_get_heap_size(const ma_job_queue_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_job_queue_heap_layout layout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_job_queue_get_heap_layout(pConfig, &layout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = layout.sizeInBytes; + + return MA_SUCCESS; +} + +MA_API ma_result ma_job_queue_init_preallocated(const ma_job_queue_config* pConfig, void* pHeap, ma_job_queue* pQueue) +{ + ma_result result; + ma_job_queue_heap_layout heapLayout; + ma_slot_allocator_config allocatorConfig; + + if (pQueue == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pQueue); + + result = ma_job_queue_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pQueue->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pQueue->flags = pConfig->flags; + pQueue->capacity = pConfig->capacity; + pQueue->pJobs = (ma_job*)ma_offset_ptr(pHeap, heapLayout.jobsOffset); + + allocatorConfig = ma_slot_allocator_config_init(pConfig->capacity); + result = ma_slot_allocator_init_preallocated(&allocatorConfig, ma_offset_ptr(pHeap, heapLayout.allocatorOffset), &pQueue->allocator); + if (result != MA_SUCCESS) { + return result; + } + + /* We need a semaphore if we're running in non-blocking mode. If threading is disabled we need to return an error. */ + if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { + #ifndef MA_NO_THREADING + { + ma_semaphore_init(0, &pQueue->sem); + } + #else + { + /* Threading is disabled and we've requested non-blocking mode. */ + return MA_INVALID_OPERATION; + } + #endif + } + + /* + Our queue needs to be initialized with a free standing node. This should always be slot 0. Required for the lock free algorithm. The first job in the queue is + just a dummy item for giving us the first item in the list which is stored in the "next" member. + */ + ma_slot_allocator_alloc(&pQueue->allocator, &pQueue->head); /* Will never fail. */ + pQueue->pJobs[ma_job_extract_slot(pQueue->head)].next = MA_JOB_ID_NONE; + pQueue->tail = pQueue->head; + + return MA_SUCCESS; +} + +MA_API ma_result ma_job_queue_init(const ma_job_queue_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_job_queue* pQueue) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_job_queue_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_job_queue_init_preallocated(pConfig, pHeap, pQueue); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pQueue->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_job_queue_uninit(ma_job_queue* pQueue, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pQueue == NULL) { + return; + } + + /* All we need to do is uninitialize the semaphore. */ + if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { + #ifndef MA_NO_THREADING + { + ma_semaphore_uninit(&pQueue->sem); + } + #else + { + MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */ + } + #endif + } + + ma_slot_allocator_uninit(&pQueue->allocator, pAllocationCallbacks); + + if (pQueue->_ownsHeap) { + ma_free(pQueue->_pHeap, pAllocationCallbacks); + } +} + +static ma_bool32 ma_job_queue_cas(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) +{ + /* The new counter is taken from the expected value. */ + return c89atomic_compare_and_swap_64(dst, expected, ma_job_set_refcount(desired, ma_job_extract_refcount(expected) + 1)) == expected; +} + +MA_API ma_result ma_job_queue_post(ma_job_queue* pQueue, const ma_job* pJob) +{ + /* + Lock free queue implementation based on the paper by Michael and Scott: Nonblocking Algorithms and Preemption-Safe Locking on Multiprogrammed Shared Memory Multiprocessors + */ + ma_result result; + ma_uint64 slot; + ma_uint64 tail; + ma_uint64 next; + + if (pQueue == NULL || pJob == NULL) { + return MA_INVALID_ARGS; + } + + /* We need a new slot. */ + result = ma_slot_allocator_alloc(&pQueue->allocator, &slot); + if (result != MA_SUCCESS) { + return result; /* Probably ran out of slots. If so, MA_OUT_OF_MEMORY will be returned. */ + } + + /* At this point we should have a slot to place the job. */ + MA_ASSERT(ma_job_extract_slot(slot) < pQueue->capacity); + + /* We need to put the job into memory before we do anything. */ + pQueue->pJobs[ma_job_extract_slot(slot)] = *pJob; + pQueue->pJobs[ma_job_extract_slot(slot)].toc.allocation = slot; /* This will overwrite the job code. */ + pQueue->pJobs[ma_job_extract_slot(slot)].toc.breakup.code = pJob->toc.breakup.code; /* The job code needs to be applied again because the line above overwrote it. */ + pQueue->pJobs[ma_job_extract_slot(slot)].next = MA_JOB_ID_NONE; /* Reset for safety. */ + + #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE + ma_spinlock_lock(&pQueue->lock); + #endif + { + /* The job is stored in memory so now we need to add it to our linked list. We only ever add items to the end of the list. */ + for (;;) { + tail = c89atomic_load_64(&pQueue->tail); + next = c89atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(tail)].next); + + if (ma_job_toc_to_allocation(tail) == ma_job_toc_to_allocation(c89atomic_load_64(&pQueue->tail))) { + if (ma_job_extract_slot(next) == 0xFFFF) { + if (ma_job_queue_cas(&pQueue->pJobs[ma_job_extract_slot(tail)].next, next, slot)) { + break; + } + } else { + ma_job_queue_cas(&pQueue->tail, tail, ma_job_extract_slot(next)); + } + } + } + ma_job_queue_cas(&pQueue->tail, tail, slot); + } + #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE + ma_spinlock_unlock(&pQueue->lock); + #endif + + + /* Signal the semaphore as the last step if we're using synchronous mode. */ + if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { + #ifndef MA_NO_THREADING + { + ma_semaphore_release(&pQueue->sem); + } + #else + { + MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */ + } + #endif + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob) +{ + ma_uint64 head; + ma_uint64 tail; + ma_uint64 next; + + if (pQueue == NULL || pJob == NULL) { + return MA_INVALID_ARGS; + } + + /* If we're running in synchronous mode we'll need to wait on a semaphore. */ + if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { + #ifndef MA_NO_THREADING + { + ma_semaphore_wait(&pQueue->sem); + } + #else + { + MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */ + } + #endif + } + + #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE + ma_spinlock_lock(&pQueue->lock); + #endif + { + /* + BUG: In lock-free mode, multiple threads can be in this section of code. The "head" variable in the loop below + is stored. One thread can fall through to the freeing of this item while another is still using "head" for the + retrieval of the "next" variable. + + The slot allocator might need to make use of some reference counting to ensure it's only truely freed when + there are no more references to the item. This must be fixed before removing these locks. + */ + + /* Now we need to remove the root item from the list. */ + for (;;) { + head = c89atomic_load_64(&pQueue->head); + tail = c89atomic_load_64(&pQueue->tail); + next = c89atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(head)].next); + + if (ma_job_toc_to_allocation(head) == ma_job_toc_to_allocation(c89atomic_load_64(&pQueue->head))) { + if (ma_job_extract_slot(head) == ma_job_extract_slot(tail)) { + if (ma_job_extract_slot(next) == 0xFFFF) { + #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE + ma_spinlock_unlock(&pQueue->lock); + #endif + return MA_NO_DATA_AVAILABLE; + } + ma_job_queue_cas(&pQueue->tail, tail, ma_job_extract_slot(next)); + } else { + *pJob = pQueue->pJobs[ma_job_extract_slot(next)]; + if (ma_job_queue_cas(&pQueue->head, head, ma_job_extract_slot(next))) { + break; + } + } + } + } + } + #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE + ma_spinlock_unlock(&pQueue->lock); + #endif + + ma_slot_allocator_free(&pQueue->allocator, head); + + /* + If it's a quit job make sure it's put back on the queue to ensure other threads have an opportunity to detect it and terminate naturally. We + could instead just leave it on the queue, but that would involve fiddling with the lock-free code above and I want to keep that as simple as + possible. + */ + if (pJob->toc.breakup.code == MA_JOB_TYPE_QUIT) { + ma_job_queue_post(pQueue, pJob); + return MA_CANCELLED; /* Return a cancelled status just in case the thread is checking return codes and not properly checking for a quit job. */ + } + + return MA_SUCCESS; +} + + + + /************************************************************************************************************************************************************ ************************************************************************************************************************************************************* @@ -11940,58 +17446,19 @@ typedef int (WINAPI * MA_PFN_StringFromGUID2)(const GUID* const rguid, LPOLE typedef HWND (WINAPI * MA_PFN_GetForegroundWindow)(void); typedef HWND (WINAPI * MA_PFN_GetDesktopWindow)(void); +#if defined(MA_WIN32_DESKTOP) /* Microsoft documents these APIs as returning LSTATUS, but the Win32 API shipping with some compilers do not define it. It's just a LONG. */ typedef LONG (WINAPI * MA_PFN_RegOpenKeyExA)(HKEY hKey, LPCSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult); typedef LONG (WINAPI * MA_PFN_RegCloseKey)(HKEY hKey); typedef LONG (WINAPI * MA_PFN_RegQueryValueExA)(HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData); -#endif +#endif /* MA_WIN32_DESKTOP */ +#endif /* MA_WIN32 */ #define MA_DEFAULT_PLAYBACK_DEVICE_NAME "Default Playback Device" #define MA_DEFAULT_CAPTURE_DEVICE_NAME "Default Capture Device" -/* Posts a log message. */ -static void ma_post_log_message(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message) -{ - if (pContext == NULL) { - if (pDevice != NULL) { - pContext = pDevice->pContext; - } - } - - if (pContext == NULL) { - return; - } - - ma_log_post(ma_context_get_log(pContext), logLevel, message); /* <-- This will deal with MA_DEBUG_OUTPUT. */ - - /* Legacy. */ -#if defined(MA_LOG_LEVEL) - if (logLevel <= MA_LOG_LEVEL) { - ma_log_proc onLog; - - onLog = pContext->logCallback; - if (onLog) { - onLog(pContext, pDevice, logLevel, message); - } - } -#endif -} - -/* Posts an log message. Throw a breakpoint in here if you're needing to debug. The return value is always "resultCode". */ -static ma_result ma_context_post_error(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message, ma_result resultCode) -{ - ma_post_log_message(pContext, pDevice, logLevel, message); - return resultCode; -} - -static ma_result ma_post_error(ma_device* pDevice, ma_uint32 logLevel, const char* message, ma_result resultCode) -{ - return ma_context_post_error(ma_device_get_context(pDevice), pDevice, logLevel, message, resultCode); -} - - /******************************************************************************* @@ -12001,7 +17468,7 @@ Timing *******************************************************************************/ #ifdef MA_WIN32 static LARGE_INTEGER g_ma_TimerFrequency; /* <-- Initialized to zero since it's static. */ - static void ma_timer_init(ma_timer* pTimer) + void ma_timer_init(ma_timer* pTimer) { LARGE_INTEGER counter; @@ -12013,7 +17480,7 @@ Timing pTimer->counter = counter.QuadPart; } - static double ma_timer_get_time_in_seconds(ma_timer* pTimer) + double ma_timer_get_time_in_seconds(ma_timer* pTimer) { LARGE_INTEGER counter; if (!QueryPerformanceCounter(&counter)) { @@ -12051,7 +17518,7 @@ Timing return (emscripten_get_now() - pTimer->counterD) / 1000; /* Emscripten is in milliseconds. */ } #else - #if _POSIX_C_SOURCE >= 199309L + #if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L #if defined(CLOCK_MONOTONIC) #define MA_CLOCK_ID CLOCK_MONOTONIC #else @@ -12215,51 +17682,234 @@ static ma_uint32 ma_get_closest_standard_sample_rate(ma_uint32 sampleRateIn) #endif +static MA_INLINE unsigned int ma_device_disable_denormals(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + if (!pDevice->noDisableDenormals) { + return ma_disable_denormals(); + } else { + return 0; + } +} + +static MA_INLINE void ma_device_restore_denormals(ma_device* pDevice, unsigned int prevState) +{ + MA_ASSERT(pDevice != NULL); + + if (!pDevice->noDisableDenormals) { + ma_restore_denormals(prevState); + } else { + /* Do nothing. */ + (void)prevState; + } +} + +static ma_device_notification ma_device_notification_init(ma_device* pDevice, ma_device_notification_type type) +{ + ma_device_notification notification; + + MA_ZERO_OBJECT(¬ification); + notification.pDevice = pDevice; + notification.type = type; + + return notification; +} + +static void ma_device__on_notification(ma_device_notification notification) +{ + MA_ASSERT(notification.pDevice != NULL); + + if (notification.pDevice->onNotification != NULL) { + notification.pDevice->onNotification(¬ification); + } + + /* TEMP FOR COMPATIBILITY: If it's a stopped notification, fire the onStop callback as well. This is only for backwards compatibility and will be removed. */ + if (notification.pDevice->onStop != NULL && notification.type == ma_device_notification_type_stopped) { + notification.pDevice->onStop(notification.pDevice); + } +} + +void ma_device__on_notification_started(ma_device* pDevice) +{ + ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_started)); +} + +void ma_device__on_notification_stopped(ma_device* pDevice) +{ + ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_stopped)); +} + +void ma_device__on_notification_rerouted(ma_device* pDevice) +{ + ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_rerouted)); +} + +void ma_device__on_notification_interruption_began(ma_device* pDevice) +{ + ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_began)); +} + +void ma_device__on_notification_interruption_ended(ma_device* pDevice) +{ + ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_ended)); +} + + +static void ma_device__on_data_inner(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) +{ + MA_ASSERT(pDevice != NULL); + MA_ASSERT(pDevice->onData != NULL); + + if (!pDevice->noPreSilencedOutputBuffer && pFramesOut != NULL) { + ma_silence_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels); + } + + pDevice->onData(pDevice, pFramesOut, pFramesIn, frameCount); +} + static void ma_device__on_data(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) +{ + MA_ASSERT(pDevice != NULL); + + if (pDevice->noFixedSizedCallback) { + /* Fast path. Not using a fixed sized callback. Process directly from the specified buffers. */ + ma_device__on_data_inner(pDevice, pFramesOut, pFramesIn, frameCount); + } else { + /* Slow path. Using a fixed sized callback. Need to use the intermediary buffer. */ + ma_uint32 totalFramesProcessed = 0; + + while (totalFramesProcessed < frameCount) { + ma_uint32 totalFramesRemaining = frameCount - totalFramesProcessed; + ma_uint32 framesToProcessThisIteration = 0; + + if (pFramesIn != NULL) { + /* Capturing. Write to the intermediary buffer. If there's no room, fire the callback to empty it. */ + if (pDevice->capture.intermediaryBufferLen < pDevice->capture.intermediaryBufferCap) { + /* There's some room left in the intermediary buffer. Write to it without firing the callback. */ + framesToProcessThisIteration = totalFramesRemaining; + if (framesToProcessThisIteration > pDevice->capture.intermediaryBufferCap - pDevice->capture.intermediaryBufferLen) { + framesToProcessThisIteration = pDevice->capture.intermediaryBufferCap - pDevice->capture.intermediaryBufferLen; + } + + ma_copy_pcm_frames( + ma_offset_pcm_frames_ptr(pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferLen, pDevice->capture.format, pDevice->capture.channels), + ma_offset_pcm_frames_const_ptr(pFramesIn, totalFramesProcessed, pDevice->capture.format, pDevice->capture.channels), + framesToProcessThisIteration, + pDevice->capture.format, pDevice->capture.channels); + + pDevice->capture.intermediaryBufferLen += framesToProcessThisIteration; + } + + if (pDevice->capture.intermediaryBufferLen == pDevice->capture.intermediaryBufferCap) { + /* No room left in the intermediary buffer. Fire the data callback. */ + if (pDevice->type == ma_device_type_duplex) { + /* We'll do the duplex data callback later after we've processed the playback data. */ + } else { + ma_device__on_data_inner(pDevice, NULL, pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap); + + /* The intermediary buffer has just been drained. */ + pDevice->capture.intermediaryBufferLen = 0; + } + } + } + + if (pFramesOut != NULL) { + /* Playing back. Read from the intermediary buffer. If there's nothing in it, fire the callback to fill it. */ + if (pDevice->playback.intermediaryBufferLen > 0) { + /* There's some content in the intermediary buffer. Read from that without firing the callback. */ + if (pDevice->type == ma_device_type_duplex) { + /* The frames processed this iteration for a duplex device will always be based on the capture side. Leave it unmodified. */ + } else { + framesToProcessThisIteration = totalFramesRemaining; + if (framesToProcessThisIteration > pDevice->playback.intermediaryBufferLen) { + framesToProcessThisIteration = pDevice->playback.intermediaryBufferLen; + } + } + + ma_copy_pcm_frames( + ma_offset_pcm_frames_ptr(pFramesOut, totalFramesProcessed, pDevice->playback.format, pDevice->playback.channels), + ma_offset_pcm_frames_ptr(pDevice->playback.pIntermediaryBuffer, pDevice->playback.intermediaryBufferCap - pDevice->playback.intermediaryBufferLen, pDevice->playback.format, pDevice->playback.channels), + framesToProcessThisIteration, + pDevice->playback.format, pDevice->playback.channels); + + pDevice->playback.intermediaryBufferLen -= framesToProcessThisIteration; + } + + if (pDevice->playback.intermediaryBufferLen == 0) { + /* There's nothing in the intermediary buffer. Fire the data callback to fill it. */ + if (pDevice->type == ma_device_type_duplex) { + /* In duplex mode, the data callback will be fired later. Nothing to do here. */ + } else { + ma_device__on_data_inner(pDevice, pDevice->playback.pIntermediaryBuffer, NULL, pDevice->playback.intermediaryBufferCap); + + /* The intermediary buffer has just been filled. */ + pDevice->playback.intermediaryBufferLen = pDevice->playback.intermediaryBufferCap; + } + } + } + + /* If we're in duplex mode we might need to do a refill of the data. */ + if (pDevice->type == ma_device_type_duplex) { + if (pDevice->capture.intermediaryBufferLen == pDevice->capture.intermediaryBufferCap) { + ma_device__on_data_inner(pDevice, pDevice->playback.pIntermediaryBuffer, pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap); + + pDevice->playback.intermediaryBufferLen = pDevice->playback.intermediaryBufferCap; /* The playback buffer will have just been filled. */ + pDevice->capture.intermediaryBufferLen = 0; /* The intermediary buffer has just been drained. */ + } + } + + /* Make sure this is only incremented once in the duplex case. */ + totalFramesProcessed += framesToProcessThisIteration; + } + } +} + +static void ma_device__handle_data_callback(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) { float masterVolumeFactor; ma_device_get_master_volume(pDevice, &masterVolumeFactor); /* Use ma_device_get_master_volume() to ensure the volume is loaded atomically. */ if (pDevice->onData) { - if (!pDevice->noPreZeroedOutputBuffer && pFramesOut != NULL) { - ma_silence_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels); - } + unsigned int prevDenormalState = ma_device_disable_denormals(pDevice); + { + /* Volume control of input makes things a bit awkward because the input buffer is read-only. We'll need to use a temp buffer and loop in this case. */ + if (pFramesIn != NULL && masterVolumeFactor < 1) { + ma_uint8 tempFramesIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; + ma_uint32 bpfCapture = ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); + ma_uint32 bpfPlayback = ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); + ma_uint32 totalFramesProcessed = 0; + while (totalFramesProcessed < frameCount) { + ma_uint32 framesToProcessThisIteration = frameCount - totalFramesProcessed; + if (framesToProcessThisIteration > sizeof(tempFramesIn)/bpfCapture) { + framesToProcessThisIteration = sizeof(tempFramesIn)/bpfCapture; + } - /* Volume control of input makes things a bit awkward because the input buffer is read-only. We'll need to use a temp buffer and loop in this case. */ - if (pFramesIn != NULL && masterVolumeFactor < 1) { - ma_uint8 tempFramesIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint32 bpfCapture = ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); - ma_uint32 bpfPlayback = ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); - ma_uint32 totalFramesProcessed = 0; - while (totalFramesProcessed < frameCount) { - ma_uint32 framesToProcessThisIteration = frameCount - totalFramesProcessed; - if (framesToProcessThisIteration > sizeof(tempFramesIn)/bpfCapture) { - framesToProcessThisIteration = sizeof(tempFramesIn)/bpfCapture; + ma_copy_and_apply_volume_factor_pcm_frames(tempFramesIn, ma_offset_ptr(pFramesIn, totalFramesProcessed*bpfCapture), framesToProcessThisIteration, pDevice->capture.format, pDevice->capture.channels, masterVolumeFactor); + + ma_device__on_data(pDevice, ma_offset_ptr(pFramesOut, totalFramesProcessed*bpfPlayback), tempFramesIn, framesToProcessThisIteration); + + totalFramesProcessed += framesToProcessThisIteration; + } + } else { + ma_device__on_data(pDevice, pFramesOut, pFramesIn, frameCount); + } + + /* Volume control and clipping for playback devices. */ + if (pFramesOut != NULL) { + if (masterVolumeFactor < 1) { + if (pFramesIn == NULL) { /* <-- In full-duplex situations, the volume will have been applied to the input samples before the data callback. Applying it again post-callback will incorrectly compound it. */ + ma_apply_volume_factor_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels, masterVolumeFactor); + } } - ma_copy_and_apply_volume_factor_pcm_frames(tempFramesIn, ma_offset_ptr(pFramesIn, totalFramesProcessed*bpfCapture), framesToProcessThisIteration, pDevice->capture.format, pDevice->capture.channels, masterVolumeFactor); - - pDevice->onData(pDevice, ma_offset_ptr(pFramesOut, totalFramesProcessed*bpfPlayback), tempFramesIn, framesToProcessThisIteration); - - totalFramesProcessed += framesToProcessThisIteration; - } - } else { - pDevice->onData(pDevice, pFramesOut, pFramesIn, frameCount); - } - - /* Volume control and clipping for playback devices. */ - if (pFramesOut != NULL) { - if (masterVolumeFactor < 1) { - if (pFramesIn == NULL) { /* <-- In full-duplex situations, the volume will have been applied to the input samples before the data callback. Applying it again post-callback will incorrectly compound it. */ - ma_apply_volume_factor_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels, masterVolumeFactor); + if (!pDevice->noClip && pDevice->playback.format == ma_format_f32) { + ma_clip_samples_f32((float*)pFramesOut, (const float*)pFramesOut, frameCount * pDevice->playback.channels); /* Intentionally specifying the same pointer for both input and output for in-place processing. */ } } - - if (!pDevice->noClip && pDevice->playback.format == ma_format_f32) { - ma_clip_pcm_frames_f32((float*)pFramesOut, frameCount, pDevice->playback.channels); - } } + ma_device_restore_denormals(pDevice, prevDenormalState); } } @@ -12273,58 +17923,99 @@ static void ma_device__read_frames_from_client(ma_device* pDevice, ma_uint32 fra MA_ASSERT(pFramesOut != NULL); if (pDevice->playback.converter.isPassthrough) { - ma_device__on_data(pDevice, pFramesOut, NULL, frameCount); + ma_device__handle_data_callback(pDevice, pFramesOut, NULL, frameCount); } else { ma_result result; ma_uint64 totalFramesReadOut; - ma_uint64 totalFramesReadIn; void* pRunningFramesOut; totalFramesReadOut = 0; - totalFramesReadIn = 0; pRunningFramesOut = pFramesOut; - while (totalFramesReadOut < frameCount) { - ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In client format. */ - ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); - ma_uint64 framesToReadThisIterationIn; - ma_uint64 framesReadThisIterationIn; - ma_uint64 framesToReadThisIterationOut; - ma_uint64 framesReadThisIterationOut; - ma_uint64 requiredInputFrameCount; + /* + We run slightly different logic depending on whether or not we're using a heap-allocated + buffer for caching input data. This will be the case if the data converter does not have + the ability to retrieve the required input frame count for a given output frame count. + */ + if (pDevice->playback.pInputCache != NULL) { + while (totalFramesReadOut < frameCount) { + ma_uint64 framesToReadThisIterationIn; + ma_uint64 framesToReadThisIterationOut; - framesToReadThisIterationOut = (frameCount - totalFramesReadOut); - framesToReadThisIterationIn = framesToReadThisIterationOut; - if (framesToReadThisIterationIn > intermediaryBufferCap) { - framesToReadThisIterationIn = intermediaryBufferCap; + /* If there's any data available in the cache, that needs to get processed first. */ + if (pDevice->playback.inputCacheRemaining > 0) { + framesToReadThisIterationOut = (frameCount - totalFramesReadOut); + framesToReadThisIterationIn = framesToReadThisIterationOut; + if (framesToReadThisIterationIn > pDevice->playback.inputCacheRemaining) { + framesToReadThisIterationIn = pDevice->playback.inputCacheRemaining; + } + + result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, ma_offset_pcm_frames_ptr(pDevice->playback.pInputCache, pDevice->playback.inputCacheConsumed, pDevice->playback.format, pDevice->playback.channels), &framesToReadThisIterationIn, pRunningFramesOut, &framesToReadThisIterationOut); + if (result != MA_SUCCESS) { + break; + } + + pDevice->playback.inputCacheConsumed += framesToReadThisIterationIn; + pDevice->playback.inputCacheRemaining -= framesToReadThisIterationIn; + + totalFramesReadOut += framesToReadThisIterationOut; + pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesToReadThisIterationOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); + + if (framesToReadThisIterationIn == 0 && framesToReadThisIterationOut == 0) { + break; /* We're done. */ + } + } + + /* Getting here means there's no data in the cache and we need to fill it up with data from the client. */ + if (pDevice->playback.inputCacheRemaining == 0) { + ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, NULL, (ma_uint32)pDevice->playback.inputCacheCap); + + pDevice->playback.inputCacheConsumed = 0; + pDevice->playback.inputCacheRemaining = pDevice->playback.inputCacheCap; + } } + } else { + while (totalFramesReadOut < frameCount) { + ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In client format. */ + ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); + ma_uint64 framesToReadThisIterationIn; + ma_uint64 framesReadThisIterationIn; + ma_uint64 framesToReadThisIterationOut; + ma_uint64 framesReadThisIterationOut; + ma_uint64 requiredInputFrameCount; - requiredInputFrameCount = ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, framesToReadThisIterationOut); - if (framesToReadThisIterationIn > requiredInputFrameCount) { - framesToReadThisIterationIn = requiredInputFrameCount; - } + framesToReadThisIterationOut = (frameCount - totalFramesReadOut); + framesToReadThisIterationIn = framesToReadThisIterationOut; + if (framesToReadThisIterationIn > intermediaryBufferCap) { + framesToReadThisIterationIn = intermediaryBufferCap; + } - if (framesToReadThisIterationIn > 0) { - ma_device__on_data(pDevice, pIntermediaryBuffer, NULL, (ma_uint32)framesToReadThisIterationIn); - totalFramesReadIn += framesToReadThisIterationIn; - } + ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, framesToReadThisIterationOut, &requiredInputFrameCount); + if (framesToReadThisIterationIn > requiredInputFrameCount) { + framesToReadThisIterationIn = requiredInputFrameCount; + } - /* - At this point we have our decoded data in input format and now we need to convert to output format. Note that even if we didn't read any - input frames, we still want to try processing frames because there may some output frames generated from cached input data. - */ - framesReadThisIterationIn = framesToReadThisIterationIn; - framesReadThisIterationOut = framesToReadThisIterationOut; - result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut); - if (result != MA_SUCCESS) { - break; - } + if (framesToReadThisIterationIn > 0) { + ma_device__handle_data_callback(pDevice, pIntermediaryBuffer, NULL, (ma_uint32)framesToReadThisIterationIn); + } - totalFramesReadOut += framesReadThisIterationOut; - pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); + /* + At this point we have our decoded data in input format and now we need to convert to output format. Note that even if we didn't read any + input frames, we still want to try processing frames because there may some output frames generated from cached input data. + */ + framesReadThisIterationIn = framesToReadThisIterationIn; + framesReadThisIterationOut = framesToReadThisIterationOut; + result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut); + if (result != MA_SUCCESS) { + break; + } - if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) { - break; /* We're done. */ + totalFramesReadOut += framesReadThisIterationOut; + pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); + + if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) { + break; /* We're done. */ + } } } } @@ -12338,7 +18029,7 @@ static void ma_device__send_frames_to_client(ma_device* pDevice, ma_uint32 frame MA_ASSERT(pFramesInDeviceFormat != NULL); if (pDevice->capture.converter.isPassthrough) { - ma_device__on_data(pDevice, NULL, pFramesInDeviceFormat, frameCountInDeviceFormat); + ma_device__handle_data_callback(pDevice, NULL, pFramesInDeviceFormat, frameCountInDeviceFormat); } else { ma_result result; ma_uint8 pFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; @@ -12361,7 +18052,7 @@ static void ma_device__send_frames_to_client(ma_device* pDevice, ma_uint32 frame } if (clientFramesProcessedThisIteration > 0) { - ma_device__on_data(pDevice, NULL, pFramesInClientFormat, (ma_uint32)clientFramesProcessedThisIteration); /* Safe cast. */ + ma_device__handle_data_callback(pDevice, NULL, pFramesInClientFormat, (ma_uint32)clientFramesProcessedThisIteration); /* Safe cast. */ } pRunningFramesInDeviceFormat = ma_offset_ptr(pRunningFramesInDeviceFormat, deviceFramesProcessedThisIteration * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); @@ -12396,7 +18087,7 @@ static ma_result ma_device__handle_duplex_callback_capture(ma_device* pDevice, m result = ma_pcm_rb_acquire_write(pRB, &framesToProcessInClientFormat, &pFramesInClientFormat); if (result != MA_SUCCESS) { - ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "Failed to acquire capture PCM frames from ring buffer.", result); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "Failed to acquire capture PCM frames from ring buffer."); break; } @@ -12414,9 +18105,9 @@ static ma_result ma_device__handle_duplex_callback_capture(ma_device* pDevice, m break; } - result = ma_pcm_rb_commit_write(pRB, (ma_uint32)framesProcessedInClientFormat, pFramesInClientFormat); /* Safe cast. */ + result = ma_pcm_rb_commit_write(pRB, (ma_uint32)framesProcessedInClientFormat); /* Safe cast. */ if (result != MA_SUCCESS) { - ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "Failed to commit capture PCM frames to ring buffer.", result); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "Failed to commit capture PCM frames to ring buffer."); break; } @@ -12435,16 +18126,14 @@ static ma_result ma_device__handle_duplex_callback_capture(ma_device* pDevice, m static ma_result ma_device__handle_duplex_callback_playback(ma_device* pDevice, ma_uint32 frameCount, void* pFramesInInternalFormat, ma_pcm_rb* pRB) { ma_result result; - ma_uint8 playbackFramesInExternalFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; ma_uint8 silentInputFrames[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint32 totalFramesToReadFromClient; - ma_uint32 totalFramesReadFromClient; ma_uint32 totalFramesReadOut = 0; MA_ASSERT(pDevice != NULL); MA_ASSERT(frameCount > 0); MA_ASSERT(pFramesInInternalFormat != NULL); MA_ASSERT(pRB != NULL); + MA_ASSERT(pDevice->playback.pInputCache != NULL); /* Sitting in the ring buffer should be captured data from the capture callback in external format. If there's not enough data in there for @@ -12452,68 +18141,61 @@ static ma_result ma_device__handle_duplex_callback_playback(ma_device* pDevice, */ MA_ZERO_MEMORY(silentInputFrames, sizeof(silentInputFrames)); - /* We need to calculate how many output frames are required to be read from the client to completely fill frameCount internal frames. */ - totalFramesToReadFromClient = (ma_uint32)ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, frameCount); - totalFramesReadFromClient = 0; - while (totalFramesReadFromClient < totalFramesToReadFromClient && ma_device_is_started(pDevice)) { - ma_uint32 framesRemainingFromClient; - ma_uint32 framesToProcessFromClient; - ma_uint32 inputFrameCount; - void* pInputFrames; - - framesRemainingFromClient = (totalFramesToReadFromClient - totalFramesReadFromClient); - framesToProcessFromClient = sizeof(playbackFramesInExternalFormat) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); - if (framesToProcessFromClient > framesRemainingFromClient) { - framesToProcessFromClient = framesRemainingFromClient; - } - - /* We need to grab captured samples before firing the callback. If there's not enough input samples we just pass silence. */ - inputFrameCount = framesToProcessFromClient; - result = ma_pcm_rb_acquire_read(pRB, &inputFrameCount, &pInputFrames); - if (result == MA_SUCCESS) { - if (inputFrameCount > 0) { - /* Use actual input frames. */ - ma_device__on_data(pDevice, playbackFramesInExternalFormat, pInputFrames, inputFrameCount); - } else { - if (ma_pcm_rb_pointer_distance(pRB) == 0) { - break; /* Underrun. */ - } - } - - /* We're done with the captured samples. */ - result = ma_pcm_rb_commit_read(pRB, inputFrameCount, pInputFrames); - if (result != MA_SUCCESS) { - break; /* Don't know what to do here... Just abandon ship. */ - } - } else { - /* Use silent input frames. */ - inputFrameCount = ma_min( - sizeof(playbackFramesInExternalFormat) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels), - sizeof(silentInputFrames) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels) - ); - - ma_device__on_data(pDevice, playbackFramesInExternalFormat, silentInputFrames, inputFrameCount); - } - - /* We have samples in external format so now we need to convert to internal format and output to the device. */ - { - ma_uint64 framesConvertedIn = inputFrameCount; + while (totalFramesReadOut < frameCount && ma_device_is_started(pDevice)) { + /* + We should have a buffer allocated on the heap. Any playback frames still sitting in there + need to be sent to the internal device before we process any more data from the client. + */ + if (pDevice->playback.inputCacheRemaining > 0) { + ma_uint64 framesConvertedIn = pDevice->playback.inputCacheRemaining; ma_uint64 framesConvertedOut = (frameCount - totalFramesReadOut); - ma_data_converter_process_pcm_frames(&pDevice->playback.converter, playbackFramesInExternalFormat, &framesConvertedIn, pFramesInInternalFormat, &framesConvertedOut); + ma_data_converter_process_pcm_frames(&pDevice->playback.converter, ma_offset_pcm_frames_ptr(pDevice->playback.pInputCache, pDevice->playback.inputCacheConsumed, pDevice->playback.format, pDevice->playback.channels), &framesConvertedIn, pFramesInInternalFormat, &framesConvertedOut); + + pDevice->playback.inputCacheConsumed += framesConvertedIn; + pDevice->playback.inputCacheRemaining -= framesConvertedIn; - totalFramesReadFromClient += (ma_uint32)framesConvertedIn; /* Safe cast. */ totalFramesReadOut += (ma_uint32)framesConvertedOut; /* Safe cast. */ pFramesInInternalFormat = ma_offset_ptr(pFramesInInternalFormat, framesConvertedOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); } + + /* If there's no more data in the cache we'll need to fill it with some. */ + if (totalFramesReadOut < frameCount && pDevice->playback.inputCacheRemaining == 0) { + ma_uint32 inputFrameCount; + void* pInputFrames; + + inputFrameCount = (ma_uint32)pDevice->playback.inputCacheCap; + result = ma_pcm_rb_acquire_read(pRB, &inputFrameCount, &pInputFrames); + if (result == MA_SUCCESS) { + if (inputFrameCount > 0) { + ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, pInputFrames, inputFrameCount); + } else { + if (ma_pcm_rb_pointer_distance(pRB) == 0) { + break; /* Underrun. */ + } + } + } else { + /* No capture data available. Feed in silence. */ + inputFrameCount = (ma_uint32)ma_min(pDevice->playback.inputCacheCap, sizeof(silentInputFrames) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels)); + ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, silentInputFrames, inputFrameCount); + } + + pDevice->playback.inputCacheConsumed = 0; + pDevice->playback.inputCacheRemaining = inputFrameCount; + + result = ma_pcm_rb_commit_read(pRB, inputFrameCount); + if (result != MA_SUCCESS) { + return result; /* Should never happen. */ + } + } } return MA_SUCCESS; } /* A helper for changing the state of the device. */ -static MA_INLINE void ma_device__set_state(ma_device* pDevice, ma_uint32 newState) +static MA_INLINE void ma_device__set_state(ma_device* pDevice, ma_device_state newState) { - c89atomic_exchange_32(&pDevice->state, newState); + c89atomic_exchange_i32((ma_int32*)&pDevice->state, (ma_int32)newState); } @@ -12541,7 +18223,6 @@ MA_API ma_uint32 ma_get_format_priority_index(ma_format format) /* Lower = bette static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType); - static ma_bool32 ma_device_descriptor_is_valid(const ma_device_descriptor* pDeviceDescriptor) { if (pDeviceDescriptor == NULL) { @@ -12552,7 +18233,7 @@ static ma_bool32 ma_device_descriptor_is_valid(const ma_device_descriptor* pDevi return MA_FALSE; } - if (pDeviceDescriptor->channels < MA_MIN_CHANNELS || pDeviceDescriptor->channels > MA_MAX_CHANNELS) { + if (pDeviceDescriptor->channels == 0 || pDeviceDescriptor->channels > MA_MAX_CHANNELS) { return MA_FALSE; } @@ -12594,7 +18275,7 @@ static ma_result ma_device_audio_thread__default_read_write(ma_device* pDevice) /* NOTE: The device was started outside of this function, in the worker thread. */ - while (ma_device_get_state(pDevice) == MA_STATE_STARTED && !exitLoop) { + while (ma_device_get_state(pDevice) == ma_device_state_started && !exitLoop) { switch (pDevice->type) { case ma_device_type_duplex: { @@ -12644,7 +18325,7 @@ static ma_result ma_device_audio_thread__default_read_write(ma_device* pDevice) break; } - ma_device__on_data(pDevice, playbackClientData, capturedClientData, (ma_uint32)capturedClientFramesToProcessThisIteration); /* Safe cast .*/ + ma_device__handle_data_callback(pDevice, playbackClientData, capturedClientData, (ma_uint32)capturedClientFramesToProcessThisIteration); /* Safe cast .*/ capturedDeviceFramesProcessed += (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */ capturedDeviceFramesRemaining -= (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */ @@ -12974,7 +18655,7 @@ static ma_result ma_device_init__null(ma_device* pDevice, const ma_device_config pDescriptorCapture->sampleRate = (pDescriptorCapture->sampleRate != 0) ? pDescriptorCapture->sampleRate : MA_DEFAULT_SAMPLE_RATE; if (pDescriptorCapture->channelMap[0] == MA_CHANNEL_NONE) { - ma_get_standard_channel_map(ma_standard_channel_map_default, pDescriptorCapture->channels, pDescriptorCapture->channelMap); + ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); } pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile); @@ -12986,7 +18667,7 @@ static ma_result ma_device_init__null(ma_device* pDevice, const ma_device_config pDescriptorPlayback->sampleRate = (pDescriptorPlayback->sampleRate != 0) ? pDescriptorPlayback->sampleRate : MA_DEFAULT_SAMPLE_RATE; if (pDescriptorPlayback->channelMap[0] == MA_CHANNEL_NONE) { - ma_get_standard_channel_map(ma_standard_channel_map_default, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap); + ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptorPlayback->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorPlayback->channels); } pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); @@ -13555,7 +19236,7 @@ static const PROPERTYKEY MA_PKEY_Device_FriendlyName = {{0xA45C254E, static const PROPERTYKEY MA_PKEY_AudioEngine_DeviceFormat = {{0xF19F064D, 0x82C, 0x4E27, {0xBC, 0x73, 0x68, 0x82, 0xA1, 0xBB, 0x8E, 0x4C}}, 0}; static const IID MA_IID_IUnknown = {0x00000000, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}; /* 00000000-0000-0000-C000-000000000046 */ -#ifndef MA_WIN32_DESKTOP +#if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK) static const IID MA_IID_IAgileObject = {0x94EA2B94, 0xE9CC, 0x49E0, {0xC0, 0xFF, 0xEE, 0x64, 0xCA, 0x8F, 0x5B, 0x90}}; /* 94EA2B94-E9CC-49E0-C0FF-EE64CA8F5B90 */ #endif @@ -13565,7 +19246,7 @@ static const IID MA_IID_IAudioClient3 = {0x7ED4EE07, static const IID MA_IID_IAudioRenderClient = {0xF294ACFC, 0x3146, 0x4483, {0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2}}; /* F294ACFC-3146-4483-A7BF-ADDCA7C260E2 = __uuidof(IAudioRenderClient) */ static const IID MA_IID_IAudioCaptureClient = {0xC8ADBD64, 0xE71E, 0x48A0, {0xA4, 0xDE, 0x18, 0x5C, 0x39, 0x5C, 0xD3, 0x17}}; /* C8ADBD64-E71E-48A0-A4DE-185C395CD317 = __uuidof(IAudioCaptureClient) */ static const IID MA_IID_IMMNotificationClient = {0x7991EEC9, 0x7E89, 0x4D85, {0x83, 0x90, 0x6C, 0x70, 0x3C, 0xEC, 0x60, 0xC0}}; /* 7991EEC9-7E89-4D85-8390-6C703CEC60C0 = __uuidof(IMMNotificationClient) */ -#ifndef MA_WIN32_DESKTOP +#if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK) static const IID MA_IID_DEVINTERFACE_AUDIO_RENDER = {0xE6327CAD, 0xDCEC, 0x4949, {0xAE, 0x8A, 0x99, 0x1E, 0x97, 0x6A, 0x79, 0xD2}}; /* E6327CAD-DCEC-4949-AE8A-991E976A79D2 */ static const IID MA_IID_DEVINTERFACE_AUDIO_CAPTURE = {0x2EEF81BE, 0x33FA, 0x4800, {0x96, 0x70, 0x1C, 0xD4, 0x74, 0x97, 0x2C, 0x3F}}; /* 2EEF81BE-33FA-4800-9670-1CD474972C3F */ static const IID MA_IID_IActivateAudioInterfaceCompletionHandler = {0x41D949AB, 0x9862, 0x444A, {0x80, 0xF6, 0xC2, 0x61, 0x33, 0x4D, 0xA5, 0xEB}}; /* 41D949AB-9862-444A-80F6-C261334DA5EB */ @@ -13582,7 +19263,7 @@ static const IID MA_IID_IMMDeviceEnumerator_Instance = {0xA95664D2, #endif typedef struct ma_IUnknown ma_IUnknown; -#ifdef MA_WIN32_DESKTOP +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) #define MA_MM_DEVICE_STATE_ACTIVE 1 #define MA_MM_DEVICE_STATE_DISABLED 2 #define MA_MM_DEVICE_STATE_NOTPRESENT 4 @@ -13668,7 +19349,7 @@ static MA_INLINE HRESULT ma_IUnknown_QueryInterface(ma_IUnknown* pThis, const II static MA_INLINE ULONG ma_IUnknown_AddRef(ma_IUnknown* pThis) { return pThis->lpVtbl->AddRef(pThis); } static MA_INLINE ULONG ma_IUnknown_Release(ma_IUnknown* pThis) { return pThis->lpVtbl->Release(pThis); } -#ifdef MA_WIN32_DESKTOP +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) /* IMMNotificationClient */ typedef struct { @@ -14012,7 +19693,7 @@ static MA_INLINE HRESULT ma_IAudioCaptureClient_GetBuffer(ma_IAudioCaptureClient static MA_INLINE HRESULT ma_IAudioCaptureClient_ReleaseBuffer(ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesRead); } static MA_INLINE HRESULT ma_IAudioCaptureClient_GetNextPacketSize(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket) { return pThis->lpVtbl->GetNextPacketSize(pThis, pNumFramesInNextPacket); } -#ifndef MA_WIN32_DESKTOP +#if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK) #include typedef struct ma_completion_handler_uwp ma_completion_handler_uwp; @@ -14029,7 +19710,7 @@ typedef struct struct ma_completion_handler_uwp { ma_completion_handler_uwp_vtbl* lpVtbl; - MA_ATOMIC ma_uint32 counter; + MA_ATOMIC(4, ma_uint32) counter; HANDLE hEvent; }; @@ -14109,7 +19790,7 @@ static void ma_completion_handler_uwp_wait(ma_completion_handler_uwp* pHandler) #endif /* !MA_WIN32_DESKTOP */ /* We need a virtual table for our notification client object that's used for detecting changes to the default device. */ -#ifdef MA_WIN32_DESKTOP +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_QueryInterface(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject) { /* @@ -14183,7 +19864,7 @@ static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(m use this to determine whether or not we need to automatically start the device when it's plugged back in again. */ - if (ma_device_get_state(pThis->pDevice) == MA_STATE_STARTED) { + if (ma_device_get_state(pThis->pDevice) == ma_device_state_started) { if (isPlayback) { pThis->pDevice->wasapi.isDetachedPlayback = MA_TRUE; } @@ -14293,13 +19974,13 @@ static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged /* Second attempt at device rerouting. We're going to retrieve the device's state at the time of the route change. We're then going to stop the device, reinitialize the device, and then start - it again if the state before stopping was MA_STATE_STARTED. + it again if the state before stopping was ma_device_state_started. */ { ma_uint32 previousState = ma_device_get_state(pThis->pDevice); ma_bool8 restartDevice = MA_FALSE; - if (previousState == MA_STATE_STARTED) { + if (previousState == ma_device_state_started) { ma_device_stop(pThis->pDevice); restartDevice = MA_TRUE; } @@ -14364,7 +20045,7 @@ static ma_IMMNotificationClientVtbl g_maNotificationCientVtbl = { }; #endif /* MA_WIN32_DESKTOP */ -#ifdef MA_WIN32_DESKTOP +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) typedef ma_IMMDevice ma_WASAPIDeviceInterface; #else typedef ma_IUnknown ma_WASAPIDeviceInterface; @@ -14584,7 +20265,8 @@ static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context if (SUCCEEDED(hr)) { ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(pWF, ma_share_mode_shared, pInfo); } else { - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve mix format for device info retrieval.", ma_result_from_HRESULT(hr)); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve mix format for device info retrieval."); + return ma_result_from_HRESULT(hr); } /* @@ -14592,7 +20274,7 @@ static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context UWP. Failure to retrieve the exclusive mode format is not considered an error, so from here on out, MA_SUCCESS is guaranteed to be returned. */ - #ifdef MA_WIN32_DESKTOP + #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) { ma_IPropertyStore *pProperties; @@ -14622,7 +20304,7 @@ static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context The format returned by PKEY_AudioEngine_DeviceFormat is not supported, so fall back to a search. We assume the channel count returned by MA_PKEY_AudioEngine_DeviceFormat is valid and correct. For simplicity we're only returning one format. */ - ma_uint32 channels = pInfo->minChannels; + ma_uint32 channels = pWF->nChannels; ma_channel defaultChannelMap[MA_MAX_CHANNELS]; WAVEFORMATEXTENSIBLE wf; ma_bool32 found; @@ -14633,7 +20315,7 @@ static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context channels = MA_MAX_CHANNELS; } - ma_get_standard_channel_map(ma_standard_channel_map_microsoft, channels, defaultChannelMap); + ma_channel_map_init_standard(ma_standard_channel_map_microsoft, defaultChannelMap, ma_countof(defaultChannelMap), channels); MA_ZERO_OBJECT(&wf); wf.Format.cbSize = sizeof(wf); @@ -14675,16 +20357,16 @@ static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context ma_PropVariantClear(pContext, &var); if (!found) { - ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to find suitable device format for device info retrieval.", MA_FORMAT_NOT_SUPPORTED); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to find suitable device format for device info retrieval."); } } } else { - ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to retrieve device format for device info retrieval.", ma_result_from_HRESULT(hr)); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to retrieve device format for device info retrieval."); } ma_IPropertyStore_Release(pProperties); } else { - ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to open property store for device info retrieval.", ma_result_from_HRESULT(hr)); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to open property store for device info retrieval."); } } #endif @@ -14692,7 +20374,7 @@ static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context return MA_SUCCESS; } -#ifdef MA_WIN32_DESKTOP +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) static ma_EDataFlow ma_device_type_to_EDataFlow(ma_device_type deviceType) { if (deviceType == ma_device_type_playback) { @@ -14717,7 +20399,8 @@ static ma_result ma_context_create_IMMDeviceEnumerator__wasapi(ma_context* pCont hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); if (FAILED(hr)) { - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.", ma_result_from_HRESULT(hr)); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator."); + return ma_result_from_HRESULT(hr); } *ppDeviceEnumerator = pDeviceEnumerator; @@ -14790,7 +20473,8 @@ static ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); if (FAILED(hr)) { - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create IMMDeviceEnumerator.", ma_result_from_HRESULT(hr)); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create IMMDeviceEnumerator."); + return ma_result_from_HRESULT(hr); } if (pDeviceID == NULL) { @@ -14801,7 +20485,8 @@ static ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); if (FAILED(hr)) { - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve IMMDevice.", ma_result_from_HRESULT(hr)); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve IMMDevice."); + return ma_result_from_HRESULT(hr); } return MA_SUCCESS; @@ -14881,7 +20566,8 @@ static ma_result ma_context_get_device_info_from_MMDevice__wasapi(ma_context* pC ma_IAudioClient_Release(pAudioClient); return result; } else { - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate audio client for device info retrieval.", ma_result_from_HRESULT(hr)); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate audio client for device info retrieval."); + return ma_result_from_HRESULT(hr); } } @@ -14908,7 +20594,8 @@ static ma_result ma_context_enumerate_devices_by_type__wasapi(ma_context* pConte if (SUCCEEDED(hr)) { hr = ma_IMMDeviceCollection_GetCount(pDeviceCollection, &deviceCount); if (FAILED(hr)) { - result = ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to get device count.", ma_result_from_HRESULT(hr)); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to get device count."); + result = ma_result_from_HRESULT(hr); goto done; } @@ -14999,13 +20686,15 @@ static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, m hr = StringFromIID(&iid, &iidStr); #endif if (FAILED(hr)) { - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to convert device IID to string for ActivateAudioInterfaceAsync(). Out of memory.", ma_result_from_HRESULT(hr)); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to convert device IID to string for ActivateAudioInterfaceAsync(). Out of memory."); + return ma_result_from_HRESULT(hr); } result = ma_completion_handler_uwp_init(&completionHandler); if (result != MA_SUCCESS) { ma_CoTaskMemFree(pContext, iidStr); - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for waiting for ActivateAudioInterfaceAsync().", result); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for waiting for ActivateAudioInterfaceAsync()."); + return result; } #if defined(__cplusplus) @@ -15016,7 +20705,8 @@ static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, m if (FAILED(hr)) { ma_completion_handler_uwp_uninit(&completionHandler); ma_CoTaskMemFree(pContext, iidStr); - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] ActivateAudioInterfaceAsync() failed.", ma_result_from_HRESULT(hr)); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] ActivateAudioInterfaceAsync() failed."); + return ma_result_from_HRESULT(hr); } ma_CoTaskMemFree(pContext, iidStr); @@ -15029,13 +20719,15 @@ static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, m ma_IActivateAudioInterfaceAsyncOperation_Release(pAsyncOp); if (FAILED(hr) || FAILED(activateResult)) { - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate device.", FAILED(hr) ? ma_result_from_HRESULT(hr) : ma_result_from_HRESULT(activateResult)); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate device."); + return FAILED(hr) ? ma_result_from_HRESULT(hr) : ma_result_from_HRESULT(activateResult); } /* Here is where we grab the IAudioClient interface. */ hr = ma_IUnknown_QueryInterface(pActivatedInterface, &MA_IID_IAudioClient, (void**)ppAudioClient); if (FAILED(hr)) { - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to query IAudioClient interface.", ma_result_from_HRESULT(hr)); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to query IAudioClient interface."); + return ma_result_from_HRESULT(hr); } if (ppActivatedInterface) { @@ -15050,7 +20742,7 @@ static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, m static ma_result ma_context_get_IAudioClient__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IAudioClient** ppAudioClient, ma_WASAPIDeviceInterface** ppDeviceInterface) { -#ifdef MA_WIN32_DESKTOP +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) return ma_context_get_IAudioClient_Desktop__wasapi(pContext, deviceType, pDeviceID, ppAudioClient, ppDeviceInterface); #else return ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, ppAudioClient, ppDeviceInterface); @@ -15061,14 +20753,15 @@ static ma_result ma_context_get_IAudioClient__wasapi(ma_context* pContext, ma_de static ma_result ma_context_enumerate_devices__wasapi(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) { /* Different enumeration for desktop and UWP. */ -#ifdef MA_WIN32_DESKTOP +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) /* Desktop */ HRESULT hr; ma_IMMDeviceEnumerator* pDeviceEnumerator; hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); if (FAILED(hr)) { - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.", ma_result_from_HRESULT(hr)); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator."); + return ma_result_from_HRESULT(hr); } ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_playback, callback, pUserData); @@ -15112,7 +20805,7 @@ static ma_result ma_context_enumerate_devices__wasapi(ma_context* pContext, ma_e static ma_result ma_context_get_device_info__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) { -#ifdef MA_WIN32_DESKTOP +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) ma_result result; ma_IMMDevice* pMMDevice = NULL; LPWSTR pDefaultDeviceID = NULL; @@ -15164,7 +20857,7 @@ static ma_result ma_device_uninit__wasapi(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); -#ifdef MA_WIN32_DESKTOP +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) if (pDevice->wasapi.pDeviceEnumerator) { ((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator)->lpVtbl->UnregisterEndpointNotificationCallback((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator, &pDevice->wasapi.notificationClient); ma_IMMDeviceEnumerator_Release((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator); @@ -15172,9 +20865,23 @@ static ma_result ma_device_uninit__wasapi(ma_device* pDevice) #endif if (pDevice->wasapi.pRenderClient) { + if (pDevice->wasapi.pMappedBufferPlayback != NULL) { + ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0); + pDevice->wasapi.pMappedBufferPlayback = NULL; + pDevice->wasapi.mappedBufferPlaybackCap = 0; + pDevice->wasapi.mappedBufferPlaybackLen = 0; + } + ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient); } if (pDevice->wasapi.pCaptureClient) { + if (pDevice->wasapi.pMappedBufferCapture != NULL) { + ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); + pDevice->wasapi.pMappedBufferCapture = NULL; + pDevice->wasapi.mappedBufferCaptureCap = 0; + pDevice->wasapi.mappedBufferCaptureLen = 0; + } + ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); } @@ -15293,7 +21000,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device /* Here is where we try to determine the best format to use with the device. If the client if wanting exclusive mode, first try finding the best format for that. If this fails, fall back to shared mode. */ result = MA_FORMAT_NOT_SUPPORTED; if (pData->shareMode == ma_share_mode_exclusive) { - #ifdef MA_WIN32_DESKTOP + #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) /* In exclusive mode on desktop we always use the backend's native format. */ ma_IPropertyStore* pStore = NULL; hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pStore); @@ -15405,7 +21112,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device /* Slightly different initialization for shared and exclusive modes. We try exclusive mode first, and if it fails, fall back to shared mode. */ if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) { - MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * 10; + MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10; /* If the periodicy is too small, Initialize() will fail with AUDCLNT_E_INVALID_DEVICE_PERIOD. In this case we should just keep increasing @@ -15439,7 +21146,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device /* Unfortunately we need to release and re-acquire the audio client according to MSDN. Seems silly - why not just call IAudioClient_Initialize() again?! */ ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient); - #ifdef MA_WIN32_DESKTOP + #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) hr = ma_IMMDevice_Activate(pDeviceInterface, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pData->pAudioClient); #else hr = ma_IUnknown_QueryInterface(pDeviceInterface, &MA_IID_IAudioClient, (void**)&pData->pAudioClient); @@ -15497,15 +21204,11 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device /* The period needs to be clamped between minPeriodInFrames and maxPeriodInFrames. */ actualPeriodInFrames = ma_clamp(actualPeriodInFrames, minPeriodInFrames, maxPeriodInFrames); - #if defined(MA_DEBUG_OUTPUT) - { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Trying IAudioClient3_InitializeSharedAudioStream(actualPeriodInFrames=%d)\n", actualPeriodInFrames); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " defaultPeriodInFrames=%d\n", defaultPeriodInFrames); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " fundamentalPeriodInFrames=%d\n", fundamentalPeriodInFrames); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " minPeriodInFrames=%d\n", minPeriodInFrames); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " maxPeriodInFrames=%d\n", maxPeriodInFrames); - } - #endif + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Trying IAudioClient3_InitializeSharedAudioStream(actualPeriodInFrames=%d)\n", actualPeriodInFrames); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " defaultPeriodInFrames=%d\n", defaultPeriodInFrames); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " fundamentalPeriodInFrames=%d\n", fundamentalPeriodInFrames); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " minPeriodInFrames=%d\n", minPeriodInFrames); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " maxPeriodInFrames=%d\n", maxPeriodInFrames); /* If the client requested a largish buffer than we don't actually want to use low latency shared mode because it forces small buffers. */ if (actualPeriodInFrames >= desiredPeriodInFrames) { @@ -15517,12 +21220,9 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device if (SUCCEEDED(hr)) { wasInitializedUsingIAudioClient3 = MA_TRUE; pData->periodSizeInFramesOut = actualPeriodInFrames; - #if defined(MA_DEBUG_OUTPUT) - { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Using IAudioClient3\n"); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " periodSizeInFramesOut=%d\n", pData->periodSizeInFramesOut); - } - #endif + + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Using IAudioClient3\n"); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " periodSizeInFramesOut=%d\n", pData->periodSizeInFramesOut); } else { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] IAudioClient3_InitializeSharedAudioStream failed. Falling back to IAudioClient.\n"); } @@ -15590,7 +21290,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device /* Grab the name of the device. */ - #ifdef MA_WIN32_DESKTOP + #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) { ma_IPropertyStore *pProperties; hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pProperties); @@ -15613,7 +21313,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device stream routing so that IDs can be compared and we can determine which device has been detached and whether or not it matches with our ma_device. */ - #ifdef MA_WIN32_DESKTOP + #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) { /* Desktop */ ma_context_get_device_id_from_MMDevice__wasapi(pContext, pDeviceInterface, &pData->id); @@ -15627,7 +21327,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device done: /* Clean up. */ -#ifdef MA_WIN32_DESKTOP +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) if (pDeviceInterface != NULL) { ma_IMMDevice_Release(pDeviceInterface); } @@ -15652,7 +21352,7 @@ done: } if (errorMsg != NULL && errorMsg[0] != '\0') { - ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_ERROR, errorMsg); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "%s", errorMsg); } return result; @@ -15750,7 +21450,7 @@ static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type dev ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, pDevice->wasapi.hEventCapture); pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut; - ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualPeriodSizeInFramesCapture); + ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture); /* We must always have a valid ID. */ ma_wcscpy_s(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi); @@ -15771,9 +21471,9 @@ static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type dev ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, pDevice->wasapi.hEventPlayback); pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut; - ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualPeriodSizeInFramesPlayback); + ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback); - /* We must always have a valid ID. */ + /* We must always have a valid ID because rerouting will look at it. */ ma_wcscpy_s(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi); } @@ -15784,7 +21484,7 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf { ma_result result = MA_SUCCESS; -#ifdef MA_WIN32_DESKTOP +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) HRESULT hr; ma_IMMDeviceEnumerator* pDeviceEnumerator; #endif @@ -15845,12 +21545,13 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf pDevice->wasapi.pAudioClientCapture = NULL; } - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for capture.", result); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for capture."); + return result; } ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, pDevice->wasapi.hEventCapture); pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut; - ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualPeriodSizeInFramesCapture); + ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture); /* We must always have a valid ID. */ ma_wcscpy_s(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi); @@ -15938,14 +21639,15 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf pDevice->wasapi.pAudioClientPlayback = NULL; } - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for playback.", result); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for playback."); + return result; } ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, pDevice->wasapi.hEventPlayback); pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut; - ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualPeriodSizeInFramesPlayback); + ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback); - /* We must always have a valid ID. */ + /* We must always have a valid ID because rerouting will look at it. */ ma_wcscpy_s(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi); /* The descriptor needs to be updated with actual values. */ @@ -15962,7 +21664,7 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf we are connecting to the default device we want to do automatic stream routing when the device is disabled or unplugged. Otherwise we want to just stop the device outright and let the application handle it. */ -#ifdef MA_WIN32_DESKTOP +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) if (pConfig->wasapi.noAutoStreamRouting == MA_FALSE) { if ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.pDeviceID == NULL) { pDevice->wasapi.allowCaptureAutoStreamRouting = MA_TRUE; @@ -15975,7 +21677,8 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf hr = ma_CoCreateInstance(pDevice->pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); if (FAILED(hr)) { ma_device_uninit__wasapi(pDevice); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.", ma_result_from_HRESULT(hr)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator."); + return ma_result_from_HRESULT(hr); } pDevice->wasapi.notificationClient.lpVtbl = (void*)&g_maNotificationCientVtbl; @@ -16036,16 +21739,16 @@ static ma_result ma_device__get_available_frames__wasapi(ma_device* pDevice, ma_ } if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) { - *pFrameCount = pDevice->wasapi.actualPeriodSizeInFramesPlayback - paddingFramesCount; + *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesPlayback - paddingFramesCount; } else { *pFrameCount = paddingFramesCount; } } else { /* Exclusive mode. */ if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) { - *pFrameCount = pDevice->wasapi.actualPeriodSizeInFramesPlayback; + *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesPlayback; } else { - *pFrameCount = pDevice->wasapi.actualPeriodSizeInFramesCapture; + *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesCapture; } } @@ -16065,12 +21768,14 @@ static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type de result = ma_device_reinit__wasapi(pDevice, deviceType); if (result != MA_SUCCESS) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Reinitializing device after route change failed.\n"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WASAPI] Reinitializing device after route change failed.\n"); return result; } ma_device__post_init_setup(pDevice, deviceType); + ma_device__on_notification_rerouted(pDevice); + return MA_SUCCESS; } @@ -16083,14 +21788,21 @@ static ma_result ma_device_start__wasapi(ma_device* pDevice) if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); if (FAILED(hr)) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device.", ma_result_from_HRESULT(hr)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device."); + return ma_result_from_HRESULT(hr); } c89atomic_exchange_32(&pDevice->wasapi.isStartedCapture, MA_TRUE); } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - /* No need to do anything for playback as that'll be started automatically in the data loop. */ + hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); + if (FAILED(hr)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device."); + return ma_result_from_HRESULT(hr); + } + + c89atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_TRUE); } return MA_SUCCESS; @@ -16106,13 +21818,23 @@ static ma_result ma_device_stop__wasapi(ma_device* pDevice) if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); if (FAILED(hr)) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal capture device.", ma_result_from_HRESULT(hr)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal capture device."); + return ma_result_from_HRESULT(hr); } /* The audio client needs to be reset otherwise restarting will fail. */ hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); if (FAILED(hr)) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal capture device.", ma_result_from_HRESULT(hr)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal capture device."); + return ma_result_from_HRESULT(hr); + } + + /* If we have a mapped buffer we need to release it. */ + if (pDevice->wasapi.pMappedBufferCapture != NULL) { + ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); + pDevice->wasapi.pMappedBufferCapture = NULL; + pDevice->wasapi.mappedBufferCaptureCap = 0; + pDevice->wasapi.mappedBufferCaptureLen = 0; } c89atomic_exchange_32(&pDevice->wasapi.isStartedCapture, MA_FALSE); @@ -16125,7 +21847,7 @@ static ma_result ma_device_stop__wasapi(ma_device* pDevice) */ if (c89atomic_load_32(&pDevice->wasapi.isStartedPlayback)) { /* We need to make sure we put a timeout here or else we'll risk getting stuck in a deadlock in some cases. */ - DWORD waitTime = pDevice->wasapi.actualPeriodSizeInFramesPlayback / pDevice->playback.internalSampleRate; + DWORD waitTime = pDevice->wasapi.actualBufferSizeInFramesPlayback / pDevice->playback.internalSampleRate; if (pDevice->playback.shareMode == ma_share_mode_exclusive) { WaitForSingleObject(pDevice->wasapi.hEventPlayback, waitTime); @@ -16138,7 +21860,7 @@ static ma_result ma_device_stop__wasapi(ma_device* pDevice) break; } - if (framesAvailablePlayback >= pDevice->wasapi.actualPeriodSizeInFramesPlayback) { + if (framesAvailablePlayback >= pDevice->wasapi.actualBufferSizeInFramesPlayback) { break; } @@ -16159,13 +21881,22 @@ static ma_result ma_device_stop__wasapi(ma_device* pDevice) hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); if (FAILED(hr)) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal playback device.", ma_result_from_HRESULT(hr)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal playback device."); + return ma_result_from_HRESULT(hr); } /* The audio client needs to be reset otherwise restarting will fail. */ hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); if (FAILED(hr)) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal playback device.", ma_result_from_HRESULT(hr)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal playback device."); + return ma_result_from_HRESULT(hr); + } + + if (pDevice->wasapi.pMappedBufferPlayback != NULL) { + ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0); + pDevice->wasapi.pMappedBufferPlayback = NULL; + pDevice->wasapi.mappedBufferPlaybackCap = 0; + pDevice->wasapi.mappedBufferPlaybackLen = 0; } c89atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_FALSE); @@ -16179,508 +21910,237 @@ static ma_result ma_device_stop__wasapi(ma_device* pDevice) #define MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS 5000 #endif -static ma_result ma_device_data_loop__wasapi(ma_device* pDevice) +static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) { - ma_result result; - HRESULT hr; - ma_bool32 exitLoop = MA_FALSE; - ma_uint32 framesWrittenToPlaybackDevice = 0; - ma_uint32 mappedDeviceBufferSizeInFramesCapture = 0; - ma_uint32 mappedDeviceBufferSizeInFramesPlayback = 0; - ma_uint32 mappedDeviceBufferFramesRemainingCapture = 0; - ma_uint32 mappedDeviceBufferFramesRemainingPlayback = 0; - BYTE* pMappedDeviceBufferCapture = NULL; - BYTE* pMappedDeviceBufferPlayback = NULL; - ma_uint32 bpfCaptureDevice = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - ma_uint32 bpfPlaybackDevice = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - ma_uint32 bpfCaptureClient = ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); - ma_uint32 bpfPlaybackClient = ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); - ma_uint8 inputDataInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint32 inputDataInClientFormatCap = 0; - ma_uint8 outputDataInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint32 outputDataInClientFormatCap = 0; - ma_uint32 outputDataInClientFormatCount = 0; - ma_uint32 outputDataInClientFormatConsumed = 0; - ma_uint32 periodSizeInFramesCapture = 0; + ma_result result = MA_SUCCESS; + ma_uint32 totalFramesProcessed = 0; - MA_ASSERT(pDevice != NULL); + /* + When reading, we need to get a buffer and process all of it before releasing it. Because the + frame count (frameCount) can be different to the size of the buffer, we'll need to cache the + pointer to the buffer. + */ - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { - periodSizeInFramesCapture = pDevice->capture.internalPeriodSizeInFrames; - inputDataInClientFormatCap = sizeof(inputDataInClientFormat) / bpfCaptureClient; - } + /* Keep running until we've processed the requested number of frames. */ + while (ma_device_get_state(pDevice) == ma_device_state_started && totalFramesProcessed < frameCount) { + ma_uint32 framesRemaining = frameCount - totalFramesProcessed; - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - outputDataInClientFormatCap = sizeof(outputDataInClientFormat) / bpfPlaybackClient; - } + /* If we have a mapped data buffer, consume that first. */ + if (pDevice->wasapi.pMappedBufferCapture != NULL) { + /* We have a cached data pointer so consume that before grabbing another one from WASAPI. */ + ma_uint32 framesToProcessNow = framesRemaining; + if (framesToProcessNow > pDevice->wasapi.mappedBufferCaptureLen) { + framesToProcessNow = pDevice->wasapi.mappedBufferCaptureLen; + } - while (ma_device_get_state(pDevice) == MA_STATE_STARTED && !exitLoop) { - switch (pDevice->type) - { - case ma_device_type_duplex: - { - ma_uint32 framesAvailableCapture; - ma_uint32 framesAvailablePlayback; - DWORD flagsCapture; /* Passed to IAudioCaptureClient_GetBuffer(). */ + /* Now just copy the data over to the output buffer. */ + ma_copy_pcm_frames( + ma_offset_pcm_frames_ptr(pFrames, totalFramesProcessed, pDevice->capture.internalFormat, pDevice->capture.internalChannels), + ma_offset_pcm_frames_const_ptr(pDevice->wasapi.pMappedBufferCapture, pDevice->wasapi.mappedBufferCaptureCap - pDevice->wasapi.mappedBufferCaptureLen, pDevice->capture.internalFormat, pDevice->capture.internalChannels), + framesToProcessNow, + pDevice->capture.internalFormat, pDevice->capture.internalChannels + ); - /* The process is to map the playback buffer and fill it as quickly as possible from input data. */ - if (pMappedDeviceBufferPlayback == NULL) { - result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback); - if (result != MA_SUCCESS) { - return result; - } + totalFramesProcessed += framesToProcessNow; + pDevice->wasapi.mappedBufferCaptureLen -= framesToProcessNow; - /* In exclusive mode, the frame count needs to exactly match the value returned by GetCurrentPadding(). */ - if (pDevice->playback.shareMode != ma_share_mode_exclusive) { - if (framesAvailablePlayback > pDevice->wasapi.periodSizeInFramesPlayback) { - framesAvailablePlayback = pDevice->wasapi.periodSizeInFramesPlayback; - } - } + /* If the data buffer has been fully consumed we need to release it. */ + if (pDevice->wasapi.mappedBufferCaptureLen == 0) { + ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); + pDevice->wasapi.pMappedBufferCapture = NULL; + pDevice->wasapi.mappedBufferCaptureCap = 0; + } + } else { + /* We don't have any cached data pointer, so grab another one. */ + HRESULT hr; + DWORD flags; - /* We're ready to map the playback device's buffer. We don't release this until it's been entirely filled. */ - hr = ma_IAudioRenderClient_GetBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, framesAvailablePlayback, &pMappedDeviceBufferPlayback); - if (FAILED(hr)) { - ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from playback device in preparation for writing to the device.", ma_result_from_HRESULT(hr)); - exitLoop = MA_TRUE; - break; - } - - mappedDeviceBufferSizeInFramesPlayback = framesAvailablePlayback; - mappedDeviceBufferFramesRemainingPlayback = framesAvailablePlayback; - } - - if (mappedDeviceBufferFramesRemainingPlayback > 0) { - /* At this point we should have a buffer available for output. We need to keep writing input samples to it. */ - for (;;) { - /* Try grabbing some captured data if we haven't already got a mapped buffer. */ - if (pMappedDeviceBufferCapture == NULL) { - if (pDevice->capture.shareMode == ma_share_mode_shared) { - if (WaitForSingleObject(pDevice->wasapi.hEventCapture, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { - return MA_ERROR; /* Wait failed. */ - } - } - - result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &framesAvailableCapture); - if (result != MA_SUCCESS) { - exitLoop = MA_TRUE; - break; - } - - /* Wait for more if nothing is available. */ - if (framesAvailableCapture == 0) { - /* In exclusive mode we waited at the top. */ - if (pDevice->capture.shareMode != ma_share_mode_shared) { - if (WaitForSingleObject(pDevice->wasapi.hEventCapture, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { - return MA_ERROR; /* Wait failed. */ - } - } - - continue; - } - - /* Getting here means there's data available for writing to the output device. */ - mappedDeviceBufferSizeInFramesCapture = ma_min(framesAvailableCapture, periodSizeInFramesCapture); - hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pMappedDeviceBufferCapture, &mappedDeviceBufferSizeInFramesCapture, &flagsCapture, NULL, NULL); - if (FAILED(hr)) { - ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from capture device in preparation for writing to the device.", ma_result_from_HRESULT(hr)); - exitLoop = MA_TRUE; - break; - } - - - /* Overrun detection. */ - if ((flagsCapture & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) { - /* Glitched. Probably due to an overrun. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity (possible overrun). framesAvailableCapture=%d, mappedBufferSizeInFramesCapture=%d\n", framesAvailableCapture, mappedDeviceBufferSizeInFramesCapture); - - /* - Exeriment: If we get an overrun it probably means we're straddling the end of the buffer. In order to prevent a never-ending sequence of glitches let's experiment - by dropping every frame until we're left with only a single period. To do this we just keep retrieving and immediately releasing buffers until we're down to the - last period. - */ - if (framesAvailableCapture >= pDevice->wasapi.actualPeriodSizeInFramesCapture) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Synchronizing capture stream. "); - do - { - hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, mappedDeviceBufferSizeInFramesCapture); - if (FAILED(hr)) { - break; - } - - framesAvailableCapture -= mappedDeviceBufferSizeInFramesCapture; - - if (framesAvailableCapture > 0) { - mappedDeviceBufferSizeInFramesCapture = ma_min(framesAvailableCapture, periodSizeInFramesCapture); - hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pMappedDeviceBufferCapture, &mappedDeviceBufferSizeInFramesCapture, &flagsCapture, NULL, NULL); - if (FAILED(hr)) { - ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from capture device in preparation for writing to the device.", ma_result_from_HRESULT(hr)); - exitLoop = MA_TRUE; - break; - } - } else { - pMappedDeviceBufferCapture = NULL; - mappedDeviceBufferSizeInFramesCapture = 0; - } - } while (framesAvailableCapture > periodSizeInFramesCapture); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "framesAvailableCapture=%d, mappedBufferSizeInFramesCapture=%d\n", framesAvailableCapture, mappedDeviceBufferSizeInFramesCapture); - } - } else { - #ifdef MA_DEBUG_OUTPUT - if (flagsCapture != 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Capture Flags: %ld\n", flagsCapture); - } - #endif - } - - mappedDeviceBufferFramesRemainingCapture = mappedDeviceBufferSizeInFramesCapture; - } - - - /* At this point we should have both input and output data available. We now need to convert the data and post it to the client. */ - for (;;) { - BYTE* pRunningDeviceBufferCapture; - BYTE* pRunningDeviceBufferPlayback; - ma_uint32 framesToProcess; - ma_uint32 framesProcessed; - - pRunningDeviceBufferCapture = pMappedDeviceBufferCapture + ((mappedDeviceBufferSizeInFramesCapture - mappedDeviceBufferFramesRemainingCapture ) * bpfCaptureDevice); - pRunningDeviceBufferPlayback = pMappedDeviceBufferPlayback + ((mappedDeviceBufferSizeInFramesPlayback - mappedDeviceBufferFramesRemainingPlayback) * bpfPlaybackDevice); - - /* There may be some data sitting in the converter that needs to be processed first. Once this is exhaused, run the data callback again. */ - if (!pDevice->playback.converter.isPassthrough && outputDataInClientFormatConsumed < outputDataInClientFormatCount) { - ma_uint64 convertedFrameCountClient = (outputDataInClientFormatCount - outputDataInClientFormatConsumed); - ma_uint64 convertedFrameCountDevice = mappedDeviceBufferFramesRemainingPlayback; - void* pConvertedFramesClient = outputDataInClientFormat + (outputDataInClientFormatConsumed * bpfPlaybackClient); - void* pConvertedFramesDevice = pRunningDeviceBufferPlayback; - result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pConvertedFramesClient, &convertedFrameCountClient, pConvertedFramesDevice, &convertedFrameCountDevice); - if (result != MA_SUCCESS) { - break; - } - - outputDataInClientFormatConsumed += (ma_uint32)convertedFrameCountClient; /* Safe cast. */ - mappedDeviceBufferFramesRemainingPlayback -= (ma_uint32)convertedFrameCountDevice; /* Safe cast. */ - - if (mappedDeviceBufferFramesRemainingPlayback == 0) { - break; - } - } - - /* - Getting here means we need to fire the callback. If format conversion is unnecessary, we can optimize this by passing the pointers to the internal - buffers directly to the callback. - */ - if (pDevice->capture.converter.isPassthrough && pDevice->playback.converter.isPassthrough) { - /* Optimal path. We can pass mapped pointers directly to the callback. */ - framesToProcess = ma_min(mappedDeviceBufferFramesRemainingCapture, mappedDeviceBufferFramesRemainingPlayback); - framesProcessed = framesToProcess; - - ma_device__on_data(pDevice, pRunningDeviceBufferPlayback, pRunningDeviceBufferCapture, framesToProcess); - - mappedDeviceBufferFramesRemainingCapture -= framesProcessed; - mappedDeviceBufferFramesRemainingPlayback -= framesProcessed; - - if (mappedDeviceBufferFramesRemainingCapture == 0) { - break; /* Exhausted input data. */ - } - if (mappedDeviceBufferFramesRemainingPlayback == 0) { - break; /* Exhausted output data. */ - } - } else if (pDevice->capture.converter.isPassthrough) { - /* The input buffer is a passthrough, but the playback buffer requires a conversion. */ - framesToProcess = ma_min(mappedDeviceBufferFramesRemainingCapture, outputDataInClientFormatCap); - framesProcessed = framesToProcess; - - ma_device__on_data(pDevice, outputDataInClientFormat, pRunningDeviceBufferCapture, framesToProcess); - outputDataInClientFormatCount = framesProcessed; - outputDataInClientFormatConsumed = 0; - - mappedDeviceBufferFramesRemainingCapture -= framesProcessed; - if (mappedDeviceBufferFramesRemainingCapture == 0) { - break; /* Exhausted input data. */ - } - } else if (pDevice->playback.converter.isPassthrough) { - /* The input buffer requires conversion, the playback buffer is passthrough. */ - ma_uint64 capturedDeviceFramesToProcess = mappedDeviceBufferFramesRemainingCapture; - ma_uint64 capturedClientFramesToProcess = ma_min(inputDataInClientFormatCap, mappedDeviceBufferFramesRemainingPlayback); - - result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningDeviceBufferCapture, &capturedDeviceFramesToProcess, inputDataInClientFormat, &capturedClientFramesToProcess); - if (result != MA_SUCCESS) { - break; - } - - if (capturedClientFramesToProcess == 0) { - break; - } - - ma_device__on_data(pDevice, pRunningDeviceBufferPlayback, inputDataInClientFormat, (ma_uint32)capturedClientFramesToProcess); /* Safe cast. */ - - mappedDeviceBufferFramesRemainingCapture -= (ma_uint32)capturedDeviceFramesToProcess; - mappedDeviceBufferFramesRemainingPlayback -= (ma_uint32)capturedClientFramesToProcess; - } else { - ma_uint64 capturedDeviceFramesToProcess = mappedDeviceBufferFramesRemainingCapture; - ma_uint64 capturedClientFramesToProcess = ma_min(inputDataInClientFormatCap, outputDataInClientFormatCap); - - result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningDeviceBufferCapture, &capturedDeviceFramesToProcess, inputDataInClientFormat, &capturedClientFramesToProcess); - if (result != MA_SUCCESS) { - break; - } - - if (capturedClientFramesToProcess == 0) { - break; - } - - ma_device__on_data(pDevice, outputDataInClientFormat, inputDataInClientFormat, (ma_uint32)capturedClientFramesToProcess); - - mappedDeviceBufferFramesRemainingCapture -= (ma_uint32)capturedDeviceFramesToProcess; - outputDataInClientFormatCount = (ma_uint32)capturedClientFramesToProcess; - outputDataInClientFormatConsumed = 0; - } - } - - - /* If at this point we've run out of capture data we need to release the buffer. */ - if (mappedDeviceBufferFramesRemainingCapture == 0 && pMappedDeviceBufferCapture != NULL) { - hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, mappedDeviceBufferSizeInFramesCapture); - if (FAILED(hr)) { - ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to release internal buffer from capture device after reading from the device.", ma_result_from_HRESULT(hr)); - exitLoop = MA_TRUE; - break; - } - - pMappedDeviceBufferCapture = NULL; - mappedDeviceBufferFramesRemainingCapture = 0; - mappedDeviceBufferSizeInFramesCapture = 0; - } - - /* Get out of this loop if we're run out of room in the playback buffer. */ - if (mappedDeviceBufferFramesRemainingPlayback == 0) { - break; - } - } - } - - - /* If at this point we've run out of data we need to release the buffer. */ - if (mappedDeviceBufferFramesRemainingPlayback == 0 && pMappedDeviceBufferPlayback != NULL) { - hr = ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, mappedDeviceBufferSizeInFramesPlayback, 0); - if (FAILED(hr)) { - ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to release internal buffer from playback device after writing to the device.", ma_result_from_HRESULT(hr)); - exitLoop = MA_TRUE; - break; - } - - framesWrittenToPlaybackDevice += mappedDeviceBufferSizeInFramesPlayback; - - pMappedDeviceBufferPlayback = NULL; - mappedDeviceBufferFramesRemainingPlayback = 0; - mappedDeviceBufferSizeInFramesPlayback = 0; - } - - if (!c89atomic_load_32(&pDevice->wasapi.isStartedPlayback)) { - ma_uint32 startThreshold = pDevice->playback.internalPeriodSizeInFrames * 1; - - /* Prevent a deadlock. If we don't clamp against the actual buffer size we'll never end up starting the playback device which will result in a deadlock. */ - if (startThreshold > pDevice->wasapi.actualPeriodSizeInFramesPlayback) { - startThreshold = pDevice->wasapi.actualPeriodSizeInFramesPlayback; - } - - if (pDevice->playback.shareMode == ma_share_mode_exclusive || framesWrittenToPlaybackDevice >= startThreshold) { - hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); - if (FAILED(hr)) { - ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device.", ma_result_from_HRESULT(hr)); - } - - c89atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_TRUE); - } - } - - /* Make sure the device has started before waiting. */ - if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { - return MA_ERROR; /* Wait failed. */ - } - } break; - - - - case ma_device_type_capture: - case ma_device_type_loopback: - { - ma_uint32 framesAvailableCapture; - DWORD flagsCapture; /* Passed to IAudioCaptureClient_GetBuffer(). */ - - /* Wait for data to become available first. */ - if (WaitForSingleObject(pDevice->wasapi.hEventCapture, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { - /* - For capture we can terminate here because it probably means the microphone just isn't delivering data for whatever reason, but - for loopback is most likely means nothing is actually playing. We want to keep trying in this situation. - */ - if (pDevice->type == ma_device_type_loopback) { - continue; /* Keep waiting in loopback mode. */ - } else { - exitLoop = MA_TRUE; - break; /* Wait failed. */ - } - } - - /* See how many frames are available. Since we waited at the top, I don't think this should ever return 0. I'm checking for this anyway. */ - result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &framesAvailableCapture); - if (result != MA_SUCCESS) { - exitLoop = MA_TRUE; - break; - } - - if (framesAvailableCapture < pDevice->wasapi.periodSizeInFramesCapture) { - continue; /* Nothing available. Keep waiting. */ - } - - /* Map the data buffer in preparation for sending to the client. */ - mappedDeviceBufferSizeInFramesCapture = framesAvailableCapture; - hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pMappedDeviceBufferCapture, &mappedDeviceBufferSizeInFramesCapture, &flagsCapture, NULL, NULL); - if (FAILED(hr)) { - ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from capture device in preparation for writing to the device.", ma_result_from_HRESULT(hr)); - exitLoop = MA_TRUE; - break; - } + /* First just ask WASAPI for a data buffer. If it's not available, we'll wait for more. */ + hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL); + if (hr == S_OK) { + /* We got a data buffer. Continue to the next loop iteration which will then read from the mapped pointer. */ /* Overrun detection. */ - if ((flagsCapture & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) { + if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) { /* Glitched. Probably due to an overrun. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity (possible overrun). framesAvailableCapture=%d, mappedBufferSizeInFramesCapture=%d\n", framesAvailableCapture, mappedDeviceBufferSizeInFramesCapture); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity (possible overrun). Attempting recovery. mappedBufferCaptureCap=%d\n", pDevice->wasapi.mappedBufferCaptureCap); /* - Exeriment: If we get an overrun it probably means we're straddling the end of the buffer. In order to prevent a never-ending sequence of glitches let's experiment - by dropping every frame until we're left with only a single period. To do this we just keep retrieving and immediately releasing buffers until we're down to the - last period. + If we got an overrun it probably means we're straddling the end of the buffer. In order to prevent + a never-ending sequence of glitches we're going to recover by completely clearing out the capture + buffer. */ - if (framesAvailableCapture >= pDevice->wasapi.actualPeriodSizeInFramesCapture) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Synchronizing capture stream. "); - do - { - hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, mappedDeviceBufferSizeInFramesCapture); + { + ma_uint32 iterationCount = 4; /* Safety to prevent an infinite loop. */ + ma_uint32 i; + + for (i = 0; i < iterationCount; i += 1) { + hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); if (FAILED(hr)) { break; } - framesAvailableCapture -= mappedDeviceBufferSizeInFramesCapture; - - if (framesAvailableCapture > 0) { - mappedDeviceBufferSizeInFramesCapture = ma_min(framesAvailableCapture, periodSizeInFramesCapture); - hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pMappedDeviceBufferCapture, &mappedDeviceBufferSizeInFramesCapture, &flagsCapture, NULL, NULL); - if (FAILED(hr)) { - ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from capture device in preparation for writing to the device.", ma_result_from_HRESULT(hr)); - exitLoop = MA_TRUE; - break; - } - } else { - pMappedDeviceBufferCapture = NULL; - mappedDeviceBufferSizeInFramesCapture = 0; + hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL); + if (hr == MA_AUDCLNT_S_BUFFER_EMPTY || FAILED(hr)) { + break; } - } while (framesAvailableCapture > periodSizeInFramesCapture); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "framesAvailableCapture=%d, mappedBufferSizeInFramesCapture=%d\n", framesAvailableCapture, mappedDeviceBufferSizeInFramesCapture); + } } + + /* We should not have a valid buffer at this point so make sure everything is empty. */ + pDevice->wasapi.pMappedBufferCapture = NULL; + pDevice->wasapi.mappedBufferCaptureCap = 0; + pDevice->wasapi.mappedBufferCaptureLen = 0; } else { - #ifdef MA_DEBUG_OUTPUT - if (flagsCapture != 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Capture Flags: %ld\n", flagsCapture); - } - #endif - } + /* The data is clean. */ + pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap; - /* We should have a buffer at this point, but let's just do a sanity check anyway. */ - if (mappedDeviceBufferSizeInFramesCapture > 0 && pMappedDeviceBufferCapture != NULL) { - ma_device__send_frames_to_client(pDevice, mappedDeviceBufferSizeInFramesCapture, pMappedDeviceBufferCapture); - - /* At this point we're done with the buffer. */ - hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, mappedDeviceBufferSizeInFramesCapture); - pMappedDeviceBufferCapture = NULL; /* <-- Important. Not doing this can result in an error once we leave this loop because it will use this to know whether or not a final ReleaseBuffer() needs to be called. */ - mappedDeviceBufferSizeInFramesCapture = 0; - if (FAILED(hr)) { - ma_post_log_message(ma_device_get_context(pDevice), pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to release internal buffer from capture device after reading from the device."); - exitLoop = MA_TRUE; - break; + if (flags != 0) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Capture Flags: %ld\n", flags); } } - } break; + continue; + } else { + if (hr == MA_AUDCLNT_S_BUFFER_EMPTY || hr == MA_AUDCLNT_E_BUFFER_ERROR) { + /* + No data is available. We need to wait for more. There's two situations to consider + here. The first is normal capture mode. If this times out it probably means the + microphone isn't delivering data for whatever reason. In this case we'll just + abort the read and return whatever we were able to get. The other situations is + loopback mode, in which case a timeout probably just means the nothing is playing + through the speakers. + */ + if (WaitForSingleObject(pDevice->wasapi.hEventCapture, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { + if (pDevice->type == ma_device_type_loopback) { + continue; /* Keep waiting in loopback mode. */ + } else { + result = MA_ERROR; + break; /* Wait failed. */ + } + } - - case ma_device_type_playback: - { - ma_uint32 framesAvailablePlayback; - - /* Check how much space is available. If this returns 0 we just keep waiting. */ - result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback); - if (result != MA_SUCCESS) { - exitLoop = MA_TRUE; + /* At this point we should be able to loop back to the start of the loop and try retrieving a data buffer again. */ + } else { + /* An error occured and we need to abort. */ + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from capture device in preparation for reading from the device. HRESULT = %d. Stopping device.\n", (int)hr); + result = ma_result_from_HRESULT(hr); break; } - - if (framesAvailablePlayback >= pDevice->wasapi.periodSizeInFramesPlayback) { - /* Map a the data buffer in preparation for the callback. */ - hr = ma_IAudioRenderClient_GetBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, framesAvailablePlayback, &pMappedDeviceBufferPlayback); - if (FAILED(hr)) { - ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from playback device in preparation for writing to the device.", ma_result_from_HRESULT(hr)); - exitLoop = MA_TRUE; - break; - } - - /* We should have a buffer at this point. */ - ma_device__read_frames_from_client(pDevice, framesAvailablePlayback, pMappedDeviceBufferPlayback); - - /* At this point we're done writing to the device and we just need to release the buffer. */ - hr = ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, framesAvailablePlayback, 0); - pMappedDeviceBufferPlayback = NULL; /* <-- Important. Not doing this can result in an error once we leave this loop because it will use this to know whether or not a final ReleaseBuffer() needs to be called. */ - mappedDeviceBufferSizeInFramesPlayback = 0; - - if (FAILED(hr)) { - ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to release internal buffer from playback device after writing to the device.", ma_result_from_HRESULT(hr)); - exitLoop = MA_TRUE; - break; - } - - framesWrittenToPlaybackDevice += framesAvailablePlayback; - } - - if (!c89atomic_load_32(&pDevice->wasapi.isStartedPlayback)) { - hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); - if (FAILED(hr)) { - ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device.", ma_result_from_HRESULT(hr)); - exitLoop = MA_TRUE; - break; - } - - c89atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_TRUE); - } - - /* Make sure we don't wait on the event before we've started the device or we may end up deadlocking. */ - if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { - exitLoop = MA_TRUE; - break; /* Wait failed. Probably timed out. */ - } - } break; - - default: return MA_INVALID_ARGS; + } } } - /* Here is where the device needs to be stopped. */ - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { - /* Any mapped buffers need to be released. */ - if (pMappedDeviceBufferCapture != NULL) { - hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, mappedDeviceBufferSizeInFramesCapture); + /* + If we were unable to process the entire requested frame count, but we still have a mapped buffer, + there's a good chance either an error occurred or the device was stopped mid-read. In this case + we'll need to make sure the buffer is released. + */ + if (totalFramesProcessed < frameCount && pDevice->wasapi.pMappedBufferCapture != NULL) { + ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); + pDevice->wasapi.pMappedBufferCapture = NULL; + pDevice->wasapi.mappedBufferCaptureCap = 0; + pDevice->wasapi.mappedBufferCaptureLen = 0; + } + + if (pFramesRead != NULL) { + *pFramesRead = totalFramesProcessed; + } + + return result; +} + +static ma_result ma_device_write__wasapi(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) +{ + ma_result result = MA_SUCCESS; + ma_uint32 totalFramesProcessed = 0; + + /* Keep writing to the device until it's stopped or we've consumed all of our input. */ + while (ma_device_get_state(pDevice) == ma_device_state_started && totalFramesProcessed < frameCount) { + ma_uint32 framesRemaining = frameCount - totalFramesProcessed; + + /* + We're going to do this in a similar way to capture. We'll first check if the cached data pointer + is valid, and if so, read from that. Otherwise We will call IAudioRenderClient_GetBuffer() with + a requested buffer size equal to our actual period size. If it returns AUDCLNT_E_BUFFER_TOO_LARGE + it means we need to wait for some data to become available. + */ + if (pDevice->wasapi.pMappedBufferPlayback != NULL) { + /* We still have some space available in the mapped data buffer. Write to it. */ + ma_uint32 framesToProcessNow = framesRemaining; + if (framesToProcessNow > (pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen)) { + framesToProcessNow = (pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen); + } + + /* Now just copy the data over to the output buffer. */ + ma_copy_pcm_frames( + ma_offset_pcm_frames_ptr(pDevice->wasapi.pMappedBufferPlayback, pDevice->wasapi.mappedBufferPlaybackLen, pDevice->playback.internalFormat, pDevice->playback.internalChannels), + ma_offset_pcm_frames_const_ptr(pFrames, totalFramesProcessed, pDevice->playback.internalFormat, pDevice->playback.internalChannels), + framesToProcessNow, + pDevice->playback.internalFormat, pDevice->playback.internalChannels + ); + + totalFramesProcessed += framesToProcessNow; + pDevice->wasapi.mappedBufferPlaybackLen += framesToProcessNow; + + /* If the data buffer has been fully consumed we need to release it. */ + if (pDevice->wasapi.mappedBufferPlaybackLen == pDevice->wasapi.mappedBufferPlaybackCap) { + ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0); + pDevice->wasapi.pMappedBufferPlayback = NULL; + pDevice->wasapi.mappedBufferPlaybackCap = 0; + pDevice->wasapi.mappedBufferPlaybackLen = 0; + + /* + In exclusive mode we need to wait here. Exclusive mode is weird because GetBuffer() never + seems to return AUDCLNT_E_BUFFER_TOO_LARGE, which is what we normally use to determine + whether or not we need to wait for more data. + */ + if (pDevice->playback.shareMode == ma_share_mode_exclusive) { + if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { + result = MA_ERROR; + break; /* Wait failed. Probably timed out. */ + } + } + } + } else { + /* We don't have a mapped data buffer so we'll need to get one. */ + HRESULT hr; + ma_uint32 bufferSizeInFrames; + + /* Special rules for exclusive mode. */ + if (pDevice->playback.shareMode == ma_share_mode_exclusive) { + bufferSizeInFrames = pDevice->wasapi.actualBufferSizeInFramesPlayback; + } else { + bufferSizeInFrames = pDevice->wasapi.periodSizeInFramesPlayback; + } + + hr = ma_IAudioRenderClient_GetBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, bufferSizeInFrames, (BYTE**)&pDevice->wasapi.pMappedBufferPlayback); + if (hr == S_OK) { + /* We have data available. */ + pDevice->wasapi.mappedBufferPlaybackCap = bufferSizeInFrames; + pDevice->wasapi.mappedBufferPlaybackLen = 0; + } else { + if (hr == MA_AUDCLNT_E_BUFFER_TOO_LARGE || hr == MA_AUDCLNT_E_BUFFER_ERROR) { + /* Not enough data available. We need to wait for more. */ + if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { + result = MA_ERROR; + break; /* Wait failed. Probably timed out. */ + } + } else { + /* Some error occurred. We'll need to abort. */ + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from playback device in preparation for writing to the device. HRESULT = %d. Stopping device.\n", (int)hr); + result = ma_result_from_HRESULT(hr); + break; + } + } } } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - /* Any mapped buffers need to be released. */ - if (pMappedDeviceBufferPlayback != NULL) { - hr = ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, mappedDeviceBufferSizeInFramesPlayback, 0); - } + if (pFramesWritten != NULL) { + *pFramesWritten = totalFramesProcessed; } - return MA_SUCCESS; + return result; } static ma_result ma_device_data_loop_wakeup__wasapi(ma_device* pDevice) @@ -16825,9 +22285,9 @@ static ma_result ma_context_init__wasapi(ma_context* pContext, const ma_context_ pCallbacks->onDeviceUninit = ma_device_uninit__wasapi; pCallbacks->onDeviceStart = ma_device_start__wasapi; pCallbacks->onDeviceStop = ma_device_stop__wasapi; - pCallbacks->onDeviceRead = NULL; /* Not used. Reading is done manually in the audio thread. */ - pCallbacks->onDeviceWrite = NULL; /* Not used. Writing is done manually in the audio thread. */ - pCallbacks->onDeviceDataLoop = ma_device_data_loop__wasapi; + pCallbacks->onDeviceRead = ma_device_read__wasapi; + pCallbacks->onDeviceWrite = ma_device_write__wasapi; + pCallbacks->onDeviceDataLoop = NULL; pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__wasapi; return MA_SUCCESS; @@ -17089,9 +22549,9 @@ struct ma_IDirectSoundCapture { ma_IDirectSoundCaptureVtbl* lpVtbl; }; -static MA_INLINE HRESULT ma_IDirectSoundCapture_QueryInterface(ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IDirectSoundCapture_AddRef(ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IDirectSoundCapture_Release(ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->Release(pThis); } +static MA_INLINE HRESULT ma_IDirectSoundCapture_QueryInterface (ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +static MA_INLINE ULONG ma_IDirectSoundCapture_AddRef (ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->AddRef(pThis); } +static MA_INLINE ULONG ma_IDirectSoundCapture_Release (ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->Release(pThis); } static MA_INLINE HRESULT ma_IDirectSoundCapture_CreateCaptureBuffer(ma_IDirectSoundCapture* pThis, const MA_DSCBUFFERDESC* pDSCBufferDesc, ma_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter) { return pThis->lpVtbl->CreateCaptureBuffer(pThis, pDSCBufferDesc, ppDSCBuffer, pUnkOuter); } static MA_INLINE HRESULT ma_IDirectSoundCapture_GetCaps (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCCaps); } static MA_INLINE HRESULT ma_IDirectSoundCapture_Initialize (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); } @@ -17250,7 +22710,8 @@ static ma_result ma_context_create_IDirectSound__dsound(ma_context* pContext, ma pDirectSound = NULL; if (FAILED(((ma_DirectSoundCreateProc)pContext->dsound.DirectSoundCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSound, NULL))) { - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCreate() failed for playback device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCreate() failed for playback device."); + return MA_FAILED_TO_OPEN_BACKEND_DEVICE; } /* The cooperative level must be set before doing anything else. */ @@ -17261,7 +22722,8 @@ static ma_result ma_context_create_IDirectSound__dsound(ma_context* pContext, ma hr = ma_IDirectSound_SetCooperativeLevel(pDirectSound, hWnd, (shareMode == ma_share_mode_exclusive) ? MA_DSSCL_EXCLUSIVE : MA_DSSCL_PRIORITY); if (FAILED(hr)) { - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_SetCooperateiveLevel() failed for playback device.", ma_result_from_HRESULT(hr)); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_SetCooperateiveLevel() failed for playback device."); + return ma_result_from_HRESULT(hr); } *ppDirectSound = pDirectSound; @@ -17286,7 +22748,8 @@ static ma_result ma_context_create_IDirectSoundCapture__dsound(ma_context* pCont hr = ((ma_DirectSoundCaptureCreateProc)pContext->dsound.DirectSoundCaptureCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSoundCapture, NULL); if (FAILED(hr)) { - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCaptureCreate() failed for capture device.", ma_result_from_HRESULT(hr)); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCaptureCreate() failed for capture device."); + return ma_result_from_HRESULT(hr); } *ppDirectSoundCapture = pDirectSoundCapture; @@ -17317,7 +22780,8 @@ static ma_result ma_context_get_format_info_for_IDirectSoundCapture__dsound(ma_c caps.dwSize = sizeof(caps); hr = ma_IDirectSoundCapture_GetCaps(pDirectSoundCapture, &caps); if (FAILED(hr)) { - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_GetCaps() failed for capture device.", ma_result_from_HRESULT(hr)); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_GetCaps() failed for capture device."); + return ma_result_from_HRESULT(hr); } if (pChannels) { @@ -17552,7 +23016,8 @@ static ma_result ma_context_get_device_info__dsound(ma_context* pContext, ma_dev caps.dwSize = sizeof(caps); hr = ma_IDirectSound_GetCaps(pDirectSound, &caps); if (FAILED(hr)) { - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device.", ma_result_from_HRESULT(hr)); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device."); + return ma_result_from_HRESULT(hr); } @@ -17628,13 +23093,13 @@ static ma_result ma_context_get_device_info__dsound(ma_context* pContext, ma_dev /* The format is always an integer format and is based on the bits per sample. */ if (bitsPerSample == 8) { - pDeviceInfo->formats[0] = ma_format_u8; + pDeviceInfo->nativeDataFormats[0].format = ma_format_u8; } else if (bitsPerSample == 16) { - pDeviceInfo->formats[0] = ma_format_s16; + pDeviceInfo->nativeDataFormats[0].format = ma_format_s16; } else if (bitsPerSample == 24) { - pDeviceInfo->formats[0] = ma_format_s24; + pDeviceInfo->nativeDataFormats[0].format = ma_format_s24; } else if (bitsPerSample == 32) { - pDeviceInfo->formats[0] = ma_format_s32; + pDeviceInfo->nativeDataFormats[0].format = ma_format_s32; } else { return MA_FORMAT_NOT_SUPPORTED; } @@ -17727,8 +23192,11 @@ static ma_result ma_config_to_WAVEFORMATEXTENSIBLE(ma_format format, ma_uint32 c static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__dsound(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) { - /* DirectSound has a minimum period size of 20ms. */ - ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(20, nativeSampleRate); + /* + DirectSound has a minimum period size of 20ms. In practice, this doesn't seem to be enough for + reliable glitch-free processing so going to use 30ms instead. + */ + ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(30, nativeSampleRate); ma_uint32 periodSizeInFrames; periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, performanceProfile); @@ -17799,7 +23267,8 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL); if (FAILED(hr)) { ma_device_uninit__dsound(pDevice); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_CreateCaptureBuffer() failed for capture device.", ma_result_from_HRESULT(hr)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_CreateCaptureBuffer() failed for capture device."); + return ma_result_from_HRESULT(hr); } /* Get the _actual_ properties of the buffer. */ @@ -17807,7 +23276,8 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf hr = ma_IDirectSoundCaptureBuffer_GetFormat((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, (WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL); if (FAILED(hr)) { ma_device_uninit__dsound(pDevice); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the capture device's buffer.", ma_result_from_HRESULT(hr)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the capture device's buffer."); + return ma_result_from_HRESULT(hr); } /* We can now start setting the output data formats. */ @@ -17833,7 +23303,8 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL); if (FAILED(hr)) { ma_device_uninit__dsound(pDevice); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Second attempt at IDirectSoundCapture_CreateCaptureBuffer() failed for capture device.", ma_result_from_HRESULT(hr)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Second attempt at IDirectSoundCapture_CreateCaptureBuffer() failed for capture device."); + return ma_result_from_HRESULT(hr); } } @@ -17869,7 +23340,8 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDSPrimary, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackPrimaryBuffer, NULL); if (FAILED(hr)) { ma_device_uninit__dsound(pDevice); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's primary buffer.", ma_result_from_HRESULT(hr)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's primary buffer."); + return ma_result_from_HRESULT(hr); } @@ -17879,7 +23351,8 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf hr = ma_IDirectSound_GetCaps((ma_IDirectSound*)pDevice->dsound.pPlayback, &caps); if (FAILED(hr)) { ma_device_uninit__dsound(pDevice); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device.", ma_result_from_HRESULT(hr)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device."); + return ma_result_from_HRESULT(hr); } if (pDescriptorPlayback->channels == 0) { @@ -17921,7 +23394,8 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (WAVEFORMATEX*)&wf); if (FAILED(hr)) { ma_device_uninit__dsound(pDevice); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to set format of playback device's primary buffer.", ma_result_from_HRESULT(hr)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to set format of playback device's primary buffer."); + return ma_result_from_HRESULT(hr); } /* Get the _actual_ properties of the buffer. */ @@ -17929,7 +23403,8 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf hr = ma_IDirectSoundBuffer_GetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL); if (FAILED(hr)) { ma_device_uninit__dsound(pDevice); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the playback device's primary buffer.", ma_result_from_HRESULT(hr)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the playback device's primary buffer."); + return ma_result_from_HRESULT(hr); } /* We now have enough information to start setting some output properties. */ @@ -17971,7 +23446,8 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDS, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackBuffer, NULL); if (FAILED(hr)) { ma_device_uninit__dsound(pDevice); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's secondary buffer.", ma_result_from_HRESULT(hr)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's secondary buffer."); + return ma_result_from_HRESULT(hr); } /* DirectSound should give us a buffer exactly the size we asked for. */ @@ -18011,12 +23487,14 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) /* The first thing to do is start the capture device. The playback device is only started after the first period is written. */ if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - if (FAILED(ma_IDirectSoundCaptureBuffer_Start((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, MA_DSCBSTART_LOOPING))) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Start() failed.", MA_FAILED_TO_START_BACKEND_DEVICE); + hr = ma_IDirectSoundCaptureBuffer_Start((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, MA_DSCBSTART_LOOPING); + if (FAILED(hr)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Start() failed."); + return ma_result_from_HRESULT(hr); } } - while (ma_device_get_state(pDevice) == MA_STATE_STARTED) { + while (ma_device_get_state(pDevice) == ma_device_state_started) { switch (pDevice->type) { case ma_device_type_duplex: @@ -18065,7 +23543,8 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0); if (FAILED(hr)) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from capture device in preparation for writing to the device.", ma_result_from_HRESULT(hr)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from capture device in preparation for writing to the device."); + return ma_result_from_HRESULT(hr); } @@ -18091,7 +23570,7 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) outputFramesInClientFormatCount = (ma_uint32)clientCapturedFramesToProcess; mappedDeviceFramesProcessedCapture += (ma_uint32)deviceCapturedFramesToProcess; - ma_device__on_data(pDevice, outputFramesInClientFormat, inputFramesInClientFormat, (ma_uint32)clientCapturedFramesToProcess); + ma_device__handle_data_callback(pDevice, outputFramesInClientFormat, inputFramesInClientFormat, (ma_uint32)clientCapturedFramesToProcess); /* At this point we have input and output data in client format. All we need to do now is convert it to the output device format. This may take a few passes. */ for (;;) { @@ -18119,7 +23598,7 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */ } else { /* This is an error. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[DirectSound] (Duplex/Playback) WARNING: Play cursor has moved in front of the write cursor (same loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Duplex/Playback): Play cursor has moved in front of the write cursor (same loop iteration). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); availableBytesPlayback = 0; } } else { @@ -18128,7 +23607,7 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; } else { /* This is an error. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[DirectSound] (Duplex/Playback) WARNING: Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Duplex/Playback): Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); availableBytesPlayback = 0; } } @@ -18140,7 +23619,8 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); if (FAILED(hr)) { ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.", ma_result_from_HRESULT(hr)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); + return ma_result_from_HRESULT(hr); } isPlaybackDeviceStarted = MA_TRUE; } else { @@ -18162,7 +23642,8 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0); if (FAILED(hr)) { - result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from playback device in preparation for writing to the device.", ma_result_from_HRESULT(hr)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from playback device in preparation for writing to the device."); + result = ma_result_from_HRESULT(hr); break; } @@ -18178,7 +23659,7 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) silentPaddingInBytes = lockSizeInBytesPlayback; } - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[DirectSound] (Duplex/Playback) Playback buffer starved. availableBytesPlayback=%ld, silentPaddingInBytes=%ld\n", availableBytesPlayback, silentPaddingInBytes); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Duplex/Playback) Playback buffer starved. availableBytesPlayback=%ld, silentPaddingInBytes=%ld\n", availableBytesPlayback, silentPaddingInBytes); } } @@ -18204,7 +23685,8 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, framesWrittenThisIteration*bpfDevicePlayback, NULL, 0); if (FAILED(hr)) { - result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from playback device after writing to the device.", ma_result_from_HRESULT(hr)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from playback device after writing to the device."); + result = ma_result_from_HRESULT(hr); break; } @@ -18223,7 +23705,8 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); if (FAILED(hr)) { ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.", ma_result_from_HRESULT(hr)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); + return ma_result_from_HRESULT(hr); } isPlaybackDeviceStarted = MA_TRUE; } @@ -18242,7 +23725,8 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) /* At this point we're done with the mapped portion of the capture buffer. */ hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0); if (FAILED(hr)) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from capture device after reading from the device.", ma_result_from_HRESULT(hr)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from capture device after reading from the device."); + return ma_result_from_HRESULT(hr); } prevReadCursorInBytesCapture = (lockOffsetInBytesCapture + mappedSizeInBytesCapture); } break; @@ -18292,20 +23776,20 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0); if (FAILED(hr)) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from capture device in preparation for writing to the device.", ma_result_from_HRESULT(hr)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from capture device in preparation for writing to the device."); + result = ma_result_from_HRESULT(hr); } - #ifdef MA_DEBUG_OUTPUT if (lockSizeInBytesCapture != mappedSizeInBytesCapture) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[DirectSound] (Capture) lockSizeInBytesCapture=%ld != mappedSizeInBytesCapture=%ld\n", lockSizeInBytesCapture, mappedSizeInBytesCapture); } - #endif ma_device__send_frames_to_client(pDevice, mappedSizeInBytesCapture/bpfDeviceCapture, pMappedDeviceBufferCapture); hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0); if (FAILED(hr)) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from capture device after reading from the device.", ma_result_from_HRESULT(hr)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from capture device after reading from the device."); + return ma_result_from_HRESULT(hr); } prevReadCursorInBytesCapture = lockOffsetInBytesCapture + mappedSizeInBytesCapture; @@ -18339,7 +23823,7 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */ } else { /* This is an error. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[DirectSound] (Playback) WARNING: Play cursor has moved in front of the write cursor (same loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Playback): Play cursor has moved in front of the write cursor (same loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); availableBytesPlayback = 0; } } else { @@ -18348,7 +23832,7 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; } else { /* This is an error. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[DirectSound] (Playback) WARNING: Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Playback): Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); availableBytesPlayback = 0; } } @@ -18359,7 +23843,8 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) if (availableBytesPlayback == 0 && !isPlaybackDeviceStarted) { hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); if (FAILED(hr)) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.", ma_result_from_HRESULT(hr)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); + return ma_result_from_HRESULT(hr); } isPlaybackDeviceStarted = MA_TRUE; } else { @@ -18380,7 +23865,8 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0); if (FAILED(hr)) { - result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from playback device in preparation for writing to the device.", ma_result_from_HRESULT(hr)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from playback device in preparation for writing to the device."); + result = ma_result_from_HRESULT(hr); break; } @@ -18389,7 +23875,8 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, mappedSizeInBytesPlayback, NULL, 0); if (FAILED(hr)) { - result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from playback device after writing to the device.", ma_result_from_HRESULT(hr)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from playback device after writing to the device."); + result = ma_result_from_HRESULT(hr); break; } @@ -18407,7 +23894,8 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= pDevice->playback.internalPeriodSizeInFrames) { hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); if (FAILED(hr)) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.", ma_result_from_HRESULT(hr)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); + return ma_result_from_HRESULT(hr); } isPlaybackDeviceStarted = MA_TRUE; } @@ -18426,7 +23914,8 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { hr = ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); if (FAILED(hr)) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Stop() failed.", ma_result_from_HRESULT(hr)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Stop() failed."); + return ma_result_from_HRESULT(hr); } } @@ -18474,7 +23963,8 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) hr = ma_IDirectSoundBuffer_Stop((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer); if (FAILED(hr)) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Stop() failed.", ma_result_from_HRESULT(hr)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Stop() failed."); + return ma_result_from_HRESULT(hr); } ma_IDirectSoundBuffer_SetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0); @@ -18789,10 +24279,10 @@ static ma_result ma_context_get_device_info_from_WAVECAPS(ma_context* pContext, if (((MA_PFN_RegOpenKeyExA)pContext->win32.RegOpenKeyExA)(HKEY_LOCAL_MACHINE, keyStr, 0, KEY_READ, &hKey) == ERROR_SUCCESS) { BYTE nameFromReg[512]; DWORD nameFromRegSize = sizeof(nameFromReg); - result = ((MA_PFN_RegQueryValueExA)pContext->win32.RegQueryValueExA)(hKey, "Name", 0, NULL, (LPBYTE)nameFromReg, (LPDWORD)&nameFromRegSize); + LONG resultWin32 = ((MA_PFN_RegQueryValueExA)pContext->win32.RegQueryValueExA)(hKey, "Name", 0, NULL, (LPBYTE)nameFromReg, (LPDWORD)&nameFromRegSize); ((MA_PFN_RegCloseKey)pContext->win32.RegCloseKey)(hKey); - if (result == ERROR_SUCCESS) { + if (resultWin32 == ERROR_SUCCESS) { /* We have the value from the registry, so now we need to construct the name string. */ char name[1024]; if (ma_strcpy_s(name, sizeof(name), pDeviceInfo->name) == 0) { @@ -19000,7 +24490,7 @@ static ma_result ma_device_uninit__winmm(ma_device* pDevice) CloseHandle((HANDLE)pDevice->winmm.hEventPlayback); } - ma__free_from_callbacks(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks); + ma_free(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks); MA_ZERO_OBJECT(&pDevice->winmm); /* Safety. */ @@ -19085,7 +24575,7 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi pDescriptorCapture->format = ma_format_from_WAVEFORMATEX(&wf); pDescriptorCapture->channels = wf.nChannels; pDescriptorCapture->sampleRate = wf.nSamplesPerSec; - ma_get_standard_channel_map(ma_standard_channel_map_microsoft, pDescriptorCapture->channels, pDescriptorCapture->channelMap); + ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); pDescriptorCapture->periodCount = pDescriptorCapture->periodCount; pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile); } @@ -19096,7 +24586,7 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi MMRESULT resultMM; /* We use an event to know when a new fragment needs to be enqueued. */ - pDevice->winmm.hEventPlayback = (ma_handle)CreateEvent(NULL, TRUE, TRUE, NULL); + pDevice->winmm.hEventPlayback = (ma_handle)CreateEventW(NULL, TRUE, TRUE, NULL); if (pDevice->winmm.hEventPlayback == NULL) { errorMsg = "[WinMM] Failed to create event for fragment enqueing for the playback device.", errorCode = ma_result_from_GetLastError(GetLastError()); goto on_error; @@ -19123,7 +24613,7 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi pDescriptorPlayback->format = ma_format_from_WAVEFORMATEX(&wf); pDescriptorPlayback->channels = wf.nChannels; pDescriptorPlayback->sampleRate = wf.nSamplesPerSec; - ma_get_standard_channel_map(ma_standard_channel_map_microsoft, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap); + ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels); pDescriptorPlayback->periodCount = pDescriptorPlayback->periodCount; pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); } @@ -19141,7 +24631,7 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi heapSize += sizeof(WAVEHDR)*pDescriptorPlayback->periodCount + (pDescriptorPlayback->periodSizeInFrames * pDescriptorPlayback->periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels)); } - pDevice->winmm._pHeapData = (ma_uint8*)ma__calloc_from_callbacks(heapSize, &pDevice->pContext->allocationCallbacks); + pDevice->winmm._pHeapData = (ma_uint8*)ma_calloc(heapSize, &pDevice->pContext->allocationCallbacks); if (pDevice->winmm._pHeapData == NULL) { errorMsg = "[WinMM] Failed to allocate memory for the intermediary buffer.", errorCode = MA_OUT_OF_MEMORY; goto on_error; @@ -19232,8 +24722,13 @@ on_error: ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((HWAVEOUT)pDevice->winmm.hDevicePlayback); } - ma__free_from_callbacks(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, errorMsg, errorCode); + ma_free(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks); + + if (errorMsg != NULL && errorMsg[0] != '\0') { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "%s", errorMsg); + } + + return errorCode; } static ma_result ma_device_start__winmm(ma_device* pDevice) @@ -19254,7 +24749,8 @@ static ma_result ma_device_start__winmm(ma_device* pDevice) for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) { resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((LPWAVEHDR)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(WAVEHDR)); if (resultMM != MMSYSERR_NOERROR) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] Failed to attach input buffers to capture device in preparation for capture.", ma_result_from_MMRESULT(resultMM)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] Failed to attach input buffers to capture device in preparation for capture."); + return ma_result_from_MMRESULT(resultMM); } /* Make sure all of the buffers start out locked. We don't want to access them until the backend tells us we can. */ @@ -19264,7 +24760,8 @@ static ma_result ma_device_start__winmm(ma_device* pDevice) /* Capture devices need to be explicitly started, unlike playback devices. */ resultMM = ((MA_PFN_waveInStart)pDevice->pContext->winmm.waveInStart)((HWAVEIN)pDevice->winmm.hDeviceCapture); if (resultMM != MMSYSERR_NOERROR) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] Failed to start backend device.", ma_result_from_MMRESULT(resultMM)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] Failed to start backend device."); + return ma_result_from_MMRESULT(resultMM); } } @@ -19288,7 +24785,7 @@ static ma_result ma_device_stop__winmm(ma_device* pDevice) resultMM = ((MA_PFN_waveInReset)pDevice->pContext->winmm.waveInReset)((HWAVEIN)pDevice->winmm.hDeviceCapture); if (resultMM != MMSYSERR_NOERROR) { - ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] WARNING: Failed to reset capture device.", ma_result_from_MMRESULT(resultMM)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WinMM] WARNING: Failed to reset capture device."); } } @@ -19314,7 +24811,7 @@ static ma_result ma_device_stop__winmm(ma_device* pDevice) resultMM = ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((HWAVEOUT)pDevice->winmm.hDevicePlayback); if (resultMM != MMSYSERR_NOERROR) { - ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] WARNING: Failed to reset playback device.", ma_result_from_MMRESULT(resultMM)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WinMM] WARNING: Failed to reset playback device."); } } @@ -19369,7 +24866,7 @@ static ma_result ma_device_write__winmm(ma_device* pDevice, const void* pPCMFram resultMM = ((MA_PFN_waveOutWrite)pDevice->pContext->winmm.waveOutWrite)((HWAVEOUT)pDevice->winmm.hDevicePlayback, &pWAVEHDR[pDevice->winmm.iNextHeaderPlayback], sizeof(WAVEHDR)); if (resultMM != MMSYSERR_NOERROR) { result = ma_result_from_MMRESULT(resultMM); - ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] waveOutWrite() failed.", result); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] waveOutWrite() failed."); break; } @@ -19401,7 +24898,7 @@ static ma_result ma_device_write__winmm(ma_device* pDevice, const void* pPCMFram } /* If the device has been stopped we need to break. */ - if (ma_device_get_state(pDevice) != MA_STATE_STARTED) { + if (ma_device_get_state(pDevice) != ma_device_state_started) { break; } } @@ -19458,7 +24955,7 @@ static ma_result ma_device_read__winmm(ma_device* pDevice, void* pPCMFrames, ma_ resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((LPWAVEHDR)pDevice->winmm.pWAVEHDRCapture)[pDevice->winmm.iNextHeaderCapture], sizeof(WAVEHDR)); if (resultMM != MMSYSERR_NOERROR) { result = ma_result_from_MMRESULT(resultMM); - ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] waveInAddBuffer() failed.", result); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] waveInAddBuffer() failed."); break; } @@ -19490,7 +24987,7 @@ static ma_result ma_device_read__winmm(ma_device* pDevice, void* pPCMFrames, ma_ } /* If the device has been stopped we need to break. */ - if (ma_device_get_state(pDevice) != MA_STATE_STARTED) { + if (ma_device_get_state(pDevice) != ma_device_state_started) { break; } } @@ -20206,7 +25703,8 @@ static ma_result ma_context_open_pcm__alsa(ma_context* pContext, ma_share_mode s } if (!isDeviceOpen) { - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed when trying to open an appropriate default device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed when trying to open an appropriate default device."); + return MA_FAILED_TO_OPEN_BACKEND_DEVICE; } } else { /* @@ -20254,7 +25752,8 @@ static ma_result ma_context_open_pcm__alsa(ma_context* pContext, ma_share_mode s } if (resultALSA < 0) { - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed.", ma_result_from_errno(-resultALSA)); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed."); + return ma_result_from_errno(-resultALSA); } } @@ -20325,9 +25824,8 @@ static ma_result ma_context_enumerate_devices__alsa(ma_context* pContext, ma_enu goto next_device; /* The device has already been enumerated. Move on to the next one. */ } else { /* The device has not yet been enumerated. Make sure it's added to our list so that it's not enumerated again. */ - size_t oldCapacity = sizeof(*pUniqueIDs) * uniqueIDCount; size_t newCapacity = sizeof(*pUniqueIDs) * (uniqueIDCount + 1); - ma_device_id* pNewUniqueIDs = (ma_device_id*)ma__realloc_from_callbacks(pUniqueIDs, newCapacity, oldCapacity, &pContext->allocationCallbacks); + ma_device_id* pNewUniqueIDs = (ma_device_id*)ma_realloc(pUniqueIDs, newCapacity, &pContext->allocationCallbacks); if (pNewUniqueIDs == NULL) { goto next_device; /* Failed to allocate memory. */ } @@ -20392,10 +25890,11 @@ static ma_result ma_context_enumerate_devices__alsa(ma_context* pContext, ma_enu /* Some devices are both playback and capture, but they are only enumerated by ALSA once. We need to fire the callback - again for the other device type in this case. We do this for known devices. + again for the other device type in this case. We do this for known devices and where the IOID hint is NULL, which + means both Input and Output. */ if (cbResult) { - if (ma_is_common_device_name__alsa(NAME)) { + if (ma_is_common_device_name__alsa(NAME) || IOID == NULL) { if (deviceType == ma_device_type_playback) { if (!ma_is_capture_device_blacklisted__alsa(NAME)) { cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); @@ -20424,7 +25923,7 @@ static ma_result ma_context_enumerate_devices__alsa(ma_context* pContext, ma_enu } } - ma__free_from_callbacks(pUniqueIDs, &pContext->allocationCallbacks); + ma_free(pUniqueIDs, &pContext->allocationCallbacks); ((ma_snd_device_name_free_hint_proc)pContext->alsa.snd_device_name_free_hint)((void**)ppDeviceHints); ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock); @@ -20548,7 +26047,7 @@ static ma_result ma_context_get_device_info__alsa(ma_context* pContext, ma_devic } /* We need to initialize a HW parameters object in order to know what formats are supported. */ - pHWParams = (ma_snd_pcm_hw_params_t*)ma__calloc_from_callbacks(((ma_snd_pcm_hw_params_sizeof_proc)pContext->alsa.snd_pcm_hw_params_sizeof)(), &pContext->allocationCallbacks); + pHWParams = (ma_snd_pcm_hw_params_t*)ma_calloc(((ma_snd_pcm_hw_params_sizeof_proc)pContext->alsa.snd_pcm_hw_params_sizeof)(), &pContext->allocationCallbacks); if (pHWParams == NULL) { ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM); return MA_OUT_OF_MEMORY; @@ -20556,9 +26055,10 @@ static ma_result ma_context_get_device_info__alsa(ma_context* pContext, ma_devic resultALSA = ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); if (resultALSA < 0) { - ma__free_from_callbacks(pHWParams, &pContext->allocationCallbacks); + ma_free(pHWParams, &pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM); - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed.", ma_result_from_errno(-resultALSA)); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed."); + return ma_result_from_errno(-resultALSA); } /* @@ -20642,7 +26142,7 @@ static ma_result ma_context_get_device_info__alsa(ma_context* pContext, ma_devic } } - ma__free_from_callbacks(pHWParams, &pContext->allocationCallbacks); + ma_free(pHWParams, &pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM); return MA_SUCCESS; @@ -20712,16 +26212,19 @@ static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_devic /* Hardware parameters. */ - pHWParams = (ma_snd_pcm_hw_params_t*)ma__calloc_from_callbacks(((ma_snd_pcm_hw_params_sizeof_proc)pDevice->pContext->alsa.snd_pcm_hw_params_sizeof)(), &pDevice->pContext->allocationCallbacks); + pHWParams = (ma_snd_pcm_hw_params_t*)ma_calloc(((ma_snd_pcm_hw_params_sizeof_proc)pDevice->pContext->alsa.snd_pcm_hw_params_sizeof)(), &pDevice->pContext->allocationCallbacks); if (pHWParams == NULL) { + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for hardware parameters."); return MA_OUT_OF_MEMORY; } resultALSA = ((ma_snd_pcm_hw_params_any_proc)pDevice->pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); if (resultALSA < 0) { - ma__free_from_callbacks(pHWParams, &pDevice->pContext->allocationCallbacks); + ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed.", ma_result_from_errno(-resultALSA)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed."); + return ma_result_from_errno(-resultALSA); } /* MMAP Mode. Try using interleaved MMAP access. If this fails, fall back to standard readi/writei. */ @@ -20739,9 +26242,10 @@ static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_devic if (!isUsingMMap) { resultALSA = ((ma_snd_pcm_hw_params_set_access_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_access)(pPCM, pHWParams, MA_SND_PCM_ACCESS_RW_INTERLEAVED); if (resultALSA < 0) { - ma__free_from_callbacks(pHWParams, &pDevice->pContext->allocationCallbacks); + ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set access mode to neither SND_PCM_ACCESS_MMAP_INTERLEAVED nor SND_PCM_ACCESS_RW_INTERLEAVED. snd_pcm_hw_params_set_access() failed.", ma_result_from_errno(-resultALSA)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set access mode to neither SND_PCM_ACCESS_MMAP_INTERLEAVED nor SND_PCM_ACCESS_RW_INTERLEAVED. snd_pcm_hw_params_set_access() failed."); + return ma_result_from_errno(-resultALSA); } } @@ -20769,24 +26273,27 @@ static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_devic } if (formatALSA == MA_SND_PCM_FORMAT_UNKNOWN) { - ma__free_from_callbacks(pHWParams, &pDevice->pContext->allocationCallbacks); + ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. The device does not support any miniaudio formats.", MA_FORMAT_NOT_SUPPORTED); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. The device does not support any miniaudio formats."); + return MA_FORMAT_NOT_SUPPORTED; } } resultALSA = ((ma_snd_pcm_hw_params_set_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, formatALSA); if (resultALSA < 0) { - ma__free_from_callbacks(pHWParams, &pDevice->pContext->allocationCallbacks); + ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. snd_pcm_hw_params_set_format() failed.", ma_result_from_errno(-resultALSA)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. snd_pcm_hw_params_set_format() failed."); + return ma_result_from_errno(-resultALSA); } internalFormat = ma_format_from_alsa(formatALSA); if (internalFormat == ma_format_unknown) { - ma__free_from_callbacks(pHWParams, &pDevice->pContext->allocationCallbacks); + ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] The chosen format is not supported by miniaudio.", MA_FORMAT_NOT_SUPPORTED); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] The chosen format is not supported by miniaudio."); + return MA_FORMAT_NOT_SUPPORTED; } } @@ -20799,9 +26306,10 @@ static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_devic resultALSA = ((ma_snd_pcm_hw_params_set_channels_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_channels_near)(pPCM, pHWParams, &channels); if (resultALSA < 0) { - ma__free_from_callbacks(pHWParams, &pDevice->pContext->allocationCallbacks); + ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set channel count. snd_pcm_hw_params_set_channels_near() failed.", ma_result_from_errno(-resultALSA)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set channel count. snd_pcm_hw_params_set_channels_near() failed."); + return ma_result_from_errno(-resultALSA); } internalChannels = (ma_uint32)channels; @@ -20837,9 +26345,10 @@ static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_devic resultALSA = ((ma_snd_pcm_hw_params_set_rate_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_rate_near)(pPCM, pHWParams, &sampleRate, 0); if (resultALSA < 0) { - ma__free_from_callbacks(pHWParams, &pDevice->pContext->allocationCallbacks); + ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Sample rate not supported. snd_pcm_hw_params_set_rate_near() failed.", ma_result_from_errno(-resultALSA)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Sample rate not supported. snd_pcm_hw_params_set_rate_near() failed."); + return ma_result_from_errno(-resultALSA); } internalSampleRate = (ma_uint32)sampleRate; @@ -20851,9 +26360,10 @@ static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_devic resultALSA = ((ma_snd_pcm_hw_params_set_periods_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_periods_near)(pPCM, pHWParams, &periods, NULL); if (resultALSA < 0) { - ma__free_from_callbacks(pHWParams, &pDevice->pContext->allocationCallbacks); + ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set period count. snd_pcm_hw_params_set_periods_near() failed.", ma_result_from_errno(-resultALSA)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set period count. snd_pcm_hw_params_set_periods_near() failed."); + return ma_result_from_errno(-resultALSA); } internalPeriods = periods; @@ -20865,9 +26375,10 @@ static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_devic resultALSA = ((ma_snd_pcm_hw_params_set_buffer_size_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_buffer_size_near)(pPCM, pHWParams, &actualBufferSizeInFrames); if (resultALSA < 0) { - ma__free_from_callbacks(pHWParams, &pDevice->pContext->allocationCallbacks); + ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set buffer size for device. snd_pcm_hw_params_set_buffer_size() failed.", ma_result_from_errno(-resultALSA)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set buffer size for device. snd_pcm_hw_params_set_buffer_size() failed."); + return ma_result_from_errno(-resultALSA); } internalPeriodSizeInFrames = actualBufferSizeInFrames / internalPeriods; @@ -20876,34 +26387,38 @@ static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_devic /* Apply hardware parameters. */ resultALSA = ((ma_snd_pcm_hw_params_proc)pDevice->pContext->alsa.snd_pcm_hw_params)(pPCM, pHWParams); if (resultALSA < 0) { - ma__free_from_callbacks(pHWParams, &pDevice->pContext->allocationCallbacks); + ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set hardware parameters. snd_pcm_hw_params() failed.", ma_result_from_errno(-resultALSA)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set hardware parameters. snd_pcm_hw_params() failed."); + return ma_result_from_errno(-resultALSA); } - ma__free_from_callbacks(pHWParams, &pDevice->pContext->allocationCallbacks); + ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); pHWParams = NULL; /* Software parameters. */ - pSWParams = (ma_snd_pcm_sw_params_t*)ma__calloc_from_callbacks(((ma_snd_pcm_sw_params_sizeof_proc)pDevice->pContext->alsa.snd_pcm_sw_params_sizeof)(), &pDevice->pContext->allocationCallbacks); + pSWParams = (ma_snd_pcm_sw_params_t*)ma_calloc(((ma_snd_pcm_sw_params_sizeof_proc)pDevice->pContext->alsa.snd_pcm_sw_params_sizeof)(), &pDevice->pContext->allocationCallbacks); if (pSWParams == NULL) { ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for software parameters."); return MA_OUT_OF_MEMORY; } resultALSA = ((ma_snd_pcm_sw_params_current_proc)pDevice->pContext->alsa.snd_pcm_sw_params_current)(pPCM, pSWParams); if (resultALSA < 0) { - ma__free_from_callbacks(pSWParams, &pDevice->pContext->allocationCallbacks); + ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize software parameters. snd_pcm_sw_params_current() failed.", ma_result_from_errno(-resultALSA)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize software parameters. snd_pcm_sw_params_current() failed."); + return ma_result_from_errno(-resultALSA); } resultALSA = ((ma_snd_pcm_sw_params_set_avail_min_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_avail_min)(pPCM, pSWParams, ma_prev_power_of_2(internalPeriodSizeInFrames)); if (resultALSA < 0) { - ma__free_from_callbacks(pSWParams, &pDevice->pContext->allocationCallbacks); + ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_sw_params_set_avail_min() failed.", ma_result_from_errno(-resultALSA)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_sw_params_set_avail_min() failed."); + return ma_result_from_errno(-resultALSA); } resultALSA = ((ma_snd_pcm_sw_params_get_boundary_proc)pDevice->pContext->alsa.snd_pcm_sw_params_get_boundary)(pSWParams, &bufferBoundary); @@ -20918,27 +26433,30 @@ static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_devic */ resultALSA = ((ma_snd_pcm_sw_params_set_start_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_start_threshold)(pPCM, pSWParams, internalPeriodSizeInFrames*2); if (resultALSA < 0) { - ma__free_from_callbacks(pSWParams, &pDevice->pContext->allocationCallbacks); + ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set start threshold for playback device. snd_pcm_sw_params_set_start_threshold() failed.", ma_result_from_errno(-resultALSA)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set start threshold for playback device. snd_pcm_sw_params_set_start_threshold() failed."); + return ma_result_from_errno(-resultALSA); } resultALSA = ((ma_snd_pcm_sw_params_set_stop_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_stop_threshold)(pPCM, pSWParams, bufferBoundary); if (resultALSA < 0) { /* Set to boundary to loop instead of stop in the event of an xrun. */ - ma__free_from_callbacks(pSWParams, &pDevice->pContext->allocationCallbacks); + ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set stop threshold for playback device. snd_pcm_sw_params_set_stop_threshold() failed.", ma_result_from_errno(-resultALSA)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set stop threshold for playback device. snd_pcm_sw_params_set_stop_threshold() failed."); + return ma_result_from_errno(-resultALSA); } } resultALSA = ((ma_snd_pcm_sw_params_proc)pDevice->pContext->alsa.snd_pcm_sw_params)(pPCM, pSWParams); if (resultALSA < 0) { - ma__free_from_callbacks(pSWParams, &pDevice->pContext->allocationCallbacks); + ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set software parameters. snd_pcm_sw_params() failed.", ma_result_from_errno(-resultALSA)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set software parameters. snd_pcm_sw_params() failed."); + return ma_result_from_errno(-resultALSA); } - ma__free_from_callbacks(pSWParams, &pDevice->pContext->allocationCallbacks); + ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); pSWParams = NULL; @@ -20964,7 +26482,7 @@ static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_devic ma_bool32 isValid = MA_TRUE; /* Fill with defaults. */ - ma_get_standard_channel_map(ma_standard_channel_map_alsa, internalChannels, internalChannelMap); + ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels); /* Overwrite first pChmap->channels channels. */ for (iChannel = 0; iChannel < pChmap->channels; ++iChannel) { @@ -20984,7 +26502,7 @@ static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_devic /* If our channel map is invalid, fall back to defaults. */ if (!isValid) { - ma_get_standard_channel_map(ma_standard_channel_map_alsa, internalChannels, internalChannelMap); + ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels); } } @@ -20992,7 +26510,7 @@ static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_devic pChmap = NULL; } else { /* Could not retrieve the channel map. Fall back to a hard-coded assumption. */ - ma_get_standard_channel_map(ma_standard_channel_map_alsa, internalChannels, internalChannelMap); + ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels); } } @@ -21005,13 +26523,15 @@ static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_devic pollDescriptorCount = ((ma_snd_pcm_poll_descriptors_count_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors_count)(pPCM); if (pollDescriptorCount <= 0) { ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors count.", MA_ERROR); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors count."); + return MA_ERROR; } - pPollDescriptors = (struct pollfd*)ma_malloc(sizeof(*pPollDescriptors) * (pollDescriptorCount + 1), &pDevice->pContext->allocationCallbacks/*, MA_ALLOCATION_TYPE_GENERAL*/); /* +1 because we want room for the wakeup descriptor. */ + pPollDescriptors = (struct pollfd*)ma_malloc(sizeof(*pPollDescriptors) * (pollDescriptorCount + 1), &pDevice->pContext->allocationCallbacks); /* +1 because we want room for the wakeup descriptor. */ if (pPollDescriptors == NULL) { ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for poll descriptors.", MA_OUT_OF_MEMORY); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for poll descriptors."); + return MA_OUT_OF_MEMORY; } /* @@ -21022,7 +26542,8 @@ static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_devic if (wakeupfd < 0) { ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to create eventfd for poll wakeup.", ma_result_from_errno(errno)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to create eventfd for poll wakeup."); + return ma_result_from_errno(errno); } /* We'll place the wakeup fd at the start of the buffer. */ @@ -21036,7 +26557,8 @@ static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_devic close(wakeupfd); ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors.", MA_ERROR); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors."); + return MA_ERROR; } if (deviceType == ma_device_type_capture) { @@ -21056,7 +26578,8 @@ static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_devic close(wakeupfd); ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to prepare device.", ma_result_from_errno(-resultALSA)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to prepare device."); + return ma_result_from_errno(-resultALSA); } @@ -21112,7 +26635,8 @@ static ma_result ma_device_start__alsa(ma_device* pDevice) if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); if (resultALSA < 0) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start capture device.", ma_result_from_errno(-resultALSA)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start capture device."); + return ma_result_from_errno(-resultALSA); } } @@ -21126,30 +26650,30 @@ static ma_result ma_device_start__alsa(ma_device* pDevice) static ma_result ma_device_stop__alsa(ma_device* pDevice) { if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device... "); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device...\n"); ((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "Done\n"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device successful.\n"); /* We need to prepare the device again, otherwise we won't be able to restart the device. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device... "); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device...\n"); if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) < 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "Failed\n"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device failed.\n"); } else { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "Done\n"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device successful.\n"); } } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping playback device... "); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping playback device...\n"); ((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "Done\n"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping playback device successful.\n"); /* We need to prepare the device again, otherwise we won't be able to restart the device. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device... "); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device...\n"); if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) < 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "Failed\n"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device failed.\n"); } else { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "Done\n"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device successful.\n"); } } @@ -21163,7 +26687,8 @@ static ma_result ma_device_wait__alsa(ma_device* pDevice, ma_snd_pcm_t* pPCM, st int resultALSA; int resultPoll = poll(pPollDescriptors, pollDescriptorCount, -1); if (resultPoll < 0) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] poll() failed.", ma_result_from_errno(errno)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] poll() failed."); + return ma_result_from_errno(errno); } /* @@ -21173,10 +26698,13 @@ static ma_result ma_device_wait__alsa(ma_device* pDevice, ma_snd_pcm_t* pPCM, st */ if ((pPollDescriptors[0].revents & POLLIN) != 0) { ma_uint64 t; - read(pPollDescriptors[0].fd, &t, sizeof(t)); /* <-- Important that we read here so that the next write() does not block. */ + int resultRead = read(pPollDescriptors[0].fd, &t, sizeof(t)); /* <-- Important that we read here so that the next write() does not block. */ + if (resultRead < 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] read() failed."); + return ma_result_from_errno(errno); + } ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] POLLIN set for wakeupfd\n"); - return MA_DEVICE_NOT_STARTED; } @@ -21186,11 +26714,13 @@ static ma_result ma_device_wait__alsa(ma_device* pDevice, ma_snd_pcm_t* pPCM, st */ resultALSA = ((ma_snd_pcm_poll_descriptors_revents_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors_revents)(pPCM, pPollDescriptors + 1, pollDescriptorCount - 1, &revents); /* +1, -1 to ignore the wakeup descriptor. */ if (resultALSA < 0) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_poll_descriptors_revents() failed.", ma_result_from_errno(-resultALSA)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_poll_descriptors_revents() failed."); + return ma_result_from_errno(-resultALSA); } if ((revents & POLLERR) != 0) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] POLLERR detected.", ma_result_from_errno(errno)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] POLLERR detected."); + return ma_result_from_errno(errno); } if ((revents & requiredEvent) == requiredEvent) { @@ -21213,7 +26743,7 @@ static ma_result ma_device_wait_write__alsa(ma_device* pDevice) static ma_result ma_device_read__alsa(ma_device* pDevice, void* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead) { - ma_snd_pcm_sframes_t resultALSA; + ma_snd_pcm_sframes_t resultALSA = 0; MA_ASSERT(pDevice != NULL); MA_ASSERT(pFramesOut != NULL); @@ -21222,7 +26752,7 @@ static ma_result ma_device_read__alsa(ma_device* pDevice, void* pFramesOut, ma_u *pFramesRead = 0; } - while (ma_device_get_state(pDevice) == MA_STATE_STARTED) { + while (ma_device_get_state(pDevice) == ma_device_state_started) { ma_result result; /* The first thing to do is wait for data to become available for reading. This will return an error code if the device has been stopped. */ @@ -21237,20 +26767,22 @@ static ma_result ma_device_read__alsa(ma_device* pDevice, void* pFramesOut, ma_u break; /* Success. */ } else { if (resultALSA == -EAGAIN) { - /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "TRACE: EGAIN (read)\n");*/ + /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EGAIN (read)\n");*/ continue; /* Try again. */ } else if (resultALSA == -EPIPE) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "TRACE: EPIPE (read)\n"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EPIPE (read)\n"); /* Overrun. Recover and try again. If this fails we need to return an error. */ resultALSA = ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, resultALSA, MA_TRUE); if (resultALSA < 0) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after overrun.", ma_result_from_errno((int)-resultALSA)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after overrun."); + return ma_result_from_errno((int)-resultALSA); } resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); if (resultALSA < 0) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun.", ma_result_from_errno((int)-resultALSA)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun."); + return ma_result_from_errno((int)-resultALSA); } continue; /* Try reading again. */ @@ -21267,7 +26799,7 @@ static ma_result ma_device_read__alsa(ma_device* pDevice, void* pFramesOut, ma_u static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) { - ma_snd_pcm_sframes_t resultALSA; + ma_snd_pcm_sframes_t resultALSA = 0; MA_ASSERT(pDevice != NULL); MA_ASSERT(pFrames != NULL); @@ -21276,7 +26808,7 @@ static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames, *pFramesWritten = 0; } - while (ma_device_get_state(pDevice) == MA_STATE_STARTED) { + while (ma_device_get_state(pDevice) == ma_device_state_started) { ma_result result; /* The first thing to do is wait for space to become available for writing. This will return an error code if the device has been stopped. */ @@ -21290,15 +26822,16 @@ static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames, break; /* Success. */ } else { if (resultALSA == -EAGAIN) { - /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "TRACE: EGAIN (write)\n");*/ + /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EGAIN (write)\n");*/ continue; /* Try again. */ } else if (resultALSA == -EPIPE) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "TRACE: EPIPE (write)\n"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EPIPE (write)\n"); /* Underrun. Recover and try again. If this fails we need to return an error. */ resultALSA = ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, resultALSA, MA_TRUE); /* MA_TRUE=silent (don't print anything on error). */ if (resultALSA < 0) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after underrun.", ma_result_from_errno((int)-resultALSA)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after underrun."); + return ma_result_from_errno((int)-resultALSA); } /* @@ -21310,7 +26843,8 @@ static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames, */ resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); if (resultALSA < 0) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun.", ma_result_from_errno((int)-resultALSA)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun."); + return ma_result_from_errno((int)-resultALSA); } continue; /* Try writing again. */ @@ -21328,20 +26862,26 @@ static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames, static ma_result ma_device_data_loop_wakeup__alsa(ma_device* pDevice) { ma_uint64 t = 1; + int resultWrite = 0; MA_ASSERT(pDevice != NULL); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Waking up... "); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Waking up...\n"); /* Write to an eventfd to trigger a wakeup from poll() and abort any reading or writing. */ if (pDevice->alsa.pPollDescriptorsCapture != NULL) { - write(pDevice->alsa.wakeupfdCapture, &t, sizeof(t)); + resultWrite = write(pDevice->alsa.wakeupfdCapture, &t, sizeof(t)); } if (pDevice->alsa.pPollDescriptorsPlayback != NULL) { - write(pDevice->alsa.wakeupfdPlayback, &t, sizeof(t)); + resultWrite = write(pDevice->alsa.wakeupfdPlayback, &t, sizeof(t)); } - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "Done\n"); + if (resultWrite < 0) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] write() failed.\n"); + return ma_result_from_errno(errno); + } + + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Waking up completed successfully.\n"); return MA_SUCCESS; } @@ -21365,6 +26905,7 @@ static ma_result ma_context_uninit__alsa(ma_context* pContext) static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) { + ma_result result; #ifndef MA_NO_RUNTIME_LINKING const char* libasoundNames[] = { "libasound.so.2", @@ -21589,8 +27130,10 @@ static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_co pContext->alsa.useVerboseDeviceEnumeration = pConfig->alsa.useVerboseDeviceEnumeration; - if (ma_mutex_init(&pContext->alsa.internalDeviceEnumLock) != MA_SUCCESS) { - ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[ALSA] WARNING: Failed to initialize mutex for internal device enumeration.", MA_ERROR); + result = ma_mutex_init(&pContext->alsa.internalDeviceEnumLock); + if (result != MA_SUCCESS) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] WARNING: Failed to initialize mutex for internal device enumeration."); + return result; } pCallbacks->onContextInit = ma_context_init__alsa; @@ -21692,7 +27235,7 @@ that point (it may still need to load files or whatnot). Instead, this callback stream be started which is how it works with literally *every* other callback-based audio API. Since miniaudio forbids firing of the data callback until the device has been started (as it should be with *all* callback based APIs), logic needs to be added to ensure miniaudio doesn't just blindly fire the application-defined data callback from within the PulseAudio callback before the stream has actually been -started. The device state is used for this - if the state is anything other than `MA_STATE_STARTING` or `MA_STATE_STARTED`, the main data +started. The device state is used for this - if the state is anything other than `ma_device_state_starting` or `ma_device_state_started`, the main data callback is not fired. This, unfortunately, is not the end of the problems with the PulseAudio write callback. Any normal callback based audio API will @@ -22288,6 +27831,7 @@ typedef const char* (* ma_pa_stream_get_device_name_proc) ( typedef void (* ma_pa_stream_set_write_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata); typedef void (* ma_pa_stream_set_read_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata); typedef void (* ma_pa_stream_set_suspended_callback_proc) (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata); +typedef void (* ma_pa_stream_set_moved_callback_proc) (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata); typedef int (* ma_pa_stream_is_suspended_proc) (const ma_pa_stream* s); typedef ma_pa_operation* (* ma_pa_stream_flush_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); typedef ma_pa_operation* (* ma_pa_stream_drain_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); @@ -22482,7 +28026,7 @@ static ma_pa_channel_position_t ma_channel_position_to_pulse(ma_channel position } #endif -static ma_result ma_wait_for_operation__pulse(ma_context* pContext, ma_pa_operation* pOP) +static ma_result ma_wait_for_operation__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_pa_operation* pOP) { int resultPA; ma_pa_operation_state_t state; @@ -22496,7 +28040,7 @@ static ma_result ma_wait_for_operation__pulse(ma_context* pContext, ma_pa_operat break; /* Done. */ } - resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pContext->pulse.pMainLoop, 1, NULL); + resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL); if (resultPA < 0) { return ma_result_from_pulse(resultPA); } @@ -22505,7 +28049,7 @@ static ma_result ma_wait_for_operation__pulse(ma_context* pContext, ma_pa_operat return MA_SUCCESS; } -static ma_result ma_wait_for_operation_and_unref__pulse(ma_context* pContext, ma_pa_operation* pOP) +static ma_result ma_wait_for_operation_and_unref__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_pa_operation* pOP) { ma_result result; @@ -22513,28 +28057,29 @@ static ma_result ma_wait_for_operation_and_unref__pulse(ma_context* pContext, ma return MA_INVALID_ARGS; } - result = ma_wait_for_operation__pulse(pContext, pOP); + result = ma_wait_for_operation__pulse(pContext, pMainLoop, pOP); ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); return result; } -static ma_result ma_context_wait_for_pa_context_to_connect__pulse(ma_context* pContext) +static ma_result ma_wait_for_pa_context_to_connect__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_ptr pPulseContext) { int resultPA; ma_pa_context_state_t state; for (;;) { - state = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)((ma_pa_context*)pContext->pulse.pPulseContext); + state = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)((ma_pa_context*)pPulseContext); if (state == MA_PA_CONTEXT_READY) { break; /* Done. */ } if (state == MA_PA_CONTEXT_FAILED || state == MA_PA_CONTEXT_TERMINATED) { - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio context.", MA_ERROR); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio context."); + return MA_ERROR; } - resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pContext->pulse.pMainLoop, 1, NULL); + resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL); if (resultPA < 0) { return ma_result_from_pulse(resultPA); } @@ -22544,22 +28089,23 @@ static ma_result ma_context_wait_for_pa_context_to_connect__pulse(ma_context* pC return MA_SUCCESS; } -static ma_result ma_context_wait_for_pa_stream_to_connect__pulse(ma_context* pContext, ma_pa_stream* pStream) +static ma_result ma_wait_for_pa_stream_to_connect__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_ptr pStream) { int resultPA; ma_pa_stream_state_t state; for (;;) { - state = ((ma_pa_stream_get_state_proc)pContext->pulse.pa_stream_get_state)(pStream); + state = ((ma_pa_stream_get_state_proc)pContext->pulse.pa_stream_get_state)((ma_pa_stream*)pStream); if (state == MA_PA_STREAM_READY) { break; /* Done. */ } if (state == MA_PA_STREAM_FAILED || state == MA_PA_STREAM_TERMINATED) { - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio stream.", MA_ERROR); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio stream."); + return MA_ERROR; } - resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pContext->pulse.pMainLoop, 1, NULL); + resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL); if (resultPA < 0) { return ma_result_from_pulse(resultPA); } @@ -22569,6 +28115,52 @@ static ma_result ma_context_wait_for_pa_stream_to_connect__pulse(ma_context* pCo } +static ma_result ma_init_pa_mainloop_and_pa_context__pulse(ma_context* pContext, const char* pApplicationName, const char* pServerName, ma_bool32 tryAutoSpawn, ma_ptr* ppMainLoop, ma_ptr* ppPulseContext) +{ + ma_result result; + ma_ptr pMainLoop; + ma_ptr pPulseContext; + + MA_ASSERT(ppMainLoop != NULL); + MA_ASSERT(ppPulseContext != NULL); + + /* The PulseAudio context maps well to miniaudio's notion of a context. The pa_context object will be initialized as part of the ma_context. */ + pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)(); + if (pMainLoop == NULL) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create mainloop."); + return MA_FAILED_TO_INIT_BACKEND; + } + + pPulseContext = ((ma_pa_context_new_proc)pContext->pulse.pa_context_new)(((ma_pa_mainloop_get_api_proc)pContext->pulse.pa_mainloop_get_api)((ma_pa_mainloop*)pMainLoop), pApplicationName); + if (pPulseContext == NULL) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio context."); + ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop)); + return MA_FAILED_TO_INIT_BACKEND; + } + + /* Now we need to connect to the context. Everything is asynchronous so we need to wait for it to connect before returning. */ + result = ma_result_from_pulse(((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)((ma_pa_context*)pPulseContext, pServerName, (tryAutoSpawn) ? 0 : MA_PA_CONTEXT_NOAUTOSPAWN, NULL)); + if (result != MA_SUCCESS) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio context."); + ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop)); + return result; + } + + /* Since ma_context_init() runs synchronously we need to wait for the PulseAudio context to connect before we return. */ + result = ma_wait_for_pa_context_to_connect__pulse(pContext, pMainLoop, pPulseContext); + if (result != MA_SUCCESS) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Waiting for connection failed."); + ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop)); + return result; + } + + *ppMainLoop = pMainLoop; + *ppPulseContext = pPulseContext; + + return MA_SUCCESS; +} + + static void ma_device_sink_info_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData) { ma_pa_sink_info* pInfoOut; @@ -22601,6 +28193,7 @@ static void ma_device_source_info_callback(ma_pa_context* pPulseContext, const m (void)pPulseContext; /* Unused. */ } +#if 0 static void ma_device_sink_name_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData) { ma_device* pDevice; @@ -22632,7 +28225,7 @@ static void ma_device_source_name_callback(ma_pa_context* pPulseContext, const m (void)pPulseContext; /* Unused. */ } - +#endif static ma_result ma_context_get_sink_info__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_sink_info* pSinkInfo) { @@ -22643,7 +28236,7 @@ static ma_result ma_context_get_sink_info__pulse(ma_context* pContext, const cha return MA_ERROR; } - return ma_wait_for_operation_and_unref__pulse(pContext, pOP); + return ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP); } static ma_result ma_context_get_source_info__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_source_info* pSourceInfo) @@ -22655,7 +28248,7 @@ static ma_result ma_context_get_source_info__pulse(ma_context* pContext, const c return MA_ERROR; } - return ma_wait_for_operation_and_unref__pulse(pContext, pOP);; + return ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP); } static ma_result ma_context_get_default_device_index__pulse(ma_context* pContext, ma_device_type deviceType, ma_uint32* pIndex) @@ -22799,7 +28392,7 @@ static ma_result ma_context_enumerate_devices__pulse(ma_context* pContext, ma_en goto done; } - result = ma_wait_for_operation__pulse(pContext, pOP); + result = ma_wait_for_operation__pulse(pContext, pContext->pulse.pMainLoop, pOP); ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); if (result != MA_SUCCESS) { @@ -22816,7 +28409,7 @@ static ma_result ma_context_enumerate_devices__pulse(ma_context* pContext, ma_en goto done; } - result = ma_wait_for_operation__pulse(pContext, pOP); + result = ma_wait_for_operation__pulse(pContext, pContext->pulse.pMainLoop, pOP); ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); if (result != MA_SUCCESS) { @@ -22937,7 +28530,7 @@ static ma_result ma_context_get_device_info__pulse(ma_context* pContext, ma_devi } if (pOP != NULL) { - ma_wait_for_operation_and_unref__pulse(pContext, pOP); + ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP); } else { result = MA_ERROR; goto done; @@ -22975,6 +28568,10 @@ static ma_result ma_device_uninit__pulse(ma_device* pDevice) ma_duplex_rb_uninit(&pDevice->duplexRB); } + ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pDevice->pulse.pPulseContext); + ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pDevice->pulse.pPulseContext); + ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pDevice->pulse.pMainLoop); + return MA_SUCCESS; } @@ -22990,7 +28587,7 @@ static ma_pa_buffer_attr ma_device__pa_buffer_attr_new(ma_uint32 periodSizeInFra return attr; } -static ma_pa_stream* ma_context__pa_stream_new__pulse(ma_context* pContext, const char* pStreamName, const ma_pa_sample_spec* ss, const ma_pa_channel_map* cmap) +static ma_pa_stream* ma_device__pa_stream_new__pulse(ma_device* pDevice, const char* pStreamName, const ma_pa_sample_spec* ss, const ma_pa_channel_map* cmap) { static int g_StreamCounter = 0; char actualStreamName[256]; @@ -23003,7 +28600,7 @@ static ma_pa_stream* ma_context__pa_stream_new__pulse(ma_context* pContext, cons } g_StreamCounter += 1; - return ((ma_pa_stream_new_proc)pContext->pulse.pa_stream_new)((ma_pa_context*)pContext->pulse.pPulseContext, actualStreamName, ss, cmap); + return ((ma_pa_stream_new_proc)pDevice->pContext->pulse.pa_stream_new)((ma_pa_context*)pDevice->pulse.pPulseContext, actualStreamName, ss, cmap); } @@ -23022,7 +28619,7 @@ static void ma_device_on_read__pulse(ma_pa_stream* pStream, size_t byteCount, vo can fire this callback before the stream has even started. Ridiculous. */ deviceState = ma_device_get_state(pDevice); - if (deviceState != MA_STATE_STARTING && deviceState != MA_STATE_STARTED) { + if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) { return; } @@ -23032,7 +28629,7 @@ static void ma_device_on_read__pulse(ma_pa_stream* pStream, size_t byteCount, vo frameCount = byteCount / bpf; framesProcessed = 0; - while (ma_device_get_state(pDevice) == MA_STATE_STARTED && framesProcessed < frameCount) { + while (ma_device_get_state(pDevice) == ma_device_state_started && framesProcessed < frameCount) { const void* pMappedPCMFrames; size_t bytesMapped; ma_uint64 framesMapped; @@ -23094,7 +28691,7 @@ static ma_result ma_device_write_to_stream__pulse(ma_device* pDevice, ma_pa_stre framesMapped = bytesMapped / bpf; - if (deviceState == MA_STATE_STARTED || deviceState == MA_STATE_STARTING) { /* Check for starting state just in case this is being used to do the initial fill. */ + if (deviceState == ma_device_state_started || deviceState == ma_device_state_starting) { /* Check for starting state just in case this is being used to do the initial fill. */ ma_device_handle_backend_data_callback(pDevice, pMappedPCMFrames, NULL, framesMapped); } else { /* Device is not started. Write silence. */ @@ -23109,7 +28706,7 @@ static ma_result ma_device_write_to_stream__pulse(ma_device* pDevice, ma_pa_stre framesProcessed += framesMapped; } else { - result = MA_ERROR; /* No data available. Abort. */ + result = MA_SUCCESS; /* No data available for writing. */ goto done; } } else { @@ -23141,7 +28738,7 @@ static void ma_device_on_write__pulse(ma_pa_stream* pStream, size_t byteCount, v can fire this callback before the stream has even started. Ridiculous. */ deviceState = ma_device_get_state(pDevice); - if (deviceState != MA_STATE_STARTING && deviceState != MA_STATE_STARTED) { + if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) { return; } @@ -23156,7 +28753,7 @@ static void ma_device_on_write__pulse(ma_pa_stream* pStream, size_t byteCount, v /* Don't keep trying to process frames if the device isn't started. */ deviceState = ma_device_get_state(pDevice); - if (deviceState != MA_STATE_STARTING && deviceState != MA_STATE_STARTED) { + if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) { break; } @@ -23185,13 +28782,47 @@ static void ma_device_on_suspended__pulse(ma_pa_stream* pStream, void* pUserData if (suspended == 1) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. Suspended.\n"); - - if (pDevice->onStop) { - pDevice->onStop(pDevice); - } + ma_device__on_notification_stopped(pDevice); } else { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. Resumed.\n"); - } + ma_device__on_notification_started(pDevice); + } +} + +static void ma_device_on_rerouted__pulse(ma_pa_stream* pStream, void* pUserData) +{ + ma_device* pDevice = (ma_device*)pUserData; + + (void)pStream; + (void)pUserData; + + ma_device__on_notification_rerouted(pDevice); +} + +static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__pulse(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) +{ + /* + There have been reports from users where buffers of < ~20ms result glitches when running through + PipeWire. To work around this we're going to have to use a different default buffer size. + */ + const ma_uint32 defaultPeriodSizeInMilliseconds_LowLatency = 25; + const ma_uint32 defaultPeriodSizeInMilliseconds_Conservative = MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE; + + MA_ASSERT(nativeSampleRate != 0); + + if (pDescriptor->periodSizeInFrames == 0) { + if (pDescriptor->periodSizeInMilliseconds == 0) { + if (performanceProfile == ma_performance_profile_low_latency) { + return ma_calculate_buffer_size_in_frames_from_milliseconds(defaultPeriodSizeInMilliseconds_LowLatency, nativeSampleRate); + } else { + return ma_calculate_buffer_size_in_frames_from_milliseconds(defaultPeriodSizeInMilliseconds_Conservative, nativeSampleRate); + } + } else { + return ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate); + } + } else { + return pDescriptor->periodSizeInFrames; + } } static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) @@ -23263,10 +28894,18 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi sampleRate = pDescriptorCapture->sampleRate; } + + + result = ma_init_pa_mainloop_and_pa_context__pulse(pDevice->pContext, pDevice->pContext->pulse.pApplicationName, pDevice->pContext->pulse.pServerName, MA_FALSE, &pDevice->pulse.pMainLoop, &pDevice->pulse.pPulseContext); + if (result != MA_SUCCESS) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to initialize PA mainloop and context for device.\n"); + return result; + } + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { result = ma_context_get_source_info__pulse(pDevice->pContext, devCapture, &sourceInfo); if (result != MA_SUCCESS) { - ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve source info for capture device.", result); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve source info for capture device."); goto on_error0; } @@ -23279,26 +28918,27 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi } else { ss.format = MA_PA_SAMPLE_FLOAT32BE; } - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[PulseAudio] WARNING: sample_spec.format not supported by miniaudio. Defaulting to PA_SAMPLE_RATE_FLOAT32\n"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.format not supported by miniaudio. Defaulting to PA_SAMPLE_FLOAT32.\n"); } if (ss.rate == 0) { ss.rate = MA_DEFAULT_SAMPLE_RATE; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[PulseAudio] WARNING: sample_spec.rate = 0. Defaulting to %d\n", ss.rate); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.rate = 0. Defaulting to %d.\n", ss.rate); } if (ss.channels == 0) { ss.channels = MA_DEFAULT_CHANNELS; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[PulseAudio] WARNING: sample_spec.channels = 0. Defaulting to %d\n", ss.channels); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.channels = 0. Defaulting to %d.\n", ss.channels); } /* We now have enough information to calculate our actual period size in frames. */ - pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, ss.rate, pConfig->performanceProfile); + pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__pulse(pDescriptorCapture, ss.rate, pConfig->performanceProfile); attr = ma_device__pa_buffer_attr_new(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->periodCount, &ss); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[PulseAudio] Capture attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorCapture->periodSizeInFrames); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Capture attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorCapture->periodSizeInFrames); - pDevice->pulse.pStreamCapture = ma_context__pa_stream_new__pulse(pDevice->pContext, pConfig->pulse.pStreamNameCapture, &ss, &cmap); + pDevice->pulse.pStreamCapture = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNameCapture, &ss, &cmap); if (pDevice->pulse.pStreamCapture == NULL) { - result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio capture stream.", MA_FAILED_TO_OPEN_BACKEND_DEVICE); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio capture stream.\n"); + result = MA_ERROR; goto on_error0; } @@ -23309,6 +28949,9 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi /* State callback for checking when the device has been corked. */ ((ma_pa_stream_set_suspended_callback_proc)pDevice->pContext->pulse.pa_stream_set_suspended_callback)((ma_pa_stream*)pDevice->pulse.pStreamCapture, ma_device_on_suspended__pulse, pDevice); + /* Rerouting notification. */ + ((ma_pa_stream_set_moved_callback_proc)pDevice->pContext->pulse.pa_stream_set_moved_callback)((ma_pa_stream*)pDevice->pulse.pStreamCapture, ma_device_on_rerouted__pulse, pDevice); + /* Connect after we've got all of our internal state set up. */ streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY | MA_PA_STREAM_FIX_FORMAT | MA_PA_STREAM_FIX_RATE | MA_PA_STREAM_FIX_CHANNELS; @@ -23318,33 +28961,64 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi error = ((ma_pa_stream_connect_record_proc)pDevice->pContext->pulse.pa_stream_connect_record)((ma_pa_stream*)pDevice->pulse.pStreamCapture, devCapture, &attr, streamFlags); if (error != MA_PA_OK) { - result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio capture stream.", ma_result_from_pulse(error)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio capture stream."); + result = ma_result_from_pulse(error); goto on_error1; } - result = ma_context_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, (ma_pa_stream*)pDevice->pulse.pStreamCapture); + result = ma_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, (ma_pa_stream*)pDevice->pulse.pStreamCapture); if (result != MA_SUCCESS) { goto on_error2; } + /* Internal format. */ pActualSS = ((ma_pa_stream_get_sample_spec_proc)pDevice->pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamCapture); if (pActualSS != NULL) { ss = *pActualSS; + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Capture sample spec: format=%s, channels=%d, rate=%d\n", ma_get_format_name(ma_format_from_pulse(ss.format)), ss.channels, ss.rate); + } else { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Failed to retrieve capture sample spec.\n"); } pDescriptorCapture->format = ma_format_from_pulse(ss.format); pDescriptorCapture->channels = ss.channels; pDescriptorCapture->sampleRate = ss.rate; - /* Internal channel map. */ - pActualCMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - if (pActualCMap != NULL) { - cmap = *pActualCMap; + if (pDescriptorCapture->format == ma_format_unknown || pDescriptorCapture->channels == 0 || pDescriptorCapture->sampleRate == 0) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Capture sample spec is invalid. Device unusable by miniaudio. format=%s, channels=%d, sampleRate=%d.\n", ma_get_format_name(pDescriptorCapture->format), pDescriptorCapture->channels, pDescriptorCapture->sampleRate); + result = MA_ERROR; + goto on_error4; } - for (iChannel = 0; iChannel < pDescriptorCapture->channels; ++iChannel) { - pDescriptorCapture->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]); + /* Internal channel map. */ + + /* + Bug in PipeWire. There have been reports that PipeWire is returning AUX channels when reporting + the channel map. To somewhat workaround this, I'm hacking in a hard coded channel map for mono + and stereo. In this case it should be safe to assume mono = MONO and stereo = LEFT/RIGHT. For + all other channel counts we need to just put up with whatever PipeWire reports and hope it gets + fixed sooner than later. I might remove this hack later. + */ + if (pDescriptorCapture->channels > 2) { + pActualCMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamCapture); + if (pActualCMap != NULL) { + cmap = *pActualCMap; + } + + for (iChannel = 0; iChannel < pDescriptorCapture->channels; ++iChannel) { + pDescriptorCapture->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]); + } + } else { + /* Hack for mono and stereo. */ + if (pDescriptorCapture->channels == 1) { + pDescriptorCapture->channelMap[0] = MA_CHANNEL_MONO; + } else if (pDescriptorCapture->channels == 2) { + pDescriptorCapture->channelMap[0] = MA_CHANNEL_FRONT_LEFT; + pDescriptorCapture->channelMap[1] = MA_CHANNEL_FRONT_RIGHT; + } else { + MA_ASSERT(MA_FALSE); /* Should never hit this. */ + } } @@ -23354,24 +29028,20 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi attr = *pActualAttr; } - pDescriptorCapture->periodCount = attr.maxlength / attr.fragsize; - pDescriptorCapture->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) / pDescriptorCapture->periodCount; - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[PulseAudio] Capture actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorCapture->periodSizeInFrames); - - - /* Name. */ - devCapture = ((ma_pa_stream_get_device_name_proc)pDevice->pContext->pulse.pa_stream_get_device_name)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - if (devCapture != NULL) { - ma_pa_operation* pOP = ((ma_pa_context_get_source_info_by_name_proc)pDevice->pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)pDevice->pContext->pulse.pPulseContext, devCapture, ma_device_source_name_callback, pDevice); - ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pOP); + if (attr.fragsize > 0) { + pDescriptorCapture->periodCount = ma_max(attr.maxlength / attr.fragsize, 1); + } else { + pDescriptorCapture->periodCount = 1; } + + pDescriptorCapture->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) / pDescriptorCapture->periodCount; + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Capture actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorCapture->periodSizeInFrames); } if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { result = ma_context_get_sink_info__pulse(pDevice->pContext, devPlayback, &sinkInfo); if (result != MA_SUCCESS) { - ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve sink info for playback device.", result); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve sink info for playback device.\n"); goto on_error2; } @@ -23384,40 +29054,44 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi } else { ss.format = MA_PA_SAMPLE_FLOAT32BE; } - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[PulseAudio] WARNING: sample_spec.format not supported by miniaudio. Defaulting to PA_SAMPLE_RATE_FLOAT32\n"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.format not supported by miniaudio. Defaulting to PA_SAMPLE_FLOAT32.\n"); } if (ss.rate == 0) { ss.rate = MA_DEFAULT_SAMPLE_RATE; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[PulseAudio] WARNING: sample_spec.rate = 0. Defaulting to %d\n", ss.rate); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.rate = 0. Defaulting to %d.\n", ss.rate); } if (ss.channels == 0) { ss.channels = MA_DEFAULT_CHANNELS; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[PulseAudio] WARNING: sample_spec.channels = 0. Defaulting to %d\n", ss.channels); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.channels = 0. Defaulting to %d.\n", ss.channels); } /* We now have enough information to calculate the actual buffer size in frames. */ - pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, ss.rate, pConfig->performanceProfile); + pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__pulse(pDescriptorPlayback, ss.rate, pConfig->performanceProfile); attr = ma_device__pa_buffer_attr_new(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->periodCount, &ss); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[PulseAudio] Playback attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorPlayback->periodSizeInFrames); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Playback attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorPlayback->periodSizeInFrames); - pDevice->pulse.pStreamPlayback = ma_context__pa_stream_new__pulse(pDevice->pContext, pConfig->pulse.pStreamNamePlayback, &ss, &cmap); + pDevice->pulse.pStreamPlayback = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNamePlayback, &ss, &cmap); if (pDevice->pulse.pStreamPlayback == NULL) { - result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio playback stream.", MA_FAILED_TO_OPEN_BACKEND_DEVICE); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio playback stream.\n"); + result = MA_ERROR; goto on_error2; } /* Note that this callback will be fired as soon as the stream is connected, even though it's started as corked. The callback needs to handle a - device state of MA_STATE_UNINITIALIZED. + device state of ma_device_state_uninitialized. */ ((ma_pa_stream_set_write_callback_proc)pDevice->pContext->pulse.pa_stream_set_write_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_write__pulse, pDevice); /* State callback for checking when the device has been corked. */ ((ma_pa_stream_set_suspended_callback_proc)pDevice->pContext->pulse.pa_stream_set_suspended_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_suspended__pulse, pDevice); + /* Rerouting notification. */ + ((ma_pa_stream_set_moved_callback_proc)pDevice->pContext->pulse.pa_stream_set_moved_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_rerouted__pulse, pDevice); + /* Connect after we've got all of our internal state set up. */ streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY | MA_PA_STREAM_FIX_FORMAT | MA_PA_STREAM_FIX_RATE | MA_PA_STREAM_FIX_CHANNELS; @@ -23427,11 +29101,12 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi error = ((ma_pa_stream_connect_playback_proc)pDevice->pContext->pulse.pa_stream_connect_playback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, devPlayback, &attr, streamFlags, NULL, NULL); if (error != MA_PA_OK) { - result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio playback stream.", ma_result_from_pulse(error)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio playback stream."); + result = ma_result_from_pulse(error); goto on_error3; } - result = ma_context_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, (ma_pa_stream*)pDevice->pulse.pStreamPlayback); + result = ma_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, (ma_pa_stream*)pDevice->pulse.pStreamPlayback); if (result != MA_SUCCESS) { goto on_error3; } @@ -23441,20 +29116,49 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi pActualSS = ((ma_pa_stream_get_sample_spec_proc)pDevice->pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); if (pActualSS != NULL) { ss = *pActualSS; + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Playback sample spec: format=%s, channels=%d, rate=%d\n", ma_get_format_name(ma_format_from_pulse(ss.format)), ss.channels, ss.rate); + } else { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Failed to retrieve playback sample spec.\n"); } pDescriptorPlayback->format = ma_format_from_pulse(ss.format); pDescriptorPlayback->channels = ss.channels; pDescriptorPlayback->sampleRate = ss.rate; - /* Internal channel map. */ - pActualCMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - if (pActualCMap != NULL) { - cmap = *pActualCMap; + if (pDescriptorPlayback->format == ma_format_unknown || pDescriptorPlayback->channels == 0 || pDescriptorPlayback->sampleRate == 0) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Playback sample spec is invalid. Device unusable by miniaudio. format=%s, channels=%d, sampleRate=%d.\n", ma_get_format_name(pDescriptorPlayback->format), pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate); + result = MA_ERROR; + goto on_error4; } - for (iChannel = 0; iChannel < pDescriptorPlayback->channels; ++iChannel) { - pDescriptorPlayback->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]); + /* Internal channel map. */ + + /* + Bug in PipeWire. There have been reports that PipeWire is returning AUX channels when reporting + the channel map. To somewhat workaround this, I'm hacking in a hard coded channel map for mono + and stereo. In this case it should be safe to assume mono = MONO and stereo = LEFT/RIGHT. For + all other channel counts we need to just put up with whatever PipeWire reports and hope it gets + fixed sooner than later. I might remove this hack later. + */ + if (pDescriptorPlayback->channels > 2) { + pActualCMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); + if (pActualCMap != NULL) { + cmap = *pActualCMap; + } + + for (iChannel = 0; iChannel < pDescriptorPlayback->channels; ++iChannel) { + pDescriptorPlayback->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]); + } + } else { + /* Hack for mono and stereo. */ + if (pDescriptorPlayback->channels == 1) { + pDescriptorPlayback->channelMap[0] = MA_CHANNEL_MONO; + } else if (pDescriptorPlayback->channels == 2) { + pDescriptorPlayback->channelMap[0] = MA_CHANNEL_FRONT_LEFT; + pDescriptorPlayback->channelMap[1] = MA_CHANNEL_FRONT_RIGHT; + } else { + MA_ASSERT(MA_FALSE); /* Should never hit this. */ + } } @@ -23464,17 +29168,14 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi attr = *pActualAttr; } - pDescriptorPlayback->periodCount = attr.maxlength / attr.tlength; - pDescriptorPlayback->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels) / pDescriptorPlayback->periodCount; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[PulseAudio] Playback actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; internalPeriodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorPlayback->periodSizeInFrames); - - - /* Name. */ - devPlayback = ((ma_pa_stream_get_device_name_proc)pDevice->pContext->pulse.pa_stream_get_device_name)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - if (devPlayback != NULL) { - ma_pa_operation* pOP = ((ma_pa_context_get_sink_info_by_name_proc)pDevice->pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)pDevice->pContext->pulse.pPulseContext, devPlayback, ma_device_sink_name_callback, pDevice); - ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pOP); + if (attr.tlength > 0) { + pDescriptorPlayback->periodCount = ma_max(attr.maxlength / attr.tlength, 1); + } else { + pDescriptorPlayback->periodCount = 1; } + + pDescriptorPlayback->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels) / pDescriptorPlayback->periodCount; + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Playback actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; internalPeriodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorPlayback->periodSizeInFrames); } @@ -23485,9 +29186,13 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi onDeviceDataLoop callback is NULL, which is not the case for PulseAudio. */ if (pConfig->deviceType == ma_device_type_duplex) { - result = ma_duplex_rb_init(format, channels, sampleRate, pDescriptorCapture->sampleRate, pDescriptorCapture->periodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB); + ma_format rbFormat = (format != ma_format_unknown) ? format : pDescriptorCapture->format; + ma_uint32 rbChannels = (channels > 0) ? channels : pDescriptorCapture->channels; + ma_uint32 rbSampleRate = (sampleRate > 0) ? sampleRate : pDescriptorCapture->sampleRate; + + result = ma_duplex_rb_init(rbFormat, rbChannels, rbSampleRate, pDescriptorCapture->sampleRate, pDescriptorCapture->periodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB); if (result != MA_SUCCESS) { - result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to initialize ring buffer.", result); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to initialize ring buffer. %s.\n", ma_result_description(result)); goto on_error4; } } @@ -23546,20 +29251,19 @@ static ma_result ma_device__cork_stream__pulse(ma_device* pDevice, ma_device_typ pOP = ((ma_pa_stream_cork_proc)pContext->pulse.pa_stream_cork)(pStream, cork, ma_pulse_operation_complete_callback, &wasSuccessful); if (pOP == NULL) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to cork PulseAudio stream.", (cork == 0) ? MA_FAILED_TO_START_BACKEND_DEVICE : MA_FAILED_TO_STOP_BACKEND_DEVICE); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to cork PulseAudio stream."); + return MA_ERROR; } - result = ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pOP); + result = ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, pOP); if (result != MA_SUCCESS) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while waiting for the PulseAudio stream to cork.", result); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while waiting for the PulseAudio stream to cork."); + return result; } if (!wasSuccessful) { - if (cork) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to stop PulseAudio stream.", MA_FAILED_TO_STOP_BACKEND_DEVICE); - } else { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to start PulseAudio stream.", MA_FAILED_TO_START_BACKEND_DEVICE); - } + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to %s PulseAudio stream.", (cork) ? "stop" : "start"); + return MA_ERROR; } return MA_SUCCESS; @@ -23579,11 +29283,12 @@ static ma_result ma_device_start__pulse(ma_device* pDevice) } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - /* We need to fill some data before uncorking. Not doing this will result in the write callback never getting fired. */ - result = ma_device_write_to_stream__pulse(pDevice, (ma_pa_stream*)(pDevice->pulse.pStreamPlayback), NULL); - if (result != MA_SUCCESS) { - return result; /* Failed to write data. Not sure what to do here... Just aborting. */ - } + /* + We need to fill some data before uncorking. Not doing this will result in the write callback + never getting fired. We're not going to abort if writing fails because I still want the device + to get uncorked. + */ + ma_device_write_to_stream__pulse(pDevice, (ma_pa_stream*)(pDevice->pulse.pStreamPlayback), NULL); /* No need to check the result here. Always want to fall through an uncork.*/ result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 0); if (result != MA_SUCCESS) { @@ -23597,7 +29302,6 @@ static ma_result ma_device_start__pulse(ma_device* pDevice) static ma_result ma_device_stop__pulse(ma_device* pDevice) { ma_result result; - ma_bool32 wasSuccessful; MA_ASSERT(pDevice != NULL); @@ -23609,9 +29313,16 @@ static ma_result ma_device_stop__pulse(ma_device* pDevice) } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - /* The stream needs to be drained if it's a playback device. */ + /* + Ideally we would drain the device here, but there's been cases where PulseAudio seems to be + broken on some systems to the point where no audio processing seems to happen. When this + happens, draining never completes and we get stuck here. For now I'm disabling draining of + the device so we don't just freeze the application. + */ + #if 0 ma_pa_operation* pOP = ((ma_pa_stream_drain_proc)pDevice->pContext->pulse.pa_stream_drain)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_pulse_operation_complete_callback, &wasSuccessful); - ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pOP); + ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, pOP); + #endif result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 1); if (result != MA_SUCCESS) { @@ -23634,8 +29345,8 @@ static ma_result ma_device_data_loop__pulse(ma_device* pDevice) All data is handled through callbacks. All we need to do is iterate over the main loop and let the callbacks deal with it. */ - while (ma_device_get_state(pDevice) == MA_STATE_STARTED) { - resultPA = ((ma_pa_mainloop_iterate_proc)pDevice->pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pContext->pulse.pMainLoop, 1, NULL); + while (ma_device_get_state(pDevice) == ma_device_state_started) { + resultPA = ((ma_pa_mainloop_iterate_proc)pDevice->pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL); if (resultPA < 0) { break; } @@ -23649,7 +29360,7 @@ static ma_result ma_device_data_loop_wakeup__pulse(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); - ((ma_pa_mainloop_wakeup_proc)pDevice->pContext->pulse.pa_mainloop_wakeup)((ma_pa_mainloop*)pDevice->pContext->pulse.pMainLoop); + ((ma_pa_mainloop_wakeup_proc)pDevice->pContext->pulse.pa_mainloop_wakeup)((ma_pa_mainloop*)pDevice->pulse.pMainLoop); return MA_SUCCESS; } @@ -23663,6 +29374,9 @@ static ma_result ma_context_uninit__pulse(ma_context* pContext) ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pContext->pulse.pPulseContext); ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pContext->pulse.pMainLoop); + ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks); + ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks); + #ifndef MA_NO_RUNTIME_LINKING ma_dlclose(pContext, pContext->pulse.pulseSO); #endif @@ -23739,6 +29453,7 @@ static ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_c pContext->pulse.pa_stream_set_write_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_write_callback"); pContext->pulse.pa_stream_set_read_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_read_callback"); pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_suspended_callback"); + pContext->pulse.pa_stream_set_moved_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_moved_callback"); pContext->pulse.pa_stream_is_suspended = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_is_suspended"); pContext->pulse.pa_stream_flush = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_flush"); pContext->pulse.pa_stream_drain = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_drain"); @@ -23801,6 +29516,7 @@ static ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_c ma_pa_stream_set_write_callback_proc _pa_stream_set_write_callback = pa_stream_set_write_callback; ma_pa_stream_set_read_callback_proc _pa_stream_set_read_callback = pa_stream_set_read_callback; ma_pa_stream_set_suspended_callback_proc _pa_stream_set_suspended_callback = pa_stream_set_suspended_callback; + ma_pa_stream_set_moved_callback_proc _pa_stream_set_moved_callback = pa_stream_set_moved_callback; ma_pa_stream_is_suspended_proc _pa_stream_is_suspended = pa_stream_is_suspended; ma_pa_stream_flush_proc _pa_stream_flush = pa_stream_flush; ma_pa_stream_drain_proc _pa_stream_drain = pa_stream_drain; @@ -23862,6 +29578,7 @@ static ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_c pContext->pulse.pa_stream_set_write_callback = (ma_proc)_pa_stream_set_write_callback; pContext->pulse.pa_stream_set_read_callback = (ma_proc)_pa_stream_set_read_callback; pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)_pa_stream_set_suspended_callback; + pContext->pulse.pa_stream_set_moved_callback = (ma_proc)_pa_stream_set_moved_callback; pContext->pulse.pa_stream_is_suspended = (ma_proc)_pa_stream_is_suspended; pContext->pulse.pa_stream_flush = (ma_proc)_pa_stream_flush; pContext->pulse.pa_stream_drain = (ma_proc)_pa_stream_drain; @@ -23876,48 +29593,28 @@ static ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_c pContext->pulse.pa_stream_readable_size = (ma_proc)_pa_stream_readable_size; #endif - /* The PulseAudio context maps well to miniaudio's notion of a context. The pa_context object will be initialized as part of the ma_context. */ - pContext->pulse.pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)(); - if (pContext->pulse.pMainLoop == NULL) { - result = ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create mainloop.", MA_FAILED_TO_INIT_BACKEND); - #ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(pContext, pContext->pulse.pulseSO); - #endif - return result; + /* We need to make a copy of the application and server names so we can pass them to the pa_context of each device. */ + pContext->pulse.pApplicationName = ma_copy_string(pConfig->pulse.pApplicationName, &pContext->allocationCallbacks); + if (pContext->pulse.pApplicationName == NULL && pConfig->pulse.pApplicationName != NULL) { + return MA_OUT_OF_MEMORY; } - pContext->pulse.pPulseContext = ((ma_pa_context_new_proc)pContext->pulse.pa_context_new)(((ma_pa_mainloop_get_api_proc)pContext->pulse.pa_mainloop_get_api)((ma_pa_mainloop*)pContext->pulse.pMainLoop), pConfig->pulse.pApplicationName); - if (pContext->pulse.pPulseContext == NULL) { - result = ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio context.", MA_FAILED_TO_INIT_BACKEND); - ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pContext->pulse.pMainLoop)); - #ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(pContext, pContext->pulse.pulseSO); - #endif - return result; + pContext->pulse.pServerName = ma_copy_string(pConfig->pulse.pServerName, &pContext->allocationCallbacks); + if (pContext->pulse.pServerName == NULL && pConfig->pulse.pServerName != NULL) { + ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks); + return MA_OUT_OF_MEMORY; } - /* Now we need to connect to the context. Everything is asynchronous so we need to wait for it to connect before returning. */ - result = ma_result_from_pulse(((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)((ma_pa_context*)pContext->pulse.pPulseContext, pConfig->pulse.pServerName, (pConfig->pulse.tryAutoSpawn) ? 0 : MA_PA_CONTEXT_NOAUTOSPAWN, NULL)); + result = ma_init_pa_mainloop_and_pa_context__pulse(pContext, pConfig->pulse.pApplicationName, pConfig->pulse.pServerName, pConfig->pulse.tryAutoSpawn, &pContext->pulse.pMainLoop, &pContext->pulse.pPulseContext); if (result != MA_SUCCESS) { - ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio context.", result); - ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pContext->pulse.pMainLoop)); + ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks); + ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks); #ifndef MA_NO_RUNTIME_LINKING ma_dlclose(pContext, pContext->pulse.pulseSO); #endif return result; } - /* Since ma_context_init() runs synchronously we need to wait for the PulseAudio context to connect before we return. */ - result = ma_context_wait_for_pa_context_to_connect__pulse(pContext); - if (result != MA_SUCCESS) { - ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pContext->pulse.pMainLoop)); - #ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(pContext, pContext->pulse.pulseSO); - #endif - return result; - } - - /* With pa_mainloop we run a synchronous backend, but we implement our own main loop. */ pCallbacks->onContextInit = ma_context_init__pulse; pCallbacks->onContextUninit = ma_context_uninit__pulse; @@ -24082,7 +29779,8 @@ static ma_result ma_context_get_device_info__jack(ma_context* pContext, ma_devic /* The channel count and sample rate can only be determined by opening the device. */ result = ma_context_open_client__jack(pContext, &pClient); if (result != MA_SUCCESS) { - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client.", result); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client."); + return result; } pDeviceInfo->nativeDataFormats[0].sampleRate = ((ma_jack_get_sample_rate_proc)pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pClient); @@ -24091,7 +29789,8 @@ static ma_result ma_context_get_device_info__jack(ma_context* pContext, ma_devic ppPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ((deviceType == ma_device_type_playback) ? ma_JackPortIsInput : ma_JackPortIsOutput)); if (ppPorts == NULL) { ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient); - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports."); + return MA_FAILED_TO_OPEN_BACKEND_DEVICE; } while (ppPorts[pDeviceInfo->nativeDataFormats[0].channels] != NULL) { @@ -24123,11 +29822,13 @@ static ma_result ma_device_uninit__jack(ma_device* pDevice) } if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma__free_from_callbacks(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks); + ma_free(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks); + ma_free(pDevice->jack.ppPortsCapture, &pDevice->pContext->allocationCallbacks); } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma__free_from_callbacks(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks); + ma_free(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks); + ma_free(pDevice->jack.ppPortsPlayback, &pDevice->pContext->allocationCallbacks); } return MA_SUCCESS; @@ -24149,12 +29850,12 @@ static int ma_device__jack_buffer_size_callback(ma_jack_nframes_t frameCount, vo if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { size_t newBufferSize = frameCount * (pDevice->capture.internalChannels * ma_get_bytes_per_sample(pDevice->capture.internalFormat)); - float* pNewBuffer = (float*)ma__calloc_from_callbacks(newBufferSize, &pDevice->pContext->allocationCallbacks); + float* pNewBuffer = (float*)ma_calloc(newBufferSize, &pDevice->pContext->allocationCallbacks); if (pNewBuffer == NULL) { return MA_OUT_OF_MEMORY; } - ma__free_from_callbacks(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks); + ma_free(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks); pDevice->jack.pIntermediaryBufferCapture = pNewBuffer; pDevice->playback.internalPeriodSizeInFrames = frameCount; @@ -24162,12 +29863,12 @@ static int ma_device__jack_buffer_size_callback(ma_jack_nframes_t frameCount, vo if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { size_t newBufferSize = frameCount * (pDevice->playback.internalChannels * ma_get_bytes_per_sample(pDevice->playback.internalFormat)); - float* pNewBuffer = (float*)ma__calloc_from_callbacks(newBufferSize, &pDevice->pContext->allocationCallbacks); + float* pNewBuffer = (float*)ma_calloc(newBufferSize, &pDevice->pContext->allocationCallbacks); if (pNewBuffer == NULL) { return MA_OUT_OF_MEMORY; } - ma__free_from_callbacks(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks); + ma_free(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks); pDevice->jack.pIntermediaryBufferPlayback = pNewBuffer; pDevice->playback.internalPeriodSizeInFrames = frameCount; @@ -24191,7 +29892,7 @@ static int ma_device__jack_process_callback(ma_jack_nframes_t frameCount, void* if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { /* Channels need to be interleaved. */ for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) { - const float* pSrc = (const float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.pPortsCapture[iChannel], frameCount); + const float* pSrc = (const float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.ppPortsCapture[iChannel], frameCount); if (pSrc != NULL) { float* pDst = pDevice->jack.pIntermediaryBufferCapture + iChannel; ma_jack_nframes_t iFrame; @@ -24212,7 +29913,7 @@ static int ma_device__jack_process_callback(ma_jack_nframes_t frameCount, void* /* Channels need to be deinterleaved. */ for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) { - float* pDst = (float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.pPortsPlayback[iChannel], frameCount); + float* pDst = (float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.ppPortsPlayback[iChannel], frameCount); if (pDst != NULL) { const float* pSrc = pDevice->jack.pIntermediaryBufferPlayback + iChannel; ma_jack_nframes_t iFrame; @@ -24238,33 +29939,39 @@ static ma_result ma_device_init__jack(ma_device* pDevice, const ma_device_config MA_ASSERT(pDevice != NULL); if (pConfig->deviceType == ma_device_type_loopback) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Loopback mode not supported."); return MA_DEVICE_TYPE_NOT_SUPPORTED; } /* Only supporting default devices with JACK. */ if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->pDeviceID != NULL && pDescriptorPlayback->pDeviceID->jack != 0) || ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->pDeviceID != NULL && pDescriptorCapture->pDeviceID->jack != 0)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Only default devices are supported."); return MA_NO_DEVICE; } /* No exclusive mode with the JACK backend. */ if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Exclusive mode not supported."); return MA_SHARE_MODE_NOT_SUPPORTED; } /* Open the client. */ result = ma_context_open_client__jack(pDevice->pContext, (ma_jack_client_t**)&pDevice->jack.pClient); if (result != MA_SUCCESS) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client.", result); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client."); + return result; } /* Callbacks. */ if (((ma_jack_set_process_callback_proc)pDevice->pContext->jack.jack_set_process_callback)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_process_callback, pDevice) != 0) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to set process callback.", MA_FAILED_TO_OPEN_BACKEND_DEVICE); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to set process callback."); + return MA_FAILED_TO_OPEN_BACKEND_DEVICE; } if (((ma_jack_set_buffer_size_callback_proc)pDevice->pContext->jack.jack_set_buffer_size_callback)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_buffer_size_callback, pDevice) != 0) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to set buffer size callback.", MA_FAILED_TO_OPEN_BACKEND_DEVICE); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to set buffer size callback."); + return MA_FAILED_TO_OPEN_BACKEND_DEVICE; } ((ma_jack_on_shutdown_proc)pDevice->pContext->jack.jack_on_shutdown)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_shutdown_callback, pDevice); @@ -24274,31 +29981,42 @@ static ma_result ma_device_init__jack(ma_device* pDevice, const ma_device_config periodSizeInFrames = ((ma_jack_get_buffer_size_proc)pDevice->pContext->jack.jack_get_buffer_size)((ma_jack_client_t*)pDevice->jack.pClient); if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + ma_uint32 iPort; const char** ppPorts; pDescriptorCapture->format = ma_format_f32; pDescriptorCapture->channels = 0; pDescriptorCapture->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient); - ma_get_standard_channel_map(ma_standard_channel_map_alsa, pDescriptorCapture->channels, pDescriptorCapture->channelMap); + ma_channel_map_init_standard(ma_standard_channel_map_alsa, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); ppPorts = ((ma_jack_get_ports_proc)pDevice->pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsOutput); if (ppPorts == NULL) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports."); + return MA_FAILED_TO_OPEN_BACKEND_DEVICE; } + /* Need to count the number of ports first so we can allocate some memory. */ while (ppPorts[pDescriptorCapture->channels] != NULL) { + pDescriptorCapture->channels += 1; + } + + pDevice->jack.ppPortsCapture = (ma_ptr*)ma_malloc(sizeof(*pDevice->jack.ppPortsCapture) * pDescriptorCapture->channels, &pDevice->pContext->allocationCallbacks); + if (pDevice->jack.ppPortsCapture == NULL) { + return MA_OUT_OF_MEMORY; + } + + for (iPort = 0; iPort < pDescriptorCapture->channels; iPort += 1) { char name[64]; ma_strcpy_s(name, sizeof(name), "capture"); - ma_itoa_s((int)pDescriptorCapture->channels, name+7, sizeof(name)-7, 10); /* 7 = length of "capture" */ + ma_itoa_s((int)iPort, name+7, sizeof(name)-7, 10); /* 7 = length of "capture" */ - pDevice->jack.pPortsCapture[pDescriptorCapture->channels] = ((ma_jack_port_register_proc)pDevice->pContext->jack.jack_port_register)((ma_jack_client_t*)pDevice->jack.pClient, name, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsInput, 0); - if (pDevice->jack.pPortsCapture[pDescriptorCapture->channels] == NULL) { + pDevice->jack.ppPortsCapture[iPort] = ((ma_jack_port_register_proc)pDevice->pContext->jack.jack_port_register)((ma_jack_client_t*)pDevice->jack.pClient, name, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsInput, 0); + if (pDevice->jack.ppPortsCapture[iPort] == NULL) { ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); ma_device_uninit__jack(pDevice); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports."); + return MA_FAILED_TO_OPEN_BACKEND_DEVICE; } - - pDescriptorCapture->channels += 1; } ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); @@ -24306,7 +30024,7 @@ static ma_result ma_device_init__jack(ma_device* pDevice, const ma_device_config pDescriptorCapture->periodSizeInFrames = periodSizeInFrames; pDescriptorCapture->periodCount = 1; /* There's no notion of a period in JACK. Just set to 1. */ - pDevice->jack.pIntermediaryBufferCapture = (float*)ma__calloc_from_callbacks(pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels), &pDevice->pContext->allocationCallbacks); + pDevice->jack.pIntermediaryBufferCapture = (float*)ma_calloc(pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels), &pDevice->pContext->allocationCallbacks); if (pDevice->jack.pIntermediaryBufferCapture == NULL) { ma_device_uninit__jack(pDevice); return MA_OUT_OF_MEMORY; @@ -24314,31 +30032,43 @@ static ma_result ma_device_init__jack(ma_device* pDevice, const ma_device_config } if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + ma_uint32 iPort; const char** ppPorts; pDescriptorPlayback->format = ma_format_f32; pDescriptorPlayback->channels = 0; pDescriptorPlayback->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient); - ma_get_standard_channel_map(ma_standard_channel_map_alsa, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap); + ma_channel_map_init_standard(ma_standard_channel_map_alsa, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels); ppPorts = ((ma_jack_get_ports_proc)pDevice->pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsInput); if (ppPorts == NULL) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports."); + return MA_FAILED_TO_OPEN_BACKEND_DEVICE; } + /* Need to count the number of ports first so we can allocate some memory. */ while (ppPorts[pDescriptorPlayback->channels] != NULL) { + pDescriptorPlayback->channels += 1; + } + + pDevice->jack.ppPortsPlayback = (ma_ptr*)ma_malloc(sizeof(*pDevice->jack.ppPortsPlayback) * pDescriptorPlayback->channels, &pDevice->pContext->allocationCallbacks); + if (pDevice->jack.ppPortsPlayback == NULL) { + ma_free(pDevice->jack.ppPortsCapture, &pDevice->pContext->allocationCallbacks); + return MA_OUT_OF_MEMORY; + } + + for (iPort = 0; iPort < pDescriptorPlayback->channels; iPort += 1) { char name[64]; ma_strcpy_s(name, sizeof(name), "playback"); - ma_itoa_s((int)pDescriptorPlayback->channels, name+8, sizeof(name)-8, 10); /* 8 = length of "playback" */ + ma_itoa_s((int)iPort, name+8, sizeof(name)-8, 10); /* 8 = length of "playback" */ - pDevice->jack.pPortsPlayback[pDescriptorPlayback->channels] = ((ma_jack_port_register_proc)pDevice->pContext->jack.jack_port_register)((ma_jack_client_t*)pDevice->jack.pClient, name, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsOutput, 0); - if (pDevice->jack.pPortsPlayback[pDescriptorPlayback->channels] == NULL) { + pDevice->jack.ppPortsPlayback[iPort] = ((ma_jack_port_register_proc)pDevice->pContext->jack.jack_port_register)((ma_jack_client_t*)pDevice->jack.pClient, name, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsOutput, 0); + if (pDevice->jack.ppPortsPlayback[iPort] == NULL) { ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); ma_device_uninit__jack(pDevice); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports."); + return MA_FAILED_TO_OPEN_BACKEND_DEVICE; } - - pDescriptorPlayback->channels += 1; } ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); @@ -24346,7 +30076,7 @@ static ma_result ma_device_init__jack(ma_device* pDevice, const ma_device_config pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames; pDescriptorPlayback->periodCount = 1; /* There's no notion of a period in JACK. Just set to 1. */ - pDevice->jack.pIntermediaryBufferPlayback = (float*)ma__calloc_from_callbacks(pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels), &pDevice->pContext->allocationCallbacks); + pDevice->jack.pIntermediaryBufferPlayback = (float*)ma_calloc(pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels), &pDevice->pContext->allocationCallbacks); if (pDevice->jack.pIntermediaryBufferPlayback == NULL) { ma_device_uninit__jack(pDevice); return MA_OUT_OF_MEMORY; @@ -24365,25 +30095,28 @@ static ma_result ma_device_start__jack(ma_device* pDevice) resultJACK = ((ma_jack_activate_proc)pContext->jack.jack_activate)((ma_jack_client_t*)pDevice->jack.pClient); if (resultJACK != 0) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to activate the JACK client.", MA_FAILED_TO_START_BACKEND_DEVICE); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to activate the JACK client."); + return MA_FAILED_TO_START_BACKEND_DEVICE; } if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { const char** ppServerPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsOutput); if (ppServerPorts == NULL) { ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports.", MA_ERROR); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports."); + return MA_ERROR; } for (i = 0; ppServerPorts[i] != NULL; ++i) { const char* pServerPort = ppServerPorts[i]; - const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.pPortsCapture[i]); + const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.ppPortsCapture[i]); resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pServerPort, pClientPort); if (resultJACK != 0) { ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports.", MA_ERROR); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports."); + return MA_ERROR; } } @@ -24394,18 +30127,20 @@ static ma_result ma_device_start__jack(ma_device* pDevice) const char** ppServerPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsInput); if (ppServerPorts == NULL) { ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports.", MA_ERROR); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports."); + return MA_ERROR; } for (i = 0; ppServerPorts[i] != NULL; ++i) { const char* pServerPort = ppServerPorts[i]; - const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.pPortsPlayback[i]); + const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.ppPortsPlayback[i]); resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pClientPort, pServerPort); if (resultJACK != 0) { ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports.", MA_ERROR); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports."); + return MA_ERROR; } } @@ -24418,16 +30153,13 @@ static ma_result ma_device_start__jack(ma_device* pDevice) static ma_result ma_device_stop__jack(ma_device* pDevice) { ma_context* pContext = pDevice->pContext; - ma_stop_proc onStop; if (((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient) != 0) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] An error occurred when deactivating the JACK client.", MA_ERROR); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] An error occurred when deactivating the JACK client."); + return MA_ERROR; } - onStop = pDevice->onStop; - if (onStop) { - onStop(pDevice); - } + ma_device__on_notification_stopped(pDevice); return MA_SUCCESS; } @@ -24592,6 +30324,13 @@ References #if defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 #define MA_APPLE_WATCH #endif + #if __has_feature(objc_arc) + #define MA_BRIDGE_TRANSFER __bridge_transfer + #define MA_BRIDGE_RETAINED __bridge_retained + #else + #define MA_BRIDGE_TRANSFER + #define MA_BRIDGE_RETAINED + #endif #else #define MA_APPLE_DESKTOP #endif @@ -24930,7 +30669,7 @@ static ma_result ma_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* case kAudioChannelLayoutTag_Binaural: case kAudioChannelLayoutTag_Ambisonic_B_Format: { - ma_get_standard_channel_map(ma_standard_channel_map_default, channelCount, pChannelMap); + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount); } break; case kAudioChannelLayoutTag_Octagonal: @@ -24958,7 +30697,7 @@ static ma_result ma_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* default: { - ma_get_standard_channel_map(ma_standard_channel_map_default, channelCount, pChannelMap); + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount); } break; } } @@ -25095,14 +30834,14 @@ static ma_bool32 ma_does_AudioObject_support_scope(ma_context* pContext, AudioOb return MA_FALSE; } - pBufferList = (AudioBufferList*)ma__malloc_from_callbacks(dataSize, &pContext->allocationCallbacks); + pBufferList = (AudioBufferList*)ma_malloc(dataSize, &pContext->allocationCallbacks); if (pBufferList == NULL) { return MA_FALSE; /* Out of memory. */ } status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pBufferList); if (status != noErr) { - ma__free_from_callbacks(pBufferList, &pContext->allocationCallbacks); + ma_free(pBufferList, &pContext->allocationCallbacks); return MA_FALSE; } @@ -25111,7 +30850,7 @@ static ma_bool32 ma_does_AudioObject_support_scope(ma_context* pContext, AudioOb isSupported = MA_TRUE; } - ma__free_from_callbacks(pBufferList, &pContext->allocationCallbacks); + ma_free(pBufferList, &pContext->allocationCallbacks); return isSupported; } @@ -25740,24 +31479,24 @@ static ma_result ma_get_AudioUnit_channel_map(ma_context* pContext, AudioUnit au return ma_result_from_OSStatus(status); } - pChannelLayout = (AudioChannelLayout*)ma__malloc_from_callbacks(channelLayoutSize, &pContext->allocationCallbacks); + pChannelLayout = (AudioChannelLayout*)ma_malloc(channelLayoutSize, &pContext->allocationCallbacks); if (pChannelLayout == NULL) { return MA_OUT_OF_MEMORY; } status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, pChannelLayout, &channelLayoutSize); if (status != noErr) { - ma__free_from_callbacks(pChannelLayout, &pContext->allocationCallbacks); + ma_free(pChannelLayout, &pContext->allocationCallbacks); return ma_result_from_OSStatus(status); } result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap); if (result != MA_SUCCESS) { - ma__free_from_callbacks(pChannelLayout, &pContext->allocationCallbacks); + ma_free(pChannelLayout, &pContext->allocationCallbacks); return result; } - ma__free_from_callbacks(pChannelLayout, &pContext->allocationCallbacks); + ma_free(pChannelLayout, &pContext->allocationCallbacks); return MA_SUCCESS; } #endif /* MA_APPLE_DESKTOP */ @@ -25993,7 +31732,7 @@ static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_ UInt32 propSize; /* We want to ensure we use a consistent device name to device enumeration. */ - if (pDeviceID != NULL) { + if (pDeviceID != NULL && pDeviceID->coreaudio[0] != '\0') { ma_bool32 found = MA_FALSE; if (deviceType == ma_device_type_playback) { NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs]; @@ -26109,7 +31848,7 @@ static AudioBufferList* ma_allocate_AudioBufferList__coreaudio(ma_uint32 sizeInF allocationSize += sizeInFrames * ma_get_bytes_per_frame(format, channels); - pBufferList = (AudioBufferList*)ma__malloc_from_callbacks(allocationSize, pAllocationCallbacks); + pBufferList = (AudioBufferList*)ma_malloc(allocationSize, pAllocationCallbacks); if (pBufferList == NULL) { return NULL; } @@ -26145,12 +31884,12 @@ static ma_result ma_device_realloc_AudioBufferList__coreaudio(ma_device* pDevice AudioBufferList* pNewAudioBufferList; pNewAudioBufferList = ma_allocate_AudioBufferList__coreaudio(sizeInFrames, format, channels, layout, &pDevice->pContext->allocationCallbacks); - if (pNewAudioBufferList != NULL) { + if (pNewAudioBufferList == NULL) { return MA_OUT_OF_MEMORY; } /* At this point we'll have a new AudioBufferList and we can free the old one. */ - ma__free_from_callbacks(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); + ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); pDevice->coreaudio.pAudioBufferList = pNewAudioBufferList; pDevice->coreaudio.audioBufferCapInFrames = sizeInFrames; } @@ -26167,7 +31906,7 @@ static OSStatus ma_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFl MA_ASSERT(pDevice != NULL); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "INFO: Output Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", busNumber, frameCount, pBufferList->mNumberBuffers); + /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "INFO: Output Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", (int)busNumber, (int)frameCount, (int)pBufferList->mNumberBuffers);*/ /* We need to check whether or not we are outputting interleaved or non-interleaved samples. The way we do this is slightly different for each type. */ layout = ma_stream_layout_interleaved; @@ -26185,7 +31924,7 @@ static OSStatus ma_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFl ma_device_handle_backend_data_callback(pDevice, pBufferList->mBuffers[iBuffer].mData, NULL, frameCountForThisBuffer); } - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pBufferList->mBuffers[iBuffer].mNumberChannels, pBufferList->mBuffers[iBuffer].mDataByteSize); + /*a_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", (int)frameCount, (int)pBufferList->mBuffers[iBuffer].mNumberChannels, (int)pBufferList->mBuffers[iBuffer].mDataByteSize);*/ } else { /* This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's @@ -26193,7 +31932,7 @@ static OSStatus ma_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFl output silence here. */ MA_ZERO_MEMORY(pBufferList->mBuffers[iBuffer].mData, pBufferList->mBuffers[iBuffer].mDataByteSize); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pBufferList->mBuffers[iBuffer].mNumberChannels, pBufferList->mBuffers[iBuffer].mDataByteSize); + /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", (int)frameCount, (int)pBufferList->mBuffers[iBuffer].mNumberChannels, (int)pBufferList->mBuffers[iBuffer].mDataByteSize);*/ } } } else { @@ -26262,7 +32001,7 @@ static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFla layout = ma_stream_layout_deinterleaved; } - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "INFO: Input Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", busNumber, frameCount, pRenderedBufferList->mNumberBuffers); + /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "INFO: Input Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", (int)busNumber, (int)frameCount, (int)pRenderedBufferList->mNumberBuffers);*/ /* There has been a situation reported where frame count passed into this function is greater than the capacity of @@ -26272,9 +32011,12 @@ static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFla */ result = ma_device_realloc_AudioBufferList__coreaudio(pDevice, frameCount, pDevice->capture.internalFormat, pDevice->capture.internalChannels, layout); if (result != MA_SUCCESS) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "Failed to allocate AudioBufferList for capture."); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "Failed to allocate AudioBufferList for capture.\n"); return noErr; } + + pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList; + MA_ASSERT(pRenderedBufferList); /* When you call AudioUnitRender(), Core Audio tries to be helpful by setting the mDataByteSize to the number of bytes @@ -26290,7 +32032,7 @@ static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFla status = ((ma_AudioUnitRender_proc)pDevice->pContext->coreaudio.AudioUnitRender)((AudioUnit)pDevice->coreaudio.audioUnitCapture, pActionFlags, pTimeStamp, busNumber, frameCount, pRenderedBufferList); if (status != noErr) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " ERROR: AudioUnitRender() failed with %d\n", status); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " ERROR: AudioUnitRender() failed with %d.\n", (int)status); return status; } @@ -26298,7 +32040,7 @@ static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFla for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) { if (pRenderedBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->capture.internalChannels) { ma_device_handle_backend_data_callback(pDevice, NULL, pRenderedBufferList->mBuffers[iBuffer].mData, frameCount); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " mDataByteSize=%d\n", pRenderedBufferList->mBuffers[iBuffer].mDataByteSize); + /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " mDataByteSize=%d.\n", (int)pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);*/ } else { /* This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's @@ -26321,7 +32063,7 @@ static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFla framesRemaining -= framesToSend; } - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pRenderedBufferList->mBuffers[iBuffer].mNumberChannels, pRenderedBufferList->mBuffers[iBuffer].mDataByteSize); + /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", (int)frameCount, (int)pRenderedBufferList->mBuffers[iBuffer].mNumberChannels, (int)pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);*/ } } } else { @@ -26383,24 +32125,17 @@ static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, Audio can try waiting on the same lock. I'm going to try working around this by not calling any Core Audio APIs in the callback when the device has been stopped or uninitialized. */ - if (ma_device_get_state(pDevice) == MA_STATE_UNINITIALIZED || ma_device_get_state(pDevice) == MA_STATE_STOPPING || ma_device_get_state(pDevice) == MA_STATE_STOPPED) { - ma_stop_proc onStop = pDevice->onStop; - if (onStop) { - onStop(pDevice); - } - - ma_event_signal(&pDevice->coreaudio.stopEvent); + if (ma_device_get_state(pDevice) == ma_device_state_uninitialized || ma_device_get_state(pDevice) == ma_device_state_stopping || ma_device_get_state(pDevice) == ma_device_state_stopped) { + ma_device__on_notification_stopped(pDevice); } else { UInt32 isRunning; UInt32 isRunningSize = sizeof(isRunning); OSStatus status = ((ma_AudioUnitGetProperty_proc)pDevice->pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioOutputUnitProperty_IsRunning, scope, element, &isRunning, &isRunningSize); if (status != noErr) { - return; /* Don't really know what to do in this case... just ignore it, I suppose... */ + goto done; /* Don't really know what to do in this case... just ignore it, I suppose... */ } if (!isRunning) { - ma_stop_proc onStop; - /* The stop event is a bit annoying in Core Audio because it will be called when we automatically switch the default device. Some scenarios to consider: @@ -26414,12 +32149,12 @@ static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, Audio /* It looks like the device is switching through an external event, such as the user unplugging the device or changing the default device via the operating system's sound settings. If we're re-initializing the device, we just terminate because we want the stopping of the - device to be seamless to the client (we don't want them receiving the onStop event and thinking that the device has stopped when it + device to be seamless to the client (we don't want them receiving the stopped event and thinking that the device has stopped when it hasn't!). */ if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) || ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) { - return; + goto done; } /* @@ -26427,20 +32162,21 @@ static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, Audio will try switching to the new default device seamlessly. We need to somehow find a way to determine whether or not Core Audio will most likely be successful in switching to the new device. - TODO: Try to predict if Core Audio will switch devices. If not, the onStop callback needs to be posted. + TODO: Try to predict if Core Audio will switch devices. If not, the stopped callback needs to be posted. */ - return; + goto done; } /* Getting here means we need to stop the device. */ - onStop = pDevice->onStop; - if (onStop) { - onStop(pDevice); - } + ma_device__on_notification_stopped(pDevice); } } (void)propertyID; /* Unused. */ + +done: + /* Always signal the stop event. It's possible for the "else" case to get hit which can happen during an interruption. */ + ma_event_signal(&pDevice->coreaudio.stopEvent); } #if defined(MA_APPLE_DESKTOP) @@ -26491,7 +32227,7 @@ static OSStatus ma_default_device_changed__coreaudio(AudioObjectID objectID, UIn ma_device__post_init_setup(pDevice, deviceType); /* Restart the device if required. If this fails we need to stop the device entirely. */ - if (ma_device_get_state(pDevice) == MA_STATE_STARTED) { + if (ma_device_get_state(pDevice) == ma_device_state_started) { OSStatus status; if (deviceType == ma_device_type_playback) { status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); @@ -26499,7 +32235,7 @@ static OSStatus ma_default_device_changed__coreaudio(AudioObjectID objectID, UIn if (pDevice->type == ma_device_type_duplex) { ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); } - ma_device__set_state(pDevice, MA_STATE_STOPPED); + ma_device__set_state(pDevice, ma_device_state_stopped); } } else if (deviceType == ma_device_type_capture) { status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture); @@ -26507,10 +32243,12 @@ static OSStatus ma_default_device_changed__coreaudio(AudioObjectID objectID, UIn if (pDevice->type == ma_device_type_duplex) { ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); } - ma_device__set_state(pDevice, MA_STATE_STOPPED); + ma_device__set_state(pDevice, ma_device_state_stopped); } } } + + ma_device__on_notification_rerouted(pDevice); } } } @@ -26574,7 +32312,7 @@ static ma_result ma_context__uninit_device_tracking__coreaudio(ma_context* pCont /* At this point there should be no tracked devices. If not there's an error somewhere. */ if (g_ppTrackedDevices_CoreAudio != NULL) { - ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_WARNING, "You have uninitialized all contexts while an associated device is still active.", MA_INVALID_OPERATION); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "You have uninitialized all contexts while an associated device is still active."); ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio); return MA_INVALID_OPERATION; } @@ -26595,17 +32333,15 @@ static ma_result ma_device__track__coreaudio(ma_device* pDevice) { /* Allocate memory if required. */ if (g_TrackedDeviceCap_CoreAudio <= g_TrackedDeviceCount_CoreAudio) { - ma_uint32 oldCap; ma_uint32 newCap; ma_device** ppNewDevices; - oldCap = g_TrackedDeviceCap_CoreAudio; newCap = g_TrackedDeviceCap_CoreAudio * 2; if (newCap == 0) { newCap = 1; } - ppNewDevices = (ma_device**)ma__realloc_from_callbacks(g_ppTrackedDevices_CoreAudio, sizeof(*g_ppTrackedDevices_CoreAudio)*newCap, sizeof(*g_ppTrackedDevices_CoreAudio)*oldCap, &pDevice->pContext->allocationCallbacks); + ppNewDevices = (ma_device**)ma_realloc(g_ppTrackedDevices_CoreAudio, sizeof(*g_ppTrackedDevices_CoreAudio)*newCap, &pDevice->pContext->allocationCallbacks); if (ppNewDevices == NULL) { ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); return MA_OUT_OF_MEMORY; @@ -26642,7 +32378,7 @@ static ma_result ma_device__untrack__coreaudio(ma_device* pDevice) /* If there's nothing else in the list we need to free memory. */ if (g_TrackedDeviceCount_CoreAudio == 0) { - ma__free_from_callbacks(g_ppTrackedDevices_CoreAudio, &pDevice->pContext->allocationCallbacks); + ma_free(g_ppTrackedDevices_CoreAudio, &pDevice->pContext->allocationCallbacks); g_ppTrackedDevices_CoreAudio = NULL; g_TrackedDeviceCap_CoreAudio = 0; } @@ -26658,30 +32394,71 @@ static ma_result ma_device__untrack__coreaudio(ma_device* pDevice) #endif #if defined(MA_APPLE_MOBILE) -@interface ma_router_change_handler:NSObject { +@interface ma_ios_notification_handler:NSObject { ma_device* m_pDevice; } @end -@implementation ma_router_change_handler +@implementation ma_ios_notification_handler -(id)init:(ma_device*)pDevice { self = [super init]; m_pDevice = pDevice; + /* For route changes. */ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_route_change:) name:AVAudioSessionRouteChangeNotification object:[AVAudioSession sharedInstance]]; + /* For interruptions. */ + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_interruption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]]; + return self; } -(void)dealloc { [self remove_handler]; + + #if defined(__has_feature) + #if !__has_feature(objc_arc) + [super dealloc]; + #endif + #endif } -(void)remove_handler { [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionRouteChangeNotification object:nil]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionInterruptionNotification object:nil]; +} + +-(void)handle_interruption:(NSNotification*)pNotification +{ + NSInteger type = [[[pNotification userInfo] objectForKey:AVAudioSessionInterruptionTypeKey] integerValue]; + switch (type) + { + case AVAudioSessionInterruptionTypeBegan: + { + ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Interruption: AVAudioSessionInterruptionTypeBegan\n"); + + /* + Core Audio will have stopped the internal device automatically, but we need explicitly + stop it at a higher level to ensure miniaudio-specific state is updated for consistency. + */ + ma_device_stop(m_pDevice); + + /* + Fire the notification after the device has been stopped to ensure it's in the correct + state when the notification handler is invoked. + */ + ma_device__on_notification_interruption_began(m_pDevice); + } break; + + case AVAudioSessionInterruptionTypeEnded: + { + ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Interruption: AVAudioSessionInterruptionTypeEnded\n"); + ma_device__on_notification_interruption_ended(m_pDevice); + } break; + } } -(void)handle_route_change:(NSNotification*)pNotification @@ -26693,66 +32470,45 @@ static ma_result ma_device__untrack__coreaudio(ma_device* pDevice) { case AVAudioSessionRouteChangeReasonOldDeviceUnavailable: { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_DEBUG, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOldDeviceUnavailable\n"); + ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOldDeviceUnavailable\n"); } break; case AVAudioSessionRouteChangeReasonNewDeviceAvailable: { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_DEBUG, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNewDeviceAvailable\n"); + ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNewDeviceAvailable\n"); } break; case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory: { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_DEBUG, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory\n"); + ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory\n"); } break; case AVAudioSessionRouteChangeReasonWakeFromSleep: { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_DEBUG, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonWakeFromSleep\n"); + ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonWakeFromSleep\n"); } break; case AVAudioSessionRouteChangeReasonOverride: { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_DEBUG, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOverride\n"); + ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOverride\n"); } break; case AVAudioSessionRouteChangeReasonCategoryChange: { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_DEBUG, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonCategoryChange\n"); + ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonCategoryChange\n"); } break; case AVAudioSessionRouteChangeReasonUnknown: default: { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_DEBUG, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonUnknown\n"); + ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonUnknown\n"); } break; } ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_DEBUG, "[Core Audio] Changing Route. inputNumberChannels=%d; outputNumberOfChannels=%d\n", (int)pSession.inputNumberOfChannels, (int)pSession.outputNumberOfChannels); - - /* Temporarily disabling this section of code because it appears to be causing errors. */ -#if 0 - ma_uint32 previousState = ma_device_get_state(m_pDevice); - - if (previousState == MA_STATE_STARTED) { - ma_device_stop(m_pDevice); - } - - if (m_pDevice->type == ma_device_type_capture || m_pDevice->type == ma_device_type_duplex) { - m_pDevice->capture.internalChannels = (ma_uint32)pSession.inputNumberOfChannels; - m_pDevice->capture.internalSampleRate = (ma_uint32)pSession.sampleRate; - ma_device__post_init_setup(m_pDevice, ma_device_type_capture); - } - if (m_pDevice->type == ma_device_type_playback || m_pDevice->type == ma_device_type_duplex) { - m_pDevice->playback.internalChannels = (ma_uint32)pSession.outputNumberOfChannels; - m_pDevice->playback.internalSampleRate = (ma_uint32)pSession.sampleRate; - ma_device__post_init_setup(m_pDevice, ma_device_type_playback); - } - - if (previousState == MA_STATE_STARTED) { - ma_device_start(m_pDevice); - } -#endif + + /* Let the application know about the route change. */ + ma_device__on_notification_rerouted(m_pDevice); } @end #endif @@ -26760,7 +32516,7 @@ static ma_result ma_device__untrack__coreaudio(ma_device* pDevice) static ma_result ma_device_uninit__coreaudio(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); - MA_ASSERT(ma_device_get_state(pDevice) == MA_STATE_UNINITIALIZED); + MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_uninitialized); #if defined(MA_APPLE_DESKTOP) /* @@ -26770,9 +32526,9 @@ static ma_result ma_device_uninit__coreaudio(ma_device* pDevice) ma_device__untrack__coreaudio(pDevice); #endif #if defined(MA_APPLE_MOBILE) - if (pDevice->coreaudio.pRouteChangeHandler != NULL) { - ma_router_change_handler* pRouteChangeHandler = (__bridge_transfer ma_router_change_handler*)pDevice->coreaudio.pRouteChangeHandler; - [pRouteChangeHandler remove_handler]; + if (pDevice->coreaudio.pNotificationHandler != NULL) { + ma_ios_notification_handler* pNotificationHandler = (MA_BRIDGE_TRANSFER ma_ios_notification_handler*)pDevice->coreaudio.pNotificationHandler; + [pNotificationHandler remove_handler]; } #endif @@ -26784,7 +32540,7 @@ static ma_result ma_device_uninit__coreaudio(ma_device* pDevice) } if (pDevice->coreaudio.pAudioBufferList) { - ma__free_from_callbacks(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); + ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); } return MA_SUCCESS; @@ -26832,8 +32588,6 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev AURenderCallbackStruct callbackInfo; #if defined(MA_APPLE_DESKTOP) AudioObjectID deviceObjectID; -#else - UInt32 actualPeriodSizeInFramesSize = sizeof(actualPeriodSizeInFrames); #endif /* This API should only be used for a single device type: playback or capture. No full-duplex mode. */ @@ -27087,12 +32841,12 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev } #else /* Fall back to default assumptions. */ - ma_get_standard_channel_map(ma_standard_channel_map_default, pData->channelsOut, pData->channelMapOut); + ma_channel_map_init_standard(ma_standard_channel_map_default, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut); #endif } #else /* TODO: Figure out how to get the channel map using AVAudioSession. */ - ma_get_standard_channel_map(ma_standard_channel_map_default, pData->channelsOut, pData->channelMapOut); + ma_channel_map_init_standard(ma_standard_channel_map_default, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut); #endif @@ -27118,13 +32872,16 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev } #else /* - I don't know how to configure buffer sizes on iOS so for now we're not allowing it to be configured. Instead we're - just going to set it to the value of kAudioUnitProperty_MaximumFramesPerSlice. + On iOS, the size of the IO buffer needs to be specified in seconds and is a floating point + number. I don't trust any potential truncation errors due to converting from float to integer + so I'm going to explicitly set the actual period size to the next power of 2. */ - status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &actualPeriodSizeInFrames, &actualPeriodSizeInFramesSize); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); + @autoreleasepool { + AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; + MA_ASSERT(pAudioSession != NULL); + + [pAudioSession setPreferredIOBufferDuration:((float)actualPeriodSizeInFrames / pAudioSession.sampleRate) error:nil]; + actualPeriodSizeInFrames = ma_next_power_of_2((ma_uint32)(pAudioSession.IOBufferDuration * pAudioSession.sampleRate)); } #endif @@ -27189,7 +32946,7 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev /* Initialize the audio unit. */ status = ((ma_AudioUnitInitialize_proc)pContext->coreaudio.AudioUnitInitialize)(pData->audioUnit); if (status != noErr) { - ma__free_from_callbacks(pData->pAudioBufferList, &pContext->allocationCallbacks); + ma_free(pData->pAudioBufferList, &pContext->allocationCallbacks); pData->pAudioBufferList = NULL; ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); return ma_result_from_OSStatus(status); @@ -27236,7 +32993,7 @@ static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_dev ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture); } if (pDevice->coreaudio.pAudioBufferList) { - ma__free_from_callbacks(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); + ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); } } else if (deviceType == ma_device_type_playback) { data.formatIn = pDevice->playback.format; @@ -27269,6 +33026,7 @@ static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_dev if (deviceType == ma_device_type_capture) { #if defined(MA_APPLE_DESKTOP) pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID; + ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio); #endif pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit; pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList; @@ -27283,6 +33041,7 @@ static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_dev } else if (deviceType == ma_device_type_playback) { #if defined(MA_APPLE_DESKTOP) pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID; + ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio); #endif pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit; @@ -27360,6 +33119,8 @@ static ma_result ma_device_init__coreaudio(ma_device* pDevice, const ma_device_c pDescriptorCapture->periodCount = data.periodsOut; #if defined(MA_APPLE_DESKTOP) + ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio); + /* If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly switch the device in the background. @@ -27398,7 +33159,7 @@ static ma_result ma_device_init__coreaudio(ma_device* pDevice, const ma_device_c if (pConfig->deviceType == ma_device_type_duplex) { ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture); if (pDevice->coreaudio.pAudioBufferList) { - ma__free_from_callbacks(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); + ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); } } return result; @@ -27422,6 +33183,8 @@ static ma_result ma_device_init__coreaudio(ma_device* pDevice, const ma_device_c pDescriptorPlayback->periodCount = data.periodsOut; #if defined(MA_APPLE_DESKTOP) + ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio); + /* If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly switch the device in the background. @@ -27445,7 +33208,7 @@ static ma_result ma_device_init__coreaudio(ma_device* pDevice, const ma_device_c differently on non-Desktop Apple platforms. */ #if defined(MA_APPLE_MOBILE) - pDevice->coreaudio.pRouteChangeHandler = (__bridge_retained void*)[[ma_router_change_handler alloc] init:pDevice]; + pDevice->coreaudio.pNotificationHandler = (MA_BRIDGE_RETAINED void*)[[ma_ios_notification_handler alloc] init:pDevice]; #endif return MA_SUCCESS; @@ -27510,7 +33273,8 @@ static ma_result ma_context_uninit__coreaudio(ma_context* pContext) #if defined(MA_APPLE_MOBILE) if (!pContext->coreaudio.noAudioSessionDeactivate) { if (![[AVAudioSession sharedInstance] setActive:false error:nil]) { - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to deactivate audio session.", MA_FAILED_TO_INIT_BACKEND); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "Failed to deactivate audio session."); + return MA_FAILED_TO_INIT_BACKEND; } } #endif @@ -27529,7 +33293,7 @@ static ma_result ma_context_uninit__coreaudio(ma_context* pContext) return MA_SUCCESS; } -#if defined(MA_APPLE_MOBILE) +#if defined(MA_APPLE_MOBILE) && defined(__IPHONE_12_0) static AVAudioSessionCategory ma_to_AVAudioSessionCategory(ma_ios_session_category category) { /* The "default" and "none" categories are treated different and should not be used as an input into this function. */ @@ -27586,15 +33350,21 @@ static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_conte } } else { if (pConfig->coreaudio.sessionCategory != ma_ios_session_category_none) { + #if defined(__IPHONE_12_0) if (![pAudioSession setCategory: ma_to_AVAudioSessionCategory(pConfig->coreaudio.sessionCategory) withOptions:options error:nil]) { return MA_INVALID_OPERATION; /* Failed to set session category. */ } + #else + /* Ignore the session category on version 11 and older, but post a warning. */ + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Session category only supported in iOS 12 and newer."); + #endif } } if (!pConfig->coreaudio.noAudioSessionActivate) { if (![pAudioSession setActive:true error:nil]) { - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to activate audio session.", MA_FAILED_TO_INIT_BACKEND); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "Failed to activate audio session."); + return MA_FAILED_TO_INIT_BACKEND; } } } @@ -28255,7 +34025,7 @@ static ma_result ma_device_init_handle__sndio(ma_device* pDevice, const ma_devic MA_ASSERT(pDevice != NULL); if (deviceType == ma_device_type_capture) { - openFlags = MA_SIO_REC; + openFlags = MA_SIO_REC; } else { openFlags = MA_SIO_PLAY; } @@ -28272,13 +34042,15 @@ static ma_result ma_device_init_handle__sndio(ma_device* pDevice, const ma_devic handle = (ma_ptr)((ma_sio_open_proc)pDevice->pContext->sndio.sio_open)(pDeviceName, openFlags, 0); if (handle == NULL) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to open device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to open device."); + return MA_FAILED_TO_OPEN_BACKEND_DEVICE; } /* We need to retrieve the device caps to determine the most appropriate format to use. */ if (((ma_sio_getcap_proc)pDevice->pContext->sndio.sio_getcap)((struct ma_sio_hdl*)handle, &caps) == 0) { ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve device caps.", MA_ERROR); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve device caps."); + return MA_ERROR; } /* @@ -28372,12 +34144,14 @@ static ma_result ma_device_init_handle__sndio(ma_device* pDevice, const ma_devic if (((ma_sio_setpar_proc)pDevice->pContext->sndio.sio_setpar)((struct ma_sio_hdl*)handle, &par) == 0) { ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to set buffer size.", MA_FORMAT_NOT_SUPPORTED); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to set buffer size."); + return MA_ERROR; } if (((ma_sio_getpar_proc)pDevice->pContext->sndio.sio_getpar)((struct ma_sio_hdl*)handle, &par) == 0) { ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve buffer size.", MA_FORMAT_NOT_SUPPORTED); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve buffer size."); + return MA_ERROR; } internalFormat = ma_format_from_sio_enc__sndio(par.bits, par.bps, par.sig, par.le, par.msb); @@ -28395,23 +34169,10 @@ static ma_result ma_device_init_handle__sndio(ma_device* pDevice, const ma_devic pDescriptor->format = internalFormat; pDescriptor->channels = internalChannels; pDescriptor->sampleRate = internalSampleRate; - ma_get_standard_channel_map(ma_standard_channel_map_sndio, pDevice->playback.internalChannels, pDevice->playback.internalChannelMap); + ma_channel_map_init_standard(ma_standard_channel_map_sndio, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), internalChannels); pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames; pDescriptor->periodCount = internalPeriods; - #ifdef MA_DEBUG_OUTPUT - { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "DEVICE INFO\n"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " Format: %s\n", ma_get_format_name(internalFormat)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " Channels: %d\n", internalChannels); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " Sample Rate: %d\n", internalSampleRate); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " Period Size: %d\n", internalPeriodSizeInFrames); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " Periods: %d\n", internalPeriods); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " appbufsz: %d\n", par.appbufsz); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " round: %d\n", par.round); - } - #endif - return MA_SUCCESS; } @@ -28492,7 +34253,8 @@ static ma_result ma_device_write__sndio(ma_device* pDevice, const void* pPCMFram result = ((ma_sio_write_proc)pDevice->pContext->sndio.sio_write)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); if (result == 0) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to send data from the client to the device.", MA_IO_ERROR); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to send data from the client to the device."); + return MA_IO_ERROR; } if (pFramesWritten != NULL) { @@ -28512,7 +34274,8 @@ static ma_result ma_device_read__sndio(ma_device* pDevice, void* pPCMFrames, ma_ result = ((ma_sio_read_proc)pDevice->pContext->sndio.sio_read)((struct ma_sio_hdl*)pDevice->sndio.handleCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); if (result == 0) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to read data from the device to be sent to the device.", MA_IO_ERROR); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to read data from the device to be sent to the device."); + return MA_IO_ERROR; } if (pFramesRead != NULL) { @@ -29055,7 +34818,8 @@ static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_c } if (fd == -1) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to open device.", ma_result_from_errno(errno)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to open device."); + return ma_result_from_errno(errno); } #if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */ @@ -29107,12 +34871,14 @@ static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_c if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) { close(fd); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device format. AUDIO_SETINFO failed.", MA_FORMAT_NOT_SUPPORTED); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device format. AUDIO_SETINFO failed."); + return ma_result_from_errno(errno); } if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) { close(fd); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] AUDIO_GETINFO failed.", MA_FORMAT_NOT_SUPPORTED); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] AUDIO_GETINFO failed."); + return ma_result_from_errno(errno); } if (deviceType == ma_device_type_capture) { @@ -29127,7 +34893,8 @@ static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_c if (internalFormat == ma_format_unknown) { close(fd); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable.", MA_FORMAT_NOT_SUPPORTED); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable."); + return MA_FORMAT_NOT_SUPPORTED; } /* Buffer. */ @@ -29153,7 +34920,8 @@ static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_c fdInfo.blocksize = internalPeriodSizeInBytes; if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) { close(fd); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to set internal buffer size. AUDIO_SETINFO failed.", MA_FORMAT_NOT_SUPPORTED); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set internal buffer size. AUDIO_SETINFO failed."); + return ma_result_from_errno(errno); } internalPeriods = fdInfo.hiwat; @@ -29167,7 +34935,8 @@ static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_c /* We need to retrieve the format of the device so we can know the channel count and sample rate. Then we can calculate the buffer size. */ if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) { close(fd); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve initial device parameters.", MA_FORMAT_NOT_SUPPORTED); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve initial device parameters."); + return ma_result_from_errno(errno); } internalFormat = ma_format_from_swpar__audio4(&fdPar); @@ -29176,7 +34945,8 @@ static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_c if (internalFormat == ma_format_unknown) { close(fd); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable.", MA_FORMAT_NOT_SUPPORTED); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable."); + return MA_FORMAT_NOT_SUPPORTED; } /* Buffer. */ @@ -29196,12 +34966,14 @@ static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_c if (ioctl(fd, AUDIO_SETPAR, &fdPar) < 0) { close(fd); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device parameters.", MA_FORMAT_NOT_SUPPORTED); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device parameters."); + return ma_result_from_errno(errno); } if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) { close(fd); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve actual device parameters.", MA_FORMAT_NOT_SUPPORTED); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve actual device parameters."); + return ma_result_from_errno(errno); } } @@ -29215,7 +34987,8 @@ static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_c if (internalFormat == ma_format_unknown) { close(fd); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable.", MA_FORMAT_NOT_SUPPORTED); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable."); + return MA_FORMAT_NOT_SUPPORTED; } if (deviceType == ma_device_type_capture) { @@ -29227,7 +35000,7 @@ static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_c pDescriptor->format = internalFormat; pDescriptor->channels = internalChannels; pDescriptor->sampleRate = internalSampleRate; - ma_get_standard_channel_map(ma_standard_channel_map_sound4, internalChannels, pDescriptor->channelMap); + ma_channel_map_init_standard(ma_standard_channel_map_sound4, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), internalChannels); pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames; pDescriptor->periodCount = internalPeriods; @@ -29309,11 +35082,13 @@ static ma_result ma_device_stop_fd__audio4(ma_device* pDevice, int fd) #if !defined(MA_AUDIO4_USE_NEW_API) if (ioctl(fd, AUDIO_FLUSH, 0) < 0) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_FLUSH failed.", ma_result_from_errno(errno)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_FLUSH failed."); + return ma_result_from_errno(errno); } #else if (ioctl(fd, AUDIO_STOP, 0) < 0) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_STOP failed.", ma_result_from_errno(errno)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_STOP failed."); + return ma_result_from_errno(errno); } #endif @@ -29361,7 +35136,8 @@ static ma_result ma_device_write__audio4(ma_device* pDevice, const void* pPCMFra result = write(pDevice->audio4.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); if (result < 0) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to write data to the device.", ma_result_from_errno(errno)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to write data to the device."); + return ma_result_from_errno(errno); } if (pFramesWritten != NULL) { @@ -29381,7 +35157,8 @@ static ma_result ma_device_read__audio4(ma_device* pDevice, void* pPCMFrames, ma result = read(pDevice->audio4.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); if (result < 0) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to read data from the device.", ma_result_from_errno(errno)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to read data from the device."); + return ma_result_from_errno(errno); } if (pFramesRead != NULL) { @@ -29496,7 +35273,8 @@ static ma_result ma_context_enumerate_devices__oss(ma_context* pContext, ma_enum fd = ma_open_temp_device__oss(); if (fd == -1) { - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[OSS] Failed to open a temporary device for retrieving system information used for device enumeration.", MA_NO_BACKEND); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open a temporary device for retrieving system information used for device enumeration."); + return MA_NO_BACKEND; } result = ioctl(fd, SNDCTL_SYSINFO, &si); @@ -29543,7 +35321,8 @@ static ma_result ma_context_enumerate_devices__oss(ma_context* pContext, ma_enum } } else { close(fd); - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration.", MA_NO_BACKEND); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration."); + return MA_NO_BACKEND; } close(fd); @@ -29626,7 +35405,8 @@ static ma_result ma_context_get_device_info__oss(ma_context* pContext, ma_device fdTemp = ma_open_temp_device__oss(); if (fdTemp == -1) { - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[OSS] Failed to open a temporary device for retrieving system information used for device enumeration.", MA_NO_BACKEND); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open a temporary device for retrieving system information used for device enumeration."); + return MA_NO_BACKEND; } result = ioctl(fdTemp, SNDCTL_SYSINFO, &si); @@ -29684,7 +35464,8 @@ static ma_result ma_context_get_device_info__oss(ma_context* pContext, ma_device } } else { close(fdTemp); - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration.", MA_NO_BACKEND); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration."); + return MA_NO_BACKEND; } @@ -29774,7 +35555,8 @@ static ma_result ma_device_init_fd__oss(ma_device* pDevice, const ma_device_conf result = ma_context_open_device__oss(pDevice->pContext, deviceType, pDeviceID, shareMode, &fd); if (result != MA_SUCCESS) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device.", result); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device."); + return result; } /* @@ -29788,21 +35570,24 @@ static ma_result ma_device_init_fd__oss(ma_device* pDevice, const ma_device_conf ossResult = ioctl(fd, SNDCTL_DSP_SETFMT, &ossFormat); if (ossResult == -1) { close(fd); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to set format.", MA_FORMAT_NOT_SUPPORTED); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set format."); + return ma_result_from_errno(errno); } /* Channels. */ ossResult = ioctl(fd, SNDCTL_DSP_CHANNELS, &ossChannels); if (ossResult == -1) { close(fd); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to set channel count.", MA_FORMAT_NOT_SUPPORTED); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set channel count."); + return ma_result_from_errno(errno); } /* Sample Rate. */ ossResult = ioctl(fd, SNDCTL_DSP_SPEED, &ossSampleRate); if (ossResult == -1) { close(fd); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to set sample rate.", MA_FORMAT_NOT_SUPPORTED); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set sample rate."); + return ma_result_from_errno(errno); } /* @@ -29836,7 +35621,8 @@ static ma_result ma_device_init_fd__oss(ma_device* pDevice, const ma_device_conf ossResult = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &ossFragment); if (ossResult == -1) { close(fd); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to set fragment size and period count.", MA_FORMAT_NOT_SUPPORTED); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set fragment size and period count."); + return ma_result_from_errno(errno); } } @@ -29850,12 +35636,13 @@ static ma_result ma_device_init_fd__oss(ma_device* pDevice, const ma_device_conf pDescriptor->format = ma_format_from_oss(ossFormat); pDescriptor->channels = ossChannels; pDescriptor->sampleRate = ossSampleRate; - ma_get_standard_channel_map(ma_standard_channel_map_sound4, pDescriptor->channels, pDescriptor->channelMap); + ma_channel_map_init_standard(ma_standard_channel_map_sound4, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels); pDescriptor->periodCount = (ma_uint32)(ossFragment >> 16); pDescriptor->periodSizeInFrames = (ma_uint32)(1 << (ossFragment & 0xFFFF)) / ma_get_bytes_per_frame(pDescriptor->format, pDescriptor->channels); if (pDescriptor->format == ma_format_unknown) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] The device's internal format is not supported by miniaudio.", MA_FORMAT_NOT_SUPPORTED); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] The device's internal format is not supported by miniaudio."); + return MA_FORMAT_NOT_SUPPORTED; } return MA_SUCCESS; @@ -29875,14 +35662,16 @@ static ma_result ma_device_init__oss(ma_device* pDevice, const ma_device_config* if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { ma_result result = ma_device_init_fd__oss(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); if (result != MA_SUCCESS) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device.", result); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device."); + return result; } } if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { ma_result result = ma_device_init_fd__oss(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); if (result != MA_SUCCESS) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device.", result); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device."); + return result; } } @@ -29939,13 +35728,14 @@ static ma_result ma_device_write__oss(ma_device* pDevice, const void* pPCMFrames /* Don't do any processing if the device is stopped. */ deviceState = ma_device_get_state(pDevice); - if (deviceState != MA_STATE_STARTED && deviceState != MA_STATE_STARTING) { + if (deviceState != ma_device_state_started && deviceState != ma_device_state_starting) { return MA_SUCCESS; } resultOSS = write(pDevice->oss.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); if (resultOSS < 0) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to send data from the client to the device.", ma_result_from_errno(errno)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to send data from the client to the device."); + return ma_result_from_errno(errno); } if (pFramesWritten != NULL) { @@ -29966,13 +35756,14 @@ static ma_result ma_device_read__oss(ma_device* pDevice, void* pPCMFrames, ma_ui /* Don't do any processing if the device is stopped. */ deviceState = ma_device_get_state(pDevice); - if (deviceState != MA_STATE_STARTED && deviceState != MA_STATE_STARTING) { + if (deviceState != ma_device_state_started && deviceState != ma_device_state_starting) { return MA_SUCCESS; } resultOSS = read(pDevice->oss.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); if (resultOSS < 0) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to read data from the device to be sent to the client.", ma_result_from_errno(errno)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to read data from the device to be sent to the client."); + return ma_result_from_errno(errno); } if (pFramesRead != NULL) { @@ -30004,7 +35795,8 @@ static ma_result ma_context_init__oss(ma_context* pContext, const ma_context_con /* Try opening a temporary device first so we can get version information. This is closed at the end. */ fd = ma_open_temp_device__oss(); if (fd == -1) { - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[OSS] Failed to open temporary device for retrieving system properties.", MA_NO_BACKEND); /* Looks liks OSS isn't installed, or there are no available devices. */ + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open temporary device for retrieving system properties."); /* Looks liks OSS isn't installed, or there are no available devices. */ + return MA_NO_BACKEND; } /* Grab the OSS version. */ @@ -30012,7 +35804,8 @@ static ma_result ma_context_init__oss(ma_context* pContext, const ma_context_con result = ioctl(fd, OSS_GETVERSION, &ossVersion); if (result == -1) { close(fd); - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve OSS version.", MA_NO_BACKEND); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve OSS version."); + return MA_NO_BACKEND; } /* The file handle to temp device is no longer needed. Close ASAP. */ @@ -30238,14 +36031,29 @@ static void ma_stream_error_callback__aaudio(ma_AAudioStream* pStream, void* pUs (void)error; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[AAudio] ERROR CALLBACK: error=%d, AAudioStream_getState()=%d\n", error, ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream)); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] ERROR CALLBACK: error=%d, AAudioStream_getState()=%d\n", error, ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream)); /* From the documentation for AAudio, when a device is disconnected all we can do is stop it. However, we cannot stop it from the callback - we need to do it from another thread. Therefore we are going to use an event thread for the AAudio backend to do this cleanly and safely. */ if (((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream) == MA_AAUDIO_STREAM_STATE_DISCONNECTED) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[AAudio] Device Disconnected.\n"); + /* We need to post a job to the job thread for processing. This will reroute the device by reinitializing the stream. */ + ma_result result; + ma_job job = ma_job_init(MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE); + job.data.device.aaudio.reroute.pDevice = pDevice; + + if (pStream == pDevice->aaudio.pStreamCapture) { + job.data.device.aaudio.reroute.deviceType = ma_device_type_capture; + } else { + job.data.device.aaudio.reroute.deviceType = ma_device_type_playback; + } + + result = ma_device_job_thread_post(&pDevice->pContext->aaudio.jobThread, &job); + if (result != MA_SUCCESS) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Device Disconnected. Failed to post job for rerouting.\n"); + return; + } } } @@ -30391,8 +36199,9 @@ static ma_result ma_open_stream__aaudio(ma_device* pDevice, const ma_device_conf ma_result result; ma_AAudioStreamBuilder* pBuilder; - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pConfig->deviceType != ma_device_type_duplex); /* This function should not be called for a full-duplex device type. */ + MA_ASSERT(pDevice != NULL); + MA_ASSERT(pDescriptor != NULL); + MA_ASSERT(deviceType != ma_device_type_duplex); /* This function should not be called for a full-duplex device type. */ *ppStream = NULL; @@ -30576,9 +36385,9 @@ static ma_result ma_device_init_by_type__aaudio(ma_device* pDevice, const ma_dev /* For the channel map we need to be sure we don't overflow any buffers. */ if (pDescriptor->channels <= MA_MAX_CHANNELS) { - ma_get_standard_channel_map(ma_standard_channel_map_default, pDescriptor->channels, pDescriptor->channelMap); /* <-- Cannot find info on channel order, so assuming a default. */ + ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels); /* <-- Cannot find info on channel order, so assuming a default. */ } else { - ma_channel_map_init_blank(MA_MAX_CHANNELS, pDescriptor->channelMap); /* Too many channels. Use a blank channel map. */ + ma_channel_map_init_blank(pDescriptor->channelMap, MA_MAX_CHANNELS); /* Too many channels. Use a blank channel map. */ } bufferCapacityInFrames = ((MA_PFN_AAudioStream_getBufferCapacityInFrames)pDevice->pContext->aaudio.AAudioStream_getBufferCapacityInFrames)(pStream); @@ -30607,11 +36416,10 @@ static ma_result ma_device_init__aaudio(ma_device* pDevice, const ma_device_conf return MA_DEVICE_TYPE_NOT_SUPPORTED; } - /* No exclusive mode with AAudio. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { - return MA_SHARE_MODE_NOT_SUPPORTED; - } + pDevice->aaudio.usage = pConfig->aaudio.usage; + pDevice->aaudio.contentType = pConfig->aaudio.contentType; + pDevice->aaudio.inputPreset = pConfig->aaudio.inputPreset; + pDevice->aaudio.noAutoStartAfterReroute = pConfig->aaudio.noAutoStartAfterReroute; if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { result = ma_device_init_by_type__aaudio(pDevice, pConfig, ma_device_type_capture, pDescriptorCapture, (ma_AAudioStream**)&pDevice->aaudio.pStreamCapture); @@ -30676,6 +36484,10 @@ static ma_result ma_device_stop_stream__aaudio(ma_device* pDevice, ma_AAudioStre This maps with miniaudio's requirement that device's be drained which means we don't need to implement any draining logic. */ + currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream); + if (currentState == MA_AAUDIO_STREAM_STATE_DISCONNECTED) { + return MA_SUCCESS; /* The device is disconnected. Don't try stopping it. */ + } resultAA = ((MA_PFN_AAudioStream_requestStop)pDevice->pContext->aaudio.AAudioStream_requestStop)(pStream); if (resultAA != MA_AAUDIO_OK) { @@ -30726,8 +36538,6 @@ static ma_result ma_device_start__aaudio(ma_device* pDevice) static ma_result ma_device_stop__aaudio(ma_device* pDevice) { - ma_stop_proc onStop; - MA_ASSERT(pDevice != NULL); if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { @@ -30744,11 +36554,131 @@ static ma_result ma_device_stop__aaudio(ma_device* pDevice) } } - onStop = pDevice->onStop; - if (onStop) { - onStop(pDevice); + ma_device__on_notification_stopped(pDevice); + + return MA_SUCCESS; +} + +static ma_result ma_device_reinit__aaudio(ma_device* pDevice, ma_device_type deviceType) +{ + ma_result result; + + MA_ASSERT(pDevice != NULL); + + /* The first thing to do is close the streams. */ + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { + ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); + pDevice->aaudio.pStreamCapture = NULL; } + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); + pDevice->aaudio.pStreamPlayback = NULL; + } + + /* Now we need to reinitialize each streams. The hardest part with this is just filling output the config and descriptors. */ + { + ma_device_config deviceConfig; + ma_device_descriptor descriptorPlayback; + ma_device_descriptor descriptorCapture; + + deviceConfig = ma_device_config_init(deviceType); + deviceConfig.playback.pDeviceID = NULL; /* Only doing rerouting with default devices. */ + deviceConfig.playback.shareMode = pDevice->playback.shareMode; + deviceConfig.playback.format = pDevice->playback.format; + deviceConfig.playback.channels = pDevice->playback.channels; + deviceConfig.capture.pDeviceID = NULL; /* Only doing rerouting with default devices. */ + deviceConfig.capture.shareMode = pDevice->capture.shareMode; + deviceConfig.capture.format = pDevice->capture.format; + deviceConfig.capture.channels = pDevice->capture.channels; + deviceConfig.sampleRate = pDevice->sampleRate; + deviceConfig.aaudio.usage = pDevice->aaudio.usage; + deviceConfig.aaudio.contentType = pDevice->aaudio.contentType; + deviceConfig.aaudio.inputPreset = pDevice->aaudio.inputPreset; + deviceConfig.aaudio.noAutoStartAfterReroute = pDevice->aaudio.noAutoStartAfterReroute; + deviceConfig.periods = 1; + + /* Try to get an accurate period size. */ + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + deviceConfig.periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames; + } else { + deviceConfig.periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames; + } + + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { + descriptorCapture.pDeviceID = deviceConfig.capture.pDeviceID; + descriptorCapture.shareMode = deviceConfig.capture.shareMode; + descriptorCapture.format = deviceConfig.capture.format; + descriptorCapture.channels = deviceConfig.capture.channels; + descriptorCapture.sampleRate = deviceConfig.sampleRate; + descriptorCapture.periodSizeInFrames = deviceConfig.periodSizeInFrames; + descriptorCapture.periodCount = deviceConfig.periods; + } + + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + descriptorPlayback.pDeviceID = deviceConfig.playback.pDeviceID; + descriptorPlayback.shareMode = deviceConfig.playback.shareMode; + descriptorPlayback.format = deviceConfig.playback.format; + descriptorPlayback.channels = deviceConfig.playback.channels; + descriptorPlayback.sampleRate = deviceConfig.sampleRate; + descriptorPlayback.periodSizeInFrames = deviceConfig.periodSizeInFrames; + descriptorPlayback.periodCount = deviceConfig.periods; + } + + result = ma_device_init__aaudio(pDevice, &deviceConfig, &descriptorPlayback, &descriptorCapture); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_device_post_init(pDevice, deviceType, &descriptorPlayback, &descriptorCapture); + if (result != MA_SUCCESS) { + ma_device_uninit__aaudio(pDevice); + return result; + } + + /* We'll only ever do this in response to a reroute. */ + ma_device__on_notification_rerouted(pDevice); + + /* If the device is started, start the streams. Maybe make this configurable? */ + if (ma_device_get_state(pDevice) == ma_device_state_started) { + if (pDevice->aaudio.noAutoStartAfterReroute == MA_FALSE) { + ma_device_start__aaudio(pDevice); + } else { + ma_device_stop(pDevice); /* Do a full device stop so we set internal state correctly. */ + } + } + + return MA_SUCCESS; + } +} + +static ma_result ma_device_get_info__aaudio(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo) +{ + ma_AAudioStream* pStream = NULL; + + MA_ASSERT(pDevice != NULL); + MA_ASSERT(type != ma_device_type_duplex); + MA_ASSERT(pDeviceInfo != NULL); + + if (type == ma_device_type_playback) { + pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamCapture; + pDeviceInfo->id.aaudio = pDevice->capture.id.aaudio; + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); /* Only supporting default devices. */ + } + if (type == ma_device_type_capture) { + pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback; + pDeviceInfo->id.aaudio = pDevice->playback.id.aaudio; + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); /* Only supporting default devices. */ + } + + /* Safety. Should never happen. */ + if (pStream == NULL) { + return MA_INVALID_OPERATION; + } + + pDeviceInfo->nativeDataFormatCount = 0; + ma_context_add_native_data_format_from_AAudioStream__aaudio(pDevice->pContext, pStream, 0, pDeviceInfo); + return MA_SUCCESS; } @@ -30758,6 +36688,8 @@ static ma_result ma_context_uninit__aaudio(ma_context* pContext) MA_ASSERT(pContext != NULL); MA_ASSERT(pContext->backend == ma_backend_aaudio); + ma_device_job_thread_uninit(&pContext->aaudio.jobThread, &pContext->allocationCallbacks); + ma_dlclose(pContext, pContext->aaudio.hAAudio); pContext->aaudio.hAAudio = NULL; @@ -30823,10 +36755,47 @@ static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_ pCallbacks->onDeviceRead = NULL; /* Not used because AAudio is asynchronous. */ pCallbacks->onDeviceWrite = NULL; /* Not used because AAudio is asynchronous. */ pCallbacks->onDeviceDataLoop = NULL; /* Not used because AAudio is asynchronous. */ + pCallbacks->onDeviceGetInfo = ma_device_get_info__aaudio; + + + /* We need a job thread so we can deal with rerouting. */ + { + ma_result result; + ma_device_job_thread_config jobThreadConfig; + + jobThreadConfig = ma_device_job_thread_config_init(); + + result = ma_device_job_thread_init(&jobThreadConfig, &pContext->allocationCallbacks, &pContext->aaudio.jobThread); + if (result != MA_SUCCESS) { + ma_dlclose(pContext, pContext->aaudio.hAAudio); + pContext->aaudio.hAAudio = NULL; + return result; + } + } + (void)pConfig; return MA_SUCCESS; } + +static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob) +{ + ma_device* pDevice; + + MA_ASSERT(pJob != NULL); + + pDevice = (ma_device*)pJob->data.device.aaudio.reroute.pDevice; + MA_ASSERT(pDevice != NULL); + + /* Here is where we need to reroute the device. To do this we need to uninitialize the stream and reinitialize it. */ + return ma_device_reinit__aaudio(pDevice, (ma_device_type)pJob->data.device.aaudio.reroute.deviceType); +} +#else +/* Getting here means there is no AAudio backend so we need a no-op job implementation. */ +static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob) +{ + return ma_job_process__noop(pJob); +} #endif /* AAudio */ @@ -31153,6 +37122,7 @@ return_default_device:; if (cbResult) { ma_device_info deviceInfo; MA_ZERO_OBJECT(&deviceInfo); + deviceInfo.id.opensl = SL_DEFAULTDEVICEID_AUDIOOUTPUT; ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); } @@ -31161,6 +37131,7 @@ return_default_device:; if (cbResult) { ma_device_info deviceInfo; MA_ZERO_OBJECT(&deviceInfo); + deviceInfo.id.opensl = SL_DEFAULTDEVICEID_AUDIOINPUT; ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); } @@ -31259,10 +37230,12 @@ return_default_device: } } - /* Name / Description */ + /* ID and Name / Description */ if (deviceType == ma_device_type_playback) { + pDeviceInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOOUTPUT; ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); } else { + pDeviceInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOINPUT; ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); } @@ -31309,7 +37282,7 @@ static void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBuff */ /* Don't do anything if the device is not started. */ - if (ma_device_get_state(pDevice) != MA_STATE_STARTED) { + if (ma_device_get_state(pDevice) != ma_device_state_started) { return; } @@ -31343,7 +37316,7 @@ static void ma_buffer_queue_callback_playback__opensl_android(SLAndroidSimpleBuf (void)pBufferQueue; /* Don't do anything if the device is not started. */ - if (ma_device_get_state(pDevice) != MA_STATE_STARTED) { + if (ma_device_get_state(pDevice) != ma_device_state_started) { return; } @@ -31380,7 +37353,7 @@ static ma_result ma_device_uninit__opensl(ma_device* pDevice) MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioRecorderObj); } - ma__free_from_callbacks(pDevice->opensl.pBufferCapture, &pDevice->pContext->allocationCallbacks); + ma_free(pDevice->opensl.pBufferCapture, &pDevice->pContext->allocationCallbacks); } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { @@ -31391,7 +37364,7 @@ static ma_result ma_device_uninit__opensl(ma_device* pDevice) MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Destroy((SLObjectItf)pDevice->opensl.pOutputMixObj); } - ma__free_from_callbacks(pDevice->opensl.pBufferPlayback, &pDevice->pContext->allocationCallbacks); + ma_free(pDevice->opensl.pBufferPlayback, &pDevice->pContext->allocationCallbacks); } return MA_SUCCESS; @@ -31428,8 +37401,8 @@ static ma_result ma_SLDataFormat_PCM_init__opensl(ma_format format, ma_uint32 ch #endif pDataFormat->numChannels = channels; - ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec = ma_round_to_standard_sample_rate__opensl(sampleRate) * 1000; /* In millihertz. Annoyingly, the sample rate variable is named differently between SLAndroidDataFormat_PCM_EX and SLDataFormat_PCM */ - pDataFormat->bitsPerSample = ma_get_bytes_per_sample(format)*8; + ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec = ma_round_to_standard_sample_rate__opensl(sampleRate * 1000); /* In millihertz. Annoyingly, the sample rate variable is named differently between SLAndroidDataFormat_PCM_EX and SLDataFormat_PCM */ + pDataFormat->bitsPerSample = ma_get_bytes_per_sample(format) * 8; pDataFormat->channelMask = ma_channel_map_to_channel_mask__opensl(channelMap, channels); pDataFormat->endianness = (ma_is_little_endian()) ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN; @@ -31556,7 +37529,7 @@ static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_conf locatorDevice.locatorType = SL_DATALOCATOR_IODEVICE; locatorDevice.deviceType = SL_IODEVICE_AUDIOINPUT; - locatorDevice.deviceID = (pDescriptorCapture->pDeviceID == NULL) ? SL_DEFAULTDEVICEID_AUDIOINPUT : pDescriptorCapture->pDeviceID->opensl; + locatorDevice.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT; /* Must always use the default device with Android. */ locatorDevice.device = NULL; source.pLocator = &locatorDevice; @@ -31568,20 +37541,21 @@ static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_conf sink.pFormat = (SLDataFormat_PCM*)&pcm; resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); - if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED) { + if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED || resultSL == SL_RESULT_PARAMETER_INVALID) { /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */ pcm.formatType = SL_DATAFORMAT_PCM; pcm.numChannels = 1; ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16; /* The name of the sample rate variable is different between SLAndroidDataFormat_PCM_EX and SLDataFormat_PCM. */ pcm.bitsPerSample = 16; pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */ - pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; + pcm.channelMask = 0; resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); } if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio recorder.", ma_result_from_OpenSL(resultSL)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio recorder."); + return ma_result_from_OpenSL(resultSL); } @@ -31600,25 +37574,29 @@ static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_conf resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Realize((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_BOOLEAN_FALSE); if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio recorder.", ma_result_from_OpenSL(resultSL)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio recorder."); + return ma_result_from_OpenSL(resultSL); } resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_RECORD, &pDevice->opensl.pAudioRecorder); if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_RECORD interface.", ma_result_from_OpenSL(resultSL)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_RECORD interface."); + return ma_result_from_OpenSL(resultSL); } resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueueCapture); if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.", ma_result_from_OpenSL(resultSL)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface."); + return ma_result_from_OpenSL(resultSL); } resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, ma_buffer_queue_callback_capture__opensl_android, pDevice); if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback.", ma_result_from_OpenSL(resultSL)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback."); + return ma_result_from_OpenSL(resultSL); } /* The internal format is determined by the "pcm" object. */ @@ -31629,10 +37607,11 @@ static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_conf pDevice->opensl.currentBufferIndexCapture = 0; bufferSizeInBytes = pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) * pDescriptorCapture->periodCount; - pDevice->opensl.pBufferCapture = (ma_uint8*)ma__calloc_from_callbacks(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks); + pDevice->opensl.pBufferCapture = (ma_uint8*)ma_calloc(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks); if (pDevice->opensl.pBufferCapture == NULL) { ma_device_uninit__opensl(pDevice); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer.", MA_OUT_OF_MEMORY); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer."); + return MA_OUT_OF_MEMORY; } MA_ZERO_MEMORY(pDevice->opensl.pBufferCapture, bufferSizeInBytes); } @@ -31649,19 +37628,22 @@ static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_conf resultSL = (*g_maEngineSL)->CreateOutputMix(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pOutputMixObj, 0, NULL, NULL); if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create output mix.", ma_result_from_OpenSL(resultSL)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create output mix."); + return ma_result_from_OpenSL(resultSL); } resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Realize((SLObjectItf)pDevice->opensl.pOutputMixObj, SL_BOOLEAN_FALSE); if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize output mix object.", ma_result_from_OpenSL(resultSL)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize output mix object."); + return ma_result_from_OpenSL(resultSL); } resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->GetInterface((SLObjectItf)pDevice->opensl.pOutputMixObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_OUTPUTMIX, &pDevice->opensl.pOutputMix); if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_OUTPUTMIX interface.", ma_result_from_OpenSL(resultSL)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_OUTPUTMIX interface."); + return ma_result_from_OpenSL(resultSL); } /* Set the output device. */ @@ -31682,7 +37664,7 @@ static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_conf sink.pFormat = NULL; resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); - if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED) { + if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED || resultSL == SL_RESULT_PARAMETER_INVALID) { /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */ pcm.formatType = SL_DATAFORMAT_PCM; pcm.numChannels = 2; @@ -31695,7 +37677,8 @@ static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_conf if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio player.", ma_result_from_OpenSL(resultSL)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio player."); + return ma_result_from_OpenSL(resultSL); } @@ -31714,25 +37697,29 @@ static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_conf resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Realize((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_BOOLEAN_FALSE); if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio player.", ma_result_from_OpenSL(resultSL)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio player."); + return ma_result_from_OpenSL(resultSL); } resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_PLAY, &pDevice->opensl.pAudioPlayer); if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_PLAY interface.", ma_result_from_OpenSL(resultSL)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_PLAY interface."); + return ma_result_from_OpenSL(resultSL); } resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueuePlayback); if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.", ma_result_from_OpenSL(resultSL)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface."); + return ma_result_from_OpenSL(resultSL); } resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, ma_buffer_queue_callback_playback__opensl_android, pDevice); if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback.", ma_result_from_OpenSL(resultSL)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback."); + return ma_result_from_OpenSL(resultSL); } /* The internal format is determined by the "pcm" object. */ @@ -31743,10 +37730,11 @@ static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_conf pDevice->opensl.currentBufferIndexPlayback = 0; bufferSizeInBytes = pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels) * pDescriptorPlayback->periodCount; - pDevice->opensl.pBufferPlayback = (ma_uint8*)ma__calloc_from_callbacks(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks); + pDevice->opensl.pBufferPlayback = (ma_uint8*)ma_calloc(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks); if (pDevice->opensl.pBufferPlayback == NULL) { ma_device_uninit__opensl(pDevice); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer.", MA_OUT_OF_MEMORY); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer."); + return MA_OUT_OF_MEMORY; } MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, bufferSizeInBytes); } @@ -31773,7 +37761,8 @@ static ma_result ma_device_start__opensl(ma_device* pDevice) if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_RECORDING); if (resultSL != SL_RESULT_SUCCESS) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal capture device.", ma_result_from_OpenSL(resultSL)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal capture device."); + return ma_result_from_OpenSL(resultSL); } periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); @@ -31781,7 +37770,8 @@ static ma_result ma_device_start__opensl(ma_device* pDevice) resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pDevice->opensl.pBufferCapture + (periodSizeInBytes * iPeriod), periodSizeInBytes); if (resultSL != SL_RESULT_SUCCESS) { MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for capture device.", ma_result_from_OpenSL(resultSL)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for capture device."); + return ma_result_from_OpenSL(resultSL); } } } @@ -31789,7 +37779,8 @@ static ma_result ma_device_start__opensl(ma_device* pDevice) if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_PLAYING); if (resultSL != SL_RESULT_SUCCESS) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal playback device.", ma_result_from_OpenSL(resultSL)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal playback device."); + return ma_result_from_OpenSL(resultSL); } /* In playback mode (no duplex) we need to load some initial buffers. In duplex mode we need to enqueu silent buffers. */ @@ -31804,7 +37795,8 @@ static ma_result ma_device_start__opensl(ma_device* pDevice) resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pDevice->opensl.pBufferPlayback + (periodSizeInBytes * iPeriod), periodSizeInBytes); if (resultSL != SL_RESULT_SUCCESS) { MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for playback device.", ma_result_from_OpenSL(resultSL)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for playback device."); + return ma_result_from_OpenSL(resultSL); } } } @@ -31849,7 +37841,6 @@ static ma_result ma_device_drain__opensl(ma_device* pDevice, ma_device_type devi static ma_result ma_device_stop__opensl(ma_device* pDevice) { SLresult resultSL; - ma_stop_proc onStop; MA_ASSERT(pDevice != NULL); @@ -31863,7 +37854,8 @@ static ma_result ma_device_stop__opensl(ma_device* pDevice) resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED); if (resultSL != SL_RESULT_SUCCESS) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal capture device.", ma_result_from_OpenSL(resultSL)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal capture device."); + return ma_result_from_OpenSL(resultSL); } MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture); @@ -31874,17 +37866,15 @@ static ma_result ma_device_stop__opensl(ma_device* pDevice) resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED); if (resultSL != SL_RESULT_SUCCESS) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal playback device.", ma_result_from_OpenSL(resultSL)); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal playback device."); + return ma_result_from_OpenSL(resultSL); } MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback); } /* Make sure the client is aware that the device has stopped. There may be an OpenSL|ES callback for this, but I haven't found it. */ - onStop = pDevice->onStop; - if (onStop) { - onStop(pDevice); - } + ma_device__on_notification_stopped(pDevice); return MA_SUCCESS; } @@ -31916,7 +37906,7 @@ static ma_result ma_dlsym_SLInterfaceID__opensl(ma_context* pContext, const char /* We need to return an error if the symbol cannot be found. This is important because there have been reports that some symbols do not exist. */ ma_handle* p = (ma_handle*)ma_dlsym(pContext, pContext->opensl.libOpenSLES, pName); if (p == NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL|ES] Cannot find symbol %s", pName); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol %s", pName); return MA_NO_BACKEND; } @@ -31979,7 +37969,7 @@ static ma_result ma_context_init__opensl(ma_context* pContext, const ma_context_ } if (pContext->opensl.libOpenSLES == NULL) { - ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_INFO, "[OpenSL|ES] Could not find libOpenSLES.so"); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Could not find libOpenSLES.so"); return MA_NO_BACKEND; } @@ -32028,7 +38018,7 @@ static ma_result ma_context_init__opensl(ma_context* pContext, const ma_context_ pContext->opensl.slCreateEngine = (ma_proc)ma_dlsym(pContext, pContext->opensl.libOpenSLES, "slCreateEngine"); if (pContext->opensl.slCreateEngine == NULL) { ma_dlclose(pContext, pContext->opensl.libOpenSLES); - ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_INFO, "[OpenSL|ES] Cannot find symbol slCreateEngine."); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol slCreateEngine."); return MA_NO_BACKEND; } #else @@ -32052,7 +38042,7 @@ static ma_result ma_context_init__opensl(ma_context* pContext, const ma_context_ if (result != MA_SUCCESS) { ma_dlclose(pContext, pContext->opensl.libOpenSLES); - ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_INFO, "[OpenSL|ES] Failed to initialize OpenSL engine."); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Failed to initialize OpenSL engine."); return result; } @@ -32290,6 +38280,7 @@ static ma_result ma_device_init_by_type__webaudio(ma_device* pDevice, const ma_d sampleRate = (pDescriptor->sampleRate > 0) ? pDescriptor->sampleRate : MA_DEFAULT_SAMPLE_RATE; periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptor, sampleRate, pConfig->performanceProfile); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "periodSizeInFrames = %d\n", (int)periodSizeInFrames); /* We create the device on the JavaScript side and reference it using an index. We use this to make it possible to reference the device between JavaScript and C. */ deviceIndex = EM_ASM_INT({ @@ -32299,7 +38290,7 @@ static ma_result ma_device_init_by_type__webaudio(ma_device* pDevice, const ma_d var isCapture = $3; var pDevice = $4; - if (typeof(miniaudio) === 'undefined') { + if (typeof(window.miniaudio) === 'undefined') { return -1; /* Context not initialized. */ } @@ -32308,7 +38299,7 @@ static ma_result ma_device_init_by_type__webaudio(ma_device* pDevice, const ma_d /* The AudioContext must be created in a suspended state. */ device.webaudio = new (window.AudioContext || window.webkitAudioContext)({sampleRate:sampleRate}); device.webaudio.suspend(); - device.state = 1; /* MA_STATE_STOPPED */ + device.state = 1; /* ma_device_state_stopped */ /* We need an intermediary buffer which we use for JavaScript and C interop. This buffer stores interleaved f32 PCM data. Because it's passed between @@ -32334,7 +38325,7 @@ static ma_result ma_device_init_by_type__webaudio(ma_device* pDevice, const ma_d how well this would work. Although ScriptProccessorNode is deprecated, in practice it seems to have pretty good browser support so I'm leaving it like this for now. If anyone knows how I could get raw PCM data using the MediaRecorder API please let me know! */ - device.scriptNode = device.webaudio.createScriptProcessor(bufferSize, channels, channels); + device.scriptNode = device.webaudio.createScriptProcessor(bufferSize, (isCapture) ? channels : 0, (isCapture) ? 0 : channels); if (isCapture) { device.scriptNode.onaudioprocess = function(e) { @@ -32342,7 +38333,7 @@ static ma_result ma_device_init_by_type__webaudio(ma_device* pDevice, const ma_d return; /* This means the device has been uninitialized. */ } - if(device.intermediaryBufferView.length == 0) { + if (device.intermediaryBufferView.length == 0) { /* Recreate intermediaryBufferView when losing reference to the underlying buffer, probably due to emscripten resizing heap. */ device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, device.intermediaryBuffer, device.intermediaryBufferSizeInBytes); } @@ -32440,8 +38431,10 @@ static ma_result ma_device_init_by_type__webaudio(ma_device* pDevice, const ma_d } } else { for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { + var outputBuffer = e.outputBuffer.getChannelData(iChannel); + var intermediaryBuffer = device.intermediaryBufferView; for (var iFrame = 0; iFrame < framesToProcess; ++iFrame) { - e.outputBuffer.getChannelData(iChannel)[totalFramesProcessed + iFrame] = device.intermediaryBufferView[iFrame*channels + iChannel]; + outputBuffer[totalFramesProcessed + iFrame] = intermediaryBuffer[iFrame*channels + iChannel]; } } } @@ -32466,12 +38459,12 @@ static ma_result ma_device_init_by_type__webaudio(ma_device* pDevice, const ma_d pDevice->webaudio.indexPlayback = deviceIndex; } - pDescriptor->format = ma_format_f32; - pDescriptor->channels = channels; - ma_get_standard_channel_map(ma_standard_channel_map_webaudio, pDescriptor->channels, pDescriptor->channelMap); - pDescriptor->sampleRate = EM_ASM_INT({ return miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex); - pDescriptor->periodSizeInFrames = periodSizeInFrames; - pDescriptor->periodCount = 1; + pDescriptor->format = ma_format_f32; + pDescriptor->channels = channels; + ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels); + pDescriptor->sampleRate = EM_ASM_INT({ return miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex); + pDescriptor->periodSizeInFrames = periodSizeInFrames; + pDescriptor->periodCount = 1; return MA_SUCCESS; } @@ -32518,7 +38511,7 @@ static ma_result ma_device_start__webaudio(ma_device* pDevice) EM_ASM({ var device = miniaudio.get_device_by_index($0); device.webaudio.resume(); - device.state = 2; /* MA_STATE_STARTED */ + device.state = 2; /* ma_device_state_started */ }, pDevice->webaudio.indexCapture); } @@ -32526,7 +38519,7 @@ static ma_result ma_device_start__webaudio(ma_device* pDevice) EM_ASM({ var device = miniaudio.get_device_by_index($0); device.webaudio.resume(); - device.state = 2; /* MA_STATE_STARTED */ + device.state = 2; /* ma_device_state_started */ }, pDevice->webaudio.indexPlayback); } @@ -32551,7 +38544,7 @@ static ma_result ma_device_stop__webaudio(ma_device* pDevice) EM_ASM({ var device = miniaudio.get_device_by_index($0); device.webaudio.suspend(); - device.state = 1; /* MA_STATE_STOPPED */ + device.state = 1; /* ma_device_state_stopped */ }, pDevice->webaudio.indexCapture); } @@ -32559,14 +38552,11 @@ static ma_result ma_device_stop__webaudio(ma_device* pDevice) EM_ASM({ var device = miniaudio.get_device_by_index($0); device.webaudio.suspend(); - device.state = 1; /* MA_STATE_STOPPED */ + device.state = 1; /* ma_device_state_stopped */ }, pDevice->webaudio.indexPlayback); } - ma_stop_proc onStop = pDevice->onStop; - if (onStop) { - onStop(pDevice); - } + ma_device__on_notification_stopped(pDevice); return MA_SUCCESS; } @@ -32596,8 +38586,8 @@ static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_contex return 0; /* Web Audio not supported. */ } - if (typeof(miniaudio) === 'undefined') { - miniaudio = {}; + if (typeof(window.miniaudio) === 'undefined') { + window.miniaudio = {}; miniaudio.devices = []; /* Device cache for mapping devices to indexes for JavaScript/C interop. */ miniaudio.track_device = function(device) { @@ -32647,7 +38637,7 @@ static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_contex miniaudio.unlock = function() { for(var i = 0; i < miniaudio.devices.length; ++i) { var device = miniaudio.devices[i]; - if (device != null && device.webaudio != null && device.state === 2 /* MA_STATE_STARTED */) { + if (device != null && device.webaudio != null && device.state === 2 /* ma_device_state_started */) { device.webaudio.resume(); } } @@ -32686,10 +38676,10 @@ static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_contex -static ma_bool32 ma__is_channel_map_valid(const ma_channel* channelMap, ma_uint32 channels) +static ma_bool32 ma__is_channel_map_valid(const ma_channel* pChannelMap, ma_uint32 channels) { /* A blank channel map should be allowed, in which case it should use an appropriate default which will depend on context. */ - if (channelMap[0] != MA_CHANNEL_NONE) { + if (pChannelMap != NULL && pChannelMap[0] != MA_CHANNEL_NONE) { ma_uint32 iChannel; if (channels == 0 || channels > MA_MAX_CHANNELS) { @@ -32700,7 +38690,7 @@ static ma_bool32 ma__is_channel_map_valid(const ma_channel* channelMap, ma_uint3 for (iChannel = 0; iChannel < channels; ++iChannel) { ma_uint32 jChannel; for (jChannel = iChannel + 1; jChannel < channels; ++jChannel) { - if (channelMap[iChannel] == channelMap[jChannel]) { + if (pChannelMap[iChannel] == pChannelMap[jChannel]) { return MA_FALSE; } } @@ -32730,9 +38720,9 @@ static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type d ma_channel_map_copy(pDevice->capture.channelMap, pDevice->capture.internalChannelMap, pDevice->capture.channels); } else { if (pDevice->capture.channelMixMode == ma_channel_mix_mode_simple) { - ma_channel_map_init_blank(pDevice->capture.channels, pDevice->capture.channelMap); + ma_channel_map_init_blank(pDevice->capture.channelMap, pDevice->capture.channels); } else { - ma_get_standard_channel_map(ma_standard_channel_map_default, pDevice->capture.channels, pDevice->capture.channelMap); + ma_channel_map_init_standard(ma_standard_channel_map_default, pDevice->capture.channelMap, ma_countof(pDevice->capture.channelMap), pDevice->capture.channels); } } } @@ -32751,9 +38741,9 @@ static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type d ma_channel_map_copy(pDevice->playback.channelMap, pDevice->playback.internalChannelMap, pDevice->playback.channels); } else { if (pDevice->playback.channelMixMode == ma_channel_mix_mode_simple) { - ma_channel_map_init_blank(pDevice->playback.channels, pDevice->playback.channelMap); + ma_channel_map_init_blank(pDevice->playback.channelMap, pDevice->playback.channels); } else { - ma_get_standard_channel_map(ma_standard_channel_map_default, pDevice->playback.channels, pDevice->playback.channelMap); + ma_channel_map_init_standard(ma_standard_channel_map_default, pDevice->playback.channelMap, ma_countof(pDevice->playback.channelMap), pDevice->playback.channels); } } } @@ -32771,21 +38761,27 @@ static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type d if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { /* Converting from internal device format to client format. */ ma_data_converter_config converterConfig = ma_data_converter_config_init_default(); - converterConfig.formatIn = pDevice->capture.internalFormat; - converterConfig.channelsIn = pDevice->capture.internalChannels; - converterConfig.sampleRateIn = pDevice->capture.internalSampleRate; - ma_channel_map_copy(converterConfig.channelMapIn, pDevice->capture.internalChannelMap, ma_min(pDevice->capture.internalChannels, MA_MAX_CHANNELS)); - converterConfig.formatOut = pDevice->capture.format; - converterConfig.channelsOut = pDevice->capture.channels; - converterConfig.sampleRateOut = pDevice->sampleRate; - ma_channel_map_copy(converterConfig.channelMapOut, pDevice->capture.channelMap, ma_min(pDevice->capture.channels, MA_MAX_CHANNELS)); - converterConfig.channelMixMode = pDevice->capture.channelMixMode; - converterConfig.resampling.allowDynamicSampleRate = MA_FALSE; - converterConfig.resampling.algorithm = pDevice->resampling.algorithm; - converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder; - converterConfig.resampling.speex.quality = pDevice->resampling.speex.quality; + converterConfig.formatIn = pDevice->capture.internalFormat; + converterConfig.channelsIn = pDevice->capture.internalChannels; + converterConfig.sampleRateIn = pDevice->capture.internalSampleRate; + converterConfig.pChannelMapIn = pDevice->capture.internalChannelMap; + converterConfig.formatOut = pDevice->capture.format; + converterConfig.channelsOut = pDevice->capture.channels; + converterConfig.sampleRateOut = pDevice->sampleRate; + converterConfig.pChannelMapOut = pDevice->capture.channelMap; + converterConfig.channelMixMode = pDevice->capture.channelMixMode; + converterConfig.allowDynamicSampleRate = MA_FALSE; + converterConfig.resampling.algorithm = pDevice->resampling.algorithm; + converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder; + converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable; + converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData; - result = ma_data_converter_init(&converterConfig, &pDevice->capture.converter); + /* Make sure the old converter is uninitialized first. */ + if (ma_device_get_state(pDevice) != ma_device_state_uninitialized) { + ma_data_converter_uninit(&pDevice->capture.converter, &pDevice->pContext->allocationCallbacks); + } + + result = ma_data_converter_init(&converterConfig, &pDevice->pContext->allocationCallbacks, &pDevice->capture.converter); if (result != MA_SUCCESS) { return result; } @@ -32794,29 +38790,164 @@ static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type d if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { /* Converting from client format to device format. */ ma_data_converter_config converterConfig = ma_data_converter_config_init_default(); - converterConfig.formatIn = pDevice->playback.format; - converterConfig.channelsIn = pDevice->playback.channels; - converterConfig.sampleRateIn = pDevice->sampleRate; - ma_channel_map_copy(converterConfig.channelMapIn, pDevice->playback.channelMap, ma_min(pDevice->playback.channels, MA_MAX_CHANNELS)); - converterConfig.formatOut = pDevice->playback.internalFormat; - converterConfig.channelsOut = pDevice->playback.internalChannels; - converterConfig.sampleRateOut = pDevice->playback.internalSampleRate; - ma_channel_map_copy(converterConfig.channelMapOut, pDevice->playback.internalChannelMap, ma_min(pDevice->playback.internalChannels, MA_MAX_CHANNELS)); - converterConfig.channelMixMode = pDevice->playback.channelMixMode; - converterConfig.resampling.allowDynamicSampleRate = MA_FALSE; - converterConfig.resampling.algorithm = pDevice->resampling.algorithm; - converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder; - converterConfig.resampling.speex.quality = pDevice->resampling.speex.quality; + converterConfig.formatIn = pDevice->playback.format; + converterConfig.channelsIn = pDevice->playback.channels; + converterConfig.sampleRateIn = pDevice->sampleRate; + converterConfig.pChannelMapIn = pDevice->playback.channelMap; + converterConfig.formatOut = pDevice->playback.internalFormat; + converterConfig.channelsOut = pDevice->playback.internalChannels; + converterConfig.sampleRateOut = pDevice->playback.internalSampleRate; + converterConfig.pChannelMapOut = pDevice->playback.internalChannelMap; + converterConfig.channelMixMode = pDevice->playback.channelMixMode; + converterConfig.allowDynamicSampleRate = MA_FALSE; + converterConfig.resampling.algorithm = pDevice->resampling.algorithm; + converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder; + converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable; + converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData; - result = ma_data_converter_init(&converterConfig, &pDevice->playback.converter); + /* Make sure the old converter is uninitialized first. */ + if (ma_device_get_state(pDevice) != ma_device_state_uninitialized) { + ma_data_converter_uninit(&pDevice->playback.converter, &pDevice->pContext->allocationCallbacks); + } + + result = ma_data_converter_init(&converterConfig, &pDevice->pContext->allocationCallbacks, &pDevice->playback.converter); if (result != MA_SUCCESS) { return result; } } + + /* + In playback mode, if the data converter does not support retrieval of the required number of + input frames given a number of output frames, we need to fall back to a heap-allocated cache. + */ + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + ma_uint64 unused; + + pDevice->playback.inputCacheConsumed = 0; + pDevice->playback.inputCacheRemaining = 0; + + if (deviceType == ma_device_type_duplex || ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, 1, &unused) != MA_SUCCESS) { + /* We need a heap allocated cache. We want to size this based on the period size. */ + void* pNewInputCache; + ma_uint64 newInputCacheCap; + ma_uint64 newInputCacheSizeInBytes; + + newInputCacheCap = ma_calculate_frame_count_after_resampling(pDevice->playback.internalSampleRate, pDevice->sampleRate, pDevice->playback.internalPeriodSizeInFrames); + + newInputCacheSizeInBytes = newInputCacheCap * ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); + if (newInputCacheSizeInBytes > MA_SIZE_MAX) { + ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); + pDevice->playback.pInputCache = NULL; + pDevice->playback.inputCacheCap = 0; + return MA_OUT_OF_MEMORY; /* Allocation too big. Should never hit this, but makes the cast below safer for 32-bit builds. */ + } + + pNewInputCache = ma_realloc(pDevice->playback.pInputCache, (size_t)newInputCacheSizeInBytes, &pDevice->pContext->allocationCallbacks); + if (pNewInputCache == NULL) { + ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); + pDevice->playback.pInputCache = NULL; + pDevice->playback.inputCacheCap = 0; + return MA_OUT_OF_MEMORY; + } + + pDevice->playback.pInputCache = pNewInputCache; + pDevice->playback.inputCacheCap = newInputCacheCap; + } else { + /* Heap allocation not required. Make sure we clear out the old cache just in case this function was called in response to a route change. */ + ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); + pDevice->playback.pInputCache = NULL; + pDevice->playback.inputCacheCap = 0; + } + } + return MA_SUCCESS; } +MA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceType, const ma_device_descriptor* pDescriptorPlayback, const ma_device_descriptor* pDescriptorCapture) +{ + ma_result result; + + if (pDevice == NULL) { + return MA_INVALID_ARGS; + } + + /* Capture. */ + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { + if (ma_device_descriptor_is_valid(pDescriptorCapture) == MA_FALSE) { + return MA_INVALID_ARGS; + } + + pDevice->capture.internalFormat = pDescriptorCapture->format; + pDevice->capture.internalChannels = pDescriptorCapture->channels; + pDevice->capture.internalSampleRate = pDescriptorCapture->sampleRate; + MA_COPY_MEMORY(pDevice->capture.internalChannelMap, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap)); + pDevice->capture.internalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames; + pDevice->capture.internalPeriods = pDescriptorCapture->periodCount; + + if (pDevice->capture.internalPeriodSizeInFrames == 0) { + pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptorCapture->periodSizeInMilliseconds, pDescriptorCapture->sampleRate); + } + } + + /* Playback. */ + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + if (ma_device_descriptor_is_valid(pDescriptorPlayback) == MA_FALSE) { + return MA_INVALID_ARGS; + } + + pDevice->playback.internalFormat = pDescriptorPlayback->format; + pDevice->playback.internalChannels = pDescriptorPlayback->channels; + pDevice->playback.internalSampleRate = pDescriptorPlayback->sampleRate; + MA_COPY_MEMORY(pDevice->playback.internalChannelMap, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap)); + pDevice->playback.internalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames; + pDevice->playback.internalPeriods = pDescriptorPlayback->periodCount; + + if (pDevice->playback.internalPeriodSizeInFrames == 0) { + pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptorPlayback->periodSizeInMilliseconds, pDescriptorPlayback->sampleRate); + } + } + + /* + The name of the device can be retrieved from device info. This may be temporary and replaced with a `ma_device_get_info(pDevice, deviceType)` instead. + For loopback devices, we need to retrieve the name of the playback device. + */ + { + ma_device_info deviceInfo; + + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { + result = ma_device_get_info(pDevice, (deviceType == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, &deviceInfo); + if (result == MA_SUCCESS) { + ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1); + } else { + /* We failed to retrieve the device info. Fall back to a default name. */ + if (pDescriptorCapture->pDeviceID == NULL) { + ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + } else { + ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), "Capture Device", (size_t)-1); + } + } + } + + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + result = ma_device_get_info(pDevice, ma_device_type_playback, &deviceInfo); + if (result == MA_SUCCESS) { + ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), deviceInfo.name, (size_t)-1); + } else { + /* We failed to retrieve the device info. Fall back to a default name. */ + if (pDescriptorPlayback->pDeviceID == NULL) { + ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + } else { + ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "Playback Device", (size_t)-1); + } + } + } + } + + /* Update data conversion. */ + return ma_device__post_init_setup(pDevice, deviceType); /* TODO: Should probably rename ma_device__post_init_setup() to something better. */ +} + static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData) { @@ -32828,17 +38959,17 @@ static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData) #endif /* - When the device is being initialized it's initial state is set to MA_STATE_UNINITIALIZED. Before returning from + When the device is being initialized it's initial state is set to ma_device_state_uninitialized. Before returning from ma_device_init(), the state needs to be set to something valid. In miniaudio the device's default state immediately after initialization is stopped, so therefore we need to mark the device as such. miniaudio will wait on the worker thread to signal an event to know when the worker thread is ready for action. */ - ma_device__set_state(pDevice, MA_STATE_STOPPED); + ma_device__set_state(pDevice, ma_device_state_stopped); ma_event_signal(&pDevice->stopEvent); for (;;) { /* <-- This loop just keeps the thread alive. The main audio loop is inside. */ ma_result startResult; - ma_result stopResult; /* <-- This will store the result from onDeviceStop(). If it returns an error, we don't fire the onStop callback. */ + ma_result stopResult; /* <-- This will store the result from onDeviceStop(). If it returns an error, we don't fire the stopped notification callback. */ /* We wait on an event to know when something has requested that the device be started and the main loop entered. */ ma_event_wait(&pDevice->wakeupEvent); @@ -32847,7 +38978,7 @@ static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData) pDevice->workResult = MA_SUCCESS; /* If the reason for the wake up is that we are terminating, just break from the loop. */ - if (ma_device_get_state(pDevice) == MA_STATE_UNINITIALIZED) { + if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { break; } @@ -32856,7 +38987,7 @@ static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData) be started will be waiting on an event (pDevice->startEvent) which means we need to make sure we signal the event in both the success and error case. It's important that the state of the device is set _before_ signaling the event. */ - MA_ASSERT(ma_device_get_state(pDevice) == MA_STATE_STARTING); + MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_starting); /* If the device has a start callback, start it now. */ if (pDevice->pContext->callbacks.onDeviceStart != NULL) { @@ -32865,15 +38996,22 @@ static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData) startResult = MA_SUCCESS; } + /* + If starting was not successful we'll need to loop back to the start and wait for something + to happen (pDevice->wakeupEvent). + */ if (startResult != MA_SUCCESS) { pDevice->workResult = startResult; - continue; /* Failed to start. Loop back to the start and wait for something to happen (pDevice->wakeupEvent). */ + ma_event_signal(&pDevice->startEvent); /* <-- Always signal the start event so ma_device_start() can return as it'll be waiting on it. */ + continue; } /* Make sure the state is set appropriately. */ - ma_device__set_state(pDevice, MA_STATE_STARTED); + ma_device__set_state(pDevice, ma_device_state_started); /* <-- Set this before signaling the event so that the state is always guaranteed to be good after ma_device_start() has returned. */ ma_event_signal(&pDevice->startEvent); + ma_device__on_notification_started(pDevice); + if (pDevice->pContext->callbacks.onDeviceDataLoop != NULL) { pDevice->pContext->callbacks.onDeviceDataLoop(pDevice); } else { @@ -32889,16 +39027,16 @@ static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData) } /* - After the device has stopped, make sure an event is posted. Don't post an onStop event if + After the device has stopped, make sure an event is posted. Don't post a stopped event if stopping failed. This can happen on some backends when the underlying stream has been stopped due to the device being physically unplugged or disabled via an OS setting. */ - if (pDevice->onStop && stopResult != MA_SUCCESS) { - pDevice->onStop(pDevice); + if (stopResult == MA_SUCCESS) { + ma_device__on_notification_stopped(pDevice); } /* A function somewhere is waiting for the device to have stopped for real so we need to signal an event to allow it to continue. */ - ma_device__set_state(pDevice, MA_STATE_STOPPED); + ma_device__set_state(pDevice, ma_device_state_stopped); ma_event_signal(&pDevice->stopEvent); } @@ -32917,17 +39055,22 @@ static ma_bool32 ma_device__is_initialized(ma_device* pDevice) return MA_FALSE; } - return ma_device_get_state(pDevice) != MA_STATE_UNINITIALIZED; + return ma_device_get_state(pDevice) != ma_device_state_uninitialized; } #ifdef MA_WIN32 static ma_result ma_context_uninit_backend_apis__win32(ma_context* pContext) { + /* For some reason UWP complains when CoUninitialize() is called. I'm just not going to call it on UWP. */ +#ifdef MA_WIN32_DESKTOP ma_CoUninitialize(pContext); ma_dlclose(pContext, pContext->win32.hUser32DLL); ma_dlclose(pContext, pContext->win32.hOle32DLL); ma_dlclose(pContext, pContext->win32.hAdvapi32DLL); +#else + (void)pContext; +#endif return MA_SUCCESS; } @@ -33087,7 +39230,138 @@ static ma_bool32 ma_context_is_backend_asynchronous(ma_context* pContext) } -MA_API ma_context_config ma_context_config_init() +/* The default capacity doesn't need to be too big. */ +#ifndef MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY +#define MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY 32 +#endif + +MA_API ma_device_job_thread_config ma_device_job_thread_config_init(void) +{ + ma_device_job_thread_config config; + + MA_ZERO_OBJECT(&config); + config.noThread = MA_FALSE; + config.jobQueueCapacity = MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY; + config.jobQueueFlags = 0; + + return config; +} + + +static ma_thread_result MA_THREADCALL ma_device_job_thread_entry(void* pUserData) +{ + ma_device_job_thread* pJobThread = (ma_device_job_thread*)pUserData; + MA_ASSERT(pJobThread != NULL); + + for (;;) { + ma_result result; + ma_job job; + + result = ma_device_job_thread_next(pJobThread, &job); + if (result != MA_SUCCESS) { + break; + } + + if (job.toc.breakup.code == MA_JOB_TYPE_QUIT) { + break; + } + + ma_job_process(&job); + } + + return (ma_thread_result)0; +} + +MA_API ma_result ma_device_job_thread_init(const ma_device_job_thread_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_device_job_thread* pJobThread) +{ + ma_result result; + ma_job_queue_config jobQueueConfig; + + if (pJobThread == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pJobThread); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + + /* Initialize the job queue before the thread to ensure it's in a valid state. */ + jobQueueConfig = ma_job_queue_config_init(pConfig->jobQueueFlags, pConfig->jobQueueCapacity); + + result = ma_job_queue_init(&jobQueueConfig, pAllocationCallbacks, &pJobThread->jobQueue); + if (result != MA_SUCCESS) { + return result; /* Failed to initialize job queue. */ + } + + + /* The thread needs to be initialized after the job queue to ensure the thread doesn't try to access it prematurely. */ + if (pConfig->noThread == MA_FALSE) { + result = ma_thread_create(&pJobThread->thread, ma_thread_priority_normal, 0, ma_device_job_thread_entry, pJobThread, pAllocationCallbacks); + if (result != MA_SUCCESS) { + ma_job_queue_uninit(&pJobThread->jobQueue, pAllocationCallbacks); + return result; /* Failed to create the job thread. */ + } + + pJobThread->_hasThread = MA_TRUE; + } else { + pJobThread->_hasThread = MA_FALSE; + } + + + return MA_SUCCESS; +} + +MA_API void ma_device_job_thread_uninit(ma_device_job_thread* pJobThread, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pJobThread == NULL) { + return; + } + + /* The first thing to do is post a quit message to the job queue. If we're using a thread we'll need to wait for it. */ + { + ma_job job = ma_job_init(MA_JOB_TYPE_QUIT); + ma_device_job_thread_post(pJobThread, &job); + } + + /* Wait for the thread to terminate naturally. */ + if (pJobThread->_hasThread) { + ma_thread_wait(&pJobThread->thread); + } + + /* At this point the thread should be terminated so we can safely uninitialize the job queue. */ + ma_job_queue_uninit(&pJobThread->jobQueue, pAllocationCallbacks); +} + +MA_API ma_result ma_device_job_thread_post(ma_device_job_thread* pJobThread, const ma_job* pJob) +{ + if (pJobThread == NULL || pJob == NULL) { + return MA_INVALID_ARGS; + } + + return ma_job_queue_post(&pJobThread->jobQueue, pJob); +} + +MA_API ma_result ma_device_job_thread_next(ma_device_job_thread* pJobThread, ma_job* pJob) +{ + if (pJob == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pJob); + + if (pJobThread == NULL) { + return MA_INVALID_ARGS; + } + + return ma_job_queue_next(&pJobThread->jobQueue, pJob); +} + + + +MA_API ma_context_config ma_context_config_init(void) { ma_context_config config; MA_ZERO_OBJECT(&config); @@ -33134,7 +39408,6 @@ MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendC } } - pContext->logCallback = pConfig->logCallback; pContext->threadPriority = pConfig->threadPriority; pContext->threadStackSize = pConfig->threadStackSize; pContext->pUserData = pConfig->pUserData; @@ -33272,23 +39545,19 @@ MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendC if (result == MA_SUCCESS) { result = ma_mutex_init(&pContext->deviceEnumLock); if (result != MA_SUCCESS) { - ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device enumeration. ma_context_get_devices() is not thread safe.\n", result); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device enumeration. ma_context_get_devices() is not thread safe.\n"); } result = ma_mutex_init(&pContext->deviceInfoLock); if (result != MA_SUCCESS) { - ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device info retrieval. ma_context_get_device_info() is not thread safe.\n", result); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device info retrieval. ma_context_get_device_info() is not thread safe.\n"); } - #ifdef MA_DEBUG_OUTPUT - { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[miniaudio] Endian: %s\n", ma_is_little_endian() ? "LE" : "BE"); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[miniaudio] SSE2: %s\n", ma_has_sse2() ? "YES" : "NO"); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[miniaudio] AVX2: %s\n", ma_has_avx2() ? "YES" : "NO"); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[miniaudio] AVX512F: %s\n", ma_has_avx512f() ? "YES" : "NO"); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[miniaudio] NEON: %s\n", ma_has_neon() ? "YES" : "NO"); - } - #endif + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "System Architecture:\n"); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " Endian: %s\n", ma_is_little_endian() ? "LE" : "BE"); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " SSE2: %s\n", ma_has_sse2() ? "YES" : "NO"); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " AVX2: %s\n", ma_has_avx2() ? "YES" : "NO"); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " NEON: %s\n", ma_has_neon() ? "YES" : "NO"); pContext->backend = backend; return result; @@ -33314,7 +39583,7 @@ MA_API ma_result ma_context_uninit(ma_context* pContext) ma_mutex_uninit(&pContext->deviceEnumLock); ma_mutex_uninit(&pContext->deviceInfoLock); - ma__free_from_callbacks(pContext->pDeviceInfos, &pContext->allocationCallbacks); + ma_free(pContext->pDeviceInfos, &pContext->allocationCallbacks); ma_context_uninit_backend_apis(pContext); if (pContext->pLog == &pContext->log) { @@ -33377,9 +39646,8 @@ static ma_bool32 ma_context_get_devices__enum_callback(ma_context* pContext, ma_ const ma_uint32 totalDeviceInfoCount = pContext->playbackDeviceInfoCount + pContext->captureDeviceInfoCount; if (totalDeviceInfoCount >= pContext->deviceInfoCapacity) { - ma_uint32 oldCapacity = pContext->deviceInfoCapacity; - ma_uint32 newCapacity = oldCapacity + bufferExpansionCount; - ma_device_info* pNewInfos = (ma_device_info*)ma__realloc_from_callbacks(pContext->pDeviceInfos, sizeof(*pContext->pDeviceInfos)*newCapacity, sizeof(*pContext->pDeviceInfos)*oldCapacity, &pContext->allocationCallbacks); + ma_uint32 newCapacity = pContext->deviceInfoCapacity + bufferExpansionCount; + ma_device_info* pNewInfos = (ma_device_info*)ma_realloc(pContext->pDeviceInfos, sizeof(*pContext->pDeviceInfos)*newCapacity, &pContext->allocationCallbacks); if (pNewInfos == NULL) { return MA_FALSE; /* Out of memory. */ } @@ -33461,13 +39729,11 @@ MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** p return result; } -MA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo) +MA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) { ma_result result; ma_device_info deviceInfo; - (void)shareMode; /* Unused. This parameter will be removed in version 0.11. */ - /* NOTE: Do not clear pDeviceInfo on entry. The reason is the pDeviceID may actually point to pDeviceInfo->id which will break things. */ if (pContext == NULL || pDeviceInfo == NULL) { return MA_INVALID_ARGS; @@ -33490,81 +39756,6 @@ MA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type } ma_mutex_unlock(&pContext->deviceInfoLock); - /* - If the backend is using the new device info system, do a pass to fill out the old settings for backwards compatibility. This will be removed in - the future when all backends have implemented the new device info system. - */ - if (deviceInfo.nativeDataFormatCount > 0) { - ma_uint32 iNativeFormat; - ma_uint32 iSampleFormat; - - deviceInfo.minChannels = 0xFFFFFFFF; - deviceInfo.maxChannels = 0; - deviceInfo.minSampleRate = 0xFFFFFFFF; - deviceInfo.maxSampleRate = 0; - - for (iNativeFormat = 0; iNativeFormat < deviceInfo.nativeDataFormatCount; iNativeFormat += 1) { - /* Formats. */ - if (deviceInfo.nativeDataFormats[iNativeFormat].format == ma_format_unknown) { - /* All formats are supported. */ - deviceInfo.formats[0] = ma_format_u8; - deviceInfo.formats[1] = ma_format_s16; - deviceInfo.formats[2] = ma_format_s24; - deviceInfo.formats[3] = ma_format_s32; - deviceInfo.formats[4] = ma_format_f32; - deviceInfo.formatCount = 5; - } else { - /* Make sure the format isn't already in the list. If so, skip. */ - ma_bool32 alreadyExists = MA_FALSE; - for (iSampleFormat = 0; iSampleFormat < deviceInfo.formatCount; iSampleFormat += 1) { - if (deviceInfo.formats[iSampleFormat] == deviceInfo.nativeDataFormats[iNativeFormat].format) { - alreadyExists = MA_TRUE; - break; - } - } - - if (!alreadyExists) { - deviceInfo.formats[deviceInfo.formatCount++] = deviceInfo.nativeDataFormats[iNativeFormat].format; - } - } - - /* Channels. */ - if (deviceInfo.nativeDataFormats[iNativeFormat].channels == 0) { - /* All channels supported. */ - deviceInfo.minChannels = MA_MIN_CHANNELS; - deviceInfo.maxChannels = MA_MAX_CHANNELS; - } else { - if (deviceInfo.minChannels > deviceInfo.nativeDataFormats[iNativeFormat].channels) { - deviceInfo.minChannels = deviceInfo.nativeDataFormats[iNativeFormat].channels; - } - if (deviceInfo.maxChannels < deviceInfo.nativeDataFormats[iNativeFormat].channels) { - deviceInfo.maxChannels = deviceInfo.nativeDataFormats[iNativeFormat].channels; - } - } - - /* Sample rate. */ - if (deviceInfo.nativeDataFormats[iNativeFormat].sampleRate == 0) { - /* All sample rates supported. */ - deviceInfo.minSampleRate = (ma_uint32)ma_standard_sample_rate_min; - deviceInfo.maxSampleRate = (ma_uint32)ma_standard_sample_rate_max; - } else { - if (deviceInfo.minSampleRate > deviceInfo.nativeDataFormats[iNativeFormat].sampleRate) { - deviceInfo.minSampleRate = deviceInfo.nativeDataFormats[iNativeFormat].sampleRate; - } - if (deviceInfo.maxSampleRate < deviceInfo.nativeDataFormats[iNativeFormat].sampleRate) { - deviceInfo.maxSampleRate = deviceInfo.nativeDataFormats[iNativeFormat].sampleRate; - } - } - } - } - - - /* Clamp ranges. */ - deviceInfo.minChannels = ma_max(deviceInfo.minChannels, MA_MIN_CHANNELS); - deviceInfo.maxChannels = ma_min(deviceInfo.maxChannels, MA_MAX_CHANNELS); - deviceInfo.minSampleRate = ma_max(deviceInfo.minSampleRate, (ma_uint32)ma_standard_sample_rate_min); - deviceInfo.maxSampleRate = ma_min(deviceInfo.maxSampleRate, (ma_uint32)ma_standard_sample_rate_max); - *pDeviceInfo = deviceInfo; return result; } @@ -33584,11 +39775,7 @@ MA_API ma_device_config ma_device_config_init(ma_device_type deviceType) ma_device_config config; MA_ZERO_OBJECT(&config); config.deviceType = deviceType; - - /* Resampling defaults. We must never use the Speex backend by default because it uses licensed third party code. */ - config.resampling.algorithm = ma_resample_algorithm_linear; - config.resampling.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER); - config.resampling.speex.quality = 3; + config.resampling = ma_resampler_config_init(ma_format_unknown, 0, 0, 0, ma_resample_algorithm_linear); /* Format/channels/rate don't matter here. */ return config; } @@ -33605,92 +39792,92 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC } if (pDevice == NULL) { - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "ma_device_init() called with invalid arguments (pDevice == NULL).", MA_INVALID_ARGS); + return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pDevice); if (pConfig == NULL) { - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "ma_device_init() called with invalid arguments (pConfig == NULL).", MA_INVALID_ARGS); + return MA_INVALID_ARGS; } - /* Check that we have our callbacks defined. */ if (pContext->callbacks.onDeviceInit == NULL) { return MA_INVALID_OPERATION; } - /* Basic config validation. */ - if (pConfig->deviceType != ma_device_type_playback && pConfig->deviceType != ma_device_type_capture && pConfig->deviceType != ma_device_type_duplex && pConfig->deviceType != ma_device_type_loopback) { - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "ma_device_init() called with an invalid config. Device type is invalid. Make sure the device type has been set in the config.", MA_INVALID_DEVICE_CONFIG); - } - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { if (pConfig->capture.channels > MA_MAX_CHANNELS) { - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "ma_device_init() called with an invalid config. Capture channel count cannot exceed 32.", MA_INVALID_DEVICE_CONFIG); + return MA_INVALID_ARGS; } - if (!ma__is_channel_map_valid(pConfig->capture.channelMap, pConfig->capture.channels)) { - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "ma_device_init() called with invalid config. Capture channel map is invalid.", MA_INVALID_DEVICE_CONFIG); + + if (!ma__is_channel_map_valid(pConfig->capture.pChannelMap, pConfig->capture.channels)) { + return MA_INVALID_ARGS; } } if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { if (pConfig->playback.channels > MA_MAX_CHANNELS) { - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "ma_device_init() called with an invalid config. Playback channel count cannot exceed 32.", MA_INVALID_DEVICE_CONFIG); + return MA_INVALID_ARGS; } - if (!ma__is_channel_map_valid(pConfig->playback.channelMap, pConfig->playback.channels)) { - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "ma_device_init() called with invalid config. Playback channel map is invalid.", MA_INVALID_DEVICE_CONFIG); + + if (!ma__is_channel_map_valid(pConfig->playback.pChannelMap, pConfig->playback.channels)) { + return MA_INVALID_ARGS; } } pDevice->pContext = pContext; /* Set the user data and log callback ASAP to ensure it is available for the entire initialization process. */ - pDevice->pUserData = pConfig->pUserData; - pDevice->onData = pConfig->dataCallback; - pDevice->onStop = pConfig->stopCallback; - - if (((ma_uintptr)pDevice % sizeof(pDevice)) != 0) { - if (pContext->logCallback) { - pContext->logCallback(pContext, pDevice, MA_LOG_LEVEL_WARNING, "WARNING: ma_device_init() called for a device that is not properly aligned. Thread safety is not supported."); - } - } + pDevice->pUserData = pConfig->pUserData; + pDevice->onData = pConfig->dataCallback; + pDevice->onNotification = pConfig->notificationCallback; + pDevice->onStop = pConfig->stopCallback; if (pConfig->playback.pDeviceID != NULL) { MA_COPY_MEMORY(&pDevice->playback.id, pConfig->playback.pDeviceID, sizeof(pDevice->playback.id)); + pDevice->playback.pID = &pDevice->playback.id; + } else { + pDevice->playback.pID = NULL; } if (pConfig->capture.pDeviceID != NULL) { MA_COPY_MEMORY(&pDevice->capture.id, pConfig->capture.pDeviceID, sizeof(pDevice->capture.id)); + pDevice->capture.pID = &pDevice->capture.id; + } else { + pDevice->capture.pID = NULL; } - pDevice->noPreZeroedOutputBuffer = pConfig->noPreZeroedOutputBuffer; - pDevice->noClip = pConfig->noClip; - pDevice->masterVolumeFactor = 1; + pDevice->noPreSilencedOutputBuffer = pConfig->noPreSilencedOutputBuffer; + pDevice->noClip = pConfig->noClip; + pDevice->noDisableDenormals = pConfig->noDisableDenormals; + pDevice->noFixedSizedCallback = pConfig->noFixedSizedCallback; + pDevice->masterVolumeFactor = 1; - pDevice->type = pConfig->deviceType; - pDevice->sampleRate = pConfig->sampleRate; - pDevice->resampling.algorithm = pConfig->resampling.algorithm; - pDevice->resampling.linear.lpfOrder = pConfig->resampling.linear.lpfOrder; - pDevice->resampling.speex.quality = pConfig->resampling.speex.quality; + pDevice->type = pConfig->deviceType; + pDevice->sampleRate = pConfig->sampleRate; + pDevice->resampling.algorithm = pConfig->resampling.algorithm; + pDevice->resampling.linear.lpfOrder = pConfig->resampling.linear.lpfOrder; + pDevice->resampling.pBackendVTable = pConfig->resampling.pBackendVTable; + pDevice->resampling.pBackendUserData = pConfig->resampling.pBackendUserData; - pDevice->capture.shareMode = pConfig->capture.shareMode; - pDevice->capture.format = pConfig->capture.format; - pDevice->capture.channels = pConfig->capture.channels; - ma_channel_map_copy(pDevice->capture.channelMap, pConfig->capture.channelMap, pConfig->capture.channels); - pDevice->capture.channelMixMode = pConfig->capture.channelMixMode; + pDevice->capture.shareMode = pConfig->capture.shareMode; + pDevice->capture.format = pConfig->capture.format; + pDevice->capture.channels = pConfig->capture.channels; + ma_channel_map_copy_or_default(pDevice->capture.channelMap, ma_countof(pDevice->capture.channelMap), pConfig->capture.pChannelMap, pConfig->capture.channels); + pDevice->capture.channelMixMode = pConfig->capture.channelMixMode; - pDevice->playback.shareMode = pConfig->playback.shareMode; - pDevice->playback.format = pConfig->playback.format; - pDevice->playback.channels = pConfig->playback.channels; - ma_channel_map_copy(pDevice->playback.channelMap, pConfig->playback.channelMap, pConfig->playback.channels); - pDevice->playback.channelMixMode = pConfig->playback.channelMixMode; + pDevice->playback.shareMode = pConfig->playback.shareMode; + pDevice->playback.format = pConfig->playback.format; + pDevice->playback.channels = pConfig->playback.channels; + ma_channel_map_copy_or_default(pDevice->playback.channelMap, ma_countof(pDevice->playback.channelMap), pConfig->playback.pChannelMap, pConfig->playback.channels); + pDevice->playback.channelMixMode = pConfig->playback.channelMixMode; result = ma_mutex_init(&pDevice->startStopLock); if (result != MA_SUCCESS) { - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to create mutex.", result); + return result; } /* @@ -33703,14 +39890,14 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC result = ma_event_init(&pDevice->wakeupEvent); if (result != MA_SUCCESS) { ma_mutex_uninit(&pDevice->startStopLock); - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to create worker thread wakeup event.", result); + return result; } result = ma_event_init(&pDevice->startEvent); if (result != MA_SUCCESS) { ma_event_uninit(&pDevice->wakeupEvent); ma_mutex_uninit(&pDevice->startStopLock); - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to create worker thread start event.", result); + return result; } result = ma_event_init(&pDevice->stopEvent); @@ -33718,7 +39905,7 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC ma_event_uninit(&pDevice->startEvent); ma_event_uninit(&pDevice->wakeupEvent); ma_mutex_uninit(&pDevice->startStopLock); - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to create worker thread stop event.", result); + return result; } @@ -33728,7 +39915,7 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC descriptorPlayback.format = pConfig->playback.format; descriptorPlayback.channels = pConfig->playback.channels; descriptorPlayback.sampleRate = pConfig->sampleRate; - ma_channel_map_copy(descriptorPlayback.channelMap, pConfig->playback.channelMap, pConfig->playback.channels); + ma_channel_map_copy_or_default(descriptorPlayback.channelMap, ma_countof(descriptorPlayback.channelMap), pConfig->playback.pChannelMap, pConfig->playback.channels); descriptorPlayback.periodSizeInFrames = pConfig->periodSizeInFrames; descriptorPlayback.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds; descriptorPlayback.periodCount = pConfig->periods; @@ -33744,7 +39931,7 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC descriptorCapture.format = pConfig->capture.format; descriptorCapture.channels = pConfig->capture.channels; descriptorCapture.sampleRate = pConfig->sampleRate; - ma_channel_map_copy(descriptorCapture.channelMap, pConfig->capture.channelMap, pConfig->capture.channels); + ma_channel_map_copy_or_default(descriptorCapture.channelMap, ma_countof(descriptorCapture.channelMap), pConfig->capture.pChannelMap, pConfig->capture.channels); descriptorCapture.periodSizeInFrames = pConfig->periodSizeInFrames; descriptorCapture.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds; descriptorCapture.periodCount = pConfig->periods; @@ -33762,7 +39949,7 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC return result; } - +#if 0 /* On output the descriptors will contain the *actual* data format of the device. We need this to know how to convert the data between the requested format and the internal format. @@ -33812,7 +39999,7 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC ma_device_info deviceInfo; if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { - result = ma_context_get_device_info(pContext, (pConfig->deviceType == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, descriptorCapture.pDeviceID, descriptorCapture.shareMode, &deviceInfo); + result = ma_device_get_info(pDevice, (pConfig->deviceType == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, &deviceInfo); if (result == MA_SUCCESS) { ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1); } else { @@ -33826,7 +40013,7 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC } if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - result = ma_context_get_device_info(pContext, ma_device_type_playback, descriptorPlayback.pDeviceID, descriptorPlayback.shareMode, &deviceInfo); + result = ma_device_get_info(pDevice, ma_device_type_playback, &deviceInfo); if (result == MA_SUCCESS) { ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), deviceInfo.name, (size_t)-1); } else { @@ -33842,6 +40029,77 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC ma_device__post_init_setup(pDevice, pConfig->deviceType); +#endif + + result = ma_device_post_init(pDevice, pConfig->deviceType, &descriptorPlayback, &descriptorCapture); + if (result != MA_SUCCESS) { + ma_device_uninit(pDevice); + return result; + } + + + + /* + If we're using fixed sized callbacks we'll need to make use of an intermediary buffer. Needs to + be done after post_init_setup() because we'll need access to the sample rate. + */ + if (pConfig->noFixedSizedCallback == MA_FALSE) { + /* We're using a fixed sized data callback so we'll need an intermediary buffer. */ + ma_uint32 intermediaryBufferCap = pConfig->periodSizeInFrames; + if (intermediaryBufferCap == 0) { + intermediaryBufferCap = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, pDevice->sampleRate); + } + + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { + ma_uint32 intermediaryBufferSizeInBytes; + + pDevice->capture.intermediaryBufferLen = 0; + pDevice->capture.intermediaryBufferCap = intermediaryBufferCap; + if (pDevice->capture.intermediaryBufferCap == 0) { + pDevice->capture.intermediaryBufferCap = pDevice->capture.internalPeriodSizeInFrames; + } + + intermediaryBufferSizeInBytes = pDevice->capture.intermediaryBufferCap * ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); + + pDevice->capture.pIntermediaryBuffer = ma_malloc((size_t)intermediaryBufferSizeInBytes, &pContext->allocationCallbacks); + if (pDevice->capture.pIntermediaryBuffer == NULL) { + ma_device_uninit(pDevice); + return MA_OUT_OF_MEMORY; + } + + /* Silence the buffer for safety. */ + ma_silence_pcm_frames(pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap, pDevice->capture.format, pDevice->capture.channels); + pDevice->capture.intermediaryBufferLen = pDevice->capture.intermediaryBufferCap; + } + + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + ma_uint64 intermediaryBufferSizeInBytes; + + pDevice->playback.intermediaryBufferLen = 0; + if (pConfig->deviceType == ma_device_type_duplex) { + pDevice->playback.intermediaryBufferCap = pDevice->capture.intermediaryBufferCap; /* In duplex mode, make sure the intermediary buffer is always the same size as the capture side. */ + } else { + pDevice->playback.intermediaryBufferCap = intermediaryBufferCap; + if (pDevice->playback.intermediaryBufferCap == 0) { + pDevice->playback.intermediaryBufferCap = pDevice->playback.internalPeriodSizeInFrames; + } + } + + intermediaryBufferSizeInBytes = pDevice->playback.intermediaryBufferCap * ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); + + pDevice->playback.pIntermediaryBuffer = ma_malloc((size_t)intermediaryBufferSizeInBytes, &pContext->allocationCallbacks); + if (pDevice->playback.pIntermediaryBuffer == NULL) { + ma_device_uninit(pDevice); + return MA_OUT_OF_MEMORY; + } + + /* Silence the buffer for safety. */ + ma_silence_pcm_frames(pDevice->playback.pIntermediaryBuffer, pDevice->playback.intermediaryBufferCap, pDevice->playback.format, pDevice->playback.channels); + pDevice->playback.intermediaryBufferLen = 0; + } + } else { + /* Not using a fixed sized data callback so no need for an intermediary buffer. */ + } /* Some backends don't require the worker thread. */ @@ -33850,12 +40108,12 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC result = ma_thread_create(&pDevice->thread, pContext->threadPriority, pContext->threadStackSize, ma_worker_thread, pDevice, &pContext->allocationCallbacks); if (result != MA_SUCCESS) { ma_device_uninit(pDevice); - return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to create worker thread.", result); + return result; } /* Wait for the worker thread to put the device into it's stopped state for real. */ ma_event_wait(&pDevice->stopEvent); - MA_ASSERT(ma_device_get_state(pDevice) == MA_STATE_STOPPED); + MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped); } else { /* If the backend is asynchronous and the device is duplex, we'll need an intermediary ring buffer. Note that this needs to be done @@ -33871,39 +40129,47 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC } } - ma_device__set_state(pDevice, MA_STATE_STOPPED); + ma_device__set_state(pDevice, ma_device_state_stopped); } + /* Log device information. */ + { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[%s]\n", ma_get_backend_name(pDevice->pContext->backend)); + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; + ma_device_get_name(pDevice, ma_device_type_capture, name, sizeof(name), NULL); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[%s]\n", ma_get_backend_name(pDevice->pContext->backend)); - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", pDevice->capture.name, "Capture"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Format: %s -> %s\n", ma_get_format_name(pDevice->capture.internalFormat), ma_get_format_name(pDevice->capture.format)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channels: %d -> %d\n", pDevice->capture.internalChannels, pDevice->capture.channels); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d\n", pDevice->capture.internalSampleRate, pDevice->sampleRate); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Buffer Size: %d*%d (%d)\n", pDevice->capture.internalPeriodSizeInFrames, pDevice->capture.internalPeriods, (pDevice->capture.internalPeriodSizeInFrames * pDevice->capture.internalPeriods)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Conversion:\n"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s\n", pDevice->capture.converter.hasPreFormatConversion ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Post Format Conversion: %s\n", pDevice->capture.converter.hasPostFormatConversion ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->capture.converter.hasChannelConverter ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->capture.converter.hasResampler ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->capture.converter.isPassthrough ? "YES" : "NO"); - } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", pDevice->playback.name, "Playback"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Format: %s -> %s\n", ma_get_format_name(pDevice->playback.format), ma_get_format_name(pDevice->playback.internalFormat)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channels: %d -> %d\n", pDevice->playback.channels, pDevice->playback.internalChannels); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d\n", pDevice->sampleRate, pDevice->playback.internalSampleRate); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Buffer Size: %d*%d (%d)\n", pDevice->playback.internalPeriodSizeInFrames, pDevice->playback.internalPeriods, (pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Conversion:\n"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s\n", pDevice->playback.converter.hasPreFormatConversion ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Post Format Conversion: %s\n", pDevice->playback.converter.hasPostFormatConversion ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->playback.converter.hasChannelConverter ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->playback.converter.hasResampler ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->playback.converter.isPassthrough ? "YES" : "NO"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", name, "Capture"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Format: %s -> %s\n", ma_get_format_name(pDevice->capture.internalFormat), ma_get_format_name(pDevice->capture.format)); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channels: %d -> %d\n", pDevice->capture.internalChannels, pDevice->capture.channels); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d\n", pDevice->capture.internalSampleRate, pDevice->sampleRate); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Buffer Size: %d*%d (%d)\n", pDevice->capture.internalPeriodSizeInFrames, pDevice->capture.internalPeriods, (pDevice->capture.internalPeriodSizeInFrames * pDevice->capture.internalPeriods)); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Conversion:\n"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s\n", pDevice->capture.converter.hasPreFormatConversion ? "YES" : "NO"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Post Format Conversion: %s\n", pDevice->capture.converter.hasPostFormatConversion ? "YES" : "NO"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->capture.converter.hasChannelConverter ? "YES" : "NO"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->capture.converter.hasResampler ? "YES" : "NO"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->capture.converter.isPassthrough ? "YES" : "NO"); + } + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; + ma_device_get_name(pDevice, ma_device_type_playback, name, sizeof(name), NULL); + + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", name, "Playback"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Format: %s -> %s\n", ma_get_format_name(pDevice->playback.format), ma_get_format_name(pDevice->playback.internalFormat)); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channels: %d -> %d\n", pDevice->playback.channels, pDevice->playback.internalChannels); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d\n", pDevice->sampleRate, pDevice->playback.internalSampleRate); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Buffer Size: %d*%d (%d)\n", pDevice->playback.internalPeriodSizeInFrames, pDevice->playback.internalPeriods, (pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods)); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Conversion:\n"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s\n", pDevice->playback.converter.hasPreFormatConversion ? "YES" : "NO"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Post Format Conversion: %s\n", pDevice->playback.converter.hasPostFormatConversion ? "YES" : "NO"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->playback.converter.hasChannelConverter ? "YES" : "NO"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->playback.converter.hasResampler ? "YES" : "NO"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->playback.converter.isPassthrough ? "YES" : "NO"); + } } - MA_ASSERT(ma_device_get_state(pDevice) == MA_STATE_STOPPED); + MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped); return MA_SUCCESS; } @@ -33931,7 +40197,7 @@ MA_API ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backen } - pContext = (ma_context*)ma__malloc_from_callbacks(sizeof(*pContext), &allocationCallbacks); + pContext = (ma_context*)ma_malloc(sizeof(*pContext), &allocationCallbacks); if (pContext == NULL) { return MA_OUT_OF_MEMORY; } @@ -33962,7 +40228,7 @@ MA_API ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backen } if (result != MA_SUCCESS) { - ma__free_from_callbacks(pContext, &allocationCallbacks); + ma_free(pContext, &allocationCallbacks); return result; } @@ -33982,7 +40248,7 @@ MA_API void ma_device_uninit(ma_device* pDevice) } /* Putting the device into an uninitialized state will make the worker thread return. */ - ma_device__set_state(pDevice, MA_STATE_UNINITIALIZED); + ma_device__set_state(pDevice, ma_device_state_uninitialized); /* Wake up the worker thread and wait for it to properly terminate. */ if (!ma_context_is_backend_asynchronous(pDevice->pContext)) { @@ -34006,11 +40272,29 @@ MA_API void ma_device_uninit(ma_device* pDevice) } } + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { + ma_data_converter_uninit(&pDevice->capture.converter, &pDevice->pContext->allocationCallbacks); + } + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + ma_data_converter_uninit(&pDevice->playback.converter, &pDevice->pContext->allocationCallbacks); + } + + if (pDevice->playback.pInputCache != NULL) { + ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); + } + + if (pDevice->capture.pIntermediaryBuffer != NULL) { + ma_free(pDevice->capture.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); + } + if (pDevice->playback.pIntermediaryBuffer != NULL) { + ma_free(pDevice->playback.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); + } + if (pDevice->isOwnerOfContext) { ma_allocation_callbacks allocationCallbacks = pDevice->pContext->allocationCallbacks; ma_context_uninit(pDevice->pContext); - ma__free_from_callbacks(pDevice->pContext, &allocationCallbacks); + ma_free(pDevice->pContext, &allocationCallbacks); } MA_ZERO_OBJECT(pDevice); @@ -34030,28 +40314,92 @@ MA_API ma_log* ma_device_get_log(ma_device* pDevice) return ma_context_get_log(ma_device_get_context(pDevice)); } +MA_API ma_result ma_device_get_info(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo) +{ + if (pDeviceInfo == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pDeviceInfo); + + if (pDevice == NULL) { + return MA_INVALID_ARGS; + } + + /* If the onDeviceGetInfo() callback is set, use that. Otherwise we'll fall back to ma_context_get_device_info(). */ + if (pDevice->pContext->callbacks.onDeviceGetInfo != NULL) { + return pDevice->pContext->callbacks.onDeviceGetInfo(pDevice, type, pDeviceInfo); + } + + /* Getting here means onDeviceGetInfo is not implemented so we need to fall back to an alternative. */ + if (type == ma_device_type_playback) { + return ma_context_get_device_info(pDevice->pContext, type, pDevice->playback.pID, pDeviceInfo); + } else { + return ma_context_get_device_info(pDevice->pContext, type, pDevice->capture.pID, pDeviceInfo); + } +} + +MA_API ma_result ma_device_get_name(ma_device* pDevice, ma_device_type type, char* pName, size_t nameCap, size_t* pLengthNotIncludingNullTerminator) +{ + ma_result result; + ma_device_info deviceInfo; + + if (pLengthNotIncludingNullTerminator != NULL) { + *pLengthNotIncludingNullTerminator = 0; + } + + if (pName != NULL && nameCap > 0) { + pName[0] = '\0'; + } + + result = ma_device_get_info(pDevice, type, &deviceInfo); + if (result != MA_SUCCESS) { + return result; + } + + if (pName != NULL) { + ma_strncpy_s(pName, nameCap, deviceInfo.name, (size_t)-1); + + /* + For safety, make sure the length is based on the truncated output string rather than the + source. Otherwise the caller might assume the output buffer contains more content than it + actually does. + */ + if (pLengthNotIncludingNullTerminator != NULL) { + *pLengthNotIncludingNullTerminator = strlen(pName); + } + } else { + /* Name not specified. Just report the length of the source string. */ + if (pLengthNotIncludingNullTerminator != NULL) { + *pLengthNotIncludingNullTerminator = strlen(deviceInfo.name); + } + } + + return MA_SUCCESS; +} + MA_API ma_result ma_device_start(ma_device* pDevice) { ma_result result; if (pDevice == NULL) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "ma_device_start() called with invalid arguments (pDevice == NULL).", MA_INVALID_ARGS); + return MA_INVALID_ARGS; } - if (ma_device_get_state(pDevice) == MA_STATE_UNINITIALIZED) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "ma_device_start() called for an uninitialized device.", MA_DEVICE_NOT_INITIALIZED); + if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { + return MA_INVALID_OPERATION; /* Not initialized. */ } - if (ma_device_get_state(pDevice) == MA_STATE_STARTED) { - return ma_post_error(pDevice, MA_LOG_LEVEL_WARNING, "ma_device_start() called when the device is already started.", MA_INVALID_OPERATION); /* Already started. Returning an error to let the application know because it probably means they're doing something wrong. */ + if (ma_device_get_state(pDevice) == ma_device_state_started) { + return MA_SUCCESS; /* Already started. */ } ma_mutex_lock(&pDevice->startStopLock); { /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a stopped or paused state. */ - MA_ASSERT(ma_device_get_state(pDevice) == MA_STATE_STOPPED); + MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped); - ma_device__set_state(pDevice, MA_STATE_STARTING); + ma_device__set_state(pDevice, ma_device_state_starting); /* Asynchronous backends need to be handled differently. */ if (ma_context_is_backend_asynchronous(pDevice->pContext)) { @@ -34062,7 +40410,8 @@ MA_API ma_result ma_device_start(ma_device* pDevice) } if (result == MA_SUCCESS) { - ma_device__set_state(pDevice, MA_STATE_STARTED); + ma_device__set_state(pDevice, ma_device_state_started); + ma_device__on_notification_started(pDevice); } } else { /* @@ -34081,7 +40430,7 @@ MA_API ma_result ma_device_start(ma_device* pDevice) /* We changed the state from stopped to started, so if we failed, make sure we put the state back to stopped. */ if (result != MA_SUCCESS) { - ma_device__set_state(pDevice, MA_STATE_STOPPED); + ma_device__set_state(pDevice, ma_device_state_stopped); } } ma_mutex_unlock(&pDevice->startStopLock); @@ -34094,23 +40443,23 @@ MA_API ma_result ma_device_stop(ma_device* pDevice) ma_result result; if (pDevice == NULL) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "ma_device_stop() called with invalid arguments (pDevice == NULL).", MA_INVALID_ARGS); + return MA_INVALID_ARGS; } - if (ma_device_get_state(pDevice) == MA_STATE_UNINITIALIZED) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "ma_device_stop() called for an uninitialized device.", MA_DEVICE_NOT_INITIALIZED); + if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { + return MA_INVALID_OPERATION; /* Not initialized. */ } - if (ma_device_get_state(pDevice) == MA_STATE_STOPPED) { - return ma_post_error(pDevice, MA_LOG_LEVEL_WARNING, "ma_device_stop() called when the device is already stopped.", MA_INVALID_OPERATION); /* Already stopped. Returning an error to let the application know because it probably means they're doing something wrong. */ + if (ma_device_get_state(pDevice) == ma_device_state_stopped) { + return MA_SUCCESS; /* Already stopped. */ } ma_mutex_lock(&pDevice->startStopLock); { /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a started or paused state. */ - MA_ASSERT(ma_device_get_state(pDevice) == MA_STATE_STARTED); + MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_started); - ma_device__set_state(pDevice, MA_STATE_STOPPING); + ma_device__set_state(pDevice, ma_device_state_stopping); /* Asynchronous backends need to be handled differently. */ if (ma_context_is_backend_asynchronous(pDevice->pContext)) { @@ -34121,7 +40470,7 @@ MA_API ma_result ma_device_stop(ma_device* pDevice) result = MA_INVALID_OPERATION; } - ma_device__set_state(pDevice, MA_STATE_STOPPED); + ma_device__set_state(pDevice, ma_device_state_stopped); } else { /* Synchronous backends. The stop callback is always called from the worker thread. Do not call the stop callback here. If @@ -34129,7 +40478,7 @@ MA_API ma_result ma_device_stop(ma_device* pDevice) sure the state of the device is *not* playing right now, which it shouldn't be since we set it above. This is super important though, so I'm asserting it here as well for extra safety in case we accidentally change something later. */ - MA_ASSERT(ma_device_get_state(pDevice) != MA_STATE_STARTED); + MA_ASSERT(ma_device_get_state(pDevice) != ma_device_state_started); if (pDevice->pContext->callbacks.onDeviceDataLoopWakeup != NULL) { pDevice->pContext->callbacks.onDeviceDataLoopWakeup(pDevice); @@ -34150,16 +40499,16 @@ MA_API ma_result ma_device_stop(ma_device* pDevice) MA_API ma_bool32 ma_device_is_started(const ma_device* pDevice) { - return ma_device_get_state(pDevice) == MA_STATE_STARTED; + return ma_device_get_state(pDevice) == ma_device_state_started; } -MA_API ma_uint32 ma_device_get_state(const ma_device* pDevice) +MA_API ma_device_state ma_device_get_state(const ma_device* pDevice) { if (pDevice == NULL) { - return MA_STATE_UNINITIALIZED; + return ma_device_state_uninitialized; } - return c89atomic_load_32((ma_uint32*)&pDevice->state); /* Naughty cast to get rid of a const warning. */ + return (ma_device_state)c89atomic_load_i32((ma_int32*)&pDevice->state); /* Naughty cast to get rid of a const warning. */ } MA_API ma_result ma_device_set_master_volume(ma_device* pDevice, float volume) @@ -34168,7 +40517,7 @@ MA_API ma_result ma_device_set_master_volume(ma_device* pDevice, float volume) return MA_INVALID_ARGS; } - if (volume < 0.0f || volume > 1.0f) { + if (volume < 0.0f) { return MA_INVALID_ARGS; } @@ -34193,16 +40542,16 @@ MA_API ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume) return MA_SUCCESS; } -MA_API ma_result ma_device_set_master_gain_db(ma_device* pDevice, float gainDB) +MA_API ma_result ma_device_set_master_volume_db(ma_device* pDevice, float gainDB) { if (gainDB > 0) { return MA_INVALID_ARGS; } - return ma_device_set_master_volume(pDevice, ma_gain_db_to_factor(gainDB)); + return ma_device_set_master_volume(pDevice, ma_volume_db_to_linear(gainDB)); } -MA_API ma_result ma_device_get_master_gain_db(ma_device* pDevice, float* pGainDB) +MA_API ma_result ma_device_get_master_volume_db(ma_device* pDevice, float* pGainDB) { float factor; ma_result result; @@ -34217,7 +40566,7 @@ MA_API ma_result ma_device_get_master_gain_db(ma_device* pDevice, float* pGainDB return result; } - *pGainDB = ma_factor_to_gain_db(factor); + *pGainDB = ma_volume_linear_to_db(factor); return MA_SUCCESS; } @@ -34300,11 +40649,6 @@ MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_descriptor(const ma_dev #endif /* MA_NO_DEVICE_IO */ -MA_API ma_uint32 ma_scale_buffer_size(ma_uint32 baseBufferSize, float scale) -{ - return ma_max(1, (ma_uint32)(baseBufferSize*scale)); -} - MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate) { /* Prevent a division by zero. */ @@ -34358,13 +40702,89 @@ MA_API const void* ma_offset_pcm_frames_const_ptr(const void* p, ma_uint64 offse } -MA_API void ma_clip_samples_f32(float* p, ma_uint64 sampleCount) +MA_API void ma_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count) { - ma_uint32 iSample; + ma_uint64 iSample; - /* TODO: Research a branchless SSE implementation. */ - for (iSample = 0; iSample < sampleCount; iSample += 1) { - p[iSample] = ma_clip_f32(p[iSample]); + MA_ASSERT(pDst != NULL); + MA_ASSERT(pSrc != NULL); + + for (iSample = 0; iSample < count; iSample += 1) { + pDst[iSample] = ma_clip_u8(pSrc[iSample]); + } +} + +MA_API void ma_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count) +{ + ma_uint64 iSample; + + MA_ASSERT(pDst != NULL); + MA_ASSERT(pSrc != NULL); + + for (iSample = 0; iSample < count; iSample += 1) { + pDst[iSample] = ma_clip_s16(pSrc[iSample]); + } +} + +MA_API void ma_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count) +{ + ma_uint64 iSample; + + MA_ASSERT(pDst != NULL); + MA_ASSERT(pSrc != NULL); + + for (iSample = 0; iSample < count; iSample += 1) { + ma_int64 s = ma_clip_s24(pSrc[iSample]); + pDst[iSample*3 + 0] = (ma_uint8)((s & 0x000000FF) >> 0); + pDst[iSample*3 + 1] = (ma_uint8)((s & 0x0000FF00) >> 8); + pDst[iSample*3 + 2] = (ma_uint8)((s & 0x00FF0000) >> 16); + } +} + +MA_API void ma_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count) +{ + ma_uint64 iSample; + + MA_ASSERT(pDst != NULL); + MA_ASSERT(pSrc != NULL); + + for (iSample = 0; iSample < count; iSample += 1) { + pDst[iSample] = ma_clip_s32(pSrc[iSample]); + } +} + +MA_API void ma_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count) +{ + ma_uint64 iSample; + + MA_ASSERT(pDst != NULL); + MA_ASSERT(pSrc != NULL); + + for (iSample = 0; iSample < count; iSample += 1) { + pDst[iSample] = ma_clip_f32(pSrc[iSample]); + } +} + +MA_API void ma_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels) +{ + ma_uint64 sampleCount; + + MA_ASSERT(pDst != NULL); + MA_ASSERT(pSrc != NULL); + + sampleCount = frameCount * channels; + + switch (format) { + case ma_format_u8: ma_clip_samples_u8( (ma_uint8*)pDst, (const ma_int16*)pSrc, sampleCount); break; + case ma_format_s16: ma_clip_samples_s16((ma_int16*)pDst, (const ma_int32*)pSrc, sampleCount); break; + case ma_format_s24: ma_clip_samples_s24((ma_uint8*)pDst, (const ma_int64*)pSrc, sampleCount); break; + case ma_format_s32: ma_clip_samples_s32((ma_int32*)pDst, (const ma_int64*)pSrc, sampleCount); break; + case ma_format_f32: ma_clip_samples_f32(( float*)pDst, (const float*)pSrc, sampleCount); break; + + /* Do nothing if we don't know the format. We're including these here to silence a compiler warning about enums not being handled by the switch. */ + case ma_format_unknown: + case ma_format_count: + break; } } @@ -34441,8 +40861,19 @@ MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* return; } - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamplesOut[iSample] = pSamplesIn[iSample] * factor; + if (factor == 1) { + if (pSamplesOut == pSamplesIn) { + /* In place. No-op. */ + } else { + /* Just a copy. */ + for (iSample = 0; iSample < sampleCount; iSample += 1) { + pSamplesOut[iSample] = pSamplesIn[iSample]; + } + } + } else { + for (iSample = 0; iSample < sampleCount; iSample += 1) { + pSamplesOut[iSample] = pSamplesIn[iSample] * factor; + } } } @@ -34471,85 +40902,236 @@ MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint64 sampleCount, f ma_copy_and_apply_volume_factor_f32(pSamples, pSamples, sampleCount, factor); } -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pPCMFramesOut, const ma_uint8* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pFramesOut, const ma_uint8* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) { - ma_copy_and_apply_volume_factor_u8(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor); + ma_copy_and_apply_volume_factor_u8(pFramesOut, pFramesIn, frameCount*channels, factor); } -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pPCMFramesOut, const ma_int16* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pFramesOut, const ma_int16* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) { - ma_copy_and_apply_volume_factor_s16(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor); + ma_copy_and_apply_volume_factor_s16(pFramesOut, pFramesIn, frameCount*channels, factor); } -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pPCMFramesOut, const void* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) { - ma_copy_and_apply_volume_factor_s24(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor); + ma_copy_and_apply_volume_factor_s24(pFramesOut, pFramesIn, frameCount*channels, factor); } -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pPCMFramesOut, const ma_int32* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pFramesOut, const ma_int32* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) { - ma_copy_and_apply_volume_factor_s32(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor); + ma_copy_and_apply_volume_factor_s32(pFramesOut, pFramesIn, frameCount*channels, factor); } -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pPCMFramesOut, const float* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) { - ma_copy_and_apply_volume_factor_f32(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor); + ma_copy_and_apply_volume_factor_f32(pFramesOut, pFramesIn, frameCount*channels, factor); } -MA_API void ma_copy_and_apply_volume_factor_pcm_frames(void* pPCMFramesOut, const void* pPCMFramesIn, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor) +MA_API void ma_copy_and_apply_volume_factor_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor) { switch (format) { - case ma_format_u8: ma_copy_and_apply_volume_factor_pcm_frames_u8 ((ma_uint8*)pPCMFramesOut, (const ma_uint8*)pPCMFramesIn, frameCount, channels, factor); return; - case ma_format_s16: ma_copy_and_apply_volume_factor_pcm_frames_s16((ma_int16*)pPCMFramesOut, (const ma_int16*)pPCMFramesIn, frameCount, channels, factor); return; - case ma_format_s24: ma_copy_and_apply_volume_factor_pcm_frames_s24( pPCMFramesOut, pPCMFramesIn, frameCount, channels, factor); return; - case ma_format_s32: ma_copy_and_apply_volume_factor_pcm_frames_s32((ma_int32*)pPCMFramesOut, (const ma_int32*)pPCMFramesIn, frameCount, channels, factor); return; - case ma_format_f32: ma_copy_and_apply_volume_factor_pcm_frames_f32( (float*)pPCMFramesOut, (const float*)pPCMFramesIn, frameCount, channels, factor); return; + case ma_format_u8: ma_copy_and_apply_volume_factor_pcm_frames_u8 ((ma_uint8*)pFramesOut, (const ma_uint8*)pFramesIn, frameCount, channels, factor); return; + case ma_format_s16: ma_copy_and_apply_volume_factor_pcm_frames_s16((ma_int16*)pFramesOut, (const ma_int16*)pFramesIn, frameCount, channels, factor); return; + case ma_format_s24: ma_copy_and_apply_volume_factor_pcm_frames_s24( pFramesOut, pFramesIn, frameCount, channels, factor); return; + case ma_format_s32: ma_copy_and_apply_volume_factor_pcm_frames_s32((ma_int32*)pFramesOut, (const ma_int32*)pFramesIn, frameCount, channels, factor); return; + case ma_format_f32: ma_copy_and_apply_volume_factor_pcm_frames_f32( (float*)pFramesOut, (const float*)pFramesIn, frameCount, channels, factor); return; default: return; /* Do nothing. */ } } -MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pPCMFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) +MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) { - ma_copy_and_apply_volume_factor_pcm_frames_u8(pPCMFrames, pPCMFrames, frameCount, channels, factor); + ma_copy_and_apply_volume_factor_pcm_frames_u8(pFrames, pFrames, frameCount, channels, factor); } -MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pPCMFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) +MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) { - ma_copy_and_apply_volume_factor_pcm_frames_s16(pPCMFrames, pPCMFrames, frameCount, channels, factor); + ma_copy_and_apply_volume_factor_pcm_frames_s16(pFrames, pFrames, frameCount, channels, factor); } -MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pPCMFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) +MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) { - ma_copy_and_apply_volume_factor_pcm_frames_s24(pPCMFrames, pPCMFrames, frameCount, channels, factor); + ma_copy_and_apply_volume_factor_pcm_frames_s24(pFrames, pFrames, frameCount, channels, factor); } -MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pPCMFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) +MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) { - ma_copy_and_apply_volume_factor_pcm_frames_s32(pPCMFrames, pPCMFrames, frameCount, channels, factor); + ma_copy_and_apply_volume_factor_pcm_frames_s32(pFrames, pFrames, frameCount, channels, factor); } -MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pPCMFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) +MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) { - ma_copy_and_apply_volume_factor_pcm_frames_f32(pPCMFrames, pPCMFrames, frameCount, channels, factor); + ma_copy_and_apply_volume_factor_pcm_frames_f32(pFrames, pFrames, frameCount, channels, factor); } -MA_API void ma_apply_volume_factor_pcm_frames(void* pPCMFrames, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor) +MA_API void ma_apply_volume_factor_pcm_frames(void* pFramesOut, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor) { - ma_copy_and_apply_volume_factor_pcm_frames(pPCMFrames, pPCMFrames, frameCount, format, channels, factor); + ma_copy_and_apply_volume_factor_pcm_frames(pFramesOut, pFramesOut, frameCount, format, channels, factor); } -MA_API float ma_factor_to_gain_db(float factor) +MA_API void ma_copy_and_apply_volume_factor_per_channel_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float* pChannelGains) { - return (float)(20*ma_log10f(factor)); + ma_uint64 iFrame; + + if (channels == 2) { + /* TODO: Do an optimized implementation for stereo and mono. Can do a SIMD optimized implementation as well. */ + } + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + pFramesOut[iFrame * channels + iChannel] = pFramesIn[iFrame * channels + iChannel] * pChannelGains[iChannel]; + } + } } -MA_API float ma_gain_db_to_factor(float gain) + + +static MA_INLINE ma_int16 ma_apply_volume_unclipped_u8(ma_int16 x, ma_int16 volume) { - return (float)ma_powf(10, gain/20.0f); + return (ma_int16)(((ma_int32)x * (ma_int32)volume) >> 8); } +static MA_INLINE ma_int32 ma_apply_volume_unclipped_s16(ma_int32 x, ma_int16 volume) +{ + return (ma_int32)((x * volume) >> 8); +} + +static MA_INLINE ma_int64 ma_apply_volume_unclipped_s24(ma_int64 x, ma_int16 volume) +{ + return (ma_int64)((x * volume) >> 8); +} + +static MA_INLINE ma_int64 ma_apply_volume_unclipped_s32(ma_int64 x, ma_int16 volume) +{ + return (ma_int64)((x * volume) >> 8); +} + +static MA_INLINE float ma_apply_volume_unclipped_f32(float x, float volume) +{ + return x * volume; +} + + +MA_API void ma_copy_and_apply_volume_and_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count, float volume) +{ + ma_uint64 iSample; + ma_int16 volumeFixed; + + MA_ASSERT(pDst != NULL); + MA_ASSERT(pSrc != NULL); + + volumeFixed = ma_float_to_fixed_16(volume); + + for (iSample = 0; iSample < count; iSample += 1) { + pDst[iSample] = ma_clip_u8(ma_apply_volume_unclipped_u8(pSrc[iSample], volumeFixed)); + } +} + +MA_API void ma_copy_and_apply_volume_and_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count, float volume) +{ + ma_uint64 iSample; + ma_int16 volumeFixed; + + MA_ASSERT(pDst != NULL); + MA_ASSERT(pSrc != NULL); + + volumeFixed = ma_float_to_fixed_16(volume); + + for (iSample = 0; iSample < count; iSample += 1) { + pDst[iSample] = ma_clip_s16(ma_apply_volume_unclipped_s16(pSrc[iSample], volumeFixed)); + } +} + +MA_API void ma_copy_and_apply_volume_and_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count, float volume) +{ + ma_uint64 iSample; + ma_int16 volumeFixed; + + MA_ASSERT(pDst != NULL); + MA_ASSERT(pSrc != NULL); + + volumeFixed = ma_float_to_fixed_16(volume); + + for (iSample = 0; iSample < count; iSample += 1) { + ma_int64 s = ma_clip_s24(ma_apply_volume_unclipped_s24(pSrc[iSample], volumeFixed)); + pDst[iSample*3 + 0] = (ma_uint8)((s & 0x000000FF) >> 0); + pDst[iSample*3 + 1] = (ma_uint8)((s & 0x0000FF00) >> 8); + pDst[iSample*3 + 2] = (ma_uint8)((s & 0x00FF0000) >> 16); + } +} + +MA_API void ma_copy_and_apply_volume_and_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count, float volume) +{ + ma_uint64 iSample; + ma_int16 volumeFixed; + + MA_ASSERT(pDst != NULL); + MA_ASSERT(pSrc != NULL); + + volumeFixed = ma_float_to_fixed_16(volume); + + for (iSample = 0; iSample < count; iSample += 1) { + pDst[iSample] = ma_clip_s32(ma_apply_volume_unclipped_s32(pSrc[iSample], volumeFixed)); + } +} + +MA_API void ma_copy_and_apply_volume_and_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count, float volume) +{ + ma_uint64 iSample; + + MA_ASSERT(pDst != NULL); + MA_ASSERT(pSrc != NULL); + + /* For the f32 case we need to make sure this supports in-place processing where the input and output buffers are the same. */ + + for (iSample = 0; iSample < count; iSample += 1) { + pDst[iSample] = ma_clip_f32(ma_apply_volume_unclipped_f32(pSrc[iSample], volume)); + } +} + +MA_API void ma_copy_and_apply_volume_and_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float volume) +{ + MA_ASSERT(pDst != NULL); + MA_ASSERT(pSrc != NULL); + + if (volume == 1) { + ma_clip_pcm_frames(pDst, pSrc, frameCount, format, channels); /* Optimized case for volume = 1. */ + } else if (volume == 0) { + ma_silence_pcm_frames(pDst, frameCount, format, channels); /* Optimized case for volume = 0. */ + } else { + ma_uint64 sampleCount = frameCount * channels; + + switch (format) { + case ma_format_u8: ma_copy_and_apply_volume_and_clip_samples_u8( (ma_uint8*)pDst, (const ma_int16*)pSrc, sampleCount, volume); break; + case ma_format_s16: ma_copy_and_apply_volume_and_clip_samples_s16((ma_int16*)pDst, (const ma_int32*)pSrc, sampleCount, volume); break; + case ma_format_s24: ma_copy_and_apply_volume_and_clip_samples_s24((ma_uint8*)pDst, (const ma_int64*)pSrc, sampleCount, volume); break; + case ma_format_s32: ma_copy_and_apply_volume_and_clip_samples_s32((ma_int32*)pDst, (const ma_int64*)pSrc, sampleCount, volume); break; + case ma_format_f32: ma_copy_and_apply_volume_and_clip_samples_f32(( float*)pDst, (const float*)pSrc, sampleCount, volume); break; + + /* Do nothing if we don't know the format. We're including these here to silence a compiler warning about enums not being handled by the switch. */ + case ma_format_unknown: + case ma_format_count: + break; + } + } +} + + + +MA_API float ma_volume_linear_to_db(float factor) +{ + return 20*ma_log10f(factor); +} + +MA_API float ma_volume_db_to_linear(float gain) +{ + return ma_powf(10, gain/20.0f); +} + + /************************************************************************************************************************************************************** @@ -34580,33 +41162,6 @@ static MA_INLINE void ma_pcm_sample_s32_to_s24_no_scale(ma_int64 x, ma_uint8* s2 } -static MA_INLINE ma_uint8 ma_clip_u8(ma_int16 x) -{ - return (ma_uint8)(ma_clamp(x, -128, 127) + 128); -} - -static MA_INLINE ma_int16 ma_clip_s16(ma_int32 x) -{ - return (ma_int16)ma_clamp(x, -32768, 32767); -} - -static MA_INLINE ma_int64 ma_clip_s24(ma_int64 x) -{ - return (ma_int64)ma_clamp(x, -8388608, 8388607); -} - -static MA_INLINE ma_int32 ma_clip_s32(ma_int64 x) -{ - /* This dance is to silence warnings with -std=c89. A good compiler should be able to optimize this away. */ - ma_int64 clipMin; - ma_int64 clipMax; - clipMin = -((ma_int64)2147483647 + 1); - clipMax = (ma_int64)2147483647; - - return (ma_int32)ma_clamp(x, clipMin, clipMax); -} - - /* u8 */ MA_API void ma_pcm_u8_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { @@ -36985,25 +43540,131 @@ MA_API ma_biquad_config ma_biquad_config_init(ma_format format, ma_uint32 channe return config; } -MA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, ma_biquad* pBQ) + +typedef struct { + size_t sizeInBytes; + size_t r1Offset; + size_t r2Offset; +} ma_biquad_heap_layout; + +static ma_result ma_biquad_get_heap_layout(const ma_biquad_config* pConfig, ma_biquad_heap_layout* pHeapLayout) +{ + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->channels == 0) { + return MA_INVALID_ARGS; + } + + pHeapLayout->sizeInBytes = 0; + + /* R0 */ + pHeapLayout->r1Offset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; + + /* R1 */ + pHeapLayout->r2Offset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; + + /* Make sure allocation size is aligned. */ + pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); + + return MA_SUCCESS; +} + +MA_API ma_result ma_biquad_get_heap_size(const ma_biquad_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_biquad_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_biquad_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return MA_SUCCESS; +} + +MA_API ma_result ma_biquad_init_preallocated(const ma_biquad_config* pConfig, void* pHeap, ma_biquad* pBQ) +{ + ma_result result; + ma_biquad_heap_layout heapLayout; + if (pBQ == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pBQ); - if (pConfig == NULL) { - return MA_INVALID_ARGS; + result = ma_biquad_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; } - if (pConfig->channels < MA_MIN_CHANNELS || pConfig->channels > MA_MAX_CHANNELS) { - return MA_INVALID_ARGS; - } + pBQ->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pBQ->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset); + pBQ->pR2 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r2Offset); return ma_biquad_reinit(pConfig, pBQ); } +MA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad* pBQ) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_biquad_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_biquad_init_preallocated(pConfig, pHeap, pBQ); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pBQ->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_biquad_uninit(ma_biquad* pBQ, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pBQ == NULL) { + return; + } + + if (pBQ->_ownsHeap) { + ma_free(pBQ->_pHeap, pAllocationCallbacks); + } +} + MA_API ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ) { if (pBQ == NULL || pConfig == NULL) { @@ -37051,6 +43712,23 @@ MA_API ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pB return MA_SUCCESS; } +MA_API ma_result ma_biquad_clear_cache(ma_biquad* pBQ) +{ + if (pBQ == NULL) { + return MA_INVALID_ARGS; + } + + if (pBQ->format == ma_format_f32) { + pBQ->pR1->f32 = 0; + pBQ->pR2->f32 = 0; + } else { + pBQ->pR1->s32 = 0; + pBQ->pR2->s32 = 0; + } + + return MA_SUCCESS; +} + static MA_INLINE void ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(ma_biquad* pBQ, float* pY, const float* pX) { ma_uint32 c; @@ -37061,10 +43739,10 @@ static MA_INLINE void ma_biquad_process_pcm_frame_f32__direct_form_2_transposed( const float a1 = pBQ->a1.f32; const float a2 = pBQ->a2.f32; - MA_ASSUME(channels >= MA_MIN_CHANNELS && channels <= MA_MAX_CHANNELS); + MA_ASSUME(channels > 0); for (c = 0; c < channels; c += 1) { - float r1 = pBQ->r1[c].f32; - float r2 = pBQ->r2[c].f32; + float r1 = pBQ->pR1[c].f32; + float r2 = pBQ->pR2[c].f32; float x = pX[c]; float y; @@ -37072,9 +43750,9 @@ static MA_INLINE void ma_biquad_process_pcm_frame_f32__direct_form_2_transposed( r1 = b1*x - a1*y + r2; r2 = b2*x - a2*y; - pY[c] = y; - pBQ->r1[c].f32 = r1; - pBQ->r2[c].f32 = r2; + pY[c] = y; + pBQ->pR1[c].f32 = r1; + pBQ->pR2[c].f32 = r2; } } @@ -37093,10 +43771,10 @@ static MA_INLINE void ma_biquad_process_pcm_frame_s16__direct_form_2_transposed( const ma_int32 a1 = pBQ->a1.s32; const ma_int32 a2 = pBQ->a2.s32; - MA_ASSUME(channels >= MA_MIN_CHANNELS && channels <= MA_MAX_CHANNELS); + MA_ASSUME(channels > 0); for (c = 0; c < channels; c += 1) { - ma_int32 r1 = pBQ->r1[c].s32; - ma_int32 r2 = pBQ->r2[c].s32; + ma_int32 r1 = pBQ->pR1[c].s32; + ma_int32 r2 = pBQ->pR2[c].s32; ma_int32 x = pX[c]; ma_int32 y; @@ -37104,9 +43782,9 @@ static MA_INLINE void ma_biquad_process_pcm_frame_s16__direct_form_2_transposed( r1 = (b1*x - a1*y + r2); r2 = (b2*x - a2*y); - pY[c] = (ma_int16)ma_clamp(y, -32768, 32767); - pBQ->r1[c].s32 = r1; - pBQ->r2[c].s32 = r2; + pY[c] = (ma_int16)ma_clamp(y, -32768, 32767); + pBQ->pR1[c].s32 = r1; + pBQ->pR2[c].s32 = r2; } } @@ -37200,25 +43878,122 @@ MA_API ma_lpf2_config ma_lpf2_config_init(ma_format format, ma_uint32 channels, } -MA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, ma_lpf1* pLPF) +typedef struct { + size_t sizeInBytes; + size_t r1Offset; +} ma_lpf1_heap_layout; + +static ma_result ma_lpf1_get_heap_layout(const ma_lpf1_config* pConfig, ma_lpf1_heap_layout* pHeapLayout) +{ + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->channels == 0) { + return MA_INVALID_ARGS; + } + + pHeapLayout->sizeInBytes = 0; + + /* R1 */ + pHeapLayout->r1Offset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; + + /* Make sure allocation size is aligned. */ + pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); + + return MA_SUCCESS; +} + +MA_API ma_result ma_lpf1_get_heap_size(const ma_lpf1_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_lpf1_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + result = ma_lpf1_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return MA_SUCCESS; +} + +MA_API ma_result ma_lpf1_init_preallocated(const ma_lpf1_config* pConfig, void* pHeap, ma_lpf1* pLPF) +{ + ma_result result; + ma_lpf1_heap_layout heapLayout; + if (pLPF == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pLPF); - if (pConfig == NULL) { - return MA_INVALID_ARGS; + result = ma_lpf1_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; } - if (pConfig->channels < MA_MIN_CHANNELS || pConfig->channels > MA_MAX_CHANNELS) { - return MA_INVALID_ARGS; - } + pLPF->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pLPF->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset); return ma_lpf1_reinit(pConfig, pLPF); } +MA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf1* pLPF) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_lpf1_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_lpf1_init_preallocated(pConfig, pHeap, pLPF); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pLPF->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_lpf1_uninit(ma_lpf1* pLPF, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pLPF == NULL) { + return; + } + + if (pLPF->_ownsHeap) { + ma_free(pLPF->_pHeap, pAllocationCallbacks); + } +} + MA_API ma_result ma_lpf1_reinit(const ma_lpf1_config* pConfig, ma_lpf1* pLPF) { double a; @@ -37255,6 +44030,21 @@ MA_API ma_result ma_lpf1_reinit(const ma_lpf1_config* pConfig, ma_lpf1* pLPF) return MA_SUCCESS; } +MA_API ma_result ma_lpf1_clear_cache(ma_lpf1* pLPF) +{ + if (pLPF == NULL) { + return MA_INVALID_ARGS; + } + + if (pLPF->format == ma_format_f32) { + pLPF->a.f32 = 0; + } else { + pLPF->a.s32 = 0; + } + + return MA_SUCCESS; +} + static MA_INLINE void ma_lpf1_process_pcm_frame_f32(ma_lpf1* pLPF, float* pY, const float* pX) { ma_uint32 c; @@ -37262,16 +44052,16 @@ static MA_INLINE void ma_lpf1_process_pcm_frame_f32(ma_lpf1* pLPF, float* pY, co const float a = pLPF->a.f32; const float b = 1 - a; - MA_ASSUME(channels >= MA_MIN_CHANNELS && channels <= MA_MAX_CHANNELS); + MA_ASSUME(channels > 0); for (c = 0; c < channels; c += 1) { - float r1 = pLPF->r1[c].f32; + float r1 = pLPF->pR1[c].f32; float x = pX[c]; float y; y = b*x + a*r1; pY[c] = y; - pLPF->r1[c].f32 = y; + pLPF->pR1[c].f32 = y; } } @@ -37282,16 +44072,16 @@ static MA_INLINE void ma_lpf1_process_pcm_frame_s16(ma_lpf1* pLPF, ma_int16* pY, const ma_int32 a = pLPF->a.s32; const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a); - MA_ASSUME(channels >= MA_MIN_CHANNELS && channels <= MA_MAX_CHANNELS); + MA_ASSUME(channels > 0); for (c = 0; c < channels; c += 1) { - ma_int32 r1 = pLPF->r1[c].s32; + ma_int32 r1 = pLPF->pR1[c].s32; ma_int32 x = pX[c]; ma_int32 y; y = (b*x + a*r1) >> MA_BIQUAD_FIXED_POINT_SHIFT; - pY[c] = (ma_int16)y; - pLPF->r1[c].s32 = (ma_int32)y; + pY[c] = (ma_int16)y; + pLPF->pR1[c].s32 = (ma_int32)y; } } @@ -37371,7 +44161,15 @@ static MA_INLINE ma_biquad_config ma_lpf2__get_biquad_config(const ma_lpf2_confi return bqConfig; } -MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, ma_lpf2* pLPF) +MA_API ma_result ma_lpf2_get_heap_size(const ma_lpf2_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_biquad_config bqConfig; + bqConfig = ma_lpf2__get_biquad_config(pConfig); + + return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); +} + +MA_API ma_result ma_lpf2_init_preallocated(const ma_lpf2_config* pConfig, void* pHeap, ma_lpf2* pLPF) { ma_result result; ma_biquad_config bqConfig; @@ -37387,7 +44185,7 @@ MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, ma_lpf2* pLPF) } bqConfig = ma_lpf2__get_biquad_config(pConfig); - result = ma_biquad_init(&bqConfig, &pLPF->bq); + result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pLPF->bq); if (result != MA_SUCCESS) { return result; } @@ -37395,6 +44193,45 @@ MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, ma_lpf2* pLPF) return MA_SUCCESS; } +MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf2* pLPF) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_lpf2_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_lpf2_init_preallocated(pConfig, pHeap, pLPF); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pLPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ + return MA_SUCCESS; +} + +MA_API void ma_lpf2_uninit(ma_lpf2* pLPF, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pLPF == NULL) { + return; + } + + ma_biquad_uninit(&pLPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ +} + MA_API ma_result ma_lpf2_reinit(const ma_lpf2_config* pConfig, ma_lpf2* pLPF) { ma_result result; @@ -37413,6 +44250,17 @@ MA_API ma_result ma_lpf2_reinit(const ma_lpf2_config* pConfig, ma_lpf2* pLPF) return MA_SUCCESS; } +MA_API ma_result ma_lpf2_clear_cache(ma_lpf2* pLPF) +{ + if (pLPF == NULL) { + return MA_INVALID_ARGS; + } + + ma_biquad_clear_cache(&pLPF->bq); + + return MA_SUCCESS; +} + static MA_INLINE void ma_lpf2_process_pcm_frame_s16(ma_lpf2* pLPF, ma_int16* pFrameOut, const ma_int16* pFrameIn) { ma_biquad_process_pcm_frame_s16(&pLPF->bq, pFrameOut, pFrameIn); @@ -37456,7 +44304,24 @@ MA_API ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma return config; } -static ma_result ma_lpf_reinit__internal(const ma_lpf_config* pConfig, ma_lpf* pLPF, ma_bool32 isNew) + +typedef struct +{ + size_t sizeInBytes; + size_t lpf1Offset; + size_t lpf2Offset; /* Offset of the first second order filter. Subsequent filters will come straight after, and will each have the same heap size. */ +} ma_lpf_heap_layout; + +static void ma_lpf_calculate_sub_lpf_counts(ma_uint32 order, ma_uint32* pLPF1Count, ma_uint32* pLPF2Count) +{ + MA_ASSERT(pLPF1Count != NULL); + MA_ASSERT(pLPF2Count != NULL); + + *pLPF1Count = order % 2; + *pLPF2Count = order / 2; +} + +static ma_result ma_lpf_get_heap_layout(const ma_lpf_config* pConfig, ma_lpf_heap_layout* pHeapLayout) { ma_result result; ma_uint32 lpf1Count; @@ -37464,6 +44329,69 @@ static ma_result ma_lpf_reinit__internal(const ma_lpf_config* pConfig, ma_lpf* p ma_uint32 ilpf1; ma_uint32 ilpf2; + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->channels == 0) { + return MA_INVALID_ARGS; + } + + if (pConfig->order > MA_MAX_FILTER_ORDER) { + return MA_INVALID_ARGS; + } + + ma_lpf_calculate_sub_lpf_counts(pConfig->order, &lpf1Count, &lpf2Count); + + pHeapLayout->sizeInBytes = 0; + + /* LPF 1 */ + pHeapLayout->lpf1Offset = pHeapLayout->sizeInBytes; + for (ilpf1 = 0; ilpf1 < lpf1Count; ilpf1 += 1) { + size_t lpf1HeapSizeInBytes; + ma_lpf1_config lpf1Config = ma_lpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); + + result = ma_lpf1_get_heap_size(&lpf1Config, &lpf1HeapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->sizeInBytes += sizeof(ma_lpf1) + lpf1HeapSizeInBytes; + } + + /* LPF 2*/ + pHeapLayout->lpf2Offset = pHeapLayout->sizeInBytes; + for (ilpf2 = 0; ilpf2 < lpf2Count; ilpf2 += 1) { + size_t lpf2HeapSizeInBytes; + ma_lpf2_config lpf2Config = ma_lpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, 0.707107); /* <-- The "q" parameter does not matter for the purpose of calculating the heap size. */ + + result = ma_lpf2_get_heap_size(&lpf2Config, &lpf2HeapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->sizeInBytes += sizeof(ma_lpf2) + lpf2HeapSizeInBytes; + } + + /* Make sure allocation size is aligned. */ + pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); + + return MA_SUCCESS; +} + +static ma_result ma_lpf_reinit__internal(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF, ma_bool32 isNew) +{ + ma_result result; + ma_uint32 lpf1Count; + ma_uint32 lpf2Count; + ma_uint32 ilpf1; + ma_uint32 ilpf2; + ma_lpf_heap_layout heapLayout; /* Only used if isNew is true. */ + if (pLPF == NULL || pConfig == NULL) { return MA_INVALID_ARGS; } @@ -37487,11 +44415,7 @@ static ma_result ma_lpf_reinit__internal(const ma_lpf_config* pConfig, ma_lpf* p return MA_INVALID_ARGS; } - lpf1Count = pConfig->order % 2; - lpf2Count = pConfig->order / 2; - - MA_ASSERT(lpf1Count <= ma_countof(pLPF->lpf1)); - MA_ASSERT(lpf2Count <= ma_countof(pLPF->lpf2)); + ma_lpf_calculate_sub_lpf_counts(pConfig->order, &lpf1Count, &lpf2Count); /* The filter order can't change between reinits. */ if (!isNew) { @@ -37500,16 +44424,42 @@ static ma_result ma_lpf_reinit__internal(const ma_lpf_config* pConfig, ma_lpf* p } } + if (isNew) { + result = ma_lpf_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pLPF->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pLPF->pLPF1 = (ma_lpf1*)ma_offset_ptr(pHeap, heapLayout.lpf1Offset); + pLPF->pLPF2 = (ma_lpf2*)ma_offset_ptr(pHeap, heapLayout.lpf2Offset); + } else { + MA_ZERO_OBJECT(&heapLayout); /* To silence a compiler warning. */ + } + for (ilpf1 = 0; ilpf1 < lpf1Count; ilpf1 += 1) { ma_lpf1_config lpf1Config = ma_lpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); if (isNew) { - result = ma_lpf1_init(&lpf1Config, &pLPF->lpf1[ilpf1]); + size_t lpf1HeapSizeInBytes; + + result = ma_lpf1_get_heap_size(&lpf1Config, &lpf1HeapSizeInBytes); + if (result == MA_SUCCESS) { + result = ma_lpf1_init_preallocated(&lpf1Config, ma_offset_ptr(pHeap, heapLayout.lpf1Offset + (sizeof(ma_lpf1) * lpf1Count) + (ilpf1 * lpf1HeapSizeInBytes)), &pLPF->pLPF1[ilpf1]); + } } else { - result = ma_lpf1_reinit(&lpf1Config, &pLPF->lpf1[ilpf1]); + result = ma_lpf1_reinit(&lpf1Config, &pLPF->pLPF1[ilpf1]); } if (result != MA_SUCCESS) { + ma_uint32 jlpf1; + + for (jlpf1 = 0; jlpf1 < ilpf1; jlpf1 += 1) { + ma_lpf1_uninit(&pLPF->pLPF1[jlpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ + } + return result; } } @@ -37530,12 +44480,28 @@ static ma_result ma_lpf_reinit__internal(const ma_lpf_config* pConfig, ma_lpf* p lpf2Config = ma_lpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q); if (isNew) { - result = ma_lpf2_init(&lpf2Config, &pLPF->lpf2[ilpf2]); + size_t lpf2HeapSizeInBytes; + + result = ma_lpf2_get_heap_size(&lpf2Config, &lpf2HeapSizeInBytes); + if (result == MA_SUCCESS) { + result = ma_lpf2_init_preallocated(&lpf2Config, ma_offset_ptr(pHeap, heapLayout.lpf2Offset + (sizeof(ma_lpf2) * lpf2Count) + (ilpf2 * lpf2HeapSizeInBytes)), &pLPF->pLPF2[ilpf2]); + } } else { - result = ma_lpf2_reinit(&lpf2Config, &pLPF->lpf2[ilpf2]); + result = ma_lpf2_reinit(&lpf2Config, &pLPF->pLPF2[ilpf2]); } if (result != MA_SUCCESS) { + ma_uint32 jlpf1; + ma_uint32 jlpf2; + + for (jlpf1 = 0; jlpf1 < lpf1Count; jlpf1 += 1) { + ma_lpf1_uninit(&pLPF->pLPF1[jlpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ + } + + for (jlpf2 = 0; jlpf2 < ilpf2; jlpf2 += 1) { + ma_lpf2_uninit(&pLPF->pLPF2[jlpf2], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ + } + return result; } } @@ -37549,7 +44515,28 @@ static ma_result ma_lpf_reinit__internal(const ma_lpf_config* pConfig, ma_lpf* p return MA_SUCCESS; } -MA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, ma_lpf* pLPF) +MA_API ma_result ma_lpf_get_heap_size(const ma_lpf_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_lpf_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_lpf_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return result; +} + +MA_API ma_result ma_lpf_init_preallocated(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF) { if (pLPF == NULL) { return MA_INVALID_ARGS; @@ -37557,16 +44544,84 @@ MA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, ma_lpf* pLPF) MA_ZERO_OBJECT(pLPF); - if (pConfig == NULL) { - return MA_INVALID_ARGS; + return ma_lpf_reinit__internal(pConfig, pHeap, pLPF, /*isNew*/MA_TRUE); +} + +MA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf* pLPF) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_lpf_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; } - return ma_lpf_reinit__internal(pConfig, pLPF, /*isNew*/MA_TRUE); + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_lpf_init_preallocated(pConfig, pHeap, pLPF); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pLPF->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_lpf_uninit(ma_lpf* pLPF, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_uint32 ilpf1; + ma_uint32 ilpf2; + + if (pLPF == NULL) { + return; + } + + for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { + ma_lpf1_uninit(&pLPF->pLPF1[ilpf1], pAllocationCallbacks); + } + + for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { + ma_lpf2_uninit(&pLPF->pLPF2[ilpf2], pAllocationCallbacks); + } + + if (pLPF->_ownsHeap) { + ma_free(pLPF->_pHeap, pAllocationCallbacks); + } } MA_API ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF) { - return ma_lpf_reinit__internal(pConfig, pLPF, /*isNew*/MA_FALSE); + return ma_lpf_reinit__internal(pConfig, NULL, pLPF, /*isNew*/MA_FALSE); +} + +MA_API ma_result ma_lpf_clear_cache(ma_lpf* pLPF) +{ + ma_uint32 ilpf1; + ma_uint32 ilpf2; + + if (pLPF == NULL) { + return MA_INVALID_ARGS; + } + + for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { + ma_lpf1_clear_cache(&pLPF->pLPF1[ilpf1]); + } + + for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { + ma_lpf2_clear_cache(&pLPF->pLPF2[ilpf2]); + } + + return MA_SUCCESS; } static MA_INLINE void ma_lpf_process_pcm_frame_f32(ma_lpf* pLPF, float* pY, const void* pX) @@ -37579,11 +44634,11 @@ static MA_INLINE void ma_lpf_process_pcm_frame_f32(ma_lpf* pLPF, float* pY, cons MA_COPY_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels)); for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { - ma_lpf1_process_pcm_frame_f32(&pLPF->lpf1[ilpf1], pY, pY); + ma_lpf1_process_pcm_frame_f32(&pLPF->pLPF1[ilpf1], pY, pY); } for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { - ma_lpf2_process_pcm_frame_f32(&pLPF->lpf2[ilpf2], pY, pY); + ma_lpf2_process_pcm_frame_f32(&pLPF->pLPF2[ilpf2], pY, pY); } } @@ -37597,11 +44652,11 @@ static MA_INLINE void ma_lpf_process_pcm_frame_s16(ma_lpf* pLPF, ma_int16* pY, c MA_COPY_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels)); for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { - ma_lpf1_process_pcm_frame_s16(&pLPF->lpf1[ilpf1], pY, pY); + ma_lpf1_process_pcm_frame_s16(&pLPF->pLPF1[ilpf1], pY, pY); } for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { - ma_lpf2_process_pcm_frame_s16(&pLPF->lpf2[ilpf2], pY, pY); + ma_lpf2_process_pcm_frame_s16(&pLPF->pLPF2[ilpf2], pY, pY); } } @@ -37618,14 +44673,14 @@ MA_API ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const /* Faster path for in-place. */ if (pFramesOut == pFramesIn) { for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { - result = ma_lpf1_process_pcm_frames(&pLPF->lpf1[ilpf1], pFramesOut, pFramesOut, frameCount); + result = ma_lpf1_process_pcm_frames(&pLPF->pLPF1[ilpf1], pFramesOut, pFramesOut, frameCount); if (result != MA_SUCCESS) { return result; } } for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { - result = ma_lpf2_process_pcm_frames(&pLPF->lpf2[ilpf2], pFramesOut, pFramesOut, frameCount); + result = ma_lpf2_process_pcm_frames(&pLPF->pLPF2[ilpf2], pFramesOut, pFramesOut, frameCount); if (result != MA_SUCCESS) { return result; } @@ -37711,23 +44766,120 @@ MA_API ma_hpf2_config ma_hpf2_config_init(ma_format format, ma_uint32 channels, } -MA_API ma_result ma_hpf1_init(const ma_hpf1_config* pConfig, ma_hpf1* pHPF) +typedef struct { - if (pHPF == NULL) { - return MA_INVALID_ARGS; - } + size_t sizeInBytes; + size_t r1Offset; +} ma_hpf1_heap_layout; - MA_ZERO_OBJECT(pHPF); +static ma_result ma_hpf1_get_heap_layout(const ma_hpf1_config* pConfig, ma_hpf1_heap_layout* pHeapLayout) +{ + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); if (pConfig == NULL) { return MA_INVALID_ARGS; } - if (pConfig->channels < MA_MIN_CHANNELS || pConfig->channels > MA_MAX_CHANNELS) { + if (pConfig->channels == 0) { return MA_INVALID_ARGS; } - return ma_hpf1_reinit(pConfig, pHPF); + pHeapLayout->sizeInBytes = 0; + + /* R1 */ + pHeapLayout->r1Offset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; + + /* Make sure allocation size is aligned. */ + pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); + + return MA_SUCCESS; +} + +MA_API ma_result ma_hpf1_get_heap_size(const ma_hpf1_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_hpf1_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + result = ma_hpf1_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return MA_SUCCESS; +} + +MA_API ma_result ma_hpf1_init_preallocated(const ma_hpf1_config* pConfig, void* pHeap, ma_hpf1* pLPF) +{ + ma_result result; + ma_hpf1_heap_layout heapLayout; + + if (pLPF == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pLPF); + + result = ma_hpf1_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pLPF->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pLPF->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset); + + return ma_hpf1_reinit(pConfig, pLPF); +} + +MA_API ma_result ma_hpf1_init(const ma_hpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf1* pLPF) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_hpf1_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_hpf1_init_preallocated(pConfig, pHeap, pLPF); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pLPF->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_hpf1_uninit(ma_hpf1* pHPF, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pHPF == NULL) { + return; + } + + if (pHPF->_ownsHeap) { + ma_free(pHPF->_pHeap, pAllocationCallbacks); + } } MA_API ma_result ma_hpf1_reinit(const ma_hpf1_config* pConfig, ma_hpf1* pHPF) @@ -37773,16 +44925,16 @@ static MA_INLINE void ma_hpf1_process_pcm_frame_f32(ma_hpf1* pHPF, float* pY, co const float a = 1 - pHPF->a.f32; const float b = 1 - a; - MA_ASSUME(channels >= MA_MIN_CHANNELS && channels <= MA_MAX_CHANNELS); + MA_ASSUME(channels > 0); for (c = 0; c < channels; c += 1) { - float r1 = pHPF->r1[c].f32; + float r1 = pHPF->pR1[c].f32; float x = pX[c]; float y; y = b*x - a*r1; - pY[c] = y; - pHPF->r1[c].f32 = y; + pY[c] = y; + pHPF->pR1[c].f32 = y; } } @@ -37793,16 +44945,16 @@ static MA_INLINE void ma_hpf1_process_pcm_frame_s16(ma_hpf1* pHPF, ma_int16* pY, const ma_int32 a = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - pHPF->a.s32); const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a); - MA_ASSUME(channels >= MA_MIN_CHANNELS && channels <= MA_MAX_CHANNELS); + MA_ASSUME(channels > 0); for (c = 0; c < channels; c += 1) { - ma_int32 r1 = pHPF->r1[c].s32; + ma_int32 r1 = pHPF->pR1[c].s32; ma_int32 x = pX[c]; ma_int32 y; y = (b*x - a*r1) >> MA_BIQUAD_FIXED_POINT_SHIFT; - pY[c] = (ma_int16)y; - pHPF->r1[c].s32 = (ma_int32)y; + pY[c] = (ma_int16)y; + pHPF->pR1[c].s32 = (ma_int32)y; } } @@ -37882,7 +45034,15 @@ static MA_INLINE ma_biquad_config ma_hpf2__get_biquad_config(const ma_hpf2_confi return bqConfig; } -MA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, ma_hpf2* pHPF) +MA_API ma_result ma_hpf2_get_heap_size(const ma_hpf2_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_biquad_config bqConfig; + bqConfig = ma_hpf2__get_biquad_config(pConfig); + + return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); +} + +MA_API ma_result ma_hpf2_init_preallocated(const ma_hpf2_config* pConfig, void* pHeap, ma_hpf2* pHPF) { ma_result result; ma_biquad_config bqConfig; @@ -37898,7 +45058,7 @@ MA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, ma_hpf2* pHPF) } bqConfig = ma_hpf2__get_biquad_config(pConfig); - result = ma_biquad_init(&bqConfig, &pHPF->bq); + result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pHPF->bq); if (result != MA_SUCCESS) { return result; } @@ -37906,6 +45066,45 @@ MA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, ma_hpf2* pHPF) return MA_SUCCESS; } +MA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf2* pHPF) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_hpf2_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_hpf2_init_preallocated(pConfig, pHeap, pHPF); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pHPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ + return MA_SUCCESS; +} + +MA_API void ma_hpf2_uninit(ma_hpf2* pHPF, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pHPF == NULL) { + return; + } + + ma_biquad_uninit(&pHPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ +} + MA_API ma_result ma_hpf2_reinit(const ma_hpf2_config* pConfig, ma_hpf2* pHPF) { ma_result result; @@ -37967,7 +45166,24 @@ MA_API ma_hpf_config ma_hpf_config_init(ma_format format, ma_uint32 channels, ma return config; } -static ma_result ma_hpf_reinit__internal(const ma_hpf_config* pConfig, ma_hpf* pHPF, ma_bool32 isNew) + +typedef struct +{ + size_t sizeInBytes; + size_t hpf1Offset; + size_t hpf2Offset; /* Offset of the first second order filter. Subsequent filters will come straight after, and will each have the same heap size. */ +} ma_hpf_heap_layout; + +static void ma_hpf_calculate_sub_hpf_counts(ma_uint32 order, ma_uint32* pHPF1Count, ma_uint32* pHPF2Count) +{ + MA_ASSERT(pHPF1Count != NULL); + MA_ASSERT(pHPF2Count != NULL); + + *pHPF1Count = order % 2; + *pHPF2Count = order / 2; +} + +static ma_result ma_hpf_get_heap_layout(const ma_hpf_config* pConfig, ma_hpf_heap_layout* pHeapLayout) { ma_result result; ma_uint32 hpf1Count; @@ -37975,6 +45191,69 @@ static ma_result ma_hpf_reinit__internal(const ma_hpf_config* pConfig, ma_hpf* p ma_uint32 ihpf1; ma_uint32 ihpf2; + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->channels == 0) { + return MA_INVALID_ARGS; + } + + if (pConfig->order > MA_MAX_FILTER_ORDER) { + return MA_INVALID_ARGS; + } + + ma_hpf_calculate_sub_hpf_counts(pConfig->order, &hpf1Count, &hpf2Count); + + pHeapLayout->sizeInBytes = 0; + + /* HPF 1 */ + pHeapLayout->hpf1Offset = pHeapLayout->sizeInBytes; + for (ihpf1 = 0; ihpf1 < hpf1Count; ihpf1 += 1) { + size_t hpf1HeapSizeInBytes; + ma_hpf1_config hpf1Config = ma_hpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); + + result = ma_hpf1_get_heap_size(&hpf1Config, &hpf1HeapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->sizeInBytes += sizeof(ma_hpf1) + hpf1HeapSizeInBytes; + } + + /* HPF 2*/ + pHeapLayout->hpf2Offset = pHeapLayout->sizeInBytes; + for (ihpf2 = 0; ihpf2 < hpf2Count; ihpf2 += 1) { + size_t hpf2HeapSizeInBytes; + ma_hpf2_config hpf2Config = ma_hpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, 0.707107); /* <-- The "q" parameter does not matter for the purpose of calculating the heap size. */ + + result = ma_hpf2_get_heap_size(&hpf2Config, &hpf2HeapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->sizeInBytes += sizeof(ma_hpf2) + hpf2HeapSizeInBytes; + } + + /* Make sure allocation size is aligned. */ + pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); + + return MA_SUCCESS; +} + +static ma_result ma_hpf_reinit__internal(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pHPF, ma_bool32 isNew) +{ + ma_result result; + ma_uint32 hpf1Count; + ma_uint32 hpf2Count; + ma_uint32 ihpf1; + ma_uint32 ihpf2; + ma_hpf_heap_layout heapLayout; /* Only used if isNew is true. */ + if (pHPF == NULL || pConfig == NULL) { return MA_INVALID_ARGS; } @@ -37998,11 +45277,7 @@ static ma_result ma_hpf_reinit__internal(const ma_hpf_config* pConfig, ma_hpf* p return MA_INVALID_ARGS; } - hpf1Count = pConfig->order % 2; - hpf2Count = pConfig->order / 2; - - MA_ASSERT(hpf1Count <= ma_countof(pHPF->hpf1)); - MA_ASSERT(hpf2Count <= ma_countof(pHPF->hpf2)); + ma_hpf_calculate_sub_hpf_counts(pConfig->order, &hpf1Count, &hpf2Count); /* The filter order can't change between reinits. */ if (!isNew) { @@ -38011,16 +45286,42 @@ static ma_result ma_hpf_reinit__internal(const ma_hpf_config* pConfig, ma_hpf* p } } + if (isNew) { + result = ma_hpf_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pHPF->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pHPF->pHPF1 = (ma_hpf1*)ma_offset_ptr(pHeap, heapLayout.hpf1Offset); + pHPF->pHPF2 = (ma_hpf2*)ma_offset_ptr(pHeap, heapLayout.hpf2Offset); + } else { + MA_ZERO_OBJECT(&heapLayout); /* To silence a compiler warning. */ + } + for (ihpf1 = 0; ihpf1 < hpf1Count; ihpf1 += 1) { ma_hpf1_config hpf1Config = ma_hpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); if (isNew) { - result = ma_hpf1_init(&hpf1Config, &pHPF->hpf1[ihpf1]); + size_t hpf1HeapSizeInBytes; + + result = ma_hpf1_get_heap_size(&hpf1Config, &hpf1HeapSizeInBytes); + if (result == MA_SUCCESS) { + result = ma_hpf1_init_preallocated(&hpf1Config, ma_offset_ptr(pHeap, heapLayout.hpf1Offset + (sizeof(ma_hpf1) * hpf1Count) + (ihpf1 * hpf1HeapSizeInBytes)), &pHPF->pHPF1[ihpf1]); + } } else { - result = ma_hpf1_reinit(&hpf1Config, &pHPF->hpf1[ihpf1]); + result = ma_hpf1_reinit(&hpf1Config, &pHPF->pHPF1[ihpf1]); } if (result != MA_SUCCESS) { + ma_uint32 jhpf1; + + for (jhpf1 = 0; jhpf1 < ihpf1; jhpf1 += 1) { + ma_hpf1_uninit(&pHPF->pHPF1[jhpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ + } + return result; } } @@ -38041,12 +45342,28 @@ static ma_result ma_hpf_reinit__internal(const ma_hpf_config* pConfig, ma_hpf* p hpf2Config = ma_hpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q); if (isNew) { - result = ma_hpf2_init(&hpf2Config, &pHPF->hpf2[ihpf2]); + size_t hpf2HeapSizeInBytes; + + result = ma_hpf2_get_heap_size(&hpf2Config, &hpf2HeapSizeInBytes); + if (result == MA_SUCCESS) { + result = ma_hpf2_init_preallocated(&hpf2Config, ma_offset_ptr(pHeap, heapLayout.hpf2Offset + (sizeof(ma_hpf2) * hpf2Count) + (ihpf2 * hpf2HeapSizeInBytes)), &pHPF->pHPF2[ihpf2]); + } } else { - result = ma_hpf2_reinit(&hpf2Config, &pHPF->hpf2[ihpf2]); + result = ma_hpf2_reinit(&hpf2Config, &pHPF->pHPF2[ihpf2]); } if (result != MA_SUCCESS) { + ma_uint32 jhpf1; + ma_uint32 jhpf2; + + for (jhpf1 = 0; jhpf1 < hpf1Count; jhpf1 += 1) { + ma_hpf1_uninit(&pHPF->pHPF1[jhpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ + } + + for (jhpf2 = 0; jhpf2 < ihpf2; jhpf2 += 1) { + ma_hpf2_uninit(&pHPF->pHPF2[jhpf2], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ + } + return result; } } @@ -38060,24 +45377,93 @@ static ma_result ma_hpf_reinit__internal(const ma_hpf_config* pConfig, ma_hpf* p return MA_SUCCESS; } -MA_API ma_result ma_hpf_init(const ma_hpf_config* pConfig, ma_hpf* pHPF) +MA_API ma_result ma_hpf_get_heap_size(const ma_hpf_config* pConfig, size_t* pHeapSizeInBytes) { + ma_result result; + ma_hpf_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_hpf_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return result; +} + +MA_API ma_result ma_hpf_init_preallocated(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pLPF) +{ + if (pLPF == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pLPF); + + return ma_hpf_reinit__internal(pConfig, pHeap, pLPF, /*isNew*/MA_TRUE); +} + +MA_API ma_result ma_hpf_init(const ma_hpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf* pHPF) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_hpf_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_hpf_init_preallocated(pConfig, pHeap, pHPF); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pHPF->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_hpf_uninit(ma_hpf* pHPF, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_uint32 ihpf1; + ma_uint32 ihpf2; + if (pHPF == NULL) { - return MA_INVALID_ARGS; + return; } - MA_ZERO_OBJECT(pHPF); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; + for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { + ma_hpf1_uninit(&pHPF->pHPF1[ihpf1], pAllocationCallbacks); } - return ma_hpf_reinit__internal(pConfig, pHPF, /*isNew*/MA_TRUE); + for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { + ma_hpf2_uninit(&pHPF->pHPF2[ihpf2], pAllocationCallbacks); + } + + if (pHPF->_ownsHeap) { + ma_free(pHPF->_pHeap, pAllocationCallbacks); + } } MA_API ma_result ma_hpf_reinit(const ma_hpf_config* pConfig, ma_hpf* pHPF) { - return ma_hpf_reinit__internal(pConfig, pHPF, /*isNew*/MA_FALSE); + return ma_hpf_reinit__internal(pConfig, NULL, pHPF, /*isNew*/MA_FALSE); } MA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) @@ -38093,14 +45479,14 @@ MA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const /* Faster path for in-place. */ if (pFramesOut == pFramesIn) { for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { - result = ma_hpf1_process_pcm_frames(&pHPF->hpf1[ihpf1], pFramesOut, pFramesOut, frameCount); + result = ma_hpf1_process_pcm_frames(&pHPF->pHPF1[ihpf1], pFramesOut, pFramesOut, frameCount); if (result != MA_SUCCESS) { return result; } } for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { - result = ma_hpf2_process_pcm_frames(&pHPF->hpf2[ihpf2], pFramesOut, pFramesOut, frameCount); + result = ma_hpf2_process_pcm_frames(&pHPF->pHPF2[ihpf2], pFramesOut, pFramesOut, frameCount); if (result != MA_SUCCESS) { return result; } @@ -38119,11 +45505,11 @@ MA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const MA_COPY_MEMORY(pFramesOutF32, pFramesInF32, ma_get_bytes_per_frame(pHPF->format, pHPF->channels)); for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { - ma_hpf1_process_pcm_frame_f32(&pHPF->hpf1[ihpf1], pFramesOutF32, pFramesOutF32); + ma_hpf1_process_pcm_frame_f32(&pHPF->pHPF1[ihpf1], pFramesOutF32, pFramesOutF32); } for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { - ma_hpf2_process_pcm_frame_f32(&pHPF->hpf2[ihpf2], pFramesOutF32, pFramesOutF32); + ma_hpf2_process_pcm_frame_f32(&pHPF->pHPF2[ihpf2], pFramesOutF32, pFramesOutF32); } pFramesOutF32 += pHPF->channels; @@ -38137,11 +45523,11 @@ MA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const MA_COPY_MEMORY(pFramesOutS16, pFramesInS16, ma_get_bytes_per_frame(pHPF->format, pHPF->channels)); for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { - ma_hpf1_process_pcm_frame_s16(&pHPF->hpf1[ihpf1], pFramesOutS16, pFramesOutS16); + ma_hpf1_process_pcm_frame_s16(&pHPF->pHPF1[ihpf1], pFramesOutS16, pFramesOutS16); } for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { - ma_hpf2_process_pcm_frame_s16(&pHPF->hpf2[ihpf2], pFramesOutS16, pFramesOutS16); + ma_hpf2_process_pcm_frame_s16(&pHPF->pHPF2[ihpf2], pFramesOutS16, pFramesOutS16); } pFramesOutS16 += pHPF->channels; @@ -38221,7 +45607,15 @@ static MA_INLINE ma_biquad_config ma_bpf2__get_biquad_config(const ma_bpf2_confi return bqConfig; } -MA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, ma_bpf2* pBPF) +MA_API ma_result ma_bpf2_get_heap_size(const ma_bpf2_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_biquad_config bqConfig; + bqConfig = ma_bpf2__get_biquad_config(pConfig); + + return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); +} + +MA_API ma_result ma_bpf2_init_preallocated(const ma_bpf2_config* pConfig, void* pHeap, ma_bpf2* pBPF) { ma_result result; ma_biquad_config bqConfig; @@ -38237,7 +45631,7 @@ MA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, ma_bpf2* pBPF) } bqConfig = ma_bpf2__get_biquad_config(pConfig); - result = ma_biquad_init(&bqConfig, &pBPF->bq); + result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pBPF->bq); if (result != MA_SUCCESS) { return result; } @@ -38245,6 +45639,45 @@ MA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, ma_bpf2* pBPF) return MA_SUCCESS; } +MA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf2* pBPF) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_bpf2_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_bpf2_init_preallocated(pConfig, pHeap, pBPF); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pBPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ + return MA_SUCCESS; +} + +MA_API void ma_bpf2_uninit(ma_bpf2* pBPF, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pBPF == NULL) { + return; + } + + ma_biquad_uninit(&pBPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ +} + MA_API ma_result ma_bpf2_reinit(const ma_bpf2_config* pConfig, ma_bpf2* pBPF) { ma_result result; @@ -38306,12 +45739,67 @@ MA_API ma_bpf_config ma_bpf_config_init(ma_format format, ma_uint32 channels, ma return config; } -static ma_result ma_bpf_reinit__internal(const ma_bpf_config* pConfig, ma_bpf* pBPF, ma_bool32 isNew) + +typedef struct +{ + size_t sizeInBytes; + size_t bpf2Offset; +} ma_bpf_heap_layout; + +static ma_result ma_bpf_get_heap_layout(const ma_bpf_config* pConfig, ma_bpf_heap_layout* pHeapLayout) { ma_result result; ma_uint32 bpf2Count; ma_uint32 ibpf2; + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->order > MA_MAX_FILTER_ORDER) { + return MA_INVALID_ARGS; + } + + /* We must have an even number of order. */ + if ((pConfig->order & 0x1) != 0) { + return MA_INVALID_ARGS; + } + + bpf2Count = pConfig->channels / 2; + + pHeapLayout->sizeInBytes = 0; + + /* BPF 2 */ + pHeapLayout->bpf2Offset = pHeapLayout->sizeInBytes; + for (ibpf2 = 0; ibpf2 < bpf2Count; ibpf2 += 1) { + size_t bpf2HeapSizeInBytes; + ma_bpf2_config bpf2Config = ma_bpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, 0.707107); /* <-- The "q" parameter does not matter for the purpose of calculating the heap size. */ + + result = ma_bpf2_get_heap_size(&bpf2Config, &bpf2HeapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->sizeInBytes += sizeof(ma_bpf2) + bpf2HeapSizeInBytes; + } + + /* Make sure allocation size is aligned. */ + pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); + + return MA_SUCCESS; +} + +static ma_result ma_bpf_reinit__internal(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF, ma_bool32 isNew) +{ + ma_result result; + ma_uint32 bpf2Count; + ma_uint32 ibpf2; + ma_bpf_heap_layout heapLayout; /* Only used if isNew is true. */ + if (pBPF == NULL || pConfig == NULL) { return MA_INVALID_ARGS; } @@ -38342,8 +45830,6 @@ static ma_result ma_bpf_reinit__internal(const ma_bpf_config* pConfig, ma_bpf* p bpf2Count = pConfig->order / 2; - MA_ASSERT(bpf2Count <= ma_countof(pBPF->bpf2)); - /* The filter order can't change between reinits. */ if (!isNew) { if (pBPF->bpf2Count != bpf2Count) { @@ -38351,6 +45837,20 @@ static ma_result ma_bpf_reinit__internal(const ma_bpf_config* pConfig, ma_bpf* p } } + if (isNew) { + result = ma_bpf_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pBPF->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pBPF->pBPF2 = (ma_bpf2*)ma_offset_ptr(pHeap, heapLayout.bpf2Offset); + } else { + MA_ZERO_OBJECT(&heapLayout); + } + for (ibpf2 = 0; ibpf2 < bpf2Count; ibpf2 += 1) { ma_bpf2_config bpf2Config; double q; @@ -38361,9 +45861,14 @@ static ma_result ma_bpf_reinit__internal(const ma_bpf_config* pConfig, ma_bpf* p bpf2Config = ma_bpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q); if (isNew) { - result = ma_bpf2_init(&bpf2Config, &pBPF->bpf2[ibpf2]); + size_t bpf2HeapSizeInBytes; + + result = ma_bpf2_get_heap_size(&bpf2Config, &bpf2HeapSizeInBytes); + if (result == MA_SUCCESS) { + result = ma_bpf2_init_preallocated(&bpf2Config, ma_offset_ptr(pHeap, heapLayout.bpf2Offset + (sizeof(ma_bpf2) * bpf2Count) + (ibpf2 * bpf2HeapSizeInBytes)), &pBPF->pBPF2[ibpf2]); + } } else { - result = ma_bpf2_reinit(&bpf2Config, &pBPF->bpf2[ibpf2]); + result = ma_bpf2_reinit(&bpf2Config, &pBPF->pBPF2[ibpf2]); } if (result != MA_SUCCESS) { @@ -38378,7 +45883,29 @@ static ma_result ma_bpf_reinit__internal(const ma_bpf_config* pConfig, ma_bpf* p return MA_SUCCESS; } -MA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, ma_bpf* pBPF) + +MA_API ma_result ma_bpf_get_heap_size(const ma_bpf_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_bpf_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_bpf_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return MA_SUCCESS; +} + +MA_API ma_result ma_bpf_init_preallocated(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF) { if (pBPF == NULL) { return MA_INVALID_ARGS; @@ -38386,16 +45913,59 @@ MA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, ma_bpf* pBPF) MA_ZERO_OBJECT(pBPF); - if (pConfig == NULL) { - return MA_INVALID_ARGS; + return ma_bpf_reinit__internal(pConfig, pHeap, pBPF, /*isNew*/MA_TRUE); +} + +MA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf* pBPF) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_bpf_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; } - return ma_bpf_reinit__internal(pConfig, pBPF, /*isNew*/MA_TRUE); + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_bpf_init_preallocated(pConfig, pHeap, pBPF); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pBPF->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_bpf_uninit(ma_bpf* pBPF, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_uint32 ibpf2; + + if (pBPF == NULL) { + return; + } + + for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { + ma_bpf2_uninit(&pBPF->pBPF2[ibpf2], pAllocationCallbacks); + } + + if (pBPF->_ownsHeap) { + ma_free(pBPF->_pHeap, pAllocationCallbacks); + } } MA_API ma_result ma_bpf_reinit(const ma_bpf_config* pConfig, ma_bpf* pBPF) { - return ma_bpf_reinit__internal(pConfig, pBPF, /*isNew*/MA_FALSE); + return ma_bpf_reinit__internal(pConfig, NULL, pBPF, /*isNew*/MA_FALSE); } MA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) @@ -38410,7 +45980,7 @@ MA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const /* Faster path for in-place. */ if (pFramesOut == pFramesIn) { for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { - result = ma_bpf2_process_pcm_frames(&pBPF->bpf2[ibpf2], pFramesOut, pFramesOut, frameCount); + result = ma_bpf2_process_pcm_frames(&pBPF->pBPF2[ibpf2], pFramesOut, pFramesOut, frameCount); if (result != MA_SUCCESS) { return result; } @@ -38429,7 +45999,7 @@ MA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const MA_COPY_MEMORY(pFramesOutF32, pFramesInF32, ma_get_bytes_per_frame(pBPF->format, pBPF->channels)); for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { - ma_bpf2_process_pcm_frame_f32(&pBPF->bpf2[ibpf2], pFramesOutF32, pFramesOutF32); + ma_bpf2_process_pcm_frame_f32(&pBPF->pBPF2[ibpf2], pFramesOutF32, pFramesOutF32); } pFramesOutF32 += pBPF->channels; @@ -38443,7 +46013,7 @@ MA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const MA_COPY_MEMORY(pFramesOutS16, pFramesInS16, ma_get_bytes_per_frame(pBPF->format, pBPF->channels)); for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { - ma_bpf2_process_pcm_frame_s16(&pBPF->bpf2[ibpf2], pFramesOutS16, pFramesOutS16); + ma_bpf2_process_pcm_frame_s16(&pBPF->pBPF2[ibpf2], pFramesOutS16, pFramesOutS16); } pFramesOutS16 += pBPF->channels; @@ -38522,7 +46092,15 @@ static MA_INLINE ma_biquad_config ma_notch2__get_biquad_config(const ma_notch2_c return bqConfig; } -MA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, ma_notch2* pFilter) +MA_API ma_result ma_notch2_get_heap_size(const ma_notch2_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_biquad_config bqConfig; + bqConfig = ma_notch2__get_biquad_config(pConfig); + + return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); +} + +MA_API ma_result ma_notch2_init_preallocated(const ma_notch2_config* pConfig, void* pHeap, ma_notch2* pFilter) { ma_result result; ma_biquad_config bqConfig; @@ -38538,7 +46116,7 @@ MA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, ma_notch2* pFil } bqConfig = ma_notch2__get_biquad_config(pConfig); - result = ma_biquad_init(&bqConfig, &pFilter->bq); + result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); if (result != MA_SUCCESS) { return result; } @@ -38546,6 +46124,45 @@ MA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, ma_notch2* pFil return MA_SUCCESS; } +MA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch2* pFilter) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_notch2_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_notch2_init_preallocated(pConfig, pHeap, pFilter); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ + return MA_SUCCESS; +} + +MA_API void ma_notch2_uninit(ma_notch2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pFilter == NULL) { + return; + } + + ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ +} + MA_API ma_result ma_notch2_reinit(const ma_notch2_config* pConfig, ma_notch2* pFilter) { ma_result result; @@ -38651,7 +46268,15 @@ static MA_INLINE ma_biquad_config ma_peak2__get_biquad_config(const ma_peak2_con return bqConfig; } -MA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, ma_peak2* pFilter) +MA_API ma_result ma_peak2_get_heap_size(const ma_peak2_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_biquad_config bqConfig; + bqConfig = ma_peak2__get_biquad_config(pConfig); + + return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); +} + +MA_API ma_result ma_peak2_init_preallocated(const ma_peak2_config* pConfig, void* pHeap, ma_peak2* pFilter) { ma_result result; ma_biquad_config bqConfig; @@ -38667,7 +46292,7 @@ MA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, ma_peak2* pFilter } bqConfig = ma_peak2__get_biquad_config(pConfig); - result = ma_biquad_init(&bqConfig, &pFilter->bq); + result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); if (result != MA_SUCCESS) { return result; } @@ -38675,6 +46300,45 @@ MA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, ma_peak2* pFilter return MA_SUCCESS; } +MA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak2* pFilter) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_peak2_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_peak2_init_preallocated(pConfig, pHeap, pFilter); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ + return MA_SUCCESS; +} + +MA_API void ma_peak2_uninit(ma_peak2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pFilter == NULL) { + return; + } + + ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ +} + MA_API ma_result ma_peak2_reinit(const ma_peak2_config* pConfig, ma_peak2* pFilter) { ma_result result; @@ -38777,7 +46441,15 @@ static MA_INLINE ma_biquad_config ma_loshelf2__get_biquad_config(const ma_loshel return bqConfig; } -MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter) +MA_API ma_result ma_loshelf2_get_heap_size(const ma_loshelf2_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_biquad_config bqConfig; + bqConfig = ma_loshelf2__get_biquad_config(pConfig); + + return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); +} + +MA_API ma_result ma_loshelf2_init_preallocated(const ma_loshelf2_config* pConfig, void* pHeap, ma_loshelf2* pFilter) { ma_result result; ma_biquad_config bqConfig; @@ -38793,7 +46465,7 @@ MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, ma_loshelf2 } bqConfig = ma_loshelf2__get_biquad_config(pConfig); - result = ma_biquad_init(&bqConfig, &pFilter->bq); + result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); if (result != MA_SUCCESS) { return result; } @@ -38801,6 +46473,45 @@ MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, ma_loshelf2 return MA_SUCCESS; } +MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf2* pFilter) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_loshelf2_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_loshelf2_init_preallocated(pConfig, pHeap, pFilter); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ + return MA_SUCCESS; +} + +MA_API void ma_loshelf2_uninit(ma_loshelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pFilter == NULL) { + return; + } + + ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ +} + MA_API ma_result ma_loshelf2_reinit(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter) { ma_result result; @@ -38903,7 +46614,15 @@ static MA_INLINE ma_biquad_config ma_hishelf2__get_biquad_config(const ma_hishel return bqConfig; } -MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter) +MA_API ma_result ma_hishelf2_get_heap_size(const ma_hishelf2_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_biquad_config bqConfig; + bqConfig = ma_hishelf2__get_biquad_config(pConfig); + + return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); +} + +MA_API ma_result ma_hishelf2_init_preallocated(const ma_hishelf2_config* pConfig, void* pHeap, ma_hishelf2* pFilter) { ma_result result; ma_biquad_config bqConfig; @@ -38919,7 +46638,7 @@ MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, ma_hishelf2 } bqConfig = ma_hishelf2__get_biquad_config(pConfig); - result = ma_biquad_init(&bqConfig, &pFilter->bq); + result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); if (result != MA_SUCCESS) { return result; } @@ -38927,6 +46646,45 @@ MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, ma_hishelf2 return MA_SUCCESS; } +MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf2* pFilter) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_hishelf2_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_hishelf2_init_preallocated(pConfig, pHeap, pFilter); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ + return MA_SUCCESS; +} + +MA_API void ma_hishelf2_uninit(ma_hishelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pFilter == NULL) { + return; + } + + ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ +} + MA_API ma_result ma_hishelf2_reinit(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter) { ma_result result; @@ -38975,6 +46733,2286 @@ MA_API ma_uint32 ma_hishelf2_get_latency(const ma_hishelf2* pFilter) +/* +Delay +*/ +MA_API ma_delay_config ma_delay_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay) +{ + ma_delay_config config; + + MA_ZERO_OBJECT(&config); + config.channels = channels; + config.sampleRate = sampleRate; + config.delayInFrames = delayInFrames; + config.delayStart = (decay == 0) ? MA_TRUE : MA_FALSE; /* Delay the start if it looks like we're not configuring an echo. */ + config.wet = 1; + config.dry = 1; + config.decay = decay; + + return config; +} + + +MA_API ma_result ma_delay_init(const ma_delay_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay* pDelay) +{ + if (pDelay == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pDelay); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->decay < 0 || pConfig->decay > 1) { + return MA_INVALID_ARGS; + } + + pDelay->config = *pConfig; + pDelay->bufferSizeInFrames = pConfig->delayInFrames; + pDelay->cursor = 0; + + pDelay->pBuffer = (float*)ma_malloc((size_t)(pDelay->bufferSizeInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->channels)), pAllocationCallbacks); + if (pDelay->pBuffer == NULL) { + return MA_OUT_OF_MEMORY; + } + + ma_silence_pcm_frames(pDelay->pBuffer, pDelay->bufferSizeInFrames, ma_format_f32, pConfig->channels); + + return MA_SUCCESS; +} + +MA_API void ma_delay_uninit(ma_delay* pDelay, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pDelay == NULL) { + return; + } + + ma_free(pDelay->pBuffer, pAllocationCallbacks); +} + +MA_API ma_result ma_delay_process_pcm_frames(ma_delay* pDelay, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) +{ + ma_uint32 iFrame; + ma_uint32 iChannel; + float* pFramesOutF32 = (float*)pFramesOut; + const float* pFramesInF32 = (const float*)pFramesIn; + + if (pDelay == NULL || pFramesOut == NULL || pFramesIn == NULL) { + return MA_INVALID_ARGS; + } + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannel = 0; iChannel < pDelay->config.channels; iChannel += 1) { + ma_uint32 iBuffer = (pDelay->cursor * pDelay->config.channels) + iChannel; + + if (pDelay->config.delayStart) { + /* Delayed start. */ + + /* Read */ + pFramesOutF32[iChannel] = pDelay->pBuffer[iBuffer] * pDelay->config.wet; + + /* Feedback */ + pDelay->pBuffer[iBuffer] = (pDelay->pBuffer[iBuffer] * pDelay->config.decay) + (pFramesInF32[iChannel] * pDelay->config.dry); + } else { + /* Immediate start */ + + /* Feedback */ + pDelay->pBuffer[iBuffer] = (pDelay->pBuffer[iBuffer] * pDelay->config.decay) + (pFramesInF32[iChannel] * pDelay->config.dry); + + /* Read */ + pFramesOutF32[iChannel] = pDelay->pBuffer[iBuffer] * pDelay->config.wet; + } + } + + pDelay->cursor = (pDelay->cursor + 1) % pDelay->bufferSizeInFrames; + + pFramesOutF32 += pDelay->config.channels; + pFramesInF32 += pDelay->config.channels; + } + + return MA_SUCCESS; +} + +MA_API void ma_delay_set_wet(ma_delay* pDelay, float value) +{ + if (pDelay == NULL) { + return; + } + + pDelay->config.wet = value; +} + +MA_API float ma_delay_get_wet(const ma_delay* pDelay) +{ + if (pDelay == NULL) { + return 0; + } + + return pDelay->config.wet; +} + +MA_API void ma_delay_set_dry(ma_delay* pDelay, float value) +{ + if (pDelay == NULL) { + return; + } + + pDelay->config.dry = value; +} + +MA_API float ma_delay_get_dry(const ma_delay* pDelay) +{ + if (pDelay == NULL) { + return 0; + } + + return pDelay->config.dry; +} + +MA_API void ma_delay_set_decay(ma_delay* pDelay, float value) +{ + if (pDelay == NULL) { + return; + } + + pDelay->config.decay = value; +} + +MA_API float ma_delay_get_decay(const ma_delay* pDelay) +{ + if (pDelay == NULL) { + return 0; + } + + return pDelay->config.decay; +} + + +MA_API ma_gainer_config ma_gainer_config_init(ma_uint32 channels, ma_uint32 smoothTimeInFrames) +{ + ma_gainer_config config; + + MA_ZERO_OBJECT(&config); + config.channels = channels; + config.smoothTimeInFrames = smoothTimeInFrames; + + return config; +} + + +typedef struct +{ + size_t sizeInBytes; + size_t oldGainsOffset; + size_t newGainsOffset; +} ma_gainer_heap_layout; + +static ma_result ma_gainer_get_heap_layout(const ma_gainer_config* pConfig, ma_gainer_heap_layout* pHeapLayout) +{ + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->channels == 0) { + return MA_INVALID_ARGS; + } + + pHeapLayout->sizeInBytes = 0; + + /* Old gains. */ + pHeapLayout->oldGainsOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; + + /* New gains. */ + pHeapLayout->newGainsOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; + + /* Alignment. */ + pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); + + return MA_SUCCESS; +} + + +MA_API ma_result ma_gainer_get_heap_size(const ma_gainer_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_gainer_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_gainer_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return MA_SUCCESS; +} + + +MA_API ma_result ma_gainer_init_preallocated(const ma_gainer_config* pConfig, void* pHeap, ma_gainer* pGainer) +{ + ma_result result; + ma_gainer_heap_layout heapLayout; + ma_uint32 iChannel; + + if (pGainer == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pGainer); + + if (pConfig == NULL || pHeap == NULL) { + return MA_INVALID_ARGS; + } + + result = ma_gainer_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pGainer->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pGainer->pOldGains = (float*)ma_offset_ptr(pHeap, heapLayout.oldGainsOffset); + pGainer->pNewGains = (float*)ma_offset_ptr(pHeap, heapLayout.newGainsOffset); + + pGainer->config = *pConfig; + pGainer->t = (ma_uint32)-1; /* No interpolation by default. */ + + for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) { + pGainer->pOldGains[iChannel] = 1; + pGainer->pNewGains[iChannel] = 1; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_gainer_init(const ma_gainer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_gainer* pGainer) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_gainer_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; /* Failed to retrieve the size of the heap allocation. */ + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_gainer_init_preallocated(pConfig, pHeap, pGainer); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pGainer->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_gainer_uninit(ma_gainer* pGainer, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pGainer == NULL) { + return; + } + + if (pGainer->_ownsHeap) { + ma_free(pGainer->_pHeap, pAllocationCallbacks); + } +} + +static float ma_gainer_calculate_current_gain(const ma_gainer* pGainer, ma_uint32 channel) +{ + float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames; + return ma_mix_f32_fast(pGainer->pOldGains[channel], pGainer->pNewGains[channel], a); +} + +MA_API ma_result ma_gainer_process_pcm_frames(ma_gainer* pGainer, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + ma_uint64 iFrame; + ma_uint32 iChannel; + float* pFramesOutF32 = (float*)pFramesOut; + const float* pFramesInF32 = (const float*)pFramesIn; + + if (pGainer == NULL) { + return MA_INVALID_ARGS; + } + + if (pGainer->t >= pGainer->config.smoothTimeInFrames) { + /* Fast path. No gain calculation required. */ + ma_copy_and_apply_volume_factor_per_channel_f32(pFramesOutF32, pFramesInF32, frameCount, pGainer->config.channels, pGainer->pNewGains); + + /* Now that some frames have been processed we need to make sure future changes to the gain are interpolated. */ + if (pGainer->t == (ma_uint32)-1) { + pGainer->t = pGainer->config.smoothTimeInFrames; + } + } else { + /* Slow path. Need to interpolate the gain for each channel individually. */ + + /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */ + if (pFramesOut != NULL && pFramesIn != NULL) { + float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames; + float d = 1.0f / pGainer->config.smoothTimeInFrames; + ma_uint32 channelCount = pGainer->config.channels; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannel = 0; iChannel < channelCount; iChannel += 1) { + pFramesOutF32[iChannel] = pFramesInF32[iChannel] * ma_mix_f32_fast(pGainer->pOldGains[iChannel], pGainer->pNewGains[iChannel], a); + } + + pFramesOutF32 += channelCount; + pFramesInF32 += channelCount; + + a += d; + if (a > 1) { + a = 1; + } + } + } + + pGainer->t = (ma_uint32)ma_min(pGainer->t + frameCount, pGainer->config.smoothTimeInFrames); + + #if 0 /* Reference implementation. */ + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */ + if (pFramesOut != NULL && pFramesIn != NULL) { + for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { + pFramesOutF32[iFrame*pGainer->config.channels + iChannel] = pFramesInF32[iFrame*pGainer->config.channels + iChannel] * ma_gainer_calculate_current_gain(pGainer, iChannel); + } + } + + /* Move interpolation time forward, but don't go beyond our smoothing time. */ + pGainer->t = ma_min(pGainer->t + 1, pGainer->config.smoothTimeInFrames); + } + #endif + } + + return MA_SUCCESS; +} + +static void ma_gainer_set_gain_by_index(ma_gainer* pGainer, float newGain, ma_uint32 iChannel) +{ + pGainer->pOldGains[iChannel] = ma_gainer_calculate_current_gain(pGainer, iChannel); + pGainer->pNewGains[iChannel] = newGain; +} + +static void ma_gainer_reset_smoothing_time(ma_gainer* pGainer) +{ + if (pGainer->t == (ma_uint32)-1) { + pGainer->t = pGainer->config.smoothTimeInFrames; /* No smoothing required for initial gains setting. */ + } else { + pGainer->t = 0; + } +} + +MA_API ma_result ma_gainer_set_gain(ma_gainer* pGainer, float newGain) +{ + ma_uint32 iChannel; + + if (pGainer == NULL) { + return MA_INVALID_ARGS; + } + + for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { + ma_gainer_set_gain_by_index(pGainer, newGain, iChannel); + } + + /* The smoothing time needs to be reset to ensure we always interpolate by the configured smoothing time, but only if it's not the first setting. */ + ma_gainer_reset_smoothing_time(pGainer); + + return MA_SUCCESS; +} + +MA_API ma_result ma_gainer_set_gains(ma_gainer* pGainer, float* pNewGains) +{ + ma_uint32 iChannel; + + if (pGainer == NULL || pNewGains == NULL) { + return MA_INVALID_ARGS; + } + + for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { + ma_gainer_set_gain_by_index(pGainer, pNewGains[iChannel], iChannel); + } + + /* The smoothing time needs to be reset to ensure we always interpolate by the configured smoothing time, but only if it's not the first setting. */ + ma_gainer_reset_smoothing_time(pGainer); + + return MA_SUCCESS; +} + + +MA_API ma_panner_config ma_panner_config_init(ma_format format, ma_uint32 channels) +{ + ma_panner_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.mode = ma_pan_mode_balance; /* Set to balancing mode by default because it's consistent with other audio engines and most likely what the caller is expecting. */ + config.pan = 0; + + return config; +} + + +MA_API ma_result ma_panner_init(const ma_panner_config* pConfig, ma_panner* pPanner) +{ + if (pPanner == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pPanner); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + pPanner->format = pConfig->format; + pPanner->channels = pConfig->channels; + pPanner->mode = pConfig->mode; + pPanner->pan = pConfig->pan; + + return MA_SUCCESS; +} + +static void ma_stereo_balance_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, float pan) +{ + ma_uint64 iFrame; + + if (pan > 0) { + float factor = 1.0f - pan; + if (pFramesOut == pFramesIn) { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0] * factor; + } + } else { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0] * factor; + pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1]; + } + } + } else { + float factor = 1.0f + pan; + if (pFramesOut == pFramesIn) { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1] * factor; + } + } else { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0]; + pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1] * factor; + } + } + } +} + +static void ma_stereo_balance_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, float pan) +{ + if (pan == 0) { + /* Fast path. No panning required. */ + if (pFramesOut == pFramesIn) { + /* No-op */ + } else { + ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); + } + + return; + } + + switch (format) { + case ma_format_f32: ma_stereo_balance_pcm_frames_f32((float*)pFramesOut, (float*)pFramesIn, frameCount, pan); break; + + /* Unknown format. Just copy. */ + default: + { + ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); + } break; + } +} + + +static void ma_stereo_pan_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, float pan) +{ + ma_uint64 iFrame; + + if (pan > 0) { + float factorL0 = 1.0f - pan; + float factorL1 = 0.0f + pan; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float sample0 = (pFramesIn[iFrame*2 + 0] * factorL0); + float sample1 = (pFramesIn[iFrame*2 + 0] * factorL1) + pFramesIn[iFrame*2 + 1]; + + pFramesOut[iFrame*2 + 0] = sample0; + pFramesOut[iFrame*2 + 1] = sample1; + } + } else { + float factorR0 = 0.0f - pan; + float factorR1 = 1.0f + pan; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float sample0 = pFramesIn[iFrame*2 + 0] + (pFramesIn[iFrame*2 + 1] * factorR0); + float sample1 = (pFramesIn[iFrame*2 + 1] * factorR1); + + pFramesOut[iFrame*2 + 0] = sample0; + pFramesOut[iFrame*2 + 1] = sample1; + } + } +} + +static void ma_stereo_pan_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, float pan) +{ + if (pan == 0) { + /* Fast path. No panning required. */ + if (pFramesOut == pFramesIn) { + /* No-op */ + } else { + ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); + } + + return; + } + + switch (format) { + case ma_format_f32: ma_stereo_pan_pcm_frames_f32((float*)pFramesOut, (float*)pFramesIn, frameCount, pan); break; + + /* Unknown format. Just copy. */ + default: + { + ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); + } break; + } +} + +MA_API ma_result ma_panner_process_pcm_frames(ma_panner* pPanner, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + if (pPanner == NULL || pFramesOut == NULL || pFramesIn == NULL) { + return MA_INVALID_ARGS; + } + + if (pPanner->channels == 2) { + /* Stereo case. For now assume channel 0 is left and channel right is 1, but should probably add support for a channel map. */ + if (pPanner->mode == ma_pan_mode_balance) { + ma_stereo_balance_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->pan); + } else { + ma_stereo_pan_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->pan); + } + } else { + if (pPanner->channels == 1) { + /* Panning has no effect on mono streams. */ + ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->channels); + } else { + /* For now we're not going to support non-stereo set ups. Not sure how I want to handle this case just yet. */ + ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->channels); + } + } + + return MA_SUCCESS; +} + +MA_API void ma_panner_set_mode(ma_panner* pPanner, ma_pan_mode mode) +{ + if (pPanner == NULL) { + return; + } + + pPanner->mode = mode; +} + +MA_API ma_pan_mode ma_panner_get_mode(const ma_panner* pPanner) +{ + if (pPanner == NULL) { + return ma_pan_mode_balance; + } + + return pPanner->mode; +} + +MA_API void ma_panner_set_pan(ma_panner* pPanner, float pan) +{ + if (pPanner == NULL) { + return; + } + + pPanner->pan = ma_clamp(pan, -1.0f, 1.0f); +} + +MA_API float ma_panner_get_pan(const ma_panner* pPanner) +{ + if (pPanner == NULL) { + return 0; + } + + return pPanner->pan; +} + + + + +MA_API ma_fader_config ma_fader_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate) +{ + ma_fader_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.sampleRate = sampleRate; + + return config; +} + + +MA_API ma_result ma_fader_init(const ma_fader_config* pConfig, ma_fader* pFader) +{ + if (pFader == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pFader); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + /* Only f32 is supported for now. */ + if (pConfig->format != ma_format_f32) { + return MA_INVALID_ARGS; + } + + pFader->config = *pConfig; + pFader->volumeBeg = 1; + pFader->volumeEnd = 1; + pFader->lengthInFrames = 0; + pFader->cursorInFrames = 0; + + return MA_SUCCESS; +} + +MA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + if (pFader == NULL) { + return MA_INVALID_ARGS; + } + + /* + For now we need to clamp frameCount so that the cursor never overflows 32-bits. This is required for + the conversion to a float which we use for the linear interpolation. This might be changed later. + */ + if (frameCount + pFader->cursorInFrames > UINT_MAX) { + frameCount = UINT_MAX - pFader->cursorInFrames; + } + + /* Optimized path if volumeBeg and volumeEnd are equal. */ + if (pFader->volumeBeg == pFader->volumeEnd) { + if (pFader->volumeBeg == 1) { + /* Straight copy. */ + ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels); + } else { + /* Copy with volume. */ + ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeEnd); + } + } else { + /* Slower path. Volumes are different, so may need to do an interpolation. */ + if (pFader->cursorInFrames >= pFader->lengthInFrames) { + /* Fast path. We've gone past the end of the fade period so just apply the end volume to all samples. */ + ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeEnd); + } else { + /* Slow path. This is where we do the actual fading. */ + ma_uint64 iFrame; + ma_uint32 iChannel; + + /* For now we only support f32. Support for other formats will be added later. */ + if (pFader->config.format == ma_format_f32) { + const float* pFramesInF32 = (const float*)pFramesIn; + /* */ float* pFramesOutF32 = ( float*)pFramesOut; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float a = (ma_uint32)ma_min(pFader->cursorInFrames + iFrame, pFader->lengthInFrames) / (float)((ma_uint32)pFader->lengthInFrames); /* Safe cast due to the frameCount clamp at the top of this function. */ + float volume = ma_mix_f32_fast(pFader->volumeBeg, pFader->volumeEnd, a); + + for (iChannel = 0; iChannel < pFader->config.channels; iChannel += 1) { + pFramesOutF32[iFrame*pFader->config.channels + iChannel] = pFramesInF32[iFrame*pFader->config.channels + iChannel] * volume; + } + } + } else { + return MA_NOT_IMPLEMENTED; + } + } + } + + pFader->cursorInFrames += frameCount; + + return MA_SUCCESS; +} + +MA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate) +{ + if (pFader == NULL) { + return; + } + + if (pFormat != NULL) { + *pFormat = pFader->config.format; + } + + if (pChannels != NULL) { + *pChannels = pFader->config.channels; + } + + if (pSampleRate != NULL) { + *pSampleRate = pFader->config.sampleRate; + } +} + +MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames) +{ + if (pFader == NULL) { + return; + } + + /* If the volume is negative, use current volume. */ + if (volumeBeg < 0) { + volumeBeg = ma_fader_get_current_volume(pFader); + } + + /* + The length needs to be clamped to 32-bits due to how we convert it to a float for linear + interpolation reasons. I might change this requirement later, but for now it's not important. + */ + if (lengthInFrames > UINT_MAX) { + lengthInFrames = UINT_MAX; + } + + pFader->volumeBeg = volumeBeg; + pFader->volumeEnd = volumeEnd; + pFader->lengthInFrames = lengthInFrames; + pFader->cursorInFrames = 0; /* Reset cursor. */ +} + +MA_API float ma_fader_get_current_volume(ma_fader* pFader) +{ + if (pFader == NULL) { + return 0.0f; + } + + /* The current volume depends on the position of the cursor. */ + if (pFader->cursorInFrames == 0) { + return pFader->volumeBeg; + } else if (pFader->cursorInFrames >= pFader->lengthInFrames) { + return pFader->volumeEnd; + } else { + /* The cursor is somewhere inside the fading period. We can figure this out with a simple linear interpoluation between volumeBeg and volumeEnd based on our cursor position. */ + return ma_mix_f32_fast(pFader->volumeBeg, pFader->volumeEnd, (ma_uint32)pFader->cursorInFrames / (float)((ma_uint32)pFader->lengthInFrames)); /* Safe cast to uint32 because we clamp it in ma_fader_process_pcm_frames(). */ + } +} + + + + + +MA_API ma_vec3f ma_vec3f_init_3f(float x, float y, float z) +{ + ma_vec3f v; + + v.x = x; + v.y = y; + v.z = z; + + return v; +} + +MA_API ma_vec3f ma_vec3f_sub(ma_vec3f a, ma_vec3f b) +{ + return ma_vec3f_init_3f( + a.x - b.x, + a.y - b.y, + a.z - b.z + ); +} + +MA_API ma_vec3f ma_vec3f_neg(ma_vec3f a) +{ + return ma_vec3f_init_3f( + -a.x, + -a.y, + -a.z + ); +} + +MA_API float ma_vec3f_dot(ma_vec3f a, ma_vec3f b) +{ + return a.x*b.x + a.y*b.y + a.z*b.z; +} + +MA_API float ma_vec3f_len2(ma_vec3f v) +{ + return ma_vec3f_dot(v, v); +} + +MA_API float ma_vec3f_len(ma_vec3f v) +{ + return (float)ma_sqrtd(ma_vec3f_len2(v)); +} + +MA_API float ma_vec3f_dist(ma_vec3f a, ma_vec3f b) +{ + return ma_vec3f_len(ma_vec3f_sub(a, b)); +} + +MA_API ma_vec3f ma_vec3f_normalize(ma_vec3f v) +{ + float f; + float l = ma_vec3f_len(v); + if (l == 0) { + return ma_vec3f_init_3f(0, 0, 0); + } + + f = 1 / l; + v.x *= f; + v.y *= f; + v.z *= f; + + return v; +} + +MA_API ma_vec3f ma_vec3f_cross(ma_vec3f a, ma_vec3f b) +{ + return ma_vec3f_init_3f( + a.y*b.z - a.z*b.y, + a.z*b.x - a.x*b.z, + a.x*b.y - a.y*b.x + ); +} + + + +static void ma_channel_map_apply_f32(float* pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount, ma_channel_mix_mode mode, ma_mono_expansion_mode monoExpansionMode); +static ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition); + + +#ifndef MA_DEFAULT_SPEED_OF_SOUND +#define MA_DEFAULT_SPEED_OF_SOUND 343.3f +#endif + +/* +These vectors represent the direction that speakers are facing from the center point. They're used +for panning in the spatializer. Must be normalized. +*/ +static ma_vec3f g_maChannelDirections[MA_CHANNEL_POSITION_COUNT] = { + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_NONE */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_MONO */ + {-0.7071f, 0.0f, -0.7071f }, /* MA_CHANNEL_FRONT_LEFT */ + {+0.7071f, 0.0f, -0.7071f }, /* MA_CHANNEL_FRONT_RIGHT */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_FRONT_CENTER */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_LFE */ + {-0.7071f, 0.0f, +0.7071f }, /* MA_CHANNEL_BACK_LEFT */ + {+0.7071f, 0.0f, +0.7071f }, /* MA_CHANNEL_BACK_RIGHT */ + {-0.3162f, 0.0f, -0.9487f }, /* MA_CHANNEL_FRONT_LEFT_CENTER */ + {+0.3162f, 0.0f, -0.9487f }, /* MA_CHANNEL_FRONT_RIGHT_CENTER */ + { 0.0f, 0.0f, +1.0f }, /* MA_CHANNEL_BACK_CENTER */ + {-1.0f, 0.0f, 0.0f }, /* MA_CHANNEL_SIDE_LEFT */ + {+1.0f, 0.0f, 0.0f }, /* MA_CHANNEL_SIDE_RIGHT */ + { 0.0f, +1.0f, 0.0f }, /* MA_CHANNEL_TOP_CENTER */ + {-0.5774f, +0.5774f, -0.5774f }, /* MA_CHANNEL_TOP_FRONT_LEFT */ + { 0.0f, +0.7071f, -0.7071f }, /* MA_CHANNEL_TOP_FRONT_CENTER */ + {+0.5774f, +0.5774f, -0.5774f }, /* MA_CHANNEL_TOP_FRONT_RIGHT */ + {-0.5774f, +0.5774f, +0.5774f }, /* MA_CHANNEL_TOP_BACK_LEFT */ + { 0.0f, +0.7071f, +0.7071f }, /* MA_CHANNEL_TOP_BACK_CENTER */ + {+0.5774f, +0.5774f, +0.5774f }, /* MA_CHANNEL_TOP_BACK_RIGHT */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_0 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_1 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_2 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_3 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_4 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_5 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_6 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_7 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_8 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_9 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_10 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_11 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_12 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_13 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_14 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_15 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_16 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_17 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_18 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_19 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_20 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_21 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_22 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_23 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_24 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_25 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_26 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_27 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_28 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_29 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_30 */ + { 0.0f, 0.0f, -1.0f } /* MA_CHANNEL_AUX_31 */ +}; + +static ma_vec3f ma_get_channel_direction(ma_channel channel) +{ + if (channel >= MA_CHANNEL_POSITION_COUNT) { + return ma_vec3f_init_3f(0, 0, -1); + } else { + return g_maChannelDirections[channel]; + } +} + + + +static float ma_attenuation_inverse(float distance, float minDistance, float maxDistance, float rolloff) +{ + if (minDistance >= maxDistance) { + return 1; /* To avoid division by zero. Do not attenuate. */ + } + + return minDistance / (minDistance + rolloff * (ma_clamp(distance, minDistance, maxDistance) - minDistance)); +} + +static float ma_attenuation_linear(float distance, float minDistance, float maxDistance, float rolloff) +{ + if (minDistance >= maxDistance) { + return 1; /* To avoid division by zero. Do not attenuate. */ + } + + return 1 - rolloff * (ma_clamp(distance, minDistance, maxDistance) - minDistance) / (maxDistance - minDistance); +} + +static float ma_attenuation_exponential(float distance, float minDistance, float maxDistance, float rolloff) +{ + if (minDistance >= maxDistance) { + return 1; /* To avoid division by zero. Do not attenuate. */ + } + + return (float)ma_powd(ma_clamp(distance, minDistance, maxDistance) / minDistance, -rolloff); +} + + +/* +Dopper Effect calculation taken from the OpenAL spec, with two main differences: + + 1) The source to listener vector will have already been calcualted at an earlier step so we can + just use that directly. We need only the position of the source relative to the origin. + + 2) We don't scale by a frequency because we actually just want the ratio which we'll plug straight + into the resampler directly. +*/ +static float ma_doppler_pitch(ma_vec3f relativePosition, ma_vec3f sourceVelocity, ma_vec3f listenVelocity, float speedOfSound, float dopplerFactor) +{ + float len; + float vls; + float vss; + + len = ma_vec3f_len(relativePosition); + + /* + There's a case where the position of the source will be right on top of the listener in which + case the length will be 0 and we'll end up with a division by zero. We can just return a ratio + of 1.0 in this case. This is not considered in the OpenAL spec, but is necessary. + */ + if (len == 0) { + return 1.0; + } + + vls = ma_vec3f_dot(relativePosition, listenVelocity) / len; + vss = ma_vec3f_dot(relativePosition, sourceVelocity) / len; + + vls = ma_min(vls, speedOfSound / dopplerFactor); + vss = ma_min(vss, speedOfSound / dopplerFactor); + + return (speedOfSound - dopplerFactor*vls) / (speedOfSound - dopplerFactor*vss); +} + + +static void ma_get_default_channel_map_for_spatializer(ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channelCount) +{ + /* + Special case for stereo. Want to default the left and right speakers to side left and side + right so that they're facing directly down the X axis rather than slightly forward. Not + doing this will result in sounds being quieter when behind the listener. This might + actually be good for some scenerios, but I don't think it's an appropriate default because + it can be a bit unexpected. + */ + if (channelCount == 2) { + pChannelMap[0] = MA_CHANNEL_SIDE_LEFT; + pChannelMap[1] = MA_CHANNEL_SIDE_RIGHT; + } else { + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount); + } +} + + +MA_API ma_spatializer_listener_config ma_spatializer_listener_config_init(ma_uint32 channelsOut) +{ + ma_spatializer_listener_config config; + + MA_ZERO_OBJECT(&config); + config.channelsOut = channelsOut; + config.pChannelMapOut = NULL; + config.handedness = ma_handedness_right; + config.worldUp = ma_vec3f_init_3f(0, 1, 0); + config.coneInnerAngleInRadians = 6.283185f; /* 360 degrees. */ + config.coneOuterAngleInRadians = 6.283185f; /* 360 degrees. */ + config.coneOuterGain = 0; + config.speedOfSound = 343.3f; /* Same as OpenAL. Used for doppler effect. */ + + return config; +} + + +typedef struct +{ + size_t sizeInBytes; + size_t channelMapOutOffset; +} ma_spatializer_listener_heap_layout; + +static ma_result ma_spatializer_listener_get_heap_layout(const ma_spatializer_listener_config* pConfig, ma_spatializer_listener_heap_layout* pHeapLayout) +{ + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->channelsOut == 0) { + return MA_INVALID_ARGS; + } + + pHeapLayout->sizeInBytes = 0; + + /* Channel map. We always need this, even for passthroughs. */ + pHeapLayout->channelMapOutOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(sizeof(*pConfig->pChannelMapOut) * pConfig->channelsOut); + + return MA_SUCCESS; +} + + +MA_API ma_result ma_spatializer_listener_get_heap_size(const ma_spatializer_listener_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_spatializer_listener_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_spatializer_listener_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return MA_SUCCESS; +} + +MA_API ma_result ma_spatializer_listener_init_preallocated(const ma_spatializer_listener_config* pConfig, void* pHeap, ma_spatializer_listener* pListener) +{ + ma_result result; + ma_spatializer_listener_heap_layout heapLayout; + + if (pListener == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pListener); + + result = ma_spatializer_listener_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pListener->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pListener->config = *pConfig; + pListener->position = ma_vec3f_init_3f(0, 0, 0); + pListener->direction = ma_vec3f_init_3f(0, 0, -1); + pListener->velocity = ma_vec3f_init_3f(0, 0, 0); + pListener->isEnabled = MA_TRUE; + + /* Swap the forward direction if we're left handed (it was initialized based on right handed). */ + if (pListener->config.handedness == ma_handedness_left) { + pListener->direction = ma_vec3f_neg(pListener->direction); + } + + + /* We must always have a valid channel map. */ + pListener->config.pChannelMapOut = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapOutOffset); + + /* Use a slightly different default channel map for stereo. */ + if (pConfig->pChannelMapOut == NULL) { + ma_get_default_channel_map_for_spatializer(pListener->config.pChannelMapOut, pConfig->channelsOut, pConfig->channelsOut); + } else { + ma_channel_map_copy_or_default(pListener->config.pChannelMapOut, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelsOut); + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_spatializer_listener_init(const ma_spatializer_listener_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer_listener* pListener) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_spatializer_listener_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_spatializer_listener_init_preallocated(pConfig, pHeap, pListener); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pListener->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_spatializer_listener_uninit(ma_spatializer_listener* pListener, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pListener == NULL) { + return; + } + + if (pListener->_ownsHeap) { + ma_free(pListener->_pHeap, pAllocationCallbacks); + } +} + +MA_API ma_channel* ma_spatializer_listener_get_channel_map(ma_spatializer_listener* pListener) +{ + if (pListener == NULL) { + return NULL; + } + + return pListener->config.pChannelMapOut; +} + +MA_API void ma_spatializer_listener_set_cone(ma_spatializer_listener* pListener, float innerAngleInRadians, float outerAngleInRadians, float outerGain) +{ + if (pListener == NULL) { + return; + } + + pListener->config.coneInnerAngleInRadians = innerAngleInRadians; + pListener->config.coneOuterAngleInRadians = outerAngleInRadians; + pListener->config.coneOuterGain = outerGain; +} + +MA_API void ma_spatializer_listener_get_cone(const ma_spatializer_listener* pListener, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) +{ + if (pListener == NULL) { + return; + } + + if (pInnerAngleInRadians != NULL) { + *pInnerAngleInRadians = pListener->config.coneInnerAngleInRadians; + } + + if (pOuterAngleInRadians != NULL) { + *pOuterAngleInRadians = pListener->config.coneOuterAngleInRadians; + } + + if (pOuterGain != NULL) { + *pOuterGain = pListener->config.coneOuterGain; + } +} + +MA_API void ma_spatializer_listener_set_position(ma_spatializer_listener* pListener, float x, float y, float z) +{ + if (pListener == NULL) { + return; + } + + pListener->position = ma_vec3f_init_3f(x, y, z); +} + +MA_API ma_vec3f ma_spatializer_listener_get_position(const ma_spatializer_listener* pListener) +{ + if (pListener == NULL) { + return ma_vec3f_init_3f(0, 0, 0); + } + + return pListener->position; +} + +MA_API void ma_spatializer_listener_set_direction(ma_spatializer_listener* pListener, float x, float y, float z) +{ + if (pListener == NULL) { + return; + } + + pListener->direction = ma_vec3f_init_3f(x, y, z); +} + +MA_API ma_vec3f ma_spatializer_listener_get_direction(const ma_spatializer_listener* pListener) +{ + if (pListener == NULL) { + return ma_vec3f_init_3f(0, 0, -1); + } + + return pListener->direction; +} + +MA_API void ma_spatializer_listener_set_velocity(ma_spatializer_listener* pListener, float x, float y, float z) +{ + if (pListener == NULL) { + return; + } + + pListener->velocity = ma_vec3f_init_3f(x, y, z); +} + +MA_API ma_vec3f ma_spatializer_listener_get_velocity(const ma_spatializer_listener* pListener) +{ + if (pListener == NULL) { + return ma_vec3f_init_3f(0, 0, 0); + } + + return pListener->velocity; +} + +MA_API void ma_spatializer_listener_set_speed_of_sound(ma_spatializer_listener* pListener, float speedOfSound) +{ + if (pListener == NULL) { + return; + } + + pListener->config.speedOfSound = speedOfSound; +} + +MA_API float ma_spatializer_listener_get_speed_of_sound(const ma_spatializer_listener* pListener) +{ + if (pListener == NULL) { + return 0; + } + + return pListener->config.speedOfSound; +} + +MA_API void ma_spatializer_listener_set_world_up(ma_spatializer_listener* pListener, float x, float y, float z) +{ + if (pListener == NULL) { + return; + } + + pListener->config.worldUp = ma_vec3f_init_3f(x, y, z); +} + +MA_API ma_vec3f ma_spatializer_listener_get_world_up(const ma_spatializer_listener* pListener) +{ + if (pListener == NULL) { + return ma_vec3f_init_3f(0, 1, 0); + } + + return pListener->config.worldUp; +} + +MA_API void ma_spatializer_listener_set_enabled(ma_spatializer_listener* pListener, ma_bool32 isEnabled) +{ + if (pListener == NULL) { + return; + } + + pListener->isEnabled = isEnabled; +} + +MA_API ma_bool32 ma_spatializer_listener_is_enabled(const ma_spatializer_listener* pListener) +{ + if (pListener == NULL) { + return MA_FALSE; + } + + return pListener->isEnabled; +} + + + + +MA_API ma_spatializer_config ma_spatializer_config_init(ma_uint32 channelsIn, ma_uint32 channelsOut) +{ + ma_spatializer_config config; + + MA_ZERO_OBJECT(&config); + config.channelsIn = channelsIn; + config.channelsOut = channelsOut; + config.pChannelMapIn = NULL; + config.attenuationModel = ma_attenuation_model_inverse; + config.positioning = ma_positioning_absolute; + config.handedness = ma_handedness_right; + config.minGain = 0; + config.maxGain = 1; + config.minDistance = 1; + config.maxDistance = MA_FLT_MAX; + config.rolloff = 1; + config.coneInnerAngleInRadians = 6.283185f; /* 360 degrees. */ + config.coneOuterAngleInRadians = 6.283185f; /* 360 degress. */ + config.coneOuterGain = 0.0f; + config.dopplerFactor = 1; + config.directionalAttenuationFactor = 1; + config.gainSmoothTimeInFrames = 360; /* 7.5ms @ 48K. */ + + return config; +} + + +static ma_gainer_config ma_spatializer_gainer_config_init(const ma_spatializer_config* pConfig) +{ + MA_ASSERT(pConfig != NULL); + return ma_gainer_config_init(pConfig->channelsOut, pConfig->gainSmoothTimeInFrames); +} + +static ma_result ma_spatializer_validate_config(const ma_spatializer_config* pConfig) +{ + MA_ASSERT(pConfig != NULL); + + if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) { + return MA_INVALID_ARGS; + } + + return MA_SUCCESS; +} + +typedef struct +{ + size_t sizeInBytes; + size_t channelMapInOffset; + size_t newChannelGainsOffset; + size_t gainerOffset; +} ma_spatializer_heap_layout; + +static ma_result ma_spatializer_get_heap_layout(const ma_spatializer_config* pConfig, ma_spatializer_heap_layout* pHeapLayout) +{ + ma_result result; + + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + result = ma_spatializer_validate_config(pConfig); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->sizeInBytes = 0; + + /* Channel map. */ + pHeapLayout->channelMapInOffset = MA_SIZE_MAX; /* <-- MA_SIZE_MAX indicates no allocation necessary. */ + if (pConfig->pChannelMapIn != NULL) { + pHeapLayout->channelMapInOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(sizeof(*pConfig->pChannelMapIn) * pConfig->channelsIn); + } + + /* New channel gains for output. */ + pHeapLayout->newChannelGainsOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(sizeof(float) * pConfig->channelsOut); + + /* Gainer. */ + { + size_t gainerHeapSizeInBytes; + ma_gainer_config gainerConfig; + + gainerConfig = ma_spatializer_gainer_config_init(pConfig); + + result = ma_gainer_get_heap_size(&gainerConfig, &gainerHeapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->gainerOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(gainerHeapSizeInBytes); + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_spatializer_get_heap_size(const ma_spatializer_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_spatializer_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; /* Safety. */ + + result = ma_spatializer_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return MA_SUCCESS; +} + + +MA_API ma_result ma_spatializer_init_preallocated(const ma_spatializer_config* pConfig, void* pHeap, ma_spatializer* pSpatializer) +{ + ma_result result; + ma_spatializer_heap_layout heapLayout; + ma_gainer_config gainerConfig; + + if (pSpatializer == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pSpatializer); + + if (pConfig == NULL || pHeap == NULL) { + return MA_INVALID_ARGS; + } + + result = ma_spatializer_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pSpatializer->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pSpatializer->channelsIn = pConfig->channelsIn; + pSpatializer->channelsOut = pConfig->channelsOut; + pSpatializer->attenuationModel = pConfig->attenuationModel; + pSpatializer->positioning = pConfig->positioning; + pSpatializer->handedness = pConfig->handedness; + pSpatializer->minGain = pConfig->minGain; + pSpatializer->maxGain = pConfig->maxGain; + pSpatializer->minDistance = pConfig->minDistance; + pSpatializer->maxDistance = pConfig->maxDistance; + pSpatializer->rolloff = pConfig->rolloff; + pSpatializer->coneInnerAngleInRadians = pConfig->coneInnerAngleInRadians; + pSpatializer->coneOuterAngleInRadians = pConfig->coneOuterAngleInRadians; + pSpatializer->coneOuterGain = pConfig->coneOuterGain; + pSpatializer->dopplerFactor = pConfig->dopplerFactor; + pSpatializer->directionalAttenuationFactor = pConfig->directionalAttenuationFactor; + pSpatializer->gainSmoothTimeInFrames = pConfig->gainSmoothTimeInFrames; + pSpatializer->position = ma_vec3f_init_3f(0, 0, 0); + pSpatializer->direction = ma_vec3f_init_3f(0, 0, -1); + pSpatializer->velocity = ma_vec3f_init_3f(0, 0, 0); + pSpatializer->dopplerPitch = 1; + + /* Swap the forward direction if we're left handed (it was initialized based on right handed). */ + if (pSpatializer->handedness == ma_handedness_left) { + pSpatializer->direction = ma_vec3f_neg(pSpatializer->direction); + } + + /* Channel map. This will be on the heap. */ + if (pConfig->pChannelMapIn != NULL) { + pSpatializer->pChannelMapIn = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapInOffset); + ma_channel_map_copy_or_default(pSpatializer->pChannelMapIn, pSpatializer->channelsIn, pConfig->pChannelMapIn, pSpatializer->channelsIn); + } + + /* New channel gains for output channels. */ + pSpatializer->pNewChannelGainsOut = (float*)ma_offset_ptr(pHeap, heapLayout.newChannelGainsOffset); + + /* Gainer. */ + gainerConfig = ma_spatializer_gainer_config_init(pConfig); + + result = ma_gainer_init_preallocated(&gainerConfig, ma_offset_ptr(pHeap, heapLayout.gainerOffset), &pSpatializer->gainer); + if (result != MA_SUCCESS) { + return result; /* Failed to initialize the gainer. */ + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_spatializer_init(const ma_spatializer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer* pSpatializer) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + /* We'll need a heap allocation to retrieve the size. */ + result = ma_spatializer_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_spatializer_init_preallocated(pConfig, pHeap, pSpatializer); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pSpatializer->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_spatializer_uninit(ma_spatializer* pSpatializer, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pSpatializer == NULL) { + return; + } + + ma_gainer_uninit(&pSpatializer->gainer, pAllocationCallbacks); + + if (pSpatializer->_ownsHeap) { + ma_free(pSpatializer->_pHeap, pAllocationCallbacks); + } +} + +static float ma_calculate_angular_gain(ma_vec3f dirA, ma_vec3f dirB, float coneInnerAngleInRadians, float coneOuterAngleInRadians, float coneOuterGain) +{ + /* + Angular attenuation. + + Unlike distance gain, the math for this is not specified by the OpenAL spec so we'll just go ahead and figure + this out for ourselves at the expense of possibly being inconsistent with other implementations. + + To do cone attenuation, I'm just using the same math that we'd use to implement a basic spotlight in OpenGL. We + just need to get the direction from the source to the listener and then do a dot product against that and the + direction of the spotlight. Then we just compare that dot product against the cosine of the inner and outer + angles. If the dot product is greater than the the outer angle, we just use coneOuterGain. If it's less than + the inner angle, we just use a gain of 1. Otherwise we linearly interpolate between 1 and coneOuterGain. + */ + if (coneInnerAngleInRadians < 6.283185f) { + float angularGain = 1; + float cutoffInner = (float)ma_cosd(coneInnerAngleInRadians*0.5f); + float cutoffOuter = (float)ma_cosd(coneOuterAngleInRadians*0.5f); + float d; + + d = ma_vec3f_dot(dirA, dirB); + + if (d > cutoffInner) { + /* It's inside the inner angle. */ + angularGain = 1; + } else { + /* It's outside the inner angle. */ + if (d > cutoffOuter) { + /* It's between the inner and outer angle. We need to linearly interpolate between 1 and coneOuterGain. */ + angularGain = ma_mix_f32(coneOuterGain, 1, (d - cutoffOuter) / (cutoffInner - cutoffOuter)); + } else { + /* It's outside the outer angle. */ + angularGain = coneOuterGain; + } + } + + /*printf("d = %f; cutoffInner = %f; cutoffOuter = %f; angularGain = %f\n", d, cutoffInner, cutoffOuter, angularGain);*/ + return angularGain; + } else { + /* Inner angle is 360 degrees so no need to do any attenuation. */ + return 1; + } +} + +MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, ma_spatializer_listener* pListener, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + ma_channel* pChannelMapIn = pSpatializer->pChannelMapIn; + ma_channel* pChannelMapOut = pListener->config.pChannelMapOut; + + if (pSpatializer == NULL) { + return MA_INVALID_ARGS; + } + + /* If we're not spatializing we need to run an optimized path. */ + if (c89atomic_load_i32(&pSpatializer->attenuationModel) == ma_attenuation_model_none) { + if (ma_spatializer_listener_is_enabled(pListener)) { + /* No attenuation is required, but we'll need to do some channel conversion. */ + if (pSpatializer->channelsIn == pSpatializer->channelsOut) { + ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, ma_format_f32, pSpatializer->channelsIn); + } else { + ma_channel_map_apply_f32((float*)pFramesOut, pChannelMapOut, pSpatializer->channelsOut, (const float*)pFramesIn, pChannelMapIn, pSpatializer->channelsIn, frameCount, ma_channel_mix_mode_rectangular, ma_mono_expansion_mode_default); /* Safe casts to float* because f32 is the only supported format. */ + } + } else { + /* The listener is disabled. Output silence. */ + ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut); + } + + /* + We're not doing attenuation so don't bother with doppler for now. I'm not sure if this is + the correct thinking so might need to review this later. + */ + pSpatializer->dopplerPitch = 1; + } else { + /* + Let's first determine which listener the sound is closest to. Need to keep in mind that we + might not have a world or any listeners, in which case we just spatializer based on the + listener being positioned at the origin (0, 0, 0). + */ + ma_vec3f relativePosNormalized; + ma_vec3f relativePos; /* The position relative to the listener. */ + ma_vec3f relativeDir; /* The direction of the sound, relative to the listener. */ + ma_vec3f listenerVel; /* The volocity of the listener. For doppler pitch calculation. */ + float speedOfSound; + float distance = 0; + float gain = 1; + ma_uint32 iChannel; + const ma_uint32 channelsOut = pSpatializer->channelsOut; + const ma_uint32 channelsIn = pSpatializer->channelsIn; + float minDistance = ma_spatializer_get_min_distance(pSpatializer); + float maxDistance = ma_spatializer_get_max_distance(pSpatializer); + float rolloff = ma_spatializer_get_rolloff(pSpatializer); + float dopplerFactor = ma_spatializer_get_doppler_factor(pSpatializer); + + /* + We'll need the listener velocity for doppler pitch calculations. The speed of sound is + defined by the listener, so we'll grab that here too. + */ + if (pListener != NULL) { + listenerVel = pListener->velocity; + speedOfSound = pListener->config.speedOfSound; + } else { + listenerVel = ma_vec3f_init_3f(0, 0, 0); + speedOfSound = MA_DEFAULT_SPEED_OF_SOUND; + } + + if (pListener == NULL || ma_spatializer_get_positioning(pSpatializer) == ma_positioning_relative) { + /* There's no listener or we're using relative positioning. */ + relativePos = pSpatializer->position; + relativeDir = pSpatializer->direction; + } else { + /* + We've found a listener and we're using absolute positioning. We need to transform the + sound's position and direction so that it's relative to listener. Later on we'll use + this for determining the factors to apply to each channel to apply the panning effect. + */ + ma_spatializer_get_relative_position_and_direction(pSpatializer, pListener, &relativePos, &relativeDir); + } + + distance = ma_vec3f_len(relativePos); + + /* We've gathered the data, so now we can apply some spatialization. */ + switch (ma_spatializer_get_attenuation_model(pSpatializer)) { + case ma_attenuation_model_inverse: + { + gain = ma_attenuation_inverse(distance, minDistance, maxDistance, rolloff); + } break; + case ma_attenuation_model_linear: + { + gain = ma_attenuation_linear(distance, minDistance, maxDistance, rolloff); + } break; + case ma_attenuation_model_exponential: + { + gain = ma_attenuation_exponential(distance, minDistance, maxDistance, rolloff); + } break; + case ma_attenuation_model_none: + default: + { + gain = 1; + } break; + } + + /* Normalize the position. */ + if (distance > 0.001f) { + float distanceInv = 1/distance; + relativePosNormalized = relativePos; + relativePosNormalized.x *= distanceInv; + relativePosNormalized.y *= distanceInv; + relativePosNormalized.z *= distanceInv; + } else { + distance = 0; + relativePosNormalized = ma_vec3f_init_3f(0, 0, 0); + } + + /* + Angular attenuation. + + Unlike distance gain, the math for this is not specified by the OpenAL spec so we'll just go ahead and figure + this out for ourselves at the expense of possibly being inconsistent with other implementations. + + To do cone attenuation, I'm just using the same math that we'd use to implement a basic spotlight in OpenGL. We + just need to get the direction from the source to the listener and then do a dot product against that and the + direction of the spotlight. Then we just compare that dot product against the cosine of the inner and outer + angles. If the dot product is greater than the the outer angle, we just use coneOuterGain. If it's less than + the inner angle, we just use a gain of 1. Otherwise we linearly interpolate between 1 and coneOuterGain. + */ + if (distance > 0) { + /* Source anglular gain. */ + float spatializerConeInnerAngle; + float spatializerConeOuterAngle; + float spatializerConeOuterGain; + ma_spatializer_get_cone(pSpatializer, &spatializerConeInnerAngle, &spatializerConeOuterAngle, &spatializerConeOuterGain); + + gain *= ma_calculate_angular_gain(relativeDir, ma_vec3f_neg(relativePosNormalized), spatializerConeInnerAngle, spatializerConeOuterAngle, spatializerConeOuterGain); + + /* + We're supporting angular gain on the listener as well for those who want to reduce the volume of sounds that + are positioned behind the listener. On default settings, this will have no effect. + */ + if (pListener != NULL && pListener->config.coneInnerAngleInRadians < 6.283185f) { + ma_vec3f listenerDirection; + float listenerInnerAngle; + float listenerOuterAngle; + float listenerOuterGain; + + if (pListener->config.handedness == ma_handedness_right) { + listenerDirection = ma_vec3f_init_3f(0, 0, -1); + } else { + listenerDirection = ma_vec3f_init_3f(0, 0, +1); + } + + listenerInnerAngle = pListener->config.coneInnerAngleInRadians; + listenerOuterAngle = pListener->config.coneOuterAngleInRadians; + listenerOuterGain = pListener->config.coneOuterGain; + + gain *= ma_calculate_angular_gain(listenerDirection, relativePosNormalized, listenerInnerAngle, listenerOuterAngle, listenerOuterGain); + } + } else { + /* The sound is right on top of the listener. Don't do any angular attenuation. */ + } + + + /* Clamp the gain. */ + gain = ma_clamp(gain, ma_spatializer_get_min_gain(pSpatializer), ma_spatializer_get_max_gain(pSpatializer)); + + /* + Panning. This is where we'll apply the gain and convert to the output channel count. We have an optimized path for + when we're converting to a mono stream. In that case we don't really need to do any panning - we just apply the + gain to the final output. + */ + /*printf("distance=%f; gain=%f\n", distance, gain);*/ + + /* We must have a valid channel map here to ensure we spatialize properly. */ + MA_ASSERT(pChannelMapOut != NULL); + + /* + We're not converting to mono so we'll want to apply some panning. This is where the feeling of something being + to the left, right, infront or behind the listener is calculated. I'm just using a basic model here. Note that + the code below is not based on any specific algorithm. I'm just implementing this off the top of my head and + seeing how it goes. There might be better ways to do this. + + To determine the direction of the sound relative to a speaker I'm using dot products. Each speaker is given a + direction. For example, the left channel in a stereo system will be -1 on the X axis and the right channel will + be +1 on the X axis. A dot product is performed against the direction vector of the channel and the normalized + position of the sound. + */ + for (iChannel = 0; iChannel < channelsOut; iChannel += 1) { + pSpatializer->pNewChannelGainsOut[iChannel] = gain; + } + + /* + Convert to our output channel count. If the listener is disabled we just output silence here. We cannot ignore + the whole section of code here because we need to update some internal spatialization state. + */ + if (ma_spatializer_listener_is_enabled(pListener)) { + ma_channel_map_apply_f32((float*)pFramesOut, pChannelMapOut, channelsOut, (const float*)pFramesIn, pChannelMapIn, channelsIn, frameCount, ma_channel_mix_mode_rectangular, ma_mono_expansion_mode_default); + } else { + ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut); + } + + /* + Calculate our per-channel gains. We do this based on the normalized relative position of the sound and it's + relation to the direction of the channel. + */ + if (distance > 0) { + ma_vec3f unitPos = relativePos; + float distanceInv = 1/distance; + unitPos.x *= distanceInv; + unitPos.y *= distanceInv; + unitPos.z *= distanceInv; + + for (iChannel = 0; iChannel < channelsOut; iChannel += 1) { + ma_channel channelOut; + float d; + float dMin; + + channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannel); + if (ma_is_spatial_channel_position(channelOut)) { + d = ma_mix_f32_fast(1, ma_vec3f_dot(unitPos, ma_get_channel_direction(channelOut)), ma_spatializer_get_directional_attenuation_factor(pSpatializer)); + } else { + d = 1; /* It's not a spatial channel so there's no real notion of direction. */ + } + + /* + In my testing, if the panning effect is too aggressive it makes spatialization feel uncomfortable. + The "dMin" variable below is used to control the aggressiveness of the panning effect. When set to + 0, panning will be most extreme and any sounds that are positioned on the opposite side of the + speaker will be completely silent from that speaker. Not only does this feel uncomfortable, it + doesn't even remotely represent the real world at all because sounds that come from your right side + are still clearly audible from your left side. Setting "dMin" to 1 will result in no panning at + all, which is also not ideal. By setting it to something greater than 0, the spatialization effect + becomes much less dramatic and a lot more bearable. + + Summary: 0 = more extreme panning; 1 = no panning. + */ + dMin = 0.2f; /* TODO: Consider making this configurable. */ + + /* + At this point, "d" will be positive if the sound is on the same side as the channel and negative if + it's on the opposite side. It will be in the range of -1..1. There's two ways I can think of to + calculate a panning value. The first is to simply convert it to 0..1, however this has a problem + which I'm not entirely happy with. Considering a stereo system, when a sound is positioned right + in front of the listener it'll result in each speaker getting a gain of 0.5. I don't know if I like + the idea of having a scaling factor of 0.5 being applied to a sound when it's sitting right in front + of the listener. I would intuitively expect that to be played at full volume, or close to it. + + The second idea I think of is to only apply a reduction in gain when the sound is on the opposite + side of the speaker. That is, reduce the gain only when the dot product is negative. The problem + with this is that there will not be any attenuation as the sound sweeps around the 180 degrees + where the dot product is positive. The idea with this option is that you leave the gain at 1 when + the sound is being played on the same side as the speaker and then you just reduce the volume when + the sound is on the other side. + + The summarize, I think the first option should give a better sense of spatialization, but the second + option is better for preserving the sound's power. + + UPDATE: In my testing, I find the first option to sound better. You can feel the sense of space a + bit better, but you can also hear the reduction in volume when it's right in front. + */ + #if 1 + { + /* + Scale the dot product from -1..1 to 0..1. Will result in a sound directly in front losing power + by being played at 0.5 gain. + */ + d = (d + 1) * 0.5f; /* -1..1 to 0..1 */ + d = ma_max(d, dMin); + pSpatializer->pNewChannelGainsOut[iChannel] *= d; + } + #else + { + /* + Only reduce the volume of the sound if it's on the opposite side. This path keeps the volume more + consistent, but comes at the expense of a worse sense of space and positioning. + */ + if (d < 0) { + d += 1; /* Move into the positive range. */ + d = ma_max(d, dMin); + channelGainsOut[iChannel] *= d; + } + } + #endif + } + } else { + /* Assume the sound is right on top of us. Don't do any panning. */ + } + + /* Now we need to apply the volume to each channel. This needs to run through the gainer to ensure we get a smooth volume transition. */ + ma_gainer_set_gains(&pSpatializer->gainer, pSpatializer->pNewChannelGainsOut); + ma_gainer_process_pcm_frames(&pSpatializer->gainer, pFramesOut, pFramesOut, frameCount); + + /* + Before leaving we'll want to update our doppler pitch so that the caller can apply some + pitch shifting if they desire. Note that we need to negate the relative position here + because the doppler calculation needs to be source-to-listener, but ours is listener-to- + source. + */ + if (dopplerFactor > 0) { + pSpatializer->dopplerPitch = ma_doppler_pitch(ma_vec3f_sub(pListener->position, pSpatializer->position), pSpatializer->velocity, listenerVel, speedOfSound, dopplerFactor); + } else { + pSpatializer->dopplerPitch = 1; + } + } + + return MA_SUCCESS; +} + +MA_API ma_uint32 ma_spatializer_get_input_channels(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return 0; + } + + return pSpatializer->channelsIn; +} + +MA_API ma_uint32 ma_spatializer_get_output_channels(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return 0; + } + + return pSpatializer->channelsOut; +} + +MA_API void ma_spatializer_set_attenuation_model(ma_spatializer* pSpatializer, ma_attenuation_model attenuationModel) +{ + if (pSpatializer == NULL) { + return; + } + + c89atomic_exchange_i32(&pSpatializer->attenuationModel, attenuationModel); +} + +MA_API ma_attenuation_model ma_spatializer_get_attenuation_model(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return ma_attenuation_model_none; + } + + return (ma_attenuation_model)c89atomic_load_i32(&pSpatializer->attenuationModel); +} + +MA_API void ma_spatializer_set_positioning(ma_spatializer* pSpatializer, ma_positioning positioning) +{ + if (pSpatializer == NULL) { + return; + } + + c89atomic_exchange_i32(&pSpatializer->positioning, positioning); +} + +MA_API ma_positioning ma_spatializer_get_positioning(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return ma_positioning_absolute; + } + + return (ma_positioning)c89atomic_load_i32(&pSpatializer->positioning); +} + +MA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rolloff) +{ + if (pSpatializer == NULL) { + return; + } + + c89atomic_exchange_f32(&pSpatializer->rolloff, rolloff); +} + +MA_API float ma_spatializer_get_rolloff(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return 0; + } + + return c89atomic_load_f32(&pSpatializer->rolloff); +} + +MA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minGain) +{ + if (pSpatializer == NULL) { + return; + } + + c89atomic_exchange_f32(&pSpatializer->minGain, minGain); +} + +MA_API float ma_spatializer_get_min_gain(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return 0; + } + + return c89atomic_load_f32(&pSpatializer->minGain); +} + +MA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxGain) +{ + if (pSpatializer == NULL) { + return; + } + + c89atomic_exchange_f32(&pSpatializer->maxGain, maxGain); +} + +MA_API float ma_spatializer_get_max_gain(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return 0; + } + + return c89atomic_load_f32(&pSpatializer->maxGain); +} + +MA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float minDistance) +{ + if (pSpatializer == NULL) { + return; + } + + c89atomic_exchange_f32(&pSpatializer->minDistance, minDistance); +} + +MA_API float ma_spatializer_get_min_distance(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return 0; + } + + return c89atomic_load_f32(&pSpatializer->minDistance); +} + +MA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float maxDistance) +{ + if (pSpatializer == NULL) { + return; + } + + c89atomic_exchange_f32(&pSpatializer->maxDistance, maxDistance); +} + +MA_API float ma_spatializer_get_max_distance(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return 0; + } + + return c89atomic_load_f32(&pSpatializer->maxDistance); +} + +MA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAngleInRadians, float outerAngleInRadians, float outerGain) +{ + if (pSpatializer == NULL) { + return; + } + + c89atomic_exchange_f32(&pSpatializer->coneInnerAngleInRadians, innerAngleInRadians); + c89atomic_exchange_f32(&pSpatializer->coneOuterAngleInRadians, outerAngleInRadians); + c89atomic_exchange_f32(&pSpatializer->coneOuterGain, outerGain); +} + +MA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) +{ + if (pSpatializer == NULL) { + return; + } + + if (pInnerAngleInRadians != NULL) { + *pInnerAngleInRadians = c89atomic_load_f32(&pSpatializer->coneInnerAngleInRadians); + } + + if (pOuterAngleInRadians != NULL) { + *pOuterAngleInRadians = c89atomic_load_f32(&pSpatializer->coneOuterAngleInRadians); + } + + if (pOuterGain != NULL) { + *pOuterGain = c89atomic_load_f32(&pSpatializer->coneOuterGain); + } +} + +MA_API void ma_spatializer_set_doppler_factor(ma_spatializer* pSpatializer, float dopplerFactor) +{ + if (pSpatializer == NULL) { + return; + } + + c89atomic_exchange_f32(&pSpatializer->dopplerFactor, dopplerFactor); +} + +MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return 1; + } + + return c89atomic_load_f32(&pSpatializer->dopplerFactor); +} + +MA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pSpatializer, float directionalAttenuationFactor) +{ + if (pSpatializer == NULL) { + return; + } + + c89atomic_exchange_f32(&pSpatializer->directionalAttenuationFactor, directionalAttenuationFactor); +} + +MA_API float ma_spatializer_get_directional_attenuation_factor(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return 1; + } + + return c89atomic_load_f32(&pSpatializer->directionalAttenuationFactor); +} + +MA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, float y, float z) +{ + if (pSpatializer == NULL) { + return; + } + + pSpatializer->position = ma_vec3f_init_3f(x, y, z); +} + +MA_API ma_vec3f ma_spatializer_get_position(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return ma_vec3f_init_3f(0, 0, 0); + } + + return pSpatializer->position; +} + +MA_API void ma_spatializer_set_direction(ma_spatializer* pSpatializer, float x, float y, float z) +{ + if (pSpatializer == NULL) { + return; + } + + pSpatializer->direction = ma_vec3f_init_3f(x, y, z); +} + +MA_API ma_vec3f ma_spatializer_get_direction(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return ma_vec3f_init_3f(0, 0, -1); + } + + return pSpatializer->direction; +} + +MA_API void ma_spatializer_set_velocity(ma_spatializer* pSpatializer, float x, float y, float z) +{ + if (pSpatializer == NULL) { + return; + } + + pSpatializer->velocity = ma_vec3f_init_3f(x, y, z); +} + +MA_API ma_vec3f ma_spatializer_get_velocity(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return ma_vec3f_init_3f(0, 0, 0); + } + + return pSpatializer->velocity; +} + +MA_API void ma_spatializer_get_relative_position_and_direction(const ma_spatializer* pSpatializer, const ma_spatializer_listener* pListener, ma_vec3f* pRelativePos, ma_vec3f* pRelativeDir) +{ + if (pRelativePos != NULL) { + pRelativePos->x = 0; + pRelativePos->y = 0; + pRelativePos->z = 0; + } + + if (pRelativeDir != NULL) { + pRelativeDir->x = 0; + pRelativeDir->y = 0; + pRelativeDir->z = -1; + } + + if (pSpatializer == NULL) { + return; + } + + if (pListener == NULL || ma_spatializer_get_positioning(pSpatializer) == ma_positioning_relative) { + /* There's no listener or we're using relative positioning. */ + if (pRelativePos != NULL) { + *pRelativePos = pSpatializer->position; + } + if (pRelativeDir != NULL) { + *pRelativeDir = pSpatializer->direction; + } + } else { + ma_vec3f v; + ma_vec3f axisX; + ma_vec3f axisY; + ma_vec3f axisZ; + float m[4][4]; + + /* + We need to calcualte the right vector from our forward and up vectors. This is done with + a cross product. + */ + axisZ = ma_vec3f_normalize(pListener->direction); /* Normalization required here because we can't trust the caller. */ + axisX = ma_vec3f_normalize(ma_vec3f_cross(axisZ, pListener->config.worldUp)); /* Normalization required here because the world up vector may not be perpendicular with the forward vector. */ + + /* + The calculation of axisX above can result in a zero-length vector if the listener is + looking straight up on the Y axis. We'll need to fall back to a +X in this case so that + the calculations below don't fall apart. This is where a quaternion based listener and + sound orientation would come in handy. + */ + if (ma_vec3f_len2(axisX) == 0) { + axisX = ma_vec3f_init_3f(1, 0, 0); + } + + axisY = ma_vec3f_cross(axisX, axisZ); /* No normalization is required here because axisX and axisZ are unit length and perpendicular. */ + + /* + We need to swap the X axis if we're left handed because otherwise the cross product above + will have resulted in it pointing in the wrong direction (right handed was assumed in the + cross products above). + */ + if (pListener->config.handedness == ma_handedness_left) { + axisX = ma_vec3f_neg(axisX); + } + + /* Lookat. */ + m[0][0] = axisX.x; m[1][0] = axisX.y; m[2][0] = axisX.z; m[3][0] = -ma_vec3f_dot(axisX, pListener->position); + m[0][1] = axisY.x; m[1][1] = axisY.y; m[2][1] = axisY.z; m[3][1] = -ma_vec3f_dot(axisY, pListener->position); + m[0][2] = -axisZ.x; m[1][2] = -axisZ.y; m[2][2] = -axisZ.z; m[3][2] = -ma_vec3f_dot(ma_vec3f_neg(axisZ), pListener->position); + m[0][3] = 0; m[1][3] = 0; m[2][3] = 0; m[3][3] = 1; + + /* + Multiply the lookat matrix by the spatializer position to transform it to listener + space. This allows calculations to work based on the sound being relative to the + origin which makes things simpler. + */ + if (pRelativePos != NULL) { + v = pSpatializer->position; + pRelativePos->x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z + m[3][0] * 1; + pRelativePos->y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z + m[3][1] * 1; + pRelativePos->z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z + m[3][2] * 1; + } + + /* + The direction of the sound needs to also be transformed so that it's relative to the + rotation of the listener. + */ + if (pRelativeDir != NULL) { + v = pSpatializer->direction; + pRelativeDir->x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z; + pRelativeDir->y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z; + pRelativeDir->z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z; + } + } +} + + + + /************************************************************************************************************************************************************** Resampling @@ -38994,6 +49032,16 @@ MA_API ma_linear_resampler_config ma_linear_resampler_config_init(ma_format form return config; } + +typedef struct +{ + size_t sizeInBytes; + size_t x0Offset; + size_t x1Offset; + size_t lpfOffset; +} ma_linear_resampler_heap_layout; + + static void ma_linear_resampler_adjust_timer_for_new_rate(ma_linear_resampler* pResampler, ma_uint32 oldSampleRateOut, ma_uint32 newSampleRateOut) { /* @@ -39012,7 +49060,7 @@ static void ma_linear_resampler_adjust_timer_for_new_rate(ma_linear_resampler* p pResampler->inTimeFrac = pResampler->inTimeFrac % pResampler->config.sampleRateOut; } -static ma_result ma_linear_resampler_set_rate_internal(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_bool32 isResamplerAlreadyInitialized) +static ma_result ma_linear_resampler_set_rate_internal(ma_linear_resampler* pResampler, void* pHeap, ma_linear_resampler_heap_layout* pHeapLayout, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_bool32 isResamplerAlreadyInitialized) { ma_result result; ma_uint32 gcf; @@ -39056,7 +49104,7 @@ static ma_result ma_linear_resampler_set_rate_internal(ma_linear_resampler* pRes if (isResamplerAlreadyInitialized) { result = ma_lpf_reinit(&lpfConfig, &pResampler->lpf); } else { - result = ma_lpf_init(&lpfConfig, &pResampler->lpf); + result = ma_lpf_init_preallocated(&lpfConfig, ma_offset_ptr(pHeap, pHeapLayout->lpfOffset), &pResampler->lpf); } if (result != MA_SUCCESS) { @@ -39073,9 +49121,88 @@ static ma_result ma_linear_resampler_set_rate_internal(ma_linear_resampler* pRes return MA_SUCCESS; } -MA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, ma_linear_resampler* pResampler) +static ma_result ma_linear_resampler_get_heap_layout(const ma_linear_resampler_config* pConfig, ma_linear_resampler_heap_layout* pHeapLayout) +{ + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { + return MA_INVALID_ARGS; + } + + if (pConfig->channels == 0) { + return MA_INVALID_ARGS; + } + + pHeapLayout->sizeInBytes = 0; + + /* x0 */ + pHeapLayout->x0Offset = pHeapLayout->sizeInBytes; + if (pConfig->format == ma_format_f32) { + pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; + } else { + pHeapLayout->sizeInBytes += sizeof(ma_int16) * pConfig->channels; + } + + /* x1 */ + pHeapLayout->x1Offset = pHeapLayout->sizeInBytes; + if (pConfig->format == ma_format_f32) { + pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; + } else { + pHeapLayout->sizeInBytes += sizeof(ma_int16) * pConfig->channels; + } + + /* LPF */ + pHeapLayout->lpfOffset = pHeapLayout->sizeInBytes; + { + ma_result result; + size_t lpfHeapSizeInBytes; + ma_lpf_config lpfConfig = ma_lpf_config_init(pConfig->format, pConfig->channels, 1, 1, pConfig->lpfOrder); /* Sample rate and cutoff frequency do not matter. */ + + result = ma_lpf_get_heap_size(&lpfConfig, &lpfHeapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->sizeInBytes += lpfHeapSizeInBytes; + } + + /* Make sure allocation size is aligned. */ + pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); + + return MA_SUCCESS; +} + +MA_API ma_result ma_linear_resampler_get_heap_size(const ma_linear_resampler_config* pConfig, size_t* pHeapSizeInBytes) { ma_result result; + ma_linear_resampler_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_linear_resampler_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return MA_SUCCESS; +} + +MA_API ma_result ma_linear_resampler_init_preallocated(const ma_linear_resampler_config* pConfig, void* pHeap, ma_linear_resampler* pResampler) +{ + ma_result result; + ma_linear_resampler_heap_layout heapLayout; if (pResampler == NULL) { return MA_INVALID_ARGS; @@ -39083,18 +49210,26 @@ MA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pCon MA_ZERO_OBJECT(pResampler); - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels < MA_MIN_CHANNELS || pConfig->channels > MA_MAX_CHANNELS) { - return MA_INVALID_ARGS; + result = ma_linear_resampler_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; } pResampler->config = *pConfig; + pResampler->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + if (pConfig->format == ma_format_f32) { + pResampler->x0.f32 = (float*)ma_offset_ptr(pHeap, heapLayout.x0Offset); + pResampler->x1.f32 = (float*)ma_offset_ptr(pHeap, heapLayout.x1Offset); + } else { + pResampler->x0.s16 = (ma_int16*)ma_offset_ptr(pHeap, heapLayout.x0Offset); + pResampler->x1.s16 = (ma_int16*)ma_offset_ptr(pHeap, heapLayout.x1Offset); + } + /* Setting the rate will set up the filter and time advances for us. */ - result = ma_linear_resampler_set_rate_internal(pResampler, pConfig->sampleRateIn, pConfig->sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_FALSE); + result = ma_linear_resampler_set_rate_internal(pResampler, pHeap, &heapLayout, pConfig->sampleRateIn, pConfig->sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_FALSE); if (result != MA_SUCCESS) { return result; } @@ -39105,11 +49240,47 @@ MA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pCon return MA_SUCCESS; } -MA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler) +MA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_linear_resampler* pResampler) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_linear_resampler_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_linear_resampler_init_preallocated(pConfig, pHeap, pResampler); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pResampler->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks) { if (pResampler == NULL) { return; } + + ma_lpf_uninit(&pResampler->lpf, pAllocationCallbacks); + + if (pResampler->_ownsHeap) { + ma_free(pResampler->_pHeap, pAllocationCallbacks); + } } static MA_INLINE ma_int16 ma_linear_resampler_mix_s16(ma_int16 x, ma_int16 y, ma_int32 a, const ma_int32 shift) @@ -39139,7 +49310,7 @@ static void ma_linear_resampler_interpolate_frame_s16(ma_linear_resampler* pResa a = (pResampler->inTimeFrac << shift) / pResampler->config.sampleRateOut; - MA_ASSUME(channels >= MA_MIN_CHANNELS && channels <= MA_MAX_CHANNELS); + MA_ASSUME(channels > 0); for (c = 0; c < channels; c += 1) { ma_int16 s = ma_linear_resampler_mix_s16(pResampler->x0.s16[c], pResampler->x1.s16[c], a, shift); pFrameOut[c] = s; @@ -39158,7 +49329,7 @@ static void ma_linear_resampler_interpolate_frame_f32(ma_linear_resampler* pResa a = (float)pResampler->inTimeFrac / pResampler->config.sampleRateOut; - MA_ASSUME(channels >= MA_MIN_CHANNELS && channels <= MA_MAX_CHANNELS); + MA_ASSUME(channels > 0); for (c = 0; c < channels; c += 1) { float s = ma_mix_f32_fast(pResampler->x0.f32[c], pResampler->x1.f32[c], a); pFrameOut[c] = s; @@ -39505,7 +49676,7 @@ MA_API ma_result ma_linear_resampler_process_pcm_frames(ma_linear_resampler* pRe MA_API ma_result ma_linear_resampler_set_rate(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) { - return ma_linear_resampler_set_rate_internal(pResampler, sampleRateIn, sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_TRUE); + return ma_linear_resampler_set_rate_internal(pResampler, NULL, NULL, sampleRateIn, sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_TRUE); } MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResampler, float ratioInOut) @@ -39513,6 +49684,14 @@ MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResamp ma_uint32 n; ma_uint32 d; + if (pResampler == NULL) { + return MA_INVALID_ARGS; + } + + if (ratioInOut <= 0) { + return MA_INVALID_ARGS; + } + d = 1000; n = (ma_uint32)(ratioInOut * d); @@ -39525,19 +49704,42 @@ MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResamp return ma_linear_resampler_set_rate(pResampler, n, d); } - -MA_API ma_uint64 ma_linear_resampler_get_required_input_frame_count(const ma_linear_resampler* pResampler, ma_uint64 outputFrameCount) +MA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler* pResampler) { - ma_uint64 inputFrameCount; - if (pResampler == NULL) { return 0; } - if (outputFrameCount == 0) { + return 1 + ma_lpf_get_latency(&pResampler->lpf); +} + +MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resampler* pResampler) +{ + if (pResampler == NULL) { return 0; } + return ma_linear_resampler_get_input_latency(pResampler) * pResampler->config.sampleRateOut / pResampler->config.sampleRateIn; +} + +MA_API ma_result ma_linear_resampler_get_required_input_frame_count(const ma_linear_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) +{ + ma_uint64 inputFrameCount; + + if (pInputFrameCount == NULL) { + return MA_INVALID_ARGS; + } + + *pInputFrameCount = 0; + + if (pResampler == NULL) { + return MA_INVALID_ARGS; + } + + if (outputFrameCount == 0) { + return MA_SUCCESS; + } + /* Any whole input frames are consumed before the first output frame is generated. */ inputFrameCount = pResampler->inTimeInt; outputFrameCount -= 1; @@ -39546,17 +49748,25 @@ MA_API ma_uint64 ma_linear_resampler_get_required_input_frame_count(const ma_lin inputFrameCount += outputFrameCount * pResampler->inAdvanceInt; inputFrameCount += (pResampler->inTimeFrac + (outputFrameCount * pResampler->inAdvanceFrac)) / pResampler->config.sampleRateOut; - return inputFrameCount; + *pInputFrameCount = inputFrameCount; + + return MA_SUCCESS; } -MA_API ma_uint64 ma_linear_resampler_get_expected_output_frame_count(const ma_linear_resampler* pResampler, ma_uint64 inputFrameCount) +MA_API ma_result ma_linear_resampler_get_expected_output_frame_count(const ma_linear_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) { ma_uint64 outputFrameCount; ma_uint64 preliminaryInputFrameCountFromFrac; ma_uint64 preliminaryInputFrameCount; + if (pOutputFrameCount == NULL) { + return MA_INVALID_ARGS; + } + + *pOutputFrameCount = 0; + if (pResampler == NULL) { - return 0; + return MA_INVALID_ARGS; } /* @@ -39583,45 +49793,157 @@ MA_API ma_uint64 ma_linear_resampler_get_expected_output_frame_count(const ma_li outputFrameCount += 1; } - return outputFrameCount; + *pOutputFrameCount = outputFrameCount; + + return MA_SUCCESS; } -MA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler* pResampler) +MA_API ma_result ma_linear_resampler_reset(ma_linear_resampler* pResampler) { + ma_uint32 iChannel; + if (pResampler == NULL) { - return 0; + return MA_INVALID_ARGS; } - return 1 + ma_lpf_get_latency(&pResampler->lpf); + /* Timers need to be cleared back to zero. */ + pResampler->inTimeInt = 1; /* Set this to one to force an input sample to always be loaded for the first output frame. */ + pResampler->inTimeFrac = 0; + + /* Cached samples need to be cleared. */ + if (pResampler->config.format == ma_format_f32) { + for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { + pResampler->x0.f32[iChannel] = 0; + pResampler->x1.f32[iChannel] = 0; + } + } else { + for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { + pResampler->x0.s16[iChannel] = 0; + pResampler->x1.s16[iChannel] = 0; + } + } + + /* The low pass filter needs to have it's cache reset. */ + ma_lpf_clear_cache(&pResampler->lpf); + + return MA_SUCCESS; } -MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resampler* pResampler) + + +/* Linear resampler backend vtable. */ +static ma_linear_resampler_config ma_resampling_backend_get_config__linear(const ma_resampler_config* pConfig) { - if (pResampler == NULL) { - return 0; - } + ma_linear_resampler_config linearConfig; - return ma_linear_resampler_get_input_latency(pResampler) * pResampler->config.sampleRateOut / pResampler->config.sampleRateIn; + linearConfig = ma_linear_resampler_config_init(pConfig->format, pConfig->channels, pConfig->sampleRateIn, pConfig->sampleRateOut); + linearConfig.lpfOrder = pConfig->linear.lpfOrder; + + return linearConfig; } - -#if defined(ma_speex_resampler_h) -#define MA_HAS_SPEEX_RESAMPLER - -static ma_result ma_result_from_speex_err(int err) +static ma_result ma_resampling_backend_get_heap_size__linear(void* pUserData, const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes) { - switch (err) - { - case RESAMPLER_ERR_SUCCESS: return MA_SUCCESS; - case RESAMPLER_ERR_ALLOC_FAILED: return MA_OUT_OF_MEMORY; - case RESAMPLER_ERR_BAD_STATE: return MA_ERROR; - case RESAMPLER_ERR_INVALID_ARG: return MA_INVALID_ARGS; - case RESAMPLER_ERR_PTR_OVERLAP: return MA_INVALID_ARGS; - case RESAMPLER_ERR_OVERFLOW: return MA_ERROR; - default: return MA_ERROR; - } + ma_linear_resampler_config linearConfig; + + (void)pUserData; + + linearConfig = ma_resampling_backend_get_config__linear(pConfig); + + return ma_linear_resampler_get_heap_size(&linearConfig, pHeapSizeInBytes); } -#endif /* ma_speex_resampler_h */ + +static ma_result ma_resampling_backend_init__linear(void* pUserData, const ma_resampler_config* pConfig, void* pHeap, ma_resampling_backend** ppBackend) +{ + ma_resampler* pResampler = (ma_resampler*)pUserData; + ma_result result; + ma_linear_resampler_config linearConfig; + + (void)pUserData; + + linearConfig = ma_resampling_backend_get_config__linear(pConfig); + + result = ma_linear_resampler_init_preallocated(&linearConfig, pHeap, &pResampler->state.linear); + if (result != MA_SUCCESS) { + return result; + } + + *ppBackend = &pResampler->state.linear; + + return MA_SUCCESS; +} + +static void ma_resampling_backend_uninit__linear(void* pUserData, ma_resampling_backend* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) +{ + (void)pUserData; + + ma_linear_resampler_uninit((ma_linear_resampler*)pBackend, pAllocationCallbacks); +} + +static ma_result ma_resampling_backend_process__linear(void* pUserData, ma_resampling_backend* pBackend, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) +{ + (void)pUserData; + + return ma_linear_resampler_process_pcm_frames((ma_linear_resampler*)pBackend, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); +} + +static ma_result ma_resampling_backend_set_rate__linear(void* pUserData, ma_resampling_backend* pBackend, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) +{ + (void)pUserData; + + return ma_linear_resampler_set_rate((ma_linear_resampler*)pBackend, sampleRateIn, sampleRateOut); +} + +static ma_uint64 ma_resampling_backend_get_input_latency__linear(void* pUserData, const ma_resampling_backend* pBackend) +{ + (void)pUserData; + + return ma_linear_resampler_get_input_latency((const ma_linear_resampler*)pBackend); +} + +static ma_uint64 ma_resampling_backend_get_output_latency__linear(void* pUserData, const ma_resampling_backend* pBackend) +{ + (void)pUserData; + + return ma_linear_resampler_get_output_latency((const ma_linear_resampler*)pBackend); +} + +static ma_result ma_resampling_backend_get_required_input_frame_count__linear(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) +{ + (void)pUserData; + + return ma_linear_resampler_get_required_input_frame_count((const ma_linear_resampler*)pBackend, outputFrameCount, pInputFrameCount); +} + +static ma_result ma_resampling_backend_get_expected_output_frame_count__linear(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) +{ + (void)pUserData; + + return ma_linear_resampler_get_expected_output_frame_count((const ma_linear_resampler*)pBackend, inputFrameCount, pOutputFrameCount); +} + +static ma_result ma_resampling_backend_reset__linear(void* pUserData, ma_resampling_backend* pBackend) +{ + (void)pUserData; + + return ma_linear_resampler_reset((ma_linear_resampler*)pBackend); +} + +static ma_resampling_backend_vtable g_ma_linear_resampler_vtable = +{ + ma_resampling_backend_get_heap_size__linear, + ma_resampling_backend_init__linear, + ma_resampling_backend_uninit__linear, + ma_resampling_backend_process__linear, + ma_resampling_backend_set_rate__linear, + ma_resampling_backend_get_input_latency__linear, + ma_resampling_backend_get_output_latency__linear, + ma_resampling_backend_get_required_input_frame_count__linear, + ma_resampling_backend_get_expected_output_frame_count__linear, + ma_resampling_backend_reset__linear +}; + + MA_API ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_resample_algorithm algorithm) { @@ -39636,15 +49958,74 @@ MA_API ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 /* Linear. */ config.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER); - config.linear.lpfNyquistFactor = 1; - - /* Speex. */ - config.speex.quality = 3; /* Cannot leave this as 0 as that is actually a valid value for Speex resampling quality. */ return config; } -MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, ma_resampler* pResampler) +static ma_result ma_resampler_get_vtable(const ma_resampler_config* pConfig, ma_resampler* pResampler, ma_resampling_backend_vtable** ppVTable, void** ppUserData) +{ + MA_ASSERT(pConfig != NULL); + MA_ASSERT(ppVTable != NULL); + MA_ASSERT(ppUserData != NULL); + + /* Safety. */ + *ppVTable = NULL; + *ppUserData = NULL; + + switch (pConfig->algorithm) + { + case ma_resample_algorithm_linear: + { + *ppVTable = &g_ma_linear_resampler_vtable; + *ppUserData = pResampler; + } break; + + case ma_resample_algorithm_custom: + { + *ppVTable = pConfig->pBackendVTable; + *ppUserData = pConfig->pBackendUserData; + } break; + + default: return MA_INVALID_ARGS; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_resampler_get_heap_size(const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_resampling_backend_vtable* pVTable; + void* pVTableUserData; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + result = ma_resampler_get_vtable(pConfig, NULL, &pVTable, &pVTableUserData); + if (result != MA_SUCCESS) { + return result; + } + + if (pVTable == NULL || pVTable->onGetHeapSize == NULL) { + return MA_NOT_IMPLEMENTED; + } + + result = pVTable->onGetHeapSize(pVTableUserData, pConfig, pHeapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_resampler_init_preallocated(const ma_resampler_config* pConfig, void* pHeap, ma_resampler* pResampler) { ma_result result; @@ -39658,291 +50039,75 @@ MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, ma_resamp return MA_INVALID_ARGS; } - if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { - return MA_INVALID_ARGS; + pResampler->_pHeap = pHeap; + pResampler->format = pConfig->format; + pResampler->channels = pConfig->channels; + pResampler->sampleRateIn = pConfig->sampleRateIn; + pResampler->sampleRateOut = pConfig->sampleRateOut; + + result = ma_resampler_get_vtable(pConfig, pResampler, &pResampler->pBackendVTable, &pResampler->pBackendUserData); + if (result != MA_SUCCESS) { + return result; } - pResampler->config = *pConfig; + if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onInit == NULL) { + return MA_NOT_IMPLEMENTED; /* onInit not implemented. */ + } - switch (pConfig->algorithm) - { - case ma_resample_algorithm_linear: - { - ma_linear_resampler_config linearConfig; - linearConfig = ma_linear_resampler_config_init(pConfig->format, pConfig->channels, pConfig->sampleRateIn, pConfig->sampleRateOut); - linearConfig.lpfOrder = pConfig->linear.lpfOrder; - linearConfig.lpfNyquistFactor = pConfig->linear.lpfNyquistFactor; - - result = ma_linear_resampler_init(&linearConfig, &pResampler->state.linear); - if (result != MA_SUCCESS) { - return result; - } - } break; - - case ma_resample_algorithm_speex: - { - #if defined(MA_HAS_SPEEX_RESAMPLER) - int speexErr; - pResampler->state.speex.pSpeexResamplerState = speex_resampler_init(pConfig->channels, pConfig->sampleRateIn, pConfig->sampleRateOut, pConfig->speex.quality, &speexErr); - if (pResampler->state.speex.pSpeexResamplerState == NULL) { - return ma_result_from_speex_err(speexErr); - } - #else - /* Speex resampler not available. */ - return MA_NO_BACKEND; - #endif - } break; - - default: return MA_INVALID_ARGS; + result = pResampler->pBackendVTable->onInit(pResampler->pBackendUserData, pConfig, pHeap, &pResampler->pBackend); + if (result != MA_SUCCESS) { + return result; } return MA_SUCCESS; } -MA_API void ma_resampler_uninit(ma_resampler* pResampler) +MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_resampler* pResampler) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_resampler_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_resampler_init_preallocated(pConfig, pHeap, pResampler); + if (result != MA_SUCCESS) { + return result; + } + + pResampler->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_resampler_uninit(ma_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks) { if (pResampler == NULL) { return; } - if (pResampler->config.algorithm == ma_resample_algorithm_linear) { - ma_linear_resampler_uninit(&pResampler->state.linear); + if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onUninit == NULL) { + return; } -#if defined(MA_HAS_SPEEX_RESAMPLER) - if (pResampler->config.algorithm == ma_resample_algorithm_speex) { - speex_resampler_destroy((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState); + pResampler->pBackendVTable->onUninit(pResampler->pBackendUserData, pResampler->pBackend, pAllocationCallbacks); + + if (pResampler->_ownsHeap) { + ma_free(pResampler->_pHeap, pAllocationCallbacks); } -#endif } -static ma_result ma_resampler_process_pcm_frames__read__linear(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - return ma_linear_resampler_process_pcm_frames(&pResampler->state.linear, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); -} - -#if defined(MA_HAS_SPEEX_RESAMPLER) -static ma_result ma_resampler_process_pcm_frames__read__speex(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - int speexErr; - ma_uint64 frameCountOut; - ma_uint64 frameCountIn; - ma_uint64 framesProcessedOut; - ma_uint64 framesProcessedIn; - unsigned int framesPerIteration = UINT_MAX; - - MA_ASSERT(pResampler != NULL); - MA_ASSERT(pFramesOut != NULL); - MA_ASSERT(pFrameCountOut != NULL); - MA_ASSERT(pFrameCountIn != NULL); - - /* - Reading from the Speex resampler requires a bit of dancing around for a few reasons. The first thing is that it's frame counts - are in unsigned int's whereas ours is in ma_uint64. We therefore need to run the conversion in a loop. The other, more complicated - problem, is that we need to keep track of the input time, similar to what we do with the linear resampler. The reason we need to - do this is for ma_resampler_get_required_input_frame_count() and ma_resampler_get_expected_output_frame_count(). - */ - frameCountOut = *pFrameCountOut; - frameCountIn = *pFrameCountIn; - framesProcessedOut = 0; - framesProcessedIn = 0; - - while (framesProcessedOut < frameCountOut && framesProcessedIn < frameCountIn) { - unsigned int frameCountInThisIteration; - unsigned int frameCountOutThisIteration; - const void* pFramesInThisIteration; - void* pFramesOutThisIteration; - - frameCountInThisIteration = framesPerIteration; - if ((ma_uint64)frameCountInThisIteration > (frameCountIn - framesProcessedIn)) { - frameCountInThisIteration = (unsigned int)(frameCountIn - framesProcessedIn); - } - - frameCountOutThisIteration = framesPerIteration; - if ((ma_uint64)frameCountOutThisIteration > (frameCountOut - framesProcessedOut)) { - frameCountOutThisIteration = (unsigned int)(frameCountOut - framesProcessedOut); - } - - pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pResampler->config.format, pResampler->config.channels)); - pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pResampler->config.format, pResampler->config.channels)); - - if (pResampler->config.format == ma_format_f32) { - speexErr = speex_resampler_process_interleaved_float((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState, (const float*)pFramesInThisIteration, &frameCountInThisIteration, (float*)pFramesOutThisIteration, &frameCountOutThisIteration); - } else if (pResampler->config.format == ma_format_s16) { - speexErr = speex_resampler_process_interleaved_int((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState, (const spx_int16_t*)pFramesInThisIteration, &frameCountInThisIteration, (spx_int16_t*)pFramesOutThisIteration, &frameCountOutThisIteration); - } else { - /* Format not supported. Should never get here. */ - MA_ASSERT(MA_FALSE); - return MA_INVALID_OPERATION; - } - - if (speexErr != RESAMPLER_ERR_SUCCESS) { - return ma_result_from_speex_err(speexErr); - } - - framesProcessedIn += frameCountInThisIteration; - framesProcessedOut += frameCountOutThisIteration; - } - - *pFrameCountOut = framesProcessedOut; - *pFrameCountIn = framesProcessedIn; - - return MA_SUCCESS; -} -#endif - -static ma_result ma_resampler_process_pcm_frames__read(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - MA_ASSERT(pResampler != NULL); - MA_ASSERT(pFramesOut != NULL); - - /* pFramesOut is not NULL, which means we must have a capacity. */ - if (pFrameCountOut == NULL) { - return MA_INVALID_ARGS; - } - - /* It doesn't make sense to not have any input frames to process. */ - if (pFrameCountIn == NULL || pFramesIn == NULL) { - return MA_INVALID_ARGS; - } - - switch (pResampler->config.algorithm) - { - case ma_resample_algorithm_linear: - { - return ma_resampler_process_pcm_frames__read__linear(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } - - case ma_resample_algorithm_speex: - { - #if defined(MA_HAS_SPEEX_RESAMPLER) - return ma_resampler_process_pcm_frames__read__speex(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - #else - break; - #endif - } - - default: break; - } - - /* Should never get here. */ - MA_ASSERT(MA_FALSE); - return MA_INVALID_ARGS; -} - - -static ma_result ma_resampler_process_pcm_frames__seek__linear(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, ma_uint64* pFrameCountOut) -{ - MA_ASSERT(pResampler != NULL); - - /* Seeking is supported natively by the linear resampler. */ - return ma_linear_resampler_process_pcm_frames(&pResampler->state.linear, pFramesIn, pFrameCountIn, NULL, pFrameCountOut); -} - -#if defined(MA_HAS_SPEEX_RESAMPLER) -static ma_result ma_resampler_process_pcm_frames__seek__speex(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, ma_uint64* pFrameCountOut) -{ - /* The generic seek method is implemented in on top of ma_resampler_process_pcm_frames__read() by just processing into a dummy buffer. */ - float devnull[4096]; - ma_uint64 totalOutputFramesToProcess; - ma_uint64 totalOutputFramesProcessed; - ma_uint64 totalInputFramesProcessed; - ma_uint32 bpf; - ma_result result; - - MA_ASSERT(pResampler != NULL); - - totalOutputFramesProcessed = 0; - totalInputFramesProcessed = 0; - bpf = ma_get_bytes_per_frame(pResampler->config.format, pResampler->config.channels); - - if (pFrameCountOut != NULL) { - /* Seek by output frames. */ - totalOutputFramesToProcess = *pFrameCountOut; - } else { - /* Seek by input frames. */ - MA_ASSERT(pFrameCountIn != NULL); - totalOutputFramesToProcess = ma_resampler_get_expected_output_frame_count(pResampler, *pFrameCountIn); - } - - if (pFramesIn != NULL) { - /* Process input data. */ - MA_ASSERT(pFrameCountIn != NULL); - while (totalOutputFramesProcessed < totalOutputFramesToProcess && totalInputFramesProcessed < *pFrameCountIn) { - ma_uint64 inputFramesToProcessThisIteration = (*pFrameCountIn - totalInputFramesProcessed); - ma_uint64 outputFramesToProcessThisIteration = (totalOutputFramesToProcess - totalOutputFramesProcessed); - if (outputFramesToProcessThisIteration > sizeof(devnull) / bpf) { - outputFramesToProcessThisIteration = sizeof(devnull) / bpf; - } - - result = ma_resampler_process_pcm_frames__read(pResampler, ma_offset_ptr(pFramesIn, totalInputFramesProcessed*bpf), &inputFramesToProcessThisIteration, ma_offset_ptr(devnull, totalOutputFramesProcessed*bpf), &outputFramesToProcessThisIteration); - if (result != MA_SUCCESS) { - return result; - } - - totalOutputFramesProcessed += outputFramesToProcessThisIteration; - totalInputFramesProcessed += inputFramesToProcessThisIteration; - } - } else { - /* Don't process input data - just update timing and filter state as if zeroes were passed in. */ - while (totalOutputFramesProcessed < totalOutputFramesToProcess) { - ma_uint64 inputFramesToProcessThisIteration = 16384; - ma_uint64 outputFramesToProcessThisIteration = (totalOutputFramesToProcess - totalOutputFramesProcessed); - if (outputFramesToProcessThisIteration > sizeof(devnull) / bpf) { - outputFramesToProcessThisIteration = sizeof(devnull) / bpf; - } - - result = ma_resampler_process_pcm_frames__read(pResampler, NULL, &inputFramesToProcessThisIteration, ma_offset_ptr(devnull, totalOutputFramesProcessed*bpf), &outputFramesToProcessThisIteration); - if (result != MA_SUCCESS) { - return result; - } - - totalOutputFramesProcessed += outputFramesToProcessThisIteration; - totalInputFramesProcessed += inputFramesToProcessThisIteration; - } - } - - - if (pFrameCountIn != NULL) { - *pFrameCountIn = totalInputFramesProcessed; - } - if (pFrameCountOut != NULL) { - *pFrameCountOut = totalOutputFramesProcessed; - } - - return MA_SUCCESS; -} -#endif - -static ma_result ma_resampler_process_pcm_frames__seek(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, ma_uint64* pFrameCountOut) -{ - MA_ASSERT(pResampler != NULL); - - switch (pResampler->config.algorithm) - { - case ma_resample_algorithm_linear: - { - return ma_resampler_process_pcm_frames__seek__linear(pResampler, pFramesIn, pFrameCountIn, pFrameCountOut); - } break; - - case ma_resample_algorithm_speex: - { - #if defined(MA_HAS_SPEEX_RESAMPLER) - return ma_resampler_process_pcm_frames__seek__speex(pResampler, pFramesIn, pFrameCountIn, pFrameCountOut); - #else - break; - #endif - }; - - default: break; - } - - /* Should never hit this. */ - MA_ASSERT(MA_FALSE); - return MA_INVALID_ARGS; -} - - MA_API ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) { if (pResampler == NULL) { @@ -39953,17 +50118,17 @@ MA_API ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const return MA_INVALID_ARGS; } - if (pFramesOut != NULL) { - /* Reading. */ - return ma_resampler_process_pcm_frames__read(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } else { - /* Seeking. */ - return ma_resampler_process_pcm_frames__seek(pResampler, pFramesIn, pFrameCountIn, pFrameCountOut); + if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onProcess == NULL) { + return MA_NOT_IMPLEMENTED; } + + return pResampler->pBackendVTable->onProcess(pResampler->pBackendUserData, pResampler->pBackend, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); } MA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) { + ma_result result; + if (pResampler == NULL) { return MA_INVALID_ARGS; } @@ -39972,137 +50137,44 @@ MA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampl return MA_INVALID_ARGS; } - pResampler->config.sampleRateIn = sampleRateIn; - pResampler->config.sampleRateOut = sampleRateOut; - - switch (pResampler->config.algorithm) - { - case ma_resample_algorithm_linear: - { - return ma_linear_resampler_set_rate(&pResampler->state.linear, sampleRateIn, sampleRateOut); - } break; - - case ma_resample_algorithm_speex: - { - #if defined(MA_HAS_SPEEX_RESAMPLER) - return ma_result_from_speex_err(speex_resampler_set_rate((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState, sampleRateIn, sampleRateOut)); - #else - break; - #endif - }; - - default: break; + if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onSetRate == NULL) { + return MA_NOT_IMPLEMENTED; } - /* Should never get here. */ - MA_ASSERT(MA_FALSE); - return MA_INVALID_OPERATION; + result = pResampler->pBackendVTable->onSetRate(pResampler->pBackendUserData, pResampler->pBackend, sampleRateIn, sampleRateOut); + if (result != MA_SUCCESS) { + return result; + } + + pResampler->sampleRateIn = sampleRateIn; + pResampler->sampleRateOut = sampleRateOut; + + return MA_SUCCESS; } MA_API ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, float ratio) { + ma_uint32 n; + ma_uint32 d; + if (pResampler == NULL) { return MA_INVALID_ARGS; } - if (pResampler->config.algorithm == ma_resample_algorithm_linear) { - return ma_linear_resampler_set_rate_ratio(&pResampler->state.linear, ratio); - } else { - /* Getting here means the backend does not have native support for setting the rate as a ratio so we just do it generically. */ - ma_uint32 n; - ma_uint32 d; - - d = 1000; - n = (ma_uint32)(ratio * d); - - if (n == 0) { - return MA_INVALID_ARGS; /* Ratio too small. */ - } - - MA_ASSERT(n != 0); - - return ma_resampler_set_rate(pResampler, n, d); - } -} - -MA_API ma_uint64 ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount) -{ - if (pResampler == NULL) { - return 0; + if (ratio <= 0) { + return MA_INVALID_ARGS; } - if (outputFrameCount == 0) { - return 0; + d = 1000; + n = (ma_uint32)(ratio * d); + + if (n == 0) { + return MA_INVALID_ARGS; /* Ratio too small. */ } - switch (pResampler->config.algorithm) - { - case ma_resample_algorithm_linear: - { - return ma_linear_resampler_get_required_input_frame_count(&pResampler->state.linear, outputFrameCount); - } + MA_ASSERT(n != 0); - case ma_resample_algorithm_speex: - { - #if defined(MA_HAS_SPEEX_RESAMPLER) - spx_uint64_t count; - int speexErr = ma_speex_resampler_get_required_input_frame_count((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState, outputFrameCount, &count); - if (speexErr != RESAMPLER_ERR_SUCCESS) { - return 0; - } - - return (ma_uint64)count; - #else - break; - #endif - } - - default: break; - } - - /* Should never get here. */ - MA_ASSERT(MA_FALSE); - return 0; -} - -MA_API ma_uint64 ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount) -{ - if (pResampler == NULL) { - return 0; /* Invalid args. */ - } - - if (inputFrameCount == 0) { - return 0; - } - - switch (pResampler->config.algorithm) - { - case ma_resample_algorithm_linear: - { - return ma_linear_resampler_get_expected_output_frame_count(&pResampler->state.linear, inputFrameCount); - } - - case ma_resample_algorithm_speex: - { - #if defined(MA_HAS_SPEEX_RESAMPLER) - spx_uint64_t count; - int speexErr = ma_speex_resampler_get_expected_output_frame_count((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState, inputFrameCount, &count); - if (speexErr != RESAMPLER_ERR_SUCCESS) { - return 0; - } - - return (ma_uint64)count; - #else - break; - #endif - } - - default: break; - } - - /* Should never get here. */ - MA_ASSERT(MA_FALSE); - return 0; + return ma_resampler_set_rate(pResampler, n, d); } MA_API ma_uint64 ma_resampler_get_input_latency(const ma_resampler* pResampler) @@ -40111,28 +50183,11 @@ MA_API ma_uint64 ma_resampler_get_input_latency(const ma_resampler* pResampler) return 0; } - switch (pResampler->config.algorithm) - { - case ma_resample_algorithm_linear: - { - return ma_linear_resampler_get_input_latency(&pResampler->state.linear); - } - - case ma_resample_algorithm_speex: - { - #if defined(MA_HAS_SPEEX_RESAMPLER) - return (ma_uint64)ma_speex_resampler_get_input_latency((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState); - #else - break; - #endif - } - - default: break; + if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetInputLatency == NULL) { + return 0; } - /* Should never get here. */ - MA_ASSERT(MA_FALSE); - return 0; + return pResampler->pBackendVTable->onGetInputLatency(pResampler->pBackendUserData, pResampler->pBackend); } MA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler) @@ -40141,28 +50196,62 @@ MA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler) return 0; } - switch (pResampler->config.algorithm) - { - case ma_resample_algorithm_linear: - { - return ma_linear_resampler_get_output_latency(&pResampler->state.linear); - } - - case ma_resample_algorithm_speex: - { - #if defined(MA_HAS_SPEEX_RESAMPLER) - return (ma_uint64)ma_speex_resampler_get_output_latency((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState); - #else - break; - #endif - } - - default: break; + if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetOutputLatency == NULL) { + return 0; } - /* Should never get here. */ - MA_ASSERT(MA_FALSE); - return 0; + return pResampler->pBackendVTable->onGetOutputLatency(pResampler->pBackendUserData, pResampler->pBackend); +} + +MA_API ma_result ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) +{ + if (pInputFrameCount == NULL) { + return MA_INVALID_ARGS; + } + + *pInputFrameCount = 0; + + if (pResampler == NULL) { + return MA_INVALID_ARGS; + } + + if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetRequiredInputFrameCount == NULL) { + return MA_NOT_IMPLEMENTED; + } + + return pResampler->pBackendVTable->onGetRequiredInputFrameCount(pResampler->pBackendUserData, pResampler->pBackend, outputFrameCount, pInputFrameCount); +} + +MA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) +{ + if (pOutputFrameCount == NULL) { + return MA_INVALID_ARGS; + } + + *pOutputFrameCount = 0; + + if (pResampler == NULL) { + return MA_INVALID_ARGS; + } + + if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetExpectedOutputFrameCount == NULL) { + return MA_NOT_IMPLEMENTED; + } + + return pResampler->pBackendVTable->onGetExpectedOutputFrameCount(pResampler->pBackendUserData, pResampler->pBackend, inputFrameCount, pOutputFrameCount); +} + +MA_API ma_result ma_resampler_reset(ma_resampler* pResampler) +{ + if (pResampler == NULL) { + return MA_INVALID_ARGS; + } + + if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onReset == NULL) { + return MA_NOT_IMPLEMENTED; + } + + return pResampler->pBackendVTable->onReset(pResampler->pBackendUserData, pResampler->pBackend); } /************************************************************************************************************************************************************** @@ -40283,17 +50372,13 @@ MA_API ma_channel_converter_config ma_channel_converter_config_init(ma_format fo { ma_channel_converter_config config; - /* Channel counts need to be clamped. */ - channelsIn = ma_min(channelsIn, ma_countof(config.channelMapIn)); - channelsOut = ma_min(channelsOut, ma_countof(config.channelMapOut)); - MA_ZERO_OBJECT(&config); - config.format = format; - config.channelsIn = channelsIn; - config.channelsOut = channelsOut; - ma_channel_map_copy_or_default(config.channelMapIn, pChannelMapIn, channelsIn); - ma_channel_map_copy_or_default(config.channelMapOut, pChannelMapOut, channelsOut); - config.mixingMode = mixingMode; + config.format = format; + config.channelsIn = channelsIn; + config.channelsOut = channelsOut; + config.pChannelMapIn = pChannelMapIn; + config.pChannelMapOut = pChannelMapOut; + config.mixingMode = mixingMode; return config; } @@ -40324,320 +50409,862 @@ static ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition) return MA_FALSE; } -MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, ma_channel_converter* pConverter) + +static ma_bool32 ma_channel_map_is_passthrough(const ma_channel* pChannelMapIn, ma_uint32 channelsIn, const ma_channel* pChannelMapOut, ma_uint32 channelsOut) +{ + if (channelsOut == channelsIn) { + return ma_channel_map_is_equal(pChannelMapOut, pChannelMapIn, channelsOut); + } else { + return MA_FALSE; /* Channel counts differ, so cannot be a passthrough. */ + } +} + +static ma_channel_conversion_path ma_channel_map_get_conversion_path(const ma_channel* pChannelMapIn, ma_uint32 channelsIn, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, ma_channel_mix_mode mode) +{ + if (ma_channel_map_is_passthrough(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut)) { + return ma_channel_conversion_path_passthrough; + } + + if (channelsOut == 1 && (pChannelMapOut == NULL || pChannelMapOut[0] == MA_CHANNEL_MONO)) { + return ma_channel_conversion_path_mono_out; + } + + if (channelsIn == 1 && (pChannelMapIn == NULL || pChannelMapIn[0] == MA_CHANNEL_MONO)) { + return ma_channel_conversion_path_mono_in; + } + + if (mode == ma_channel_mix_mode_custom_weights) { + return ma_channel_conversion_path_weights; + } + + /* + We can use a simple shuffle if both channel maps have the same channel count and all channel + positions are present in both. + */ + if (channelsIn == channelsOut) { + ma_uint32 iChannelIn; + ma_bool32 areAllChannelPositionsPresent = MA_TRUE; + for (iChannelIn = 0; iChannelIn < channelsIn; ++iChannelIn) { + ma_bool32 isInputChannelPositionInOutput = MA_FALSE; + if (ma_channel_map_contains_channel_position(channelsOut, pChannelMapOut, ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn))) { + isInputChannelPositionInOutput = MA_TRUE; + break; + } + + if (!isInputChannelPositionInOutput) { + areAllChannelPositionsPresent = MA_FALSE; + break; + } + } + + if (areAllChannelPositionsPresent) { + return ma_channel_conversion_path_shuffle; + } + } + + /* Getting here means we'll need to use weights. */ + return ma_channel_conversion_path_weights; +} + + +static ma_result ma_channel_map_build_shuffle_table(const ma_channel* pChannelMapIn, ma_uint32 channelCountIn, const ma_channel* pChannelMapOut, ma_uint32 channelCountOut, ma_uint8* pShuffleTable) { ma_uint32 iChannelIn; ma_uint32 iChannelOut; + if (pShuffleTable == NULL || channelCountIn == 0 || channelCountOut == 0) { + return MA_INVALID_ARGS; + } + + /* + When building the shuffle table we just do a 1:1 mapping based on the first occurance of a channel. If the + input channel has more than one occurance of a channel position, the second one will be ignored. + */ + for (iChannelOut = 0; iChannelOut < channelCountOut; iChannelOut += 1) { + ma_channel channelOut; + + /* Default to MA_CHANNEL_INDEX_NULL so that if a mapping is not found it'll be set appropriately. */ + pShuffleTable[iChannelOut] = MA_CHANNEL_INDEX_NULL; + + channelOut = ma_channel_map_get_channel(pChannelMapOut, channelCountOut, iChannelOut); + for (iChannelIn = 0; iChannelIn < channelCountIn; iChannelIn += 1) { + ma_channel channelIn; + + channelIn = ma_channel_map_get_channel(pChannelMapIn, channelCountIn, iChannelIn); + if (channelOut == channelIn) { + pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn; + break; + } + + /* + Getting here means the channels don't exactly match, but we are going to support some + relaxed matching for practicality. If, for example, there are two stereo channel maps, + but one uses front left/right and the other uses side left/right, it makes logical + sense to just map these. The way we'll do it is we'll check if there is a logical + corresponding mapping, and if so, apply it, but we will *not* break from the loop, + thereby giving the loop a chance to find an exact match later which will take priority. + */ + switch (channelOut) + { + /* Left channels. */ + case MA_CHANNEL_FRONT_LEFT: + case MA_CHANNEL_SIDE_LEFT: + { + switch (channelIn) { + case MA_CHANNEL_FRONT_LEFT: + case MA_CHANNEL_SIDE_LEFT: + { + pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn; + } break; + } + } break; + + /* Right channels. */ + case MA_CHANNEL_FRONT_RIGHT: + case MA_CHANNEL_SIDE_RIGHT: + { + switch (channelIn) { + case MA_CHANNEL_FRONT_RIGHT: + case MA_CHANNEL_SIDE_RIGHT: + { + pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn; + } break; + } + } break; + + default: break; + } + } + } + + return MA_SUCCESS; +} + + +static void ma_channel_map_apply_shuffle_table_u8(ma_uint8* pFramesOut, ma_uint32 channelsOut, const ma_uint8* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) +{ + ma_uint64 iFrame; + ma_uint32 iChannelOut; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; + if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ + pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; + } else { + pFramesOut[iChannelOut] = 0; + } + } + + pFramesOut += channelsOut; + pFramesIn += channelsIn; + } +} + +static void ma_channel_map_apply_shuffle_table_s16(ma_int16* pFramesOut, ma_uint32 channelsOut, const ma_int16* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) +{ + ma_uint64 iFrame; + ma_uint32 iChannelOut; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; + if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ + pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; + } else { + pFramesOut[iChannelOut] = 0; + } + } + + pFramesOut += channelsOut; + pFramesIn += channelsIn; + } +} + +static void ma_channel_map_apply_shuffle_table_s24(ma_uint8* pFramesOut, ma_uint32 channelsOut, const ma_uint8* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) +{ + ma_uint64 iFrame; + ma_uint32 iChannelOut; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; + if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ + pFramesOut[iChannelOut*3 + 0] = pFramesIn[iChannelIn*3 + 0]; + pFramesOut[iChannelOut*3 + 1] = pFramesIn[iChannelIn*3 + 1]; + pFramesOut[iChannelOut*3 + 2] = pFramesIn[iChannelIn*3 + 2]; + } else { + pFramesOut[iChannelOut*3 + 0] = 0; + } pFramesOut[iChannelOut*3 + 1] = 0; + } pFramesOut[iChannelOut*3 + 2] = 0; + + pFramesOut += channelsOut*3; + pFramesIn += channelsIn*3; + } +} + +static void ma_channel_map_apply_shuffle_table_s32(ma_int32* pFramesOut, ma_uint32 channelsOut, const ma_int32* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) +{ + ma_uint64 iFrame; + ma_uint32 iChannelOut; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; + if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ + pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; + } else { + pFramesOut[iChannelOut] = 0; + } + } + + pFramesOut += channelsOut; + pFramesIn += channelsIn; + } +} + +static void ma_channel_map_apply_shuffle_table_f32(float* pFramesOut, ma_uint32 channelsOut, const float* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) +{ + ma_uint64 iFrame; + ma_uint32 iChannelOut; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; + if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ + pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; + } else { + pFramesOut[iChannelOut] = 0; + } + } + + pFramesOut += channelsOut; + pFramesIn += channelsIn; + } +} + +static ma_result ma_channel_map_apply_shuffle_table(void* pFramesOut, ma_uint32 channelsOut, const void* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable, ma_format format) +{ + if (pFramesOut == NULL || pFramesIn == NULL || channelsOut == 0 || pShuffleTable == NULL) { + return MA_INVALID_ARGS; + } + + switch (format) + { + case ma_format_u8: + { + ma_channel_map_apply_shuffle_table_u8((ma_uint8*)pFramesOut, channelsOut, (const ma_uint8*)pFramesIn, channelsIn, frameCount, pShuffleTable); + } break; + + case ma_format_s16: + { + ma_channel_map_apply_shuffle_table_s16((ma_int16*)pFramesOut, channelsOut, (const ma_int16*)pFramesIn, channelsIn, frameCount, pShuffleTable); + } break; + + case ma_format_s24: + { + ma_channel_map_apply_shuffle_table_s24((ma_uint8*)pFramesOut, channelsOut, (const ma_uint8*)pFramesIn, channelsIn, frameCount, pShuffleTable); + } break; + + case ma_format_s32: + { + ma_channel_map_apply_shuffle_table_s32((ma_int32*)pFramesOut, channelsOut, (const ma_int32*)pFramesIn, channelsIn, frameCount, pShuffleTable); + } break; + + case ma_format_f32: + { + ma_channel_map_apply_shuffle_table_f32((float*)pFramesOut, channelsOut, (const float*)pFramesIn, channelsIn, frameCount, pShuffleTable); + } break; + + default: return MA_INVALID_ARGS; /* Unknown format. */ + } + + return MA_SUCCESS; +} + +static ma_result ma_channel_map_apply_mono_out_f32(float* pFramesOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount) +{ + ma_uint64 iFrame; + ma_uint32 iChannelIn; + ma_uint32 accumulationCount; + + if (pFramesOut == NULL || pFramesIn == NULL || channelsIn == 0) { + return MA_INVALID_ARGS; + } + + /* In this case the output stream needs to be the average of all channels, ignoring NONE. */ + + /* A quick pre-processing step to get the accumulation counter since we're ignoring NONE channels. */ + accumulationCount = 0; + for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { + if (ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn) != MA_CHANNEL_NONE) { + accumulationCount += 1; + } + } + + if (accumulationCount > 0) { /* <-- Prevent a division by zero. */ + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float accumulation = 0; + + for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { + ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn); + if (channelIn != MA_CHANNEL_NONE) { + accumulation += pFramesIn[iChannelIn]; + } + } + + pFramesOut[0] = accumulation / accumulationCount; + pFramesOut += 1; + pFramesIn += channelsIn; + } + } else { + ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, 1); + } + + return MA_SUCCESS; +} + +static ma_result ma_channel_map_apply_mono_in_f32(float* pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* pFramesIn, ma_uint64 frameCount, ma_mono_expansion_mode monoExpansionMode) +{ + ma_uint64 iFrame; + ma_uint32 iChannelOut; + + if (pFramesOut == NULL || channelsOut == 0 || pFramesIn == NULL) { + return MA_INVALID_ARGS; + } + + /* Note that the MA_CHANNEL_NONE channel must be ignored in all cases. */ + switch (monoExpansionMode) + { + case ma_mono_expansion_mode_average: + { + float weight; + ma_uint32 validChannelCount = 0; + + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); + if (channelOut != MA_CHANNEL_NONE) { + validChannelCount += 1; + } + } + + weight = 1.0f / validChannelCount; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); + if (channelOut != MA_CHANNEL_NONE) { + pFramesOut[iChannelOut] = pFramesIn[0] * weight; + } + } + + pFramesOut += channelsOut; + pFramesIn += 1; + } + } break; + + case ma_mono_expansion_mode_stereo_only: + { + if (channelsOut >= 2) { + ma_uint32 iChannelLeft = (ma_uint32)-1; + ma_uint32 iChannelRight = (ma_uint32)-1; + + /* + We first need to find our stereo channels. We prefer front-left and front-right, but + if they're not available, we'll also try side-left and side-right. If neither are + available we'll fall through to the default case below. + */ + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); + if (channelOut == MA_CHANNEL_SIDE_LEFT) { + iChannelLeft = iChannelOut; + } + if (channelOut == MA_CHANNEL_SIDE_RIGHT) { + iChannelRight = iChannelOut; + } + } + + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); + if (channelOut == MA_CHANNEL_FRONT_LEFT) { + iChannelLeft = iChannelOut; + } + if (channelOut == MA_CHANNEL_FRONT_RIGHT) { + iChannelRight = iChannelOut; + } + } + + + if (iChannelLeft != (ma_uint32)-1 && iChannelRight != (ma_uint32)-1) { + /* We found our stereo channels so we can duplicate the signal across those channels. */ + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); + if (channelOut != MA_CHANNEL_NONE) { + if (iChannelOut == iChannelLeft || iChannelOut == iChannelRight) { + pFramesOut[iChannelOut] = pFramesIn[0]; + } else { + pFramesOut[iChannelOut] = 0.0f; + } + } + } + + pFramesOut += channelsOut; + pFramesIn += 1; + } + + break; /* Get out of the switch. */ + } else { + /* Fallthrough. Does not have left and right channels. */ + goto default_handler; + } + } else { + /* Fallthrough. Does not have stereo channels. */ + goto default_handler; + } + }; /* Fallthrough. See comments above. */ + + case ma_mono_expansion_mode_duplicate: + default: + { + default_handler: + { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); + if (channelOut != MA_CHANNEL_NONE) { + pFramesOut[iChannelOut] = pFramesIn[0]; + } + } + + pFramesOut += channelsOut; + pFramesIn += 1; + } + } + } break; + } + + return MA_SUCCESS; +} + +static void ma_channel_map_apply_f32(float* pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount, ma_channel_mix_mode mode, ma_mono_expansion_mode monoExpansionMode) +{ + ma_channel_conversion_path conversionPath = ma_channel_map_get_conversion_path(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut, mode); + + /* Optimized Path: Passthrough */ + if (conversionPath == ma_channel_conversion_path_passthrough) { + ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, ma_format_f32, channelsOut); + return; + } + + /* Special Path: Mono Output. */ + if (conversionPath == ma_channel_conversion_path_mono_out) { + ma_channel_map_apply_mono_out_f32(pFramesOut, pFramesIn, pChannelMapIn, channelsIn, frameCount); + return; + } + + /* Special Path: Mono Input. */ + if (conversionPath == ma_channel_conversion_path_mono_in) { + ma_channel_map_apply_mono_in_f32(pFramesOut, pChannelMapOut, channelsOut, pFramesIn, frameCount, monoExpansionMode); + return; + } + + /* Getting here means we aren't running on an optimized conversion path. */ + if (channelsOut <= MA_MAX_CHANNELS) { + ma_result result; + + if (mode == ma_channel_mix_mode_simple) { + ma_channel shuffleTable[MA_MAX_CHANNELS]; + + result = ma_channel_map_build_shuffle_table(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut, shuffleTable); + if (result != MA_SUCCESS) { + return; + } + + result = ma_channel_map_apply_shuffle_table(pFramesOut, channelsOut, pFramesIn, channelsIn, frameCount, shuffleTable, ma_format_f32); + if (result != MA_SUCCESS) { + return; + } + } else { + ma_uint32 iFrame; + ma_uint32 iChannelOut; + ma_uint32 iChannelIn; + float weights[32][32]; /* Do not use MA_MAX_CHANNELS here! */ + + /* + If we have a small enough number of channels, pre-compute the weights. Otherwise we'll just need to + fall back to a slower path because otherwise we'll run out of stack space. + */ + if (channelsIn <= ma_countof(weights) && channelsOut <= ma_countof(weights)) { + /* Pre-compute weights. */ + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); + for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { + ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn); + weights[iChannelOut][iChannelIn] = ma_calculate_channel_position_rectangular_weight(channelOut, channelIn); + } + } + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + float accumulation = 0; + + for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { + accumulation += pFramesIn[iChannelIn] * weights[iChannelOut][iChannelIn]; + } + + pFramesOut[iChannelOut] = accumulation; + } + + pFramesOut += channelsOut; + pFramesIn += channelsIn; + } + } else { + /* Cannot pre-compute weights because not enough room in stack-allocated buffer. */ + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + float accumulation = 0; + ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); + + for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { + ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn); + accumulation += pFramesIn[iChannelIn] * ma_calculate_channel_position_rectangular_weight(channelOut, channelIn); + } + + pFramesOut[iChannelOut] = accumulation; + } + + pFramesOut += channelsOut; + pFramesIn += channelsIn; + } + } + } + } else { + /* Fall back to silence. If you hit this, what are you doing with so many channels?! */ + ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, channelsOut); + } +} + + +typedef struct +{ + size_t sizeInBytes; + size_t channelMapInOffset; + size_t channelMapOutOffset; + size_t shuffleTableOffset; + size_t weightsOffset; +} ma_channel_converter_heap_layout; + +static ma_channel_conversion_path ma_channel_converter_config_get_conversion_path(const ma_channel_converter_config* pConfig) +{ + return ma_channel_map_get_conversion_path(pConfig->pChannelMapIn, pConfig->channelsIn, pConfig->pChannelMapOut, pConfig->channelsOut, pConfig->mixingMode); +} + +static ma_result ma_channel_converter_get_heap_layout(const ma_channel_converter_config* pConfig, ma_channel_converter_heap_layout* pHeapLayout) +{ + ma_channel_conversion_path conversionPath; + + MA_ASSERT(pHeapLayout != NULL); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) { + return MA_INVALID_ARGS; + } + + if (!ma_channel_map_is_valid(pConfig->pChannelMapIn, pConfig->channelsIn)) { + return MA_INVALID_ARGS; + } + + if (!ma_channel_map_is_valid(pConfig->pChannelMapOut, pConfig->channelsOut)) { + return MA_INVALID_ARGS; + } + + pHeapLayout->sizeInBytes = 0; + + /* Input channel map. Only need to allocate this if we have an input channel map (otherwise default channel map is assumed). */ + pHeapLayout->channelMapInOffset = pHeapLayout->sizeInBytes; + if (pConfig->pChannelMapIn != NULL) { + pHeapLayout->sizeInBytes += sizeof(ma_channel) * pConfig->channelsIn; + } + + /* Output channel map. Only need to allocate this if we have an output channel map (otherwise default channel map is assumed). */ + pHeapLayout->channelMapOutOffset = pHeapLayout->sizeInBytes; + if (pConfig->pChannelMapOut != NULL) { + pHeapLayout->sizeInBytes += sizeof(ma_channel) * pConfig->channelsOut; + } + + /* Alignment for the next section. */ + pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); + + /* Whether or not we use weights of a shuffle table depends on the channel map themselves and the algorithm we've chosen. */ + conversionPath = ma_channel_converter_config_get_conversion_path(pConfig); + + /* Shuffle table */ + pHeapLayout->shuffleTableOffset = pHeapLayout->sizeInBytes; + if (conversionPath == ma_channel_conversion_path_shuffle) { + pHeapLayout->sizeInBytes += sizeof(ma_uint8) * pConfig->channelsOut; + } + + /* Weights */ + pHeapLayout->weightsOffset = pHeapLayout->sizeInBytes; + if (conversionPath == ma_channel_conversion_path_weights) { + pHeapLayout->sizeInBytes += sizeof(float*) * pConfig->channelsIn; + pHeapLayout->sizeInBytes += sizeof(float ) * pConfig->channelsIn * pConfig->channelsOut; + } + + /* Make sure allocation size is aligned. */ + pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); + + return MA_SUCCESS; +} + +MA_API ma_result ma_channel_converter_get_heap_size(const ma_channel_converter_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_channel_converter_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_channel_converter_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return MA_SUCCESS; +} + +MA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_converter_config* pConfig, void* pHeap, ma_channel_converter* pConverter) +{ + ma_result result; + ma_channel_converter_heap_layout heapLayout; + if (pConverter == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pConverter); - if (pConfig == NULL) { - return MA_INVALID_ARGS; + result = ma_channel_converter_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; } - /* Basic validation for channel counts. */ - if (pConfig->channelsIn < MA_MIN_CHANNELS || pConfig->channelsIn > MA_MAX_CHANNELS || - pConfig->channelsOut < MA_MIN_CHANNELS || pConfig->channelsOut > MA_MAX_CHANNELS) { - return MA_INVALID_ARGS; - } - - if (!ma_channel_map_valid(pConfig->channelsIn, pConfig->channelMapIn)) { - return MA_INVALID_ARGS; /* Invalid input channel map. */ - } - if (!ma_channel_map_valid(pConfig->channelsOut, pConfig->channelMapOut)) { - return MA_INVALID_ARGS; /* Invalid output channel map. */ - } + pConverter->_pHeap = pHeap; + MA_ZERO_MEMORY(pConverter->_pHeap, heapLayout.sizeInBytes); pConverter->format = pConfig->format; pConverter->channelsIn = pConfig->channelsIn; pConverter->channelsOut = pConfig->channelsOut; - ma_channel_map_copy_or_default(pConverter->channelMapIn, pConfig->channelMapIn, pConfig->channelsIn); - ma_channel_map_copy_or_default(pConverter->channelMapOut, pConfig->channelMapOut, pConfig->channelsOut); pConverter->mixingMode = pConfig->mixingMode; - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - if (pConverter->format == ma_format_f32) { - pConverter->weights.f32[iChannelIn][iChannelOut] = pConfig->weights[iChannelIn][iChannelOut]; - } else { - pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(pConfig->weights[iChannelIn][iChannelOut]); + if (pConfig->pChannelMapIn != NULL) { + pConverter->pChannelMapIn = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapInOffset); + ma_channel_map_copy_or_default(pConverter->pChannelMapIn, pConfig->channelsIn, pConfig->pChannelMapIn, pConfig->channelsIn); + } else { + pConverter->pChannelMapIn = NULL; /* Use default channel map. */ + } + + if (pConfig->pChannelMapOut != NULL) { + pConverter->pChannelMapOut = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapOutOffset); + ma_channel_map_copy_or_default(pConverter->pChannelMapOut, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelsOut); + } else { + pConverter->pChannelMapOut = NULL; /* Use default channel map. */ + } + + pConverter->conversionPath = ma_channel_converter_config_get_conversion_path(pConfig); + + if (pConverter->conversionPath == ma_channel_conversion_path_shuffle) { + pConverter->pShuffleTable = (ma_uint8*)ma_offset_ptr(pHeap, heapLayout.shuffleTableOffset); + ma_channel_map_build_shuffle_table(pConverter->pChannelMapIn, pConverter->channelsIn, pConverter->pChannelMapOut, pConverter->channelsOut, pConverter->pShuffleTable); + } + + if (pConverter->conversionPath == ma_channel_conversion_path_weights) { + ma_uint32 iChannelIn; + ma_uint32 iChannelOut; + + if (pConverter->format == ma_format_f32) { + pConverter->weights.f32 = (float** )ma_offset_ptr(pHeap, heapLayout.weightsOffset); + for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { + pConverter->weights.f32[iChannelIn] = (float*)ma_offset_ptr(pHeap, heapLayout.weightsOffset + ((sizeof(float*) * pConverter->channelsIn) + (sizeof(float) * pConverter->channelsOut * iChannelIn))); + } + } else { + pConverter->weights.s16 = (ma_int32**)ma_offset_ptr(pHeap, heapLayout.weightsOffset); + for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { + pConverter->weights.s16[iChannelIn] = (ma_int32*)ma_offset_ptr(pHeap, heapLayout.weightsOffset + ((sizeof(ma_int32*) * pConverter->channelsIn) + (sizeof(ma_int32) * pConverter->channelsOut * iChannelIn))); } } - } - - - /* If the input and output channels and channel maps are the same we should use a passthrough. */ - if (pConverter->channelsIn == pConverter->channelsOut) { - if (ma_channel_map_equal(pConverter->channelsIn, pConverter->channelMapIn, pConverter->channelMapOut)) { - pConverter->isPassthrough = MA_TRUE; - } - if (ma_channel_map_blank(pConverter->channelsIn, pConverter->channelMapIn) || ma_channel_map_blank(pConverter->channelsOut, pConverter->channelMapOut)) { - pConverter->isPassthrough = MA_TRUE; - } - } - - - /* - We can use a simple case for expanding the mono channel. This will used when expanding a mono input into any output so long - as no LFE is present in the output. - */ - if (!pConverter->isPassthrough) { - if (pConverter->channelsIn == 1 && pConverter->channelMapIn[0] == MA_CHANNEL_MONO) { - /* Optimal case if no LFE is in the output channel map. */ - pConverter->isSimpleMonoExpansion = MA_TRUE; - if (ma_channel_map_contains_channel_position(pConverter->channelsOut, pConverter->channelMapOut, MA_CHANNEL_LFE)) { - pConverter->isSimpleMonoExpansion = MA_FALSE; - } - } - } - - /* Another optimized case is stereo to mono. */ - if (!pConverter->isPassthrough) { - if (pConverter->channelsOut == 1 && pConverter->channelMapOut[0] == MA_CHANNEL_MONO && pConverter->channelsIn == 2) { - /* Optimal case if no LFE is in the input channel map. */ - pConverter->isStereoToMono = MA_TRUE; - if (ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->channelMapIn, MA_CHANNEL_LFE)) { - pConverter->isStereoToMono = MA_FALSE; - } - } - } - - - /* - Here is where we do a bit of pre-processing to know how each channel should be combined to make up the output. Rules: - - 1) If it's a passthrough, do nothing - it's just a simple memcpy(). - 2) If the channel counts are the same and every channel position in the input map is present in the output map, use a - simple shuffle. An example might be different 5.1 channel layouts. - 3) Otherwise channels are blended based on spatial locality. - */ - if (!pConverter->isPassthrough) { - if (pConverter->channelsIn == pConverter->channelsOut) { - ma_bool32 areAllChannelPositionsPresent = MA_TRUE; - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - ma_bool32 isInputChannelPositionInOutput = MA_FALSE; - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - if (pConverter->channelMapIn[iChannelIn] == pConverter->channelMapOut[iChannelOut]) { - isInputChannelPositionInOutput = MA_TRUE; - break; - } - } - - if (!isInputChannelPositionInOutput) { - areAllChannelPositionsPresent = MA_FALSE; - break; + /* Silence our weights by default. */ + for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { + for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; iChannelOut += 1) { + if (pConverter->format == ma_format_f32) { + pConverter->weights.f32[iChannelIn][iChannelOut] = 0.0f; + } else { + pConverter->weights.s16[iChannelIn][iChannelOut] = 0; } } + } - if (areAllChannelPositionsPresent) { - pConverter->isSimpleShuffle = MA_TRUE; + /* + We now need to fill out our weights table. This is determined by the mixing mode. + */ + switch (pConverter->mixingMode) + { + case ma_channel_mix_mode_custom_weights: + { + if (pConfig->ppWeights == NULL) { + return MA_INVALID_ARGS; /* Config specified a custom weights mixing mode, but no custom weights have been specified. */ + } - /* - All the router will be doing is rearranging channels which means all we need to do is use a shuffling table which is just - a mapping between the index of the input channel to the index of the output channel. - */ - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - if (pConverter->channelMapIn[iChannelIn] == pConverter->channelMapOut[iChannelOut]) { - pConverter->shuffleTable[iChannelIn] = (ma_uint8)iChannelOut; - break; + for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { + for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; iChannelOut += 1) { + float weight = pConfig->ppWeights[iChannelIn][iChannelOut]; + + if (pConverter->format == ma_format_f32) { + pConverter->weights.f32[iChannelIn][iChannelOut] = weight; + } else { + pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); } } } - } - } - } + } break; - - /* - Here is where weights are calculated. Note that we calculate the weights at all times, even when using a passthrough and simple - shuffling. We use different algorithms for calculating weights depending on our mixing mode. - - In simple mode we don't do any blending (except for converting between mono, which is done in a later step). Instead we just - map 1:1 matching channels. In this mode, if no channels in the input channel map correspond to anything in the output channel - map, nothing will be heard! - */ - - /* In all cases we need to make sure all channels that are present in both channel maps have a 1:1 mapping. */ - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - ma_channel channelPosIn = pConverter->channelMapIn[iChannelIn]; - - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_channel channelPosOut = pConverter->channelMapOut[iChannelOut]; - - if (channelPosIn == channelPosOut) { - if (pConverter->format == ma_format_f32) { - pConverter->weights.f32[iChannelIn][iChannelOut] = 1; - } else { - pConverter->weights.s16[iChannelIn][iChannelOut] = (1 << MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT); - } - } - } - } - - /* - The mono channel is accumulated on all other channels, except LFE. Make sure in this loop we exclude output mono channels since - they were handled in the pass above. - */ - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - ma_channel channelPosIn = pConverter->channelMapIn[iChannelIn]; - - if (channelPosIn == MA_CHANNEL_MONO) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_channel channelPosOut = pConverter->channelMapOut[iChannelOut]; - - if (channelPosOut != MA_CHANNEL_NONE && channelPosOut != MA_CHANNEL_MONO && channelPosOut != MA_CHANNEL_LFE) { + case ma_channel_mix_mode_simple: + { + /* In simple mode, excess channels need to be silenced or dropped. */ + ma_uint32 iChannel; + for (iChannel = 0; iChannel < ma_min(pConverter->channelsIn, pConverter->channelsOut); iChannel += 1) { if (pConverter->format == ma_format_f32) { - pConverter->weights.f32[iChannelIn][iChannelOut] = 1; + if (pConverter->weights.f32[iChannel][iChannel] == 0) { + pConverter->weights.f32[iChannel][iChannel] = 1; + } } else { - pConverter->weights.s16[iChannelIn][iChannelOut] = (1 << MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT); - } - } - } - } - } - - /* The output mono channel is the average of all non-none, non-mono and non-lfe input channels. */ - { - ma_uint32 len = 0; - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - ma_channel channelPosIn = pConverter->channelMapIn[iChannelIn]; - - if (channelPosIn != MA_CHANNEL_NONE && channelPosIn != MA_CHANNEL_MONO && channelPosIn != MA_CHANNEL_LFE) { - len += 1; - } - } - - if (len > 0) { - float monoWeight = 1.0f / len; - - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_channel channelPosOut = pConverter->channelMapOut[iChannelOut]; - - if (channelPosOut == MA_CHANNEL_MONO) { - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - ma_channel channelPosIn = pConverter->channelMapIn[iChannelIn]; - - if (channelPosIn != MA_CHANNEL_NONE && channelPosIn != MA_CHANNEL_MONO && channelPosIn != MA_CHANNEL_LFE) { - if (pConverter->format == ma_format_f32) { - pConverter->weights.f32[iChannelIn][iChannelOut] = monoWeight; - } else { - pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(monoWeight); - } + if (pConverter->weights.s16[iChannel][iChannel] == 0) { + pConverter->weights.s16[iChannel][iChannel] = ma_channel_converter_float_to_fixed(1); } } } - } - } - } + } break; + case ma_channel_mix_mode_rectangular: + default: + { + /* Unmapped input channels. */ + for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { + ma_channel channelPosIn = pConverter->pChannelMapIn[iChannelIn]; - /* Input and output channels that are not present on the other side need to be blended in based on spatial locality. */ - switch (pConverter->mixingMode) - { - case ma_channel_mix_mode_rectangular: - { - /* Unmapped input channels. */ - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - ma_channel channelPosIn = pConverter->channelMapIn[iChannelIn]; + if (ma_is_spatial_channel_position(channelPosIn)) { + if (!ma_channel_map_contains_channel_position(pConverter->channelsOut, pConverter->pChannelMapOut, channelPosIn)) { + for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { + ma_channel channelPosOut = pConverter->pChannelMapOut[iChannelOut]; - if (ma_is_spatial_channel_position(channelPosIn)) { - if (!ma_channel_map_contains_channel_position(pConverter->channelsOut, pConverter->channelMapOut, channelPosIn)) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_channel channelPosOut = pConverter->channelMapOut[iChannelOut]; - - if (ma_is_spatial_channel_position(channelPosOut)) { - float weight = 0; - if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) { - weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut); - } - - /* Only apply the weight if we haven't already got some contribution from the respective channels. */ - if (pConverter->format == ma_format_f32) { - if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) { - pConverter->weights.f32[iChannelIn][iChannelOut] = weight; + if (ma_is_spatial_channel_position(channelPosOut)) { + float weight = 0; + if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) { + weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut); } - } else { - if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) { - pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); + + /* Only apply the weight if we haven't already got some contribution from the respective channels. */ + if (pConverter->format == ma_format_f32) { + if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) { + pConverter->weights.f32[iChannelIn][iChannelOut] = weight; + } + } else { + if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) { + pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); + } } } } } } } - } - /* Unmapped output channels. */ - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_channel channelPosOut = pConverter->channelMapOut[iChannelOut]; + /* Unmapped output channels. */ + for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { + ma_channel channelPosOut = pConverter->pChannelMapOut[iChannelOut]; - if (ma_is_spatial_channel_position(channelPosOut)) { - if (!ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->channelMapIn, channelPosOut)) { - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - ma_channel channelPosIn = pConverter->channelMapIn[iChannelIn]; + if (ma_is_spatial_channel_position(channelPosOut)) { + if (!ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->pChannelMapIn, channelPosOut)) { + for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { + ma_channel channelPosIn = pConverter->pChannelMapIn[iChannelIn]; - if (ma_is_spatial_channel_position(channelPosIn)) { - float weight = 0; - if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) { - weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut); - } - - /* Only apply the weight if we haven't already got some contribution from the respective channels. */ - if (pConverter->format == ma_format_f32) { - if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) { - pConverter->weights.f32[iChannelIn][iChannelOut] = weight; + if (ma_is_spatial_channel_position(channelPosIn)) { + float weight = 0; + if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) { + weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut); } - } else { - if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) { - pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); + + /* Only apply the weight if we haven't already got some contribution from the respective channels. */ + if (pConverter->format == ma_format_f32) { + if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) { + pConverter->weights.f32[iChannelIn][iChannelOut] = weight; + } + } else { + if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) { + pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); + } } } } } } } - } - } break; - - case ma_channel_mix_mode_simple: - { - /* In simple mode, excess channels need to be silenced or dropped. */ - ma_uint32 iChannel; - for (iChannel = 0; iChannel < ma_min(pConverter->channelsIn, pConverter->channelsOut); iChannel += 1) { - if (pConverter->format == ma_format_f32) { - if (pConverter->weights.f32[iChannel][iChannel] == 0) { - pConverter->weights.f32[iChannel][iChannel] = 1; - } - } else { - if (pConverter->weights.s16[iChannel][iChannel] == 0) { - pConverter->weights.s16[iChannel][iChannel] = ma_channel_converter_float_to_fixed(1); - } - } - } - } break; - - case ma_channel_mix_mode_custom_weights: - default: - { - /* Fallthrough. */ - } break; + } break; + } } - return MA_SUCCESS; } -MA_API void ma_channel_converter_uninit(ma_channel_converter* pConverter) +MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_channel_converter* pConverter) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_channel_converter_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_channel_converter_init_preallocated(pConfig, pHeap, pConverter); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pConverter->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_channel_converter_uninit(ma_channel_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks) { if (pConverter == NULL) { return; } + + if (pConverter->_ownsHeap) { + ma_free(pConverter->_pHeap, pAllocationCallbacks); + } } static ma_result ma_channel_converter_process_pcm_frames__passthrough(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) @@ -40650,103 +51277,17 @@ static ma_result ma_channel_converter_process_pcm_frames__passthrough(ma_channel return MA_SUCCESS; } -static ma_result ma_channel_converter_process_pcm_frames__simple_shuffle(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +static ma_result ma_channel_converter_process_pcm_frames__shuffle(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) { - ma_uint32 iFrame; - ma_uint32 iChannelIn; - MA_ASSERT(pConverter != NULL); MA_ASSERT(pFramesOut != NULL); MA_ASSERT(pFramesIn != NULL); MA_ASSERT(pConverter->channelsIn == pConverter->channelsOut); - switch (pConverter->format) - { - case ma_format_u8: - { - /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut; - const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - pFramesOutU8[pConverter->shuffleTable[iChannelIn]] = pFramesInU8[iChannelIn]; - } - - pFramesOutU8 += pConverter->channelsOut; - pFramesInU8 += pConverter->channelsIn; - } - } break; - - case ma_format_s16: - { - /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; - const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - pFramesOutS16[pConverter->shuffleTable[iChannelIn]] = pFramesInS16[iChannelIn]; - } - - pFramesOutS16 += pConverter->channelsOut; - pFramesInS16 += pConverter->channelsIn; - } - } break; - - case ma_format_s24: - { - /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut; - const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - ma_uint32 iChannelOut = pConverter->shuffleTable[iChannelIn]; - pFramesOutS24[iChannelOut*3 + 0] = pFramesInS24[iChannelIn*3 + 0]; - pFramesOutS24[iChannelOut*3 + 1] = pFramesInS24[iChannelIn*3 + 1]; - pFramesOutS24[iChannelOut*3 + 2] = pFramesInS24[iChannelIn*3 + 2]; - } - - pFramesOutS24 += pConverter->channelsOut*3; - pFramesInS24 += pConverter->channelsIn*3; - } - } break; - - case ma_format_s32: - { - /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut; - const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - pFramesOutS32[pConverter->shuffleTable[iChannelIn]] = pFramesInS32[iChannelIn]; - } - - pFramesOutS32 += pConverter->channelsOut; - pFramesInS32 += pConverter->channelsIn; - } - } break; - - case ma_format_f32: - { - /* */ float* pFramesOutF32 = ( float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - pFramesOutF32[pConverter->shuffleTable[iChannelIn]] = pFramesInF32[iChannelIn]; - } - - pFramesOutF32 += pConverter->channelsOut; - pFramesInF32 += pConverter->channelsIn; - } - } break; - - default: return MA_INVALID_OPERATION; /* Unknown format. */ - } - - return MA_SUCCESS; + return ma_channel_map_apply_shuffle_table(pFramesOut, pConverter->channelsOut, pFramesIn, pConverter->channelsIn, frameCount, pConverter->pShuffleTable, pConverter->format); } -static ma_result ma_channel_converter_process_pcm_frames__simple_mono_expansion(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +static ma_result ma_channel_converter_process_pcm_frames__mono_in(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) { ma_uint64 iFrame; @@ -40846,14 +51387,14 @@ static ma_result ma_channel_converter_process_pcm_frames__simple_mono_expansion( return MA_SUCCESS; } -static ma_result ma_channel_converter_process_pcm_frames__stereo_to_mono(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +static ma_result ma_channel_converter_process_pcm_frames__mono_out(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) { ma_uint64 iFrame; + ma_uint32 iChannel; MA_ASSERT(pConverter != NULL); MA_ASSERT(pFramesOut != NULL); MA_ASSERT(pFramesIn != NULL); - MA_ASSERT(pConverter->channelsIn == 2); MA_ASSERT(pConverter->channelsOut == 1); switch (pConverter->format) @@ -40864,7 +51405,12 @@ static ma_result ma_channel_converter_process_pcm_frames__stereo_to_mono(ma_chan const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn; for (iFrame = 0; iFrame < frameCount; ++iFrame) { - pFramesOutU8[iFrame] = ma_clip_u8((ma_int16)((ma_pcm_sample_u8_to_s16_no_scale(pFramesInU8[iFrame*2+0]) + ma_pcm_sample_u8_to_s16_no_scale(pFramesInU8[iFrame*2+1])) / 2)); + ma_int32 t = 0; + for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { + t += ma_pcm_sample_u8_to_s16_no_scale(pFramesInU8[iFrame*pConverter->channelsIn + iChannel]); + } + + pFramesOutU8[iFrame] = ma_clip_u8(t / pConverter->channelsOut); } } break; @@ -40874,7 +51420,12 @@ static ma_result ma_channel_converter_process_pcm_frames__stereo_to_mono(ma_chan const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; for (iFrame = 0; iFrame < frameCount; ++iFrame) { - pFramesOutS16[iFrame] = (ma_int16)(((ma_int32)pFramesInS16[iFrame*2+0] + (ma_int32)pFramesInS16[iFrame*2+1]) / 2); + ma_int32 t = 0; + for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { + t += pFramesInS16[iFrame*pConverter->channelsIn + iChannel]; + } + + pFramesOutS16[iFrame] = (ma_int16)(t / pConverter->channelsIn); } } break; @@ -40884,9 +51435,12 @@ static ma_result ma_channel_converter_process_pcm_frames__stereo_to_mono(ma_chan const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn; for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_int64 s24_0 = ma_pcm_sample_s24_to_s32_no_scale(&pFramesInS24[(iFrame*2+0)*3]); - ma_int64 s24_1 = ma_pcm_sample_s24_to_s32_no_scale(&pFramesInS24[(iFrame*2+1)*3]); - ma_pcm_sample_s32_to_s24_no_scale((s24_0 + s24_1) / 2, &pFramesOutS24[iFrame*3]); + ma_int64 t = 0; + for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { + t += ma_pcm_sample_s24_to_s32_no_scale(&pFramesInS24[(iFrame*pConverter->channelsIn + iChannel)*3]); + } + + ma_pcm_sample_s32_to_s24_no_scale(t / pConverter->channelsIn, &pFramesOutS24[iFrame*3]); } } break; @@ -40896,7 +51450,12 @@ static ma_result ma_channel_converter_process_pcm_frames__stereo_to_mono(ma_chan const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn; for (iFrame = 0; iFrame < frameCount; ++iFrame) { - pFramesOutS32[iFrame] = (ma_int16)(((ma_int32)pFramesInS32[iFrame*2+0] + (ma_int32)pFramesInS32[iFrame*2+1]) / 2); + ma_int64 t = 0; + for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { + t += pFramesInS32[iFrame*pConverter->channelsIn + iChannel]; + } + + pFramesOutS32[iFrame] = (ma_int32)(t / pConverter->channelsIn); } } break; @@ -40906,7 +51465,12 @@ static ma_result ma_channel_converter_process_pcm_frames__stereo_to_mono(ma_chan const float* pFramesInF32 = (const float*)pFramesIn; for (iFrame = 0; iFrame < frameCount; ++iFrame) { - pFramesOutF32[iFrame] = (pFramesInF32[iFrame*2+0] + pFramesInF32[iFrame*2+1]) * 0.5f; + float t = 0; + for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { + t += pFramesInF32[iFrame*pConverter->channelsIn + iChannel]; + } + + pFramesOutF32[iFrame] = t / pConverter->channelsIn; } } break; @@ -41037,19 +51601,42 @@ MA_API ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* p return MA_SUCCESS; } - if (pConverter->isPassthrough) { - return ma_channel_converter_process_pcm_frames__passthrough(pConverter, pFramesOut, pFramesIn, frameCount); - } else if (pConverter->isSimpleShuffle) { - return ma_channel_converter_process_pcm_frames__simple_shuffle(pConverter, pFramesOut, pFramesIn, frameCount); - } else if (pConverter->isSimpleMonoExpansion) { - return ma_channel_converter_process_pcm_frames__simple_mono_expansion(pConverter, pFramesOut, pFramesIn, frameCount); - } else if (pConverter->isStereoToMono) { - return ma_channel_converter_process_pcm_frames__stereo_to_mono(pConverter, pFramesOut, pFramesIn, frameCount); - } else { - return ma_channel_converter_process_pcm_frames__weights(pConverter, pFramesOut, pFramesIn, frameCount); + switch (pConverter->conversionPath) + { + case ma_channel_conversion_path_passthrough: return ma_channel_converter_process_pcm_frames__passthrough(pConverter, pFramesOut, pFramesIn, frameCount); + case ma_channel_conversion_path_mono_out: return ma_channel_converter_process_pcm_frames__mono_out(pConverter, pFramesOut, pFramesIn, frameCount); + case ma_channel_conversion_path_mono_in: return ma_channel_converter_process_pcm_frames__mono_in(pConverter, pFramesOut, pFramesIn, frameCount); + case ma_channel_conversion_path_shuffle: return ma_channel_converter_process_pcm_frames__shuffle(pConverter, pFramesOut, pFramesIn, frameCount); + case ma_channel_conversion_path_weights: + default: + { + return ma_channel_converter_process_pcm_frames__weights(pConverter, pFramesOut, pFramesIn, frameCount); + } } } +MA_API ma_result ma_channel_converter_get_input_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) +{ + if (pConverter == NULL || pChannelMap == NULL) { + return MA_INVALID_ARGS; + } + + ma_channel_map_copy_or_default(pChannelMap, channelMapCap, pConverter->pChannelMapIn, pConverter->channelsIn); + + return MA_SUCCESS; +} + +MA_API ma_result ma_channel_converter_get_output_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) +{ + if (pConverter == NULL || pChannelMap == NULL) { + return MA_INVALID_ARGS; + } + + ma_channel_map_copy_or_default(pChannelMap, channelMapCap, pConverter->pChannelMapOut, pConverter->channelsOut); + + return MA_SUCCESS; +} + /************************************************************************************************************************************************************** @@ -41063,14 +51650,10 @@ MA_API ma_data_converter_config ma_data_converter_config_init_default() config.ditherMode = ma_dither_mode_none; config.resampling.algorithm = ma_resample_algorithm_linear; - config.resampling.allowDynamicSampleRate = MA_FALSE; /* Disable dynamic sample rates by default because dynamic rate adjustments should be quite rare and it allows an optimization for cases when the in and out sample rates are the same. */ + config.allowDynamicSampleRate = MA_FALSE; /* Disable dynamic sample rates by default because dynamic rate adjustments should be quite rare and it allows an optimization for cases when the in and out sample rates are the same. */ /* Linear resampling defaults. */ config.resampling.linear.lpfOrder = 1; - config.resampling.linear.lpfNyquistFactor = 1; - - /* Speex resampling defaults. */ - config.resampling.speex.quality = 3; return config; } @@ -41080,18 +51663,168 @@ MA_API ma_data_converter_config ma_data_converter_config_init(ma_format formatIn ma_data_converter_config config = ma_data_converter_config_init_default(); config.formatIn = formatIn; config.formatOut = formatOut; - config.channelsIn = ma_min(channelsIn, MA_MAX_CHANNELS); - config.channelsOut = ma_min(channelsOut, MA_MAX_CHANNELS); + config.channelsIn = channelsIn; + config.channelsOut = channelsOut; config.sampleRateIn = sampleRateIn; config.sampleRateOut = sampleRateOut; return config; } -MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, ma_data_converter* pConverter) + +typedef struct +{ + size_t sizeInBytes; + size_t channelConverterOffset; + size_t resamplerOffset; +} ma_data_converter_heap_layout; + +static ma_bool32 ma_data_converter_config_is_resampler_required(const ma_data_converter_config* pConfig) +{ + MA_ASSERT(pConfig != NULL); + + return pConfig->allowDynamicSampleRate || pConfig->sampleRateIn != pConfig->sampleRateOut; +} + +static ma_format ma_data_converter_config_get_mid_format(const ma_data_converter_config* pConfig) +{ + MA_ASSERT(pConfig != NULL); + + /* + We want to avoid as much data conversion as possible. The channel converter and linear + resampler both support s16 and f32 natively. We need to decide on the format to use for this + stage. We call this the mid format because it's used in the middle stage of the conversion + pipeline. If the output format is either s16 or f32 we use that one. If that is not the case it + will do the same thing for the input format. If it's neither we just use f32. If we are using a + custom resampling backend, we can only guarantee that f32 will be supported so we'll be forced + to use that if resampling is required. + */ + if (ma_data_converter_config_is_resampler_required(pConfig) && pConfig->resampling.algorithm != ma_resample_algorithm_linear) { + return ma_format_f32; /* <-- Force f32 since that is the only one we can guarantee will be supported by the resampler. */ + } else { + /* */ if (pConfig->formatOut == ma_format_s16 || pConfig->formatOut == ma_format_f32) { + return pConfig->formatOut; + } else if (pConfig->formatIn == ma_format_s16 || pConfig->formatIn == ma_format_f32) { + return pConfig->formatIn; + } else { + return ma_format_f32; + } + } +} + +static ma_channel_converter_config ma_channel_converter_config_init_from_data_converter_config(const ma_data_converter_config* pConfig) +{ + ma_channel_converter_config channelConverterConfig; + + MA_ASSERT(pConfig != NULL); + + channelConverterConfig = ma_channel_converter_config_init(ma_data_converter_config_get_mid_format(pConfig), pConfig->channelsIn, pConfig->pChannelMapIn, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelMixMode); + channelConverterConfig.ppWeights = pConfig->ppChannelWeights; + + return channelConverterConfig; +} + +static ma_resampler_config ma_resampler_config_init_from_data_converter_config(const ma_data_converter_config* pConfig) +{ + ma_resampler_config resamplerConfig; + ma_uint32 resamplerChannels; + + MA_ASSERT(pConfig != NULL); + + /* The resampler is the most expensive part of the conversion process, so we need to do it at the stage where the channel count is at it's lowest. */ + if (pConfig->channelsIn < pConfig->channelsOut) { + resamplerChannels = pConfig->channelsIn; + } else { + resamplerChannels = pConfig->channelsOut; + } + + resamplerConfig = ma_resampler_config_init(ma_data_converter_config_get_mid_format(pConfig), resamplerChannels, pConfig->sampleRateIn, pConfig->sampleRateOut, pConfig->resampling.algorithm); + resamplerConfig.linear = pConfig->resampling.linear; + resamplerConfig.pBackendVTable = pConfig->resampling.pBackendVTable; + resamplerConfig.pBackendUserData = pConfig->resampling.pBackendUserData; + + return resamplerConfig; +} + +static ma_result ma_data_converter_get_heap_layout(const ma_data_converter_config* pConfig, ma_data_converter_heap_layout* pHeapLayout) { ma_result result; + + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) { + return MA_INVALID_ARGS; + } + + pHeapLayout->sizeInBytes = 0; + + /* Channel converter. */ + pHeapLayout->channelConverterOffset = pHeapLayout->sizeInBytes; + { + size_t heapSizeInBytes; + ma_channel_converter_config channelConverterConfig = ma_channel_converter_config_init_from_data_converter_config(pConfig); + + result = ma_channel_converter_get_heap_size(&channelConverterConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->sizeInBytes += heapSizeInBytes; + } + + /* Resampler. */ + pHeapLayout->resamplerOffset = pHeapLayout->sizeInBytes; + if (ma_data_converter_config_is_resampler_required(pConfig)) { + size_t heapSizeInBytes; + ma_resampler_config resamplerConfig = ma_resampler_config_init_from_data_converter_config(pConfig); + + result = ma_resampler_get_heap_size(&resamplerConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->sizeInBytes += heapSizeInBytes; + } + + /* Make sure allocation size is aligned. */ + pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); + + return MA_SUCCESS; +} + +MA_API ma_result ma_data_converter_get_heap_size(const ma_data_converter_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_data_converter_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_data_converter_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return MA_SUCCESS; +} + +MA_API ma_result ma_data_converter_init_preallocated(const ma_data_converter_config* pConfig, void* pHeap, ma_data_converter* pConverter) +{ + ma_result result; + ma_data_converter_heap_layout heapLayout; ma_format midFormat; + ma_bool32 isResamplingRequired; if (pConverter == NULL) { return MA_INVALID_ARGS; @@ -41099,82 +51832,52 @@ MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, MA_ZERO_OBJECT(pConverter); - if (pConfig == NULL) { - return MA_INVALID_ARGS; + result = ma_data_converter_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; } - pConverter->config = *pConfig; + pConverter->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - /* Basic validation. */ - if (pConfig->channelsIn < MA_MIN_CHANNELS || pConfig->channelsOut < MA_MIN_CHANNELS || - pConfig->channelsIn > MA_MAX_CHANNELS || pConfig->channelsOut > MA_MAX_CHANNELS) { - return MA_INVALID_ARGS; - } + pConverter->formatIn = pConfig->formatIn; + pConverter->formatOut = pConfig->formatOut; + pConverter->channelsIn = pConfig->channelsIn; + pConverter->channelsOut = pConfig->channelsOut; + pConverter->sampleRateIn = pConfig->sampleRateIn; + pConverter->sampleRateOut = pConfig->sampleRateOut; + pConverter->ditherMode = pConfig->ditherMode; /* - We want to avoid as much data conversion as possible. The channel converter and resampler both support s16 and f32 natively. We need to decide - on the format to use for this stage. We call this the mid format because it's used in the middle stage of the conversion pipeline. If the output - format is either s16 or f32 we use that one. If that is not the case it will do the same thing for the input format. If it's neither we just - use f32. + Determine if resampling is required. We need to do this so we can determine an appropriate + mid format to use. If resampling is required, the mid format must be ma_format_f32 since + that is the only one that is guaranteed to supported by custom resampling backends. */ - /* */ if (pConverter->config.formatOut == ma_format_s16 || pConverter->config.formatOut == ma_format_f32) { - midFormat = pConverter->config.formatOut; - } else if (pConverter->config.formatIn == ma_format_s16 || pConverter->config.formatIn == ma_format_f32) { - midFormat = pConverter->config.formatIn; - } else { - midFormat = ma_format_f32; - } + isResamplingRequired = ma_data_converter_config_is_resampler_required(pConfig); + midFormat = ma_data_converter_config_get_mid_format(pConfig); + /* Channel converter. We always initialize this, but we check if it configures itself as a passthrough to determine whether or not it's needed. */ { - ma_uint32 iChannelIn; - ma_uint32 iChannelOut; - ma_channel_converter_config channelConverterConfig; + ma_channel_converter_config channelConverterConfig = ma_channel_converter_config_init_from_data_converter_config(pConfig); - channelConverterConfig = ma_channel_converter_config_init(midFormat, pConverter->config.channelsIn, pConverter->config.channelMapIn, pConverter->config.channelsOut, pConverter->config.channelMapOut, pConverter->config.channelMixMode); - - /* Channel weights. */ - for (iChannelIn = 0; iChannelIn < pConverter->config.channelsIn; iChannelIn += 1) { - for (iChannelOut = 0; iChannelOut < pConverter->config.channelsOut; iChannelOut += 1) { - channelConverterConfig.weights[iChannelIn][iChannelOut] = pConverter->config.channelWeights[iChannelIn][iChannelOut]; - } - } - - result = ma_channel_converter_init(&channelConverterConfig, &pConverter->channelConverter); + result = ma_channel_converter_init_preallocated(&channelConverterConfig, ma_offset_ptr(pHeap, heapLayout.channelConverterOffset), &pConverter->channelConverter); if (result != MA_SUCCESS) { return result; } /* If the channel converter is not a passthrough we need to enable it. Otherwise we can skip it. */ - if (pConverter->channelConverter.isPassthrough == MA_FALSE) { + if (pConverter->channelConverter.conversionPath != ma_channel_conversion_path_passthrough) { pConverter->hasChannelConverter = MA_TRUE; } } - /* Always enable dynamic sample rates if the input sample rate is different because we're always going to need a resampler in this case anyway. */ - if (pConverter->config.resampling.allowDynamicSampleRate == MA_FALSE) { - pConverter->config.resampling.allowDynamicSampleRate = pConverter->config.sampleRateIn != pConverter->config.sampleRateOut; - } - /* Resampler. */ - if (pConverter->config.resampling.allowDynamicSampleRate) { - ma_resampler_config resamplerConfig; - ma_uint32 resamplerChannels; + if (isResamplingRequired) { + ma_resampler_config resamplerConfig = ma_resampler_config_init_from_data_converter_config(pConfig); - /* The resampler is the most expensive part of the conversion process, so we need to do it at the stage where the channel count is at it's lowest. */ - if (pConverter->config.channelsIn < pConverter->config.channelsOut) { - resamplerChannels = pConverter->config.channelsIn; - } else { - resamplerChannels = pConverter->config.channelsOut; - } - - resamplerConfig = ma_resampler_config_init(midFormat, resamplerChannels, pConverter->config.sampleRateIn, pConverter->config.sampleRateOut, pConverter->config.resampling.algorithm); - resamplerConfig.linear.lpfOrder = pConverter->config.resampling.linear.lpfOrder; - resamplerConfig.linear.lpfNyquistFactor = pConverter->config.resampling.linear.lpfNyquistFactor; - resamplerConfig.speex.quality = pConverter->config.resampling.speex.quality; - - result = ma_resampler_init(&resamplerConfig, &pConverter->resampler); + result = ma_resampler_init_preallocated(&resamplerConfig, ma_offset_ptr(pHeap, heapLayout.resamplerOffset), &pConverter->resampler); if (result != MA_SUCCESS) { return result; } @@ -41186,7 +51889,7 @@ MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, /* We can simplify pre- and post-format conversion if we have neither channel conversion nor resampling. */ if (pConverter->hasChannelConverter == MA_FALSE && pConverter->hasResampler == MA_FALSE) { /* We have neither channel conversion nor resampling so we'll only need one of pre- or post-format conversion, or none if the input and output formats are the same. */ - if (pConverter->config.formatIn == pConverter->config.formatOut) { + if (pConverter->formatIn == pConverter->formatOut) { /* The formats are the same so we can just pass through. */ pConverter->hasPreFormatConversion = MA_FALSE; pConverter->hasPostFormatConversion = MA_FALSE; @@ -41197,10 +51900,10 @@ MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, } } else { /* We have a channel converter and/or resampler so we'll need channel conversion based on the mid format. */ - if (pConverter->config.formatIn != midFormat) { - pConverter->hasPreFormatConversion = MA_TRUE; + if (pConverter->formatIn != midFormat) { + pConverter->hasPreFormatConversion = MA_TRUE; } - if (pConverter->config.formatOut != midFormat) { + if (pConverter->formatOut != midFormat) { pConverter->hasPostFormatConversion = MA_TRUE; } } @@ -41213,17 +51916,86 @@ MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, pConverter->isPassthrough = MA_TRUE; } + + /* We now need to determine our execution path. */ + if (pConverter->isPassthrough) { + pConverter->executionPath = ma_data_converter_execution_path_passthrough; + } else { + if (pConverter->channelsIn < pConverter->channelsOut) { + /* Do resampling first, if necessary. */ + MA_ASSERT(pConverter->hasChannelConverter == MA_TRUE); + + if (pConverter->hasResampler) { + pConverter->executionPath = ma_data_converter_execution_path_resample_first; + } else { + pConverter->executionPath = ma_data_converter_execution_path_channels_only; + } + } else { + /* Do channel conversion first, if necessary. */ + if (pConverter->hasChannelConverter) { + if (pConverter->hasResampler) { + pConverter->executionPath = ma_data_converter_execution_path_channels_first; + } else { + pConverter->executionPath = ma_data_converter_execution_path_channels_only; + } + } else { + /* Channel routing not required. */ + if (pConverter->hasResampler) { + pConverter->executionPath = ma_data_converter_execution_path_resample_only; + } else { + pConverter->executionPath = ma_data_converter_execution_path_format_only; + } + } + } + } + return MA_SUCCESS; } -MA_API void ma_data_converter_uninit(ma_data_converter* pConverter) +MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_converter* pConverter) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_data_converter_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_data_converter_init_preallocated(pConfig, pHeap, pConverter); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pConverter->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_data_converter_uninit(ma_data_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks) { if (pConverter == NULL) { return; } if (pConverter->hasResampler) { - ma_resampler_uninit(&pConverter->resampler); + ma_resampler_uninit(&pConverter->resampler, pAllocationCallbacks); + } + + ma_channel_converter_uninit(&pConverter->channelConverter, pAllocationCallbacks); + + if (pConverter->_ownsHeap) { + ma_free(pConverter->_pHeap, pAllocationCallbacks); } } @@ -41249,9 +52021,9 @@ static ma_result ma_data_converter_process_pcm_frames__passthrough(ma_data_conve if (pFramesOut != NULL) { if (pFramesIn != NULL) { - ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->config.formatOut, pConverter->config.channelsOut)); + ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); } else { - ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->config.formatOut, pConverter->config.channelsOut)); + ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); } } @@ -41287,9 +52059,9 @@ static ma_result ma_data_converter_process_pcm_frames__format_only(ma_data_conve if (pFramesOut != NULL) { if (pFramesIn != NULL) { - ma_convert_pcm_frames_format(pFramesOut, pConverter->config.formatOut, pFramesIn, pConverter->config.formatIn, frameCount, pConverter->config.channelsIn, pConverter->config.ditherMode); + ma_convert_pcm_frames_format(pFramesOut, pConverter->formatOut, pFramesIn, pConverter->formatIn, frameCount, pConverter->channelsIn, pConverter->ditherMode); } else { - ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->config.formatOut, pConverter->config.channelsOut)); + ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); } } @@ -41329,20 +52101,20 @@ static ma_result ma_data_converter_process_pcm_frames__resample_with_format_conv while (framesProcessedOut < frameCountOut) { ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.config.format, pConverter->resampler.config.channels); + const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); const void* pFramesInThisIteration; /* */ void* pFramesOutThisIteration; ma_uint64 frameCountInThisIteration; ma_uint64 frameCountOutThisIteration; if (pFramesIn != NULL) { - pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->config.formatIn, pConverter->config.channelsIn)); + pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); } else { pFramesInThisIteration = NULL; } if (pFramesOut != NULL) { - pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->config.formatOut, pConverter->config.channelsOut)); + pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); } else { pFramesOutThisIteration = NULL; } @@ -41350,7 +52122,7 @@ static ma_result ma_data_converter_process_pcm_frames__resample_with_format_conv /* Do a pre format conversion if necessary. */ if (pConverter->hasPreFormatConversion) { ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.config.format, pConverter->resampler.config.channels); + const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); frameCountInThisIteration = (frameCountIn - framesProcessedIn); if (frameCountInThisIteration > tempBufferInCap) { @@ -41364,7 +52136,7 @@ static ma_result ma_data_converter_process_pcm_frames__resample_with_format_conv } if (pFramesInThisIteration != NULL) { - ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.config.format, pFramesInThisIteration, pConverter->config.formatIn, frameCountInThisIteration, pConverter->config.channelsIn, pConverter->config.ditherMode); + ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.format, pFramesInThisIteration, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode); } else { MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn)); } @@ -41405,7 +52177,7 @@ static ma_result ma_data_converter_process_pcm_frames__resample_with_format_conv /* If we are doing a post format conversion we need to do that now. */ if (pConverter->hasPostFormatConversion) { if (pFramesOutThisIteration != NULL) { - ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->config.formatOut, pTempBufferOut, pConverter->resampler.config.format, frameCountOutThisIteration, pConverter->resampler.config.channels, pConverter->config.ditherMode); + ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->formatOut, pTempBufferOut, pConverter->resampler.format, frameCountOutThisIteration, pConverter->resampler.channels, pConverter->ditherMode); } } @@ -41482,13 +52254,13 @@ static ma_result ma_data_converter_process_pcm_frames__channels_only(ma_data_con ma_uint64 frameCountThisIteration; if (pFramesIn != NULL) { - pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessed * ma_get_bytes_per_frame(pConverter->config.formatIn, pConverter->config.channelsIn)); + pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessed * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); } else { pFramesInThisIteration = NULL; } if (pFramesOut != NULL) { - pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessed * ma_get_bytes_per_frame(pConverter->config.formatOut, pConverter->config.channelsOut)); + pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessed * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); } else { pFramesOutThisIteration = NULL; } @@ -41510,7 +52282,7 @@ static ma_result ma_data_converter_process_pcm_frames__channels_only(ma_data_con } if (pFramesInThisIteration != NULL) { - ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pFramesInThisIteration, pConverter->config.formatIn, frameCountThisIteration, pConverter->config.channelsIn, pConverter->config.ditherMode); + ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pFramesInThisIteration, pConverter->formatIn, frameCountThisIteration, pConverter->channelsIn, pConverter->ditherMode); } else { MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn)); } @@ -41544,7 +52316,7 @@ static ma_result ma_data_converter_process_pcm_frames__channels_only(ma_data_con /* If we are doing a post format conversion we need to do that now. */ if (pConverter->hasPostFormatConversion) { if (pFramesOutThisIteration != NULL) { - ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->config.formatOut, pTempBufferOut, pConverter->channelConverter.format, frameCountThisIteration, pConverter->channelConverter.channelsOut, pConverter->config.ditherMode); + ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->formatOut, pTempBufferOut, pConverter->channelConverter.format, frameCountThisIteration, pConverter->channelConverter.channelsOut, pConverter->ditherMode); } } @@ -41562,7 +52334,7 @@ static ma_result ma_data_converter_process_pcm_frames__channels_only(ma_data_con return MA_SUCCESS; } -static ma_result ma_data_converter_process_pcm_frames__resampling_first(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) +static ma_result ma_data_converter_process_pcm_frames__resample_first(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) { ma_result result; ma_uint64 frameCountIn; @@ -41577,9 +52349,9 @@ static ma_result ma_data_converter_process_pcm_frames__resampling_first(ma_data_ ma_uint64 tempBufferOutCap; MA_ASSERT(pConverter != NULL); - MA_ASSERT(pConverter->resampler.config.format == pConverter->channelConverter.format); - MA_ASSERT(pConverter->resampler.config.channels == pConverter->channelConverter.channelsIn); - MA_ASSERT(pConverter->resampler.config.channels < pConverter->channelConverter.channelsOut); + MA_ASSERT(pConverter->resampler.format == pConverter->channelConverter.format); + MA_ASSERT(pConverter->resampler.channels == pConverter->channelConverter.channelsIn); + MA_ASSERT(pConverter->resampler.channels < pConverter->channelConverter.channelsOut); frameCountIn = 0; if (pFrameCountIn != NULL) { @@ -41594,8 +52366,8 @@ static ma_result ma_data_converter_process_pcm_frames__resampling_first(ma_data_ framesProcessedIn = 0; framesProcessedOut = 0; - tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.config.format, pConverter->resampler.config.channels); - tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.config.format, pConverter->resampler.config.channels); + tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); + tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut); while (framesProcessedOut < frameCountOut) { @@ -41607,10 +52379,10 @@ static ma_result ma_data_converter_process_pcm_frames__resampling_first(ma_data_ void* pChannelsBufferOut; if (pFramesIn != NULL) { - pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->config.formatIn, pConverter->config.channelsIn)); + pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); } if (pFramesOut != NULL) { - pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->config.formatOut, pConverter->config.channelsOut)); + pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); } /* Run input data through the resampler and output it to the temporary buffer. */ @@ -41635,16 +52407,31 @@ static ma_result ma_data_converter_process_pcm_frames__resampling_first(ma_data_ } /* We need to ensure we don't try to process too many input frames that we run out of room in the output buffer. If this happens we'll end up glitching. */ + + /* + We need to try to predict how many input frames will be required for the resampler. If the + resampler can tell us, we'll use that. Otherwise we'll need to make a best guess. The further + off we are from this, the more wasted format conversions we'll end up doing. + */ + #if 1 { - ma_uint64 requiredInputFrameCount = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration); + ma_uint64 requiredInputFrameCount; + + result = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration, &requiredInputFrameCount); + if (result != MA_SUCCESS) { + /* Fall back to a best guess. */ + requiredInputFrameCount = (frameCountOutThisIteration * pConverter->resampler.sampleRateIn) / pConverter->resampler.sampleRateOut; + } + if (frameCountInThisIteration > requiredInputFrameCount) { frameCountInThisIteration = requiredInputFrameCount; } } + #endif if (pConverter->hasPreFormatConversion) { if (pFramesIn != NULL) { - ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.config.format, pRunningFramesIn, pConverter->config.formatIn, frameCountInThisIteration, pConverter->config.channelsIn, pConverter->config.ditherMode); + ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.format, pRunningFramesIn, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode); pResampleBufferIn = pTempBufferIn; } else { pResampleBufferIn = NULL; @@ -41677,7 +52464,7 @@ static ma_result ma_data_converter_process_pcm_frames__resampling_first(ma_data_ /* Finally we do post format conversion. */ if (pConverter->hasPostFormatConversion) { - ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->config.formatOut, pChannelsBufferOut, pConverter->channelConverter.format, frameCountOutThisIteration, pConverter->channelConverter.channelsOut, pConverter->config.ditherMode); + ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->formatOut, pChannelsBufferOut, pConverter->channelConverter.format, frameCountOutThisIteration, pConverter->channelConverter.channelsOut, pConverter->ditherMode); } } @@ -41718,9 +52505,9 @@ static ma_result ma_data_converter_process_pcm_frames__channels_first(ma_data_co ma_uint64 tempBufferOutCap; MA_ASSERT(pConverter != NULL); - MA_ASSERT(pConverter->resampler.config.format == pConverter->channelConverter.format); - MA_ASSERT(pConverter->resampler.config.channels == pConverter->channelConverter.channelsOut); - MA_ASSERT(pConverter->resampler.config.channels < pConverter->channelConverter.channelsIn); + MA_ASSERT(pConverter->resampler.format == pConverter->channelConverter.format); + MA_ASSERT(pConverter->resampler.channels == pConverter->channelConverter.channelsOut); + MA_ASSERT(pConverter->resampler.channels <= pConverter->channelConverter.channelsIn); frameCountIn = 0; if (pFrameCountIn != NULL) { @@ -41737,7 +52524,7 @@ static ma_result ma_data_converter_process_pcm_frames__channels_first(ma_data_co tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn); tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut); - tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.config.format, pConverter->resampler.config.channels); + tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); while (framesProcessedOut < frameCountOut) { ma_uint64 frameCountInThisIteration; @@ -41748,22 +52535,67 @@ static ma_result ma_data_converter_process_pcm_frames__channels_first(ma_data_co void* pResampleBufferOut; if (pFramesIn != NULL) { - pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->config.formatIn, pConverter->config.channelsIn)); + pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); } if (pFramesOut != NULL) { - pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->config.formatOut, pConverter->config.channelsOut)); + pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); } - /* Run input data through the channel converter and output it to the temporary buffer. */ - frameCountInThisIteration = (frameCountIn - framesProcessedIn); + /* + Before doing any processing we need to determine how many frames we should try processing + this iteration, for both input and output. The resampler requires us to perform format and + channel conversion before passing any data into it. If we get our input count wrong, we'll + end up peforming redundant pre-processing. This isn't the end of the world, but it does + result in some inefficiencies proportionate to how far our estimates are off. + If the resampler has a means to calculate exactly how much we'll need, we'll use that. + Otherwise we'll make a best guess. In order to do this, we'll need to calculate the output + frame count first. + */ + frameCountOutThisIteration = (frameCountOut - framesProcessedOut); + if (frameCountOutThisIteration > tempBufferMidCap) { + frameCountOutThisIteration = tempBufferMidCap; + } + + if (pConverter->hasPostFormatConversion) { + if (frameCountOutThisIteration > tempBufferOutCap) { + frameCountOutThisIteration = tempBufferOutCap; + } + } + + /* Now that we have the output frame count we can determine the input frame count. */ + frameCountInThisIteration = (frameCountIn - framesProcessedIn); if (pConverter->hasPreFormatConversion) { if (frameCountInThisIteration > tempBufferInCap) { frameCountInThisIteration = tempBufferInCap; } + } + if (frameCountInThisIteration > tempBufferMidCap) { + frameCountInThisIteration = tempBufferMidCap; + } + + #if 1 + { + ma_uint64 requiredInputFrameCount; + + result = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration, &requiredInputFrameCount); + if (result != MA_SUCCESS) { + /* Fall back to a best guess. */ + requiredInputFrameCount = (frameCountOutThisIteration * pConverter->resampler.sampleRateIn) / pConverter->resampler.sampleRateOut; + } + + if (frameCountInThisIteration > requiredInputFrameCount) { + frameCountInThisIteration = requiredInputFrameCount; + } + } + #endif + + + /* Pre format conversion. */ + if (pConverter->hasPreFormatConversion) { if (pRunningFramesIn != NULL) { - ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pRunningFramesIn, pConverter->config.formatIn, frameCountInThisIteration, pConverter->config.channelsIn, pConverter->config.ditherMode); + ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pRunningFramesIn, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode); pChannelsBufferIn = pTempBufferIn; } else { pChannelsBufferIn = NULL; @@ -41772,43 +52604,15 @@ static ma_result ma_data_converter_process_pcm_frames__channels_first(ma_data_co pChannelsBufferIn = pRunningFramesIn; } - /* - We can't convert more frames than will fit in the output buffer. We shouldn't actually need to do this check because the channel count is always reduced - in this case which means we should always have capacity, but I'm leaving it here just for safety for future maintenance. - */ - if (frameCountInThisIteration > tempBufferMidCap) { - frameCountInThisIteration = tempBufferMidCap; - } - - /* - Make sure we don't read any more input frames than we need to fill the output frame count. If we do this we will end up in a situation where we lose some - input samples and will end up glitching. - */ - frameCountOutThisIteration = (frameCountOut - framesProcessedOut); - if (frameCountOutThisIteration > tempBufferMidCap) { - frameCountOutThisIteration = tempBufferMidCap; - } - - if (pConverter->hasPostFormatConversion) { - ma_uint64 requiredInputFrameCount; - - if (frameCountOutThisIteration > tempBufferOutCap) { - frameCountOutThisIteration = tempBufferOutCap; - } - - requiredInputFrameCount = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration); - if (frameCountInThisIteration > requiredInputFrameCount) { - frameCountInThisIteration = requiredInputFrameCount; - } - } + /* Channel conversion. */ result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferMid, pChannelsBufferIn, frameCountInThisIteration); if (result != MA_SUCCESS) { return result; } - /* At this point we have converted the channels to the output channel count which we now need to resample. */ + /* Resampling. */ if (pConverter->hasPostFormatConversion) { pResampleBufferOut = pTempBufferOut; } else { @@ -41820,13 +52624,15 @@ static ma_result ma_data_converter_process_pcm_frames__channels_first(ma_data_co return result; } - /* Finally we can do the post format conversion. */ + + /* Post format conversion. */ if (pConverter->hasPostFormatConversion) { if (pRunningFramesOut != NULL) { - ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->config.formatOut, pResampleBufferOut, pConverter->resampler.config.format, frameCountOutThisIteration, pConverter->config.channelsOut, pConverter->config.ditherMode); + ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->formatOut, pResampleBufferOut, pConverter->resampler.format, frameCountOutThisIteration, pConverter->channelsOut, pConverter->ditherMode); } } + framesProcessedIn += frameCountInThisIteration; framesProcessedOut += frameCountOutThisIteration; @@ -41854,46 +52660,15 @@ MA_API ma_result ma_data_converter_process_pcm_frames(ma_data_converter* pConver return MA_INVALID_ARGS; } - if (pConverter->isPassthrough) { - return ma_data_converter_process_pcm_frames__passthrough(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } - - /* - Here is where the real work is done. Getting here means we're not using a passthrough and we need to move the data through each of the relevant stages. The order - of our stages depends on the input and output channel count. If the input channels is less than the output channels we want to do sample rate conversion first so - that it has less work (resampling is the most expensive part of format conversion). - */ - if (pConverter->config.channelsIn < pConverter->config.channelsOut) { - /* Do resampling first, if necessary. */ - MA_ASSERT(pConverter->hasChannelConverter == MA_TRUE); - - if (pConverter->hasResampler) { - /* Resampling first. */ - return ma_data_converter_process_pcm_frames__resampling_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } else { - /* Resampling not required. */ - return ma_data_converter_process_pcm_frames__channels_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } - } else { - /* Do channel conversion first, if necessary. */ - if (pConverter->hasChannelConverter) { - if (pConverter->hasResampler) { - /* Channel routing first. */ - return ma_data_converter_process_pcm_frames__channels_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } else { - /* Resampling not required. */ - return ma_data_converter_process_pcm_frames__channels_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } - } else { - /* Channel routing not required. */ - if (pConverter->hasResampler) { - /* Resampling only. */ - return ma_data_converter_process_pcm_frames__resample_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } else { - /* No channel routing nor resampling required. Just format conversion. */ - return ma_data_converter_process_pcm_frames__format_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } - } + switch (pConverter->executionPath) + { + case ma_data_converter_execution_path_passthrough: return ma_data_converter_process_pcm_frames__passthrough(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); + case ma_data_converter_execution_path_format_only: return ma_data_converter_process_pcm_frames__format_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); + case ma_data_converter_execution_path_channels_only: return ma_data_converter_process_pcm_frames__channels_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); + case ma_data_converter_execution_path_resample_only: return ma_data_converter_process_pcm_frames__resample_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); + case ma_data_converter_execution_path_resample_first: return ma_data_converter_process_pcm_frames__resample_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); + case ma_data_converter_execution_path_channels_first: return ma_data_converter_process_pcm_frames__channels_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); + default: return MA_INVALID_OPERATION; /* Should never hit this. */ } } @@ -41923,32 +52698,6 @@ MA_API ma_result ma_data_converter_set_rate_ratio(ma_data_converter* pConverter, return ma_resampler_set_rate_ratio(&pConverter->resampler, ratioInOut); } -MA_API ma_uint64 ma_data_converter_get_required_input_frame_count(const ma_data_converter* pConverter, ma_uint64 outputFrameCount) -{ - if (pConverter == NULL) { - return 0; - } - - if (pConverter->hasResampler) { - return ma_resampler_get_required_input_frame_count(&pConverter->resampler, outputFrameCount); - } else { - return outputFrameCount; /* 1:1 */ - } -} - -MA_API ma_uint64 ma_data_converter_get_expected_output_frame_count(const ma_data_converter* pConverter, ma_uint64 inputFrameCount) -{ - if (pConverter == NULL) { - return 0; - } - - if (pConverter->hasResampler) { - return ma_resampler_get_expected_output_frame_count(&pConverter->resampler, inputFrameCount); - } else { - return inputFrameCount; /* 1:1 */ - } -} - MA_API ma_uint64 ma_data_converter_get_input_latency(const ma_data_converter* pConverter) { if (pConverter == NULL) { @@ -41975,6 +52724,90 @@ MA_API ma_uint64 ma_data_converter_get_output_latency(const ma_data_converter* p return 0; /* No latency without a resampler. */ } +MA_API ma_result ma_data_converter_get_required_input_frame_count(const ma_data_converter* pConverter, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) +{ + if (pInputFrameCount == NULL) { + return MA_INVALID_ARGS; + } + + *pInputFrameCount = 0; + + if (pConverter == NULL) { + return MA_INVALID_ARGS; + } + + if (pConverter->hasResampler) { + return ma_resampler_get_required_input_frame_count(&pConverter->resampler, outputFrameCount, pInputFrameCount); + } else { + *pInputFrameCount = outputFrameCount; /* 1:1 */ + return MA_SUCCESS; + } +} + +MA_API ma_result ma_data_converter_get_expected_output_frame_count(const ma_data_converter* pConverter, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) +{ + if (pOutputFrameCount == NULL) { + return MA_INVALID_ARGS; + } + + *pOutputFrameCount = 0; + + if (pConverter == NULL) { + return MA_INVALID_ARGS; + } + + if (pConverter->hasResampler) { + return ma_resampler_get_expected_output_frame_count(&pConverter->resampler, inputFrameCount, pOutputFrameCount); + } else { + *pOutputFrameCount = inputFrameCount; /* 1:1 */ + return MA_SUCCESS; + } +} + +MA_API ma_result ma_data_converter_get_input_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) +{ + if (pConverter == NULL || pChannelMap == NULL) { + return MA_INVALID_ARGS; + } + + if (pConverter->hasChannelConverter) { + ma_channel_converter_get_output_channel_map(&pConverter->channelConverter, pChannelMap, channelMapCap); + } else { + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pConverter->channelsOut); + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_data_converter_get_output_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) +{ + if (pConverter == NULL || pChannelMap == NULL) { + return MA_INVALID_ARGS; + } + + if (pConverter->hasChannelConverter) { + ma_channel_converter_get_input_channel_map(&pConverter->channelConverter, pChannelMap, channelMapCap); + } else { + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pConverter->channelsIn); + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_data_converter_reset(ma_data_converter* pConverter) +{ + if (pConverter == NULL) { + return MA_INVALID_ARGS; + } + + /* There's nothing to do if we're not resampling. */ + if (pConverter->hasResampler == MA_FALSE) { + return MA_SUCCESS; + } + + return ma_resampler_reset(&pConverter->resampler); +} + /************************************************************************************************************************************************************** @@ -41982,7 +52815,32 @@ MA_API ma_uint64 ma_data_converter_get_output_latency(const ma_data_converter* p Channel Maps **************************************************************************************************************************************************************/ -MA_API ma_channel ma_channel_map_get_default_channel(ma_uint32 channelCount, ma_uint32 channelIndex) +static ma_channel ma_channel_map_init_standard_channel(ma_standard_channel_map standardChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex); + +MA_API ma_channel ma_channel_map_get_channel(const ma_channel* pChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex) +{ + if (pChannelMap == NULL) { + return ma_channel_map_init_standard_channel(ma_standard_channel_map_default, channelCount, channelIndex); + } else { + if (channelIndex >= channelCount) { + return MA_CHANNEL_NONE; + } + + return pChannelMap[channelIndex]; + } +} + +MA_API void ma_channel_map_init_blank(ma_channel* pChannelMap, ma_uint32 channels) +{ + if (pChannelMap == NULL) { + return; + } + + MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channels); +} + + +static ma_channel ma_channel_map_init_standard_channel_microsoft(ma_uint32 channelCount, ma_uint32 channelIndex) { if (channelCount == 0 || channelIndex >= channelCount) { return MA_CHANNEL_NONE; @@ -41991,7 +52849,7 @@ MA_API ma_channel ma_channel_map_get_default_channel(ma_uint32 channelCount, ma_ /* This is the Microsoft channel map. Based off the speaker configurations mentioned here: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ksmedia/ns-ksmedia-ksaudio_channel_config */ switch (channelCount) { - case 0: return MA_CHANNEL_NONE; + case 0: return MA_CHANNEL_NONE; case 1: { @@ -42096,645 +52954,619 @@ MA_API ma_channel ma_channel_map_get_default_channel(ma_uint32 channelCount, ma_ return MA_CHANNEL_NONE; } -MA_API ma_channel ma_channel_map_get_channel(const ma_channel* pChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex) +static ma_channel ma_channel_map_init_standard_channel_alsa(ma_uint32 channelCount, ma_uint32 channelIndex) { - if (pChannelMap == NULL) { - return ma_channel_map_get_default_channel(channelCount, channelIndex); - } else { - if (channelIndex >= channelCount) { - return MA_CHANNEL_NONE; - } - - return pChannelMap[channelIndex]; - } -} - - -MA_API void ma_channel_map_init_blank(ma_uint32 channels, ma_channel* pChannelMap) -{ - if (pChannelMap == NULL) { - return; - } - - MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channels); -} - -static void ma_get_standard_channel_map_microsoft(ma_uint32 channels, ma_channel* pChannelMap) -{ - /* Based off the speaker configurations mentioned here: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ksmedia/ns-ksmedia-ksaudio_channel_config */ - switch (channels) + switch (channelCount) { + case 0: return MA_CHANNEL_NONE; + case 1: { - pChannelMap[0] = MA_CHANNEL_MONO; + return MA_CHANNEL_MONO; } break; case 2: { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + } } break; - case 3: /* Not defined, but best guess. */ + case 3: { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - pChannelMap[2] = MA_CHANNEL_FRONT_CENTER; + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + } } break; case 4: { -#ifndef MA_USE_QUAD_MICROSOFT_CHANNEL_MAP - /* Surround. Using the Surround profile has the advantage of the 3rd channel (MA_CHANNEL_FRONT_CENTER) mapping nicely with higher channel counts. */ - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - pChannelMap[2] = MA_CHANNEL_FRONT_CENTER; - pChannelMap[3] = MA_CHANNEL_BACK_CENTER; -#else - /* Quad. */ - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - pChannelMap[2] = MA_CHANNEL_BACK_LEFT; - pChannelMap[3] = MA_CHANNEL_BACK_RIGHT; -#endif + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_BACK_LEFT; + case 3: return MA_CHANNEL_BACK_RIGHT; + } + } break; + + case 5: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_BACK_LEFT; + case 3: return MA_CHANNEL_BACK_RIGHT; + case 4: return MA_CHANNEL_FRONT_CENTER; + } + } break; + + case 6: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_BACK_LEFT; + case 3: return MA_CHANNEL_BACK_RIGHT; + case 4: return MA_CHANNEL_FRONT_CENTER; + case 5: return MA_CHANNEL_LFE; + } + } break; + + case 7: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_BACK_LEFT; + case 3: return MA_CHANNEL_BACK_RIGHT; + case 4: return MA_CHANNEL_FRONT_CENTER; + case 5: return MA_CHANNEL_LFE; + case 6: return MA_CHANNEL_BACK_CENTER; + } + } break; + + case 8: + default: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_BACK_LEFT; + case 3: return MA_CHANNEL_BACK_RIGHT; + case 4: return MA_CHANNEL_FRONT_CENTER; + case 5: return MA_CHANNEL_LFE; + case 6: return MA_CHANNEL_SIDE_LEFT; + case 7: return MA_CHANNEL_SIDE_RIGHT; + } + } break; + } + + if (channelCount > 8) { + if (channelIndex < 32) { /* We have 32 AUX channels. */ + return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); + } + } + + /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ + return MA_CHANNEL_NONE; +} + +static ma_channel ma_channel_map_init_standard_channel_rfc3551(ma_uint32 channelCount, ma_uint32 channelIndex) +{ + switch (channelCount) + { + case 0: return MA_CHANNEL_NONE; + + case 1: + { + return MA_CHANNEL_MONO; + } break; + + case 2: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + } + } break; + + case 3: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + } + } break; + + case 4: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 2: return MA_CHANNEL_FRONT_CENTER; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 3: return MA_CHANNEL_BACK_CENTER; + } + } break; + + case 5: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + case 3: return MA_CHANNEL_BACK_LEFT; + case 4: return MA_CHANNEL_BACK_RIGHT; + } + } break; + + case 6: + default: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_SIDE_LEFT; + case 2: return MA_CHANNEL_FRONT_CENTER; + case 3: return MA_CHANNEL_FRONT_RIGHT; + case 4: return MA_CHANNEL_SIDE_RIGHT; + case 5: return MA_CHANNEL_BACK_CENTER; + } + } break; + } + + if (channelCount > 6) { + if (channelIndex < 32) { /* We have 32 AUX channels. */ + return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 6)); + } + } + + /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ + return MA_CHANNEL_NONE; +} + +static ma_channel ma_channel_map_init_standard_channel_flac(ma_uint32 channelCount, ma_uint32 channelIndex) +{ + switch (channelCount) + { + case 0: return MA_CHANNEL_NONE; + + case 1: + { + return MA_CHANNEL_MONO; + } break; + + case 2: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + } + } break; + + case 3: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + } + } break; + + case 4: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_BACK_LEFT; + case 3: return MA_CHANNEL_BACK_RIGHT; + } + } break; + + case 5: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + case 3: return MA_CHANNEL_BACK_LEFT; + case 4: return MA_CHANNEL_BACK_RIGHT; + } + } break; + + case 6: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + case 3: return MA_CHANNEL_LFE; + case 4: return MA_CHANNEL_BACK_LEFT; + case 5: return MA_CHANNEL_BACK_RIGHT; + } + } break; + + case 7: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + case 3: return MA_CHANNEL_LFE; + case 4: return MA_CHANNEL_BACK_CENTER; + case 5: return MA_CHANNEL_SIDE_LEFT; + case 6: return MA_CHANNEL_SIDE_RIGHT; + } + } break; + + case 8: + default: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + case 3: return MA_CHANNEL_LFE; + case 4: return MA_CHANNEL_BACK_LEFT; + case 5: return MA_CHANNEL_BACK_RIGHT; + case 6: return MA_CHANNEL_SIDE_LEFT; + case 7: return MA_CHANNEL_SIDE_RIGHT; + } + } break; + } + + if (channelCount > 8) { + if (channelIndex < 32) { /* We have 32 AUX channels. */ + return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); + } + } + + /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ + return MA_CHANNEL_NONE; +} + +static ma_channel ma_channel_map_init_standard_channel_vorbis(ma_uint32 channelCount, ma_uint32 channelIndex) +{ + switch (channelCount) + { + case 0: return MA_CHANNEL_NONE; + + case 1: + { + return MA_CHANNEL_MONO; + } break; + + case 2: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + } + } break; + + case 3: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_CENTER; + case 2: return MA_CHANNEL_FRONT_RIGHT; + } + } break; + + case 4: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_BACK_LEFT; + case 3: return MA_CHANNEL_BACK_RIGHT; + } + } break; + + case 5: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_CENTER; + case 2: return MA_CHANNEL_FRONT_RIGHT; + case 3: return MA_CHANNEL_BACK_LEFT; + case 4: return MA_CHANNEL_BACK_RIGHT; + } + } break; + + case 6: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_CENTER; + case 2: return MA_CHANNEL_FRONT_RIGHT; + case 3: return MA_CHANNEL_BACK_LEFT; + case 4: return MA_CHANNEL_BACK_RIGHT; + case 5: return MA_CHANNEL_LFE; + } + } break; + + case 7: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_CENTER; + case 2: return MA_CHANNEL_FRONT_RIGHT; + case 3: return MA_CHANNEL_SIDE_LEFT; + case 4: return MA_CHANNEL_SIDE_RIGHT; + case 5: return MA_CHANNEL_BACK_CENTER; + case 6: return MA_CHANNEL_LFE; + } + } break; + + case 8: + default: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_CENTER; + case 2: return MA_CHANNEL_FRONT_RIGHT; + case 3: return MA_CHANNEL_SIDE_LEFT; + case 4: return MA_CHANNEL_SIDE_RIGHT; + case 5: return MA_CHANNEL_BACK_LEFT; + case 6: return MA_CHANNEL_BACK_RIGHT; + case 7: return MA_CHANNEL_LFE; + } + } break; + } + + if (channelCount > 8) { + if (channelIndex < 32) { /* We have 32 AUX channels. */ + return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); + } + } + + /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ + return MA_CHANNEL_NONE; +} + +static ma_channel ma_channel_map_init_standard_channel_sound4(ma_uint32 channelCount, ma_uint32 channelIndex) +{ + switch (channelCount) + { + case 0: return MA_CHANNEL_NONE; + + case 1: + { + return MA_CHANNEL_MONO; + } break; + + case 2: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + } + } break; + + case 3: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + } + } break; + + case 4: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_BACK_LEFT; + case 3: return MA_CHANNEL_BACK_RIGHT; + } + } break; + + case 5: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + case 3: return MA_CHANNEL_BACK_LEFT; + case 4: return MA_CHANNEL_BACK_RIGHT; + } + } break; + + case 6: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_CENTER; + case 2: return MA_CHANNEL_FRONT_RIGHT; + case 3: return MA_CHANNEL_BACK_LEFT; + case 4: return MA_CHANNEL_BACK_RIGHT; + case 5: return MA_CHANNEL_LFE; + } + } break; + + case 7: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_CENTER; + case 2: return MA_CHANNEL_FRONT_RIGHT; + case 3: return MA_CHANNEL_SIDE_LEFT; + case 4: return MA_CHANNEL_SIDE_RIGHT; + case 5: return MA_CHANNEL_BACK_CENTER; + case 6: return MA_CHANNEL_LFE; + } + } break; + + case 8: + default: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_CENTER; + case 2: return MA_CHANNEL_FRONT_RIGHT; + case 3: return MA_CHANNEL_SIDE_LEFT; + case 4: return MA_CHANNEL_SIDE_RIGHT; + case 5: return MA_CHANNEL_BACK_LEFT; + case 6: return MA_CHANNEL_BACK_RIGHT; + case 7: return MA_CHANNEL_LFE; + } + } break; + } + + if (channelCount > 8) { + if (channelIndex < 32) { /* We have 32 AUX channels. */ + return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); + } + } + + /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ + return MA_CHANNEL_NONE; +} + +static ma_channel ma_channel_map_init_standard_channel_sndio(ma_uint32 channelCount, ma_uint32 channelIndex) +{ + switch (channelCount) + { + case 0: return MA_CHANNEL_NONE; + + case 1: + { + return MA_CHANNEL_MONO; + } break; + + case 2: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + } + } break; + + case 3: /* No defined, but best guess. */ + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + } + } break; + + case 4: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_BACK_LEFT; + case 3: return MA_CHANNEL_BACK_RIGHT; + } } break; case 5: /* Not defined, but best guess. */ { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - pChannelMap[2] = MA_CHANNEL_FRONT_CENTER; - pChannelMap[3] = MA_CHANNEL_BACK_LEFT; - pChannelMap[4] = MA_CHANNEL_BACK_RIGHT; - } break; - - case 6: - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - pChannelMap[2] = MA_CHANNEL_FRONT_CENTER; - pChannelMap[3] = MA_CHANNEL_LFE; - pChannelMap[4] = MA_CHANNEL_SIDE_LEFT; - pChannelMap[5] = MA_CHANNEL_SIDE_RIGHT; - } break; - - case 7: /* Not defined, but best guess. */ - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - pChannelMap[2] = MA_CHANNEL_FRONT_CENTER; - pChannelMap[3] = MA_CHANNEL_LFE; - pChannelMap[4] = MA_CHANNEL_BACK_CENTER; - pChannelMap[5] = MA_CHANNEL_SIDE_LEFT; - pChannelMap[6] = MA_CHANNEL_SIDE_RIGHT; - } break; - - case 8: - default: - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - pChannelMap[2] = MA_CHANNEL_FRONT_CENTER; - pChannelMap[3] = MA_CHANNEL_LFE; - pChannelMap[4] = MA_CHANNEL_BACK_LEFT; - pChannelMap[5] = MA_CHANNEL_BACK_RIGHT; - pChannelMap[6] = MA_CHANNEL_SIDE_LEFT; - pChannelMap[7] = MA_CHANNEL_SIDE_RIGHT; - } break; - } - - /* Remainder. */ - if (channels > 8) { - ma_uint32 iChannel; - for (iChannel = 8; iChannel < channels; ++iChannel) { - if (iChannel < MA_MAX_CHANNELS) { - pChannelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8)); - } else { - pChannelMap[iChannel] = MA_CHANNEL_NONE; + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_BACK_LEFT; + case 3: return MA_CHANNEL_BACK_RIGHT; + case 4: return MA_CHANNEL_FRONT_CENTER; } - } - } -} - -static void ma_get_standard_channel_map_alsa(ma_uint32 channels, ma_channel* pChannelMap) -{ - switch (channels) - { - case 1: - { - pChannelMap[0] = MA_CHANNEL_MONO; - } break; - - case 2: - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - } break; - - case 3: - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - pChannelMap[2] = MA_CHANNEL_FRONT_CENTER; - } break; - - case 4: - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - pChannelMap[2] = MA_CHANNEL_BACK_LEFT; - pChannelMap[3] = MA_CHANNEL_BACK_RIGHT; - } break; - - case 5: - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - pChannelMap[2] = MA_CHANNEL_BACK_LEFT; - pChannelMap[3] = MA_CHANNEL_BACK_RIGHT; - pChannelMap[4] = MA_CHANNEL_FRONT_CENTER; - } break; - - case 6: - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - pChannelMap[2] = MA_CHANNEL_BACK_LEFT; - pChannelMap[3] = MA_CHANNEL_BACK_RIGHT; - pChannelMap[4] = MA_CHANNEL_FRONT_CENTER; - pChannelMap[5] = MA_CHANNEL_LFE; - } break; - - case 7: - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - pChannelMap[2] = MA_CHANNEL_BACK_LEFT; - pChannelMap[3] = MA_CHANNEL_BACK_RIGHT; - pChannelMap[4] = MA_CHANNEL_FRONT_CENTER; - pChannelMap[5] = MA_CHANNEL_LFE; - pChannelMap[6] = MA_CHANNEL_BACK_CENTER; - } break; - - case 8: - default: - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - pChannelMap[2] = MA_CHANNEL_BACK_LEFT; - pChannelMap[3] = MA_CHANNEL_BACK_RIGHT; - pChannelMap[4] = MA_CHANNEL_FRONT_CENTER; - pChannelMap[5] = MA_CHANNEL_LFE; - pChannelMap[6] = MA_CHANNEL_SIDE_LEFT; - pChannelMap[7] = MA_CHANNEL_SIDE_RIGHT; - } break; - } - - /* Remainder. */ - if (channels > 8) { - ma_uint32 iChannel; - for (iChannel = 8; iChannel < channels; ++iChannel) { - if (iChannel < MA_MAX_CHANNELS) { - pChannelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8)); - } else { - pChannelMap[iChannel] = MA_CHANNEL_NONE; - } - } - } -} - -static void ma_get_standard_channel_map_rfc3551(ma_uint32 channels, ma_channel* pChannelMap) -{ - switch (channels) - { - case 1: - { - pChannelMap[0] = MA_CHANNEL_MONO; - } break; - - case 2: - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - } break; - - case 3: - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - pChannelMap[2] = MA_CHANNEL_FRONT_CENTER; - } break; - - case 4: - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_CENTER; - pChannelMap[2] = MA_CHANNEL_FRONT_RIGHT; - pChannelMap[3] = MA_CHANNEL_BACK_CENTER; - } break; - - case 5: - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - pChannelMap[2] = MA_CHANNEL_FRONT_CENTER; - pChannelMap[3] = MA_CHANNEL_BACK_LEFT; - pChannelMap[4] = MA_CHANNEL_BACK_RIGHT; - } break; - - case 6: - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_SIDE_LEFT; - pChannelMap[2] = MA_CHANNEL_FRONT_CENTER; - pChannelMap[3] = MA_CHANNEL_FRONT_RIGHT; - pChannelMap[4] = MA_CHANNEL_SIDE_RIGHT; - pChannelMap[5] = MA_CHANNEL_BACK_CENTER; - } break; - } - - /* Remainder. */ - if (channels > 8) { - ma_uint32 iChannel; - for (iChannel = 6; iChannel < channels; ++iChannel) { - if (iChannel < MA_MAX_CHANNELS) { - pChannelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-6)); - } else { - pChannelMap[iChannel] = MA_CHANNEL_NONE; - } - } - } -} - -static void ma_get_standard_channel_map_flac(ma_uint32 channels, ma_channel* pChannelMap) -{ - switch (channels) - { - case 1: - { - pChannelMap[0] = MA_CHANNEL_MONO; - } break; - - case 2: - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - } break; - - case 3: - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - pChannelMap[2] = MA_CHANNEL_FRONT_CENTER; - } break; - - case 4: - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - pChannelMap[2] = MA_CHANNEL_BACK_LEFT; - pChannelMap[3] = MA_CHANNEL_BACK_RIGHT; - } break; - - case 5: - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - pChannelMap[2] = MA_CHANNEL_FRONT_CENTER; - pChannelMap[3] = MA_CHANNEL_BACK_LEFT; - pChannelMap[4] = MA_CHANNEL_BACK_RIGHT; - } break; - - case 6: - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - pChannelMap[2] = MA_CHANNEL_FRONT_CENTER; - pChannelMap[3] = MA_CHANNEL_LFE; - pChannelMap[4] = MA_CHANNEL_BACK_LEFT; - pChannelMap[5] = MA_CHANNEL_BACK_RIGHT; - } break; - - case 7: - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - pChannelMap[2] = MA_CHANNEL_FRONT_CENTER; - pChannelMap[3] = MA_CHANNEL_LFE; - pChannelMap[4] = MA_CHANNEL_BACK_CENTER; - pChannelMap[5] = MA_CHANNEL_SIDE_LEFT; - pChannelMap[6] = MA_CHANNEL_SIDE_RIGHT; - } break; - - case 8: - default: - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - pChannelMap[2] = MA_CHANNEL_FRONT_CENTER; - pChannelMap[3] = MA_CHANNEL_LFE; - pChannelMap[4] = MA_CHANNEL_BACK_LEFT; - pChannelMap[5] = MA_CHANNEL_BACK_RIGHT; - pChannelMap[6] = MA_CHANNEL_SIDE_LEFT; - pChannelMap[7] = MA_CHANNEL_SIDE_RIGHT; - } break; - } - - /* Remainder. */ - if (channels > 8) { - ma_uint32 iChannel; - for (iChannel = 8; iChannel < channels; ++iChannel) { - if (iChannel < MA_MAX_CHANNELS) { - pChannelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8)); - } else { - pChannelMap[iChannel] = MA_CHANNEL_NONE; - } - } - } -} - -static void ma_get_standard_channel_map_vorbis(ma_uint32 channels, ma_channel* pChannelMap) -{ - /* In Vorbis' type 0 channel mapping, the first two channels are not always the standard left/right - it will have the center speaker where the right usually goes. Why?! */ - switch (channels) - { - case 1: - { - pChannelMap[0] = MA_CHANNEL_MONO; - } break; - - case 2: - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - } break; - - case 3: - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_CENTER; - pChannelMap[2] = MA_CHANNEL_FRONT_RIGHT; - } break; - - case 4: - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - pChannelMap[2] = MA_CHANNEL_BACK_LEFT; - pChannelMap[3] = MA_CHANNEL_BACK_RIGHT; - } break; - - case 5: - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_CENTER; - pChannelMap[2] = MA_CHANNEL_FRONT_RIGHT; - pChannelMap[3] = MA_CHANNEL_BACK_LEFT; - pChannelMap[4] = MA_CHANNEL_BACK_RIGHT; - } break; - - case 6: - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_CENTER; - pChannelMap[2] = MA_CHANNEL_FRONT_RIGHT; - pChannelMap[3] = MA_CHANNEL_BACK_LEFT; - pChannelMap[4] = MA_CHANNEL_BACK_RIGHT; - pChannelMap[5] = MA_CHANNEL_LFE; - } break; - - case 7: - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_CENTER; - pChannelMap[2] = MA_CHANNEL_FRONT_RIGHT; - pChannelMap[3] = MA_CHANNEL_SIDE_LEFT; - pChannelMap[4] = MA_CHANNEL_SIDE_RIGHT; - pChannelMap[5] = MA_CHANNEL_BACK_CENTER; - pChannelMap[6] = MA_CHANNEL_LFE; - } break; - - case 8: - default: - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_CENTER; - pChannelMap[2] = MA_CHANNEL_FRONT_RIGHT; - pChannelMap[3] = MA_CHANNEL_SIDE_LEFT; - pChannelMap[4] = MA_CHANNEL_SIDE_RIGHT; - pChannelMap[5] = MA_CHANNEL_BACK_LEFT; - pChannelMap[6] = MA_CHANNEL_BACK_RIGHT; - pChannelMap[7] = MA_CHANNEL_LFE; - } break; - } - - /* Remainder. */ - if (channels > 8) { - ma_uint32 iChannel; - for (iChannel = 8; iChannel < channels; ++iChannel) { - if (iChannel < MA_MAX_CHANNELS) { - pChannelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8)); - } else { - pChannelMap[iChannel] = MA_CHANNEL_NONE; - } - } - } -} - -static void ma_get_standard_channel_map_sound4(ma_uint32 channels, ma_channel* pChannelMap) -{ - switch (channels) - { - case 1: - { - pChannelMap[0] = MA_CHANNEL_MONO; - } break; - - case 2: - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - } break; - - case 3: - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - pChannelMap[2] = MA_CHANNEL_BACK_CENTER; - } break; - - case 4: - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - pChannelMap[2] = MA_CHANNEL_BACK_LEFT; - pChannelMap[3] = MA_CHANNEL_BACK_RIGHT; - } break; - - case 5: - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - pChannelMap[2] = MA_CHANNEL_BACK_LEFT; - pChannelMap[3] = MA_CHANNEL_BACK_RIGHT; - pChannelMap[4] = MA_CHANNEL_FRONT_CENTER; - } break; - - case 6: - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - pChannelMap[2] = MA_CHANNEL_BACK_LEFT; - pChannelMap[3] = MA_CHANNEL_BACK_RIGHT; - pChannelMap[4] = MA_CHANNEL_FRONT_CENTER; - pChannelMap[5] = MA_CHANNEL_LFE; - } break; - - case 7: - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - pChannelMap[2] = MA_CHANNEL_BACK_LEFT; - pChannelMap[3] = MA_CHANNEL_BACK_RIGHT; - pChannelMap[4] = MA_CHANNEL_FRONT_CENTER; - pChannelMap[5] = MA_CHANNEL_BACK_CENTER; - pChannelMap[6] = MA_CHANNEL_LFE; - } break; - - case 8: - default: - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - pChannelMap[2] = MA_CHANNEL_BACK_LEFT; - pChannelMap[3] = MA_CHANNEL_BACK_RIGHT; - pChannelMap[4] = MA_CHANNEL_FRONT_CENTER; - pChannelMap[5] = MA_CHANNEL_LFE; - pChannelMap[6] = MA_CHANNEL_SIDE_LEFT; - pChannelMap[7] = MA_CHANNEL_SIDE_RIGHT; - } break; - } - - /* Remainder. */ - if (channels > 8) { - ma_uint32 iChannel; - for (iChannel = 8; iChannel < MA_MAX_CHANNELS; ++iChannel) { - if (iChannel < MA_MAX_CHANNELS) { - pChannelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8)); - } else { - pChannelMap[iChannel] = MA_CHANNEL_NONE; - } - } - } -} - -static void ma_get_standard_channel_map_sndio(ma_uint32 channels, ma_channel* pChannelMap) -{ - switch (channels) - { - case 1: - { - pChannelMap[0] = MA_CHANNEL_MONO; - } break; - - case 2: - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - } break; - - case 3: - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - pChannelMap[2] = MA_CHANNEL_FRONT_CENTER; - } break; - - case 4: - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - pChannelMap[2] = MA_CHANNEL_BACK_LEFT; - pChannelMap[3] = MA_CHANNEL_BACK_RIGHT; - } break; - - case 5: - { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - pChannelMap[2] = MA_CHANNEL_BACK_LEFT; - pChannelMap[3] = MA_CHANNEL_BACK_RIGHT; - pChannelMap[4] = MA_CHANNEL_FRONT_CENTER; } break; case 6: default: { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - pChannelMap[2] = MA_CHANNEL_BACK_LEFT; - pChannelMap[3] = MA_CHANNEL_BACK_RIGHT; - pChannelMap[4] = MA_CHANNEL_FRONT_CENTER; - pChannelMap[5] = MA_CHANNEL_LFE; + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_BACK_LEFT; + case 3: return MA_CHANNEL_BACK_RIGHT; + case 4: return MA_CHANNEL_FRONT_CENTER; + case 5: return MA_CHANNEL_LFE; + } } break; } - /* Remainder. */ - if (channels > 6) { - ma_uint32 iChannel; - for (iChannel = 6; iChannel < channels && iChannel < MA_MAX_CHANNELS; ++iChannel) { - if (iChannel < MA_MAX_CHANNELS) { - pChannelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-6)); - } else { - pChannelMap[iChannel] = MA_CHANNEL_NONE; - } + if (channelCount > 6) { + if (channelIndex < 32) { /* We have 32 AUX channels. */ + return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 6)); } } + + /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ + return MA_CHANNEL_NONE; } -MA_API void ma_get_standard_channel_map(ma_standard_channel_map standardChannelMap, ma_uint32 channels, ma_channel* pChannelMap) + +static ma_channel ma_channel_map_init_standard_channel(ma_standard_channel_map standardChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex) { + if (channelCount == 0 || channelIndex >= channelCount) { + return MA_CHANNEL_NONE; + } + switch (standardChannelMap) { case ma_standard_channel_map_alsa: { - ma_get_standard_channel_map_alsa(channels, pChannelMap); + return ma_channel_map_init_standard_channel_alsa(channelCount, channelIndex); } break; case ma_standard_channel_map_rfc3551: { - ma_get_standard_channel_map_rfc3551(channels, pChannelMap); + return ma_channel_map_init_standard_channel_rfc3551(channelCount, channelIndex); } break; case ma_standard_channel_map_flac: { - ma_get_standard_channel_map_flac(channels, pChannelMap); + return ma_channel_map_init_standard_channel_flac(channelCount, channelIndex); } break; case ma_standard_channel_map_vorbis: { - ma_get_standard_channel_map_vorbis(channels, pChannelMap); + return ma_channel_map_init_standard_channel_vorbis(channelCount, channelIndex); } break; case ma_standard_channel_map_sound4: { - ma_get_standard_channel_map_sound4(channels, pChannelMap); + return ma_channel_map_init_standard_channel_sound4(channelCount, channelIndex); } break; case ma_standard_channel_map_sndio: { - ma_get_standard_channel_map_sndio(channels, pChannelMap); + return ma_channel_map_init_standard_channel_sndio(channelCount, channelIndex); } break; case ma_standard_channel_map_microsoft: /* Also default. */ /*case ma_standard_channel_map_default;*/ default: { - ma_get_standard_channel_map_microsoft(channels, pChannelMap); + return ma_channel_map_init_standard_channel_microsoft(channelCount, channelIndex); } break; } } +MA_API void ma_channel_map_init_standard(ma_standard_channel_map standardChannelMap, ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channels) +{ + ma_uint32 iChannel; + + if (pChannelMap == NULL || channelMapCap == 0 || channels == 0) { + return; + } + + for (iChannel = 0; iChannel < channels; iChannel += 1) { + if (channelMapCap == 0) { + break; /* Ran out of room. */ + } + + pChannelMap[0] = ma_channel_map_init_standard_channel(standardChannelMap, channels, iChannel); + pChannelMap += 1; + channelMapCap -= 1; + } +} + MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels) { if (pOut != NULL && pIn != NULL && channels > 0) { @@ -42742,7 +53574,7 @@ MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint } } -MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels) +MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, size_t channelMapCapOut, const ma_channel* pIn, ma_uint32 channels) { if (pOut == NULL || channels == 0) { return; @@ -42751,16 +53583,12 @@ MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, const ma_channel* p if (pIn != NULL) { ma_channel_map_copy(pOut, pIn, channels); } else { - ma_get_standard_channel_map(ma_standard_channel_map_default, channels, pOut); + ma_channel_map_init_standard(ma_standard_channel_map_default, pOut, channelMapCapOut, channels); } } -MA_API ma_bool32 ma_channel_map_valid(ma_uint32 channels, const ma_channel* pChannelMap) +MA_API ma_bool32 ma_channel_map_is_valid(const ma_channel* pChannelMap, ma_uint32 channels) { - if (pChannelMap == NULL) { - return MA_FALSE; - } - /* A channel count of 0 is invalid. */ if (channels == 0) { return MA_FALSE; @@ -42770,7 +53598,7 @@ MA_API ma_bool32 ma_channel_map_valid(ma_uint32 channels, const ma_channel* pCha if (channels > 1) { ma_uint32 iChannel; for (iChannel = 0; iChannel < channels; ++iChannel) { - if (pChannelMap[iChannel] == MA_CHANNEL_MONO) { + if (ma_channel_map_get_channel(pChannelMap, channels, iChannel) == MA_CHANNEL_MONO) { return MA_FALSE; } } @@ -42779,7 +53607,7 @@ MA_API ma_bool32 ma_channel_map_valid(ma_uint32 channels, const ma_channel* pCha return MA_TRUE; } -MA_API ma_bool32 ma_channel_map_equal(ma_uint32 channels, const ma_channel* pChannelMapA, const ma_channel* pChannelMapB) +MA_API ma_bool32 ma_channel_map_is_equal(const ma_channel* pChannelMapA, const ma_channel* pChannelMapB, ma_uint32 channels) { ma_uint32 iChannel; @@ -42796,7 +53624,7 @@ MA_API ma_bool32 ma_channel_map_equal(ma_uint32 channels, const ma_channel* pCha return MA_TRUE; } -MA_API ma_bool32 ma_channel_map_blank(ma_uint32 channels, const ma_channel* pChannelMap) +MA_API ma_bool32 ma_channel_map_is_blank(const ma_channel* pChannelMap, ma_uint32 channels) { ma_uint32 iChannel; @@ -42839,8 +53667,6 @@ MA_API ma_uint64 ma_convert_frames(void* pOut, ma_uint64 frameCountOut, ma_forma ma_data_converter_config config; config = ma_data_converter_config_init(formatIn, formatOut, channelsIn, channelsOut, sampleRateIn, sampleRateOut); - ma_get_standard_channel_map(ma_standard_channel_map_default, channelsOut, config.channelMapOut); - ma_get_standard_channel_map(ma_standard_channel_map_default, channelsIn, config.channelMapIn); config.resampling.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER); return ma_convert_frames_ex(pOut, frameCountOut, pIn, frameCountIn, &config); @@ -42855,13 +53681,31 @@ MA_API ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const return 0; } - result = ma_data_converter_init(pConfig, &converter); + result = ma_data_converter_init(pConfig, NULL, &converter); if (result != MA_SUCCESS) { return 0; /* Failed to initialize the data converter. */ } if (pOut == NULL) { - frameCountOut = ma_data_converter_get_expected_output_frame_count(&converter, frameCountIn); + result = ma_data_converter_get_expected_output_frame_count(&converter, frameCountIn, &frameCountOut); + if (result != MA_SUCCESS) { + if (result == MA_NOT_IMPLEMENTED) { + /* No way to calculate the number of frames, so we'll need to brute force it and loop. */ + frameCountOut = 0; + + while (frameCountIn > 0) { + ma_uint64 framesProcessedIn = frameCountIn; + ma_uint64 framesProcessedOut = 0xFFFFFFFF; + + result = ma_data_converter_process_pcm_frames(&converter, pIn, &framesProcessedIn, NULL, &framesProcessedOut); + if (result != MA_SUCCESS) { + break; + } + + frameCountIn -= framesProcessedIn; + } + } + } } else { result = ma_data_converter_process_pcm_frames(&converter, pIn, &frameCountIn, pOut, &frameCountOut); if (result != MA_SUCCESS) { @@ -42869,7 +53713,7 @@ MA_API ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const } } - ma_data_converter_uninit(&converter); + ma_data_converter_uninit(&converter, NULL); return frameCountOut; } @@ -43038,7 +53882,7 @@ MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppB return MA_SUCCESS; } -MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes, void* pBufferOut) +MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes) { ma_uint32 readOffset; ma_uint32 readOffsetInBytes; @@ -43050,11 +53894,6 @@ MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes, void* pBuffer return MA_INVALID_ARGS; } - /* Validate the buffer. */ - if (pBufferOut != ma_rb__get_read_ptr(pRB)) { - return MA_INVALID_ARGS; - } - readOffset = c89atomic_load_32(&pRB->encodedReadOffset); ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); @@ -43129,7 +53968,7 @@ MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** pp return MA_SUCCESS; } -MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes, void* pBufferOut) +MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes) { ma_uint32 writeOffset; ma_uint32 writeOffsetInBytes; @@ -43141,11 +53980,6 @@ MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes, void* pBuffe return MA_INVALID_ARGS; } - /* Validate the buffer. */ - if (pBufferOut != ma_rb__get_write_ptr(pRB)) { - return MA_INVALID_ARGS; - } - writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset); ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); @@ -43429,13 +54263,13 @@ MA_API ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames return MA_SUCCESS; } -MA_API ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames, void* pBufferOut) +MA_API ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames) { if (pRB == NULL) { return MA_INVALID_ARGS; } - return ma_rb_commit_read(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB), pBufferOut); + return ma_rb_commit_read(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB)); } MA_API ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut) @@ -43458,13 +54292,13 @@ MA_API ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrame return MA_SUCCESS; } -MA_API ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames, void* pBufferOut) +MA_API ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames) { if (pRB == NULL) { return MA_INVALID_ARGS; } - return ma_rb_commit_write(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB), pBufferOut); + return ma_rb_commit_write(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB)); } MA_API ma_result ma_pcm_rb_seek_read(ma_pcm_rb* pRB, ma_uint32 offsetInFrames) @@ -43665,19 +54499,33 @@ MA_API const char* ma_result_description(ma_result result) MA_API void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks != NULL) { - return ma__malloc_from_callbacks(sz, pAllocationCallbacks); + if (pAllocationCallbacks->onMalloc != NULL) { + return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); + } else { + return NULL; /* Do not fall back to the default implementation. */ + } } else { return ma__malloc_default(sz, NULL); } } +MA_API void* ma_calloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) +{ + void* p = ma_malloc(sz, pAllocationCallbacks); + if (p != NULL) { + MA_ZERO_MEMORY(p, sz); + } + + return p; +} + MA_API void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks != NULL) { if (pAllocationCallbacks->onRealloc != NULL) { return pAllocationCallbacks->onRealloc(p, sz, pAllocationCallbacks->pUserData); } else { - return NULL; /* This requires a native implementation of realloc(). */ + return NULL; /* Do not fall back to the default implementation. */ } } else { return ma__realloc_default(p, sz, NULL); @@ -43686,8 +54534,16 @@ MA_API void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllo MA_API void ma_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) { + if (p == NULL) { + return; + } + if (pAllocationCallbacks != NULL) { - ma__free_from_callbacks(p, pAllocationCallbacks); + if (pAllocationCallbacks->onFree != NULL) { + pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); + } else { + return; /* Do no fall back to the default implementation. */ + } } else { ma__free_default(p, NULL); } @@ -43792,11 +54648,6 @@ MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_da pDataSourceBase->pNext = NULL; pDataSourceBase->onGetNext = NULL; - /* Compatibility: Need to make a copy of the callbacks. This will be removed in version 0.11. */ - if (pConfig->vtable != NULL) { - pDataSourceBase->cb = *pConfig->vtable; - } - return MA_SUCCESS; } @@ -43812,7 +54663,6 @@ MA_API void ma_data_source_uninit(ma_data_source* pDataSource) */ } -#if defined(MA_EXPERIMENTAL__DATA_LOOPING_AND_CHAINING) static ma_result ma_data_source_resolve_current(ma_data_source* pDataSource, ma_data_source** ppCurrentDataSource) { ma_data_source_base* pCurrentDataSource = (ma_data_source_base*)pDataSource; @@ -43839,63 +54689,76 @@ static ma_result ma_data_source_resolve_current(ma_data_source* pDataSource, ma_ return MA_SUCCESS; } -static ma_result ma_data_source_read_pcm_frames_within_range(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead, ma_bool32 loop) +static ma_result ma_data_source_read_pcm_frames_within_range(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - + ma_result result; + ma_uint64 framesRead = 0; + ma_bool32 loop = ma_data_source_is_looping(pDataSource); + if (pDataSourceBase == NULL) { return MA_AT_END; } - if (pDataSourceBase->rangeEndInFrames == ~((ma_uint64)0) && (pDataSourceBase->loopEndInFrames == ~((ma_uint64)0) || loop == MA_FALSE)) { - /* No range is set - just read like normal. The data source itself will tell us when the end is reached. */ - return pDataSourceBase->cb.onRead(pDataSourceBase, pFramesOut, frameCount, pFramesRead); + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + + if ((pDataSourceBase->vtable->flags & MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT) != 0 || (pDataSourceBase->rangeEndInFrames == ~((ma_uint64)0) && (pDataSourceBase->loopEndInFrames == ~((ma_uint64)0) || loop == MA_FALSE))) { + /* Either the data source is self-managing the range, or no range is set - just read like normal. The data source itself will tell us when the end is reached. */ + result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead); } else { /* Need to clamp to within the range. */ - ma_result result; ma_uint64 cursor; - ma_uint64 framesRead = 0; - ma_uint64 rangeEnd; result = ma_data_source_get_cursor_in_pcm_frames(pDataSourceBase, &cursor); if (result != MA_SUCCESS) { /* Failed to retrieve the cursor. Cannot read within a range or loop points. Just read like normal - this may happen for things like noise data sources where it doesn't really matter. */ - return pDataSourceBase->cb.onRead(pDataSourceBase, pFramesOut, frameCount, pFramesRead); - } + result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead); + } else { + ma_uint64 rangeEnd; - /* We have the cursor. We need to make sure we don't read beyond our range. */ - rangeEnd = pDataSourceBase->rangeEndInFrames; + /* We have the cursor. We need to make sure we don't read beyond our range. */ + rangeEnd = pDataSourceBase->rangeEndInFrames; - /* If looping, make sure we're within range. */ - if (loop) { - if (pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) { - rangeEnd = ma_min(rangeEnd, pDataSourceBase->rangeBegInFrames + pDataSourceBase->loopEndInFrames); + /* If looping, make sure we're within range. */ + if (loop) { + if (pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) { + rangeEnd = ma_min(rangeEnd, pDataSourceBase->rangeBegInFrames + pDataSourceBase->loopEndInFrames); + } + } + + if (frameCount > (rangeEnd - cursor) && rangeEnd != ~((ma_uint64)0)) { + frameCount = (rangeEnd - cursor); + } + + /* + If the cursor is sitting on the end of the range the frame count will be set to 0 which can + result in MA_INVALID_ARGS. In this case, we don't want to try reading, but instead return + MA_AT_END so the higher level function can know about it. + */ + if (frameCount > 0) { + result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead); + } else { + result = MA_AT_END; /* The cursor is sitting on the end of the range which means we're at the end. */ } } - - if (frameCount > (rangeEnd - cursor) && rangeEnd != ~((ma_uint64)0)) { - frameCount = (rangeEnd - cursor); - } - - result = pDataSourceBase->cb.onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead); - - if (pFramesRead != NULL) { - *pFramesRead = framesRead; - } - - /* We need to make sure MA_AT_END is returned if we hit the end of the range. */ - if (result != MA_AT_END && framesRead == 0) { - result = MA_AT_END; - } - - return result; } -} -#endif -MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead, ma_bool32 loop) + if (pFramesRead != NULL) { + *pFramesRead = framesRead; + } + + /* We need to make sure MA_AT_END is returned if we hit the end of the range. */ + if (result == MA_SUCCESS && framesRead == 0) { + result = MA_AT_END; + } + + return result; +} + +MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { -#if defined(MA_EXPERIMENTAL__DATA_LOOPING_AND_CHAINING) ma_result result = MA_SUCCESS; ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; ma_data_source_base* pCurrentDataSource; @@ -43904,26 +54767,33 @@ MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, voi ma_format format; ma_uint32 channels; ma_uint32 emptyLoopCounter = 0; /* Keeps track of how many times 0 frames have been read. For infinite loop detection of sounds with no audio data. */ + ma_bool32 loop; if (pFramesRead != NULL) { *pFramesRead = 0; } + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + if (pDataSourceBase == NULL) { return MA_INVALID_ARGS; } + loop = ma_data_source_is_looping(pDataSource); + /* We need to know the data format so we can advance the output buffer as we read frames. If this fails, chaining will not work and we'll just read as much as we can from the current source. */ - if (ma_data_source_get_data_format(pDataSource, &format, &channels, NULL) != MA_SUCCESS) { + if (ma_data_source_get_data_format(pDataSource, &format, &channels, NULL, NULL, 0) != MA_SUCCESS) { result = ma_data_source_resolve_current(pDataSource, (ma_data_source**)&pCurrentDataSource); if (result != MA_SUCCESS) { return result; } - return ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pFramesOut, frameCount, pFramesRead, loop); + return ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pFramesOut, frameCount, pFramesRead); } /* @@ -43946,7 +54816,7 @@ MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, voi break; } - result = ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pRunningFramesOut, framesRemaining, &framesProcessed, loop); + result = ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pRunningFramesOut, framesRemaining, &framesProcessed); totalFramesProcessed += framesProcessed; /* @@ -43958,10 +54828,18 @@ MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, voi } /* - We can determine if we've reached the end by checking the return value of the onRead() - callback. To loop back to the start, all we need to do is seek back to the first frame. + We can determine if we've reached the end by checking if ma_data_source_read_pcm_frames_within_range() returned + MA_AT_END. To loop back to the start, all we need to do is seek back to the first frame. */ if (result == MA_AT_END) { + /* + The result needs to be reset back to MA_SUCCESS (from MA_AT_END) so that we don't + accidentally return MA_AT_END when data has been read in prior loop iterations. at the + end of this function, the result will be checked for MA_SUCCESS, and if the total + number of frames processed is 0, will be explicitly set to MA_AT_END. + */ + result = MA_SUCCESS; + /* We reached the end. If we're looping, we just loop back to the start of the current data source. If we're not looping we need to check if we have another in the chain, and @@ -43977,7 +54855,8 @@ MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, voi emptyLoopCounter = 0; } - if (ma_data_source_seek_to_pcm_frame(pCurrentDataSource, pCurrentDataSource->loopBegInFrames) != MA_SUCCESS) { + result = ma_data_source_seek_to_pcm_frame(pCurrentDataSource, pCurrentDataSource->loopBegInFrames); + if (result != MA_SUCCESS) { break; /* Failed to loop. Abort. */ } @@ -43997,14 +54876,10 @@ MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, voi } /* The next data source needs to be rewound to ensure data is read in looping scenarios. */ - ma_data_source_seek_to_pcm_frame(pDataSourceBase->pCurrent, 0); - - /* - We need to make sure we clear the MA_AT_END result so we don't accidentally return - it in the event that we coincidentally ended reading at the exact transition point - of two data sources in a chain. - */ - result = MA_SUCCESS; + result = ma_data_source_seek_to_pcm_frame(pDataSourceBase->pCurrent, 0); + if (result != MA_SUCCESS) { + break; + } } } @@ -44017,93 +54892,29 @@ MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, voi *pFramesRead = totalFramesProcessed; } + MA_ASSERT(!(result == MA_AT_END && totalFramesProcessed > 0)); /* We should never be returning MA_AT_END if we read some data. */ + + if (result == MA_SUCCESS && totalFramesProcessed == 0) { + result = MA_AT_END; + } + return result; -#else - ma_data_source_callbacks* pCallbacks = (ma_data_source_callbacks*)pDataSource; - - /* Safety. */ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (pCallbacks == NULL) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onRead == NULL) { - return MA_NOT_IMPLEMENTED; - } - - /* A very small optimization for the non looping case. */ - if (loop == MA_FALSE) { - return pCallbacks->onRead(pDataSource, pFramesOut, frameCount, pFramesRead); - } else { - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - if (ma_data_source_get_data_format(pDataSource, &format, &channels, &sampleRate) != MA_SUCCESS) { - return pCallbacks->onRead(pDataSource, pFramesOut, frameCount, pFramesRead); /* We don't have a way to retrieve the data format which means we don't know how to offset the output buffer. Just read as much as we can. */ - } else { - ma_result result = MA_SUCCESS; - ma_uint64 totalFramesProcessed; - void* pRunningFramesOut = pFramesOut; - - totalFramesProcessed = 0; - while (totalFramesProcessed < frameCount) { - ma_uint64 framesProcessed; - ma_uint64 framesRemaining = frameCount - totalFramesProcessed; - - result = pCallbacks->onRead(pDataSource, pRunningFramesOut, framesRemaining, &framesProcessed); - totalFramesProcessed += framesProcessed; - - /* - If we encounted an error from the read callback, make sure it's propagated to the caller. The caller may need to know whether or not MA_BUSY is returned which is - not necessarily considered an error. - */ - if (result != MA_SUCCESS && result != MA_AT_END) { - break; - } - - /* - We can determine if we've reached the end by checking the return value of the onRead() callback. If it's less than what we requested it means - we've reached the end. To loop back to the start, all we need to do is seek back to the first frame. - */ - if (framesProcessed < framesRemaining || result == MA_AT_END) { - if (ma_data_source_seek_to_pcm_frame(pDataSource, 0) != MA_SUCCESS) { - break; - } - } - - if (pRunningFramesOut != NULL) { - pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesProcessed * ma_get_bytes_per_frame(format, channels)); - } - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesProcessed; - } - - return result; - } - } -#endif } -MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked, ma_bool32 loop) +MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked) { - return ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, pFramesSeeked, loop); + return ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, pFramesSeeked); } MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex) { -#if defined(MA_EXPERIMENTAL__DATA_LOOPING_AND_CHAINING) ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; if (pDataSourceBase == NULL) { return MA_SUCCESS; } - if (pDataSourceBase->cb.onSeek == NULL) { + if (pDataSourceBase->vtable->onSeek == NULL) { return MA_NOT_IMPLEMENTED; } @@ -44111,78 +54922,40 @@ MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, m return MA_INVALID_OPERATION; /* Trying to seek to far forward. */ } - return pDataSourceBase->cb.onSeek(pDataSource, pDataSourceBase->rangeBegInFrames + frameIndex); -#else - ma_data_source_callbacks* pCallbacks = (ma_data_source_callbacks*)pDataSource; - if (pCallbacks == NULL) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onSeek == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pCallbacks->onSeek(pDataSource, frameIndex); -#endif + return pDataSourceBase->vtable->onSeek(pDataSource, pDataSourceBase->rangeBegInFrames + frameIndex); } -MA_API ma_result ma_data_source_map(ma_data_source* pDataSource, void** ppFramesOut, ma_uint64* pFrameCount) -{ - ma_data_source_callbacks* pCallbacks = (ma_data_source_callbacks*)pDataSource; - if (pCallbacks == NULL) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onMap == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pCallbacks->onMap(pDataSource, ppFramesOut, pFrameCount); -} - -MA_API ma_result ma_data_source_unmap(ma_data_source* pDataSource, ma_uint64 frameCount) -{ - ma_data_source_callbacks* pCallbacks = (ma_data_source_callbacks*)pDataSource; - if (pCallbacks == NULL) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onUnmap == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pCallbacks->onUnmap(pDataSource, frameCount); -} - -MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate) +MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) { + ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; ma_result result; ma_format format; ma_uint32 channels; ma_uint32 sampleRate; - ma_data_source_callbacks* pCallbacks = (ma_data_source_callbacks*)pDataSource; + /* Initialize to defaults for safety just in case the data source does not implement this callback. */ if (pFormat != NULL) { *pFormat = ma_format_unknown; } - if (pChannels != NULL) { *pChannels = 0; } - if (pSampleRate != NULL) { *pSampleRate = 0; } + if (pChannelMap != NULL) { + MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); + } - if (pCallbacks == NULL) { + if (pDataSourceBase == NULL) { return MA_INVALID_ARGS; } - if (pCallbacks->onGetDataFormat == NULL) { + if (pDataSourceBase->vtable->onGetDataFormat == NULL) { return MA_NOT_IMPLEMENTED; } - result = pCallbacks->onGetDataFormat(pDataSource, &format, &channels, &sampleRate); + result = pDataSourceBase->vtable->onGetDataFormat(pDataSource, &format, &channels, &sampleRate, pChannelMap, channelMapCap); if (result != MA_SUCCESS) { return result; } @@ -44197,12 +54970,13 @@ MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_ *pSampleRate = sampleRate; } + /* Channel map was passed in directly to the callback. This is safe due to the channelMapCap parameter. */ + return MA_SUCCESS; } MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor) { -#if defined(MA_EXPERIMENTAL__DATA_LOOPING_AND_CHAINING) ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; ma_result result; ma_uint64 cursor; @@ -44217,11 +54991,11 @@ MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSo return MA_SUCCESS; } - if (pDataSourceBase->cb.onGetCursor == NULL) { + if (pDataSourceBase->vtable->onGetCursor == NULL) { return MA_NOT_IMPLEMENTED; } - result = pDataSourceBase->cb.onGetCursor(pDataSourceBase, &cursor); + result = pDataSourceBase->vtable->onGetCursor(pDataSourceBase, &cursor); if (result != MA_SUCCESS) { return result; } @@ -44232,32 +55006,12 @@ MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSo } else { *pCursor = cursor - pDataSourceBase->rangeBegInFrames; } - + return MA_SUCCESS; -#else - ma_data_source_callbacks* pCallbacks = (ma_data_source_callbacks*)pDataSource; - - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - if (pCallbacks == NULL) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onGetCursor == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pCallbacks->onGetCursor(pDataSource, pCursor); -#endif } MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength) { -#if defined(MA_EXPERIMENTAL__DATA_LOOPING_AND_CHAINING) ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; if (pLength == NULL) { @@ -44284,13 +55038,45 @@ MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSo Getting here means a range is not defined so we'll need to get the data source itself to tell us the length. */ - if (pDataSourceBase->cb.onGetLength == NULL) { + if (pDataSourceBase->vtable->onGetLength == NULL) { return MA_NOT_IMPLEMENTED; } - return pDataSourceBase->cb.onGetLength(pDataSource, pLength); -#else - ma_data_source_callbacks* pCallbacks = (ma_data_source_callbacks*)pDataSource; + return pDataSourceBase->vtable->onGetLength(pDataSource, pLength); +} + +MA_API ma_result ma_data_source_get_cursor_in_seconds(ma_data_source* pDataSource, float* pCursor) +{ + ma_result result; + ma_uint64 cursorInPCMFrames; + ma_uint32 sampleRate; + + if (pCursor == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = 0; + + result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &cursorInPCMFrames); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0); + if (result != MA_SUCCESS) { + return result; + } + + *pCursor = cursorInPCMFrames / (float)sampleRate; + + return MA_SUCCESS; +} + +MA_API ma_result ma_data_source_get_length_in_seconds(ma_data_source* pDataSource, float* pLength) +{ + ma_result result; + ma_uint64 lengthInPCMFrames; + ma_uint32 sampleRate; if (pLength == NULL) { return MA_INVALID_ARGS; @@ -44298,20 +55084,50 @@ MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSo *pLength = 0; - if (pCallbacks == NULL) { + result = ma_data_source_get_length_in_pcm_frames(pDataSource, &lengthInPCMFrames); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0); + if (result != MA_SUCCESS) { + return result; + } + + *pLength = lengthInPCMFrames / (float)sampleRate; + + return MA_SUCCESS; +} + +MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool32 isLooping) +{ + ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; + + if (pDataSource == NULL) { return MA_INVALID_ARGS; } - if (pCallbacks->onGetLength == NULL) { - return MA_NOT_IMPLEMENTED; + c89atomic_exchange_32(&pDataSourceBase->isLooping, isLooping); + + /* If there's no callback for this just treat it as a successful no-op. */ + if (pDataSourceBase->vtable->onSetLooping == NULL) { + return MA_SUCCESS; } - return pCallbacks->onGetLength(pDataSource, pLength); -#endif + return pDataSourceBase->vtable->onSetLooping(pDataSource, isLooping); } +MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource) +{ + const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; + + if (pDataSource == NULL) { + return MA_FALSE; + } + + return c89atomic_load_32(&pDataSourceBase->isLooping); +} -#if defined(MA_EXPERIMENTAL__DATA_LOOPING_AND_CHAINING) MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames) { ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; @@ -44356,12 +55172,12 @@ MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSou } else { pDataSourceBase->loopEndInFrames = 0; } - + if (pDataSourceBase->loopEndInFrames > pDataSourceBase->rangeEndInFrames && pDataSourceBase->loopEndInFrames) { pDataSourceBase->loopEndInFrames = pDataSourceBase->rangeEndInFrames; } } - + /* If the new range is past the current cursor position we need to seek to it. */ result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &cursor); @@ -44379,9 +55195,9 @@ MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSou return MA_SUCCESS; } -MA_API void ma_data_source_get_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames) +MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames) { - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; + const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; if (pDataSource == NULL) { return; @@ -44423,9 +55239,9 @@ MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDa return MA_SUCCESS; } -MA_API void ma_data_source_get_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames) +MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames) { - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; + const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; if (pDataSource == NULL) { return; @@ -44453,9 +55269,9 @@ MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data return MA_SUCCESS; } -MA_API ma_data_source* ma_data_source_get_current(ma_data_source* pDataSource) +MA_API ma_data_source* ma_data_source_get_current(const ma_data_source* pDataSource) { - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; + const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; if (pDataSource == NULL) { return NULL; @@ -44477,9 +55293,9 @@ MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_so return MA_SUCCESS; } -MA_API ma_data_source* ma_data_source_get_next(ma_data_source* pDataSource) +MA_API ma_data_source* ma_data_source_get_next(const ma_data_source* pDataSource) { - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; + const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; if (pDataSource == NULL) { return NULL; @@ -44501,9 +55317,9 @@ MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, m return MA_SUCCESS; } -MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(ma_data_source* pDataSource) +MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(const ma_data_source* pDataSource) { - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; + const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; if (pDataSource == NULL) { return NULL; @@ -44511,7 +55327,6 @@ MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(ma_data_sou return pDataSourceBase->onGetNext; } -#endif static ma_result ma_audio_buffer_ref__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) @@ -44535,23 +55350,14 @@ static ma_result ma_audio_buffer_ref__data_source_on_seek(ma_data_source* pDataS return ma_audio_buffer_ref_seek_to_pcm_frame((ma_audio_buffer_ref*)pDataSource, frameIndex); } -static ma_result ma_audio_buffer_ref__data_source_on_map(ma_data_source* pDataSource, void** ppFramesOut, ma_uint64* pFrameCount) -{ - return ma_audio_buffer_ref_map((ma_audio_buffer_ref*)pDataSource, ppFramesOut, pFrameCount); -} - -static ma_result ma_audio_buffer_ref__data_source_on_unmap(ma_data_source* pDataSource, ma_uint64 frameCount) -{ - return ma_audio_buffer_ref_unmap((ma_audio_buffer_ref*)pDataSource, frameCount); -} - -static ma_result ma_audio_buffer_ref__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate) +static ma_result ma_audio_buffer_ref__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) { ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource; *pFormat = pAudioBufferRef->format; *pChannels = pAudioBufferRef->channels; - *pSampleRate = 0; /* There is no notion of a sample rate with audio buffers. */ + *pSampleRate = pAudioBufferRef->sampleRate; + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pAudioBufferRef->channels); return MA_SUCCESS; } @@ -44578,11 +55384,11 @@ static ma_data_source_vtable g_ma_audio_buffer_ref_data_source_vtable = { ma_audio_buffer_ref__data_source_on_read, ma_audio_buffer_ref__data_source_on_seek, - ma_audio_buffer_ref__data_source_on_map, - ma_audio_buffer_ref__data_source_on_unmap, ma_audio_buffer_ref__data_source_on_get_data_format, ma_audio_buffer_ref__data_source_on_get_cursor, - ma_audio_buffer_ref__data_source_on_get_length + ma_audio_buffer_ref__data_source_on_get_length, + NULL, /* onSetLooping */ + 0 }; MA_API ma_result ma_audio_buffer_ref_init(ma_format format, ma_uint32 channels, const void* pData, ma_uint64 sizeInFrames, ma_audio_buffer_ref* pAudioBufferRef) @@ -44606,6 +55412,7 @@ MA_API ma_result ma_audio_buffer_ref_init(ma_format format, ma_uint32 channels, pAudioBufferRef->format = format; pAudioBufferRef->channels = channels; + pAudioBufferRef->sampleRate = 0; /* TODO: Version 0.12. Set this to sampleRate. */ pAudioBufferRef->cursor = 0; pAudioBufferRef->sizeInFrames = sizeInFrames; pAudioBufferRef->pData = pData; @@ -44658,7 +55465,7 @@ MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudio } if (pFramesOut != NULL) { - ma_copy_pcm_frames(pFramesOut, ma_offset_ptr(pAudioBufferRef->pData, pAudioBufferRef->cursor * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels)), framesToRead, pAudioBufferRef->format, pAudioBufferRef->channels); + ma_copy_pcm_frames(ma_offset_ptr(pFramesOut, totalFramesRead * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels)), ma_offset_ptr(pAudioBufferRef->pData, pAudioBufferRef->cursor * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels)), framesToRead, pAudioBufferRef->format, pAudioBufferRef->channels); } totalFramesRead += framesToRead; @@ -44816,10 +55623,11 @@ MA_API ma_audio_buffer_config ma_audio_buffer_config_init(ma_format format, ma_u ma_audio_buffer_config config; MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; + config.format = format; + config.channels = channels; + config.sampleRate = 0; /* TODO: Version 0.12. Set this to sampleRate. */ config.sizeInFrames = sizeInFrames; - config.pData = pData; + config.pData = pData; ma_allocation_callbacks_init_copy(&config.allocationCallbacks, pAllocationCallbacks); return config; @@ -44848,6 +55656,9 @@ static ma_result ma_audio_buffer_init_ex(const ma_audio_buffer_config* pConfig, return result; } + /* TODO: Version 0.12. Set this in ma_audio_buffer_ref_init() instead of here. */ + pAudioBuffer->ref.sampleRate = pConfig->sampleRate; + ma_allocation_callbacks_init_copy(&pAudioBuffer->allocationCallbacks, &pConfig->allocationCallbacks); if (doCopy) { @@ -44859,7 +55670,7 @@ static ma_result ma_audio_buffer_init_ex(const ma_audio_buffer_config* pConfig, return MA_OUT_OF_MEMORY; /* Too big. */ } - pData = ma__malloc_from_callbacks((size_t)allocationSizeInBytes, &pAudioBuffer->allocationCallbacks); /* Safe cast to size_t. */ + pData = ma_malloc((size_t)allocationSizeInBytes, &pAudioBuffer->allocationCallbacks); /* Safe cast to size_t. */ if (pData == NULL) { return MA_OUT_OF_MEMORY; } @@ -44887,11 +55698,11 @@ static void ma_audio_buffer_uninit_ex(ma_audio_buffer* pAudioBuffer, ma_bool32 d } if (pAudioBuffer->ownsData && pAudioBuffer->ref.pData != &pAudioBuffer->_pExtraData[0]) { - ma__free_from_callbacks((void*)pAudioBuffer->ref.pData, &pAudioBuffer->allocationCallbacks); /* Naugty const cast, but OK in this case since we've guarded it with the ownsData check. */ + ma_free((void*)pAudioBuffer->ref.pData, &pAudioBuffer->allocationCallbacks); /* Naugty const cast, but OK in this case since we've guarded it with the ownsData check. */ } if (doFree) { - ma__free_from_callbacks(pAudioBuffer, &pAudioBuffer->allocationCallbacks); + ma_free(pAudioBuffer, &pAudioBuffer->allocationCallbacks); } ma_audio_buffer_ref_uninit(&pAudioBuffer->ref); @@ -44932,7 +55743,7 @@ MA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config* pC return MA_OUT_OF_MEMORY; /* Too big. */ } - pAudioBuffer = (ma_audio_buffer*)ma__malloc_from_callbacks((size_t)allocationSizeInBytes, &innerConfig.allocationCallbacks); /* Safe cast to size_t. */ + pAudioBuffer = (ma_audio_buffer*)ma_malloc((size_t)allocationSizeInBytes, &innerConfig.allocationCallbacks); /* Safe cast to size_t. */ if (pAudioBuffer == NULL) { return MA_OUT_OF_MEMORY; } @@ -44947,7 +55758,7 @@ MA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config* pC result = ma_audio_buffer_init_ex(&innerConfig, MA_FALSE, pAudioBuffer); if (result != MA_SUCCESS) { - ma__free_from_callbacks(pAudioBuffer, &innerConfig.allocationCallbacks); + ma_free(pAudioBuffer, &innerConfig.allocationCallbacks); return result; } @@ -45054,6 +55865,392 @@ MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAu + + +MA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data* pData) +{ + if (pData == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pData); + + pData->format = format; + pData->channels = channels; + pData->pTail = &pData->head; + + return MA_SUCCESS; +} + +MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_paged_audio_buffer_page* pPage; + + if (pData == NULL) { + return; + } + + /* All pages need to be freed. */ + pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pData->head.pNext); + while (pPage != NULL) { + ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pPage->pNext); + + ma_free(pPage, pAllocationCallbacks); + pPage = pNext; + } +} + +MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data* pData) +{ + if (pData == NULL) { + return NULL; + } + + return &pData->head; +} + +MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data* pData) +{ + if (pData == NULL) { + return NULL; + } + + return pData->pTail; +} + +MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data* pData, ma_uint64* pLength) +{ + ma_paged_audio_buffer_page* pPage; + + if (pLength == NULL) { + return MA_INVALID_ARGS; + } + + *pLength = 0; + + if (pData == NULL) { + return MA_INVALID_ARGS; + } + + /* Calculate the length from the linked list. */ + for (pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pData->head.pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pPage->pNext)) { + *pLength += pPage->sizeInFrames; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_paged_audio_buffer_data_allocate_page(ma_paged_audio_buffer_data* pData, ma_uint64 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage) +{ + ma_paged_audio_buffer_page* pPage; + ma_uint64 allocationSize; + + if (ppPage == NULL) { + return MA_INVALID_ARGS; + } + + *ppPage = NULL; + + if (pData == NULL) { + return MA_INVALID_ARGS; + } + + allocationSize = sizeof(*pPage) + (pageSizeInFrames * ma_get_bytes_per_frame(pData->format, pData->channels)); + if (allocationSize > MA_SIZE_MAX) { + return MA_OUT_OF_MEMORY; /* Too big. */ + } + + pPage = (ma_paged_audio_buffer_page*)ma_malloc((size_t)allocationSize, pAllocationCallbacks); /* Safe cast to size_t. */ + if (pPage == NULL) { + return MA_OUT_OF_MEMORY; + } + + pPage->pNext = NULL; + pPage->sizeInFrames = pageSizeInFrames; + + if (pInitialData != NULL) { + ma_copy_pcm_frames(pPage->pAudioData, pInitialData, pageSizeInFrames, pData->format, pData->channels); + } + + *ppPage = pPage; + + return MA_SUCCESS; +} + +MA_API ma_result ma_paged_audio_buffer_data_free_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pData == NULL || pPage == NULL) { + return MA_INVALID_ARGS; + } + + /* It's assumed the page is not attached to the list. */ + ma_free(pPage, pAllocationCallbacks); + + return MA_SUCCESS; +} + +MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage) +{ + if (pData == NULL || pPage == NULL) { + return MA_INVALID_ARGS; + } + + /* This function assumes the page has been filled with audio data by this point. As soon as we append, the page will be available for reading. */ + + /* First thing to do is update the tail. */ + for (;;) { + ma_paged_audio_buffer_page* pOldTail = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pData->pTail); + ma_paged_audio_buffer_page* pNewTail = pPage; + + if (c89atomic_compare_exchange_weak_ptr((volatile void**)&pData->pTail, (void**)&pOldTail, pNewTail)) { + /* Here is where we append the page to the list. After this, the page is attached to the list and ready to be read from. */ + c89atomic_exchange_ptr(&pOldTail->pNext, pPage); + break; /* Done. */ + } + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_paged_audio_buffer_data_allocate_and_append_page(ma_paged_audio_buffer_data* pData, ma_uint32 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_result result; + ma_paged_audio_buffer_page* pPage; + + result = ma_paged_audio_buffer_data_allocate_page(pData, pageSizeInFrames, pInitialData, pAllocationCallbacks, &pPage); + if (result != MA_SUCCESS) { + return result; + } + + return ma_paged_audio_buffer_data_append_page(pData, pPage); /* <-- Should never fail. */ +} + + +MA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data* pData) +{ + ma_paged_audio_buffer_config config; + + MA_ZERO_OBJECT(&config); + config.pData = pData; + + return config; +} + + +static ma_result ma_paged_audio_buffer__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + return ma_paged_audio_buffer_read_pcm_frames((ma_paged_audio_buffer*)pDataSource, pFramesOut, frameCount, pFramesRead); +} + +static ma_result ma_paged_audio_buffer__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) +{ + return ma_paged_audio_buffer_seek_to_pcm_frame((ma_paged_audio_buffer*)pDataSource, frameIndex); +} + +static ma_result ma_paged_audio_buffer__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + ma_paged_audio_buffer* pPagedAudioBuffer = (ma_paged_audio_buffer*)pDataSource; + + *pFormat = pPagedAudioBuffer->pData->format; + *pChannels = pPagedAudioBuffer->pData->channels; + *pSampleRate = 0; /* There is no notion of a sample rate with audio buffers. */ + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pPagedAudioBuffer->pData->channels); + + return MA_SUCCESS; +} + +static ma_result ma_paged_audio_buffer__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) +{ + return ma_paged_audio_buffer_get_cursor_in_pcm_frames((ma_paged_audio_buffer*)pDataSource, pCursor); +} + +static ma_result ma_paged_audio_buffer__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength) +{ + return ma_paged_audio_buffer_get_length_in_pcm_frames((ma_paged_audio_buffer*)pDataSource, pLength); +} + +static ma_data_source_vtable g_ma_paged_audio_buffer_data_source_vtable = +{ + ma_paged_audio_buffer__data_source_on_read, + ma_paged_audio_buffer__data_source_on_seek, + ma_paged_audio_buffer__data_source_on_get_data_format, + ma_paged_audio_buffer__data_source_on_get_cursor, + ma_paged_audio_buffer__data_source_on_get_length, + NULL, /* onSetLooping */ + 0 +}; + +MA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config* pConfig, ma_paged_audio_buffer* pPagedAudioBuffer) +{ + ma_result result; + ma_data_source_config dataSourceConfig; + + if (pPagedAudioBuffer == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pPagedAudioBuffer); + + /* A config is required for the format and channel count. */ + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->pData == NULL) { + return MA_INVALID_ARGS; /* No underlying data specified. */ + } + + dataSourceConfig = ma_data_source_config_init(); + dataSourceConfig.vtable = &g_ma_paged_audio_buffer_data_source_vtable; + + result = ma_data_source_init(&dataSourceConfig, &pPagedAudioBuffer->ds); + if (result != MA_SUCCESS) { + return result; + } + + pPagedAudioBuffer->pData = pConfig->pData; + pPagedAudioBuffer->pCurrent = ma_paged_audio_buffer_data_get_head(pConfig->pData); + pPagedAudioBuffer->relativeCursor = 0; + pPagedAudioBuffer->absoluteCursor = 0; + + return MA_SUCCESS; +} + +MA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer* pPagedAudioBuffer) +{ + if (pPagedAudioBuffer == NULL) { + return; + } + + /* Nothing to do. The data needs to be deleted separately. */ +} + +MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + ma_result result = MA_SUCCESS; + ma_uint64 totalFramesRead = 0; + ma_format format; + ma_uint32 channels; + + if (pPagedAudioBuffer == NULL) { + return MA_INVALID_ARGS; + } + + format = pPagedAudioBuffer->pData->format; + channels = pPagedAudioBuffer->pData->channels; + + while (totalFramesRead < frameCount) { + /* Read from the current page. The buffer should never be in a state where this is NULL. */ + ma_uint64 framesRemainingInCurrentPage; + ma_uint64 framesRemainingToRead = frameCount - totalFramesRead; + ma_uint64 framesToReadThisIteration; + + MA_ASSERT(pPagedAudioBuffer->pCurrent != NULL); + + framesRemainingInCurrentPage = pPagedAudioBuffer->pCurrent->sizeInFrames - pPagedAudioBuffer->relativeCursor; + + framesToReadThisIteration = ma_min(framesRemainingInCurrentPage, framesRemainingToRead); + ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), ma_offset_pcm_frames_ptr(pPagedAudioBuffer->pCurrent->pAudioData, pPagedAudioBuffer->relativeCursor, format, channels), framesToReadThisIteration, format, channels); + totalFramesRead += framesToReadThisIteration; + + pPagedAudioBuffer->absoluteCursor += framesToReadThisIteration; + pPagedAudioBuffer->relativeCursor += framesToReadThisIteration; + + /* Move to the next page if necessary. If there's no more pages, we need to return MA_AT_END. */ + MA_ASSERT(pPagedAudioBuffer->relativeCursor <= pPagedAudioBuffer->pCurrent->sizeInFrames); + + if (pPagedAudioBuffer->relativeCursor == pPagedAudioBuffer->pCurrent->sizeInFrames) { + /* We reached the end of the page. Need to move to the next. If there's no more pages, we're done. */ + ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pPagedAudioBuffer->pCurrent->pNext); + if (pNext == NULL) { + result = MA_AT_END; + break; /* We've reached the end. */ + } else { + pPagedAudioBuffer->pCurrent = pNext; + pPagedAudioBuffer->relativeCursor = 0; + } + } + } + + if (pFramesRead != NULL) { + *pFramesRead = totalFramesRead; + } + + return result; +} + +MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64 frameIndex) +{ + if (pPagedAudioBuffer == NULL) { + return MA_INVALID_ARGS; + } + + if (frameIndex == pPagedAudioBuffer->absoluteCursor) { + return MA_SUCCESS; /* Nothing to do. */ + } + + if (frameIndex < pPagedAudioBuffer->absoluteCursor) { + /* Moving backwards. Need to move the cursor back to the start, and then move forward. */ + pPagedAudioBuffer->pCurrent = ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData); + pPagedAudioBuffer->absoluteCursor = 0; + pPagedAudioBuffer->relativeCursor = 0; + + /* Fall through to the forward seeking section below. */ + } + + if (frameIndex > pPagedAudioBuffer->absoluteCursor) { + /* Moving forward. */ + ma_paged_audio_buffer_page* pPage; + ma_uint64 runningCursor = 0; + + for (pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData)->pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pPage->pNext)) { + ma_uint64 pageRangeBeg = runningCursor; + ma_uint64 pageRangeEnd = pageRangeBeg + pPage->sizeInFrames; + + if (frameIndex >= pageRangeBeg) { + if (frameIndex < pageRangeEnd || (frameIndex == pageRangeEnd && pPage == (ma_paged_audio_buffer_page*)c89atomic_load_ptr(ma_paged_audio_buffer_data_get_tail(pPagedAudioBuffer->pData)))) { /* A small edge case - allow seeking to the very end of the buffer. */ + /* We found the page. */ + pPagedAudioBuffer->pCurrent = pPage; + pPagedAudioBuffer->absoluteCursor = frameIndex; + pPagedAudioBuffer->relativeCursor = frameIndex - pageRangeBeg; + return MA_SUCCESS; + } + } + + runningCursor = pageRangeEnd; + } + + /* Getting here means we tried seeking too far forward. Don't change any state. */ + return MA_BAD_SEEK; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pCursor) +{ + if (pCursor == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = 0; /* Safety. */ + + if (pPagedAudioBuffer == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = pPagedAudioBuffer->absoluteCursor; + + return MA_SUCCESS; +} + +MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pLength) +{ + return ma_paged_audio_buffer_data_get_length_in_pcm_frames(pPagedAudioBuffer->pData, pLength); +} + + + /************************************************************************************************************************************************************** VFS @@ -45119,6 +56316,8 @@ MA_API ma_result ma_vfs_close(ma_vfs* pVFS, ma_vfs_file file) MA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) { ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; + ma_result result; + size_t bytesRead; if (pBytesRead != NULL) { *pBytesRead = 0; @@ -45132,7 +56331,17 @@ MA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t return MA_NOT_IMPLEMENTED; } - return pCallbacks->onRead(pVFS, file, pDst, sizeInBytes, pBytesRead); + result = pCallbacks->onRead(pVFS, file, pDst, sizeInBytes, &bytesRead); + + if (pBytesRead != NULL) { + *pBytesRead = bytesRead; + } + + if (result == MA_SUCCESS && bytesRead == 0 && sizeInBytes > 0) { + result = MA_AT_END; + } + + return result; } MA_API ma_result ma_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) @@ -45212,7 +56421,7 @@ MA_API ma_result ma_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo } -static ma_result ma_vfs_open_and_read_file_ex(ma_vfs* pVFS, const char* pFilePath, const wchar_t* pFilePathW, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks, ma_uint32 allocationType) +static ma_result ma_vfs_open_and_read_file_ex(ma_vfs* pVFS, const char* pFilePath, const wchar_t* pFilePathW, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) { ma_result result; ma_vfs_file file; @@ -45220,8 +56429,6 @@ static ma_result ma_vfs_open_and_read_file_ex(ma_vfs* pVFS, const char* pFilePat void* pData; size_t bytesRead; - (void)allocationType; - if (ppData != NULL) { *ppData = NULL; } @@ -45253,7 +56460,7 @@ static ma_result ma_vfs_open_and_read_file_ex(ma_vfs* pVFS, const char* pFilePat return MA_TOO_BIG; } - pData = ma__malloc_from_callbacks((size_t)info.sizeInBytes, pAllocationCallbacks); /* Safe cast. */ + pData = ma_malloc((size_t)info.sizeInBytes, pAllocationCallbacks); /* Safe cast. */ if (pData == NULL) { ma_vfs_close(pVFS, file); return result; @@ -45263,7 +56470,7 @@ static ma_result ma_vfs_open_and_read_file_ex(ma_vfs* pVFS, const char* pFilePat ma_vfs_close(pVFS, file); if (result != MA_SUCCESS) { - ma__free_from_callbacks(pData, pAllocationCallbacks); + ma_free(pData, pAllocationCallbacks); return result; } @@ -45279,12 +56486,12 @@ static ma_result ma_vfs_open_and_read_file_ex(ma_vfs* pVFS, const char* pFilePat MA_API ma_result ma_vfs_open_and_read_file(ma_vfs* pVFS, const char* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) { - return ma_vfs_open_and_read_file_ex(pVFS, pFilePath, NULL, ppData, pSize, pAllocationCallbacks, 0 /*MA_ALLOCATION_TYPE_GENERAL*/); + return ma_vfs_open_and_read_file_ex(pVFS, pFilePath, NULL, ppData, pSize, pAllocationCallbacks); } MA_API ma_result ma_vfs_open_and_read_file_w(ma_vfs* pVFS, const wchar_t* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) { - return ma_vfs_open_and_read_file_ex(pVFS, NULL, pFilePath, ppData, pSize, pAllocationCallbacks, 0 /*MA_ALLOCATION_TYPE_GENERAL*/); + return ma_vfs_open_and_read_file_ex(pVFS, NULL, pFilePath, ppData, pSize, pAllocationCallbacks); } @@ -45992,7 +57199,7 @@ extern "C" { #define DRWAV_XSTRINGIFY(x) DRWAV_STRINGIFY(x) #define DRWAV_VERSION_MAJOR 0 #define DRWAV_VERSION_MINOR 13 -#define DRWAV_VERSION_REVISION 1 +#define DRWAV_VERSION_REVISION 6 #define DRWAV_VERSION_STRING DRWAV_XSTRINGIFY(DRWAV_VERSION_MAJOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_MINOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_REVISION) #include typedef signed char drwav_int8; @@ -46527,7 +57734,7 @@ extern "C" { #define DRFLAC_XSTRINGIFY(x) DRFLAC_STRINGIFY(x) #define DRFLAC_VERSION_MAJOR 0 #define DRFLAC_VERSION_MINOR 12 -#define DRFLAC_VERSION_REVISION 31 +#define DRFLAC_VERSION_REVISION 38 #define DRFLAC_VERSION_STRING DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MAJOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MINOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_REVISION) #include typedef signed char drflac_int8; @@ -46536,7 +57743,7 @@ typedef signed short drflac_int16; typedef unsigned short drflac_uint16; typedef signed int drflac_int32; typedef unsigned int drflac_uint32; -#if defined(_MSC_VER) +#if defined(_MSC_VER) && !defined(__clang__) typedef signed __int64 drflac_int64; typedef unsigned __int64 drflac_uint64; #else @@ -46553,7 +57760,7 @@ typedef unsigned int drflac_uint32; #pragma GCC diagnostic pop #endif #endif -#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) +#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined(_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) typedef drflac_uint64 drflac_uintptr; #else typedef drflac_uint32 drflac_uintptr; @@ -46888,7 +58095,7 @@ extern "C" { #define DRMP3_XSTRINGIFY(x) DRMP3_STRINGIFY(x) #define DRMP3_VERSION_MAJOR 0 #define DRMP3_VERSION_MINOR 6 -#define DRMP3_VERSION_REVISION 31 +#define DRMP3_VERSION_REVISION 33 #define DRMP3_VERSION_STRING DRMP3_XSTRINGIFY(DRMP3_VERSION_MAJOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_MINOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_REVISION) #include typedef signed char drmp3_int8; @@ -46897,7 +58104,7 @@ typedef signed short drmp3_int16; typedef unsigned short drmp3_uint16; typedef signed int drmp3_int32; typedef unsigned int drmp3_uint32; -#if defined(_MSC_VER) +#if defined(_MSC_VER) && !defined(__clang__) typedef signed __int64 drmp3_int64; typedef unsigned __int64 drmp3_uint64; #else @@ -47012,9 +58219,14 @@ typedef drmp3_int32 drmp3_result; #define DRMP3_INLINE __forceinline #elif defined(__GNUC__) #if defined(__STRICT_ANSI__) - #define DRMP3_INLINE __inline__ __attribute__((always_inline)) + #define DRMP3_GNUC_INLINE_HINT __inline__ #else - #define DRMP3_INLINE inline __attribute__((always_inline)) + #define DRMP3_GNUC_INLINE_HINT inline + #endif + #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) + #define DRMP3_INLINE DRMP3_GNUC_INLINE_HINT __attribute__((always_inline)) + #else + #define DRMP3_INLINE DRMP3_GNUC_INLINE_HINT #endif #elif defined(__WATCOMC__) #define DRMP3_INLINE __inline @@ -47135,43 +58347,22 @@ Decoding static ma_result ma_decoder_read_bytes(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) { - size_t bytesRead; + MA_ASSERT(pDecoder != NULL); - MA_ASSERT(pDecoder != NULL); - MA_ASSERT(pBufferOut != NULL); - MA_ASSERT(bytesToRead > 0); /* It's an error to call this with a byte count of zero. */ - - bytesRead = pDecoder->onRead(pDecoder, pBufferOut, bytesToRead); - - if (pBytesRead != NULL) { - *pBytesRead = bytesRead; - } - - if (bytesRead == 0) { - return MA_AT_END; - } - - return MA_SUCCESS; + return pDecoder->onRead(pDecoder, pBufferOut, bytesToRead, pBytesRead); } static ma_result ma_decoder_seek_bytes(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin) { - ma_bool32 wasSuccessful; - MA_ASSERT(pDecoder != NULL); - wasSuccessful = pDecoder->onSeek(pDecoder, byteOffset, origin); - if (wasSuccessful) { - return MA_SUCCESS; - } else { - return MA_ERROR; - } + return pDecoder->onSeek(pDecoder, byteOffset, origin); } static ma_result ma_decoder_tell_bytes(ma_decoder* pDecoder, ma_int64* pCursor) { MA_ASSERT(pDecoder != NULL); - + if (pDecoder->onTell == NULL) { return MA_NOT_IMPLEMENTED; } @@ -47180,12 +58371,13 @@ static ma_result ma_decoder_tell_bytes(ma_decoder* pDecoder, ma_int64* pCursor) } -MA_API ma_decoding_backend_config ma_decoding_backend_config_init(ma_format preferredFormat) +MA_API ma_decoding_backend_config ma_decoding_backend_config_init(ma_format preferredFormat, ma_uint32 seekPointCount) { ma_decoding_backend_config config; MA_ZERO_OBJECT(&config); config.preferredFormat = preferredFormat; + config.seekPointCount = seekPointCount; return config; } @@ -47195,12 +58387,10 @@ MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint3 { ma_decoder_config config; MA_ZERO_OBJECT(&config); - config.format = outputFormat; - config.channels = ma_min(outputChannels, ma_countof(config.channelMap)); - config.sampleRate = outputSampleRate; - config.resampling.algorithm = ma_resample_algorithm_linear; - config.resampling.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER); - config.resampling.speex.quality = 3; + config.format = outputFormat; + config.channels = outputChannels; + config.sampleRate = outputSampleRate; + config.resampling = ma_resampler_config_init(ma_format_unknown, 0, 0, 0, ma_resample_algorithm_linear); /* Format/channels/rate doesn't matter here. */ config.encodingFormat = ma_encoding_format_unknown; /* Note that we are intentionally leaving the channel map empty here which will cause the default channel map to be used. */ @@ -47237,19 +58427,11 @@ static ma_result ma_decoder__init_data_converter(ma_decoder* pDecoder, const ma_ MA_ASSERT(pDecoder != NULL); MA_ASSERT(pConfig != NULL); - result = ma_data_source_get_data_format(pDecoder->pBackend, &internalFormat, &internalChannels, &internalSampleRate); + result = ma_data_source_get_data_format(pDecoder->pBackend, &internalFormat, &internalChannels, &internalSampleRate, internalChannelMap, ma_countof(internalChannelMap)); if (result != MA_SUCCESS) { return result; /* Failed to retrieve the internal data format. */ } - /* Channel map needs to be retrieved separately. */ - if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onGetChannelMap != NULL) { - pDecoder->pBackendVTable->onGetChannelMap(pDecoder->pBackendUserData, pDecoder->pBackend, internalChannelMap, ma_countof(internalChannelMap)); - } else { - ma_get_standard_channel_map(ma_standard_channel_map_default, ma_min(internalChannels, ma_countof(internalChannelMap)), internalChannelMap); - } - - /* Make sure we're not asking for too many channels. */ if (pConfig->channels > MA_MAX_CHANNELS) { @@ -47281,28 +58463,57 @@ static ma_result ma_decoder__init_data_converter(ma_decoder* pDecoder, const ma_ pDecoder->outputSampleRate = pConfig->sampleRate; } - if (ma_channel_map_blank(pDecoder->outputChannels, pConfig->channelMap)) { - ma_get_standard_channel_map(ma_standard_channel_map_default, pDecoder->outputChannels, pDecoder->outputChannelMap); - } else { - MA_COPY_MEMORY(pDecoder->outputChannelMap, pConfig->channelMap, sizeof(pConfig->channelMap)); - } - - converterConfig = ma_data_converter_config_init( internalFormat, pDecoder->outputFormat, internalChannels, pDecoder->outputChannels, internalSampleRate, pDecoder->outputSampleRate ); - ma_channel_map_copy(converterConfig.channelMapIn, internalChannelMap, internalChannels); - ma_channel_map_copy(converterConfig.channelMapOut, pDecoder->outputChannelMap, pDecoder->outputChannels); - converterConfig.channelMixMode = pConfig->channelMixMode; - converterConfig.ditherMode = pConfig->ditherMode; - converterConfig.resampling.allowDynamicSampleRate = MA_FALSE; /* Never allow dynamic sample rate conversion. Setting this to true will disable passthrough optimizations. */ - converterConfig.resampling.algorithm = pConfig->resampling.algorithm; - converterConfig.resampling.linear.lpfOrder = pConfig->resampling.linear.lpfOrder; - converterConfig.resampling.speex.quality = pConfig->resampling.speex.quality; + converterConfig.pChannelMapIn = internalChannelMap; + converterConfig.pChannelMapOut = pConfig->pChannelMap; + converterConfig.channelMixMode = pConfig->channelMixMode; + converterConfig.ditherMode = pConfig->ditherMode; + converterConfig.allowDynamicSampleRate = MA_FALSE; /* Never allow dynamic sample rate conversion. Setting this to true will disable passthrough optimizations. */ + converterConfig.resampling = pConfig->resampling; - return ma_data_converter_init(&converterConfig, &pDecoder->converter); + result = ma_data_converter_init(&converterConfig, &pDecoder->allocationCallbacks, &pDecoder->converter); + if (result != MA_SUCCESS) { + return result; + } + + /* + Now that we have the decoder we need to determine whether or not we need a heap-allocated cache. We'll + need this if the data converter does not support calculation of the required input frame count. To + determine support for this we'll just run a test. + */ + { + ma_uint64 unused; + + result = ma_data_converter_get_required_input_frame_count(&pDecoder->converter, 1, &unused); + if (result != MA_SUCCESS) { + /* + We were unable to calculate the required input frame count which means we'll need to use + a heap-allocated cache. + */ + ma_uint64 inputCacheCapSizeInBytes; + + pDecoder->inputCacheCap = MA_DATA_CONVERTER_STACK_BUFFER_SIZE / ma_get_bytes_per_frame(internalFormat, internalChannels); + + /* Not strictly necessary, but keeping here for safety in case we change the default value of pDecoder->inputCacheCap. */ + inputCacheCapSizeInBytes = pDecoder->inputCacheCap * ma_get_bytes_per_frame(internalFormat, internalChannels); + if (inputCacheCapSizeInBytes > MA_SIZE_MAX) { + ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks); + return MA_OUT_OF_MEMORY; + } + + pDecoder->pInputCache = ma_malloc((size_t)inputCacheCapSizeInBytes, &pDecoder->allocationCallbacks); /* Safe cast to size_t. */ + if (pDecoder->pInputCache == NULL) { + ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks); + return MA_OUT_OF_MEMORY; + } + } + } + + return MA_SUCCESS; } @@ -47346,7 +58557,7 @@ static ma_result ma_decoder_init_from_vtable(const ma_decoding_backend_vtable* p return MA_NOT_IMPLEMENTED; } - backendConfig = ma_decoding_backend_config_init(pConfig->format); + backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); result = pVTable->onInit(pVTableUserData, ma_decoder_internal_on_read__custom, ma_decoder_internal_on_seek__custom, ma_decoder_internal_on_tell__custom, pDecoder, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); if (result != MA_SUCCESS) { @@ -47438,9 +58649,9 @@ static ma_result ma_wav_ds_seek(ma_data_source* pDataSource, ma_uint64 frameInde return ma_wav_seek_to_pcm_frame((ma_wav*)pDataSource, frameIndex); } -static ma_result ma_wav_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate) +static ma_result ma_wav_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) { - return ma_wav_get_data_format((ma_wav*)pDataSource, pFormat, pChannels, pSampleRate, NULL, 0); + return ma_wav_get_data_format((ma_wav*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); } static ma_result ma_wav_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) @@ -47457,11 +58668,11 @@ static ma_data_source_vtable g_ma_wav_ds_vtable = { ma_wav_ds_read, ma_wav_ds_seek, - NULL, /* onMap() */ - NULL, /* onUnmap() */ ma_wav_ds_get_data_format, ma_wav_ds_get_cursor, - ma_wav_ds_get_length + ma_wav_ds_get_length, + NULL, /* onSetLooping */ + 0 }; @@ -47531,7 +58742,7 @@ static ma_result ma_wav_init_internal(const ma_decoding_backend_config* pConfig, } MA_ZERO_OBJECT(pWav); - pWav->format = ma_format_f32; /* f32 by default. */ + pWav->format = ma_format_unknown; /* Use closest match to source file by default. */ if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16 || pConfig->preferredFormat == ma_format_s32)) { pWav->format = pConfig->preferredFormat; @@ -47578,6 +58789,42 @@ MA_API ma_result ma_wav_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_p return MA_INVALID_FILE; } + /* + If an explicit format was not specified, try picking the closest match based on the internal + format. The format needs to be supported by miniaudio. + */ + if (pWav->format == ma_format_unknown) { + switch (pWav->dr.translatedFormatTag) + { + case DR_WAVE_FORMAT_PCM: + { + if (pWav->dr.bitsPerSample == 8) { + pWav->format = ma_format_u8; + } else if (pWav->dr.bitsPerSample == 16) { + pWav->format = ma_format_s16; + } else if (pWav->dr.bitsPerSample == 24) { + pWav->format = ma_format_s24; + } else if (pWav->dr.bitsPerSample == 32) { + pWav->format = ma_format_s32; + } + } break; + + case DR_WAVE_FORMAT_IEEE_FLOAT: + { + if (pWav->dr.bitsPerSample == 32) { + pWav->format = ma_format_f32; + } + } break; + + default: break; + } + + /* Fall back to f32 if we couldn't find anything. */ + if (pWav->format == ma_format_unknown) { + pWav->format = ma_format_f32; + } + } + return MA_SUCCESS; } #else @@ -47707,6 +58954,14 @@ MA_API void ma_wav_uninit(ma_wav* pWav, const ma_allocation_callbacks* pAllocati MA_API ma_result ma_wav_read_pcm_frames(ma_wav* pWav, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + if (pWav == NULL) { return MA_INVALID_ARGS; } @@ -47738,7 +58993,7 @@ MA_API ma_result ma_wav_read_pcm_frames(ma_wav* pWav, void* pFramesOut, ma_uint6 } break; /* Fallback to a raw read. */ - case ma_format_unknown: return MA_INVALID_OPERATION; /* <-- this should never be hit because initialization would just fall back to supported format. */ + case ma_format_unknown: return MA_INVALID_OPERATION; /* <-- this should never be hit because initialization would just fall back to a supported format. */ default: { totalFramesRead = drwav_read_pcm_frames(&pWav->dr, frameCount, pFramesOut); @@ -47754,6 +59009,10 @@ MA_API ma_result ma_wav_read_pcm_frames(ma_wav* pWav, void* pFramesOut, ma_uint6 *pFramesRead = totalFramesRead; } + if (result == MA_SUCCESS && totalFramesRead == 0) { + result = MA_AT_END; + } + return result; } #else @@ -47779,7 +59038,7 @@ MA_API ma_result ma_wav_seek_to_pcm_frame(ma_wav* pWav, ma_uint64 frameIndex) #if !defined(MA_NO_WAV) { drwav_bool32 wavResult; - + wavResult = drwav_seek_to_pcm_frame(&pWav->dr, frameIndex); if (wavResult != DRWAV_TRUE) { return MA_ERROR; @@ -47834,7 +59093,7 @@ MA_API ma_result ma_wav_get_data_format(ma_wav* pWav, ma_format* pFormat, ma_uin } if (pChannelMap != NULL) { - ma_get_standard_channel_map(ma_standard_channel_map_microsoft, (ma_uint32)ma_min(pWav->dr.channels, channelMapCap), pChannelMap); + ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channelMapCap, pWav->dr.channels); } return MA_SUCCESS; @@ -48015,23 +59274,13 @@ static void ma_decoding_backend_uninit__wav(void* pUserData, ma_data_source* pBa ma_free(pWav, pAllocationCallbacks); } -static ma_result ma_decoding_backend_get_channel_map__wav(void* pUserData, ma_data_source* pBackend, ma_channel* pChannelMap, size_t channelMapCap) -{ - ma_wav* pWav = (ma_wav*)pBackend; - - (void)pUserData; - - return ma_wav_get_data_format(pWav, NULL, NULL, NULL, pChannelMap, channelMapCap); -} - static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_wav = { ma_decoding_backend_init__wav, ma_decoding_backend_init_file__wav, ma_decoding_backend_init_file_w__wav, ma_decoding_backend_init_memory__wav, - ma_decoding_backend_uninit__wav, - ma_decoding_backend_get_channel_map__wav + ma_decoding_backend_uninit__wav }; static ma_result ma_decoder_init_wav__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) @@ -48079,9 +59328,9 @@ static ma_result ma_flac_ds_seek(ma_data_source* pDataSource, ma_uint64 frameInd return ma_flac_seek_to_pcm_frame((ma_flac*)pDataSource, frameIndex); } -static ma_result ma_flac_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate) +static ma_result ma_flac_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) { - return ma_flac_get_data_format((ma_flac*)pDataSource, pFormat, pChannels, pSampleRate, NULL, 0); + return ma_flac_get_data_format((ma_flac*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); } static ma_result ma_flac_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) @@ -48098,11 +59347,11 @@ static ma_data_source_vtable g_ma_flac_ds_vtable = { ma_flac_ds_read, ma_flac_ds_seek, - NULL, /* onMap() */ - NULL, /* onUnmap() */ ma_flac_ds_get_data_format, ma_flac_ds_get_cursor, - ma_flac_ds_get_length + ma_flac_ds_get_length, + NULL, /* onSetLooping */ + 0 }; @@ -48344,6 +59593,14 @@ MA_API void ma_flac_uninit(ma_flac* pFlac, const ma_allocation_callbacks* pAlloc MA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + if (pFlac == NULL) { return MA_INVALID_ARGS; } @@ -48392,6 +59649,10 @@ MA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_ui *pFramesRead = totalFramesRead; } + if (result == MA_SUCCESS && totalFramesRead == 0) { + result = MA_AT_END; + } + return result; } #else @@ -48417,7 +59678,7 @@ MA_API ma_result ma_flac_seek_to_pcm_frame(ma_flac* pFlac, ma_uint64 frameIndex) #if !defined(MA_NO_FLAC) { drflac_bool32 flacResult; - + flacResult = drflac_seek_to_pcm_frame(pFlac->dr, frameIndex); if (flacResult != DRFLAC_TRUE) { return MA_ERROR; @@ -48472,7 +59733,7 @@ MA_API ma_result ma_flac_get_data_format(ma_flac* pFlac, ma_format* pFormat, ma_ } if (pChannelMap != NULL) { - ma_get_standard_channel_map(ma_standard_channel_map_microsoft, (ma_uint32)ma_min(pFlac->dr->channels, channelMapCap), pChannelMap); + ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channelMapCap, pFlac->dr->channels); } return MA_SUCCESS; @@ -48647,23 +59908,13 @@ static void ma_decoding_backend_uninit__flac(void* pUserData, ma_data_source* pB ma_free(pFlac, pAllocationCallbacks); } -static ma_result ma_decoding_backend_get_channel_map__flac(void* pUserData, ma_data_source* pBackend, ma_channel* pChannelMap, size_t channelMapCap) -{ - ma_flac* pFlac = (ma_flac*)pBackend; - - (void)pUserData; - - return ma_flac_get_data_format(pFlac, NULL, NULL, NULL, pChannelMap, channelMapCap); -} - static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_flac = { ma_decoding_backend_init__flac, ma_decoding_backend_init_file__flac, ma_decoding_backend_init_file_w__flac, ma_decoding_backend_init_memory__flac, - ma_decoding_backend_uninit__flac, - ma_decoding_backend_get_channel_map__flac + ma_decoding_backend_uninit__flac }; static ma_result ma_decoder_init_flac__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) @@ -48686,6 +59937,8 @@ typedef struct ma_format format; /* Can be f32 or s16. */ #if !defined(MA_NO_MP3) drmp3 dr; + drmp3_uint32 seekPointCount; + drmp3_seek_point* pSeekPoints; /* Only used if seek table generation is used. */ #endif } ma_mp3; @@ -48711,9 +59964,9 @@ static ma_result ma_mp3_ds_seek(ma_data_source* pDataSource, ma_uint64 frameInde return ma_mp3_seek_to_pcm_frame((ma_mp3*)pDataSource, frameIndex); } -static ma_result ma_mp3_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate) +static ma_result ma_mp3_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) { - return ma_mp3_get_data_format((ma_mp3*)pDataSource, pFormat, pChannels, pSampleRate, NULL, 0); + return ma_mp3_get_data_format((ma_mp3*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); } static ma_result ma_mp3_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) @@ -48730,11 +59983,11 @@ static ma_data_source_vtable g_ma_mp3_ds_vtable = { ma_mp3_ds_read, ma_mp3_ds_seek, - NULL, /* onMap() */ - NULL, /* onUnmap() */ ma_mp3_ds_get_data_format, ma_mp3_ds_get_cursor, - ma_mp3_ds_get_length + ma_mp3_ds_get_length, + NULL, /* onSetLooping */ + 0 }; @@ -48823,6 +60076,40 @@ static ma_result ma_mp3_init_internal(const ma_decoding_backend_config* pConfig, return MA_SUCCESS; } +static ma_result ma_mp3_generate_seek_table(ma_mp3* pMP3, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks) +{ + drmp3_bool32 mp3Result; + drmp3_uint32 seekPointCount = 0; + drmp3_seek_point* pSeekPoints = NULL; + + MA_ASSERT(pMP3 != NULL); + MA_ASSERT(pConfig != NULL); + + seekPointCount = pConfig->seekPointCount; + if (seekPointCount > 0) { + pSeekPoints = (drmp3_seek_point*)ma_malloc(sizeof(*pMP3->pSeekPoints) * seekPointCount, pAllocationCallbacks); + if (pSeekPoints == NULL) { + return MA_OUT_OF_MEMORY; + } + } + + mp3Result = drmp3_calculate_seek_points(&pMP3->dr, &seekPointCount, pSeekPoints); + if (mp3Result != MA_TRUE) { + return MA_ERROR; + } + + mp3Result = drmp3_bind_seek_table(&pMP3->dr, seekPointCount, pSeekPoints); + if (mp3Result != MA_TRUE) { + ma_free(pSeekPoints, pAllocationCallbacks); + return MA_ERROR; + } + + pMP3->seekPointCount = seekPointCount; + pMP3->pSeekPoints = pSeekPoints; + + return MA_SUCCESS; +} + MA_API ma_result ma_mp3_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) { ma_result result; @@ -48851,6 +60138,8 @@ MA_API ma_result ma_mp3_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_p return MA_INVALID_FILE; } + ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks); + return MA_SUCCESS; } #else @@ -48881,6 +60170,8 @@ MA_API ma_result ma_mp3_init_file(const char* pFilePath, const ma_decoding_backe return MA_INVALID_FILE; } + ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks); + return MA_SUCCESS; } #else @@ -48912,6 +60203,8 @@ MA_API ma_result ma_mp3_init_file_w(const wchar_t* pFilePath, const ma_decoding_ return MA_INVALID_FILE; } + ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks); + return MA_SUCCESS; } #else @@ -48943,6 +60236,8 @@ MA_API ma_result ma_mp3_init_memory(const void* pData, size_t dataSize, const ma return MA_INVALID_FILE; } + ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks); + return MA_SUCCESS; } #else @@ -48962,8 +60257,6 @@ MA_API void ma_mp3_uninit(ma_mp3* pMP3, const ma_allocation_callbacks* pAllocati return; } - (void)pAllocationCallbacks; - #if !defined(MA_NO_MP3) { drmp3_uninit(&pMP3->dr); @@ -48975,11 +60268,22 @@ MA_API void ma_mp3_uninit(ma_mp3* pMP3, const ma_allocation_callbacks* pAllocati } #endif + /* Seek points need to be freed after the MP3 decoder has been uninitialized to ensure they're no longer being referenced. */ + ma_free(pMP3->pSeekPoints, pAllocationCallbacks); + ma_data_source_uninit(&pMP3->ds); } MA_API ma_result ma_mp3_read_pcm_frames(ma_mp3* pMP3, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + if (pMP3 == NULL) { return MA_INVALID_ARGS; } @@ -49049,7 +60353,7 @@ MA_API ma_result ma_mp3_seek_to_pcm_frame(ma_mp3* pMP3, ma_uint64 frameIndex) #if !defined(MA_NO_MP3) { drmp3_bool32 mp3Result; - + mp3Result = drmp3_seek_to_pcm_frame(&pMP3->dr, frameIndex); if (mp3Result != DRMP3_TRUE) { return MA_ERROR; @@ -49104,7 +60408,7 @@ MA_API ma_result ma_mp3_get_data_format(ma_mp3* pMP3, ma_format* pFormat, ma_uin } if (pChannelMap != NULL) { - ma_get_standard_channel_map(ma_standard_channel_map_default, (ma_uint32)ma_min(pMP3->dr.channels, channelMapCap), pChannelMap); + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pMP3->dr.channels); } return MA_SUCCESS; @@ -49279,23 +60583,13 @@ static void ma_decoding_backend_uninit__mp3(void* pUserData, ma_data_source* pBa ma_free(pMP3, pAllocationCallbacks); } -static ma_result ma_decoding_backend_get_channel_map__mp3(void* pUserData, ma_data_source* pBackend, ma_channel* pChannelMap, size_t channelMapCap) -{ - ma_mp3* pMP3 = (ma_mp3*)pBackend; - - (void)pUserData; - - return ma_mp3_get_data_format(pMP3, NULL, NULL, NULL, pChannelMap, channelMapCap); -} - static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_mp3 = { ma_decoding_backend_init__mp3, ma_decoding_backend_init_file__mp3, ma_decoding_backend_init_file_w__mp3, ma_decoding_backend_init_memory__mp3, - ma_decoding_backend_uninit__mp3, - ma_decoding_backend_get_channel_map__mp3 + ma_decoding_backend_uninit__mp3 }; static ma_result ma_decoder_init_mp3__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) @@ -49359,9 +60653,9 @@ static ma_result ma_stbvorbis_ds_seek(ma_data_source* pDataSource, ma_uint64 fra return ma_stbvorbis_seek_to_pcm_frame((ma_stbvorbis*)pDataSource, frameIndex); } -static ma_result ma_stbvorbis_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate) +static ma_result ma_stbvorbis_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) { - return ma_stbvorbis_get_data_format((ma_stbvorbis*)pDataSource, pFormat, pChannels, pSampleRate, NULL, 0); + return ma_stbvorbis_get_data_format((ma_stbvorbis*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); } static ma_result ma_stbvorbis_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) @@ -49378,11 +60672,11 @@ static ma_data_source_vtable g_ma_stbvorbis_ds_vtable = { ma_stbvorbis_ds_read, ma_stbvorbis_ds_seek, - NULL, /* onMap() */ - NULL, /* onUnmap() */ ma_stbvorbis_ds_get_data_format, ma_stbvorbis_ds_get_cursor, - ma_stbvorbis_ds_get_length + ma_stbvorbis_ds_get_length, + NULL, /* onSetLooping */ + 0 }; @@ -49645,6 +60939,14 @@ MA_API void ma_stbvorbis_uninit(ma_stbvorbis* pVorbis, const ma_allocation_callb MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + if (pVorbis == NULL) { return MA_INVALID_ARGS; } @@ -49753,7 +61055,7 @@ MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFram while (totalFramesRead < frameCount) { ma_uint64 framesRemaining = (frameCount - totalFramesRead); int framesRead; - + if (framesRemaining > INT_MAX) { framesRemaining = INT_MAX; } @@ -49761,7 +61063,7 @@ MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFram framesRead = stb_vorbis_get_samples_float_interleaved(pVorbis->stb, channels, (float*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), (int)framesRemaining * channels); /* Safe cast. */ totalFramesRead += framesRead; - if (framesRead < framesRemaining) { + if (framesRead < (int)framesRemaining) { break; /* Nothing left to read. Get out. */ } } @@ -49780,6 +61082,10 @@ MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFram *pFramesRead = totalFramesRead; } + if (result == MA_SUCCESS && totalFramesRead == 0) { + result = MA_AT_END; + } + return result; } #else @@ -49910,7 +61216,7 @@ MA_API ma_result ma_stbvorbis_get_data_format(ma_stbvorbis* pVorbis, ma_format* } if (pChannelMap != NULL) { - ma_get_standard_channel_map(ma_standard_channel_map_vorbis, (ma_uint32)ma_min(pVorbis->channels, channelMapCap), pChannelMap); + ma_channel_map_init_standard(ma_standard_channel_map_vorbis, pChannelMap, channelMapCap, pVorbis->channels); } return MA_SUCCESS; @@ -49970,7 +61276,7 @@ MA_API ma_result ma_stbvorbis_get_length_in_pcm_frames(ma_stbvorbis* pVorbis, ma } else { *pLength = stb_vorbis_stream_length_in_samples(pVorbis->stb); } - + return MA_SUCCESS; } #else @@ -50065,23 +61371,13 @@ static void ma_decoding_backend_uninit__stbvorbis(void* pUserData, ma_data_sourc ma_free(pVorbis, pAllocationCallbacks); } -static ma_result ma_decoding_backend_get_channel_map__stbvorbis(void* pUserData, ma_data_source* pBackend, ma_channel* pChannelMap, size_t channelMapCap) -{ - ma_stbvorbis* pVorbis = (ma_stbvorbis*)pBackend; - - (void)pUserData; - - return ma_stbvorbis_get_data_format(pVorbis, NULL, NULL, NULL, pChannelMap, channelMapCap); -} - static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_stbvorbis = { ma_decoding_backend_init__stbvorbis, ma_decoding_backend_init_file__stbvorbis, NULL, /* onInitFileW() */ ma_decoding_backend_init_memory__stbvorbis, - ma_decoding_backend_uninit__stbvorbis, - ma_decoding_backend_get_channel_map__stbvorbis + ma_decoding_backend_uninit__stbvorbis }; static ma_result ma_decoder_init_vorbis__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) @@ -50106,17 +61402,7 @@ static ma_result ma_decoder__init_allocation_callbacks(const ma_decoder_config* static ma_result ma_decoder__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { - ma_uint64 framesRead = ma_decoder_read_pcm_frames((ma_decoder*)pDataSource, pFramesOut, frameCount); - - if (pFramesRead != NULL) { - *pFramesRead = framesRead; - } - - if (framesRead == 0) { - return MA_AT_END; - } - - return MA_SUCCESS; + return ma_decoder_read_pcm_frames((ma_decoder*)pDataSource, pFramesOut, frameCount, pFramesRead); } static ma_result ma_decoder__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) @@ -50124,45 +61410,30 @@ static ma_result ma_decoder__data_source_on_seek(ma_data_source* pDataSource, ma return ma_decoder_seek_to_pcm_frame((ma_decoder*)pDataSource, frameIndex); } -static ma_result ma_decoder__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate) +static ma_result ma_decoder__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) { - ma_decoder* pDecoder = (ma_decoder*)pDataSource; - - *pFormat = pDecoder->outputFormat; - *pChannels = pDecoder->outputChannels; - *pSampleRate = pDecoder->outputSampleRate; - - return MA_SUCCESS; + return ma_decoder_get_data_format((ma_decoder*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); } static ma_result ma_decoder__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) { - ma_decoder* pDecoder = (ma_decoder*)pDataSource; - - return ma_decoder_get_cursor_in_pcm_frames(pDecoder, pCursor); + return ma_decoder_get_cursor_in_pcm_frames((ma_decoder*)pDataSource, pCursor); } static ma_result ma_decoder__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength) { - ma_decoder* pDecoder = (ma_decoder*)pDataSource; - - *pLength = ma_decoder_get_length_in_pcm_frames(pDecoder); - if (*pLength == 0) { - return MA_NOT_IMPLEMENTED; - } - - return MA_SUCCESS; + return ma_decoder_get_length_in_pcm_frames((ma_decoder*)pDataSource, pLength); } static ma_data_source_vtable g_ma_decoder_data_source_vtable = { ma_decoder__data_source_on_read, ma_decoder__data_source_on_seek, - NULL, /* onMap */ - NULL, /* onUnmap */ ma_decoder__data_source_on_get_data_format, ma_decoder__data_source_on_get_cursor, - ma_decoder__data_source_on_get_length + ma_decoder__data_source_on_get_length, + NULL, /* onSetLooping */ + 0 }; static ma_result ma_decoder__preinit(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, ma_decoder_tell_proc onTell, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) @@ -50206,22 +61477,9 @@ static ma_result ma_decoder__preinit(ma_decoder_read_proc onRead, ma_decoder_see static ma_result ma_decoder__postinit(const ma_decoder_config* pConfig, ma_decoder* pDecoder) { - ma_result result = MA_SUCCESS; + ma_result result; - /* Basic validation in case the internal decoder supports different limits to miniaudio. */ - { - /* TODO: Remove this block once we remove MA_MIN_CHANNELS and MA_MAX_CHANNELS. */ - ma_uint32 internalChannels; - ma_data_source_get_data_format(pDecoder->pBackend, NULL, &internalChannels, NULL); - - if (internalChannels < MA_MIN_CHANNELS || internalChannels > MA_MAX_CHANNELS) { - result = MA_INVALID_DATA; - } - } - - if (result == MA_SUCCESS) { - result = ma_decoder__init_data_converter(pDecoder, pConfig); - } + result = ma_decoder__init_data_converter(pDecoder, pConfig); /* If we failed post initialization we need to uninitialize the decoder before returning to prevent a memory leak. */ if (result != MA_SUCCESS) { @@ -50232,83 +61490,6 @@ static ma_result ma_decoder__postinit(const ma_decoder_config* pConfig, ma_decod return result; } -MA_API ma_result ma_decoder_init_wav(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ -#ifdef MA_HAS_WAV - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); - config.encodingFormat = ma_encoding_format_wav; - - return ma_decoder_init(onRead, onSeek, pUserData, &config, pDecoder); -#else - (void)onRead; - (void)onSeek; - (void)pUserData; - (void)pConfig; - (void)pDecoder; - return MA_NO_BACKEND; -#endif -} - -MA_API ma_result ma_decoder_init_flac(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ -#ifdef MA_HAS_FLAC - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); - config.encodingFormat = ma_encoding_format_flac; - - return ma_decoder_init(onRead, onSeek, pUserData, &config, pDecoder); -#else - (void)onRead; - (void)onSeek; - (void)pUserData; - (void)pConfig; - (void)pDecoder; - return MA_NO_BACKEND; -#endif -} - -MA_API ma_result ma_decoder_init_mp3(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ -#ifdef MA_HAS_MP3 - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); - config.encodingFormat = ma_encoding_format_mp3; - - return ma_decoder_init(onRead, onSeek, pUserData, &config, pDecoder); -#else - (void)onRead; - (void)onSeek; - (void)pUserData; - (void)pConfig; - (void)pDecoder; - return MA_NO_BACKEND; -#endif -} - -MA_API ma_result ma_decoder_init_vorbis(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ -#ifdef MA_HAS_VORBIS - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); - config.encodingFormat = ma_encoding_format_vorbis; - - return ma_decoder_init(onRead, onSeek, pUserData, &config, pDecoder); -#else - (void)onRead; - (void)onSeek; - (void)pUserData; - (void)pConfig; - (void)pDecoder; - return MA_NO_BACKEND; -#endif -} - - static ma_result ma_decoder_init__internal(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) { @@ -50431,29 +61612,41 @@ MA_API ma_result ma_decoder_init(ma_decoder_read_proc onRead, ma_decoder_seek_pr } -static size_t ma_decoder__on_read_memory(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead) +static ma_result ma_decoder__on_read_memory(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) { size_t bytesRemaining; MA_ASSERT(pDecoder->data.memory.dataSize >= pDecoder->data.memory.currentReadPos); + if (pBytesRead != NULL) { + *pBytesRead = 0; + } + bytesRemaining = pDecoder->data.memory.dataSize - pDecoder->data.memory.currentReadPos; if (bytesToRead > bytesRemaining) { bytesToRead = bytesRemaining; } + if (bytesRemaining == 0) { + return MA_AT_END; + } + if (bytesToRead > 0) { MA_COPY_MEMORY(pBufferOut, pDecoder->data.memory.pData + pDecoder->data.memory.currentReadPos, bytesToRead); pDecoder->data.memory.currentReadPos += bytesToRead; } - return bytesToRead; + if (pBytesRead != NULL) { + *pBytesRead = bytesToRead; + } + + return MA_SUCCESS; } -static ma_bool32 ma_decoder__on_seek_memory(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin) +static ma_result ma_decoder__on_seek_memory(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin) { if (byteOffset > 0 && (ma_uint64)byteOffset > MA_SIZE_MAX) { - return MA_FALSE; /* Too far. */ + return MA_BAD_SEEK; } if (origin == ma_seek_origin_current) { @@ -50490,7 +61683,7 @@ static ma_bool32 ma_decoder__on_seek_memory(ma_decoder* pDecoder, ma_int64 byteO } } - return MA_TRUE; + return MA_SUCCESS; } static ma_result ma_decoder__on_tell_memory(ma_decoder* pDecoder, ma_int64* pCursor) @@ -50537,79 +61730,6 @@ MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, cons return ma_decoder_init__internal(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, NULL, &config, pDecoder); } -MA_API ma_result ma_decoder_init_memory_wav(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ -#ifdef MA_HAS_WAV - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */ - config.encodingFormat = ma_encoding_format_wav; - - return ma_decoder_init_memory(pData, dataSize, &config, pDecoder); -#else - (void)pData; - (void)dataSize; - (void)pConfig; - (void)pDecoder; - return MA_NO_BACKEND; -#endif -} - -MA_API ma_result ma_decoder_init_memory_flac(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ -#ifdef MA_HAS_FLAC - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */ - config.encodingFormat = ma_encoding_format_flac; - - return ma_decoder_init_memory(pData, dataSize, &config, pDecoder); -#else - (void)pData; - (void)dataSize; - (void)pConfig; - (void)pDecoder; - return MA_NO_BACKEND; -#endif -} - -MA_API ma_result ma_decoder_init_memory_mp3(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ -#ifdef MA_HAS_MP3 - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */ - config.encodingFormat = ma_encoding_format_mp3; - - return ma_decoder_init_memory(pData, dataSize, &config, pDecoder); -#else - (void)pData; - (void)dataSize; - (void)pConfig; - (void)pDecoder; - return MA_NO_BACKEND; -#endif -} - -MA_API ma_result ma_decoder_init_memory_vorbis(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ -#ifdef MA_HAS_VORBIS - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */ - config.encodingFormat = ma_encoding_format_vorbis; - - return ma_decoder_init_memory(pData, dataSize, &config, pDecoder); -#else - (void)pData; - (void)dataSize; - (void)pConfig; - (void)pDecoder; - return MA_NO_BACKEND; -#endif -} - - #if defined(MA_HAS_WAV) || \ defined(MA_HAS_MP3) || \ @@ -50790,30 +61910,19 @@ static ma_bool32 ma_path_extension_equal_w(const wchar_t* path, const wchar_t* e -static size_t ma_decoder__on_read_vfs(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead) +static ma_result ma_decoder__on_read_vfs(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) { - size_t bytesRead; - MA_ASSERT(pDecoder != NULL); MA_ASSERT(pBufferOut != NULL); - ma_vfs_or_default_read(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, pBufferOut, bytesToRead, &bytesRead); - - return bytesRead; + return ma_vfs_or_default_read(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, pBufferOut, bytesToRead, pBytesRead); } -static ma_bool32 ma_decoder__on_seek_vfs(ma_decoder* pDecoder, ma_int64 offset, ma_seek_origin origin) +static ma_result ma_decoder__on_seek_vfs(ma_decoder* pDecoder, ma_int64 offset, ma_seek_origin origin) { - ma_result result; - MA_ASSERT(pDecoder != NULL); - result = ma_vfs_or_default_seek(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, offset, origin); - if (result != MA_SUCCESS) { - return MA_FALSE; - } - - return MA_TRUE; + return ma_vfs_or_default_seek(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, offset, origin); } static ma_result ma_decoder__on_tell_vfs(ma_decoder* pDecoder, ma_int64* pCursor) @@ -50955,79 +62064,6 @@ MA_API ma_result ma_decoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const return MA_SUCCESS; } -MA_API ma_result ma_decoder_init_vfs_wav(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ -#ifdef MA_HAS_WAV - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); - config.encodingFormat = ma_encoding_format_wav; - - return ma_decoder_init_vfs(pVFS, pFilePath, &config, pDecoder); -#else - (void)pVFS; - (void)pFilePath; - (void)pConfig; - (void)pDecoder; - return MA_NO_BACKEND; -#endif -} - -MA_API ma_result ma_decoder_init_vfs_flac(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ -#ifdef MA_HAS_FLAC - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); - config.encodingFormat = ma_encoding_format_flac; - - return ma_decoder_init_vfs(pVFS, pFilePath, &config, pDecoder); -#else - (void)pVFS; - (void)pFilePath; - (void)pConfig; - (void)pDecoder; - return MA_NO_BACKEND; -#endif -} - -MA_API ma_result ma_decoder_init_vfs_mp3(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ -#ifdef MA_HAS_MP3 - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); - config.encodingFormat = ma_encoding_format_mp3; - - return ma_decoder_init_vfs(pVFS, pFilePath, &config, pDecoder); -#else - (void)pVFS; - (void)pFilePath; - (void)pConfig; - (void)pDecoder; - return MA_NO_BACKEND; -#endif -} - -MA_API ma_result ma_decoder_init_vfs_vorbis(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ -#ifdef MA_HAS_VORBIS - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); - config.encodingFormat = ma_encoding_format_vorbis; - - return ma_decoder_init_vfs(pVFS, pFilePath, &config, pDecoder); -#else - (void)pVFS; - (void)pFilePath; - (void)pConfig; - (void)pDecoder; - return MA_NO_BACKEND; -#endif -} - - static ma_result ma_decoder__preinit_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) { @@ -51158,132 +62194,16 @@ MA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, c return MA_SUCCESS; } -MA_API ma_result ma_decoder_init_vfs_wav_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ -#ifdef MA_HAS_WAV - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); - config.encodingFormat = ma_encoding_format_wav; - - return ma_decoder_init_vfs_w(pVFS, pFilePath, &config, pDecoder); -#else - (void)pVFS; - (void)pFilePath; - (void)pConfig; - (void)pDecoder; - return MA_NO_BACKEND; -#endif -} - -MA_API ma_result ma_decoder_init_vfs_flac_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ -#ifdef MA_HAS_FLAC - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); - config.encodingFormat = ma_encoding_format_flac; - - return ma_decoder_init_vfs_w(pVFS, pFilePath, &config, pDecoder); -#else - (void)pVFS; - (void)pFilePath; - (void)pConfig; - (void)pDecoder; - return MA_NO_BACKEND; -#endif -} - -MA_API ma_result ma_decoder_init_vfs_mp3_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ -#ifdef MA_HAS_MP3 - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); - config.encodingFormat = ma_encoding_format_mp3; - - return ma_decoder_init_vfs_w(pVFS, pFilePath, &config, pDecoder); -#else - (void)pVFS; - (void)pFilePath; - (void)pConfig; - (void)pDecoder; - return MA_NO_BACKEND; -#endif -} - -MA_API ma_result ma_decoder_init_vfs_vorbis_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ -#ifdef MA_HAS_VORBIS - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); - config.encodingFormat = ma_encoding_format_vorbis; - - return ma_decoder_init_vfs_w(pVFS, pFilePath, &config, pDecoder); -#else - (void)pVFS; - (void)pFilePath; - (void)pConfig; - (void)pDecoder; - return MA_NO_BACKEND; -#endif -} - - - MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) { return ma_decoder_init_vfs(NULL, pFilePath, pConfig, pDecoder); } -MA_API ma_result ma_decoder_init_file_wav(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_vfs_wav(NULL, pFilePath, pConfig, pDecoder); -} - -MA_API ma_result ma_decoder_init_file_flac(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_vfs_flac(NULL, pFilePath, pConfig, pDecoder); -} - -MA_API ma_result ma_decoder_init_file_mp3(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_vfs_mp3(NULL, pFilePath, pConfig, pDecoder); -} - -MA_API ma_result ma_decoder_init_file_vorbis(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_vfs_vorbis(NULL, pFilePath, pConfig, pDecoder); -} - - - MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) { return ma_decoder_init_vfs_w(NULL, pFilePath, pConfig, pDecoder); } -MA_API ma_result ma_decoder_init_file_wav_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_vfs_wav_w(NULL, pFilePath, pConfig, pDecoder); -} - -MA_API ma_result ma_decoder_init_file_flac_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_vfs_flac_w(NULL, pFilePath, pConfig, pDecoder); -} - -MA_API ma_result ma_decoder_init_file_mp3_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_vfs_mp3_w(NULL, pFilePath, pConfig, pDecoder); -} - -MA_API ma_result ma_decoder_init_file_vorbis_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_vfs_vorbis_w(NULL, pFilePath, pConfig, pDecoder); -} - MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder) { if (pDecoder == NULL) { @@ -51296,15 +62216,244 @@ MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder) } } - /* Legacy. */ if (pDecoder->onRead == ma_decoder__on_read_vfs) { ma_vfs_or_default_close(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file); pDecoder->data.vfs.file = NULL; } - ma_data_converter_uninit(&pDecoder->converter); + ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks); ma_data_source_uninit(&pDecoder->ds); + if (pDecoder->pInputCache != NULL) { + ma_free(pDecoder->pInputCache, &pDecoder->allocationCallbacks); + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + ma_result result = MA_SUCCESS; + ma_uint64 totalFramesReadOut; + void* pRunningFramesOut; + + if (pFramesRead != NULL) { + *pFramesRead = 0; /* Safety. */ + } + + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + + if (pDecoder == NULL) { + return MA_INVALID_ARGS; + } + + if (pDecoder->pBackend == NULL) { + return MA_INVALID_OPERATION; + } + + /* Fast path. */ + if (pDecoder->converter.isPassthrough) { + result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pFramesOut, frameCount, &totalFramesReadOut); + } else { + /* + Getting here means we need to do data conversion. If we're seeking forward and are _not_ doing resampling we can run this in a fast path. If we're doing resampling we + need to run through each sample because we need to ensure it's internal cache is updated. + */ + if (pFramesOut == NULL && pDecoder->converter.hasResampler == MA_FALSE) { + result = ma_data_source_read_pcm_frames(pDecoder->pBackend, NULL, frameCount, &totalFramesReadOut); + } else { + /* Slow path. Need to run everything through the data converter. */ + ma_format internalFormat; + ma_uint32 internalChannels; + + totalFramesReadOut = 0; + pRunningFramesOut = pFramesOut; + + result = ma_data_source_get_data_format(pDecoder->pBackend, &internalFormat, &internalChannels, NULL, NULL, 0); + if (result != MA_SUCCESS) { + return result; /* Failed to retrieve the internal format and channel count. */ + } + + /* + We run a different path depending on whether or not we are using a heap-allocated + intermediary buffer or not. If the data converter does not support the calculation of + the required number of input frames, we'll use the heap-allocated path. Otherwise we'll + use the stack-allocated path. + */ + if (pDecoder->pInputCache != NULL) { + /* We don't have a way of determining the required number of input frames, so need to persistently store input data in a cache. */ + while (totalFramesReadOut < frameCount) { + ma_uint64 framesToReadThisIterationIn; + ma_uint64 framesToReadThisIterationOut; + + /* If there's any data available in the cache, that needs to get processed first. */ + if (pDecoder->inputCacheRemaining > 0) { + framesToReadThisIterationOut = (frameCount - totalFramesReadOut); + framesToReadThisIterationIn = framesToReadThisIterationOut; + if (framesToReadThisIterationIn > pDecoder->inputCacheRemaining) { + framesToReadThisIterationIn = pDecoder->inputCacheRemaining; + } + + result = ma_data_converter_process_pcm_frames(&pDecoder->converter, ma_offset_pcm_frames_ptr(pDecoder->pInputCache, pDecoder->inputCacheConsumed, internalFormat, internalChannels), &framesToReadThisIterationIn, pRunningFramesOut, &framesToReadThisIterationOut); + if (result != MA_SUCCESS) { + break; + } + + pDecoder->inputCacheConsumed += framesToReadThisIterationIn; + pDecoder->inputCacheRemaining -= framesToReadThisIterationIn; + + totalFramesReadOut += framesToReadThisIterationOut; + + if (pRunningFramesOut != NULL) { + pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesToReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels)); + } + + if (framesToReadThisIterationIn == 0 && framesToReadThisIterationOut == 0) { + break; /* We're done. */ + } + } + + /* Getting here means there's no data in the cache and we need to fill it up from the data source. */ + if (pDecoder->inputCacheRemaining == 0) { + pDecoder->inputCacheConsumed = 0; + + result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pDecoder->pInputCache, pDecoder->inputCacheCap, &pDecoder->inputCacheRemaining); + if (result != MA_SUCCESS) { + break; + } + } + } + } else { + /* We have a way of determining the required number of input frames so just use the stack. */ + while (totalFramesReadOut < frameCount) { + ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In internal format. */ + ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(internalFormat, internalChannels); + ma_uint64 framesToReadThisIterationIn; + ma_uint64 framesReadThisIterationIn; + ma_uint64 framesToReadThisIterationOut; + ma_uint64 framesReadThisIterationOut; + ma_uint64 requiredInputFrameCount; + + framesToReadThisIterationOut = (frameCount - totalFramesReadOut); + framesToReadThisIterationIn = framesToReadThisIterationOut; + if (framesToReadThisIterationIn > intermediaryBufferCap) { + framesToReadThisIterationIn = intermediaryBufferCap; + } + + ma_data_converter_get_required_input_frame_count(&pDecoder->converter, framesToReadThisIterationOut, &requiredInputFrameCount); + if (framesToReadThisIterationIn > requiredInputFrameCount) { + framesToReadThisIterationIn = requiredInputFrameCount; + } + + if (requiredInputFrameCount > 0) { + result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pIntermediaryBuffer, framesToReadThisIterationIn, &framesReadThisIterationIn); + } else { + framesReadThisIterationIn = 0; + } + + /* + At this point we have our decoded data in input format and now we need to convert to output format. Note that even if we didn't read any + input frames, we still want to try processing frames because there may some output frames generated from cached input data. + */ + framesReadThisIterationOut = framesToReadThisIterationOut; + result = ma_data_converter_process_pcm_frames(&pDecoder->converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut); + if (result != MA_SUCCESS) { + break; + } + + totalFramesReadOut += framesReadThisIterationOut; + + if (pRunningFramesOut != NULL) { + pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels)); + } + + if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) { + break; /* We're done. */ + } + } + } + } + } + + pDecoder->readPointerInPCMFrames += totalFramesReadOut; + + if (pFramesRead != NULL) { + *pFramesRead = totalFramesReadOut; + } + + if (result == MA_SUCCESS && totalFramesReadOut == 0) { + result = MA_AT_END; + } + + return result; +} + +MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex) +{ + if (pDecoder == NULL) { + return MA_INVALID_ARGS; + } + + if (pDecoder->pBackend != NULL) { + ma_result result; + ma_uint64 internalFrameIndex; + ma_uint32 internalSampleRate; + ma_uint64 currentFrameIndex; + + result = ma_data_source_get_data_format(pDecoder->pBackend, NULL, NULL, &internalSampleRate, NULL, 0); + if (result != MA_SUCCESS) { + return result; /* Failed to retrieve the internal sample rate. */ + } + + if (internalSampleRate == pDecoder->outputSampleRate) { + internalFrameIndex = frameIndex; + } else { + internalFrameIndex = ma_calculate_frame_count_after_resampling(internalSampleRate, pDecoder->outputSampleRate, frameIndex); + } + + /* Only seek if we're requesting a different frame to what we're currently sitting on. */ + ma_data_source_get_cursor_in_pcm_frames(pDecoder->pBackend, ¤tFrameIndex); + if (currentFrameIndex != internalFrameIndex) { + result = ma_data_source_seek_to_pcm_frame(pDecoder->pBackend, internalFrameIndex); + if (result == MA_SUCCESS) { + pDecoder->readPointerInPCMFrames = frameIndex; + } + + /* Reset the data converter so that any cached data in the resampler is cleared. */ + ma_data_converter_reset(&pDecoder->converter); + } + + return result; + } + + /* Should never get here, but if we do it means onSeekToPCMFrame was not set by the backend. */ + return MA_INVALID_ARGS; +} + +MA_API ma_result ma_decoder_get_data_format(ma_decoder* pDecoder, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + if (pDecoder == NULL) { + return MA_INVALID_ARGS; + } + + if (pFormat != NULL) { + *pFormat = pDecoder->outputFormat; + } + + if (pChannels != NULL) { + *pChannels = pDecoder->outputChannels; + } + + if (pSampleRate != NULL) { + *pSampleRate = pDecoder->outputSampleRate; + } + + if (pChannelMap != NULL) { + ma_data_converter_get_output_channel_map(&pDecoder->converter, pChannelMap, channelMapCap); + } + return MA_SUCCESS; } @@ -51325,164 +62474,48 @@ MA_API ma_result ma_decoder_get_cursor_in_pcm_frames(ma_decoder* pDecoder, ma_ui return MA_SUCCESS; } -MA_API ma_uint64 ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder) +MA_API ma_result ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pLength) { - if (pDecoder == NULL) { - return 0; + if (pLength == NULL) { + return MA_INVALID_ARGS; } - if (pDecoder->pBackend != NULL) { - ma_result result; - ma_uint64 nativeLengthInPCMFrames; - ma_uint32 internalSampleRate; + *pLength = 0; - ma_data_source_get_length_in_pcm_frames(pDecoder->pBackend, &nativeLengthInPCMFrames); - - result = ma_data_source_get_data_format(pDecoder->pBackend, NULL, NULL, &internalSampleRate); - if (result != MA_SUCCESS) { - return 0; /* Failed to retrieve the internal sample rate. */ - } - - if (internalSampleRate == pDecoder->outputSampleRate) { - return nativeLengthInPCMFrames; - } else { - return ma_calculate_frame_count_after_resampling(pDecoder->outputSampleRate, internalSampleRate, nativeLengthInPCMFrames); - } - } - - return 0; -} - -MA_API ma_uint64 ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount) -{ - ma_result result; - ma_uint64 totalFramesReadOut; - ma_uint64 totalFramesReadIn; - void* pRunningFramesOut; - - if (pDecoder == NULL) { - return 0; - } - - if (pDecoder->pBackend == NULL) { - return 0; - } - - /* Fast path. */ - if (pDecoder->converter.isPassthrough) { - result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pFramesOut, frameCount, &totalFramesReadOut, MA_FALSE); - } else { - /* - Getting here means we need to do data conversion. If we're seeking forward and are _not_ doing resampling we can run this in a fast path. If we're doing resampling we - need to run through each sample because we need to ensure it's internal cache is updated. - */ - if (pFramesOut == NULL && pDecoder->converter.hasResampler == MA_FALSE) { - result = ma_data_source_read_pcm_frames(pDecoder->pBackend, NULL, frameCount, &totalFramesReadOut, MA_FALSE); - } else { - /* Slow path. Need to run everything through the data converter. */ - ma_format internalFormat; - ma_uint32 internalChannels; - - totalFramesReadOut = 0; - totalFramesReadIn = 0; - pRunningFramesOut = pFramesOut; - - result = ma_data_source_get_data_format(pDecoder->pBackend, &internalFormat, &internalChannels, NULL); - if (result != MA_SUCCESS) { - return 0; /* Failed to retrieve the internal format and channel count. */ - } - - while (totalFramesReadOut < frameCount) { - ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In internal format. */ - ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(internalFormat, internalChannels); - ma_uint64 framesToReadThisIterationIn; - ma_uint64 framesReadThisIterationIn; - ma_uint64 framesToReadThisIterationOut; - ma_uint64 framesReadThisIterationOut; - ma_uint64 requiredInputFrameCount; - - framesToReadThisIterationOut = (frameCount - totalFramesReadOut); - framesToReadThisIterationIn = framesToReadThisIterationOut; - if (framesToReadThisIterationIn > intermediaryBufferCap) { - framesToReadThisIterationIn = intermediaryBufferCap; - } - - requiredInputFrameCount = ma_data_converter_get_required_input_frame_count(&pDecoder->converter, framesToReadThisIterationOut); - if (framesToReadThisIterationIn > requiredInputFrameCount) { - framesToReadThisIterationIn = requiredInputFrameCount; - } - - if (requiredInputFrameCount > 0) { - result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pIntermediaryBuffer, framesToReadThisIterationIn, &framesReadThisIterationIn, MA_FALSE); - totalFramesReadIn += framesReadThisIterationIn; - } else { - framesReadThisIterationIn = 0; - } - - /* - At this point we have our decoded data in input format and now we need to convert to output format. Note that even if we didn't read any - input frames, we still want to try processing frames because there may some output frames generated from cached input data. - */ - framesReadThisIterationOut = framesToReadThisIterationOut; - result = ma_data_converter_process_pcm_frames(&pDecoder->converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut); - if (result != MA_SUCCESS) { - break; - } - - totalFramesReadOut += framesReadThisIterationOut; - - if (pRunningFramesOut != NULL) { - pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels)); - } - - if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) { - break; /* We're done. */ - } - } - } - } - - pDecoder->readPointerInPCMFrames += totalFramesReadOut; - - return totalFramesReadOut; -} - -MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex) -{ if (pDecoder == NULL) { return MA_INVALID_ARGS; } if (pDecoder->pBackend != NULL) { ma_result result; - ma_uint64 internalFrameIndex; + ma_uint64 internalLengthInPCMFrames; ma_uint32 internalSampleRate; - result = ma_data_source_get_data_format(pDecoder->pBackend, NULL, NULL, &internalSampleRate); + result = ma_data_source_get_length_in_pcm_frames(pDecoder->pBackend, &internalLengthInPCMFrames); if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the internal sample rate. */ + return result; /* Failed to retrieve the internal length. */ + } + + result = ma_data_source_get_data_format(pDecoder->pBackend, NULL, NULL, &internalSampleRate, NULL, 0); + if (result != MA_SUCCESS) { + return result; /* Failed to retrieve the internal sample rate. */ } if (internalSampleRate == pDecoder->outputSampleRate) { - internalFrameIndex = frameIndex; + *pLength = internalLengthInPCMFrames; } else { - internalFrameIndex = ma_calculate_frame_count_after_resampling(internalSampleRate, pDecoder->outputSampleRate, frameIndex); + *pLength = ma_calculate_frame_count_after_resampling(pDecoder->outputSampleRate, internalSampleRate, internalLengthInPCMFrames); } - result = ma_data_source_seek_to_pcm_frame(pDecoder->pBackend, internalFrameIndex); - if (result == MA_SUCCESS) { - pDecoder->readPointerInPCMFrames = frameIndex; - } - - return result; + return MA_SUCCESS; + } else { + return MA_NO_BACKEND; } - - /* Should never get here, but if we do it means onSeekToPCMFrame was not set by the backend. */ - return MA_INVALID_ARGS; } MA_API ma_result ma_decoder_get_available_frames(ma_decoder* pDecoder, ma_uint64* pAvailableFrames) { + ma_result result; ma_uint64 totalFrameCount; if (pAvailableFrames == NULL) { @@ -51495,9 +62528,9 @@ MA_API ma_result ma_decoder_get_available_frames(ma_decoder* pDecoder, ma_uint64 return MA_INVALID_ARGS; } - totalFrameCount = ma_decoder_get_length_in_pcm_frames(pDecoder); - if (totalFrameCount == 0) { - return MA_NOT_IMPLEMENTED; + result = ma_decoder_get_length_in_pcm_frames(pDecoder, &totalFrameCount); + if (result != MA_SUCCESS) { + return result; } if (totalFrameCount <= pDecoder->readPointerInPCMFrames) { @@ -51506,12 +62539,13 @@ MA_API ma_result ma_decoder_get_available_frames(ma_decoder* pDecoder, ma_uint64 *pAvailableFrames = totalFrameCount - pDecoder->readPointerInPCMFrames; } - return MA_SUCCESS; /* No frames available. */ + return MA_SUCCESS; } static ma_result ma_decoder__full_decode_and_uninit(ma_decoder* pDecoder, ma_decoder_config* pConfigOut, ma_uint64* pFrameCountOut, void** ppPCMFramesOut) { + ma_result result; ma_uint64 totalFrameCount; ma_uint64 bpf; ma_uint64 dataCapInFrames; @@ -51532,21 +62566,19 @@ static ma_result ma_decoder__full_decode_and_uninit(ma_decoder* pDecoder, ma_dec /* Make room if there's not enough. */ if (totalFrameCount == dataCapInFrames) { void* pNewPCMFramesOut; - ma_uint64 oldDataCapInFrames = dataCapInFrames; ma_uint64 newDataCapInFrames = dataCapInFrames*2; if (newDataCapInFrames == 0) { newDataCapInFrames = 4096; } if ((newDataCapInFrames * bpf) > MA_SIZE_MAX) { - ma__free_from_callbacks(pPCMFramesOut, &pDecoder->allocationCallbacks); + ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks); return MA_TOO_BIG; } - - pNewPCMFramesOut = (void*)ma__realloc_from_callbacks(pPCMFramesOut, (size_t)(newDataCapInFrames * bpf), (size_t)(oldDataCapInFrames * bpf), &pDecoder->allocationCallbacks); + pNewPCMFramesOut = (void*)ma_realloc(pPCMFramesOut, (size_t)(newDataCapInFrames * bpf), &pDecoder->allocationCallbacks); if (pNewPCMFramesOut == NULL) { - ma__free_from_callbacks(pPCMFramesOut, &pDecoder->allocationCallbacks); + ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks); return MA_OUT_OF_MEMORY; } @@ -51557,9 +62589,13 @@ static ma_result ma_decoder__full_decode_and_uninit(ma_decoder* pDecoder, ma_dec frameCountToTryReading = dataCapInFrames - totalFrameCount; MA_ASSERT(frameCountToTryReading > 0); - framesJustRead = ma_decoder_read_pcm_frames(pDecoder, (ma_uint8*)pPCMFramesOut + (totalFrameCount * bpf), frameCountToTryReading); + result = ma_decoder_read_pcm_frames(pDecoder, (ma_uint8*)pPCMFramesOut + (totalFrameCount * bpf), frameCountToTryReading, &framesJustRead); totalFrameCount += framesJustRead; + if (result != MA_SUCCESS) { + break; + } + if (framesJustRead < frameCountToTryReading) { break; } @@ -51567,16 +62603,15 @@ static ma_result ma_decoder__full_decode_and_uninit(ma_decoder* pDecoder, ma_dec if (pConfigOut != NULL) { - pConfigOut->format = pDecoder->outputFormat; - pConfigOut->channels = pDecoder->outputChannels; + pConfigOut->format = pDecoder->outputFormat; + pConfigOut->channels = pDecoder->outputChannels; pConfigOut->sampleRate = pDecoder->outputSampleRate; - ma_channel_map_copy(pConfigOut->channelMap, pDecoder->outputChannelMap, pDecoder->outputChannels); } if (ppPCMFramesOut != NULL) { *ppPCMFramesOut = pPCMFramesOut; } else { - ma__free_from_callbacks(pPCMFramesOut, &pDecoder->allocationCallbacks); + ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks); } if (pFrameCountOut != NULL) { @@ -51652,17 +62687,27 @@ MA_API ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder static size_t ma_encoder__internal_on_write_wav(void* pUserData, const void* pData, size_t bytesToWrite) { ma_encoder* pEncoder = (ma_encoder*)pUserData; + size_t bytesWritten = 0; + MA_ASSERT(pEncoder != NULL); - return pEncoder->onWrite(pEncoder, pData, bytesToWrite); + pEncoder->onWrite(pEncoder, pData, bytesToWrite, &bytesWritten); + return bytesWritten; } static drwav_bool32 ma_encoder__internal_on_seek_wav(void* pUserData, int offset, drwav_seek_origin origin) { ma_encoder* pEncoder = (ma_encoder*)pUserData; + ma_result result; + MA_ASSERT(pEncoder != NULL); - return pEncoder->onSeek(pEncoder, offset, (origin == drwav_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current); + result = pEncoder->onSeek(pEncoder, offset, (origin == drwav_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current); + if (result != MA_SUCCESS) { + return DRWAV_FALSE; + } else { + return DRWAV_TRUE; + } } static ma_result ma_encoder__on_init_wav(ma_encoder* pEncoder) @@ -51673,7 +62718,7 @@ static ma_result ma_encoder__on_init_wav(ma_encoder* pEncoder) MA_ASSERT(pEncoder != NULL); - pWav = (drwav*)ma__malloc_from_callbacks(sizeof(*pWav), &pEncoder->config.allocationCallbacks); + pWav = (drwav*)ma_malloc(sizeof(*pWav), &pEncoder->config.allocationCallbacks); if (pWav == NULL) { return MA_OUT_OF_MEMORY; } @@ -51712,28 +62757,35 @@ static void ma_encoder__on_uninit_wav(ma_encoder* pEncoder) MA_ASSERT(pWav != NULL); drwav_uninit(pWav); - ma__free_from_callbacks(pWav, &pEncoder->config.allocationCallbacks); + ma_free(pWav, &pEncoder->config.allocationCallbacks); } -static ma_uint64 ma_encoder__on_write_pcm_frames_wav(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount) +static ma_result ma_encoder__on_write_pcm_frames_wav(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten) { drwav* pWav; + ma_uint64 framesWritten; MA_ASSERT(pEncoder != NULL); pWav = (drwav*)pEncoder->pInternalEncoder; MA_ASSERT(pWav != NULL); - return drwav_write_pcm_frames(pWav, frameCount, pFramesIn); + framesWritten = drwav_write_pcm_frames(pWav, frameCount, pFramesIn); + + if (pFramesWritten != NULL) { + *pFramesWritten = framesWritten; + } + + return MA_SUCCESS; } #endif -MA_API ma_encoder_config ma_encoder_config_init(ma_resource_format resourceFormat, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) +MA_API ma_encoder_config ma_encoder_config_init(ma_encoding_format encodingFormat, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) { ma_encoder_config config; MA_ZERO_OBJECT(&config); - config.resourceFormat = resourceFormat; + config.encodingFormat = encodingFormat; config.format = format; config.channels = channels; config.sampleRate = sampleRate; @@ -51784,9 +62836,9 @@ MA_API ma_result ma_encoder_init__internal(ma_encoder_write_proc onWrite, ma_enc pEncoder->onSeek = onSeek; pEncoder->pUserData = pUserData; - switch (pEncoder->config.resourceFormat) + switch (pEncoder->config.encodingFormat) { - case ma_resource_format_wav: + case ma_encoding_format_wav: { #if defined(MA_HAS_WAV) pEncoder->onInit = ma_encoder__on_init_wav; @@ -51806,64 +62858,85 @@ MA_API ma_result ma_encoder_init__internal(ma_encoder_write_proc onWrite, ma_enc /* Getting here means we should have our backend callbacks set up. */ if (result == MA_SUCCESS) { result = pEncoder->onInit(pEncoder); - if (result != MA_SUCCESS) { - return result; - } + } + + return result; +} + +static ma_result ma_encoder__on_write_vfs(ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite, size_t* pBytesWritten) +{ + return ma_vfs_or_default_write(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file, pBufferIn, bytesToWrite, pBytesWritten); +} + +static ma_result ma_encoder__on_seek_vfs(ma_encoder* pEncoder, ma_int64 offset, ma_seek_origin origin) +{ + return ma_vfs_or_default_seek(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file, offset, origin); +} + +MA_API ma_result ma_encoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) +{ + ma_result result; + ma_vfs_file file; + + result = ma_encoder_preinit(pConfig, pEncoder); + if (result != MA_SUCCESS) { + return result; + } + + /* Now open the file. If this fails we don't need to uninitialize the encoder. */ + result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_WRITE, &file); + if (result != MA_SUCCESS) { + return result; + } + + pEncoder->data.vfs.pVFS = pVFS; + pEncoder->data.vfs.file = file; + + result = ma_encoder_init__internal(ma_encoder__on_write_vfs, ma_encoder__on_seek_vfs, NULL, pEncoder); + if (result != MA_SUCCESS) { + ma_vfs_or_default_close(pVFS, file); + return result; } return MA_SUCCESS; } -MA_API size_t ma_encoder__on_write_stdio(ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite) +MA_API ma_result ma_encoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) { - return fwrite(pBufferIn, 1, bytesToWrite, (FILE*)pEncoder->pFile); -} + ma_result result; + ma_vfs_file file; -MA_API ma_bool32 ma_encoder__on_seek_stdio(ma_encoder* pEncoder, int byteOffset, ma_seek_origin origin) -{ - return fseek((FILE*)pEncoder->pFile, byteOffset, (origin == ma_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; + result = ma_encoder_preinit(pConfig, pEncoder); + if (result != MA_SUCCESS) { + return result; + } + + /* Now open the file. If this fails we don't need to uninitialize the encoder. */ + result = ma_vfs_or_default_open_w(pVFS, pFilePath, MA_OPEN_MODE_WRITE, &file); + if (result != MA_SUCCESS) { + return result; + } + + pEncoder->data.vfs.pVFS = pVFS; + pEncoder->data.vfs.file = file; + + result = ma_encoder_init__internal(ma_encoder__on_write_vfs, ma_encoder__on_seek_vfs, NULL, pEncoder); + if (result != MA_SUCCESS) { + ma_vfs_or_default_close(pVFS, file); + return result; + } + + return MA_SUCCESS; } MA_API ma_result ma_encoder_init_file(const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) { - ma_result result; - FILE* pFile; - - result = ma_encoder_preinit(pConfig, pEncoder); - if (result != MA_SUCCESS) { - return result; - } - - /* Now open the file. If this fails we don't need to uninitialize the encoder. */ - result = ma_fopen(&pFile, pFilePath, "wb"); - if (pFile == NULL) { - return result; - } - - pEncoder->pFile = pFile; - - return ma_encoder_init__internal(ma_encoder__on_write_stdio, ma_encoder__on_seek_stdio, NULL, pEncoder); + return ma_encoder_init_vfs(NULL, pFilePath, pConfig, pEncoder); } MA_API ma_result ma_encoder_init_file_w(const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) { - ma_result result; - FILE* pFile; - - result = ma_encoder_preinit(pConfig, pEncoder); - if (result != MA_SUCCESS) { - return result; - } - - /* Now open the file. If this fails we don't need to uninitialize the encoder. */ - result = ma_wfopen(&pFile, pFilePath, L"wb", &pEncoder->config.allocationCallbacks); - if (pFile == NULL) { - return result; - } - - pEncoder->pFile = pFile; - - return ma_encoder_init__internal(ma_encoder__on_write_stdio, ma_encoder__on_seek_stdio, NULL, pEncoder); + return ma_encoder_init_vfs_w(NULL, pFilePath, pConfig, pEncoder); } MA_API ma_result ma_encoder_init(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, const ma_encoder_config* pConfig, ma_encoder* pEncoder) @@ -51890,19 +62963,24 @@ MA_API void ma_encoder_uninit(ma_encoder* pEncoder) } /* If we have a file handle, close it. */ - if (pEncoder->onWrite == ma_encoder__on_write_stdio) { - fclose((FILE*)pEncoder->pFile); + if (pEncoder->onWrite == ma_encoder__on_write_vfs) { + ma_vfs_or_default_close(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file); + pEncoder->data.vfs.file = NULL; } } -MA_API ma_uint64 ma_encoder_write_pcm_frames(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount) +MA_API ma_result ma_encoder_write_pcm_frames(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten) { - if (pEncoder == NULL || pFramesIn == NULL) { - return 0; + if (pFramesWritten != NULL) { + *pFramesWritten = 0; } - return pEncoder->onWritePCMFrames(pEncoder, pFramesIn, frameCount); + if (pEncoder == NULL || pFramesIn == NULL) { + return MA_INVALID_ARGS; + } + + return pEncoder->onWritePCMFrames(pEncoder, pFramesIn, frameCount, pFramesWritten); } #endif /* MA_NO_ENCODING */ @@ -51931,17 +63009,7 @@ MA_API ma_waveform_config ma_waveform_config_init(ma_format format, ma_uint32 ch static ma_result ma_waveform__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { - ma_uint64 framesRead = ma_waveform_read_pcm_frames((ma_waveform*)pDataSource, pFramesOut, frameCount); - - if (pFramesRead != NULL) { - *pFramesRead = framesRead; - } - - if (framesRead == 0) { - return MA_AT_END; - } - - return MA_SUCCESS; + return ma_waveform_read_pcm_frames((ma_waveform*)pDataSource, pFramesOut, frameCount, pFramesRead); } static ma_result ma_waveform__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) @@ -51949,13 +63017,14 @@ static ma_result ma_waveform__data_source_on_seek(ma_data_source* pDataSource, m return ma_waveform_seek_to_pcm_frame((ma_waveform*)pDataSource, frameIndex); } -static ma_result ma_waveform__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate) +static ma_result ma_waveform__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) { ma_waveform* pWaveform = (ma_waveform*)pDataSource; *pFormat = pWaveform->config.format; *pChannels = pWaveform->config.channels; *pSampleRate = pWaveform->config.sampleRate; + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pWaveform->config.channels); return MA_SUCCESS; } @@ -51983,11 +63052,11 @@ static ma_data_source_vtable g_ma_waveform_data_source_vtable = { ma_waveform__data_source_on_read, ma_waveform__data_source_on_seek, - NULL, /* onMap */ - NULL, /* onUnmap */ ma_waveform__data_source_on_get_data_format, ma_waveform__data_source_on_get_cursor, - NULL /* onGetLength. There's no notion of a length in waveforms. */ + NULL, /* onGetLength. There's no notion of a length in waveforms. */ + NULL, /* onSetLooping */ + 0 }; MA_API ma_result ma_waveform_init(const ma_waveform_config* pConfig, ma_waveform* pWaveform) @@ -52296,10 +63365,18 @@ static void ma_waveform_read_pcm_frames__sawtooth(ma_waveform* pWaveform, void* } } -MA_API ma_uint64 ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount) +MA_API ma_result ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + if (pWaveform == NULL) { - return 0; + return MA_INVALID_ARGS; } if (pFramesOut != NULL) { @@ -52325,13 +63402,17 @@ MA_API ma_uint64 ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFram ma_waveform_read_pcm_frames__sawtooth(pWaveform, pFramesOut, frameCount); } break; - default: return 0; + default: return MA_INVALID_OPERATION; /* Unknown waveform type. */ } } else { pWaveform->time += pWaveform->advance * (ma_int64)frameCount; /* Cast to int64 required for VC6. Won't affect anything in practice. */ } - return frameCount; + if (pFramesRead != NULL) { + *pFramesRead = frameCount; + } + + return MA_SUCCESS; } MA_API ma_result ma_waveform_seek_to_pcm_frame(ma_waveform* pWaveform, ma_uint64 frameIndex) @@ -52367,17 +63448,7 @@ MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels static ma_result ma_noise__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { - ma_uint64 framesRead = ma_noise_read_pcm_frames((ma_noise*)pDataSource, pFramesOut, frameCount); - - if (pFramesRead != NULL) { - *pFramesRead = framesRead; - } - - if (framesRead == 0) { - return MA_AT_END; - } - - return MA_SUCCESS; + return ma_noise_read_pcm_frames((ma_noise*)pDataSource, pFramesOut, frameCount, pFramesRead); } static ma_result ma_noise__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) @@ -52388,13 +63459,14 @@ static ma_result ma_noise__data_source_on_seek(ma_data_source* pDataSource, ma_u return MA_SUCCESS; } -static ma_result ma_noise__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate) +static ma_result ma_noise__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) { ma_noise* pNoise = (ma_noise*)pDataSource; *pFormat = pNoise->config.format; *pChannels = pNoise->config.channels; *pSampleRate = 0; /* There is no notion of sample rate with noise generation. */ + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pNoise->config.channels); return MA_SUCCESS; } @@ -52403,17 +63475,105 @@ static ma_data_source_vtable g_ma_noise_data_source_vtable = { ma_noise__data_source_on_read, ma_noise__data_source_on_seek, /* No-op for noise. */ - NULL, /* onMap */ - NULL, /* onUnmap */ ma_noise__data_source_on_get_data_format, NULL, /* onGetCursor. No notion of a cursor for noise. */ - NULL /* onGetLength. No notion of a length for noise. */ + NULL, /* onGetLength. No notion of a length for noise. */ + NULL, /* onSetLooping */ + 0 }; -MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, ma_noise* pNoise) + +#ifndef MA_PINK_NOISE_BIN_SIZE +#define MA_PINK_NOISE_BIN_SIZE 16 +#endif + +typedef struct +{ + size_t sizeInBytes; + struct + { + size_t binOffset; + size_t accumulationOffset; + size_t counterOffset; + } pink; + struct + { + size_t accumulationOffset; + } brownian; +} ma_noise_heap_layout; + +static ma_result ma_noise_get_heap_layout(const ma_noise_config* pConfig, ma_noise_heap_layout* pHeapLayout) +{ + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->channels == 0) { + return MA_INVALID_ARGS; + } + + pHeapLayout->sizeInBytes = 0; + + /* Pink. */ + if (pConfig->type == ma_noise_type_pink) { + /* bin */ + pHeapLayout->pink.binOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += sizeof(double*) * pConfig->channels; + pHeapLayout->sizeInBytes += sizeof(double ) * pConfig->channels * MA_PINK_NOISE_BIN_SIZE; + + /* accumulation */ + pHeapLayout->pink.accumulationOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += sizeof(double) * pConfig->channels; + + /* counter */ + pHeapLayout->pink.counterOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += sizeof(ma_uint32) * pConfig->channels; + } + + /* Brownian. */ + if (pConfig->type == ma_noise_type_brownian) { + /* accumulation */ + pHeapLayout->brownian.accumulationOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += sizeof(double) * pConfig->channels; + } + + /* Make sure allocation size is aligned. */ + pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); + + return MA_SUCCESS; +} + +MA_API ma_result ma_noise_get_heap_size(const ma_noise_config* pConfig, size_t* pHeapSizeInBytes) { ma_result result; + ma_noise_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_noise_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return MA_SUCCESS; +} + +MA_API ma_result ma_noise_init_preallocated(const ma_noise_config* pConfig, void* pHeap, ma_noise* pNoise) +{ + ma_result result; + ma_noise_heap_layout heapLayout; ma_data_source_config dataSourceConfig; + ma_uint32 iChannel; if (pNoise == NULL) { return MA_INVALID_ARGS; @@ -52421,13 +63581,13 @@ MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, ma_noise* pNoise) MA_ZERO_OBJECT(pNoise); - if (pConfig == NULL) { - return MA_INVALID_ARGS; + result = ma_noise_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; } - if (pConfig->channels < MA_MIN_CHANNELS || pConfig->channels > MA_MAX_CHANNELS) { - return MA_INVALID_ARGS; - } + pNoise->_pHeap = pHeap; + MA_ZERO_MEMORY(pNoise->_pHeap, heapLayout.sizeInBytes); dataSourceConfig = ma_data_source_config_init(); dataSourceConfig.vtable = &g_ma_noise_data_source_vtable; @@ -52441,15 +63601,20 @@ MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, ma_noise* pNoise) ma_lcg_seed(&pNoise->lcg, pConfig->seed); if (pNoise->config.type == ma_noise_type_pink) { - ma_uint32 iChannel; + pNoise->state.pink.bin = (double** )ma_offset_ptr(pHeap, heapLayout.pink.binOffset); + pNoise->state.pink.accumulation = (double* )ma_offset_ptr(pHeap, heapLayout.pink.accumulationOffset); + pNoise->state.pink.counter = (ma_uint32*)ma_offset_ptr(pHeap, heapLayout.pink.counterOffset); + for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) { + pNoise->state.pink.bin[iChannel] = (double*)ma_offset_ptr(pHeap, heapLayout.pink.binOffset + (sizeof(double*) * pConfig->channels) + (sizeof(double) * MA_PINK_NOISE_BIN_SIZE * iChannel)); pNoise->state.pink.accumulation[iChannel] = 0; pNoise->state.pink.counter[iChannel] = 1; } } if (pNoise->config.type == ma_noise_type_brownian) { - ma_uint32 iChannel; + pNoise->state.brownian.accumulation = (double*)ma_offset_ptr(pHeap, heapLayout.brownian.accumulationOffset); + for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) { pNoise->state.brownian.accumulation[iChannel] = 0; } @@ -52458,13 +63623,47 @@ MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, ma_noise* pNoise) return MA_SUCCESS; } -MA_API void ma_noise_uninit(ma_noise* pNoise) +MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_noise* pNoise) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_noise_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_noise_init_preallocated(pConfig, pHeap, pNoise); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pNoise->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_noise_uninit(ma_noise* pNoise, const ma_allocation_callbacks* pAllocationCallbacks) { if (pNoise == NULL) { return; } ma_data_source_uninit(&pNoise->ds); + + if (pNoise->_ownsHeap) { + ma_free(pNoise->_pHeap, pAllocationCallbacks); + } } MA_API ma_result ma_noise_set_amplitude(ma_noise* pNoise, double amplitude) @@ -52513,7 +63712,7 @@ static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__white(ma_noise* pNoise, voi ma_uint64 iFrame; ma_uint32 iChannel; const ma_uint32 channels = pNoise->config.channels; - MA_ASSUME(channels >= MA_MIN_CHANNELS && channels <= MA_MAX_CHANNELS); + MA_ASSUME(channels > 0); if (pNoise->config.format == ma_format_f32) { float* pFramesOutF32 = (float*)pFramesOut; @@ -52607,7 +63806,7 @@ static MA_INLINE float ma_noise_f32_pink(ma_noise* pNoise, ma_uint32 iChannel) double binNext; unsigned int ibin; - ibin = ma_tzcnt32(pNoise->state.pink.counter[iChannel]) & (ma_countof(pNoise->state.pink.bin[0]) - 1); + ibin = ma_tzcnt32(pNoise->state.pink.counter[iChannel]) & (MA_PINK_NOISE_BIN_SIZE - 1); binPrev = pNoise->state.pink.bin[iChannel][ibin]; binNext = ma_lcg_rand_f64(&pNoise->lcg); @@ -52632,7 +63831,7 @@ static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__pink(ma_noise* pNoise, void ma_uint64 iFrame; ma_uint32 iChannel; const ma_uint32 channels = pNoise->config.channels; - MA_ASSUME(channels >= MA_MIN_CHANNELS && channels <= MA_MAX_CHANNELS); + MA_ASSUME(channels > 0); if (pNoise->config.format == ma_format_f32) { float* pFramesOutF32 = (float*)pFramesOut; @@ -52714,7 +63913,7 @@ static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__brownian(ma_noise* pNoise, ma_uint64 iFrame; ma_uint32 iChannel; const ma_uint32 channels = pNoise->config.channels; - MA_ASSUME(channels >= MA_MIN_CHANNELS && channels <= MA_MAX_CHANNELS); + MA_ASSUME(channels > 0); if (pNoise->config.format == ma_format_f32) { float* pFramesOutF32 = (float*)pFramesOut; @@ -52772,37 +63971,9798 @@ static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__brownian(ma_noise* pNoise, return frameCount; } -MA_API ma_uint64 ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount) +MA_API ma_result ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { + ma_uint64 framesRead = 0; + + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + if (pNoise == NULL) { - return 0; + return MA_INVALID_ARGS; } /* The output buffer is allowed to be NULL. Since we aren't tracking cursors or anything we can just do nothing and pretend to be successful. */ if (pFramesOut == NULL) { - return frameCount; + framesRead = frameCount; + } else { + switch (pNoise->config.type) { + case ma_noise_type_white: framesRead = ma_noise_read_pcm_frames__white (pNoise, pFramesOut, frameCount); break; + case ma_noise_type_pink: framesRead = ma_noise_read_pcm_frames__pink (pNoise, pFramesOut, frameCount); break; + case ma_noise_type_brownian: framesRead = ma_noise_read_pcm_frames__brownian(pNoise, pFramesOut, frameCount); break; + default: return MA_INVALID_OPERATION; /* Unknown noise type. */ + } } - if (pNoise->config.type == ma_noise_type_white) { - return ma_noise_read_pcm_frames__white(pNoise, pFramesOut, frameCount); + if (pFramesRead != NULL) { + *pFramesRead = framesRead; } - if (pNoise->config.type == ma_noise_type_pink) { - return ma_noise_read_pcm_frames__pink(pNoise, pFramesOut, frameCount); - } - - if (pNoise->config.type == ma_noise_type_brownian) { - return ma_noise_read_pcm_frames__brownian(pNoise, pFramesOut, frameCount); - } - - /* Should never get here. */ - MA_ASSERT(MA_FALSE); - return 0; + return MA_SUCCESS; } #endif /* MA_NO_GENERATION */ +#ifndef MA_NO_RESOURCE_MANAGER +#ifndef MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS +#define MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS 1000 +#endif + +#ifndef MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY +#define MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY 1024 +#endif + +MA_API ma_resource_manager_pipeline_notifications ma_resource_manager_pipeline_notifications_init(void) +{ + ma_resource_manager_pipeline_notifications notifications; + + MA_ZERO_OBJECT(¬ifications); + + return notifications; +} + +static void ma_resource_manager_pipeline_notifications_signal_all_notifications(const ma_resource_manager_pipeline_notifications* pPipelineNotifications) +{ + if (pPipelineNotifications == NULL) { + return; + } + + if (pPipelineNotifications->init.pNotification) { ma_async_notification_signal(pPipelineNotifications->init.pNotification); } + if (pPipelineNotifications->done.pNotification) { ma_async_notification_signal(pPipelineNotifications->done.pNotification); } +} + +static void ma_resource_manager_pipeline_notifications_acquire_all_fences(const ma_resource_manager_pipeline_notifications* pPipelineNotifications) +{ + if (pPipelineNotifications == NULL) { + return; + } + + if (pPipelineNotifications->init.pFence != NULL) { ma_fence_acquire(pPipelineNotifications->init.pFence); } + if (pPipelineNotifications->done.pFence != NULL) { ma_fence_acquire(pPipelineNotifications->done.pFence); } +} + +static void ma_resource_manager_pipeline_notifications_release_all_fences(const ma_resource_manager_pipeline_notifications* pPipelineNotifications) +{ + if (pPipelineNotifications == NULL) { + return; + } + + if (pPipelineNotifications->init.pFence != NULL) { ma_fence_release(pPipelineNotifications->init.pFence); } + if (pPipelineNotifications->done.pFence != NULL) { ma_fence_release(pPipelineNotifications->done.pFence); } +} + + + +#ifndef MA_DEFAULT_HASH_SEED +#define MA_DEFAULT_HASH_SEED 42 +#endif + +/* MurmurHash3. Based on code from https://github.com/PeterScott/murmur3/blob/master/murmur3.c (public domain). */ +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic push + #if __GNUC__ >= 7 + #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" + #endif +#endif + +static MA_INLINE ma_uint32 ma_rotl32(ma_uint32 x, ma_int8 r) +{ + return (x << r) | (x >> (32 - r)); +} + +static MA_INLINE ma_uint32 ma_hash_getblock(const ma_uint32* blocks, int i) +{ + if (ma_is_little_endian()) { + return blocks[i]; + } else { + return ma_swap_endian_uint32(blocks[i]); + } +} + +static MA_INLINE ma_uint32 ma_hash_fmix32(ma_uint32 h) +{ + h ^= h >> 16; + h *= 0x85ebca6b; + h ^= h >> 13; + h *= 0xc2b2ae35; + h ^= h >> 16; + + return h; +} + +static ma_uint32 ma_hash_32(const void* key, int len, ma_uint32 seed) +{ + const ma_uint8* data = (const ma_uint8*)key; + const ma_uint32* blocks; + const ma_uint8* tail; + const int nblocks = len / 4; + ma_uint32 h1 = seed; + ma_uint32 c1 = 0xcc9e2d51; + ma_uint32 c2 = 0x1b873593; + ma_uint32 k1; + int i; + + blocks = (const ma_uint32 *)(data + nblocks*4); + + for(i = -nblocks; i; i++) { + k1 = ma_hash_getblock(blocks,i); + + k1 *= c1; + k1 = ma_rotl32(k1, 15); + k1 *= c2; + + h1 ^= k1; + h1 = ma_rotl32(h1, 13); + h1 = h1*5 + 0xe6546b64; + } + + + tail = (const ma_uint8*)(data + nblocks*4); + + k1 = 0; + switch(len & 3) { + case 3: k1 ^= tail[2] << 16; + case 2: k1 ^= tail[1] << 8; + case 1: k1 ^= tail[0]; + k1 *= c1; k1 = ma_rotl32(k1, 15); k1 *= c2; h1 ^= k1; + }; + + + h1 ^= len; + h1 = ma_hash_fmix32(h1); + + return h1; +} + +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic push +#endif +/* End MurmurHash3 */ + +static ma_uint32 ma_hash_string_32(const char* str) +{ + return ma_hash_32(str, (int)strlen(str), MA_DEFAULT_HASH_SEED); +} + +static ma_uint32 ma_hash_string_w_32(const wchar_t* str) +{ + return ma_hash_32(str, (int)wcslen(str) * sizeof(*str), MA_DEFAULT_HASH_SEED); +} + + + + +/* +Basic BST Functions +*/ +static ma_result ma_resource_manager_data_buffer_node_search(ma_resource_manager* pResourceManager, ma_uint32 hashedName32, ma_resource_manager_data_buffer_node** ppDataBufferNode) +{ + ma_resource_manager_data_buffer_node* pCurrentNode; + + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(ppDataBufferNode != NULL); + + pCurrentNode = pResourceManager->pRootDataBufferNode; + while (pCurrentNode != NULL) { + if (hashedName32 == pCurrentNode->hashedName32) { + break; /* Found. */ + } else if (hashedName32 < pCurrentNode->hashedName32) { + pCurrentNode = pCurrentNode->pChildLo; + } else { + pCurrentNode = pCurrentNode->pChildHi; + } + } + + *ppDataBufferNode = pCurrentNode; + + if (pCurrentNode == NULL) { + return MA_DOES_NOT_EXIST; + } else { + return MA_SUCCESS; + } +} + +static ma_result ma_resource_manager_data_buffer_node_insert_point(ma_resource_manager* pResourceManager, ma_uint32 hashedName32, ma_resource_manager_data_buffer_node** ppInsertPoint) +{ + ma_result result = MA_SUCCESS; + ma_resource_manager_data_buffer_node* pCurrentNode; + + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(ppInsertPoint != NULL); + + *ppInsertPoint = NULL; + + if (pResourceManager->pRootDataBufferNode == NULL) { + return MA_SUCCESS; /* No items. */ + } + + /* We need to find the node that will become the parent of the new node. If a node is found that already has the same hashed name we need to return MA_ALREADY_EXISTS. */ + pCurrentNode = pResourceManager->pRootDataBufferNode; + while (pCurrentNode != NULL) { + if (hashedName32 == pCurrentNode->hashedName32) { + result = MA_ALREADY_EXISTS; + break; + } else { + if (hashedName32 < pCurrentNode->hashedName32) { + if (pCurrentNode->pChildLo == NULL) { + result = MA_SUCCESS; + break; + } else { + pCurrentNode = pCurrentNode->pChildLo; + } + } else { + if (pCurrentNode->pChildHi == NULL) { + result = MA_SUCCESS; + break; + } else { + pCurrentNode = pCurrentNode->pChildHi; + } + } + } + } + + *ppInsertPoint = pCurrentNode; + return result; +} + +static ma_result ma_resource_manager_data_buffer_node_insert_at(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_resource_manager_data_buffer_node* pInsertPoint) +{ + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(pDataBufferNode != NULL); + + /* The key must have been set before calling this function. */ + MA_ASSERT(pDataBufferNode->hashedName32 != 0); + + if (pInsertPoint == NULL) { + /* It's the first node. */ + pResourceManager->pRootDataBufferNode = pDataBufferNode; + } else { + /* It's not the first node. It needs to be inserted. */ + if (pDataBufferNode->hashedName32 < pInsertPoint->hashedName32) { + MA_ASSERT(pInsertPoint->pChildLo == NULL); + pInsertPoint->pChildLo = pDataBufferNode; + } else { + MA_ASSERT(pInsertPoint->pChildHi == NULL); + pInsertPoint->pChildHi = pDataBufferNode; + } + } + + pDataBufferNode->pParent = pInsertPoint; + + return MA_SUCCESS; +} + +#if 0 /* Unused for now. */ +static ma_result ma_resource_manager_data_buffer_node_insert(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode) +{ + ma_result result; + ma_resource_manager_data_buffer_node* pInsertPoint; + + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(pDataBufferNode != NULL); + + result = ma_resource_manager_data_buffer_node_insert_point(pResourceManager, pDataBufferNode->hashedName32, &pInsertPoint); + if (result != MA_SUCCESS) { + return MA_INVALID_ARGS; + } + + return ma_resource_manager_data_buffer_node_insert_at(pResourceManager, pDataBufferNode, pInsertPoint); +} +#endif + +static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_min(ma_resource_manager_data_buffer_node* pDataBufferNode) +{ + ma_resource_manager_data_buffer_node* pCurrentNode; + + MA_ASSERT(pDataBufferNode != NULL); + + pCurrentNode = pDataBufferNode; + while (pCurrentNode->pChildLo != NULL) { + pCurrentNode = pCurrentNode->pChildLo; + } + + return pCurrentNode; +} + +static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_max(ma_resource_manager_data_buffer_node* pDataBufferNode) +{ + ma_resource_manager_data_buffer_node* pCurrentNode; + + MA_ASSERT(pDataBufferNode != NULL); + + pCurrentNode = pDataBufferNode; + while (pCurrentNode->pChildHi != NULL) { + pCurrentNode = pCurrentNode->pChildHi; + } + + return pCurrentNode; +} + +static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_inorder_successor(ma_resource_manager_data_buffer_node* pDataBufferNode) +{ + MA_ASSERT(pDataBufferNode != NULL); + MA_ASSERT(pDataBufferNode->pChildHi != NULL); + + return ma_resource_manager_data_buffer_node_find_min(pDataBufferNode->pChildHi); +} + +static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_inorder_predecessor(ma_resource_manager_data_buffer_node* pDataBufferNode) +{ + MA_ASSERT(pDataBufferNode != NULL); + MA_ASSERT(pDataBufferNode->pChildLo != NULL); + + return ma_resource_manager_data_buffer_node_find_max(pDataBufferNode->pChildLo); +} + +static ma_result ma_resource_manager_data_buffer_node_remove(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode) +{ + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(pDataBufferNode != NULL); + + if (pDataBufferNode->pChildLo == NULL) { + if (pDataBufferNode->pChildHi == NULL) { + /* Simple case - deleting a buffer with no children. */ + if (pDataBufferNode->pParent == NULL) { + MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode); /* There is only a single buffer in the tree which should be equal to the root node. */ + pResourceManager->pRootDataBufferNode = NULL; + } else { + if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { + pDataBufferNode->pParent->pChildLo = NULL; + } else { + pDataBufferNode->pParent->pChildHi = NULL; + } + } + } else { + /* Node has one child - pChildHi != NULL. */ + pDataBufferNode->pChildHi->pParent = pDataBufferNode->pParent; + + if (pDataBufferNode->pParent == NULL) { + MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode); + pResourceManager->pRootDataBufferNode = pDataBufferNode->pChildHi; + } else { + if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { + pDataBufferNode->pParent->pChildLo = pDataBufferNode->pChildHi; + } else { + pDataBufferNode->pParent->pChildHi = pDataBufferNode->pChildHi; + } + } + } + } else { + if (pDataBufferNode->pChildHi == NULL) { + /* Node has one child - pChildLo != NULL. */ + pDataBufferNode->pChildLo->pParent = pDataBufferNode->pParent; + + if (pDataBufferNode->pParent == NULL) { + MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode); + pResourceManager->pRootDataBufferNode = pDataBufferNode->pChildLo; + } else { + if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { + pDataBufferNode->pParent->pChildLo = pDataBufferNode->pChildLo; + } else { + pDataBufferNode->pParent->pChildHi = pDataBufferNode->pChildLo; + } + } + } else { + /* Complex case - deleting a node with two children. */ + ma_resource_manager_data_buffer_node* pReplacementDataBufferNode; + + /* For now we are just going to use the in-order successor as the replacement, but we may want to try to keep this balanced by switching between the two. */ + pReplacementDataBufferNode = ma_resource_manager_data_buffer_node_find_inorder_successor(pDataBufferNode); + MA_ASSERT(pReplacementDataBufferNode != NULL); + + /* + Now that we have our replacement node we can make the change. The simple way to do this would be to just exchange the values, and then remove the replacement + node, however we track specific nodes via pointers which means we can't just swap out the values. We need to instead just change the pointers around. The + replacement node should have at most 1 child. Therefore, we can detach it in terms of our simpler cases above. What we're essentially doing is detaching the + replacement node and reinserting it into the same position as the deleted node. + */ + MA_ASSERT(pReplacementDataBufferNode->pParent != NULL); /* The replacement node should never be the root which means it should always have a parent. */ + MA_ASSERT(pReplacementDataBufferNode->pChildLo == NULL); /* Because we used in-order successor. This would be pChildHi == NULL if we used in-order predecessor. */ + + if (pReplacementDataBufferNode->pChildHi == NULL) { + if (pReplacementDataBufferNode->pParent->pChildLo == pReplacementDataBufferNode) { + pReplacementDataBufferNode->pParent->pChildLo = NULL; + } else { + pReplacementDataBufferNode->pParent->pChildHi = NULL; + } + } else { + pReplacementDataBufferNode->pChildHi->pParent = pReplacementDataBufferNode->pParent; + if (pReplacementDataBufferNode->pParent->pChildLo == pReplacementDataBufferNode) { + pReplacementDataBufferNode->pParent->pChildLo = pReplacementDataBufferNode->pChildHi; + } else { + pReplacementDataBufferNode->pParent->pChildHi = pReplacementDataBufferNode->pChildHi; + } + } + + + /* The replacement node has essentially been detached from the binary tree, so now we need to replace the old data buffer with it. The first thing to update is the parent */ + if (pDataBufferNode->pParent != NULL) { + if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { + pDataBufferNode->pParent->pChildLo = pReplacementDataBufferNode; + } else { + pDataBufferNode->pParent->pChildHi = pReplacementDataBufferNode; + } + } + + /* Now need to update the replacement node's pointers. */ + pReplacementDataBufferNode->pParent = pDataBufferNode->pParent; + pReplacementDataBufferNode->pChildLo = pDataBufferNode->pChildLo; + pReplacementDataBufferNode->pChildHi = pDataBufferNode->pChildHi; + + /* Now the children of the replacement node need to have their parent pointers updated. */ + if (pReplacementDataBufferNode->pChildLo != NULL) { + pReplacementDataBufferNode->pChildLo->pParent = pReplacementDataBufferNode; + } + if (pReplacementDataBufferNode->pChildHi != NULL) { + pReplacementDataBufferNode->pChildHi->pParent = pReplacementDataBufferNode; + } + + /* Now the root node needs to be updated. */ + if (pResourceManager->pRootDataBufferNode == pDataBufferNode) { + pResourceManager->pRootDataBufferNode = pReplacementDataBufferNode; + } + } + } + + return MA_SUCCESS; +} + +#if 0 /* Unused for now. */ +static ma_result ma_resource_manager_data_buffer_node_remove_by_key(ma_resource_manager* pResourceManager, ma_uint32 hashedName32) +{ + ma_result result; + ma_resource_manager_data_buffer_node* pDataBufferNode; + + result = ma_resource_manager_data_buffer_search(pResourceManager, hashedName32, &pDataBufferNode); + if (result != MA_SUCCESS) { + return result; /* Could not find the data buffer. */ + } + + return ma_resource_manager_data_buffer_remove(pResourceManager, pDataBufferNode); +} +#endif + +static ma_resource_manager_data_supply_type ma_resource_manager_data_buffer_node_get_data_supply_type(ma_resource_manager_data_buffer_node* pDataBufferNode) +{ + return (ma_resource_manager_data_supply_type)c89atomic_load_i32(&pDataBufferNode->data.type); +} + +static void ma_resource_manager_data_buffer_node_set_data_supply_type(ma_resource_manager_data_buffer_node* pDataBufferNode, ma_resource_manager_data_supply_type supplyType) +{ + c89atomic_exchange_i32(&pDataBufferNode->data.type, supplyType); +} + +static ma_result ma_resource_manager_data_buffer_node_increment_ref(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_uint32* pNewRefCount) +{ + ma_uint32 refCount; + + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(pDataBufferNode != NULL); + + (void)pResourceManager; + + refCount = c89atomic_fetch_add_32(&pDataBufferNode->refCount, 1) + 1; + + if (pNewRefCount != NULL) { + *pNewRefCount = refCount; + } + + return MA_SUCCESS; +} + +static ma_result ma_resource_manager_data_buffer_node_decrement_ref(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_uint32* pNewRefCount) +{ + ma_uint32 refCount; + + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(pDataBufferNode != NULL); + + (void)pResourceManager; + + refCount = c89atomic_fetch_sub_32(&pDataBufferNode->refCount, 1) - 1; + + if (pNewRefCount != NULL) { + *pNewRefCount = refCount; + } + + return MA_SUCCESS; +} + +static void ma_resource_manager_data_buffer_node_free(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode) +{ + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(pDataBufferNode != NULL); + + if (pDataBufferNode->isDataOwnedByResourceManager) { + if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_encoded) { + ma_free((void*)pDataBufferNode->data.backend.encoded.pData, &pResourceManager->config.allocationCallbacks); + pDataBufferNode->data.backend.encoded.pData = NULL; + pDataBufferNode->data.backend.encoded.sizeInBytes = 0; + } else if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_decoded) { + ma_free((void*)pDataBufferNode->data.backend.decoded.pData, &pResourceManager->config.allocationCallbacks); + pDataBufferNode->data.backend.decoded.pData = NULL; + pDataBufferNode->data.backend.decoded.totalFrameCount = 0; + } else if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_decoded_paged) { + ma_paged_audio_buffer_data_uninit(&pDataBufferNode->data.backend.decodedPaged.data, &pResourceManager->config.allocationCallbacks); + } else { + /* Should never hit this if the node was successfully initialized. */ + MA_ASSERT(pDataBufferNode->result != MA_SUCCESS); + } + } + + /* The data buffer itself needs to be freed. */ + ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); +} + +static ma_result ma_resource_manager_data_buffer_node_result(const ma_resource_manager_data_buffer_node* pDataBufferNode) +{ + MA_ASSERT(pDataBufferNode != NULL); + + return (ma_result)c89atomic_load_i32((ma_result*)&pDataBufferNode->result); /* Need a naughty const-cast here. */ +} + + +static ma_bool32 ma_resource_manager_is_threading_enabled(const ma_resource_manager* pResourceManager) +{ + MA_ASSERT(pResourceManager != NULL); + + return (pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) == 0; +} + + +typedef struct +{ + union + { + ma_async_notification_event e; + ma_async_notification_poll p; + } backend; /* Must be the first member. */ + ma_resource_manager* pResourceManager; +} ma_resource_manager_inline_notification; + +static ma_result ma_resource_manager_inline_notification_init(ma_resource_manager* pResourceManager, ma_resource_manager_inline_notification* pNotification) +{ + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(pNotification != NULL); + + pNotification->pResourceManager = pResourceManager; + + if (ma_resource_manager_is_threading_enabled(pResourceManager)) { + return ma_async_notification_event_init(&pNotification->backend.e); + } else { + return ma_async_notification_poll_init(&pNotification->backend.p); + } +} + +static void ma_resource_manager_inline_notification_uninit(ma_resource_manager_inline_notification* pNotification) +{ + MA_ASSERT(pNotification != NULL); + + if (ma_resource_manager_is_threading_enabled(pNotification->pResourceManager)) { + ma_async_notification_event_uninit(&pNotification->backend.e); + } else { + /* No need to uninitialize a polling notification. */ + } +} + +static void ma_resource_manager_inline_notification_wait(ma_resource_manager_inline_notification* pNotification) +{ + MA_ASSERT(pNotification != NULL); + + if (ma_resource_manager_is_threading_enabled(pNotification->pResourceManager)) { + ma_async_notification_event_wait(&pNotification->backend.e); + } else { + while (ma_async_notification_poll_is_signalled(&pNotification->backend.p) == MA_FALSE) { + ma_result result = ma_resource_manager_process_next_job(pNotification->pResourceManager); + if (result == MA_NO_DATA_AVAILABLE || result == MA_CANCELLED) { + break; + } + } + } +} + +static void ma_resource_manager_inline_notification_wait_and_uninit(ma_resource_manager_inline_notification* pNotification) +{ + ma_resource_manager_inline_notification_wait(pNotification); + ma_resource_manager_inline_notification_uninit(pNotification); +} + + +static void ma_resource_manager_data_buffer_bst_lock(ma_resource_manager* pResourceManager) +{ + MA_ASSERT(pResourceManager != NULL); + + if (ma_resource_manager_is_threading_enabled(pResourceManager)) { + #ifndef MA_NO_THREADING + { + ma_mutex_lock(&pResourceManager->dataBufferBSTLock); + } + #else + { + MA_ASSERT(MA_FALSE); /* Should never hit this. */ + } + #endif + } else { + /* Threading not enabled. Do nothing. */ + } +} + +static void ma_resource_manager_data_buffer_bst_unlock(ma_resource_manager* pResourceManager) +{ + MA_ASSERT(pResourceManager != NULL); + + if (ma_resource_manager_is_threading_enabled(pResourceManager)) { + #ifndef MA_NO_THREADING + { + ma_mutex_unlock(&pResourceManager->dataBufferBSTLock); + } + #else + { + MA_ASSERT(MA_FALSE); /* Should never hit this. */ + } + #endif + } else { + /* Threading not enabled. Do nothing. */ + } +} + +#ifndef MA_NO_THREADING +static ma_thread_result MA_THREADCALL ma_resource_manager_job_thread(void* pUserData) +{ + ma_resource_manager* pResourceManager = (ma_resource_manager*)pUserData; + MA_ASSERT(pResourceManager != NULL); + + for (;;) { + ma_result result; + ma_job job; + + result = ma_resource_manager_next_job(pResourceManager, &job); + if (result != MA_SUCCESS) { + break; + } + + /* Terminate if we got a quit message. */ + if (job.toc.breakup.code == MA_JOB_TYPE_QUIT) { + break; + } + + ma_job_process(&job); + } + + return (ma_thread_result)0; +} +#endif + +MA_API ma_resource_manager_config ma_resource_manager_config_init(void) +{ + ma_resource_manager_config config; + + MA_ZERO_OBJECT(&config); + config.decodedFormat = ma_format_unknown; + config.decodedChannels = 0; + config.decodedSampleRate = 0; + config.jobThreadCount = 1; /* A single miniaudio-managed job thread by default. */ + config.jobQueueCapacity = MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY; + + /* Flags. */ + config.flags = 0; + #ifdef MA_NO_THREADING + { + /* Threading is disabled at compile time so disable threading at runtime as well by default. */ + config.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING; + config.jobThreadCount = 0; + } + #endif + + return config; +} + + +MA_API ma_result ma_resource_manager_init(const ma_resource_manager_config* pConfig, ma_resource_manager* pResourceManager) +{ + ma_result result; + ma_job_queue_config jobQueueConfig; + + if (pResourceManager == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pResourceManager); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + #ifndef MA_NO_THREADING + { + if (pConfig->jobThreadCount > ma_countof(pResourceManager->jobThreads)) { + return MA_INVALID_ARGS; /* Requesting too many job threads. */ + } + } + #endif + + pResourceManager->config = *pConfig; + ma_allocation_callbacks_init_copy(&pResourceManager->config.allocationCallbacks, &pConfig->allocationCallbacks); + + /* Get the log set up early so we can start using it as soon as possible. */ + if (pResourceManager->config.pLog == NULL) { + result = ma_log_init(&pResourceManager->config.allocationCallbacks, &pResourceManager->log); + if (result == MA_SUCCESS) { + pResourceManager->config.pLog = &pResourceManager->log; + } else { + pResourceManager->config.pLog = NULL; /* Logging is unavailable. */ + } + } + + if (pResourceManager->config.pVFS == NULL) { + result = ma_default_vfs_init(&pResourceManager->defaultVFS, &pResourceManager->config.allocationCallbacks); + if (result != MA_SUCCESS) { + return result; /* Failed to initialize the default file system. */ + } + + pResourceManager->config.pVFS = &pResourceManager->defaultVFS; + } + + /* If threading has been disabled at compile time, enfore it at run time as well. */ + #ifdef MA_NO_THREADING + { + pResourceManager->config.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING; + } + #endif + + /* We need to force MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING if MA_RESOURCE_MANAGER_FLAG_NO_THREADING is set. */ + if ((pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) != 0) { + pResourceManager->config.flags |= MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING; + + /* We cannot allow job threads when MA_RESOURCE_MANAGER_FLAG_NO_THREADING has been set. This is an invalid use case. */ + if (pResourceManager->config.jobThreadCount > 0) { + return MA_INVALID_ARGS; + } + } + + /* Job queue. */ + jobQueueConfig.capacity = pResourceManager->config.jobQueueCapacity; + jobQueueConfig.flags = 0; + if ((pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING) != 0) { + if (pResourceManager->config.jobThreadCount > 0) { + return MA_INVALID_ARGS; /* Non-blocking mode is only valid for self-managed job threads. */ + } + + jobQueueConfig.flags |= MA_JOB_QUEUE_FLAG_NON_BLOCKING; + } + + result = ma_job_queue_init(&jobQueueConfig, &pResourceManager->config.allocationCallbacks, &pResourceManager->jobQueue); + if (result != MA_SUCCESS) { + return result; + } + + + /* Custom decoding backends. */ + if (pConfig->ppCustomDecodingBackendVTables != NULL && pConfig->customDecodingBackendCount > 0) { + size_t sizeInBytes = sizeof(*pResourceManager->config.ppCustomDecodingBackendVTables) * pConfig->customDecodingBackendCount; + + pResourceManager->config.ppCustomDecodingBackendVTables = (ma_decoding_backend_vtable**)ma_malloc(sizeInBytes, &pResourceManager->config.allocationCallbacks); + if (pResourceManager->config.ppCustomDecodingBackendVTables == NULL) { + ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); + return MA_OUT_OF_MEMORY; + } + + MA_COPY_MEMORY(pResourceManager->config.ppCustomDecodingBackendVTables, pConfig->ppCustomDecodingBackendVTables, sizeInBytes); + + pResourceManager->config.customDecodingBackendCount = pConfig->customDecodingBackendCount; + pResourceManager->config.pCustomDecodingBackendUserData = pConfig->pCustomDecodingBackendUserData; + } + + + + /* Here is where we initialize our threading stuff. We don't do this if we don't support threading. */ + if (ma_resource_manager_is_threading_enabled(pResourceManager)) { + #ifndef MA_NO_THREADING + { + ma_uint32 iJobThread; + + /* Data buffer lock. */ + result = ma_mutex_init(&pResourceManager->dataBufferBSTLock); + if (result != MA_SUCCESS) { + ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); + return result; + } + + /* Create the job threads last to ensure the threads has access to valid data. */ + for (iJobThread = 0; iJobThread < pResourceManager->config.jobThreadCount; iJobThread += 1) { + result = ma_thread_create(&pResourceManager->jobThreads[iJobThread], ma_thread_priority_normal, 0, ma_resource_manager_job_thread, pResourceManager, &pResourceManager->config.allocationCallbacks); + if (result != MA_SUCCESS) { + ma_mutex_uninit(&pResourceManager->dataBufferBSTLock); + ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); + return result; + } + } + } + #else + { + /* Threading is disabled at compile time. We should never get here because validation checks should have already been performed. */ + MA_ASSERT(MA_FALSE); + } + #endif + } + + return MA_SUCCESS; +} + + +static void ma_resource_manager_delete_all_data_buffer_nodes(ma_resource_manager* pResourceManager) +{ + MA_ASSERT(pResourceManager); + + /* If everything was done properly, there shouldn't be any active data buffers. */ + while (pResourceManager->pRootDataBufferNode != NULL) { + ma_resource_manager_data_buffer_node* pDataBufferNode = pResourceManager->pRootDataBufferNode; + ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); + + /* The data buffer has been removed from the BST, so now we need to free it's data. */ + ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode); + } +} + +MA_API void ma_resource_manager_uninit(ma_resource_manager* pResourceManager) +{ + if (pResourceManager == NULL) { + return; + } + + /* + Job threads need to be killed first. To do this we need to post a quit message to the message queue and then wait for the thread. The quit message will never be removed from the + queue which means it will never not be returned after being encounted for the first time which means all threads will eventually receive it. + */ + ma_resource_manager_post_job_quit(pResourceManager); + + /* Wait for every job to finish before continuing to ensure nothing is sill trying to access any of our objects below. */ + if (ma_resource_manager_is_threading_enabled(pResourceManager)) { + #ifndef MA_NO_THREADING + { + ma_uint32 iJobThread; + + for (iJobThread = 0; iJobThread < pResourceManager->config.jobThreadCount; iJobThread += 1) { + ma_thread_wait(&pResourceManager->jobThreads[iJobThread]); + } + } + #else + { + MA_ASSERT(MA_FALSE); /* Should never hit this. */ + } + #endif + } + + /* At this point the thread should have returned and no other thread should be accessing our data. We can now delete all data buffers. */ + ma_resource_manager_delete_all_data_buffer_nodes(pResourceManager); + + /* The job queue is no longer needed. */ + ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); + + /* We're no longer doing anything with data buffers so the lock can now be uninitialized. */ + if (ma_resource_manager_is_threading_enabled(pResourceManager)) { + #ifndef MA_NO_THREADING + { + ma_mutex_uninit(&pResourceManager->dataBufferBSTLock); + } + #else + { + MA_ASSERT(MA_FALSE); /* Should never hit this. */ + } + #endif + } + + ma_free(pResourceManager->config.ppCustomDecodingBackendVTables, &pResourceManager->config.allocationCallbacks); + + if (pResourceManager->config.pLog == &pResourceManager->log) { + ma_log_uninit(&pResourceManager->log); + } +} + +MA_API ma_log* ma_resource_manager_get_log(ma_resource_manager* pResourceManager) +{ + if (pResourceManager == NULL) { + return NULL; + } + + return pResourceManager->config.pLog; +} + + + +MA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_config_init(void) +{ + ma_resource_manager_data_source_config config; + + MA_ZERO_OBJECT(&config); + config.rangeEndInPCMFrames = ~((ma_uint64)0); + config.loopPointEndInPCMFrames = ~((ma_uint64)0); + + return config; +} + + +static ma_decoder_config ma_resource_manager__init_decoder_config(ma_resource_manager* pResourceManager) +{ + ma_decoder_config config; + + config = ma_decoder_config_init(pResourceManager->config.decodedFormat, pResourceManager->config.decodedChannels, pResourceManager->config.decodedSampleRate); + config.allocationCallbacks = pResourceManager->config.allocationCallbacks; + config.ppCustomBackendVTables = pResourceManager->config.ppCustomDecodingBackendVTables; + config.customBackendCount = pResourceManager->config.customDecodingBackendCount; + config.pCustomBackendUserData = pResourceManager->config.pCustomDecodingBackendUserData; + + return config; +} + +static ma_result ma_resource_manager__init_decoder(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_decoder* pDecoder) +{ + ma_result result; + ma_decoder_config config; + + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(pFilePath != NULL || pFilePathW != NULL); + MA_ASSERT(pDecoder != NULL); + + config = ma_resource_manager__init_decoder_config(pResourceManager); + + if (pFilePath != NULL) { + result = ma_decoder_init_vfs(pResourceManager->config.pVFS, pFilePath, &config, pDecoder); + if (result != MA_SUCCESS) { + ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%s\". %s.\n", pFilePath, ma_result_description(result)); + return result; + } + } else { + result = ma_decoder_init_vfs_w(pResourceManager->config.pVFS, pFilePathW, &config, pDecoder); + if (result != MA_SUCCESS) { + #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER) + ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%ls\". %s.\n", pFilePathW, ma_result_description(result)); + #endif + return result; + } + } + + return MA_SUCCESS; +} + +static ma_data_source* ma_resource_manager_data_buffer_get_connector(ma_resource_manager_data_buffer* pDataBuffer) +{ + switch (pDataBuffer->pNode->data.type) + { + case ma_resource_manager_data_supply_type_encoded: return &pDataBuffer->connector.decoder; + case ma_resource_manager_data_supply_type_decoded: return &pDataBuffer->connector.buffer; + case ma_resource_manager_data_supply_type_decoded_paged: return &pDataBuffer->connector.pagedBuffer; + + case ma_resource_manager_data_supply_type_unknown: + default: + { + ma_log_postf(ma_resource_manager_get_log(pDataBuffer->pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to retrieve data buffer connector. Unknown data supply type.\n"); + return NULL; + }; + }; +} + +static ma_result ma_resource_manager_data_buffer_init_connector(ma_resource_manager_data_buffer* pDataBuffer, const ma_resource_manager_data_source_config* pConfig, ma_async_notification* pInitNotification, ma_fence* pInitFence) +{ + ma_result result; + + MA_ASSERT(pDataBuffer != NULL); + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pDataBuffer->isConnectorInitialized == MA_FALSE); + + /* The underlying data buffer must be initialized before we'll be able to know how to initialize the backend. */ + result = ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode); + if (result != MA_SUCCESS && result != MA_BUSY) { + return result; /* The data buffer is in an erroneous state. */ + } + + /* + We need to initialize either a ma_decoder or an ma_audio_buffer depending on whether or not the backing data is encoded or decoded. These act as the + "instance" to the data and are used to form the connection between underlying data buffer and the data source. If the data buffer is decoded, we can use + an ma_audio_buffer. This enables us to use memory mapping when mixing which saves us a bit of data movement overhead. + */ + switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) + { + case ma_resource_manager_data_supply_type_encoded: /* Connector is a decoder. */ + { + ma_decoder_config config; + config = ma_resource_manager__init_decoder_config(pDataBuffer->pResourceManager); + result = ma_decoder_init_memory(pDataBuffer->pNode->data.backend.encoded.pData, pDataBuffer->pNode->data.backend.encoded.sizeInBytes, &config, &pDataBuffer->connector.decoder); + } break; + + case ma_resource_manager_data_supply_type_decoded: /* Connector is an audio buffer. */ + { + ma_audio_buffer_config config; + config = ma_audio_buffer_config_init(pDataBuffer->pNode->data.backend.decoded.format, pDataBuffer->pNode->data.backend.decoded.channels, pDataBuffer->pNode->data.backend.decoded.totalFrameCount, pDataBuffer->pNode->data.backend.decoded.pData, NULL); + result = ma_audio_buffer_init(&config, &pDataBuffer->connector.buffer); + } break; + + case ma_resource_manager_data_supply_type_decoded_paged: /* Connector is a paged audio buffer. */ + { + ma_paged_audio_buffer_config config; + config = ma_paged_audio_buffer_config_init(&pDataBuffer->pNode->data.backend.decodedPaged.data); + result = ma_paged_audio_buffer_init(&config, &pDataBuffer->connector.pagedBuffer); + } break; + + case ma_resource_manager_data_supply_type_unknown: + default: + { + /* Unknown data supply type. Should never happen. Need to post an error here. */ + return MA_INVALID_ARGS; + }; + } + + /* + Initialization of the connector is when we can fire the init notification. This will give the application access to + the format/channels/rate of the data source. + */ + if (result == MA_SUCCESS) { + /* + Make sure the looping state is set before returning in order to handle the case where the + loop state was set on the data buffer before the connector was initialized. + */ + ma_data_source_set_range_in_pcm_frames(pDataBuffer, pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames); + ma_data_source_set_loop_point_in_pcm_frames(pDataBuffer, pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); + ma_data_source_set_looping(pDataBuffer, pConfig->isLooping); + + pDataBuffer->isConnectorInitialized = MA_TRUE; + + if (pInitNotification != NULL) { + ma_async_notification_signal(pInitNotification); + } + + if (pInitFence != NULL) { + ma_fence_release(pInitFence); + } + } + + /* At this point the backend should be initialized. We do *not* want to set pDataSource->result here - that needs to be done at a higher level to ensure it's done as the last step. */ + return result; +} + +static ma_result ma_resource_manager_data_buffer_uninit_connector(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer* pDataBuffer) +{ + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(pDataBuffer != NULL); + + switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) + { + case ma_resource_manager_data_supply_type_encoded: /* Connector is a decoder. */ + { + ma_decoder_uninit(&pDataBuffer->connector.decoder); + } break; + + case ma_resource_manager_data_supply_type_decoded: /* Connector is an audio buffer. */ + { + ma_audio_buffer_uninit(&pDataBuffer->connector.buffer); + } break; + + case ma_resource_manager_data_supply_type_decoded_paged: /* Connector is a paged audio buffer. */ + { + ma_paged_audio_buffer_uninit(&pDataBuffer->connector.pagedBuffer); + } break; + + case ma_resource_manager_data_supply_type_unknown: + default: + { + /* Unknown data supply type. Should never happen. Need to post an error here. */ + return MA_INVALID_ARGS; + }; + } + + return MA_SUCCESS; +} + +static ma_uint32 ma_resource_manager_data_buffer_node_next_execution_order(ma_resource_manager_data_buffer_node* pDataBufferNode) +{ + MA_ASSERT(pDataBufferNode != NULL); + return c89atomic_fetch_add_32(&pDataBufferNode->executionCounter, 1); +} + +static ma_result ma_resource_manager_data_buffer_node_init_supply_encoded(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pFilePath, const wchar_t* pFilePathW) +{ + ma_result result; + size_t dataSizeInBytes; + void* pData; + + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(pDataBufferNode != NULL); + MA_ASSERT(pFilePath != NULL || pFilePathW != NULL); + + result = ma_vfs_open_and_read_file_ex(pResourceManager->config.pVFS, pFilePath, pFilePathW, &pData, &dataSizeInBytes, &pResourceManager->config.allocationCallbacks); + if (result != MA_SUCCESS) { + if (pFilePath != NULL) { + ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%s\". %s.\n", pFilePath, ma_result_description(result)); + } else { + #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER) + ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%ls\". %s.\n", pFilePathW, ma_result_description(result)); + #endif + } + + return result; + } + + pDataBufferNode->data.backend.encoded.pData = pData; + pDataBufferNode->data.backend.encoded.sizeInBytes = dataSizeInBytes; + ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_encoded); /* <-- Must be set last. */ + + return MA_SUCCESS; +} + +static ma_result ma_resource_manager_data_buffer_node_init_supply_decoded(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 flags, ma_decoder** ppDecoder) +{ + ma_result result = MA_SUCCESS; + ma_decoder* pDecoder; + ma_uint64 totalFrameCount; + + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(pDataBufferNode != NULL); + MA_ASSERT(ppDecoder != NULL); + MA_ASSERT(pFilePath != NULL || pFilePathW != NULL); + + *ppDecoder = NULL; /* For safety. */ + + pDecoder = (ma_decoder*)ma_malloc(sizeof(*pDecoder), &pResourceManager->config.allocationCallbacks); + if (pDecoder == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_resource_manager__init_decoder(pResourceManager, pFilePath, pFilePathW, pDecoder); + if (result != MA_SUCCESS) { + ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); + return result; + } + + /* + At this point we have the decoder and we now need to initialize the data supply. This will + be either a decoded buffer, or a decoded paged buffer. A regular buffer is just one big heap + allocated buffer, whereas a paged buffer is a linked list of paged-sized buffers. The latter + is used when the length of a sound is unknown until a full decode has been performed. + */ + if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH) == 0) { + result = ma_decoder_get_length_in_pcm_frames(pDecoder, &totalFrameCount); + if (result != MA_SUCCESS) { + return result; + } + } else { + totalFrameCount = 0; + } + + if (totalFrameCount > 0) { + /* It's a known length. The data supply is a regular decoded buffer. */ + ma_uint64 dataSizeInBytes; + void* pData; + + dataSizeInBytes = totalFrameCount * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels); + if (dataSizeInBytes > MA_SIZE_MAX) { + ma_decoder_uninit(pDecoder); + ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); + return MA_TOO_BIG; + } + + pData = ma_malloc((size_t)dataSizeInBytes, &pResourceManager->config.allocationCallbacks); + if (pData == NULL) { + ma_decoder_uninit(pDecoder); + ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); + return MA_OUT_OF_MEMORY; + } + + /* The buffer needs to be initialized to silence in case the caller reads from it. */ + ma_silence_pcm_frames(pData, totalFrameCount, pDecoder->outputFormat, pDecoder->outputChannels); + + /* Data has been allocated and the data supply can now be initialized. */ + pDataBufferNode->data.backend.decoded.pData = pData; + pDataBufferNode->data.backend.decoded.totalFrameCount = totalFrameCount; + pDataBufferNode->data.backend.decoded.format = pDecoder->outputFormat; + pDataBufferNode->data.backend.decoded.channels = pDecoder->outputChannels; + pDataBufferNode->data.backend.decoded.sampleRate = pDecoder->outputSampleRate; + pDataBufferNode->data.backend.decoded.decodedFrameCount = 0; + ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_decoded); /* <-- Must be set last. */ + } else { + /* + It's an unknown length. The data supply is a paged decoded buffer. Setting this up is + actually easier than the non-paged decoded buffer because we just need to initialize + a ma_paged_audio_buffer object. + */ + result = ma_paged_audio_buffer_data_init(pDecoder->outputFormat, pDecoder->outputChannels, &pDataBufferNode->data.backend.decodedPaged.data); + if (result != MA_SUCCESS) { + ma_decoder_uninit(pDecoder); + ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); + return result; + } + + pDataBufferNode->data.backend.decodedPaged.sampleRate = pDecoder->outputSampleRate; + pDataBufferNode->data.backend.decodedPaged.decodedFrameCount = 0; + ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_decoded_paged); /* <-- Must be set last. */ + } + + *ppDecoder = pDecoder; + + return MA_SUCCESS; +} + +static ma_result ma_resource_manager_data_buffer_node_decode_next_page(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_decoder* pDecoder) +{ + ma_result result = MA_SUCCESS; + ma_uint64 pageSizeInFrames; + ma_uint64 framesToTryReading; + ma_uint64 framesRead; + + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(pDataBufferNode != NULL); + MA_ASSERT(pDecoder != NULL); + + /* We need to know the size of a page in frames to know how many frames to decode. */ + pageSizeInFrames = MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS * (pDecoder->outputSampleRate/1000); + framesToTryReading = pageSizeInFrames; + + /* + Here is where we do the decoding of the next page. We'll run a slightly different path depending + on whether or not we're using a flat or paged buffer because the allocation of the page differs + between the two. For a flat buffer it's an offset to an already-allocated buffer. For a paged + buffer, we need to allocate a new page and attach it to the linked list. + */ + switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode)) + { + case ma_resource_manager_data_supply_type_decoded: + { + /* The destination buffer is an offset to the existing buffer. Don't read more than we originally retrieved when we first initialized the decoder. */ + void* pDst; + ma_uint64 framesRemaining = pDataBufferNode->data.backend.decoded.totalFrameCount - pDataBufferNode->data.backend.decoded.decodedFrameCount; + if (framesToTryReading > framesRemaining) { + framesToTryReading = framesRemaining; + } + + if (framesToTryReading > 0) { + pDst = ma_offset_ptr( + pDataBufferNode->data.backend.decoded.pData, + pDataBufferNode->data.backend.decoded.decodedFrameCount * ma_get_bytes_per_frame(pDataBufferNode->data.backend.decoded.format, pDataBufferNode->data.backend.decoded.channels) + ); + MA_ASSERT(pDst != NULL); + + result = ma_decoder_read_pcm_frames(pDecoder, pDst, framesToTryReading, &framesRead); + if (framesRead > 0) { + pDataBufferNode->data.backend.decoded.decodedFrameCount += framesRead; + } + } else { + framesRead = 0; + } + } break; + + case ma_resource_manager_data_supply_type_decoded_paged: + { + /* The destination buffer is a freshly allocated page. */ + ma_paged_audio_buffer_page* pPage; + + result = ma_paged_audio_buffer_data_allocate_page(&pDataBufferNode->data.backend.decodedPaged.data, framesToTryReading, NULL, &pResourceManager->config.allocationCallbacks, &pPage); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_decoder_read_pcm_frames(pDecoder, pPage->pAudioData, framesToTryReading, &framesRead); + if (framesRead > 0) { + pPage->sizeInFrames = framesRead; + + result = ma_paged_audio_buffer_data_append_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage); + if (result == MA_SUCCESS) { + pDataBufferNode->data.backend.decodedPaged.decodedFrameCount += framesRead; + } else { + /* Failed to append the page. Just abort and set the status to MA_AT_END. */ + ma_paged_audio_buffer_data_free_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage, &pResourceManager->config.allocationCallbacks); + result = MA_AT_END; + } + } else { + /* No frames were read. Free the page and just set the status to MA_AT_END. */ + ma_paged_audio_buffer_data_free_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage, &pResourceManager->config.allocationCallbacks); + result = MA_AT_END; + } + } break; + + case ma_resource_manager_data_supply_type_encoded: + case ma_resource_manager_data_supply_type_unknown: + default: + { + /* Unexpected data supply type. */ + ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Unexpected data supply type (%d) when decoding page.", ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode)); + return MA_ERROR; + }; + } + + if (result == MA_SUCCESS && framesRead == 0) { + result = MA_AT_END; + } + + return result; +} + +static ma_result ma_resource_manager_data_buffer_node_acquire_critical_section(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 hashedName32, ma_uint32 flags, const ma_resource_manager_data_supply* pExistingData, ma_fence* pInitFence, ma_fence* pDoneFence, ma_resource_manager_inline_notification* pInitNotification, ma_resource_manager_data_buffer_node** ppDataBufferNode) +{ + ma_result result = MA_SUCCESS; + ma_resource_manager_data_buffer_node* pDataBufferNode = NULL; + ma_resource_manager_data_buffer_node* pInsertPoint; + + if (ppDataBufferNode != NULL) { + *ppDataBufferNode = NULL; + } + + result = ma_resource_manager_data_buffer_node_insert_point(pResourceManager, hashedName32, &pInsertPoint); + if (result == MA_ALREADY_EXISTS) { + /* The node already exists. We just need to increment the reference count. */ + pDataBufferNode = pInsertPoint; + + result = ma_resource_manager_data_buffer_node_increment_ref(pResourceManager, pDataBufferNode, NULL); + if (result != MA_SUCCESS) { + return result; /* Should never happen. Failed to increment the reference count. */ + } + + result = MA_ALREADY_EXISTS; + goto done; + } else { + /* + The node does not already exist. We need to post a LOAD_DATA_BUFFER_NODE job here. This + needs to be done inside the critical section to ensure an uninitialization of the node + does not occur before initialization on another thread. + */ + pDataBufferNode = (ma_resource_manager_data_buffer_node*)ma_malloc(sizeof(*pDataBufferNode), &pResourceManager->config.allocationCallbacks); + if (pDataBufferNode == NULL) { + return MA_OUT_OF_MEMORY; + } + + MA_ZERO_OBJECT(pDataBufferNode); + pDataBufferNode->hashedName32 = hashedName32; + pDataBufferNode->refCount = 1; /* Always set to 1 by default (this is our first reference). */ + + if (pExistingData == NULL) { + pDataBufferNode->data.type = ma_resource_manager_data_supply_type_unknown; /* <-- We won't know this until we start decoding. */ + pDataBufferNode->result = MA_BUSY; /* Must be set to MA_BUSY before we leave the critical section, so might as well do it now. */ + pDataBufferNode->isDataOwnedByResourceManager = MA_TRUE; + } else { + pDataBufferNode->data = *pExistingData; + pDataBufferNode->result = MA_SUCCESS; /* Not loading asynchronously, so just set the status */ + pDataBufferNode->isDataOwnedByResourceManager = MA_FALSE; + } + + result = ma_resource_manager_data_buffer_node_insert_at(pResourceManager, pDataBufferNode, pInsertPoint); + if (result != MA_SUCCESS) { + ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); + return result; /* Should never happen. Failed to insert the data buffer into the BST. */ + } + + /* + Here is where we'll post the job, but only if we're loading asynchronously. If we're + loading synchronously we'll defer loading to a later stage, outside of the critical + section. + */ + if (pDataBufferNode->isDataOwnedByResourceManager && (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0) { + /* Loading asynchronously. Post the job. */ + ma_job job; + char* pFilePathCopy = NULL; + wchar_t* pFilePathWCopy = NULL; + + /* We need a copy of the file path. We should probably make this more efficient, but for now we'll do a transient memory allocation. */ + if (pFilePath != NULL) { + pFilePathCopy = ma_copy_string(pFilePath, &pResourceManager->config.allocationCallbacks); + } else { + pFilePathWCopy = ma_copy_string_w(pFilePathW, &pResourceManager->config.allocationCallbacks); + } + + if (pFilePathCopy == NULL && pFilePathWCopy == NULL) { + ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); + ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); + return MA_OUT_OF_MEMORY; + } + + if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { + ma_resource_manager_inline_notification_init(pResourceManager, pInitNotification); + } + + /* Acquire init and done fences before posting the job. These will be unacquired by the job thread. */ + if (pInitFence != NULL) { ma_fence_acquire(pInitFence); } + if (pDoneFence != NULL) { ma_fence_acquire(pDoneFence); } + + /* We now have everything we need to post the job to the job thread. */ + job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE); + job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); + job.data.resourceManager.loadDataBufferNode.pResourceManager = pResourceManager; + job.data.resourceManager.loadDataBufferNode.pDataBufferNode = pDataBufferNode; + job.data.resourceManager.loadDataBufferNode.pFilePath = pFilePathCopy; + job.data.resourceManager.loadDataBufferNode.pFilePathW = pFilePathWCopy; + job.data.resourceManager.loadDataBufferNode.flags = flags; + job.data.resourceManager.loadDataBufferNode.pInitNotification = ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? pInitNotification : NULL; + job.data.resourceManager.loadDataBufferNode.pDoneNotification = NULL; + job.data.resourceManager.loadDataBufferNode.pInitFence = pInitFence; + job.data.resourceManager.loadDataBufferNode.pDoneFence = pDoneFence; + + result = ma_resource_manager_post_job(pResourceManager, &job); + if (result != MA_SUCCESS) { + /* Failed to post job. Probably ran out of memory. */ + ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE job. %s.\n", ma_result_description(result)); + + /* + Fences were acquired before posting the job, but since the job was not able to + be posted, we need to make sure we release them so nothing gets stuck waiting. + */ + if (pInitFence != NULL) { ma_fence_release(pInitFence); } + if (pDoneFence != NULL) { ma_fence_release(pDoneFence); } + + if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { + ma_resource_manager_inline_notification_init(pResourceManager, pInitNotification); + } + + ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks); + ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks); + + ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); + ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); + + return result; + } + } + } + +done: + if (ppDataBufferNode != NULL) { + *ppDataBufferNode = pDataBufferNode; + } + + return result; +} + +static ma_result ma_resource_manager_data_buffer_node_acquire(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 hashedName32, ma_uint32 flags, const ma_resource_manager_data_supply* pExistingData, ma_fence* pInitFence, ma_fence* pDoneFence, ma_resource_manager_data_buffer_node** ppDataBufferNode) +{ + ma_result result = MA_SUCCESS; + ma_bool32 nodeAlreadyExists = MA_FALSE; + ma_resource_manager_data_buffer_node* pDataBufferNode = NULL; + ma_resource_manager_inline_notification initNotification; /* Used when the WAIT_INIT flag is set. */ + + if (ppDataBufferNode != NULL) { + *ppDataBufferNode = NULL; /* Safety. */ + } + + if (pResourceManager == NULL || (pFilePath == NULL && pFilePathW == NULL && hashedName32 == 0)) { + return MA_INVALID_ARGS; + } + + /* If we're specifying existing data, it must be valid. */ + if (pExistingData != NULL && pExistingData->type == ma_resource_manager_data_supply_type_unknown) { + return MA_INVALID_ARGS; + } + + /* If we don't support threading, remove the ASYNC flag to make the rest of this a bit simpler. */ + if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) { + flags &= ~MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC; + } + + if (hashedName32 == 0) { + if (pFilePath != NULL) { + hashedName32 = ma_hash_string_32(pFilePath); + } else { + hashedName32 = ma_hash_string_w_32(pFilePathW); + } + } + + /* + Here is where we either increment the node's reference count or allocate a new one and add it + to the BST. When allocating a new node, we need to make sure the LOAD_DATA_BUFFER_NODE job is + posted inside the critical section just in case the caller immediately uninitializes the node + as this will ensure the FREE_DATA_BUFFER_NODE job is given an execution order such that the + node is not uninitialized before initialization. + */ + ma_resource_manager_data_buffer_bst_lock(pResourceManager); + { + result = ma_resource_manager_data_buffer_node_acquire_critical_section(pResourceManager, pFilePath, pFilePathW, hashedName32, flags, pExistingData, pInitFence, pDoneFence, &initNotification, &pDataBufferNode); + } + ma_resource_manager_data_buffer_bst_unlock(pResourceManager); + + if (result == MA_ALREADY_EXISTS) { + nodeAlreadyExists = MA_TRUE; + result = MA_SUCCESS; + } else { + if (result != MA_SUCCESS) { + return result; + } + } + + /* + If we're loading synchronously, we'll need to load everything now. When loading asynchronously, + a job will have been posted inside the BST critical section so that an uninitialization can be + allocated an appropriate execution order thereby preventing it from being uninitialized before + the node is initialized by the decoding thread(s). + */ + if (nodeAlreadyExists == MA_FALSE) { /* Don't need to try loading anything if the node already exists. */ + if (pFilePath == NULL && pFilePathW == NULL) { + /* + If this path is hit, it means a buffer is being copied (i.e. initialized from only the + hashed name), but that node has been freed in the meantime, probably from some other + thread. This is an invalid operation. + */ + ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Cloning data buffer node failed because the source node was released. The source node must remain valid until the cloning has completed.\n"); + result = MA_INVALID_OPERATION; + goto done; + } + + if (pDataBufferNode->isDataOwnedByResourceManager) { + if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) == 0) { + /* Loading synchronously. Load the sound in it's entirety here. */ + if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE) == 0) { + /* No decoding. This is the simple case - just store the file contents in memory. */ + result = ma_resource_manager_data_buffer_node_init_supply_encoded(pResourceManager, pDataBufferNode, pFilePath, pFilePathW); + if (result != MA_SUCCESS) { + goto done; + } + } else { + /* Decoding. We do this the same way as we do when loading asynchronously. */ + ma_decoder* pDecoder; + result = ma_resource_manager_data_buffer_node_init_supply_decoded(pResourceManager, pDataBufferNode, pFilePath, pFilePathW, flags, &pDecoder); + if (result != MA_SUCCESS) { + goto done; + } + + /* We have the decoder, now decode page by page just like we do when loading asynchronously. */ + for (;;) { + /* Decode next page. */ + result = ma_resource_manager_data_buffer_node_decode_next_page(pResourceManager, pDataBufferNode, pDecoder); + if (result != MA_SUCCESS) { + break; /* Will return MA_AT_END when the last page has been decoded. */ + } + } + + /* Reaching the end needs to be considered successful. */ + if (result == MA_AT_END) { + result = MA_SUCCESS; + } + + /* + At this point the data buffer is either fully decoded or some error occurred. Either + way, the decoder is no longer necessary. + */ + ma_decoder_uninit(pDecoder); + ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); + } + + /* Getting here means we were successful. Make sure the status of the node is updated accordingly. */ + c89atomic_exchange_i32(&pDataBufferNode->result, result); + } else { + /* Loading asynchronously. We may need to wait for initialization. */ + if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { + ma_resource_manager_inline_notification_wait(&initNotification); + } + } + } else { + /* The data is not managed by the resource manager so there's nothing else to do. */ + MA_ASSERT(pExistingData != NULL); + } + } + +done: + /* If we failed to initialize the data buffer we need to free it. */ + if (result != MA_SUCCESS) { + if (nodeAlreadyExists == MA_FALSE) { + ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); + ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); + } + } + + /* + The init notification needs to be uninitialized. This will be used if the node does not already + exist, and we've specified ASYNC | WAIT_INIT. + */ + if (nodeAlreadyExists == MA_FALSE && pDataBufferNode->isDataOwnedByResourceManager && (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0) { + if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { + ma_resource_manager_inline_notification_uninit(&initNotification); + } + } + + if (ppDataBufferNode != NULL) { + *ppDataBufferNode = pDataBufferNode; + } + + return result; +} + +static ma_result ma_resource_manager_data_buffer_node_unacquire(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pName, const wchar_t* pNameW) +{ + ma_result result = MA_SUCCESS; + ma_uint32 refCount = 0xFFFFFFFF; /* The new reference count of the node after decrementing. Initialize to non-0 to be safe we don't fall into the freeing path. */ + ma_uint32 hashedName32 = 0; + + if (pResourceManager == NULL) { + return MA_INVALID_ARGS; + } + + if (pDataBufferNode == NULL) { + if (pName == NULL && pNameW == NULL) { + return MA_INVALID_ARGS; + } + + if (pName != NULL) { + hashedName32 = ma_hash_string_32(pName); + } else { + hashedName32 = ma_hash_string_w_32(pNameW); + } + } + + /* + The first thing to do is decrement the reference counter of the node. Then, if the reference + count is zero, we need to free the node. If the node is still in the process of loading, we'll + need to post a job to the job queue to free the node. Otherwise we'll just do it here. + */ + ma_resource_manager_data_buffer_bst_lock(pResourceManager); + { + /* Might need to find the node. Must be done inside the critical section. */ + if (pDataBufferNode == NULL) { + result = ma_resource_manager_data_buffer_node_search(pResourceManager, hashedName32, &pDataBufferNode); + if (result != MA_SUCCESS) { + goto stage2; /* Couldn't find the node. */ + } + } + + result = ma_resource_manager_data_buffer_node_decrement_ref(pResourceManager, pDataBufferNode, &refCount); + if (result != MA_SUCCESS) { + goto stage2; /* Should never happen. */ + } + + if (refCount == 0) { + result = ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); + if (result != MA_SUCCESS) { + goto stage2; /* An error occurred when trying to remove the data buffer. This should never happen. */ + } + } + } + ma_resource_manager_data_buffer_bst_unlock(pResourceManager); + +stage2: + if (result != MA_SUCCESS) { + return result; + } + + /* + Here is where we need to free the node. We don't want to do this inside the critical section + above because we want to keep that as small as possible for multi-threaded efficiency. + */ + if (refCount == 0) { + if (ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_BUSY) { + /* The sound is still loading. We need to delay the freeing of the node to a safe time. */ + ma_job job; + + /* We need to mark the node as unavailable for the sake of the resource manager worker threads. */ + c89atomic_exchange_i32(&pDataBufferNode->result, MA_UNAVAILABLE); + + job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE); + job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); + job.data.resourceManager.freeDataBufferNode.pResourceManager = pResourceManager; + job.data.resourceManager.freeDataBufferNode.pDataBufferNode = pDataBufferNode; + + result = ma_resource_manager_post_job(pResourceManager, &job); + if (result != MA_SUCCESS) { + ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE job. %s.\n", ma_result_description(result)); + return result; + } + + /* If we don't support threading, process the job queue here. */ + if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) { + while (ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_BUSY) { + result = ma_resource_manager_process_next_job(pResourceManager); + if (result == MA_NO_DATA_AVAILABLE || result == MA_CANCELLED) { + result = MA_SUCCESS; + break; + } + } + } else { + /* Threading is enabled. The job queue will deal with the rest of the cleanup from here. */ + } + } else { + /* The sound isn't loading so we can just free the node here. */ + ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode); + } + } + + return result; +} + + + +static ma_uint32 ma_resource_manager_data_buffer_next_execution_order(ma_resource_manager_data_buffer* pDataBuffer) +{ + MA_ASSERT(pDataBuffer != NULL); + return c89atomic_fetch_add_32(&pDataBuffer->executionCounter, 1); +} + +static ma_result ma_resource_manager_data_buffer_cb__read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + return ma_resource_manager_data_buffer_read_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pFramesOut, frameCount, pFramesRead); +} + +static ma_result ma_resource_manager_data_buffer_cb__seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex) +{ + return ma_resource_manager_data_buffer_seek_to_pcm_frame((ma_resource_manager_data_buffer*)pDataSource, frameIndex); +} + +static ma_result ma_resource_manager_data_buffer_cb__get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + return ma_resource_manager_data_buffer_get_data_format((ma_resource_manager_data_buffer*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); +} + +static ma_result ma_resource_manager_data_buffer_cb__get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor) +{ + return ma_resource_manager_data_buffer_get_cursor_in_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pCursor); +} + +static ma_result ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength) +{ + return ma_resource_manager_data_buffer_get_length_in_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pLength); +} + +static ma_result ma_resource_manager_data_buffer_cb__set_looping(ma_data_source* pDataSource, ma_bool32 isLooping) +{ + ma_resource_manager_data_buffer* pDataBuffer = (ma_resource_manager_data_buffer*)pDataSource; + MA_ASSERT(pDataBuffer != NULL); + + c89atomic_exchange_32(&pDataBuffer->isLooping, isLooping); + + /* The looping state needs to be set on the connector as well or else looping won't work when we read audio data. */ + ma_data_source_set_looping(ma_resource_manager_data_buffer_get_connector(pDataBuffer), isLooping); + + return MA_SUCCESS; +} + +static ma_data_source_vtable g_ma_resource_manager_data_buffer_vtable = +{ + ma_resource_manager_data_buffer_cb__read_pcm_frames, + ma_resource_manager_data_buffer_cb__seek_to_pcm_frame, + ma_resource_manager_data_buffer_cb__get_data_format, + ma_resource_manager_data_buffer_cb__get_cursor_in_pcm_frames, + ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames, + ma_resource_manager_data_buffer_cb__set_looping, + 0 +}; + +static ma_result ma_resource_manager_data_buffer_init_ex_internal(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_uint32 hashedName32, ma_resource_manager_data_buffer* pDataBuffer) +{ + ma_result result = MA_SUCCESS; + ma_resource_manager_data_buffer_node* pDataBufferNode; + ma_data_source_config dataSourceConfig; + ma_bool32 async; + ma_uint32 flags; + ma_resource_manager_pipeline_notifications notifications; + + if (pDataBuffer == NULL) { + if (pConfig != NULL && pConfig->pNotifications != NULL) { + ma_resource_manager_pipeline_notifications_signal_all_notifications(pConfig->pNotifications); + } + + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pDataBuffer); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->pNotifications != NULL) { + notifications = *pConfig->pNotifications; /* From here on out we should be referencing `notifications` instead of `pNotifications`. Set this to NULL to catch errors at testing time. */ + } else { + MA_ZERO_OBJECT(¬ifications); + } + + /* For safety, always remove the ASYNC flag if threading is disabled on the resource manager. */ + flags = pConfig->flags; + if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) { + flags &= ~MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC; + } + + async = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0; + + /* + Fences need to be acquired before doing anything. These must be aquired and released outside of + the node to ensure there's no holes where ma_fence_wait() could prematurely return before the + data buffer has completed initialization. + + When loading asynchronously, the node acquisition routine below will acquire the fences on this + thread and then release them on the async thread when the operation is complete. + + These fences are always released at the "done" tag at the end of this function. They'll be + acquired a second if loading asynchronously. This double acquisition system is just done to + simplify code maintanence. + */ + ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications); + { + /* We first need to acquire a node. If ASYNC is not set, this will not return until the entire sound has been loaded. */ + result = ma_resource_manager_data_buffer_node_acquire(pResourceManager, pConfig->pFilePath, pConfig->pFilePathW, hashedName32, flags, NULL, notifications.init.pFence, notifications.done.pFence, &pDataBufferNode); + if (result != MA_SUCCESS) { + ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); + goto done; + } + + dataSourceConfig = ma_data_source_config_init(); + dataSourceConfig.vtable = &g_ma_resource_manager_data_buffer_vtable; + + result = ma_data_source_init(&dataSourceConfig, &pDataBuffer->ds); + if (result != MA_SUCCESS) { + ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL); + ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); + goto done; + } + + pDataBuffer->pResourceManager = pResourceManager; + pDataBuffer->pNode = pDataBufferNode; + pDataBuffer->flags = flags; + pDataBuffer->result = MA_BUSY; /* Always default to MA_BUSY for safety. It'll be overwritten when loading completes or an error occurs. */ + + /* If we're loading asynchronously we need to post a job to the job queue to initialize the connector. */ + if (async == MA_FALSE || ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_SUCCESS) { + /* Loading synchronously or the data has already been fully loaded. We can just initialize the connector from here without a job. */ + result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, pConfig, NULL, NULL); + c89atomic_exchange_i32(&pDataBuffer->result, result); + + ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); + goto done; + } else { + /* The node's data supply isn't initialized yet. The caller has requested that we load asynchronously so we need to post a job to do this. */ + ma_job job; + ma_resource_manager_inline_notification initNotification; /* Used when the WAIT_INIT flag is set. */ + + if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { + ma_resource_manager_inline_notification_init(pResourceManager, &initNotification); + } + + /* + The status of the data buffer needs to be set to MA_BUSY before posting the job so that the + worker thread is aware of it's busy state. If the LOAD_DATA_BUFFER job sees a status other + than MA_BUSY, it'll assume an error and fall through to an early exit. + */ + c89atomic_exchange_i32(&pDataBuffer->result, MA_BUSY); + + /* Acquire fences a second time. These will be released by the async thread. */ + ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications); + + job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER); + job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer); + job.data.resourceManager.loadDataBuffer.pDataBuffer = pDataBuffer; + job.data.resourceManager.loadDataBuffer.pInitNotification = ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? &initNotification : notifications.init.pNotification; + job.data.resourceManager.loadDataBuffer.pDoneNotification = notifications.done.pNotification; + job.data.resourceManager.loadDataBuffer.pInitFence = notifications.init.pFence; + job.data.resourceManager.loadDataBuffer.pDoneFence = notifications.done.pFence; + job.data.resourceManager.loadDataBuffer.rangeBegInPCMFrames = pConfig->rangeBegInPCMFrames; + job.data.resourceManager.loadDataBuffer.rangeEndInPCMFrames = pConfig->rangeEndInPCMFrames; + job.data.resourceManager.loadDataBuffer.loopPointBegInPCMFrames = pConfig->loopPointBegInPCMFrames; + job.data.resourceManager.loadDataBuffer.loopPointEndInPCMFrames = pConfig->loopPointEndInPCMFrames; + job.data.resourceManager.loadDataBuffer.isLooping = pConfig->isLooping; + + result = ma_resource_manager_post_job(pResourceManager, &job); + if (result != MA_SUCCESS) { + /* We failed to post the job. Most likely there isn't enough room in the queue's buffer. */ + ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER job. %s.\n", ma_result_description(result)); + c89atomic_exchange_i32(&pDataBuffer->result, result); + + /* Release the fences after the result has been set on the data buffer. */ + ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications); + } else { + if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { + ma_resource_manager_inline_notification_wait(&initNotification); + + if (notifications.init.pNotification != NULL) { + ma_async_notification_signal(notifications.init.pNotification); + } + + /* NOTE: Do not release the init fence here. It will have been done by the job. */ + + /* Make sure we return an error if initialization failed on the async thread. */ + result = ma_resource_manager_data_buffer_result(pDataBuffer); + if (result == MA_BUSY) { + result = MA_SUCCESS; + } + } + } + + if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { + ma_resource_manager_inline_notification_uninit(&initNotification); + } + } + + if (result != MA_SUCCESS) { + ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL); + goto done; + } + } +done: + if (result == MA_SUCCESS) { + if (pConfig->initialSeekPointInPCMFrames > 0) { + ma_resource_manager_data_buffer_seek_to_pcm_frame(pDataBuffer, pConfig->initialSeekPointInPCMFrames); + } + } + + ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications); + + return result; +} + +MA_API ma_result ma_resource_manager_data_buffer_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_buffer* pDataBuffer) +{ + return ma_resource_manager_data_buffer_init_ex_internal(pResourceManager, pConfig, 0, pDataBuffer); +} + +MA_API ma_result ma_resource_manager_data_buffer_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer) +{ + ma_resource_manager_data_source_config config; + + config = ma_resource_manager_data_source_config_init(); + config.pFilePath = pFilePath; + config.flags = flags; + config.pNotifications = pNotifications; + + return ma_resource_manager_data_buffer_init_ex(pResourceManager, &config, pDataBuffer); +} + +MA_API ma_result ma_resource_manager_data_buffer_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer) +{ + ma_resource_manager_data_source_config config; + + config = ma_resource_manager_data_source_config_init(); + config.pFilePathW = pFilePath; + config.flags = flags; + config.pNotifications = pNotifications; + + return ma_resource_manager_data_buffer_init_ex(pResourceManager, &config, pDataBuffer); +} + +MA_API ma_result ma_resource_manager_data_buffer_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_buffer* pExistingDataBuffer, ma_resource_manager_data_buffer* pDataBuffer) +{ + ma_resource_manager_data_source_config config; + + if (pExistingDataBuffer == NULL) { + return MA_INVALID_ARGS; + } + + MA_ASSERT(pExistingDataBuffer->pNode != NULL); /* <-- If you've triggered this, you've passed in an invalid existing data buffer. */ + + config = ma_resource_manager_data_source_config_init(); + config.flags = pExistingDataBuffer->flags; + + return ma_resource_manager_data_buffer_init_ex_internal(pResourceManager, &config, pExistingDataBuffer->pNode->hashedName32, pDataBuffer); +} + +static ma_result ma_resource_manager_data_buffer_uninit_internal(ma_resource_manager_data_buffer* pDataBuffer) +{ + MA_ASSERT(pDataBuffer != NULL); + + /* The connector should be uninitialized first. */ + ma_resource_manager_data_buffer_uninit_connector(pDataBuffer->pResourceManager, pDataBuffer); + + /* With the connector uninitialized we can unacquire the node. */ + ma_resource_manager_data_buffer_node_unacquire(pDataBuffer->pResourceManager, pDataBuffer->pNode, NULL, NULL); + + /* The base data source needs to be uninitialized as well. */ + ma_data_source_uninit(&pDataBuffer->ds); + + return MA_SUCCESS; +} + +MA_API ma_result ma_resource_manager_data_buffer_uninit(ma_resource_manager_data_buffer* pDataBuffer) +{ + ma_result result; + + if (pDataBuffer == NULL) { + return MA_INVALID_ARGS; + } + + if (ma_resource_manager_data_buffer_result(pDataBuffer) == MA_SUCCESS) { + /* The data buffer can be deleted synchronously. */ + return ma_resource_manager_data_buffer_uninit_internal(pDataBuffer); + } else { + /* + The data buffer needs to be deleted asynchronously because it's still loading. With the status set to MA_UNAVAILABLE, no more pages will + be loaded and the uninitialization should happen fairly quickly. Since the caller owns the data buffer, we need to wait for this event + to get processed before returning. + */ + ma_resource_manager_inline_notification notification; + ma_job job; + + /* + We need to mark the node as unavailable so we don't try reading from it anymore, but also to + let the loading thread know that it needs to abort it's loading procedure. + */ + c89atomic_exchange_i32(&pDataBuffer->result, MA_UNAVAILABLE); + + result = ma_resource_manager_inline_notification_init(pDataBuffer->pResourceManager, ¬ification); + if (result != MA_SUCCESS) { + return result; /* Failed to create the notification. This should rarely, if ever, happen. */ + } + + job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER); + job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer); + job.data.resourceManager.freeDataBuffer.pDataBuffer = pDataBuffer; + job.data.resourceManager.freeDataBuffer.pDoneNotification = ¬ification; + job.data.resourceManager.freeDataBuffer.pDoneFence = NULL; + + result = ma_resource_manager_post_job(pDataBuffer->pResourceManager, &job); + if (result != MA_SUCCESS) { + ma_resource_manager_inline_notification_uninit(¬ification); + return result; + } + + ma_resource_manager_inline_notification_wait_and_uninit(¬ification); + } + + return result; +} + +MA_API ma_result ma_resource_manager_data_buffer_read_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + ma_result result = MA_SUCCESS; + ma_uint64 framesRead = 0; + ma_bool32 isDecodedBufferBusy = MA_FALSE; + + /* Safety. */ + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + + /* + We cannot be using the data buffer after it's been uninitialized. If you trigger this assert it means you're trying to read from the data buffer after + it's been uninitialized or is in the process of uninitializing. + */ + MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); + + /* If the node is not initialized we need to abort with a busy code. */ + if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) { + return MA_BUSY; /* Still loading. */ + } + + if (pDataBuffer->seekToCursorOnNextRead) { + pDataBuffer->seekToCursorOnNextRead = MA_FALSE; + + result = ma_data_source_seek_to_pcm_frame(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pDataBuffer->seekTargetInPCMFrames); + if (result != MA_SUCCESS) { + return result; + } + } + + /* + For decoded buffers (not paged) we need to check beforehand how many frames we have available. We cannot + exceed this amount. We'll read as much as we can, and then return MA_BUSY. + */ + if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_decoded) { + ma_uint64 availableFrames; + + isDecodedBufferBusy = (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY); + + if (ma_resource_manager_data_buffer_get_available_frames(pDataBuffer, &availableFrames) == MA_SUCCESS) { + /* Don't try reading more than the available frame count. */ + if (frameCount > availableFrames) { + frameCount = availableFrames; + + /* + If there's no frames available we want to set the status to MA_AT_END. The logic below + will check if the node is busy, and if so, change it to MA_BUSY. The reason we do this + is because we don't want to call `ma_data_source_read_pcm_frames()` if the frame count + is 0 because that'll result in a situation where it's possible MA_AT_END won't get + returned. + */ + if (frameCount == 0) { + result = MA_AT_END; + } + } else { + isDecodedBufferBusy = MA_FALSE; /* We have enough frames available in the buffer to avoid a MA_BUSY status. */ + } + } + } + + /* Don't attempt to read anything if we've got no frames available. */ + if (frameCount > 0) { + result = ma_data_source_read_pcm_frames(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pFramesOut, frameCount, &framesRead); + } + + /* + If we returned MA_AT_END, but the node is still loading, we don't want to return that code or else the caller will interpret the sound + as at the end and terminate decoding. + */ + if (result == MA_AT_END) { + if (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY) { + result = MA_BUSY; + } + } + + if (isDecodedBufferBusy) { + result = MA_BUSY; + } + + if (pFramesRead != NULL) { + *pFramesRead = framesRead; + } + + if (result == MA_SUCCESS && framesRead == 0) { + result = MA_AT_END; + } + + return result; +} + +MA_API ma_result ma_resource_manager_data_buffer_seek_to_pcm_frame(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64 frameIndex) +{ + ma_result result; + + /* We cannot be using the data source after it's been uninitialized. */ + MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); + + /* If we haven't yet got a connector we need to abort. */ + if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) { + pDataBuffer->seekTargetInPCMFrames = frameIndex; + pDataBuffer->seekToCursorOnNextRead = MA_TRUE; + return MA_BUSY; /* Still loading. */ + } + + result = ma_data_source_seek_to_pcm_frame(ma_resource_manager_data_buffer_get_connector(pDataBuffer), frameIndex); + if (result != MA_SUCCESS) { + return result; + } + + pDataBuffer->seekTargetInPCMFrames = ~(ma_uint64)0; /* <-- For identification purposes. */ + pDataBuffer->seekToCursorOnNextRead = MA_FALSE; + + return MA_SUCCESS; +} + +MA_API ma_result ma_resource_manager_data_buffer_get_data_format(ma_resource_manager_data_buffer* pDataBuffer, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + /* We cannot be using the data source after it's been uninitialized. */ + MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); + + switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) + { + case ma_resource_manager_data_supply_type_encoded: + { + return ma_data_source_get_data_format(&pDataBuffer->connector.decoder, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); + }; + + case ma_resource_manager_data_supply_type_decoded: + { + *pFormat = pDataBuffer->pNode->data.backend.decoded.format; + *pChannels = pDataBuffer->pNode->data.backend.decoded.channels; + *pSampleRate = pDataBuffer->pNode->data.backend.decoded.sampleRate; + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pDataBuffer->pNode->data.backend.decoded.channels); + return MA_SUCCESS; + }; + + case ma_resource_manager_data_supply_type_decoded_paged: + { + *pFormat = pDataBuffer->pNode->data.backend.decodedPaged.data.format; + *pChannels = pDataBuffer->pNode->data.backend.decodedPaged.data.channels; + *pSampleRate = pDataBuffer->pNode->data.backend.decodedPaged.sampleRate; + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pDataBuffer->pNode->data.backend.decoded.channels); + return MA_SUCCESS; + }; + + case ma_resource_manager_data_supply_type_unknown: + { + return MA_BUSY; /* Still loading. */ + }; + + default: + { + /* Unknown supply type. Should never hit this. */ + return MA_INVALID_ARGS; + } + } +} + +MA_API ma_result ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pCursor) +{ + /* We cannot be using the data source after it's been uninitialized. */ + MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); + + if (pDataBuffer == NULL || pCursor == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = 0; + + switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) + { + case ma_resource_manager_data_supply_type_encoded: + { + return ma_decoder_get_cursor_in_pcm_frames(&pDataBuffer->connector.decoder, pCursor); + }; + + case ma_resource_manager_data_supply_type_decoded: + { + return ma_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.buffer, pCursor); + }; + + case ma_resource_manager_data_supply_type_decoded_paged: + { + return ma_paged_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.pagedBuffer, pCursor); + }; + + case ma_resource_manager_data_supply_type_unknown: + { + return MA_BUSY; + }; + + default: + { + return MA_INVALID_ARGS; + } + } +} + +MA_API ma_result ma_resource_manager_data_buffer_get_length_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pLength) +{ + /* We cannot be using the data source after it's been uninitialized. */ + MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); + + if (pDataBuffer == NULL || pLength == NULL) { + return MA_INVALID_ARGS; + } + + if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) { + return MA_BUSY; /* Still loading. */ + } + + return ma_data_source_get_length_in_pcm_frames(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pLength); +} + +MA_API ma_result ma_resource_manager_data_buffer_result(const ma_resource_manager_data_buffer* pDataBuffer) +{ + if (pDataBuffer == NULL) { + return MA_INVALID_ARGS; + } + + return (ma_result)c89atomic_load_i32((ma_result*)&pDataBuffer->result); /* Need a naughty const-cast here. */ +} + +MA_API ma_result ma_resource_manager_data_buffer_set_looping(ma_resource_manager_data_buffer* pDataBuffer, ma_bool32 isLooping) +{ + return ma_data_source_set_looping(pDataBuffer, isLooping); +} + +MA_API ma_bool32 ma_resource_manager_data_buffer_is_looping(const ma_resource_manager_data_buffer* pDataBuffer) +{ + return ma_data_source_is_looping(pDataBuffer); +} + +MA_API ma_result ma_resource_manager_data_buffer_get_available_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pAvailableFrames) +{ + if (pAvailableFrames == NULL) { + return MA_INVALID_ARGS; + } + + *pAvailableFrames = 0; + + if (pDataBuffer == NULL) { + return MA_INVALID_ARGS; + } + + if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) { + if (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY) { + return MA_BUSY; + } else { + return MA_INVALID_OPERATION; /* No connector. */ + } + } + + switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) + { + case ma_resource_manager_data_supply_type_encoded: + { + return ma_decoder_get_available_frames(&pDataBuffer->connector.decoder, pAvailableFrames); + }; + + case ma_resource_manager_data_supply_type_decoded: + { + return ma_audio_buffer_get_available_frames(&pDataBuffer->connector.buffer, pAvailableFrames); + }; + + case ma_resource_manager_data_supply_type_decoded_paged: + { + ma_uint64 cursor; + ma_paged_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.pagedBuffer, &cursor); + + if (pDataBuffer->pNode->data.backend.decodedPaged.decodedFrameCount > cursor) { + *pAvailableFrames = pDataBuffer->pNode->data.backend.decodedPaged.decodedFrameCount - cursor; + } else { + *pAvailableFrames = 0; + } + + return MA_SUCCESS; + }; + + case ma_resource_manager_data_supply_type_unknown: + default: + { + /* Unknown supply type. Should never hit this. */ + return MA_INVALID_ARGS; + } + } +} + +MA_API ma_result ma_resource_manager_register_file(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags) +{ + return ma_resource_manager_data_buffer_node_acquire(pResourceManager, pFilePath, NULL, 0, flags, NULL, NULL, NULL, NULL); +} + +MA_API ma_result ma_resource_manager_register_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags) +{ + return ma_resource_manager_data_buffer_node_acquire(pResourceManager, NULL, pFilePath, 0, flags, NULL, NULL, NULL, NULL); +} + + +static ma_result ma_resource_manager_register_data(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, ma_resource_manager_data_supply* pExistingData) +{ + return ma_resource_manager_data_buffer_node_acquire(pResourceManager, pName, pNameW, 0, 0, pExistingData, NULL, NULL, NULL); +} + +static ma_result ma_resource_manager_register_decoded_data_internal(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) +{ + ma_resource_manager_data_supply data; + data.type = ma_resource_manager_data_supply_type_decoded; + data.backend.decoded.pData = pData; + data.backend.decoded.totalFrameCount = frameCount; + data.backend.decoded.format = format; + data.backend.decoded.channels = channels; + data.backend.decoded.sampleRate = sampleRate; + + return ma_resource_manager_register_data(pResourceManager, pName, pNameW, &data); +} + +MA_API ma_result ma_resource_manager_register_decoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) +{ + return ma_resource_manager_register_decoded_data_internal(pResourceManager, pName, NULL, pData, frameCount, format, channels, sampleRate); +} + +MA_API ma_result ma_resource_manager_register_decoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) +{ + return ma_resource_manager_register_decoded_data_internal(pResourceManager, NULL, pName, pData, frameCount, format, channels, sampleRate); +} + + +static ma_result ma_resource_manager_register_encoded_data_internal(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, const void* pData, size_t sizeInBytes) +{ + ma_resource_manager_data_supply data; + data.type = ma_resource_manager_data_supply_type_encoded; + data.backend.encoded.pData = pData; + data.backend.encoded.sizeInBytes = sizeInBytes; + + return ma_resource_manager_register_data(pResourceManager, pName, pNameW, &data); +} + +MA_API ma_result ma_resource_manager_register_encoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, size_t sizeInBytes) +{ + return ma_resource_manager_register_encoded_data_internal(pResourceManager, pName, NULL, pData, sizeInBytes); +} + +MA_API ma_result ma_resource_manager_register_encoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, size_t sizeInBytes) +{ + return ma_resource_manager_register_encoded_data_internal(pResourceManager, NULL, pName, pData, sizeInBytes); +} + + +MA_API ma_result ma_resource_manager_unregister_file(ma_resource_manager* pResourceManager, const char* pFilePath) +{ + return ma_resource_manager_unregister_data(pResourceManager, pFilePath); +} + +MA_API ma_result ma_resource_manager_unregister_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath) +{ + return ma_resource_manager_unregister_data_w(pResourceManager, pFilePath); +} + +MA_API ma_result ma_resource_manager_unregister_data(ma_resource_manager* pResourceManager, const char* pName) +{ + return ma_resource_manager_data_buffer_node_unacquire(pResourceManager, NULL, pName, NULL); +} + +MA_API ma_result ma_resource_manager_unregister_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName) +{ + return ma_resource_manager_data_buffer_node_unacquire(pResourceManager, NULL, NULL, pName); +} + + +static ma_uint32 ma_resource_manager_data_stream_next_execution_order(ma_resource_manager_data_stream* pDataStream) +{ + MA_ASSERT(pDataStream != NULL); + return c89atomic_fetch_add_32(&pDataStream->executionCounter, 1); +} + +static ma_bool32 ma_resource_manager_data_stream_is_decoder_at_end(const ma_resource_manager_data_stream* pDataStream) +{ + MA_ASSERT(pDataStream != NULL); + return c89atomic_load_32((ma_bool32*)&pDataStream->isDecoderAtEnd); +} + +static ma_uint32 ma_resource_manager_data_stream_seek_counter(const ma_resource_manager_data_stream* pDataStream) +{ + MA_ASSERT(pDataStream != NULL); + return c89atomic_load_32((ma_uint32*)&pDataStream->seekCounter); +} + + +static ma_result ma_resource_manager_data_stream_cb__read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + return ma_resource_manager_data_stream_read_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pFramesOut, frameCount, pFramesRead); +} + +static ma_result ma_resource_manager_data_stream_cb__seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex) +{ + return ma_resource_manager_data_stream_seek_to_pcm_frame((ma_resource_manager_data_stream*)pDataSource, frameIndex); +} + +static ma_result ma_resource_manager_data_stream_cb__get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + return ma_resource_manager_data_stream_get_data_format((ma_resource_manager_data_stream*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); +} + +static ma_result ma_resource_manager_data_stream_cb__get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor) +{ + return ma_resource_manager_data_stream_get_cursor_in_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pCursor); +} + +static ma_result ma_resource_manager_data_stream_cb__get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength) +{ + return ma_resource_manager_data_stream_get_length_in_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pLength); +} + +static ma_result ma_resource_manager_data_stream_cb__set_looping(ma_data_source* pDataSource, ma_bool32 isLooping) +{ + ma_resource_manager_data_stream* pDataStream = (ma_resource_manager_data_stream*)pDataSource; + MA_ASSERT(pDataStream != NULL); + + c89atomic_exchange_32(&pDataStream->isLooping, isLooping); + + return MA_SUCCESS; +} + +static ma_data_source_vtable g_ma_resource_manager_data_stream_vtable = +{ + ma_resource_manager_data_stream_cb__read_pcm_frames, + ma_resource_manager_data_stream_cb__seek_to_pcm_frame, + ma_resource_manager_data_stream_cb__get_data_format, + ma_resource_manager_data_stream_cb__get_cursor_in_pcm_frames, + ma_resource_manager_data_stream_cb__get_length_in_pcm_frames, + ma_resource_manager_data_stream_cb__set_looping, + MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT +}; + +static void ma_resource_manager_data_stream_set_absolute_cursor(ma_resource_manager_data_stream* pDataStream, ma_uint64 absoluteCursor) +{ + /* Loop if possible. */ + if (absoluteCursor > pDataStream->totalLengthInPCMFrames && pDataStream->totalLengthInPCMFrames > 0) { + absoluteCursor = absoluteCursor % pDataStream->totalLengthInPCMFrames; + } + + c89atomic_exchange_64(&pDataStream->absoluteCursor, absoluteCursor); +} + +MA_API ma_result ma_resource_manager_data_stream_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_stream* pDataStream) +{ + ma_result result; + ma_data_source_config dataSourceConfig; + char* pFilePathCopy = NULL; + wchar_t* pFilePathWCopy = NULL; + ma_job job; + ma_bool32 waitBeforeReturning = MA_FALSE; + ma_resource_manager_inline_notification waitNotification; + ma_resource_manager_pipeline_notifications notifications; + + if (pDataStream == NULL) { + if (pConfig != NULL && pConfig->pNotifications != NULL) { + ma_resource_manager_pipeline_notifications_signal_all_notifications(pConfig->pNotifications); + } + + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pDataStream); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->pNotifications != NULL) { + notifications = *pConfig->pNotifications; /* From here on out, `notifications` should be used instead of `pNotifications`. Setting this to NULL to catch any errors at testing time. */ + } else { + MA_ZERO_OBJECT(¬ifications); + } + + dataSourceConfig = ma_data_source_config_init(); + dataSourceConfig.vtable = &g_ma_resource_manager_data_stream_vtable; + + result = ma_data_source_init(&dataSourceConfig, &pDataStream->ds); + if (result != MA_SUCCESS) { + ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); + return result; + } + + pDataStream->pResourceManager = pResourceManager; + pDataStream->flags = pConfig->flags; + pDataStream->result = MA_BUSY; + + ma_data_source_set_range_in_pcm_frames(pDataStream, pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames); + ma_data_source_set_loop_point_in_pcm_frames(pDataStream, pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); + ma_data_source_set_looping(pDataStream, pConfig->isLooping); + + if (pResourceManager == NULL || (pConfig->pFilePath == NULL && pConfig->pFilePathW == NULL)) { + ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); + return MA_INVALID_ARGS; + } + + /* We want all access to the VFS and the internal decoder to happen on the job thread just to keep things easier to manage for the VFS. */ + + /* We need a copy of the file path. We should probably make this more efficient, but for now we'll do a transient memory allocation. */ + if (pConfig->pFilePath != NULL) { + pFilePathCopy = ma_copy_string(pConfig->pFilePath, &pResourceManager->config.allocationCallbacks); + } else { + pFilePathWCopy = ma_copy_string_w(pConfig->pFilePathW, &pResourceManager->config.allocationCallbacks); + } + + if (pFilePathCopy == NULL && pFilePathWCopy == NULL) { + ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); + return MA_OUT_OF_MEMORY; + } + + /* + We need to check for the presence of MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC. If it's not set, we need to wait before returning. Otherwise we + can return immediately. Likewise, we'll also check for MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT and do the same. + */ + if ((pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) == 0 || (pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { + waitBeforeReturning = MA_TRUE; + ma_resource_manager_inline_notification_init(pResourceManager, &waitNotification); + } + + ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications); + + /* Set the absolute cursor to our initial seek position so retrieval of the cursor returns a good value. */ + ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, pConfig->initialSeekPointInPCMFrames); + + /* We now have everything we need to post the job. This is the last thing we need to do from here. The rest will be done by the job thread. */ + job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM); + job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); + job.data.resourceManager.loadDataStream.pDataStream = pDataStream; + job.data.resourceManager.loadDataStream.pFilePath = pFilePathCopy; + job.data.resourceManager.loadDataStream.pFilePathW = pFilePathWCopy; + job.data.resourceManager.loadDataStream.initialSeekPoint = pConfig->initialSeekPointInPCMFrames; + job.data.resourceManager.loadDataStream.pInitNotification = (waitBeforeReturning == MA_TRUE) ? &waitNotification : notifications.init.pNotification; + job.data.resourceManager.loadDataStream.pInitFence = notifications.init.pFence; + result = ma_resource_manager_post_job(pResourceManager, &job); + if (result != MA_SUCCESS) { + ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); + ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications); + + if (waitBeforeReturning) { + ma_resource_manager_inline_notification_uninit(&waitNotification); + } + + ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks); + ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks); + return result; + } + + /* Wait if needed. */ + if (waitBeforeReturning) { + ma_resource_manager_inline_notification_wait_and_uninit(&waitNotification); + + if (notifications.init.pNotification != NULL) { + ma_async_notification_signal(notifications.init.pNotification); + } + + /* NOTE: Do not release pInitFence here. That will be done by the job. */ + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_resource_manager_data_stream_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream) +{ + ma_resource_manager_data_source_config config; + + config = ma_resource_manager_data_source_config_init(); + config.pFilePath = pFilePath; + config.flags = flags; + config.pNotifications = pNotifications; + + return ma_resource_manager_data_stream_init_ex(pResourceManager, &config, pDataStream); +} + +MA_API ma_result ma_resource_manager_data_stream_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream) +{ + ma_resource_manager_data_source_config config; + + config = ma_resource_manager_data_source_config_init(); + config.pFilePathW = pFilePath; + config.flags = flags; + config.pNotifications = pNotifications; + + return ma_resource_manager_data_stream_init_ex(pResourceManager, &config, pDataStream); +} + +MA_API ma_result ma_resource_manager_data_stream_uninit(ma_resource_manager_data_stream* pDataStream) +{ + ma_resource_manager_inline_notification freeEvent; + ma_job job; + + if (pDataStream == NULL) { + return MA_INVALID_ARGS; + } + + /* The first thing to do is set the result to unavailable. This will prevent future page decoding. */ + c89atomic_exchange_i32(&pDataStream->result, MA_UNAVAILABLE); + + /* + We need to post a job to ensure we're not in the middle or decoding or anything. Because the object is owned by the caller, we'll need + to wait for it to complete before returning which means we need an event. + */ + ma_resource_manager_inline_notification_init(pDataStream->pResourceManager, &freeEvent); + + job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM); + job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); + job.data.resourceManager.freeDataStream.pDataStream = pDataStream; + job.data.resourceManager.freeDataStream.pDoneNotification = &freeEvent; + job.data.resourceManager.freeDataStream.pDoneFence = NULL; + ma_resource_manager_post_job(pDataStream->pResourceManager, &job); + + /* We need to wait for the job to finish processing before we return. */ + ma_resource_manager_inline_notification_wait_and_uninit(&freeEvent); + + return MA_SUCCESS; +} + + +static ma_uint32 ma_resource_manager_data_stream_get_page_size_in_frames(ma_resource_manager_data_stream* pDataStream) +{ + MA_ASSERT(pDataStream != NULL); + MA_ASSERT(pDataStream->isDecoderInitialized == MA_TRUE); + + return MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS * (pDataStream->decoder.outputSampleRate/1000); +} + +static void* ma_resource_manager_data_stream_get_page_data_pointer(ma_resource_manager_data_stream* pDataStream, ma_uint32 pageIndex, ma_uint32 relativeCursor) +{ + MA_ASSERT(pDataStream != NULL); + MA_ASSERT(pDataStream->isDecoderInitialized == MA_TRUE); + MA_ASSERT(pageIndex == 0 || pageIndex == 1); + + return ma_offset_ptr(pDataStream->pPageData, ((ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream) * pageIndex) + relativeCursor) * ma_get_bytes_per_frame(pDataStream->decoder.outputFormat, pDataStream->decoder.outputChannels)); +} + +static void ma_resource_manager_data_stream_fill_page(ma_resource_manager_data_stream* pDataStream, ma_uint32 pageIndex) +{ + ma_result result = MA_SUCCESS; + ma_uint64 pageSizeInFrames; + ma_uint64 totalFramesReadForThisPage = 0; + void* pPageData = ma_resource_manager_data_stream_get_page_data_pointer(pDataStream, pageIndex, 0); + + pageSizeInFrames = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream); + + /* The decoder needs to inherit the stream's looping and range state. */ + { + ma_uint64 rangeBeg; + ma_uint64 rangeEnd; + ma_uint64 loopPointBeg; + ma_uint64 loopPointEnd; + + ma_data_source_set_looping(&pDataStream->decoder, ma_resource_manager_data_stream_is_looping(pDataStream)); + + ma_data_source_get_range_in_pcm_frames(pDataStream, &rangeBeg, &rangeEnd); + ma_data_source_set_range_in_pcm_frames(&pDataStream->decoder, rangeBeg, rangeEnd); + + ma_data_source_get_loop_point_in_pcm_frames(pDataStream, &loopPointBeg, &loopPointEnd); + ma_data_source_set_loop_point_in_pcm_frames(&pDataStream->decoder, loopPointBeg, loopPointEnd); + } + + /* Just read straight from the decoder. It will deal with ranges and looping for us. */ + result = ma_data_source_read_pcm_frames(&pDataStream->decoder, pPageData, pageSizeInFrames, &totalFramesReadForThisPage); + if (result == MA_AT_END || totalFramesReadForThisPage < pageSizeInFrames) { + c89atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_TRUE); + } + + c89atomic_exchange_32(&pDataStream->pageFrameCount[pageIndex], (ma_uint32)totalFramesReadForThisPage); + c89atomic_exchange_32(&pDataStream->isPageValid[pageIndex], MA_TRUE); +} + +static void ma_resource_manager_data_stream_fill_pages(ma_resource_manager_data_stream* pDataStream) +{ + ma_uint32 iPage; + + MA_ASSERT(pDataStream != NULL); + + for (iPage = 0; iPage < 2; iPage += 1) { + ma_resource_manager_data_stream_fill_page(pDataStream, iPage); + } +} + + +static ma_result ma_resource_manager_data_stream_map(ma_resource_manager_data_stream* pDataStream, void** ppFramesOut, ma_uint64* pFrameCount) +{ + ma_uint64 framesAvailable; + ma_uint64 frameCount = 0; + + /* We cannot be using the data source after it's been uninitialized. */ + MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); + + if (pFrameCount != NULL) { + frameCount = *pFrameCount; + *pFrameCount = 0; + } + if (ppFramesOut != NULL) { + *ppFramesOut = NULL; + } + + if (pDataStream == NULL || ppFramesOut == NULL || pFrameCount == NULL) { + return MA_INVALID_ARGS; + } + + if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { + return MA_INVALID_OPERATION; + } + + /* Don't attempt to read while we're in the middle of seeking. Tell the caller that we're busy. */ + if (ma_resource_manager_data_stream_seek_counter(pDataStream) > 0) { + return MA_BUSY; + } + + /* If the page we're on is invalid it means we've caught up to the job thread. */ + if (c89atomic_load_32(&pDataStream->isPageValid[pDataStream->currentPageIndex]) == MA_FALSE) { + framesAvailable = 0; + } else { + /* + The page we're on is valid so we must have some frames available. We need to make sure that we don't overflow into the next page, even if it's valid. The reason is + that the unmap process will only post an update for one page at a time. Keeping mapping tied to page boundaries makes this simpler. + */ + ma_uint32 currentPageFrameCount = c89atomic_load_32(&pDataStream->pageFrameCount[pDataStream->currentPageIndex]); + MA_ASSERT(currentPageFrameCount >= pDataStream->relativeCursor); + + framesAvailable = currentPageFrameCount - pDataStream->relativeCursor; + } + + /* If there's no frames available and the result is set to MA_AT_END we need to return MA_AT_END. */ + if (framesAvailable == 0) { + if (ma_resource_manager_data_stream_is_decoder_at_end(pDataStream)) { + return MA_AT_END; + } else { + return MA_BUSY; /* There are no frames available, but we're not marked as EOF so we might have caught up to the job thread. Need to return MA_BUSY and wait for more data. */ + } + } + + MA_ASSERT(framesAvailable > 0); + + if (frameCount > framesAvailable) { + frameCount = framesAvailable; + } + + *ppFramesOut = ma_resource_manager_data_stream_get_page_data_pointer(pDataStream, pDataStream->currentPageIndex, pDataStream->relativeCursor); + *pFrameCount = frameCount; + + return MA_SUCCESS; +} + +static ma_result ma_resource_manager_data_stream_unmap(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameCount) +{ + ma_uint32 newRelativeCursor; + ma_uint32 pageSizeInFrames; + ma_job job; + + /* We cannot be using the data source after it's been uninitialized. */ + MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); + + if (pDataStream == NULL) { + return MA_INVALID_ARGS; + } + + if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { + return MA_INVALID_OPERATION; + } + + /* The frame count should always fit inside a 32-bit integer. */ + if (frameCount > 0xFFFFFFFF) { + return MA_INVALID_ARGS; + } + + pageSizeInFrames = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream); + + /* The absolute cursor needs to be updated for ma_resource_manager_data_stream_get_cursor_in_pcm_frames(). */ + ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, c89atomic_load_64(&pDataStream->absoluteCursor) + frameCount); + + /* Here is where we need to check if we need to load a new page, and if so, post a job to load it. */ + newRelativeCursor = pDataStream->relativeCursor + (ma_uint32)frameCount; + + /* If the new cursor has flowed over to the next page we need to mark the old one as invalid and post an event for it. */ + if (newRelativeCursor >= pageSizeInFrames) { + newRelativeCursor -= pageSizeInFrames; + + /* Here is where we post the job start decoding. */ + job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM); + job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); + job.data.resourceManager.pageDataStream.pDataStream = pDataStream; + job.data.resourceManager.pageDataStream.pageIndex = pDataStream->currentPageIndex; + + /* The page needs to be marked as invalid so that the public API doesn't try reading from it. */ + c89atomic_exchange_32(&pDataStream->isPageValid[pDataStream->currentPageIndex], MA_FALSE); + + /* Before posting the job we need to make sure we set some state. */ + pDataStream->relativeCursor = newRelativeCursor; + pDataStream->currentPageIndex = (pDataStream->currentPageIndex + 1) & 0x01; + return ma_resource_manager_post_job(pDataStream->pResourceManager, &job); + } else { + /* We haven't moved into a new page so we can just move the cursor forward. */ + pDataStream->relativeCursor = newRelativeCursor; + return MA_SUCCESS; + } +} + + +MA_API ma_result ma_resource_manager_data_stream_read_pcm_frames(ma_resource_manager_data_stream* pDataStream, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + ma_result result = MA_SUCCESS; + ma_uint64 totalFramesProcessed; + ma_format format; + ma_uint32 channels; + + /* Safety. */ + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + + /* We cannot be using the data source after it's been uninitialized. */ + MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); + + if (pDataStream == NULL) { + return MA_INVALID_ARGS; + } + + if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { + return MA_INVALID_OPERATION; + } + + /* Don't attempt to read while we're in the middle of seeking. Tell the caller that we're busy. */ + if (ma_resource_manager_data_stream_seek_counter(pDataStream) > 0) { + return MA_BUSY; + } + + ma_resource_manager_data_stream_get_data_format(pDataStream, &format, &channels, NULL, NULL, 0); + + /* Reading is implemented in terms of map/unmap. We need to run this in a loop because mapping is clamped against page boundaries. */ + totalFramesProcessed = 0; + while (totalFramesProcessed < frameCount) { + void* pMappedFrames; + ma_uint64 mappedFrameCount; + + mappedFrameCount = frameCount - totalFramesProcessed; + result = ma_resource_manager_data_stream_map(pDataStream, &pMappedFrames, &mappedFrameCount); + if (result != MA_SUCCESS) { + break; + } + + /* Copy the mapped data to the output buffer if we have one. It's allowed for pFramesOut to be NULL in which case a relative forward seek is performed. */ + if (pFramesOut != NULL) { + ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesProcessed, format, channels), pMappedFrames, mappedFrameCount, format, channels); + } + + totalFramesProcessed += mappedFrameCount; + + result = ma_resource_manager_data_stream_unmap(pDataStream, mappedFrameCount); + if (result != MA_SUCCESS) { + break; /* This is really bad - will only get an error here if we failed to post a job to the queue for loading the next page. */ + } + } + + if (pFramesRead != NULL) { + *pFramesRead = totalFramesProcessed; + } + + if (result == MA_SUCCESS && totalFramesProcessed == 0) { + result = MA_AT_END; + } + + return result; +} + +MA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameIndex) +{ + ma_job job; + ma_result streamResult; + + streamResult = ma_resource_manager_data_stream_result(pDataStream); + + /* We cannot be using the data source after it's been uninitialized. */ + MA_ASSERT(streamResult != MA_UNAVAILABLE); + + if (pDataStream == NULL) { + return MA_INVALID_ARGS; + } + + if (streamResult != MA_SUCCESS && streamResult != MA_BUSY) { + return MA_INVALID_OPERATION; + } + + /* If we're not already seeking and we're sitting on the same frame, just make this a no-op. */ + if (c89atomic_load_32(&pDataStream->seekCounter) == 0) { + if (c89atomic_load_64(&pDataStream->absoluteCursor) == frameIndex) { + return MA_SUCCESS; + } + } + + + /* Increment the seek counter first to indicate to read_paged_pcm_frames() and map_paged_pcm_frames() that we are in the middle of a seek and MA_BUSY should be returned. */ + c89atomic_fetch_add_32(&pDataStream->seekCounter, 1); + + /* Update the absolute cursor so that ma_resource_manager_data_stream_get_cursor_in_pcm_frames() returns the new position. */ + ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, frameIndex); + + /* + We need to clear our currently loaded pages so that the stream starts playback from the new seek point as soon as possible. These are for the purpose of the public + API and will be ignored by the seek job. The seek job will operate on the assumption that both pages have been marked as invalid and the cursor is at the start of + the first page. + */ + pDataStream->relativeCursor = 0; + pDataStream->currentPageIndex = 0; + c89atomic_exchange_32(&pDataStream->isPageValid[0], MA_FALSE); + c89atomic_exchange_32(&pDataStream->isPageValid[1], MA_FALSE); + + /* Make sure the data stream is not marked as at the end or else if we seek in response to hitting the end, we won't be able to read any more data. */ + c89atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_FALSE); + + /* + The public API is not allowed to touch the internal decoder so we need to use a job to perform the seek. When seeking, the job thread will assume both pages + are invalid and any content contained within them will be discarded and replaced with newly decoded data. + */ + job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM); + job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); + job.data.resourceManager.seekDataStream.pDataStream = pDataStream; + job.data.resourceManager.seekDataStream.frameIndex = frameIndex; + return ma_resource_manager_post_job(pDataStream->pResourceManager, &job); +} + +MA_API ma_result ma_resource_manager_data_stream_get_data_format(ma_resource_manager_data_stream* pDataStream, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + /* We cannot be using the data source after it's been uninitialized. */ + MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); + + if (pFormat != NULL) { + *pFormat = ma_format_unknown; + } + + if (pChannels != NULL) { + *pChannels = 0; + } + + if (pSampleRate != NULL) { + *pSampleRate = 0; + } + + if (pChannelMap != NULL) { + MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); + } + + if (pDataStream == NULL) { + return MA_INVALID_ARGS; + } + + if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { + return MA_INVALID_OPERATION; + } + + /* + We're being a little bit naughty here and accessing the internal decoder from the public API. The output data format is constant, and we've defined this function + such that the application is responsible for ensuring it's not called while uninitializing so it should be safe. + */ + return ma_data_source_get_data_format(&pDataStream->decoder, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); +} + +MA_API ma_result ma_resource_manager_data_stream_get_cursor_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pCursor) +{ + ma_result result; + + if (pCursor == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = 0; + + /* We cannot be using the data source after it's been uninitialized. */ + MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); + + if (pDataStream == NULL) { + return MA_INVALID_ARGS; + } + + /* + If the stream is in an erroneous state we need to return an invalid operation. We can allow + this to be called when the data stream is in a busy state because the caller may have asked + for an initial seek position and it's convenient to return that as the cursor position. + */ + result = ma_resource_manager_data_stream_result(pDataStream); + if (result != MA_SUCCESS && result != MA_BUSY) { + return MA_INVALID_OPERATION; + } + + *pCursor = c89atomic_load_64(&pDataStream->absoluteCursor); + + return MA_SUCCESS; +} + +MA_API ma_result ma_resource_manager_data_stream_get_length_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pLength) +{ + ma_result streamResult; + + if (pLength == NULL) { + return MA_INVALID_ARGS; + } + + *pLength = 0; + + streamResult = ma_resource_manager_data_stream_result(pDataStream); + + /* We cannot be using the data source after it's been uninitialized. */ + MA_ASSERT(streamResult != MA_UNAVAILABLE); + + if (pDataStream == NULL) { + return MA_INVALID_ARGS; + } + + if (streamResult != MA_SUCCESS) { + return streamResult; + } + + /* + We most definitely do not want to be calling ma_decoder_get_length_in_pcm_frames() directly. Instead we want to use a cached value that we + calculated when we initialized it on the job thread. + */ + *pLength = pDataStream->totalLengthInPCMFrames; + if (*pLength == 0) { + return MA_NOT_IMPLEMENTED; /* Some decoders may not have a known length. */ + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_resource_manager_data_stream_result(const ma_resource_manager_data_stream* pDataStream) +{ + if (pDataStream == NULL) { + return MA_INVALID_ARGS; + } + + return (ma_result)c89atomic_load_i32(&pDataStream->result); +} + +MA_API ma_result ma_resource_manager_data_stream_set_looping(ma_resource_manager_data_stream* pDataStream, ma_bool32 isLooping) +{ + return ma_data_source_set_looping(pDataStream, isLooping); +} + +MA_API ma_bool32 ma_resource_manager_data_stream_is_looping(const ma_resource_manager_data_stream* pDataStream) +{ + if (pDataStream == NULL) { + return MA_FALSE; + } + + return c89atomic_load_32((ma_bool32*)&pDataStream->isLooping); /* Naughty const-cast. Value won't change from here in practice (maybe from another thread). */ +} + +MA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pAvailableFrames) +{ + ma_uint32 pageIndex0; + ma_uint32 pageIndex1; + ma_uint32 relativeCursor; + ma_uint64 availableFrames; + + if (pAvailableFrames == NULL) { + return MA_INVALID_ARGS; + } + + *pAvailableFrames = 0; + + if (pDataStream == NULL) { + return MA_INVALID_ARGS; + } + + pageIndex0 = pDataStream->currentPageIndex; + pageIndex1 = (pDataStream->currentPageIndex + 1) & 0x01; + relativeCursor = pDataStream->relativeCursor; + + availableFrames = 0; + if (c89atomic_load_32(&pDataStream->isPageValid[pageIndex0])) { + availableFrames += c89atomic_load_32(&pDataStream->pageFrameCount[pageIndex0]) - relativeCursor; + if (c89atomic_load_32(&pDataStream->isPageValid[pageIndex1])) { + availableFrames += c89atomic_load_32(&pDataStream->pageFrameCount[pageIndex1]); + } + } + + *pAvailableFrames = availableFrames; + return MA_SUCCESS; +} + + +static ma_result ma_resource_manager_data_source_preinit(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource) +{ + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pDataSource); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pResourceManager == NULL) { + return MA_INVALID_ARGS; + } + + pDataSource->flags = pConfig->flags; + + return MA_SUCCESS; +} + +MA_API ma_result ma_resource_manager_data_source_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource) +{ + ma_result result; + + result = ma_resource_manager_data_source_preinit(pResourceManager, pConfig, pDataSource); + if (result != MA_SUCCESS) { + return result; + } + + /* The data source itself is just a data stream or a data buffer. */ + if ((pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return ma_resource_manager_data_stream_init_ex(pResourceManager, pConfig, &pDataSource->backend.stream); + } else { + return ma_resource_manager_data_buffer_init_ex(pResourceManager, pConfig, &pDataSource->backend.buffer); + } +} + +MA_API ma_result ma_resource_manager_data_source_init(ma_resource_manager* pResourceManager, const char* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource) +{ + ma_resource_manager_data_source_config config; + + config = ma_resource_manager_data_source_config_init(); + config.pFilePath = pName; + config.flags = flags; + config.pNotifications = pNotifications; + + return ma_resource_manager_data_source_init_ex(pResourceManager, &config, pDataSource); +} + +MA_API ma_result ma_resource_manager_data_source_init_w(ma_resource_manager* pResourceManager, const wchar_t* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource) +{ + ma_resource_manager_data_source_config config; + + config = ma_resource_manager_data_source_config_init(); + config.pFilePathW = pName; + config.flags = flags; + config.pNotifications = pNotifications; + + return ma_resource_manager_data_source_init_ex(pResourceManager, &config, pDataSource); +} + +MA_API ma_result ma_resource_manager_data_source_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source* pExistingDataSource, ma_resource_manager_data_source* pDataSource) +{ + ma_result result; + ma_resource_manager_data_source_config config; + + if (pExistingDataSource == NULL) { + return MA_INVALID_ARGS; + } + + config = ma_resource_manager_data_source_config_init(); + config.flags = pExistingDataSource->flags; + + result = ma_resource_manager_data_source_preinit(pResourceManager, &config, pDataSource); + if (result != MA_SUCCESS) { + return result; + } + + /* Copying can only be done from data buffers. Streams cannot be copied. */ + if ((pExistingDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return MA_INVALID_OPERATION; + } + + return ma_resource_manager_data_buffer_init_copy(pResourceManager, &pExistingDataSource->backend.buffer, &pDataSource->backend.buffer); +} + +MA_API ma_result ma_resource_manager_data_source_uninit(ma_resource_manager_data_source* pDataSource) +{ + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + /* All we need to is uninitialize the underlying data buffer or data stream. */ + if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return ma_resource_manager_data_stream_uninit(&pDataSource->backend.stream); + } else { + return ma_resource_manager_data_buffer_uninit(&pDataSource->backend.buffer); + } +} + +MA_API ma_result ma_resource_manager_data_source_read_pcm_frames(ma_resource_manager_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + /* Safety. */ + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return ma_resource_manager_data_stream_read_pcm_frames(&pDataSource->backend.stream, pFramesOut, frameCount, pFramesRead); + } else { + return ma_resource_manager_data_buffer_read_pcm_frames(&pDataSource->backend.buffer, pFramesOut, frameCount, pFramesRead); + } +} + +MA_API ma_result ma_resource_manager_data_source_seek_to_pcm_frame(ma_resource_manager_data_source* pDataSource, ma_uint64 frameIndex) +{ + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return ma_resource_manager_data_stream_seek_to_pcm_frame(&pDataSource->backend.stream, frameIndex); + } else { + return ma_resource_manager_data_buffer_seek_to_pcm_frame(&pDataSource->backend.buffer, frameIndex); + } +} + +MA_API ma_result ma_resource_manager_data_source_map(ma_resource_manager_data_source* pDataSource, void** ppFramesOut, ma_uint64* pFrameCount) +{ + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return ma_resource_manager_data_stream_map(&pDataSource->backend.stream, ppFramesOut, pFrameCount); + } else { + return MA_NOT_IMPLEMENTED; /* Mapping not supported with data buffers. */ + } +} + +MA_API ma_result ma_resource_manager_data_source_unmap(ma_resource_manager_data_source* pDataSource, ma_uint64 frameCount) +{ + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return ma_resource_manager_data_stream_unmap(&pDataSource->backend.stream, frameCount); + } else { + return MA_NOT_IMPLEMENTED; /* Mapping not supported with data buffers. */ + } +} + +MA_API ma_result ma_resource_manager_data_source_get_data_format(ma_resource_manager_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return ma_resource_manager_data_stream_get_data_format(&pDataSource->backend.stream, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); + } else { + return ma_resource_manager_data_buffer_get_data_format(&pDataSource->backend.buffer, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); + } +} + +MA_API ma_result ma_resource_manager_data_source_get_cursor_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pCursor) +{ + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return ma_resource_manager_data_stream_get_cursor_in_pcm_frames(&pDataSource->backend.stream, pCursor); + } else { + return ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(&pDataSource->backend.buffer, pCursor); + } +} + +MA_API ma_result ma_resource_manager_data_source_get_length_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pLength) +{ + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return ma_resource_manager_data_stream_get_length_in_pcm_frames(&pDataSource->backend.stream, pLength); + } else { + return ma_resource_manager_data_buffer_get_length_in_pcm_frames(&pDataSource->backend.buffer, pLength); + } +} + +MA_API ma_result ma_resource_manager_data_source_result(const ma_resource_manager_data_source* pDataSource) +{ + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return ma_resource_manager_data_stream_result(&pDataSource->backend.stream); + } else { + return ma_resource_manager_data_buffer_result(&pDataSource->backend.buffer); + } +} + +MA_API ma_result ma_resource_manager_data_source_set_looping(ma_resource_manager_data_source* pDataSource, ma_bool32 isLooping) +{ + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return ma_resource_manager_data_stream_set_looping(&pDataSource->backend.stream, isLooping); + } else { + return ma_resource_manager_data_buffer_set_looping(&pDataSource->backend.buffer, isLooping); + } +} + +MA_API ma_bool32 ma_resource_manager_data_source_is_looping(const ma_resource_manager_data_source* pDataSource) +{ + if (pDataSource == NULL) { + return MA_FALSE; + } + + if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return ma_resource_manager_data_stream_is_looping(&pDataSource->backend.stream); + } else { + return ma_resource_manager_data_buffer_is_looping(&pDataSource->backend.buffer); + } +} + +MA_API ma_result ma_resource_manager_data_source_get_available_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pAvailableFrames) +{ + if (pAvailableFrames == NULL) { + return MA_INVALID_ARGS; + } + + *pAvailableFrames = 0; + + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return ma_resource_manager_data_stream_get_available_frames(&pDataSource->backend.stream, pAvailableFrames); + } else { + return ma_resource_manager_data_buffer_get_available_frames(&pDataSource->backend.buffer, pAvailableFrames); + } +} + + +MA_API ma_result ma_resource_manager_post_job(ma_resource_manager* pResourceManager, const ma_job* pJob) +{ + if (pResourceManager == NULL) { + return MA_INVALID_ARGS; + } + + return ma_job_queue_post(&pResourceManager->jobQueue, pJob); +} + +MA_API ma_result ma_resource_manager_post_job_quit(ma_resource_manager* pResourceManager) +{ + ma_job job = ma_job_init(MA_JOB_TYPE_QUIT); + return ma_resource_manager_post_job(pResourceManager, &job); +} + +MA_API ma_result ma_resource_manager_next_job(ma_resource_manager* pResourceManager, ma_job* pJob) +{ + if (pResourceManager == NULL) { + return MA_INVALID_ARGS; + } + + return ma_job_queue_next(&pResourceManager->jobQueue, pJob); +} + + +static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob) +{ + ma_result result = MA_SUCCESS; + ma_resource_manager* pResourceManager; + ma_resource_manager_data_buffer_node* pDataBufferNode; + + MA_ASSERT(pJob != NULL); + + pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.loadDataBufferNode.pResourceManager; + MA_ASSERT(pResourceManager != NULL); + + pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.loadDataBufferNode.pDataBufferNode; + MA_ASSERT(pDataBufferNode != NULL); + MA_ASSERT(pDataBufferNode->isDataOwnedByResourceManager == MA_TRUE); /* The data should always be owned by the resource manager. */ + + /* The data buffer is not getting deleted, but we may be getting executed out of order. If so, we need to push the job back onto the queue and return. */ + if (pJob->order != c89atomic_load_32(&pDataBufferNode->executionPointer)) { + return ma_resource_manager_post_job(pResourceManager, pJob); /* Attempting to execute out of order. Probably interleaved with a MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER job. */ + } + + /* First thing we need to do is check whether or not the data buffer is getting deleted. If so we just abort. */ + if (ma_resource_manager_data_buffer_node_result(pDataBufferNode) != MA_BUSY) { + result = ma_resource_manager_data_buffer_node_result(pDataBufferNode); /* The data buffer may be getting deleted before it's even been loaded. */ + goto done; + } + + /* + We're ready to start loading. Essentially what we're doing here is initializing the data supply + of the node. Once this is complete, data buffers can have their connectors initialized which + will allow then to have audio data read from them. + + Note that when the data supply type has been moved away from "unknown", that is when other threads + will determine that the node is available for data delivery and the data buffer connectors can be + initialized. Therefore, it's important that it is set after the data supply has been initialized. + */ + if ((pJob->data.resourceManager.loadDataBufferNode.flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE) != 0) { + /* + Decoding. This is the complex case because we're not going to be doing the entire decoding + process here. Instead it's going to be split of multiple jobs and loaded in pages. The + reason for this is to evenly distribute decoding time across multiple sounds, rather than + having one huge sound hog all the available processing resources. + + The first thing we do is initialize a decoder. This is allocated on the heap and is passed + around to the paging jobs. When the last paging job has completed it's processing, it'll + free the decoder for us. + + This job does not do any actual decoding. It instead just posts a PAGE_DATA_BUFFER_NODE job + which is where the actual decoding work will be done. However, once this job is complete, + the node will be in a state where data buffer connectors can be initialized. + */ + ma_decoder* pDecoder; /* <-- Free'd on the last page decode. */ + ma_job pageDataBufferNodeJob; + + /* Allocate the decoder by initializing a decoded data supply. */ + result = ma_resource_manager_data_buffer_node_init_supply_decoded(pResourceManager, pDataBufferNode, pJob->data.resourceManager.loadDataBufferNode.pFilePath, pJob->data.resourceManager.loadDataBufferNode.pFilePathW, pJob->data.resourceManager.loadDataBufferNode.flags, &pDecoder); + + /* + Don't ever propagate an MA_BUSY result code or else the resource manager will think the + node is just busy decoding rather than in an error state. This should never happen, but + including this logic for safety just in case. + */ + if (result == MA_BUSY) { + result = MA_ERROR; + } + + if (result != MA_SUCCESS) { + if (pJob->data.resourceManager.loadDataBufferNode.pFilePath != NULL) { + ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to initialize data supply for \"%s\". %s.\n", pJob->data.resourceManager.loadDataBufferNode.pFilePath, ma_result_description(result)); + } else { + #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER) + ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to initialize data supply for \"%ls\", %s.\n", pJob->data.resourceManager.loadDataBufferNode.pFilePathW, ma_result_description(result)); + #endif + } + + goto done; + } + + /* + At this point the node's data supply is initialized and other threads can start initializing + their data buffer connectors. However, no data will actually be available until we start to + actually decode it. To do this, we need to post a paging job which is where the decoding + work is done. + + Note that if an error occurred at an earlier point, this section will have been skipped. + */ + pageDataBufferNodeJob = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE); + pageDataBufferNodeJob.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); + pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pResourceManager = pResourceManager; + pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDataBufferNode = pDataBufferNode; + pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDecoder = pDecoder; + pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDoneNotification = pJob->data.resourceManager.loadDataBufferNode.pDoneNotification; + pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDoneFence = pJob->data.resourceManager.loadDataBufferNode.pDoneFence; + + /* The job has been set up so it can now be posted. */ + result = ma_resource_manager_post_job(pResourceManager, &pageDataBufferNodeJob); + + /* + When we get here, we want to make sure the result code is set to MA_BUSY. The reason for + this is that the result will be copied over to the node's internal result variable. In + this case, since the decoding is still in-progress, we need to make sure the result code + is set to MA_BUSY. + */ + if (result != MA_SUCCESS) { + ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE job. %s\n", ma_result_description(result)); + ma_decoder_uninit(pDecoder); + ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); + } else { + result = MA_BUSY; + } + } else { + /* No decoding. This is the simple case. We need only read the file content into memory and we're done. */ + result = ma_resource_manager_data_buffer_node_init_supply_encoded(pResourceManager, pDataBufferNode, pJob->data.resourceManager.loadDataBufferNode.pFilePath, pJob->data.resourceManager.loadDataBufferNode.pFilePathW); + } + + +done: + /* File paths are no longer needed. */ + ma_free(pJob->data.resourceManager.loadDataBufferNode.pFilePath, &pResourceManager->config.allocationCallbacks); + ma_free(pJob->data.resourceManager.loadDataBufferNode.pFilePathW, &pResourceManager->config.allocationCallbacks); + + /* + We need to set the result to at the very end to ensure no other threads try reading the data before we've fully initialized the object. Other threads + are going to be inspecting this variable to determine whether or not they're ready to read data. We can only change the result if it's set to MA_BUSY + because otherwise we may be changing away from an error code which would be bad. An example is if the application creates a data buffer, but then + immediately deletes it before we've got to this point. In this case, pDataBuffer->result will be MA_UNAVAILABLE, and setting it to MA_SUCCESS or any + other error code would cause the buffer to look like it's in a state that it's not. + */ + c89atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result); + + /* At this point initialization is complete and we can signal the notification if any. */ + if (pJob->data.resourceManager.loadDataBufferNode.pInitNotification != NULL) { + ma_async_notification_signal(pJob->data.resourceManager.loadDataBufferNode.pInitNotification); + } + if (pJob->data.resourceManager.loadDataBufferNode.pInitFence != NULL) { + ma_fence_release(pJob->data.resourceManager.loadDataBufferNode.pInitFence); + } + + /* If we have a success result it means we've fully loaded the buffer. This will happen in the non-decoding case. */ + if (result != MA_BUSY) { + if (pJob->data.resourceManager.loadDataBufferNode.pDoneNotification != NULL) { + ma_async_notification_signal(pJob->data.resourceManager.loadDataBufferNode.pDoneNotification); + } + if (pJob->data.resourceManager.loadDataBufferNode.pDoneFence != NULL) { + ma_fence_release(pJob->data.resourceManager.loadDataBufferNode.pDoneFence); + } + } + + /* Increment the node's execution pointer so that the next jobs can be processed. This is how we keep decoding of pages in-order. */ + c89atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); + return result; +} + +static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob) +{ + ma_resource_manager* pResourceManager; + ma_resource_manager_data_buffer_node* pDataBufferNode; + + MA_ASSERT(pJob != NULL); + + pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.freeDataBufferNode.pResourceManager; + MA_ASSERT(pResourceManager != NULL); + + pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.freeDataBufferNode.pDataBufferNode; + MA_ASSERT(pDataBufferNode != NULL); + + if (pJob->order != c89atomic_load_32(&pDataBufferNode->executionPointer)) { + return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ + } + + ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode); + + /* The event needs to be signalled last. */ + if (pJob->data.resourceManager.freeDataBufferNode.pDoneNotification != NULL) { + ma_async_notification_signal(pJob->data.resourceManager.freeDataBufferNode.pDoneNotification); + } + + if (pJob->data.resourceManager.freeDataBufferNode.pDoneFence != NULL) { + ma_fence_release(pJob->data.resourceManager.freeDataBufferNode.pDoneFence); + } + + c89atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); + return MA_SUCCESS; +} + +static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob) +{ + ma_result result = MA_SUCCESS; + ma_resource_manager* pResourceManager; + ma_resource_manager_data_buffer_node* pDataBufferNode; + + MA_ASSERT(pJob != NULL); + + pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.pageDataBufferNode.pResourceManager; + MA_ASSERT(pResourceManager != NULL); + + pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.pageDataBufferNode.pDataBufferNode; + MA_ASSERT(pDataBufferNode != NULL); + + if (pJob->order != c89atomic_load_32(&pDataBufferNode->executionPointer)) { + return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ + } + + /* Don't do any more decoding if the data buffer has started the uninitialization process. */ + result = ma_resource_manager_data_buffer_node_result(pDataBufferNode); + if (result != MA_BUSY) { + goto done; + } + + /* We're ready to decode the next page. */ + result = ma_resource_manager_data_buffer_node_decode_next_page(pResourceManager, pDataBufferNode, (ma_decoder*)pJob->data.resourceManager.pageDataBufferNode.pDecoder); + + /* + If we have a success code by this point, we want to post another job. We're going to set the + result back to MA_BUSY to make it clear that there's still more to load. + */ + if (result == MA_SUCCESS) { + ma_job newJob; + newJob = *pJob; /* Everything is the same as the input job, except the execution order. */ + newJob.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); /* We need a fresh execution order. */ + + result = ma_resource_manager_post_job(pResourceManager, &newJob); + + /* Since the sound isn't yet fully decoded we want the status to be set to busy. */ + if (result == MA_SUCCESS) { + result = MA_BUSY; + } + } + +done: + /* If there's still more to decode the result will be set to MA_BUSY. Otherwise we can free the decoder. */ + if (result != MA_BUSY) { + ma_decoder_uninit((ma_decoder*)pJob->data.resourceManager.pageDataBufferNode.pDecoder); + ma_free(pJob->data.resourceManager.pageDataBufferNode.pDecoder, &pResourceManager->config.allocationCallbacks); + } + + /* If we reached the end we need to treat it as successful. */ + if (result == MA_AT_END) { + result = MA_SUCCESS; + } + + /* Make sure we set the result of node in case some error occurred. */ + c89atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result); + + /* Signal the notification after setting the result in case the notification callback wants to inspect the result code. */ + if (result != MA_BUSY) { + if (pJob->data.resourceManager.pageDataBufferNode.pDoneNotification != NULL) { + ma_async_notification_signal(pJob->data.resourceManager.pageDataBufferNode.pDoneNotification); + } + + if (pJob->data.resourceManager.pageDataBufferNode.pDoneFence != NULL) { + ma_fence_release(pJob->data.resourceManager.pageDataBufferNode.pDoneFence); + } + } + + c89atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); + return result; +} + + +static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob) +{ + ma_result result = MA_SUCCESS; + ma_resource_manager* pResourceManager; + ma_resource_manager_data_buffer* pDataBuffer; + ma_resource_manager_data_supply_type dataSupplyType = ma_resource_manager_data_supply_type_unknown; + ma_bool32 isConnectorInitialized = MA_FALSE; + + /* + All we're doing here is checking if the node has finished loading. If not, we just re-post the job + and keep waiting. Otherwise we increment the execution counter and set the buffer's result code. + */ + MA_ASSERT(pJob != NULL); + + pDataBuffer = (ma_resource_manager_data_buffer*)pJob->data.resourceManager.loadDataBuffer.pDataBuffer; + MA_ASSERT(pDataBuffer != NULL); + + pResourceManager = pDataBuffer->pResourceManager; + + if (pJob->order != c89atomic_load_32(&pDataBuffer->executionPointer)) { + return ma_resource_manager_post_job(pResourceManager, pJob); /* Attempting to execute out of order. Probably interleaved with a MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER job. */ + } + + /* + First thing we need to do is check whether or not the data buffer is getting deleted. If so we + just abort, but making sure we increment the execution pointer. + */ + result = ma_resource_manager_data_buffer_result(pDataBuffer); + if (result != MA_BUSY) { + goto done; /* <-- This will ensure the exucution pointer is incremented. */ + } else { + result = MA_SUCCESS; /* <-- Make sure this is reset. */ + } + + /* Try initializing the connector if we haven't already. */ + isConnectorInitialized = pDataBuffer->isConnectorInitialized; + if (isConnectorInitialized == MA_FALSE) { + dataSupplyType = ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode); + + if (dataSupplyType != ma_resource_manager_data_supply_type_unknown) { + /* We can now initialize the connector. If this fails, we need to abort. It's very rare for this to fail. */ + ma_resource_manager_data_source_config dataSourceConfig; /* For setting initial looping state and range. */ + dataSourceConfig = ma_resource_manager_data_source_config_init(); + dataSourceConfig.rangeBegInPCMFrames = pJob->data.resourceManager.loadDataBuffer.rangeBegInPCMFrames; + dataSourceConfig.rangeEndInPCMFrames = pJob->data.resourceManager.loadDataBuffer.rangeEndInPCMFrames; + dataSourceConfig.loopPointBegInPCMFrames = pJob->data.resourceManager.loadDataBuffer.loopPointBegInPCMFrames; + dataSourceConfig.loopPointEndInPCMFrames = pJob->data.resourceManager.loadDataBuffer.loopPointEndInPCMFrames; + dataSourceConfig.isLooping = pJob->data.resourceManager.loadDataBuffer.isLooping; + + result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, &dataSourceConfig, pJob->data.resourceManager.loadDataBuffer.pInitNotification, pJob->data.resourceManager.loadDataBuffer.pInitFence); + if (result != MA_SUCCESS) { + ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to initialize connector for data buffer. %s.\n", ma_result_description(result)); + goto done; + } + } else { + /* Don't have a known data supply type. Most likely the data buffer node is still loading, but it could be that an error occurred. */ + } + } else { + /* The connector is already initialized. Nothing to do here. */ + } + + /* + If the data node is still loading, we need to repost the job and *not* increment the execution + pointer (i.e. we need to not fall through to the "done" label). + + There is a hole between here and the where the data connector is initialized where the data + buffer node may have finished initializing. We need to check for this by checking the result of + the data buffer node and whether or not we had an unknown data supply type at the time of + trying to initialize the data connector. + */ + result = ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode); + if (result == MA_BUSY || (result == MA_SUCCESS && isConnectorInitialized == MA_FALSE && dataSupplyType == ma_resource_manager_data_supply_type_unknown)) { + return ma_resource_manager_post_job(pResourceManager, pJob); + } + +done: + /* Only move away from a busy code so that we don't trash any existing error codes. */ + c89atomic_compare_and_swap_i32(&pDataBuffer->result, MA_BUSY, result); + + /* Only signal the other threads after the result has been set just for cleanliness sake. */ + if (pJob->data.resourceManager.loadDataBuffer.pDoneNotification != NULL) { + ma_async_notification_signal(pJob->data.resourceManager.loadDataBuffer.pDoneNotification); + } + if (pJob->data.resourceManager.loadDataBuffer.pDoneFence != NULL) { + ma_fence_release(pJob->data.resourceManager.loadDataBuffer.pDoneFence); + } + + /* + If at this point the data buffer has not had it's connector initialized, it means the + notification event was never signalled which means we need to signal it here. + */ + if (pDataBuffer->isConnectorInitialized == MA_FALSE && result != MA_SUCCESS) { + if (pJob->data.resourceManager.loadDataBuffer.pInitNotification != NULL) { + ma_async_notification_signal(pJob->data.resourceManager.loadDataBuffer.pInitNotification); + } + if (pJob->data.resourceManager.loadDataBuffer.pInitFence != NULL) { + ma_fence_release(pJob->data.resourceManager.loadDataBuffer.pInitFence); + } + } + + c89atomic_fetch_add_32(&pDataBuffer->executionPointer, 1); + return result; +} + +static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob) +{ + ma_resource_manager* pResourceManager; + ma_resource_manager_data_buffer* pDataBuffer; + + MA_ASSERT(pJob != NULL); + + pDataBuffer = (ma_resource_manager_data_buffer*)pJob->data.resourceManager.freeDataBuffer.pDataBuffer; + MA_ASSERT(pDataBuffer != NULL); + + pResourceManager = pDataBuffer->pResourceManager; + + if (pJob->order != c89atomic_load_32(&pDataBuffer->executionPointer)) { + return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ + } + + ma_resource_manager_data_buffer_uninit_internal(pDataBuffer); + + /* The event needs to be signalled last. */ + if (pJob->data.resourceManager.freeDataBuffer.pDoneNotification != NULL) { + ma_async_notification_signal(pJob->data.resourceManager.freeDataBuffer.pDoneNotification); + } + + if (pJob->data.resourceManager.freeDataBuffer.pDoneFence != NULL) { + ma_fence_release(pJob->data.resourceManager.freeDataBuffer.pDoneFence); + } + + c89atomic_fetch_add_32(&pDataBuffer->executionPointer, 1); + return MA_SUCCESS; +} + +static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob) +{ + ma_result result = MA_SUCCESS; + ma_decoder_config decoderConfig; + ma_uint32 pageBufferSizeInBytes; + ma_resource_manager* pResourceManager; + ma_resource_manager_data_stream* pDataStream; + + MA_ASSERT(pJob != NULL); + + pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.loadDataStream.pDataStream; + MA_ASSERT(pDataStream != NULL); + + pResourceManager = pDataStream->pResourceManager; + + if (pJob->order != c89atomic_load_32(&pDataStream->executionPointer)) { + return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ + } + + if (ma_resource_manager_data_stream_result(pDataStream) != MA_BUSY) { + result = MA_INVALID_OPERATION; /* Most likely the data stream is being uninitialized. */ + goto done; + } + + /* We need to initialize the decoder first so we can determine the size of the pages. */ + decoderConfig = ma_resource_manager__init_decoder_config(pResourceManager); + + if (pJob->data.resourceManager.loadDataStream.pFilePath != NULL) { + result = ma_decoder_init_vfs(pResourceManager->config.pVFS, pJob->data.resourceManager.loadDataStream.pFilePath, &decoderConfig, &pDataStream->decoder); + } else { + result = ma_decoder_init_vfs_w(pResourceManager->config.pVFS, pJob->data.resourceManager.loadDataStream.pFilePathW, &decoderConfig, &pDataStream->decoder); + } + if (result != MA_SUCCESS) { + goto done; + } + + /* Retrieve the total length of the file before marking the decoder are loaded. */ + if ((pDataStream->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH) == 0) { + result = ma_decoder_get_length_in_pcm_frames(&pDataStream->decoder, &pDataStream->totalLengthInPCMFrames); + if (result != MA_SUCCESS) { + goto done; /* Failed to retrieve the length. */ + } + } else { + pDataStream->totalLengthInPCMFrames = 0; + } + + /* + Only mark the decoder as initialized when the length of the decoder has been retrieved because that can possibly require a scan over the whole file + and we don't want to have another thread trying to access the decoder while it's scanning. + */ + pDataStream->isDecoderInitialized = MA_TRUE; + + /* We have the decoder so we can now initialize our page buffer. */ + pageBufferSizeInBytes = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream) * 2 * ma_get_bytes_per_frame(pDataStream->decoder.outputFormat, pDataStream->decoder.outputChannels); + + pDataStream->pPageData = ma_malloc(pageBufferSizeInBytes, &pResourceManager->config.allocationCallbacks); + if (pDataStream->pPageData == NULL) { + ma_decoder_uninit(&pDataStream->decoder); + result = MA_OUT_OF_MEMORY; + goto done; + } + + /* Seek to our initial seek point before filling the initial pages. */ + ma_decoder_seek_to_pcm_frame(&pDataStream->decoder, pJob->data.resourceManager.loadDataStream.initialSeekPoint); + + /* We have our decoder and our page buffer, so now we need to fill our pages. */ + ma_resource_manager_data_stream_fill_pages(pDataStream); + + /* And now we're done. We want to make sure the result is MA_SUCCESS. */ + result = MA_SUCCESS; + +done: + ma_free(pJob->data.resourceManager.loadDataStream.pFilePath, &pResourceManager->config.allocationCallbacks); + ma_free(pJob->data.resourceManager.loadDataStream.pFilePathW, &pResourceManager->config.allocationCallbacks); + + /* We can only change the status away from MA_BUSY. If it's set to anything else it means an error has occurred somewhere or the uninitialization process has started (most likely). */ + c89atomic_compare_and_swap_i32(&pDataStream->result, MA_BUSY, result); + + /* Only signal the other threads after the result has been set just for cleanliness sake. */ + if (pJob->data.resourceManager.loadDataStream.pInitNotification != NULL) { + ma_async_notification_signal(pJob->data.resourceManager.loadDataStream.pInitNotification); + } + if (pJob->data.resourceManager.loadDataStream.pInitFence != NULL) { + ma_fence_release(pJob->data.resourceManager.loadDataStream.pInitFence); + } + + c89atomic_fetch_add_32(&pDataStream->executionPointer, 1); + return result; +} + +static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob) +{ + ma_resource_manager* pResourceManager; + ma_resource_manager_data_stream* pDataStream; + + MA_ASSERT(pJob != NULL); + + pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.freeDataStream.pDataStream; + MA_ASSERT(pDataStream != NULL); + + pResourceManager = pDataStream->pResourceManager; + + if (pJob->order != c89atomic_load_32(&pDataStream->executionPointer)) { + return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ + } + + /* If our status is not MA_UNAVAILABLE we have a bug somewhere. */ + MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) == MA_UNAVAILABLE); + + if (pDataStream->isDecoderInitialized) { + ma_decoder_uninit(&pDataStream->decoder); + } + + if (pDataStream->pPageData != NULL) { + ma_free(pDataStream->pPageData, &pResourceManager->config.allocationCallbacks); + pDataStream->pPageData = NULL; /* Just in case... */ + } + + ma_data_source_uninit(&pDataStream->ds); + + /* The event needs to be signalled last. */ + if (pJob->data.resourceManager.freeDataStream.pDoneNotification != NULL) { + ma_async_notification_signal(pJob->data.resourceManager.freeDataStream.pDoneNotification); + } + if (pJob->data.resourceManager.freeDataStream.pDoneFence != NULL) { + ma_fence_release(pJob->data.resourceManager.freeDataStream.pDoneFence); + } + + /*c89atomic_fetch_add_32(&pDataStream->executionPointer, 1);*/ + return MA_SUCCESS; +} + +static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob) +{ + ma_result result = MA_SUCCESS; + ma_resource_manager* pResourceManager; + ma_resource_manager_data_stream* pDataStream; + + MA_ASSERT(pJob != NULL); + + pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.pageDataStream.pDataStream; + MA_ASSERT(pDataStream != NULL); + + pResourceManager = pDataStream->pResourceManager; + + if (pJob->order != c89atomic_load_32(&pDataStream->executionPointer)) { + return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ + } + + /* For streams, the status should be MA_SUCCESS. */ + if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { + result = MA_INVALID_OPERATION; + goto done; + } + + ma_resource_manager_data_stream_fill_page(pDataStream, pJob->data.resourceManager.pageDataStream.pageIndex); + +done: + c89atomic_fetch_add_32(&pDataStream->executionPointer, 1); + return result; +} + +static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob) +{ + ma_result result = MA_SUCCESS; + ma_resource_manager* pResourceManager; + ma_resource_manager_data_stream* pDataStream; + + MA_ASSERT(pJob != NULL); + + pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.seekDataStream.pDataStream; + MA_ASSERT(pDataStream != NULL); + + pResourceManager = pDataStream->pResourceManager; + + if (pJob->order != c89atomic_load_32(&pDataStream->executionPointer)) { + return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ + } + + /* For streams the status should be MA_SUCCESS for this to do anything. */ + if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS || pDataStream->isDecoderInitialized == MA_FALSE) { + result = MA_INVALID_OPERATION; + goto done; + } + + /* + With seeking we just assume both pages are invalid and the relative frame cursor at position 0. This is basically exactly the same as loading, except + instead of initializing the decoder, we seek to a frame. + */ + ma_decoder_seek_to_pcm_frame(&pDataStream->decoder, pJob->data.resourceManager.seekDataStream.frameIndex); + + /* After seeking we'll need to reload the pages. */ + ma_resource_manager_data_stream_fill_pages(pDataStream); + + /* We need to let the public API know that we're done seeking. */ + c89atomic_fetch_sub_32(&pDataStream->seekCounter, 1); + +done: + c89atomic_fetch_add_32(&pDataStream->executionPointer, 1); + return result; +} + +MA_API ma_result ma_resource_manager_process_job(ma_resource_manager* pResourceManager, ma_job* pJob) +{ + if (pResourceManager == NULL || pJob == NULL) { + return MA_INVALID_ARGS; + } + + return ma_job_process(pJob); +} + +MA_API ma_result ma_resource_manager_process_next_job(ma_resource_manager* pResourceManager) +{ + ma_result result; + ma_job job; + + if (pResourceManager == NULL) { + return MA_INVALID_ARGS; + } + + /* This will return MA_CANCELLED if the next job is a quit job. */ + result = ma_resource_manager_next_job(pResourceManager, &job); + if (result != MA_SUCCESS) { + return result; + } + + return ma_job_process(&job); +} +#else +/* We'll get here if the resource manager is being excluded from the build. We need to define the job processing callbacks as no-ops. */ +static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); } +static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); } +static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); } +static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob) { return ma_job_process__noop(pJob); } +static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob) { return ma_job_process__noop(pJob); } +static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } +static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } +static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } +static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } +#endif /* MA_NO_RESOURCE_MANAGER */ + + +#ifndef MA_NO_NODE_GRAPH +/* 10ms @ 48K = 480. Must never exceed 65535. */ +#ifndef MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS +#define MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS 480 +#endif + + +static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusIndex, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime); + +MA_API void ma_debug_fill_pcm_frames_with_sine_wave(float* pFramesOut, ma_uint32 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) +{ + #ifndef MA_NO_GENERATION + { + ma_waveform_config waveformConfig; + ma_waveform waveform; + + waveformConfig = ma_waveform_config_init(format, channels, sampleRate, ma_waveform_type_sine, 1.0, 400); + ma_waveform_init(&waveformConfig, &waveform); + ma_waveform_read_pcm_frames(&waveform, pFramesOut, frameCount, NULL); + } + #else + { + (void)pFramesOut; + (void)frameCount; + (void)format; + (void)channels; + (void)sampleRate; + #if defined(MA_DEBUG_OUTPUT) + { + #if _MSC_VER + #pragma message ("ma_debug_fill_pcm_frames_with_sine_wave() will do nothing because MA_NO_GENERATION is enabled.") + #endif + } + #endif + } + #endif +} + + + +static ma_result ma_mix_pcm_frames_f32(float* pDst, const float* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume) +{ + ma_uint64 iSample; + ma_uint64 sampleCount; + + if (pDst == NULL || pSrc == NULL || channels == 0) { + return MA_INVALID_ARGS; + } + + if (volume == 0) { + return MA_SUCCESS; /* No changes if the volume is 0. */ + } + + sampleCount = frameCount * channels; + + if (volume == 1) { + for (iSample = 0; iSample < sampleCount; iSample += 1) { + pDst[iSample] += pSrc[iSample]; + } + } else { + for (iSample = 0; iSample < sampleCount; iSample += 1) { + pDst[iSample] += ma_apply_volume_unclipped_f32(pSrc[iSample], volume); + } + } + + return MA_SUCCESS; +} + + +MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels) +{ + ma_node_graph_config config; + + MA_ZERO_OBJECT(&config); + config.channels = channels; + config.nodeCacheCapInFrames = MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS; + + return config; +} + + +static void ma_node_graph_set_is_reading(ma_node_graph* pNodeGraph, ma_bool32 isReading) +{ + MA_ASSERT(pNodeGraph != NULL); + c89atomic_exchange_32(&pNodeGraph->isReading, isReading); +} + +#if 0 +static ma_bool32 ma_node_graph_is_reading(ma_node_graph* pNodeGraph) +{ + MA_ASSERT(pNodeGraph != NULL); + return c89atomic_load_32(&pNodeGraph->isReading); +} +#endif + + +static void ma_node_graph_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_node_graph* pNodeGraph = (ma_node_graph*)pNode; + ma_uint64 framesRead; + + ma_node_graph_read_pcm_frames(pNodeGraph, ppFramesOut[0], *pFrameCountOut, &framesRead); + + *pFrameCountOut = (ma_uint32)framesRead; /* Safe cast. */ + + (void)ppFramesIn; + (void)pFrameCountIn; +} + +static ma_node_vtable g_node_graph_node_vtable = +{ + ma_node_graph_node_process_pcm_frames, + NULL, /* onGetRequiredInputFrameCount */ + 0, /* 0 input buses. */ + 1, /* 1 output bus. */ + 0 /* Flags. */ +}; + +static void ma_node_graph_endpoint_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + MA_ASSERT(pNode != NULL); + MA_ASSERT(ma_node_get_input_bus_count(pNode) == 1); + MA_ASSERT(ma_node_get_output_bus_count(pNode) == 1); + + /* Input channel count needs to be the same as the output channel count. */ + MA_ASSERT(ma_node_get_input_channels(pNode, 0) == ma_node_get_output_channels(pNode, 0)); + + /* We don't need to do anything here because it's a passthrough. */ + (void)pNode; + (void)ppFramesIn; + (void)pFrameCountIn; + (void)ppFramesOut; + (void)pFrameCountOut; + +#if 0 + /* The data has already been mixed. We just need to move it to the output buffer. */ + if (ppFramesIn != NULL) { + ma_copy_pcm_frames(ppFramesOut[0], ppFramesIn[0], *pFrameCountOut, ma_format_f32, ma_node_get_output_channels(pNode, 0)); + } +#endif +} + +static ma_node_vtable g_node_graph_endpoint_vtable = +{ + ma_node_graph_endpoint_process_pcm_frames, + NULL, /* onGetRequiredInputFrameCount */ + 1, /* 1 input bus. */ + 1, /* 1 output bus. */ + MA_NODE_FLAG_PASSTHROUGH /* Flags. The endpoint is a passthrough. */ +}; + +MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node_graph* pNodeGraph) +{ + ma_result result; + ma_node_config baseConfig; + ma_node_config endpointConfig; + + if (pNodeGraph == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pNodeGraph); + pNodeGraph->nodeCacheCapInFrames = pConfig->nodeCacheCapInFrames; + if (pNodeGraph->nodeCacheCapInFrames == 0) { + pNodeGraph->nodeCacheCapInFrames = MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS; + } + + + /* Base node so we can use the node graph as a node into another graph. */ + baseConfig = ma_node_config_init(); + baseConfig.vtable = &g_node_graph_node_vtable; + baseConfig.pOutputChannels = &pConfig->channels; + + result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pNodeGraph->base); + if (result != MA_SUCCESS) { + return result; + } + + + /* Endpoint. */ + endpointConfig = ma_node_config_init(); + endpointConfig.vtable = &g_node_graph_endpoint_vtable; + endpointConfig.pInputChannels = &pConfig->channels; + endpointConfig.pOutputChannels = &pConfig->channels; + + result = ma_node_init(pNodeGraph, &endpointConfig, pAllocationCallbacks, &pNodeGraph->endpoint); + if (result != MA_SUCCESS) { + ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks); + return result; + } + + return MA_SUCCESS; +} + +MA_API void ma_node_graph_uninit(ma_node_graph* pNodeGraph, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pNodeGraph == NULL) { + return; + } + + ma_node_uninit(&pNodeGraph->endpoint, pAllocationCallbacks); +} + +MA_API ma_node* ma_node_graph_get_endpoint(ma_node_graph* pNodeGraph) +{ + if (pNodeGraph == NULL) { + return NULL; + } + + return &pNodeGraph->endpoint; +} + +MA_API ma_result ma_node_graph_read_pcm_frames(ma_node_graph* pNodeGraph, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + ma_result result = MA_SUCCESS; + ma_uint64 totalFramesRead; + ma_uint32 channels; + + if (pFramesRead != NULL) { + *pFramesRead = 0; /* Safety. */ + } + + if (pNodeGraph == NULL) { + return MA_INVALID_ARGS; + } + + channels = ma_node_get_output_channels(&pNodeGraph->endpoint, 0); + + + /* We'll be nice and try to do a full read of all frameCount frames. */ + totalFramesRead = 0; + while (totalFramesRead < frameCount) { + ma_uint32 framesJustRead; + ma_uint64 framesToRead = frameCount - totalFramesRead; + + if (framesToRead > 0xFFFFFFFF) { + framesToRead = 0xFFFFFFFF; + } + + ma_node_graph_set_is_reading(pNodeGraph, MA_TRUE); + { + result = ma_node_read_pcm_frames(&pNodeGraph->endpoint, 0, (float*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels), (ma_uint32)framesToRead, &framesJustRead, ma_node_get_time(&pNodeGraph->endpoint)); + } + ma_node_graph_set_is_reading(pNodeGraph, MA_FALSE); + + totalFramesRead += framesJustRead; + + if (result != MA_SUCCESS) { + break; + } + + /* Abort if we weren't able to read any frames or else we risk getting stuck in a loop. */ + if (framesJustRead == 0) { + break; + } + } + + /* Let's go ahead and silence any leftover frames just for some added safety to ensure the caller doesn't try emitting garbage out of the speakers. */ + if (totalFramesRead < frameCount) { + ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels), (frameCount - totalFramesRead), ma_format_f32, channels); + } + + if (pFramesRead != NULL) { + *pFramesRead = totalFramesRead; + } + + return result; +} + +MA_API ma_uint32 ma_node_graph_get_channels(const ma_node_graph* pNodeGraph) +{ + if (pNodeGraph == NULL) { + return 0; + } + + return ma_node_get_output_channels(&pNodeGraph->endpoint, 0); +} + +MA_API ma_uint64 ma_node_graph_get_time(const ma_node_graph* pNodeGraph) +{ + if (pNodeGraph == NULL) { + return 0; + } + + return ma_node_get_time(&pNodeGraph->endpoint); /* Global time is just the local time of the endpoint. */ +} + +MA_API ma_result ma_node_graph_set_time(ma_node_graph* pNodeGraph, ma_uint64 globalTime) +{ + if (pNodeGraph == NULL) { + return MA_INVALID_ARGS; + } + + return ma_node_set_time(&pNodeGraph->endpoint, globalTime); /* Global time is just the local time of the endpoint. */ +} + + +#define MA_NODE_OUTPUT_BUS_FLAG_HAS_READ 0x01 /* Whether or not this bus ready to read more data. Only used on nodes with multiple output buses. */ + +static ma_result ma_node_output_bus_init(ma_node* pNode, ma_uint32 outputBusIndex, ma_uint32 channels, ma_node_output_bus* pOutputBus) +{ + MA_ASSERT(pOutputBus != NULL); + MA_ASSERT(outputBusIndex < MA_MAX_NODE_BUS_COUNT); + MA_ASSERT(outputBusIndex < ma_node_get_output_bus_count(pNode)); + MA_ASSERT(channels < 256); + + MA_ZERO_OBJECT(pOutputBus); + + if (channels == 0) { + return MA_INVALID_ARGS; + } + + pOutputBus->pNode = pNode; + pOutputBus->outputBusIndex = (ma_uint8)outputBusIndex; + pOutputBus->channels = (ma_uint8)channels; + pOutputBus->flags = MA_NODE_OUTPUT_BUS_FLAG_HAS_READ; /* <-- Important that this flag is set by default. */ + pOutputBus->volume = 1; + + return MA_SUCCESS; +} + +static void ma_node_output_bus_lock(ma_node_output_bus* pOutputBus) +{ + ma_spinlock_lock(&pOutputBus->lock); +} + +static void ma_node_output_bus_unlock(ma_node_output_bus* pOutputBus) +{ + ma_spinlock_unlock(&pOutputBus->lock); +} + + +static ma_uint32 ma_node_output_bus_get_channels(const ma_node_output_bus* pOutputBus) +{ + return pOutputBus->channels; +} + + +static void ma_node_output_bus_set_has_read(ma_node_output_bus* pOutputBus, ma_bool32 hasRead) +{ + if (hasRead) { + c89atomic_fetch_or_32(&pOutputBus->flags, MA_NODE_OUTPUT_BUS_FLAG_HAS_READ); + } else { + c89atomic_fetch_and_32(&pOutputBus->flags, (ma_uint32)~MA_NODE_OUTPUT_BUS_FLAG_HAS_READ); + } +} + +static ma_bool32 ma_node_output_bus_has_read(ma_node_output_bus* pOutputBus) +{ + return (c89atomic_load_32(&pOutputBus->flags) & MA_NODE_OUTPUT_BUS_FLAG_HAS_READ) != 0; +} + + +static void ma_node_output_bus_set_is_attached(ma_node_output_bus* pOutputBus, ma_bool32 isAttached) +{ + c89atomic_exchange_32(&pOutputBus->isAttached, isAttached); +} + +static ma_bool32 ma_node_output_bus_is_attached(ma_node_output_bus* pOutputBus) +{ + return c89atomic_load_32(&pOutputBus->isAttached); +} + + +static ma_result ma_node_output_bus_set_volume(ma_node_output_bus* pOutputBus, float volume) +{ + MA_ASSERT(pOutputBus != NULL); + + if (volume < 0.0f) { + volume = 0.0f; + } + + c89atomic_exchange_f32(&pOutputBus->volume, volume); + + return MA_SUCCESS; +} + +static float ma_node_output_bus_get_volume(const ma_node_output_bus* pOutputBus) +{ + return c89atomic_load_f32((float*)&pOutputBus->volume); +} + + +static ma_result ma_node_input_bus_init(ma_uint32 channels, ma_node_input_bus* pInputBus) +{ + MA_ASSERT(pInputBus != NULL); + MA_ASSERT(channels < 256); + + MA_ZERO_OBJECT(pInputBus); + + if (channels == 0) { + return MA_INVALID_ARGS; + } + + pInputBus->channels = (ma_uint8)channels; + + return MA_SUCCESS; +} + +static void ma_node_input_bus_lock(ma_node_input_bus* pInputBus) +{ + ma_spinlock_lock(&pInputBus->lock); +} + +static void ma_node_input_bus_unlock(ma_node_input_bus* pInputBus) +{ + ma_spinlock_unlock(&pInputBus->lock); +} + + +static void ma_node_input_bus_next_begin(ma_node_input_bus* pInputBus) +{ + c89atomic_fetch_add_32(&pInputBus->nextCounter, 1); +} + +static void ma_node_input_bus_next_end(ma_node_input_bus* pInputBus) +{ + c89atomic_fetch_sub_32(&pInputBus->nextCounter, 1); +} + +static ma_uint32 ma_node_input_bus_get_next_counter(ma_node_input_bus* pInputBus) +{ + return c89atomic_load_32(&pInputBus->nextCounter); +} + + +static ma_uint32 ma_node_input_bus_get_channels(const ma_node_input_bus* pInputBus) +{ + return pInputBus->channels; +} + + +static void ma_node_input_bus_detach__no_output_bus_lock(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus) +{ + MA_ASSERT(pInputBus != NULL); + MA_ASSERT(pOutputBus != NULL); + + /* + Mark the output bus as detached first. This will prevent future iterations on the audio thread + from iterating this output bus. + */ + ma_node_output_bus_set_is_attached(pOutputBus, MA_FALSE); + + /* + We cannot use the output bus lock here since it'll be getting used at a higher level, but we do + still need to use the input bus lock since we'll be updating pointers on two different output + buses. The same rules apply here as the attaching case. Although we're using a lock here, we're + *not* using a lock when iterating over the list in the audio thread. We therefore need to craft + this in a way such that the iteration on the audio thread doesn't break. + + The the first thing to do is swap out the "next" pointer of the previous output bus with the + new "next" output bus. This is the operation that matters for iteration on the audio thread. + After that, the previous pointer on the new "next" pointer needs to be updated, after which + point the linked list will be in a good state. + */ + ma_node_input_bus_lock(pInputBus); + { + ma_node_output_bus* pOldPrev = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pPrev); + ma_node_output_bus* pOldNext = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pNext); + + if (pOldPrev != NULL) { + c89atomic_exchange_ptr(&pOldPrev->pNext, pOldNext); /* <-- This is where the output bus is detached from the list. */ + } + if (pOldNext != NULL) { + c89atomic_exchange_ptr(&pOldNext->pPrev, pOldPrev); /* <-- This is required for detachment. */ + } + } + ma_node_input_bus_unlock(pInputBus); + + /* At this point the output bus is detached and the linked list is completely unaware of it. Reset some data for safety. */ + c89atomic_exchange_ptr(&pOutputBus->pNext, NULL); /* Using atomic exchanges here, mainly for the benefit of analysis tools which don't always recognize spinlocks. */ + c89atomic_exchange_ptr(&pOutputBus->pPrev, NULL); /* As above. */ + pOutputBus->pInputNode = NULL; + pOutputBus->inputNodeInputBusIndex = 0; + + + /* + For thread-safety reasons, we don't want to be returning from this straight away. We need to + wait for the audio thread to finish with the output bus. There's two things we need to wait + for. The first is the part that selects the next output bus in the list, and the other is the + part that reads from the output bus. Basically all we're doing is waiting for the input bus + to stop referencing the output bus. + + We're doing this part last because we want the section above to run while the audio thread + is finishing up with the output bus, just for efficiency reasons. We marked the output bus as + detached right at the top of this function which is going to prevent the audio thread from + iterating the output bus again. + */ + + /* Part 1: Wait for the current iteration to complete. */ + while (ma_node_input_bus_get_next_counter(pInputBus) > 0) { + ma_yield(); + } + + /* Part 2: Wait for any reads to complete. */ + while (c89atomic_load_32(&pOutputBus->refCount) > 0) { + ma_yield(); + } + + /* + At this point we're done detaching and we can be guaranteed that the audio thread is not going + to attempt to reference this output bus again (until attached again). + */ +} + +#if 0 /* Not used at the moment, but leaving here in case I need it later. */ +static void ma_node_input_bus_detach(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus) +{ + MA_ASSERT(pInputBus != NULL); + MA_ASSERT(pOutputBus != NULL); + + ma_node_output_bus_lock(pOutputBus); + { + ma_node_input_bus_detach__no_output_bus_lock(pInputBus, pOutputBus); + } + ma_node_output_bus_unlock(pOutputBus); +} +#endif + +static void ma_node_input_bus_attach(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus, ma_node* pNewInputNode, ma_uint32 inputNodeInputBusIndex) +{ + MA_ASSERT(pInputBus != NULL); + MA_ASSERT(pOutputBus != NULL); + + ma_node_output_bus_lock(pOutputBus); + { + ma_node_output_bus* pOldInputNode = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pInputNode); + + /* Detach from any existing attachment first if necessary. */ + if (pOldInputNode != NULL) { + ma_node_input_bus_detach__no_output_bus_lock(pInputBus, pOutputBus); + } + + /* + At this point we can be sure the output bus is not attached to anything. The linked list in the + old input bus has been updated so that pOutputBus will not get iterated again. + */ + pOutputBus->pInputNode = pNewInputNode; /* No need for an atomic assignment here because modification of this variable always happens within a lock. */ + pOutputBus->inputNodeInputBusIndex = (ma_uint8)inputNodeInputBusIndex; /* As above. */ + + /* + Now we need to attach the output bus to the linked list. This involves updating two pointers on + two different output buses so I'm going to go ahead and keep this simple and just use a lock. + There are ways to do this without a lock, but it's just too hard to maintain for it's value. + + Although we're locking here, it's important to remember that we're *not* locking when iterating + and reading audio data since that'll be running on the audio thread. As a result we need to be + careful how we craft this so that we don't break iteration. What we're going to do is always + attach the new item so that it becomes the first item in the list. That way, as we're iterating + we won't break any links in the list and iteration will continue safely. The detaching case will + also be crafted in a way as to not break list iteration. It's important to remember to use + atomic exchanges here since no locking is happening on the audio thread during iteration. + */ + ma_node_input_bus_lock(pInputBus); + { + ma_node_output_bus* pNewPrev = &pInputBus->head; + ma_node_output_bus* pNewNext = (ma_node_output_bus*)c89atomic_load_ptr(&pInputBus->head.pNext); + + /* Update the local output bus. */ + c89atomic_exchange_ptr(&pOutputBus->pPrev, pNewPrev); + c89atomic_exchange_ptr(&pOutputBus->pNext, pNewNext); + + /* Update the other output buses to point back to the local output bus. */ + c89atomic_exchange_ptr(&pInputBus->head.pNext, pOutputBus); /* <-- This is where the output bus is actually attached to the input bus. */ + + /* Do the previous pointer last. This is only used for detachment. */ + if (pNewNext != NULL) { + c89atomic_exchange_ptr(&pNewNext->pPrev, pOutputBus); + } + } + ma_node_input_bus_unlock(pInputBus); + + /* + Mark the node as attached last. This is used to controlling whether or the output bus will be + iterated on the audio thread. Mainly required for detachment purposes. + */ + ma_node_output_bus_set_is_attached(pOutputBus, MA_TRUE); + } + ma_node_output_bus_unlock(pOutputBus); +} + +static ma_node_output_bus* ma_node_input_bus_next(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus) +{ + ma_node_output_bus* pNext; + + MA_ASSERT(pInputBus != NULL); + + if (pOutputBus == NULL) { + return NULL; + } + + ma_node_input_bus_next_begin(pInputBus); + { + pNext = pOutputBus; + for (;;) { + pNext = (ma_node_output_bus*)c89atomic_load_ptr(&pNext->pNext); + if (pNext == NULL) { + break; /* Reached the end. */ + } + + if (ma_node_output_bus_is_attached(pNext) == MA_FALSE) { + continue; /* The node is not attached. Keep checking. */ + } + + /* The next node has been selected. */ + break; + } + + /* We need to increment the reference count of the selected node. */ + if (pNext != NULL) { + c89atomic_fetch_add_32(&pNext->refCount, 1); + } + + /* The previous node is no longer being referenced. */ + c89atomic_fetch_sub_32(&pOutputBus->refCount, 1); + } + ma_node_input_bus_next_end(pInputBus); + + return pNext; +} + +static ma_node_output_bus* ma_node_input_bus_first(ma_node_input_bus* pInputBus) +{ + return ma_node_input_bus_next(pInputBus, &pInputBus->head); +} + + + +static ma_result ma_node_input_bus_read_pcm_frames(ma_node* pInputNode, ma_node_input_bus* pInputBus, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime) +{ + ma_result result = MA_SUCCESS; + ma_node_output_bus* pOutputBus; + ma_node_output_bus* pFirst; + ma_uint32 inputChannels; + ma_bool32 doesOutputBufferHaveContent = MA_FALSE; + + /* + This will be called from the audio thread which means we can't be doing any locking. Basically, + this function will not perfom any locking, whereas attaching and detaching will, but crafted in + such a way that we don't need to perform any locking here. The important thing to remember is + to always iterate in a forward direction. + + In order to process any data we need to first read from all input buses. That's where this + function comes in. This iterates over each of the attachments and accumulates/mixes them. We + also convert the channels to the nodes output channel count before mixing. We want to do this + channel conversion so that the caller of this function can invoke the processing callback + without having to do it themselves. + + When we iterate over each of the attachments on the input bus, we need to read as much data as + we can from each of them so that we don't end up with holes between each of the attachments. To + do this, we need to read from each attachment in a loop and read as many frames as we can, up + to `frameCount`. + */ + MA_ASSERT(pInputNode != NULL); + MA_ASSERT(pFramesRead != NULL); /* pFramesRead is critical and must always be specified. On input it's undefined and on output it'll be set to the number of frames actually read. */ + + *pFramesRead = 0; /* Safety. */ + + inputChannels = ma_node_input_bus_get_channels(pInputBus); + + /* + We need to be careful with how we call ma_node_input_bus_first() and ma_node_input_bus_next(). They + are both critical to our lock-free thread-safety system. We can only call ma_node_input_bus_first() + once per iteration, however we have an optimization to checks whether or not it's the first item in + the list. We therefore need to store a pointer to the first item rather than repeatedly calling + ma_node_input_bus_first(). It's safe to keep hold of this pointer, so long as we don't dereference it + after calling ma_node_input_bus_next(), which we won't be. + */ + pFirst = ma_node_input_bus_first(pInputBus); + if (pFirst == NULL) { + return MA_SUCCESS; /* No attachments. Read nothing. */ + } + + for (pOutputBus = pFirst; pOutputBus != NULL; pOutputBus = ma_node_input_bus_next(pInputBus, pOutputBus)) { + ma_uint32 framesProcessed = 0; + ma_bool32 isSilentOutput = MA_FALSE; + + MA_ASSERT(pOutputBus->pNode != NULL); + + isSilentOutput = (((ma_node_base*)pOutputBus->pNode)->vtable->flags & MA_NODE_FLAG_SILENT_OUTPUT) != 0; + + if (pFramesOut != NULL) { + /* Read. */ + float temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE / sizeof(float)]; + ma_uint32 tempCapInFrames = ma_countof(temp) / inputChannels; + + while (framesProcessed < frameCount) { + float* pRunningFramesOut; + ma_uint32 framesToRead; + ma_uint32 framesJustRead; + + framesToRead = frameCount - framesProcessed; + if (framesToRead > tempCapInFrames) { + framesToRead = tempCapInFrames; + } + + pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(pFramesOut, framesProcessed, inputChannels); + + if (doesOutputBufferHaveContent == MA_FALSE) { + /* Fast path. First attachment. We just read straight into the output buffer (no mixing required). */ + result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, pRunningFramesOut, framesToRead, &framesJustRead, globalTime + framesProcessed); + } else { + /* Slow path. Not the first attachment. Mixing required. */ + result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, temp, framesToRead, &framesJustRead, globalTime + framesProcessed); + if (result == MA_SUCCESS || result == MA_AT_END) { + if (isSilentOutput == MA_FALSE) { /* Don't mix if the node outputs silence. */ + ma_mix_pcm_frames_f32(pRunningFramesOut, temp, framesJustRead, inputChannels, /*volume*/1); + } + } + } + + framesProcessed += framesJustRead; + + /* If we reached the end or otherwise failed to read any data we need to finish up with this output node. */ + if (result != MA_SUCCESS) { + break; + } + + /* If we didn't read anything, abort so we don't get stuck in a loop. */ + if (framesJustRead == 0) { + break; + } + } + + /* If it's the first attachment we didn't do any mixing. Any leftover samples need to be silenced. */ + if (pOutputBus == pFirst && framesProcessed < frameCount) { + ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, framesProcessed, ma_format_f32, inputChannels), (frameCount - framesProcessed), ma_format_f32, inputChannels); + } + + if (isSilentOutput == MA_FALSE) { + doesOutputBufferHaveContent = MA_TRUE; + } + } else { + /* Seek. */ + ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, NULL, frameCount, &framesProcessed, globalTime); + } + } + + /* If we didn't output anything, output silence. */ + if (doesOutputBufferHaveContent == MA_FALSE && pFramesOut != NULL) { + ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, inputChannels); + } + + /* In this path we always "process" the entire amount. */ + *pFramesRead = frameCount; + + return result; +} + + +MA_API ma_node_config ma_node_config_init(void) +{ + ma_node_config config; + + MA_ZERO_OBJECT(&config); + config.initialState = ma_node_state_started; /* Nodes are started by default. */ + config.inputBusCount = MA_NODE_BUS_COUNT_UNKNOWN; + config.outputBusCount = MA_NODE_BUS_COUNT_UNKNOWN; + + return config; +} + + + +static ma_result ma_node_detach_full(ma_node* pNode); + +static float* ma_node_get_cached_input_ptr(ma_node* pNode, ma_uint32 inputBusIndex) +{ + ma_node_base* pNodeBase = (ma_node_base*)pNode; + ma_uint32 iInputBus; + float* pBasePtr; + + MA_ASSERT(pNodeBase != NULL); + + /* Input data is stored at the front of the buffer. */ + pBasePtr = pNodeBase->pCachedData; + for (iInputBus = 0; iInputBus < inputBusIndex; iInputBus += 1) { + pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iInputBus]); + } + + return pBasePtr; +} + +static float* ma_node_get_cached_output_ptr(ma_node* pNode, ma_uint32 outputBusIndex) +{ + ma_node_base* pNodeBase = (ma_node_base*)pNode; + ma_uint32 iInputBus; + ma_uint32 iOutputBus; + float* pBasePtr; + + MA_ASSERT(pNodeBase != NULL); + + /* Cached output data starts after the input data. */ + pBasePtr = pNodeBase->pCachedData; + for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNodeBase); iInputBus += 1) { + pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iInputBus]); + } + + for (iOutputBus = 0; iOutputBus < outputBusIndex; iOutputBus += 1) { + pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iOutputBus]); + } + + return pBasePtr; +} + + +typedef struct +{ + size_t sizeInBytes; + size_t inputBusOffset; + size_t outputBusOffset; + size_t cachedDataOffset; + ma_uint32 inputBusCount; /* So it doesn't have to be calculated twice. */ + ma_uint32 outputBusCount; /* So it doesn't have to be calculated twice. */ +} ma_node_heap_layout; + +static ma_result ma_node_translate_bus_counts(const ma_node_config* pConfig, ma_uint32* pInputBusCount, ma_uint32* pOutputBusCount) +{ + ma_uint32 inputBusCount; + ma_uint32 outputBusCount; + + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pInputBusCount != NULL); + MA_ASSERT(pOutputBusCount != NULL); + + /* Bus counts are determined by the vtable, unless they're set to `MA_NODE_BUS_COUNT_UNKNWON`, in which case they're taken from the config. */ + if (pConfig->vtable->inputBusCount == MA_NODE_BUS_COUNT_UNKNOWN) { + inputBusCount = pConfig->inputBusCount; + } else { + inputBusCount = pConfig->vtable->inputBusCount; + + if (pConfig->inputBusCount != MA_NODE_BUS_COUNT_UNKNOWN && pConfig->inputBusCount != pConfig->vtable->inputBusCount) { + return MA_INVALID_ARGS; /* Invalid configuration. You must not specify a conflicting bus count between the node's config and the vtable. */ + } + } + + if (pConfig->vtable->outputBusCount == MA_NODE_BUS_COUNT_UNKNOWN) { + outputBusCount = pConfig->outputBusCount; + } else { + outputBusCount = pConfig->vtable->outputBusCount; + + if (pConfig->outputBusCount != MA_NODE_BUS_COUNT_UNKNOWN && pConfig->outputBusCount != pConfig->vtable->outputBusCount) { + return MA_INVALID_ARGS; /* Invalid configuration. You must not specify a conflicting bus count between the node's config and the vtable. */ + } + } + + /* Bus counts must be within limits. */ + if (inputBusCount > MA_MAX_NODE_BUS_COUNT || outputBusCount > MA_MAX_NODE_BUS_COUNT) { + return MA_INVALID_ARGS; + } + + + /* We must have channel counts for each bus. */ + if ((inputBusCount > 0 && pConfig->pInputChannels == NULL) || (outputBusCount > 0 && pConfig->pOutputChannels == NULL)) { + return MA_INVALID_ARGS; /* You must specify channel counts for each input and output bus. */ + } + + + /* Some special rules for passthrough nodes. */ + if ((pConfig->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) { + if (pConfig->vtable->inputBusCount != 1 || pConfig->vtable->outputBusCount != 1) { + return MA_INVALID_ARGS; /* Passthrough nodes must have exactly 1 input bus and 1 output bus. */ + } + + if (pConfig->pInputChannels[0] != pConfig->pOutputChannels[0]) { + return MA_INVALID_ARGS; /* Passthrough nodes must have the same number of channels between input and output nodes. */ + } + } + + + *pInputBusCount = inputBusCount; + *pOutputBusCount = outputBusCount; + + return MA_SUCCESS; +} + +static ma_result ma_node_get_heap_layout(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, ma_node_heap_layout* pHeapLayout) +{ + ma_result result; + ma_uint32 inputBusCount; + ma_uint32 outputBusCount; + + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL || pConfig->vtable == NULL || pConfig->vtable->onProcess == NULL) { + return MA_INVALID_ARGS; + } + + result = ma_node_translate_bus_counts(pConfig, &inputBusCount, &outputBusCount); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->sizeInBytes = 0; + + /* Input buses. */ + if (inputBusCount > MA_MAX_NODE_LOCAL_BUS_COUNT) { + pHeapLayout->inputBusOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(sizeof(ma_node_input_bus) * inputBusCount); + } else { + pHeapLayout->inputBusOffset = MA_SIZE_MAX; /* MA_SIZE_MAX indicates that no heap allocation is required for the input bus. */ + } + + /* Output buses. */ + if (outputBusCount > MA_MAX_NODE_LOCAL_BUS_COUNT) { + pHeapLayout->outputBusOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(sizeof(ma_node_output_bus) * outputBusCount); + } else { + pHeapLayout->outputBusOffset = MA_SIZE_MAX; + } + + /* + Cached audio data. + + We need to allocate memory for a caching both input and output data. We have an optimization + where no caching is necessary for specific conditions: + + - The node has 0 inputs and 1 output. + + When a node meets the above conditions, no cache is allocated. + + The size choice for this buffer is a little bit finicky. We don't want to be too wasteful by + allocating too much, but at the same time we want it be large enough so that enough frames can + be processed for each call to ma_node_read_pcm_frames() so that it keeps things efficient. For + now I'm going with 10ms @ 48K which is 480 frames per bus. This is configurable at compile + time. It might also be worth investigating whether or not this can be configured at run time. + */ + if (inputBusCount == 0 && outputBusCount == 1) { + /* Fast path. No cache needed. */ + pHeapLayout->cachedDataOffset = MA_SIZE_MAX; + } else { + /* Slow path. Cache needed. */ + size_t cachedDataSizeInBytes = 0; + ma_uint32 iBus; + + for (iBus = 0; iBus < inputBusCount; iBus += 1) { + cachedDataSizeInBytes += pNodeGraph->nodeCacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pInputChannels[iBus]); + } + + for (iBus = 0; iBus < outputBusCount; iBus += 1) { + cachedDataSizeInBytes += pNodeGraph->nodeCacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pOutputChannels[iBus]); + } + + pHeapLayout->cachedDataOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(cachedDataSizeInBytes); + } + + + /* + Not technically part of the heap, but we can output the input and output bus counts so we can + avoid a redundant call to ma_node_translate_bus_counts(). + */ + pHeapLayout->inputBusCount = inputBusCount; + pHeapLayout->outputBusCount = outputBusCount; + + /* Make sure allocation size is aligned. */ + pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); + + return MA_SUCCESS; +} + +MA_API ma_result ma_node_get_heap_size(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_node_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_node_get_heap_layout(pNodeGraph, pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return MA_SUCCESS; +} + +MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, void* pHeap, ma_node* pNode) +{ + ma_node_base* pNodeBase = (ma_node_base*)pNode; + ma_result result; + ma_node_heap_layout heapLayout; + ma_uint32 iInputBus; + ma_uint32 iOutputBus; + + if (pNodeBase == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pNodeBase); + + result = ma_node_get_heap_layout(pNodeGraph, pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pNodeBase->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pNodeBase->pNodeGraph = pNodeGraph; + pNodeBase->vtable = pConfig->vtable; + pNodeBase->state = pConfig->initialState; + pNodeBase->stateTimes[ma_node_state_started] = 0; + pNodeBase->stateTimes[ma_node_state_stopped] = (ma_uint64)(ma_int64)-1; /* Weird casting for VC6 compatibility. */ + pNodeBase->inputBusCount = heapLayout.inputBusCount; + pNodeBase->outputBusCount = heapLayout.outputBusCount; + + if (heapLayout.inputBusOffset != MA_SIZE_MAX) { + pNodeBase->pInputBuses = (ma_node_input_bus*)ma_offset_ptr(pHeap, heapLayout.inputBusOffset); + } else { + pNodeBase->pInputBuses = pNodeBase->_inputBuses; + } + + if (heapLayout.outputBusOffset != MA_SIZE_MAX) { + pNodeBase->pOutputBuses = (ma_node_output_bus*)ma_offset_ptr(pHeap, heapLayout.inputBusOffset); + } else { + pNodeBase->pOutputBuses = pNodeBase->_outputBuses; + } + + if (heapLayout.cachedDataOffset != MA_SIZE_MAX) { + pNodeBase->pCachedData = (float*)ma_offset_ptr(pHeap, heapLayout.cachedDataOffset); + pNodeBase->cachedDataCapInFramesPerBus = pNodeGraph->nodeCacheCapInFrames; + } else { + pNodeBase->pCachedData = NULL; + } + + + + /* We need to run an initialization step for each input and output bus. */ + for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNodeBase); iInputBus += 1) { + result = ma_node_input_bus_init(pConfig->pInputChannels[iInputBus], &pNodeBase->pInputBuses[iInputBus]); + if (result != MA_SUCCESS) { + return result; + } + } + + for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNodeBase); iOutputBus += 1) { + result = ma_node_output_bus_init(pNodeBase, iOutputBus, pConfig->pOutputChannels[iOutputBus], &pNodeBase->pOutputBuses[iOutputBus]); + if (result != MA_SUCCESS) { + return result; + } + } + + + /* The cached data needs to be initialized to silence (or a sine wave tone if we're debugging). */ + if (pNodeBase->pCachedData != NULL) { + ma_uint32 iBus; + + #if 1 /* Toggle this between 0 and 1 to turn debugging on or off. 1 = fill with a sine wave for debugging; 0 = fill with silence. */ + /* For safety we'll go ahead and default the buffer to silence. */ + for (iBus = 0; iBus < ma_node_get_input_bus_count(pNodeBase); iBus += 1) { + ma_silence_pcm_frames(ma_node_get_cached_input_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iBus])); + } + for (iBus = 0; iBus < ma_node_get_output_bus_count(pNodeBase); iBus += 1) { + ma_silence_pcm_frames(ma_node_get_cached_output_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iBus])); + } + #else + /* For debugging. Default to a sine wave. */ + for (iBus = 0; iBus < ma_node_get_input_bus_count(pNodeBase); iBus += 1) { + ma_debug_fill_pcm_frames_with_sine_wave(ma_node_get_cached_input_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iBus]), 48000); + } + for (iBus = 0; iBus < ma_node_get_output_bus_count(pNodeBase); iBus += 1) { + ma_debug_fill_pcm_frames_with_sine_wave(ma_node_get_cached_output_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iBus]), 48000); + } + #endif + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_node_get_heap_size(pNodeGraph, pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_node_init_preallocated(pNodeGraph, pConfig, pHeap, pNode); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + ((ma_node_base*)pNode)->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_node_base* pNodeBase = (ma_node_base*)pNode; + + if (pNodeBase == NULL) { + return; + } + + /* + The first thing we need to do is fully detach the node. This will detach all inputs and + outputs. We need to do this first because it will sever the connection with the node graph and + allow us to complete uninitialization without needing to worry about thread-safety with the + audio thread. The detachment process will wait for any local processing of the node to finish. + */ + ma_node_detach_full(pNode); + + /* + At this point the node should be completely unreferenced by the node graph and we can finish up + the uninitialization process without needing to worry about thread-safety. + */ + if (pNodeBase->_ownsHeap) { + ma_free(pNodeBase->_pHeap, pAllocationCallbacks); + } +} + +MA_API ma_node_graph* ma_node_get_node_graph(const ma_node* pNode) +{ + if (pNode == NULL) { + return NULL; + } + + return ((const ma_node_base*)pNode)->pNodeGraph; +} + +MA_API ma_uint32 ma_node_get_input_bus_count(const ma_node* pNode) +{ + if (pNode == NULL) { + return 0; + } + + return ((ma_node_base*)pNode)->inputBusCount; +} + +MA_API ma_uint32 ma_node_get_output_bus_count(const ma_node* pNode) +{ + if (pNode == NULL) { + return 0; + } + + return ((ma_node_base*)pNode)->outputBusCount; +} + + +MA_API ma_uint32 ma_node_get_input_channels(const ma_node* pNode, ma_uint32 inputBusIndex) +{ + const ma_node_base* pNodeBase = (const ma_node_base*)pNode; + + if (pNode == NULL) { + return 0; + } + + if (inputBusIndex >= ma_node_get_input_bus_count(pNode)) { + return 0; /* Invalid bus index. */ + } + + return ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[inputBusIndex]); +} + +MA_API ma_uint32 ma_node_get_output_channels(const ma_node* pNode, ma_uint32 outputBusIndex) +{ + const ma_node_base* pNodeBase = (const ma_node_base*)pNode; + + if (pNode == NULL) { + return 0; + } + + if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { + return 0; /* Invalid bus index. */ + } + + return ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[outputBusIndex]); +} + + +static ma_result ma_node_detach_full(ma_node* pNode) +{ + ma_node_base* pNodeBase = (ma_node_base*)pNode; + ma_uint32 iInputBus; + + if (pNodeBase == NULL) { + return MA_INVALID_ARGS; + } + + /* + Make sure the node is completely detached first. This will not return until the output bus is + guaranteed to no longer be referenced by the audio thread. + */ + ma_node_detach_all_output_buses(pNode); + + /* + At this point all output buses will have been detached from the graph and we can be guaranteed + that none of it's input nodes will be getting processed by the graph. We can detach these + without needing to worry about the audio thread touching them. + */ + for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNode); iInputBus += 1) { + ma_node_input_bus* pInputBus; + ma_node_output_bus* pOutputBus; + + pInputBus = &pNodeBase->pInputBuses[iInputBus]; + + /* + This is important. We cannot be using ma_node_input_bus_first() or ma_node_input_bus_next(). Those + functions are specifically for the audio thread. We'll instead just manually iterate using standard + linked list logic. We don't need to worry about the audio thread referencing these because the step + above severed the connection to the graph. + */ + for (pOutputBus = (ma_node_output_bus*)c89atomic_load_ptr(&pInputBus->head.pNext); pOutputBus != NULL; pOutputBus = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pNext)) { + ma_node_detach_output_bus(pOutputBus->pNode, pOutputBus->outputBusIndex); /* This won't do any waiting in practice and should be efficient. */ + } + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_node_detach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex) +{ + ma_result result = MA_SUCCESS; + ma_node_base* pNodeBase = (ma_node_base*)pNode; + ma_node_base* pInputNodeBase; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { + return MA_INVALID_ARGS; /* Invalid output bus index. */ + } + + /* We need to lock the output bus because we need to inspect the input node and grab it's input bus. */ + ma_node_output_bus_lock(&pNodeBase->pOutputBuses[outputBusIndex]); + { + pInputNodeBase = (ma_node_base*)pNodeBase->pOutputBuses[outputBusIndex].pInputNode; + if (pInputNodeBase != NULL) { + ma_node_input_bus_detach__no_output_bus_lock(&pInputNodeBase->pInputBuses[pNodeBase->pOutputBuses[outputBusIndex].inputNodeInputBusIndex], &pNodeBase->pOutputBuses[outputBusIndex]); + } + } + ma_node_output_bus_unlock(&pNodeBase->pOutputBuses[outputBusIndex]); + + return result; +} + +MA_API ma_result ma_node_detach_all_output_buses(ma_node* pNode) +{ + ma_uint32 iOutputBus; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNode); iOutputBus += 1) { + ma_node_detach_output_bus(pNode, iOutputBus); + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_node_attach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex, ma_node* pOtherNode, ma_uint32 otherNodeInputBusIndex) +{ + ma_node_base* pNodeBase = (ma_node_base*)pNode; + ma_node_base* pOtherNodeBase = (ma_node_base*)pOtherNode; + + if (pNodeBase == NULL || pOtherNodeBase == NULL) { + return MA_INVALID_ARGS; + } + + if (pNodeBase == pOtherNodeBase) { + return MA_INVALID_OPERATION; /* Cannot attach a node to itself. */ + } + + if (outputBusIndex >= ma_node_get_output_bus_count(pNode) || otherNodeInputBusIndex >= ma_node_get_input_bus_count(pOtherNode)) { + return MA_INVALID_OPERATION; /* Invalid bus index. */ + } + + /* The output channel count of the output node must be the same as the input channel count of the input node. */ + if (ma_node_get_output_channels(pNode, outputBusIndex) != ma_node_get_input_channels(pOtherNode, otherNodeInputBusIndex)) { + return MA_INVALID_OPERATION; /* Channel count is incompatible. */ + } + + /* This will deal with detaching if the output bus is already attached to something. */ + ma_node_input_bus_attach(&pOtherNodeBase->pInputBuses[otherNodeInputBusIndex], &pNodeBase->pOutputBuses[outputBusIndex], pOtherNode, otherNodeInputBusIndex); + + return MA_SUCCESS; +} + +MA_API ma_result ma_node_set_output_bus_volume(ma_node* pNode, ma_uint32 outputBusIndex, float volume) +{ + ma_node_base* pNodeBase = (ma_node_base*)pNode; + + if (pNodeBase == NULL) { + return MA_INVALID_ARGS; + } + + if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { + return MA_INVALID_ARGS; /* Invalid bus index. */ + } + + return ma_node_output_bus_set_volume(&pNodeBase->pOutputBuses[outputBusIndex], volume); +} + +MA_API float ma_node_get_output_bus_volume(const ma_node* pNode, ma_uint32 outputBusIndex) +{ + const ma_node_base* pNodeBase = (const ma_node_base*)pNode; + + if (pNodeBase == NULL) { + return 0; + } + + if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { + return 0; /* Invalid bus index. */ + } + + return ma_node_output_bus_get_volume(&pNodeBase->pOutputBuses[outputBusIndex]); +} + +MA_API ma_result ma_node_set_state(ma_node* pNode, ma_node_state state) +{ + ma_node_base* pNodeBase = (ma_node_base*)pNode; + + if (pNodeBase == NULL) { + return MA_INVALID_ARGS; + } + + c89atomic_exchange_i32(&pNodeBase->state, state); + + return MA_SUCCESS; +} + +MA_API ma_node_state ma_node_get_state(const ma_node* pNode) +{ + const ma_node_base* pNodeBase = (const ma_node_base*)pNode; + + if (pNodeBase == NULL) { + return ma_node_state_stopped; + } + + return (ma_node_state)c89atomic_load_i32(&pNodeBase->state); +} + +MA_API ma_result ma_node_set_state_time(ma_node* pNode, ma_node_state state, ma_uint64 globalTime) +{ + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + /* Validation check for safety since we'll be using this as an index into stateTimes[]. */ + if (state != ma_node_state_started && state != ma_node_state_stopped) { + return MA_INVALID_ARGS; + } + + c89atomic_exchange_64(&((ma_node_base*)pNode)->stateTimes[state], globalTime); + + return MA_SUCCESS; +} + +MA_API ma_uint64 ma_node_get_state_time(const ma_node* pNode, ma_node_state state) +{ + if (pNode == NULL) { + return 0; + } + + /* Validation check for safety since we'll be using this as an index into stateTimes[]. */ + if (state != ma_node_state_started && state != ma_node_state_stopped) { + return 0; + } + + return c89atomic_load_64(&((ma_node_base*)pNode)->stateTimes[state]); +} + +MA_API ma_node_state ma_node_get_state_by_time(const ma_node* pNode, ma_uint64 globalTime) +{ + if (pNode == NULL) { + return ma_node_state_stopped; + } + + return ma_node_get_state_by_time_range(pNode, globalTime, globalTime); +} + +MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_uint64 globalTimeBeg, ma_uint64 globalTimeEnd) +{ + ma_node_state state; + + if (pNode == NULL) { + return ma_node_state_stopped; + } + + state = ma_node_get_state(pNode); + + /* An explicitly stopped node is always stopped. */ + if (state == ma_node_state_stopped) { + return ma_node_state_stopped; + } + + /* + Getting here means the node is marked as started, but it may still not be truly started due to + it's start time not having been reached yet. Also, the stop time may have also been reached in + which case it'll be considered stopped. + */ + if (ma_node_get_state_time(pNode, ma_node_state_started) > globalTimeBeg) { + return ma_node_state_stopped; /* Start time has not yet been reached. */ + } + + if (ma_node_get_state_time(pNode, ma_node_state_stopped) <= globalTimeEnd) { + return ma_node_state_stopped; /* Stop time has been reached. */ + } + + /* Getting here means the node is marked as started and is within it's start/stop times. */ + return ma_node_state_started; +} + +MA_API ma_uint64 ma_node_get_time(const ma_node* pNode) +{ + if (pNode == NULL) { + return 0; + } + + return c89atomic_load_64(&((ma_node_base*)pNode)->localTime); +} + +MA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime) +{ + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + c89atomic_exchange_64(&((ma_node_base*)pNode)->localTime, localTime); + + return MA_SUCCESS; +} + + + +static void ma_node_process_pcm_frames_internal(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_node_base* pNodeBase = (ma_node_base*)pNode; + + MA_ASSERT(pNode != NULL); + + if (pNodeBase->vtable->onProcess) { + pNodeBase->vtable->onProcess(pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut); + } +} + +static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusIndex, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime) +{ + ma_node_base* pNodeBase = (ma_node_base*)pNode; + ma_result result = MA_SUCCESS; + ma_uint32 iInputBus; + ma_uint32 iOutputBus; + ma_uint32 inputBusCount; + ma_uint32 outputBusCount; + ma_uint32 totalFramesRead = 0; + float* ppFramesIn[MA_MAX_NODE_BUS_COUNT]; + float* ppFramesOut[MA_MAX_NODE_BUS_COUNT]; + ma_uint64 globalTimeBeg; + ma_uint64 globalTimeEnd; + ma_uint64 startTime; + ma_uint64 stopTime; + ma_uint32 timeOffsetBeg; + ma_uint32 timeOffsetEnd; + ma_uint32 frameCountIn; + ma_uint32 frameCountOut; + + /* + pFramesRead is mandatory. It must be used to determine how many frames were read. It's normal and + expected that the number of frames read may be different to that requested. Therefore, the caller + must look at this value to correctly determine how many frames were read. + */ + MA_ASSERT(pFramesRead != NULL); /* <-- If you've triggered this assert, you're using this function wrong. You *must* use this variable and inspect it after the call returns. */ + if (pFramesRead == NULL) { + return MA_INVALID_ARGS; + } + + *pFramesRead = 0; /* Safety. */ + + if (pNodeBase == NULL) { + return MA_INVALID_ARGS; + } + + if (outputBusIndex >= ma_node_get_output_bus_count(pNodeBase)) { + return MA_INVALID_ARGS; /* Invalid output bus index. */ + } + + /* Don't do anything if we're in a stopped state. */ + if (ma_node_get_state_by_time_range(pNode, globalTime, globalTime + frameCount) != ma_node_state_started) { + return MA_SUCCESS; /* We're in a stopped state. This is not an error - we just need to not read anything. */ + } + + + globalTimeBeg = globalTime; + globalTimeEnd = globalTime + frameCount; + startTime = ma_node_get_state_time(pNode, ma_node_state_started); + stopTime = ma_node_get_state_time(pNode, ma_node_state_stopped); + + /* + At this point we know that we are inside our start/stop times. However, we may need to adjust + our frame count and output pointer to accomodate since we could be straddling the time period + that this function is getting called for. + + It's possible (and likely) that the start time does not line up with the output buffer. We + therefore need to offset it by a number of frames to accomodate. The same thing applies for + the stop time. + */ + timeOffsetBeg = (globalTimeBeg < startTime) ? (ma_uint32)(globalTimeEnd - startTime) : 0; + timeOffsetEnd = (globalTimeEnd > stopTime) ? (ma_uint32)(globalTimeEnd - stopTime) : 0; + + /* Trim based on the start offset. We need to silence the start of the buffer. */ + if (timeOffsetBeg > 0) { + ma_silence_pcm_frames(pFramesOut, timeOffsetBeg, ma_format_f32, ma_node_get_output_channels(pNode, outputBusIndex)); + pFramesOut += timeOffsetBeg * ma_node_get_output_channels(pNode, outputBusIndex); + frameCount -= timeOffsetBeg; + } + + /* Trim based on the end offset. We don't need to silence the tail section because we'll just have a reduced value written to pFramesRead. */ + if (timeOffsetEnd > 0) { + frameCount -= timeOffsetEnd; + } + + + /* We run on different paths depending on the bus counts. */ + inputBusCount = ma_node_get_input_bus_count(pNode); + outputBusCount = ma_node_get_output_bus_count(pNode); + + /* + Run a simplified path when there are no inputs and one output. In this case there's nothing to + actually read and we can go straight to output. This is a very common scenario because the vast + majority of data source nodes will use this setup so this optimization I think is worthwhile. + */ + if (inputBusCount == 0 && outputBusCount == 1) { + /* Fast path. No need to read from input and no need for any caching. */ + frameCountIn = 0; + frameCountOut = frameCount; /* Just read as much as we can. The callback will return what was actually read. */ + + ppFramesOut[0] = pFramesOut; + ma_node_process_pcm_frames_internal(pNode, NULL, &frameCountIn, ppFramesOut, &frameCountOut); + totalFramesRead = frameCountOut; + } else { + /* Slow path. Need to read input data. */ + if ((pNodeBase->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) { + /* + Fast path. We're running a passthrough. We need to read directly into the output buffer, but + still fire the callback so that event handling and trigger nodes can do their thing. Since + it's a passthrough there's no need for any kind of caching logic. + */ + MA_ASSERT(outputBusCount == inputBusCount); + MA_ASSERT(outputBusCount == 1); + MA_ASSERT(outputBusIndex == 0); + + /* We just read directly from input bus to output buffer, and then afterwards fire the callback. */ + ppFramesOut[0] = pFramesOut; + ppFramesIn[0] = ppFramesOut[0]; + + result = ma_node_input_bus_read_pcm_frames(pNodeBase, &pNodeBase->pInputBuses[0], ppFramesIn[0], frameCount, &totalFramesRead, globalTime); + if (result == MA_SUCCESS) { + /* Even though it's a passthrough, we still need to fire the callback. */ + frameCountIn = totalFramesRead; + frameCountOut = totalFramesRead; + + if (totalFramesRead > 0) { + ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut); /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? Excplicit cast to silence the warning. */ + } + + /* + A passthrough should never have modified the input and output frame counts. If you're + triggering these assers you need to fix your processing callback. + */ + MA_ASSERT(frameCountIn == totalFramesRead); + MA_ASSERT(frameCountOut == totalFramesRead); + } + } else { + /* Slow path. Need to do caching. */ + ma_uint32 framesToProcessIn; + ma_uint32 framesToProcessOut; + ma_bool32 consumeNullInput = MA_FALSE; + + /* + We use frameCount as a basis for the number of frames to read since that's what's being + requested, however we still need to clamp it to whatever can fit in the cache. + + This will also be used as the basis for determining how many input frames to read. This is + not ideal because it can result in too many input frames being read which introduces latency. + To solve this, nodes can implement an optional callback called onGetRequiredInputFrameCount + which is used as hint to miniaudio as to how many input frames it needs to read at a time. This + callback is completely optional, and if it's not set, miniaudio will assume `frameCount`. + + This function will be called multiple times for each period of time, once for each output node. + We cannot read from each input node each time this function is called. Instead we need to check + whether or not this is first output bus to be read from for this time period, and if so, read + from our input data. + + To determine whether or not we're ready to read data, we check a flag. There will be one flag + for each output. When the flag is set, it means data has been read previously and that we're + ready to advance time forward for our input nodes by reading fresh data. + */ + framesToProcessOut = frameCount; + if (framesToProcessOut > pNodeBase->cachedDataCapInFramesPerBus) { + framesToProcessOut = pNodeBase->cachedDataCapInFramesPerBus; + } + + framesToProcessIn = frameCount; + if (pNodeBase->vtable->onGetRequiredInputFrameCount) { + pNodeBase->vtable->onGetRequiredInputFrameCount(pNode, framesToProcessOut, &framesToProcessIn); /* <-- It does not matter if this fails. */ + } + if (framesToProcessIn > pNodeBase->cachedDataCapInFramesPerBus) { + framesToProcessIn = pNodeBase->cachedDataCapInFramesPerBus; + } + + + MA_ASSERT(framesToProcessIn <= 0xFFFF); + MA_ASSERT(framesToProcessOut <= 0xFFFF); + + if (ma_node_output_bus_has_read(&pNodeBase->pOutputBuses[outputBusIndex])) { + /* Getting here means we need to do another round of processing. */ + pNodeBase->cachedFrameCountOut = 0; + + for (;;) { + frameCountOut = 0; + + /* + We need to prepare our output frame pointers for processing. In the same iteration we need + to mark every output bus as unread so that future calls to this function for different buses + for the current time period don't pull in data when they should instead be reading from cache. + */ + for (iOutputBus = 0; iOutputBus < outputBusCount; iOutputBus += 1) { + ma_node_output_bus_set_has_read(&pNodeBase->pOutputBuses[iOutputBus], MA_FALSE); /* <-- This is what tells the next calls to this function for other output buses for this time period to read from cache instead of pulling in more data. */ + ppFramesOut[iOutputBus] = ma_node_get_cached_output_ptr(pNode, iOutputBus); + } + + /* We only need to read from input buses if there isn't already some data in the cache. */ + if (pNodeBase->cachedFrameCountIn == 0) { + ma_uint32 maxFramesReadIn = 0; + + /* Here is where we pull in data from the input buses. This is what will trigger an advance in time. */ + for (iInputBus = 0; iInputBus < inputBusCount; iInputBus += 1) { + ma_uint32 framesRead; + + /* The first thing to do is get the offset within our bulk allocation to store this input data. */ + ppFramesIn[iInputBus] = ma_node_get_cached_input_ptr(pNode, iInputBus); + + /* Once we've determined our destination pointer we can read. Note that we must inspect the number of frames read and fill any leftovers with silence for safety. */ + result = ma_node_input_bus_read_pcm_frames(pNodeBase, &pNodeBase->pInputBuses[iInputBus], ppFramesIn[iInputBus], framesToProcessIn, &framesRead, globalTime); + if (result != MA_SUCCESS) { + /* It doesn't really matter if we fail because we'll just fill with silence. */ + framesRead = 0; /* Just for safety, but I don't think it's really needed. */ + } + + /* TODO: Minor optimization opportunity here. If no frames were read and the buffer is already filled with silence, no need to re-silence it. */ + /* Any leftover frames need to silenced for safety. */ + if (framesRead < framesToProcessIn) { + ma_silence_pcm_frames(ppFramesIn[iInputBus] + (framesRead * ma_node_get_input_channels(pNodeBase, iInputBus)), (framesToProcessIn - framesRead), ma_format_f32, ma_node_get_input_channels(pNodeBase, iInputBus)); + } + + maxFramesReadIn = ma_max(maxFramesReadIn, framesRead); + } + + /* This was a fresh load of input data so reset our consumption counter. */ + pNodeBase->consumedFrameCountIn = 0; + + /* + We don't want to keep processing if there's nothing to process, so set the number of cached + input frames to the maximum number we read from each attachment (the lesser will be padded + with silence). If we didn't read anything, this will be set to 0 and the entire buffer will + have been assigned to silence. This being equal to 0 is an important property for us because + it allows us to detect when NULL can be passed into the processing callback for the input + buffer for the purpose of continuous processing. + */ + pNodeBase->cachedFrameCountIn = (ma_uint16)maxFramesReadIn; + } else { + /* We don't need to read anything, but we do need to prepare our input frame pointers. */ + for (iInputBus = 0; iInputBus < inputBusCount; iInputBus += 1) { + ppFramesIn[iInputBus] = ma_node_get_cached_input_ptr(pNode, iInputBus) + (pNodeBase->consumedFrameCountIn * ma_node_get_input_channels(pNodeBase, iInputBus)); + } + } + + /* + At this point we have our input data so now we need to do some processing. Sneaky little + optimization here - we can set the pointer to the output buffer for this output bus so + that the final copy into the output buffer is done directly by onProcess(). + */ + if (pFramesOut != NULL) { + ppFramesOut[outputBusIndex] = ma_offset_pcm_frames_ptr_f32(pFramesOut, pNodeBase->cachedFrameCountOut, ma_node_get_output_channels(pNode, outputBusIndex)); + } + + + /* Give the processing function the entire capacity of the output buffer. */ + frameCountOut = (framesToProcessOut - pNodeBase->cachedFrameCountOut); + + /* + We need to treat nodes with continuous processing a little differently. For these ones, + we always want to fire the callback with the requested number of frames, regardless of + pNodeBase->cachedFrameCountIn, which could be 0. Also, we want to check if we can pass + in NULL for the input buffer to the callback. + */ + if ((pNodeBase->vtable->flags & MA_NODE_FLAG_CONTINUOUS_PROCESSING) != 0) { + /* We're using continuous processing. Make sure we specify the whole frame count at all times. */ + frameCountIn = framesToProcessIn; /* Give the processing function as much input data as we've got in the buffer, including any silenced padding from short reads. */ + + if ((pNodeBase->vtable->flags & MA_NODE_FLAG_ALLOW_NULL_INPUT) != 0 && pNodeBase->consumedFrameCountIn == 0 && pNodeBase->cachedFrameCountIn == 0) { + consumeNullInput = MA_TRUE; + } else { + consumeNullInput = MA_FALSE; + } + + /* + Since we're using continuous processing we're always passing in a full frame count + regardless of how much input data was read. If this is greater than what we read as + input, we'll end up with an underflow. We instead need to make sure our cached frame + count is set to the number of frames we'll be passing to the data callback. Not + doing this will result in an underflow when we "consume" the cached data later on. + + Note that this check needs to be done after the "consumeNullInput" check above because + we use the property of cachedFrameCountIn being 0 to determine whether or not we + should be passing in a null pointer to the processing callback for when the node is + configured with MA_NODE_FLAG_ALLOW_NULL_INPUT. + */ + if (pNodeBase->cachedFrameCountIn < (ma_uint16)frameCountIn) { + pNodeBase->cachedFrameCountIn = (ma_uint16)frameCountIn; + } + } else { + frameCountIn = pNodeBase->cachedFrameCountIn; /* Give the processing function as much valid input data as we've got. */ + consumeNullInput = MA_FALSE; + } + + /* + Process data slightly differently depending on whether or not we're consuming NULL + input (checked just above). + */ + if (consumeNullInput) { + ma_node_process_pcm_frames_internal(pNode, NULL, &frameCountIn, ppFramesOut, &frameCountOut); + } else { + /* + We want to skip processing if there's no input data, but we can only do that safely if + we know that there is no chance of any output frames being produced. If continuous + processing is being used, this won't be a problem because the input frame count will + always be non-0. However, if continuous processing is *not* enabled and input and output + data is processed at different rates, we still need to process that last input frame + because there could be a few excess output frames needing to be produced from cached + data. The `MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES` flag is used as the indicator for + determining whether or not we need to process the node even when there are no input + frames available right now. + */ + if (frameCountIn > 0 || (pNodeBase->vtable->flags & MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES) != 0) { + ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut); /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? Excplicit cast to silence the warning. */ + } else { + frameCountOut = 0; /* No data was processed. */ + } + } + + /* + Thanks to our sneaky optimization above we don't need to do any data copying directly into + the output buffer - the onProcess() callback just did that for us. We do, however, need to + apply the number of input and output frames that were processed. Note that due to continuous + processing above, we need to do explicit checks here. If we just consumed a NULL input + buffer it means that no actual input data was processed from the internal buffers and we + don't want to be modifying any counters. + */ + if (consumeNullInput == MA_FALSE) { + pNodeBase->consumedFrameCountIn += (ma_uint16)frameCountIn; + pNodeBase->cachedFrameCountIn -= (ma_uint16)frameCountIn; + } + + /* The cached output frame count is always equal to what we just read. */ + pNodeBase->cachedFrameCountOut += (ma_uint16)frameCountOut; + + /* If we couldn't process any data, we're done. The loop needs to be terminated here or else we'll get stuck in a loop. */ + if (pNodeBase->cachedFrameCountOut == framesToProcessOut || (frameCountOut == 0 && frameCountIn == 0)) { + break; + } + } + } else { + /* + We're not needing to read anything from the input buffer so just read directly from our + already-processed data. + */ + if (pFramesOut != NULL) { + ma_copy_pcm_frames(pFramesOut, ma_node_get_cached_output_ptr(pNodeBase, outputBusIndex), pNodeBase->cachedFrameCountOut, ma_format_f32, ma_node_get_output_channels(pNodeBase, outputBusIndex)); + } + } + + /* The number of frames read is always equal to the number of cached output frames. */ + totalFramesRead = pNodeBase->cachedFrameCountOut; + + /* Now that we've read the data, make sure our read flag is set. */ + ma_node_output_bus_set_has_read(&pNodeBase->pOutputBuses[outputBusIndex], MA_TRUE); + } + } + + /* Apply volume, if necessary. */ + ma_apply_volume_factor_f32(pFramesOut, totalFramesRead * ma_node_get_output_channels(pNodeBase, outputBusIndex), ma_node_output_bus_get_volume(&pNodeBase->pOutputBuses[outputBusIndex])); + + /* Advance our local time forward. */ + c89atomic_fetch_add_64(&pNodeBase->localTime, (ma_uint64)totalFramesRead); + + *pFramesRead = totalFramesRead + timeOffsetBeg; /* Must include the silenced section at the start of the buffer. */ + return result; +} + + + + +/* Data source node. */ +MA_API ma_data_source_node_config ma_data_source_node_config_init(ma_data_source* pDataSource) +{ + ma_data_source_node_config config; + + MA_ZERO_OBJECT(&config); + config.nodeConfig = ma_node_config_init(); + config.pDataSource = pDataSource; + + return config; +} + + +static void ma_data_source_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_data_source_node* pDataSourceNode = (ma_data_source_node*)pNode; + ma_format format; + ma_uint32 channels; + ma_uint32 frameCount; + ma_uint64 framesRead = 0; + + MA_ASSERT(pDataSourceNode != NULL); + MA_ASSERT(pDataSourceNode->pDataSource != NULL); + MA_ASSERT(ma_node_get_input_bus_count(pDataSourceNode) == 0); + MA_ASSERT(ma_node_get_output_bus_count(pDataSourceNode) == 1); + + /* We don't want to read from ppFramesIn at all. Instead we read from the data source. */ + (void)ppFramesIn; + (void)pFrameCountIn; + + frameCount = *pFrameCountOut; + + /* miniaudio should never be calling this with a frame count of zero. */ + MA_ASSERT(frameCount > 0); + + if (ma_data_source_get_data_format(pDataSourceNode->pDataSource, &format, &channels, NULL, NULL, 0) == MA_SUCCESS) { /* <-- Don't care about sample rate here. */ + /* The node graph system requires samples be in floating point format. This is checked in ma_data_source_node_init(). */ + MA_ASSERT(format == ma_format_f32); + (void)format; /* Just to silence some static analysis tools. */ + + ma_data_source_read_pcm_frames(pDataSourceNode->pDataSource, ppFramesOut[0], frameCount, &framesRead); + } + + *pFrameCountOut = (ma_uint32)framesRead; +} + +static ma_node_vtable g_ma_data_source_node_vtable = +{ + ma_data_source_node_process_pcm_frames, + NULL, /* onGetRequiredInputFrameCount */ + 0, /* 0 input buses. */ + 1, /* 1 output bus. */ + 0 +}; + +MA_API ma_result ma_data_source_node_init(ma_node_graph* pNodeGraph, const ma_data_source_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source_node* pDataSourceNode) +{ + ma_result result; + ma_format format; /* For validating the format, which must be ma_format_f32. */ + ma_uint32 channels; /* For specifying the channel count of the output bus. */ + ma_node_config baseConfig; + + if (pDataSourceNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pDataSourceNode); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + result = ma_data_source_get_data_format(pConfig->pDataSource, &format, &channels, NULL, NULL, 0); /* Don't care about sample rate. This will check pDataSource for NULL. */ + if (result != MA_SUCCESS) { + return result; + } + + MA_ASSERT(format == ma_format_f32); /* <-- If you've triggered this it means your data source is not outputting floating-point samples. You must configure your data source to use ma_format_f32. */ + if (format != ma_format_f32) { + return MA_INVALID_ARGS; /* Invalid format. */ + } + + /* The channel count is defined by the data source. If the caller has manually changed the channels we just ignore it. */ + baseConfig = pConfig->nodeConfig; + baseConfig.vtable = &g_ma_data_source_node_vtable; /* Explicitly set the vtable here to prevent callers from setting it incorrectly. */ + + /* + The channel count is defined by the data source. It is invalid for the caller to manually set + the channel counts in the config. `ma_data_source_node_config_init()` will have defaulted the + channel count pointer to NULL which is how it must remain. If you trigger any of these asserts + it means you're explicitly setting the channel count. Instead, configure the output channel + count of your data source to be the necessary channel count. + */ + if (baseConfig.pOutputChannels != NULL) { + return MA_INVALID_ARGS; + } + + baseConfig.pOutputChannels = &channels; + + result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pDataSourceNode->base); + if (result != MA_SUCCESS) { + return result; + } + + pDataSourceNode->pDataSource = pConfig->pDataSource; + + return MA_SUCCESS; +} + +MA_API void ma_data_source_node_uninit(ma_data_source_node* pDataSourceNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_node_uninit(&pDataSourceNode->base, pAllocationCallbacks); +} + +MA_API ma_result ma_data_source_node_set_looping(ma_data_source_node* pDataSourceNode, ma_bool32 isLooping) +{ + if (pDataSourceNode == NULL) { + return MA_INVALID_ARGS; + } + + return ma_data_source_set_looping(pDataSourceNode->pDataSource, isLooping); +} + +MA_API ma_bool32 ma_data_source_node_is_looping(ma_data_source_node* pDataSourceNode) +{ + if (pDataSourceNode == NULL) { + return MA_FALSE; + } + + return ma_data_source_is_looping(pDataSourceNode->pDataSource); +} + + + +/* Splitter Node. */ +MA_API ma_splitter_node_config ma_splitter_node_config_init(ma_uint32 channels) +{ + ma_splitter_node_config config; + + MA_ZERO_OBJECT(&config); + config.nodeConfig = ma_node_config_init(); + config.channels = channels; + + return config; +} + + +static void ma_splitter_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_node_base* pNodeBase = (ma_node_base*)pNode; + ma_uint32 iOutputBus; + ma_uint32 channels; + + MA_ASSERT(pNodeBase != NULL); + MA_ASSERT(ma_node_get_input_bus_count(pNodeBase) == 1); + MA_ASSERT(ma_node_get_output_bus_count(pNodeBase) >= 2); + + /* We don't need to consider the input frame count - it'll be the same as the output frame count and we process everything. */ + (void)pFrameCountIn; + + /* NOTE: This assumes the same number of channels for all inputs and outputs. This was checked in ma_splitter_node_init(). */ + channels = ma_node_get_input_channels(pNodeBase, 0); + + /* Splitting is just copying the first input bus and copying it over to each output bus. */ + for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNodeBase); iOutputBus += 1) { + ma_copy_pcm_frames(ppFramesOut[iOutputBus], ppFramesIn[0], *pFrameCountOut, ma_format_f32, channels); + } +} + +static ma_node_vtable g_ma_splitter_node_vtable = +{ + ma_splitter_node_process_pcm_frames, + NULL, /* onGetRequiredInputFrameCount */ + 1, /* 1 input bus. */ + 2, /* 2 output buses. */ + 0 +}; + +MA_API ma_result ma_splitter_node_init(ma_node_graph* pNodeGraph, const ma_splitter_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_splitter_node* pSplitterNode) +{ + ma_result result; + ma_node_config baseConfig; + ma_uint32 pInputChannels[1]; + ma_uint32 pOutputChannels[2]; + + if (pSplitterNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pSplitterNode); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + /* Splitters require the same number of channels between inputs and outputs. */ + pInputChannels[0] = pConfig->channels; + pOutputChannels[0] = pConfig->channels; + pOutputChannels[1] = pConfig->channels; + + baseConfig = pConfig->nodeConfig; + baseConfig.vtable = &g_ma_splitter_node_vtable; + baseConfig.pInputChannels = pInputChannels; + baseConfig.pOutputChannels = pOutputChannels; + + result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pSplitterNode->base); + if (result != MA_SUCCESS) { + return result; /* Failed to initialize the base node. */ + } + + return MA_SUCCESS; +} + +MA_API void ma_splitter_node_uninit(ma_splitter_node* pSplitterNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_node_uninit(pSplitterNode, pAllocationCallbacks); +} + + +/* +Biquad Node +*/ +MA_API ma_biquad_node_config ma_biquad_node_config_init(ma_uint32 channels, float b0, float b1, float b2, float a0, float a1, float a2) +{ + ma_biquad_node_config config; + + config.nodeConfig = ma_node_config_init(); + config.biquad = ma_biquad_config_init(ma_format_f32, channels, b0, b1, b2, a0, a1, a2); + + return config; +} + +static void ma_biquad_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode; + + MA_ASSERT(pNode != NULL); + (void)pFrameCountIn; + + ma_biquad_process_pcm_frames(&pLPFNode->biquad, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); +} + +static ma_node_vtable g_ma_biquad_node_vtable = +{ + ma_biquad_node_process_pcm_frames, + NULL, /* onGetRequiredInputFrameCount */ + 1, /* One input. */ + 1, /* One output. */ + 0 /* Default flags. */ +}; + +MA_API ma_result ma_biquad_node_init(ma_node_graph* pNodeGraph, const ma_biquad_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad_node* pNode) +{ + ma_result result; + ma_node_config baseNodeConfig; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pNode); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->biquad.format != ma_format_f32) { + return MA_INVALID_ARGS; /* The format must be f32. */ + } + + result = ma_biquad_init(&pConfig->biquad, pAllocationCallbacks, &pNode->biquad); + if (result != MA_SUCCESS) { + return result; + } + + baseNodeConfig = ma_node_config_init(); + baseNodeConfig.vtable = &g_ma_biquad_node_vtable; + baseNodeConfig.pInputChannels = &pConfig->biquad.channels; + baseNodeConfig.pOutputChannels = &pConfig->biquad.channels; + + result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); + if (result != MA_SUCCESS) { + return result; + } + + return result; +} + +MA_API ma_result ma_biquad_node_reinit(const ma_biquad_config* pConfig, ma_biquad_node* pNode) +{ + ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode; + + MA_ASSERT(pNode != NULL); + + return ma_biquad_reinit(pConfig, &pLPFNode->biquad); +} + +MA_API void ma_biquad_node_uninit(ma_biquad_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode; + + if (pNode == NULL) { + return; + } + + ma_node_uninit(pNode, pAllocationCallbacks); + ma_biquad_uninit(&pLPFNode->biquad, pAllocationCallbacks); +} + + + +/* +Low Pass Filter Node +*/ +MA_API ma_lpf_node_config ma_lpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) +{ + ma_lpf_node_config config; + + config.nodeConfig = ma_node_config_init(); + config.lpf = ma_lpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order); + + return config; +} + +static void ma_lpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode; + + MA_ASSERT(pNode != NULL); + (void)pFrameCountIn; + + ma_lpf_process_pcm_frames(&pLPFNode->lpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); +} + +static ma_node_vtable g_ma_lpf_node_vtable = +{ + ma_lpf_node_process_pcm_frames, + NULL, /* onGetRequiredInputFrameCount */ + 1, /* One input. */ + 1, /* One output. */ + 0 /* Default flags. */ +}; + +MA_API ma_result ma_lpf_node_init(ma_node_graph* pNodeGraph, const ma_lpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf_node* pNode) +{ + ma_result result; + ma_node_config baseNodeConfig; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pNode); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->lpf.format != ma_format_f32) { + return MA_INVALID_ARGS; /* The format must be f32. */ + } + + result = ma_lpf_init(&pConfig->lpf, pAllocationCallbacks, &pNode->lpf); + if (result != MA_SUCCESS) { + return result; + } + + baseNodeConfig = ma_node_config_init(); + baseNodeConfig.vtable = &g_ma_lpf_node_vtable; + baseNodeConfig.pInputChannels = &pConfig->lpf.channels; + baseNodeConfig.pOutputChannels = &pConfig->lpf.channels; + + result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); + if (result != MA_SUCCESS) { + return result; + } + + return result; +} + +MA_API ma_result ma_lpf_node_reinit(const ma_lpf_config* pConfig, ma_lpf_node* pNode) +{ + ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + return ma_lpf_reinit(pConfig, &pLPFNode->lpf); +} + +MA_API void ma_lpf_node_uninit(ma_lpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode; + + if (pNode == NULL) { + return; + } + + ma_node_uninit(pNode, pAllocationCallbacks); + ma_lpf_uninit(&pLPFNode->lpf, pAllocationCallbacks); +} + + + +/* +High Pass Filter Node +*/ +MA_API ma_hpf_node_config ma_hpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) +{ + ma_hpf_node_config config; + + config.nodeConfig = ma_node_config_init(); + config.hpf = ma_hpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order); + + return config; +} + +static void ma_hpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode; + + MA_ASSERT(pNode != NULL); + (void)pFrameCountIn; + + ma_hpf_process_pcm_frames(&pHPFNode->hpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); +} + +static ma_node_vtable g_ma_hpf_node_vtable = +{ + ma_hpf_node_process_pcm_frames, + NULL, /* onGetRequiredInputFrameCount */ + 1, /* One input. */ + 1, /* One output. */ + 0 /* Default flags. */ +}; + +MA_API ma_result ma_hpf_node_init(ma_node_graph* pNodeGraph, const ma_hpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf_node* pNode) +{ + ma_result result; + ma_node_config baseNodeConfig; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pNode); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->hpf.format != ma_format_f32) { + return MA_INVALID_ARGS; /* The format must be f32. */ + } + + result = ma_hpf_init(&pConfig->hpf, pAllocationCallbacks, &pNode->hpf); + if (result != MA_SUCCESS) { + return result; + } + + baseNodeConfig = ma_node_config_init(); + baseNodeConfig.vtable = &g_ma_hpf_node_vtable; + baseNodeConfig.pInputChannels = &pConfig->hpf.channels; + baseNodeConfig.pOutputChannels = &pConfig->hpf.channels; + + result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); + if (result != MA_SUCCESS) { + return result; + } + + return result; +} + +MA_API ma_result ma_hpf_node_reinit(const ma_hpf_config* pConfig, ma_hpf_node* pNode) +{ + ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + return ma_hpf_reinit(pConfig, &pHPFNode->hpf); +} + +MA_API void ma_hpf_node_uninit(ma_hpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode; + + if (pNode == NULL) { + return; + } + + ma_node_uninit(pNode, pAllocationCallbacks); + ma_hpf_uninit(&pHPFNode->hpf, pAllocationCallbacks); +} + + + + +/* +Band Pass Filter Node +*/ +MA_API ma_bpf_node_config ma_bpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) +{ + ma_bpf_node_config config; + + config.nodeConfig = ma_node_config_init(); + config.bpf = ma_bpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order); + + return config; +} + +static void ma_bpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode; + + MA_ASSERT(pNode != NULL); + (void)pFrameCountIn; + + ma_bpf_process_pcm_frames(&pBPFNode->bpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); +} + +static ma_node_vtable g_ma_bpf_node_vtable = +{ + ma_bpf_node_process_pcm_frames, + NULL, /* onGetRequiredInputFrameCount */ + 1, /* One input. */ + 1, /* One output. */ + 0 /* Default flags. */ +}; + +MA_API ma_result ma_bpf_node_init(ma_node_graph* pNodeGraph, const ma_bpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf_node* pNode) +{ + ma_result result; + ma_node_config baseNodeConfig; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pNode); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->bpf.format != ma_format_f32) { + return MA_INVALID_ARGS; /* The format must be f32. */ + } + + result = ma_bpf_init(&pConfig->bpf, pAllocationCallbacks, &pNode->bpf); + if (result != MA_SUCCESS) { + return result; + } + + baseNodeConfig = ma_node_config_init(); + baseNodeConfig.vtable = &g_ma_bpf_node_vtable; + baseNodeConfig.pInputChannels = &pConfig->bpf.channels; + baseNodeConfig.pOutputChannels = &pConfig->bpf.channels; + + result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); + if (result != MA_SUCCESS) { + return result; + } + + return result; +} + +MA_API ma_result ma_bpf_node_reinit(const ma_bpf_config* pConfig, ma_bpf_node* pNode) +{ + ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + return ma_bpf_reinit(pConfig, &pBPFNode->bpf); +} + +MA_API void ma_bpf_node_uninit(ma_bpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode; + + if (pNode == NULL) { + return; + } + + ma_node_uninit(pNode, pAllocationCallbacks); + ma_bpf_uninit(&pBPFNode->bpf, pAllocationCallbacks); +} + + + +/* +Notching Filter Node +*/ +MA_API ma_notch_node_config ma_notch_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency) +{ + ma_notch_node_config config; + + config.nodeConfig = ma_node_config_init(); + config.notch = ma_notch2_config_init(ma_format_f32, channels, sampleRate, q, frequency); + + return config; +} + +static void ma_notch_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_notch_node* pBPFNode = (ma_notch_node*)pNode; + + MA_ASSERT(pNode != NULL); + (void)pFrameCountIn; + + ma_notch2_process_pcm_frames(&pBPFNode->notch, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); +} + +static ma_node_vtable g_ma_notch_node_vtable = +{ + ma_notch_node_process_pcm_frames, + NULL, /* onGetRequiredInputFrameCount */ + 1, /* One input. */ + 1, /* One output. */ + 0 /* Default flags. */ +}; + +MA_API ma_result ma_notch_node_init(ma_node_graph* pNodeGraph, const ma_notch_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch_node* pNode) +{ + ma_result result; + ma_node_config baseNodeConfig; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pNode); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->notch.format != ma_format_f32) { + return MA_INVALID_ARGS; /* The format must be f32. */ + } + + result = ma_notch2_init(&pConfig->notch, pAllocationCallbacks, &pNode->notch); + if (result != MA_SUCCESS) { + return result; + } + + baseNodeConfig = ma_node_config_init(); + baseNodeConfig.vtable = &g_ma_notch_node_vtable; + baseNodeConfig.pInputChannels = &pConfig->notch.channels; + baseNodeConfig.pOutputChannels = &pConfig->notch.channels; + + result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); + if (result != MA_SUCCESS) { + return result; + } + + return result; +} + +MA_API ma_result ma_notch_node_reinit(const ma_notch_config* pConfig, ma_notch_node* pNode) +{ + ma_notch_node* pNotchNode = (ma_notch_node*)pNode; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + return ma_notch2_reinit(pConfig, &pNotchNode->notch); +} + +MA_API void ma_notch_node_uninit(ma_notch_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_notch_node* pNotchNode = (ma_notch_node*)pNode; + + if (pNode == NULL) { + return; + } + + ma_node_uninit(pNode, pAllocationCallbacks); + ma_notch2_uninit(&pNotchNode->notch, pAllocationCallbacks); +} + + + +/* +Peaking Filter Node +*/ +MA_API ma_peak_node_config ma_peak_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency) +{ + ma_peak_node_config config; + + config.nodeConfig = ma_node_config_init(); + config.peak = ma_peak2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency); + + return config; +} + +static void ma_peak_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_peak_node* pBPFNode = (ma_peak_node*)pNode; + + MA_ASSERT(pNode != NULL); + (void)pFrameCountIn; + + ma_peak2_process_pcm_frames(&pBPFNode->peak, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); +} + +static ma_node_vtable g_ma_peak_node_vtable = +{ + ma_peak_node_process_pcm_frames, + NULL, /* onGetRequiredInputFrameCount */ + 1, /* One input. */ + 1, /* One output. */ + 0 /* Default flags. */ +}; + +MA_API ma_result ma_peak_node_init(ma_node_graph* pNodeGraph, const ma_peak_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak_node* pNode) +{ + ma_result result; + ma_node_config baseNodeConfig; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pNode); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->peak.format != ma_format_f32) { + return MA_INVALID_ARGS; /* The format must be f32. */ + } + + result = ma_peak2_init(&pConfig->peak, pAllocationCallbacks, &pNode->peak); + if (result != MA_SUCCESS) { + ma_node_uninit(pNode, pAllocationCallbacks); + return result; + } + + baseNodeConfig = ma_node_config_init(); + baseNodeConfig.vtable = &g_ma_peak_node_vtable; + baseNodeConfig.pInputChannels = &pConfig->peak.channels; + baseNodeConfig.pOutputChannels = &pConfig->peak.channels; + + result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); + if (result != MA_SUCCESS) { + return result; + } + + return result; +} + +MA_API ma_result ma_peak_node_reinit(const ma_peak_config* pConfig, ma_peak_node* pNode) +{ + ma_peak_node* pPeakNode = (ma_peak_node*)pNode; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + return ma_peak2_reinit(pConfig, &pPeakNode->peak); +} + +MA_API void ma_peak_node_uninit(ma_peak_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_peak_node* pPeakNode = (ma_peak_node*)pNode; + + if (pNode == NULL) { + return; + } + + ma_node_uninit(pNode, pAllocationCallbacks); + ma_peak2_uninit(&pPeakNode->peak, pAllocationCallbacks); +} + + + +/* +Low Shelf Filter Node +*/ +MA_API ma_loshelf_node_config ma_loshelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency) +{ + ma_loshelf_node_config config; + + config.nodeConfig = ma_node_config_init(); + config.loshelf = ma_loshelf2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency); + + return config; +} + +static void ma_loshelf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_loshelf_node* pBPFNode = (ma_loshelf_node*)pNode; + + MA_ASSERT(pNode != NULL); + (void)pFrameCountIn; + + ma_loshelf2_process_pcm_frames(&pBPFNode->loshelf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); +} + +static ma_node_vtable g_ma_loshelf_node_vtable = +{ + ma_loshelf_node_process_pcm_frames, + NULL, /* onGetRequiredInputFrameCount */ + 1, /* One input. */ + 1, /* One output. */ + 0 /* Default flags. */ +}; + +MA_API ma_result ma_loshelf_node_init(ma_node_graph* pNodeGraph, const ma_loshelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf_node* pNode) +{ + ma_result result; + ma_node_config baseNodeConfig; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pNode); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->loshelf.format != ma_format_f32) { + return MA_INVALID_ARGS; /* The format must be f32. */ + } + + result = ma_loshelf2_init(&pConfig->loshelf, pAllocationCallbacks, &pNode->loshelf); + if (result != MA_SUCCESS) { + return result; + } + + baseNodeConfig = ma_node_config_init(); + baseNodeConfig.vtable = &g_ma_loshelf_node_vtable; + baseNodeConfig.pInputChannels = &pConfig->loshelf.channels; + baseNodeConfig.pOutputChannels = &pConfig->loshelf.channels; + + result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); + if (result != MA_SUCCESS) { + return result; + } + + return result; +} + +MA_API ma_result ma_loshelf_node_reinit(const ma_loshelf_config* pConfig, ma_loshelf_node* pNode) +{ + ma_loshelf_node* pLoshelfNode = (ma_loshelf_node*)pNode; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + return ma_loshelf2_reinit(pConfig, &pLoshelfNode->loshelf); +} + +MA_API void ma_loshelf_node_uninit(ma_loshelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_loshelf_node* pLoshelfNode = (ma_loshelf_node*)pNode; + + if (pNode == NULL) { + return; + } + + ma_node_uninit(pNode, pAllocationCallbacks); + ma_loshelf2_uninit(&pLoshelfNode->loshelf, pAllocationCallbacks); +} + + + +/* +High Shelf Filter Node +*/ +MA_API ma_hishelf_node_config ma_hishelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency) +{ + ma_hishelf_node_config config; + + config.nodeConfig = ma_node_config_init(); + config.hishelf = ma_hishelf2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency); + + return config; +} + +static void ma_hishelf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_hishelf_node* pBPFNode = (ma_hishelf_node*)pNode; + + MA_ASSERT(pNode != NULL); + (void)pFrameCountIn; + + ma_hishelf2_process_pcm_frames(&pBPFNode->hishelf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); +} + +static ma_node_vtable g_ma_hishelf_node_vtable = +{ + ma_hishelf_node_process_pcm_frames, + NULL, /* onGetRequiredInputFrameCount */ + 1, /* One input. */ + 1, /* One output. */ + 0 /* Default flags. */ +}; + +MA_API ma_result ma_hishelf_node_init(ma_node_graph* pNodeGraph, const ma_hishelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf_node* pNode) +{ + ma_result result; + ma_node_config baseNodeConfig; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pNode); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->hishelf.format != ma_format_f32) { + return MA_INVALID_ARGS; /* The format must be f32. */ + } + + result = ma_hishelf2_init(&pConfig->hishelf, pAllocationCallbacks, &pNode->hishelf); + if (result != MA_SUCCESS) { + return result; + } + + baseNodeConfig = ma_node_config_init(); + baseNodeConfig.vtable = &g_ma_hishelf_node_vtable; + baseNodeConfig.pInputChannels = &pConfig->hishelf.channels; + baseNodeConfig.pOutputChannels = &pConfig->hishelf.channels; + + result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); + if (result != MA_SUCCESS) { + return result; + } + + return result; +} + +MA_API ma_result ma_hishelf_node_reinit(const ma_hishelf_config* pConfig, ma_hishelf_node* pNode) +{ + ma_hishelf_node* pHishelfNode = (ma_hishelf_node*)pNode; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + return ma_hishelf2_reinit(pConfig, &pHishelfNode->hishelf); +} + +MA_API void ma_hishelf_node_uninit(ma_hishelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_hishelf_node* pHishelfNode = (ma_hishelf_node*)pNode; + + if (pNode == NULL) { + return; + } + + ma_node_uninit(pNode, pAllocationCallbacks); + ma_hishelf2_uninit(&pHishelfNode->hishelf, pAllocationCallbacks); +} + + + + +MA_API ma_delay_node_config ma_delay_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay) +{ + ma_delay_node_config config; + + config.nodeConfig = ma_node_config_init(); + config.delay = ma_delay_config_init(channels, sampleRate, delayInFrames, decay); + + return config; +} + + +static void ma_delay_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_delay_node* pDelayNode = (ma_delay_node*)pNode; + + (void)pFrameCountIn; + + ma_delay_process_pcm_frames(&pDelayNode->delay, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); +} + +static ma_node_vtable g_ma_delay_node_vtable = +{ + ma_delay_node_process_pcm_frames, + NULL, + 1, /* 1 input channels. */ + 1, /* 1 output channel. */ + MA_NODE_FLAG_CONTINUOUS_PROCESSING /* Delay requires continuous processing to ensure the tail get's processed. */ +}; + +MA_API ma_result ma_delay_node_init(ma_node_graph* pNodeGraph, const ma_delay_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay_node* pDelayNode) +{ + ma_result result; + ma_node_config baseConfig; + + if (pDelayNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pDelayNode); + + result = ma_delay_init(&pConfig->delay, pAllocationCallbacks, &pDelayNode->delay); + if (result != MA_SUCCESS) { + return result; + } + + baseConfig = pConfig->nodeConfig; + baseConfig.vtable = &g_ma_delay_node_vtable; + baseConfig.pInputChannels = &pConfig->delay.channels; + baseConfig.pOutputChannels = &pConfig->delay.channels; + + result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pDelayNode->baseNode); + if (result != MA_SUCCESS) { + ma_delay_uninit(&pDelayNode->delay, pAllocationCallbacks); + return result; + } + + return result; +} + +MA_API void ma_delay_node_uninit(ma_delay_node* pDelayNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pDelayNode == NULL) { + return; + } + + /* The base node is always uninitialized first. */ + ma_node_uninit(pDelayNode, pAllocationCallbacks); + ma_delay_uninit(&pDelayNode->delay, pAllocationCallbacks); +} + +MA_API void ma_delay_node_set_wet(ma_delay_node* pDelayNode, float value) +{ + if (pDelayNode == NULL) { + return; + } + + ma_delay_set_wet(&pDelayNode->delay, value); +} + +MA_API float ma_delay_node_get_wet(const ma_delay_node* pDelayNode) +{ + if (pDelayNode == NULL) { + return 0; + } + + return ma_delay_get_wet(&pDelayNode->delay); +} + +MA_API void ma_delay_node_set_dry(ma_delay_node* pDelayNode, float value) +{ + if (pDelayNode == NULL) { + return; + } + + ma_delay_set_dry(&pDelayNode->delay, value); +} + +MA_API float ma_delay_node_get_dry(const ma_delay_node* pDelayNode) +{ + if (pDelayNode == NULL) { + return 0; + } + + return ma_delay_get_dry(&pDelayNode->delay); +} + +MA_API void ma_delay_node_set_decay(ma_delay_node* pDelayNode, float value) +{ + if (pDelayNode == NULL) { + return; + } + + ma_delay_set_decay(&pDelayNode->delay, value); +} + +MA_API float ma_delay_node_get_decay(const ma_delay_node* pDelayNode) +{ + if (pDelayNode == NULL) { + return 0; + } + + return ma_delay_get_decay(&pDelayNode->delay); +} +#endif /* MA_NO_NODE_GRAPH */ + + +#if !defined(MA_NO_ENGINE) && !defined(MA_NO_NODE_GRAPH) +/************************************************************************************************************************************************************** + +Engine + +**************************************************************************************************************************************************************/ +#define MA_SEEK_TARGET_NONE (~(ma_uint64)0) + +MA_API ma_engine_node_config ma_engine_node_config_init(ma_engine* pEngine, ma_engine_node_type type, ma_uint32 flags) +{ + ma_engine_node_config config; + + MA_ZERO_OBJECT(&config); + config.pEngine = pEngine; + config.type = type; + config.isPitchDisabled = (flags & MA_SOUND_FLAG_NO_PITCH) != 0; + config.isSpatializationDisabled = (flags & MA_SOUND_FLAG_NO_SPATIALIZATION) != 0; + + return config; +} + + +static void ma_engine_node_update_pitch_if_required(ma_engine_node* pEngineNode) +{ + ma_bool32 isUpdateRequired = MA_FALSE; + float newPitch; + + MA_ASSERT(pEngineNode != NULL); + + newPitch = c89atomic_load_explicit_f32(&pEngineNode->pitch, c89atomic_memory_order_acquire); + + if (pEngineNode->oldPitch != newPitch) { + pEngineNode->oldPitch = newPitch; + isUpdateRequired = MA_TRUE; + } + + if (pEngineNode->oldDopplerPitch != pEngineNode->spatializer.dopplerPitch) { + pEngineNode->oldDopplerPitch = pEngineNode->spatializer.dopplerPitch; + isUpdateRequired = MA_TRUE; + } + + if (isUpdateRequired) { + float basePitch = (float)pEngineNode->sampleRate / ma_engine_get_sample_rate(pEngineNode->pEngine); + ma_linear_resampler_set_rate_ratio(&pEngineNode->resampler, basePitch * pEngineNode->oldPitch * pEngineNode->oldDopplerPitch); + } +} + +static ma_bool32 ma_engine_node_is_pitching_enabled(const ma_engine_node* pEngineNode) +{ + MA_ASSERT(pEngineNode != NULL); + + /* Don't try to be clever by skiping resampling in the pitch=1 case or else you'll glitch when moving away from 1. */ + return !c89atomic_load_explicit_32(&pEngineNode->isPitchDisabled, c89atomic_memory_order_acquire); +} + +static ma_bool32 ma_engine_node_is_spatialization_enabled(const ma_engine_node* pEngineNode) +{ + MA_ASSERT(pEngineNode != NULL); + + return !c89atomic_load_explicit_32(&pEngineNode->isSpatializationDisabled, c89atomic_memory_order_acquire); +} + +static ma_uint64 ma_engine_node_get_required_input_frame_count(const ma_engine_node* pEngineNode, ma_uint64 outputFrameCount) +{ + ma_uint64 inputFrameCount = 0; + + if (ma_engine_node_is_pitching_enabled(pEngineNode)) { + ma_result result = ma_linear_resampler_get_required_input_frame_count(&pEngineNode->resampler, outputFrameCount, &inputFrameCount); + if (result != MA_SUCCESS) { + inputFrameCount = 0; + } + } else { + inputFrameCount = outputFrameCount; /* No resampling, so 1:1. */ + } + + return inputFrameCount; +} + +static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_uint32 frameCountIn; + ma_uint32 frameCountOut; + ma_uint32 totalFramesProcessedIn; + ma_uint32 totalFramesProcessedOut; + ma_uint32 channelsIn; + ma_uint32 channelsOut; + ma_bool32 isPitchingEnabled; + ma_bool32 isFadingEnabled; + ma_bool32 isSpatializationEnabled; + ma_bool32 isPanningEnabled; + + frameCountIn = *pFrameCountIn; + frameCountOut = *pFrameCountOut; + + channelsIn = ma_spatializer_get_input_channels(&pEngineNode->spatializer); + channelsOut = ma_spatializer_get_output_channels(&pEngineNode->spatializer); + + totalFramesProcessedIn = 0; + totalFramesProcessedOut = 0; + + isPitchingEnabled = ma_engine_node_is_pitching_enabled(pEngineNode); + isFadingEnabled = pEngineNode->fader.volumeBeg != 1 || pEngineNode->fader.volumeEnd != 1; + isSpatializationEnabled = ma_engine_node_is_spatialization_enabled(pEngineNode); + isPanningEnabled = pEngineNode->panner.pan != 0 && channelsOut != 1; + + /* Keep going while we've still got data available for processing. */ + while (totalFramesProcessedOut < frameCountOut) { + /* + We need to process in a specific order. We always do resampling first because it's likely + we're going to be increasing the channel count after spatialization. Also, I want to do + fading based on the output sample rate. + + We'll first read into a buffer from the resampler. Then we'll do all processing that + operates on the on the input channel count. We'll then get the spatializer to output to + the output buffer and then do all effects from that point directly in the output buffer + in-place. + + Note that we're always running the resampler. If we try to be clever and skip resampling + when the pitch is 1, we'll get a glitch when we move away from 1, back to 1, and then + away from 1 again. We'll want to implement any pitch=1 optimizations in the resampler + itself. + + There's a small optimization here that we'll utilize since it might be a fairly common + case. When the input and output channel counts are the same, we'll read straight into the + output buffer from the resampler and do everything in-place. + */ + const float* pRunningFramesIn; + float* pRunningFramesOut; + float* pWorkingBuffer; /* This is the buffer that we'll be processing frames in. This is in input channels. */ + float temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE / sizeof(float)]; + ma_uint32 tempCapInFrames = ma_countof(temp) / channelsIn; + ma_uint32 framesAvailableIn; + ma_uint32 framesAvailableOut; + ma_uint32 framesJustProcessedIn; + ma_uint32 framesJustProcessedOut; + ma_bool32 isWorkingBufferValid = MA_FALSE; + + framesAvailableIn = frameCountIn - totalFramesProcessedIn; + framesAvailableOut = frameCountOut - totalFramesProcessedOut; + + pRunningFramesIn = ma_offset_pcm_frames_const_ptr_f32(ppFramesIn[0], totalFramesProcessedIn, channelsIn); + pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesProcessedOut, channelsOut); + + if (channelsIn == channelsOut) { + /* Fast path. Channel counts are the same. No need for an intermediary input buffer. */ + pWorkingBuffer = pRunningFramesOut; + } else { + /* Slow path. Channel counts are different. Need to use an intermediary input buffer. */ + pWorkingBuffer = temp; + if (framesAvailableOut > tempCapInFrames) { + framesAvailableOut = tempCapInFrames; + } + } + + /* First is resampler. */ + if (isPitchingEnabled) { + ma_uint64 resampleFrameCountIn = framesAvailableIn; + ma_uint64 resampleFrameCountOut = framesAvailableOut; + + ma_linear_resampler_process_pcm_frames(&pEngineNode->resampler, pRunningFramesIn, &resampleFrameCountIn, pWorkingBuffer, &resampleFrameCountOut); + isWorkingBufferValid = MA_TRUE; + + framesJustProcessedIn = (ma_uint32)resampleFrameCountIn; + framesJustProcessedOut = (ma_uint32)resampleFrameCountOut; + } else { + framesJustProcessedIn = ma_min(framesAvailableIn, framesAvailableOut); + framesJustProcessedOut = framesJustProcessedIn; /* When no resampling is being performed, the number of output frames is the same as input frames. */ + } + + /* Fading. */ + if (isFadingEnabled) { + if (isWorkingBufferValid) { + ma_fader_process_pcm_frames(&pEngineNode->fader, pWorkingBuffer, pWorkingBuffer, framesJustProcessedOut); /* In-place processing. */ + } else { + ma_fader_process_pcm_frames(&pEngineNode->fader, pWorkingBuffer, pRunningFramesIn, framesJustProcessedOut); + isWorkingBufferValid = MA_TRUE; + } + } + + /* + If at this point we still haven't actually done anything with the working buffer we need + to just read straight from the input buffer. + */ + if (isWorkingBufferValid == MA_FALSE) { + pWorkingBuffer = (float*)pRunningFramesIn; /* Naughty const cast, but it's safe at this point because we won't ever be writing to it from this point out. */ + } + + /* Spatialization. */ + if (isSpatializationEnabled) { + ma_uint32 iListener; + + /* + When determining the listener to use, we first check to see if the sound is pinned to a + specific listener. If so, we use that. Otherwise we just use the closest listener. + */ + if (pEngineNode->pinnedListenerIndex != MA_LISTENER_INDEX_CLOSEST && pEngineNode->pinnedListenerIndex < ma_engine_get_listener_count(pEngineNode->pEngine)) { + iListener = pEngineNode->pinnedListenerIndex; + } else { + iListener = ma_engine_find_closest_listener(pEngineNode->pEngine, pEngineNode->spatializer.position.x, pEngineNode->spatializer.position.y, pEngineNode->spatializer.position.z); + } + + ma_spatializer_process_pcm_frames(&pEngineNode->spatializer, &pEngineNode->pEngine->listeners[iListener], pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut); + } else { + /* No spatialization, but we still need to do channel conversion. */ + if (channelsIn == channelsOut) { + /* No channel conversion required. Just copy straight to the output buffer. */ + ma_copy_pcm_frames(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut, ma_format_f32, channelsOut); + } else { + /* Channel conversion required. TODO: Add support for channel maps here. */ + ma_channel_map_apply_f32(pRunningFramesOut, NULL, channelsOut, pWorkingBuffer, NULL, channelsIn, framesJustProcessedOut, ma_channel_mix_mode_simple, pEngineNode->pEngine->monoExpansionMode); + } + } + + /* At this point we can guarantee that the output buffer contains valid data. We can process everything in place now. */ + + /* Panning. */ + if (isPanningEnabled) { + ma_panner_process_pcm_frames(&pEngineNode->panner, pRunningFramesOut, pRunningFramesOut, framesJustProcessedOut); /* In-place processing. */ + } + + /* We're done for this chunk. */ + totalFramesProcessedIn += framesJustProcessedIn; + totalFramesProcessedOut += framesJustProcessedOut; + + /* If we didn't process any output frames this iteration it means we've either run out of input data, or run out of room in the output buffer. */ + if (framesJustProcessedOut == 0) { + break; + } + } + + /* At this point we're done processing. */ + *pFrameCountIn = totalFramesProcessedIn; + *pFrameCountOut = totalFramesProcessedOut; +} + +static void ma_engine_node_process_pcm_frames__sound(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + /* For sounds, we need to first read from the data source. Then we need to apply the engine effects (pan, pitch, fades, etc.). */ + ma_result result = MA_SUCCESS; + ma_sound* pSound = (ma_sound*)pNode; + ma_uint32 frameCount = *pFrameCountOut; + ma_uint32 totalFramesRead = 0; + ma_format dataSourceFormat; + ma_uint32 dataSourceChannels; + ma_uint8 temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; + ma_uint32 tempCapInFrames; + ma_uint64 seekTarget; + + /* This is a data source node which means no input buses. */ + (void)ppFramesIn; + (void)pFrameCountIn; + + /* If we're marked at the end we need to stop the sound and do nothing. */ + if (ma_sound_at_end(pSound)) { + ma_sound_stop(pSound); + *pFrameCountOut = 0; + return; + } + + /* If we're seeking, do so now before reading. */ + seekTarget = c89atomic_load_64(&pSound->seekTarget); + if (seekTarget != MA_SEEK_TARGET_NONE) { + ma_data_source_seek_to_pcm_frame(pSound->pDataSource, seekTarget); + + /* Any time-dependant effects need to have their times updated. */ + ma_node_set_time(pSound, seekTarget); + + c89atomic_exchange_64(&pSound->seekTarget, MA_SEEK_TARGET_NONE); + } + + /* + We want to update the pitch once. For sounds, this can be either at the start or at the end. If + we don't force this to only ever be updating once, we could end up in a situation where + retrieving the required input frame count ends up being different to what we actually retrieve. + What could happen is that the required input frame count is calculated, the pitch is update, + and then this processing function is called resulting in a different number of input frames + being processed. Do not call this in ma_engine_node_process_pcm_frames__general() or else + you'll hit the aforementioned bug. + */ + ma_engine_node_update_pitch_if_required(&pSound->engineNode); + + /* + For the convenience of the caller, we're doing to allow data sources to use non-floating-point formats and channel counts that differ + from the main engine. + */ + result = ma_data_source_get_data_format(pSound->pDataSource, &dataSourceFormat, &dataSourceChannels, NULL, NULL, 0); + if (result == MA_SUCCESS) { + tempCapInFrames = sizeof(temp) / ma_get_bytes_per_frame(dataSourceFormat, dataSourceChannels); + + /* Keep reading until we've read as much as was requested or we reach the end of the data source. */ + while (totalFramesRead < frameCount) { + ma_uint32 framesRemaining = frameCount - totalFramesRead; + ma_uint32 framesToRead; + ma_uint64 framesJustRead; + ma_uint32 frameCountIn; + ma_uint32 frameCountOut; + const float* pRunningFramesIn; + float* pRunningFramesOut; + + /* + The first thing we need to do is read into the temporary buffer. We can calculate exactly + how many input frames we'll need after resampling. + */ + framesToRead = (ma_uint32)ma_engine_node_get_required_input_frame_count(&pSound->engineNode, framesRemaining); + if (framesToRead > tempCapInFrames) { + framesToRead = tempCapInFrames; + } + + result = ma_data_source_read_pcm_frames(pSound->pDataSource, temp, framesToRead, &framesJustRead); + + /* If we reached the end of the sound we'll want to mark it as at the end and stop it. This should never be returned for looping sounds. */ + if (result == MA_AT_END) { + c89atomic_exchange_32(&pSound->atEnd, MA_TRUE); /* This will be set to false in ma_sound_start(). */ + } + + pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesRead, ma_engine_get_channels(ma_sound_get_engine(pSound))); + + frameCountIn = (ma_uint32)framesJustRead; + frameCountOut = framesRemaining; + + /* Convert if necessary. */ + if (dataSourceFormat == ma_format_f32) { + /* Fast path. No data conversion necessary. */ + pRunningFramesIn = (float*)temp; + ma_engine_node_process_pcm_frames__general(&pSound->engineNode, &pRunningFramesIn, &frameCountIn, &pRunningFramesOut, &frameCountOut); + } else { + /* Slow path. Need to do sample format conversion to f32. If we give the f32 buffer the same count as the first temp buffer, we're guaranteed it'll be large enough. */ + float tempf32[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* Do not do `MA_DATA_CONVERTER_STACK_BUFFER_SIZE/sizeof(float)` here like we've done in other places. */ + ma_convert_pcm_frames_format(tempf32, ma_format_f32, temp, dataSourceFormat, framesJustRead, dataSourceChannels, ma_dither_mode_none); + + /* Now that we have our samples in f32 format we can process like normal. */ + pRunningFramesIn = tempf32; + ma_engine_node_process_pcm_frames__general(&pSound->engineNode, &pRunningFramesIn, &frameCountIn, &pRunningFramesOut, &frameCountOut); + } + + /* We should have processed all of our input frames since we calculated the required number of input frames at the top. */ + MA_ASSERT(frameCountIn == framesJustRead); + totalFramesRead += (ma_uint32)frameCountOut; /* Safe cast. */ + + if (result != MA_SUCCESS || ma_sound_at_end(pSound)) { + break; /* Might have reached the end. */ + } + } + } + + *pFrameCountOut = totalFramesRead; +} + +static void ma_engine_node_process_pcm_frames__group(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + /* + Make sure the pitch is updated before trying to read anything. It's important that this is done + only once and not in ma_engine_node_process_pcm_frames__general(). The reason for this is that + ma_engine_node_process_pcm_frames__general() will call ma_engine_node_get_required_input_frame_count(), + and if another thread modifies the pitch just after that call it can result in a glitch due to + the input rate changing. + */ + ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode); + + /* For groups, the input data has already been read and we just need to apply the effect. */ + ma_engine_node_process_pcm_frames__general((ma_engine_node*)pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut); +} + +static ma_result ma_engine_node_get_required_input_frame_count__group(ma_node* pNode, ma_uint32 outputFrameCount, ma_uint32* pInputFrameCount) +{ + ma_uint64 inputFrameCount; + + MA_ASSERT(pInputFrameCount != NULL); + + /* Our pitch will affect this calculation. We need to update it. */ + ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode); + + inputFrameCount = ma_engine_node_get_required_input_frame_count((ma_engine_node*)pNode, outputFrameCount); + if (inputFrameCount > 0xFFFFFFFF) { + inputFrameCount = 0xFFFFFFFF; /* Will never happen because miniaudio will only ever process in relatively small chunks. */ + } + + *pInputFrameCount = (ma_uint32)inputFrameCount; + + return MA_SUCCESS; +} + + +static ma_node_vtable g_ma_engine_node_vtable__sound = +{ + ma_engine_node_process_pcm_frames__sound, + NULL, /* onGetRequiredInputFrameCount */ + 0, /* Sounds are data source nodes which means they have zero inputs (their input is drawn from the data source itself). */ + 1, /* Sounds have one output bus. */ + 0 /* Default flags. */ +}; + +static ma_node_vtable g_ma_engine_node_vtable__group = +{ + ma_engine_node_process_pcm_frames__group, + ma_engine_node_get_required_input_frame_count__group, + 1, /* Groups have one input bus. */ + 1, /* Groups have one output bus. */ + MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES /* The engine node does resampling so should let miniaudio know about it. */ +}; + + + +static ma_node_config ma_engine_node_base_node_config_init(const ma_engine_node_config* pConfig) +{ + ma_node_config baseNodeConfig; + + if (pConfig->type == ma_engine_node_type_sound) { + /* Sound. */ + baseNodeConfig = ma_node_config_init(); + baseNodeConfig.vtable = &g_ma_engine_node_vtable__sound; + baseNodeConfig.initialState = ma_node_state_stopped; /* Sounds are stopped by default. */ + } else { + /* Group. */ + baseNodeConfig = ma_node_config_init(); + baseNodeConfig.vtable = &g_ma_engine_node_vtable__group; + baseNodeConfig.initialState = ma_node_state_started; /* Groups are started by default. */ + } + + return baseNodeConfig; +} + +static ma_spatializer_config ma_engine_node_spatializer_config_init(const ma_node_config* pBaseNodeConfig) +{ + return ma_spatializer_config_init(pBaseNodeConfig->pInputChannels[0], pBaseNodeConfig->pOutputChannels[0]); +} + +typedef struct +{ + size_t sizeInBytes; + size_t baseNodeOffset; + size_t resamplerOffset; + size_t spatializerOffset; +} ma_engine_node_heap_layout; + +static ma_result ma_engine_node_get_heap_layout(const ma_engine_node_config* pConfig, ma_engine_node_heap_layout* pHeapLayout) +{ + ma_result result; + size_t tempHeapSize; + ma_node_config baseNodeConfig; + ma_linear_resampler_config resamplerConfig; + ma_spatializer_config spatializerConfig; + ma_uint32 channelsIn; + ma_uint32 channelsOut; + + MA_ASSERT(pHeapLayout); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->pEngine == NULL) { + return MA_INVALID_ARGS; /* An engine must be specified. */ + } + + pHeapLayout->sizeInBytes = 0; + + channelsIn = (pConfig->channelsIn != 0) ? pConfig->channelsIn : ma_engine_get_channels(pConfig->pEngine); + channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine); + + + /* Base node. */ + baseNodeConfig = ma_engine_node_base_node_config_init(pConfig); + baseNodeConfig.pInputChannels = &channelsIn; + baseNodeConfig.pOutputChannels = &channelsOut; + + result = ma_node_get_heap_size(ma_engine_get_node_graph(pConfig->pEngine), &baseNodeConfig, &tempHeapSize); + if (result != MA_SUCCESS) { + return result; /* Failed to retrieve the size of the heap for the base node. */ + } + + pHeapLayout->baseNodeOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); + + + /* Resmapler. */ + resamplerConfig = ma_linear_resampler_config_init(ma_format_f32, channelsIn, 1, 1); /* Input and output sample rates don't affect the calculation of the heap size. */ + resamplerConfig.lpfOrder = 0; + + result = ma_linear_resampler_get_heap_size(&resamplerConfig, &tempHeapSize); + if (result != MA_SUCCESS) { + return result; /* Failed to retrieve the size of the heap for the resampler. */ + } + + pHeapLayout->resamplerOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); + + + /* Spatializer. */ + spatializerConfig = ma_engine_node_spatializer_config_init(&baseNodeConfig); + + result = ma_spatializer_get_heap_size(&spatializerConfig, &tempHeapSize); + if (result != MA_SUCCESS) { + return result; /* Failed to retrieve the size of the heap for the spatializer. */ + } + + pHeapLayout->spatializerOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); + + + return MA_SUCCESS; +} + +MA_API ma_result ma_engine_node_get_heap_size(const ma_engine_node_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_engine_node_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_engine_node_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return MA_SUCCESS; +} + +MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* pConfig, void* pHeap, ma_engine_node* pEngineNode) +{ + ma_result result; + ma_engine_node_heap_layout heapLayout; + ma_node_config baseNodeConfig; + ma_linear_resampler_config resamplerConfig; + ma_fader_config faderConfig; + ma_spatializer_config spatializerConfig; + ma_panner_config pannerConfig; + ma_uint32 channelsIn; + ma_uint32 channelsOut; + + if (pEngineNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pEngineNode); + + result = ma_engine_node_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + if (pConfig->pinnedListenerIndex != MA_LISTENER_INDEX_CLOSEST && pConfig->pinnedListenerIndex >= ma_engine_get_listener_count(pConfig->pEngine)) { + return MA_INVALID_ARGS; /* Invalid listener. */ + } + + pEngineNode->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pEngineNode->pEngine = pConfig->pEngine; + pEngineNode->sampleRate = (pConfig->sampleRate > 0) ? pConfig->sampleRate : ma_engine_get_sample_rate(pEngineNode->pEngine); + pEngineNode->pitch = 1; + pEngineNode->oldPitch = 1; + pEngineNode->oldDopplerPitch = 1; + pEngineNode->isPitchDisabled = pConfig->isPitchDisabled; + pEngineNode->isSpatializationDisabled = pConfig->isSpatializationDisabled; + pEngineNode->pinnedListenerIndex = pConfig->pinnedListenerIndex; + + + channelsIn = (pConfig->channelsIn != 0) ? pConfig->channelsIn : ma_engine_get_channels(pConfig->pEngine); + channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine); + + + /* Base node. */ + baseNodeConfig = ma_engine_node_base_node_config_init(pConfig); + baseNodeConfig.pInputChannels = &channelsIn; + baseNodeConfig.pOutputChannels = &channelsOut; + + result = ma_node_init_preallocated(&pConfig->pEngine->nodeGraph, &baseNodeConfig, ma_offset_ptr(pHeap, heapLayout.baseNodeOffset), &pEngineNode->baseNode); + if (result != MA_SUCCESS) { + goto error0; + } + + + /* + We can now initialize the effects we need in order to implement the engine node. There's a + defined order of operations here, mainly centered around when we convert our channels from the + data source's native channel count to the engine's channel count. As a rule, we want to do as + much computation as possible before spatialization because there's a chance that will increase + the channel count, thereby increasing the amount of work needing to be done to process. + */ + + /* We'll always do resampling first. */ + resamplerConfig = ma_linear_resampler_config_init(ma_format_f32, baseNodeConfig.pInputChannels[0], pEngineNode->sampleRate, ma_engine_get_sample_rate(pEngineNode->pEngine)); + resamplerConfig.lpfOrder = 0; /* <-- Need to disable low-pass filtering for pitch shifting for now because there's cases where the biquads are becoming unstable. Need to figure out a better fix for this. */ + + result = ma_linear_resampler_init_preallocated(&resamplerConfig, ma_offset_ptr(pHeap, heapLayout.resamplerOffset), &pEngineNode->resampler); + if (result != MA_SUCCESS) { + goto error1; + } + + + /* After resampling will come the fader. */ + faderConfig = ma_fader_config_init(ma_format_f32, baseNodeConfig.pInputChannels[0], ma_engine_get_sample_rate(pEngineNode->pEngine)); + + result = ma_fader_init(&faderConfig, &pEngineNode->fader); + if (result != MA_SUCCESS) { + goto error2; + } + + + /* + Spatialization comes next. We spatialize based ont he node's output channel count. It's up the caller to + ensure channels counts link up correctly in the node graph. + */ + spatializerConfig = ma_engine_node_spatializer_config_init(&baseNodeConfig); + spatializerConfig.gainSmoothTimeInFrames = pEngineNode->pEngine->gainSmoothTimeInFrames; + + result = ma_spatializer_init_preallocated(&spatializerConfig, ma_offset_ptr(pHeap, heapLayout.spatializerOffset), &pEngineNode->spatializer); + if (result != MA_SUCCESS) { + goto error2; + } + + + /* + After spatialization comes panning. We need to do this after spatialization because otherwise we wouldn't + be able to pan mono sounds. + */ + pannerConfig = ma_panner_config_init(ma_format_f32, baseNodeConfig.pOutputChannels[0]); + + result = ma_panner_init(&pannerConfig, &pEngineNode->panner); + if (result != MA_SUCCESS) { + goto error3; + } + + return MA_SUCCESS; + + /* No need for allocation callbacks here because we use a preallocated heap. */ +error3: ma_spatializer_uninit(&pEngineNode->spatializer, NULL); +error2: ma_linear_resampler_uninit(&pEngineNode->resampler, NULL); +error1: ma_node_uninit(&pEngineNode->baseNode, NULL); +error0: return result; +} + +MA_API ma_result ma_engine_node_init(const ma_engine_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_engine_node* pEngineNode) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_engine_node_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_engine_node_init_preallocated(pConfig, pHeap, pEngineNode); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pEngineNode->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + /* + The base node always needs to be uninitialized first to ensure it's detached from the graph completely before we + destroy anything that might be in the middle of being used by the processing function. + */ + ma_node_uninit(&pEngineNode->baseNode, pAllocationCallbacks); + + /* Now that the node has been uninitialized we can safely uninitialize the rest. */ + ma_spatializer_uninit(&pEngineNode->spatializer, pAllocationCallbacks); + ma_linear_resampler_uninit(&pEngineNode->resampler, pAllocationCallbacks); + + /* Free the heap last. */ + if (pEngineNode->_ownsHeap) { + ma_free(pEngineNode->_pHeap, pAllocationCallbacks); + } +} + + +MA_API ma_sound_config ma_sound_config_init(void) +{ + ma_sound_config config; + + MA_ZERO_OBJECT(&config); + config.rangeEndInPCMFrames = ~((ma_uint64)0); + config.loopPointEndInPCMFrames = ~((ma_uint64)0); + + return config; +} + +MA_API ma_sound_group_config ma_sound_group_config_init(void) +{ + ma_sound_group_config config; + + MA_ZERO_OBJECT(&config); + + return config; +} + + +MA_API ma_engine_config ma_engine_config_init(void) +{ + ma_engine_config config; + + MA_ZERO_OBJECT(&config); + config.listenerCount = 1; /* Always want at least one listener. */ + config.monoExpansionMode = ma_mono_expansion_mode_default; + + return config; +} + + +#if !defined(MA_NO_DEVICE_IO) +static void ma_engine_data_callback_internal(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) +{ + ma_engine* pEngine = (ma_engine*)pDevice->pUserData; + + (void)pFramesIn; + + /* + Experiment: Try processing a resource manager job if we're on the Emscripten build. + + This serves two purposes: + + 1) It ensures jobs are actually processed at some point since we cannot guarantee that the + caller is doing the right thing and calling ma_resource_manager_process_next_job(); and + + 2) It's an attempt at working around an issue where processing jobs on the Emscripten main + loop doesn't work as well as it should. When trying to load sounds without the `DECODE` + flag or with the `ASYNC` flag, the sound data is just not able to be loaded in time + before the callback is processed. I think it's got something to do with the single- + threaded nature of Web, but I'm not entirely sure. + */ + #if !defined(MA_NO_RESOURCE_MANAGER) && defined(MA_EMSCRIPTEN) + { + if (pEngine->pResourceManager != NULL) { + if ((pEngine->pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) != 0) { + ma_resource_manager_process_next_job(pEngine->pResourceManager); + } + } + } + #endif + + ma_engine_read_pcm_frames(pEngine, pFramesOut, frameCount, NULL); +} +#endif + +MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEngine) +{ + ma_result result; + ma_node_graph_config nodeGraphConfig; + ma_engine_config engineConfig; + ma_spatializer_listener_config listenerConfig; + ma_uint32 iListener; + + if (pEngine == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pEngine); + + /* The config is allowed to be NULL in which case we use defaults for everything. */ + if (pConfig != NULL) { + engineConfig = *pConfig; + } else { + engineConfig = ma_engine_config_init(); + } + + pEngine->monoExpansionMode = engineConfig.monoExpansionMode; + ma_allocation_callbacks_init_copy(&pEngine->allocationCallbacks, &engineConfig.allocationCallbacks); + + #if !defined(MA_NO_RESOURCE_MANAGER) + { + pEngine->pResourceManager = engineConfig.pResourceManager; + } + #endif + + #if !defined(MA_NO_DEVICE_IO) + { + pEngine->pDevice = engineConfig.pDevice; + + /* If we don't have a device, we need one. */ + if (pEngine->pDevice == NULL && engineConfig.noDevice == MA_FALSE) { + ma_device_config deviceConfig; + + pEngine->pDevice = (ma_device*)ma_malloc(sizeof(*pEngine->pDevice), &pEngine->allocationCallbacks); + if (pEngine->pDevice == NULL) { + return MA_OUT_OF_MEMORY; + } + + deviceConfig = ma_device_config_init(ma_device_type_playback); + deviceConfig.playback.pDeviceID = engineConfig.pPlaybackDeviceID; + deviceConfig.playback.format = ma_format_f32; + deviceConfig.playback.channels = engineConfig.channels; + deviceConfig.sampleRate = engineConfig.sampleRate; + deviceConfig.dataCallback = ma_engine_data_callback_internal; + deviceConfig.pUserData = pEngine; + deviceConfig.periodSizeInFrames = engineConfig.periodSizeInFrames; + deviceConfig.periodSizeInMilliseconds = engineConfig.periodSizeInMilliseconds; + deviceConfig.noPreSilencedOutputBuffer = MA_TRUE; /* We'll always be outputting to every frame in the callback so there's no need for a pre-silenced buffer. */ + deviceConfig.noClip = MA_TRUE; /* The engine will do clipping itself. */ + + if (engineConfig.pContext == NULL) { + ma_context_config contextConfig = ma_context_config_init(); + contextConfig.allocationCallbacks = pEngine->allocationCallbacks; + contextConfig.pLog = engineConfig.pLog; + + /* If the engine config does not specify a log, use the resource manager's if we have one. */ + #ifndef MA_NO_RESOURCE_MANAGER + { + if (contextConfig.pLog == NULL && engineConfig.pResourceManager != NULL) { + contextConfig.pLog = ma_resource_manager_get_log(engineConfig.pResourceManager); + } + } + #endif + + result = ma_device_init_ex(NULL, 0, &contextConfig, &deviceConfig, pEngine->pDevice); + } else { + result = ma_device_init(engineConfig.pContext, &deviceConfig, pEngine->pDevice); + } + + if (result != MA_SUCCESS) { + ma_free(pEngine->pDevice, &pEngine->allocationCallbacks); + pEngine->pDevice = NULL; + return result; + } + + pEngine->ownsDevice = MA_TRUE; + } + + /* Update the channel count and sample rate of the engine config so we can reference it below. */ + if (pEngine->pDevice != NULL) { + engineConfig.channels = pEngine->pDevice->playback.channels; + engineConfig.sampleRate = pEngine->pDevice->sampleRate; + } + } + #endif + + if (engineConfig.channels == 0 || engineConfig.sampleRate == 0) { + return MA_INVALID_ARGS; + } + + pEngine->sampleRate = engineConfig.sampleRate; + + /* The engine always uses either the log that was passed into the config, or the context's log is available. */ + if (engineConfig.pLog != NULL) { + pEngine->pLog = engineConfig.pLog; + } else { + #if !defined(MA_NO_DEVICE_IO) + { + pEngine->pLog = ma_device_get_log(pEngine->pDevice); + } + #else + { + pEngine->pLog = NULL; + } + #endif + } + + + /* The engine is a node graph. This needs to be initialized after we have the device so we can can determine the channel count. */ + nodeGraphConfig = ma_node_graph_config_init(engineConfig.channels); + nodeGraphConfig.nodeCacheCapInFrames = (engineConfig.periodSizeInFrames > 0xFFFF) ? 0xFFFF : (ma_uint16)engineConfig.periodSizeInFrames; + + result = ma_node_graph_init(&nodeGraphConfig, &pEngine->allocationCallbacks, &pEngine->nodeGraph); + if (result != MA_SUCCESS) { + goto on_error_1; + } + + + /* We need at least one listener. */ + if (engineConfig.listenerCount == 0) { + engineConfig.listenerCount = 1; + } + + if (engineConfig.listenerCount > MA_ENGINE_MAX_LISTENERS) { + result = MA_INVALID_ARGS; /* Too many listeners. */ + goto on_error_1; + } + + for (iListener = 0; iListener < engineConfig.listenerCount; iListener += 1) { + listenerConfig = ma_spatializer_listener_config_init(ma_node_graph_get_channels(&pEngine->nodeGraph)); + + /* + If we're using a device, use the device's channel map for the listener. Otherwise just use + miniaudio's default channel map. + */ + #if !defined(MA_NO_DEVICE_IO) + { + if (pEngine->pDevice != NULL) { + /* + Temporarily disabled. There is a subtle bug here where front-left and front-right + will be used by the device's channel map, but this is not what we want to use for + spatialization. Instead we want to use side-left and side-right. I need to figure + out a better solution for this. For now, disabling the user of device channel maps. + */ + /*listenerConfig.pChannelMapOut = pEngine->pDevice->playback.channelMap;*/ + } + } + #endif + + result = ma_spatializer_listener_init(&listenerConfig, &pEngine->allocationCallbacks, &pEngine->listeners[iListener]); /* TODO: Change this to a pre-allocated heap. */ + if (result != MA_SUCCESS) { + goto on_error_2; + } + + pEngine->listenerCount += 1; + } + + + /* Gain smoothing for spatialized sounds. */ + pEngine->gainSmoothTimeInFrames = engineConfig.gainSmoothTimeInFrames; + if (pEngine->gainSmoothTimeInFrames == 0) { + ma_uint32 gainSmoothTimeInMilliseconds = engineConfig.gainSmoothTimeInMilliseconds; + if (gainSmoothTimeInMilliseconds == 0) { + gainSmoothTimeInMilliseconds = 8; + } + + pEngine->gainSmoothTimeInFrames = (gainSmoothTimeInMilliseconds * ma_engine_get_sample_rate(pEngine)) / 1000; /* 8ms by default. */ + } + + + /* We need a resource manager. */ + #ifndef MA_NO_RESOURCE_MANAGER + { + if (pEngine->pResourceManager == NULL) { + ma_resource_manager_config resourceManagerConfig; + + pEngine->pResourceManager = (ma_resource_manager*)ma_malloc(sizeof(*pEngine->pResourceManager), &pEngine->allocationCallbacks); + if (pEngine->pResourceManager == NULL) { + result = MA_OUT_OF_MEMORY; + goto on_error_2; + } + + resourceManagerConfig = ma_resource_manager_config_init(); + resourceManagerConfig.pLog = pEngine->pLog; /* Always use the engine's log for internally-managed resource managers. */ + resourceManagerConfig.decodedFormat = ma_format_f32; + resourceManagerConfig.decodedChannels = 0; /* Leave the decoded channel count as 0 so we can get good spatialization. */ + resourceManagerConfig.decodedSampleRate = ma_engine_get_sample_rate(pEngine); + ma_allocation_callbacks_init_copy(&resourceManagerConfig.allocationCallbacks, &pEngine->allocationCallbacks); + resourceManagerConfig.pVFS = engineConfig.pResourceManagerVFS; + + /* The Emscripten build cannot use threads. */ + #if defined(MA_EMSCRIPTEN) + { + resourceManagerConfig.jobThreadCount = 0; + resourceManagerConfig.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING; + } + #endif + + result = ma_resource_manager_init(&resourceManagerConfig, pEngine->pResourceManager); + if (result != MA_SUCCESS) { + goto on_error_3; + } + + pEngine->ownsResourceManager = MA_TRUE; + } + } + #endif + + /* Setup some stuff for inlined sounds. That is sounds played with ma_engine_play_sound(). */ + pEngine->inlinedSoundLock = 0; + pEngine->pInlinedSoundHead = NULL; + + /* Start the engine if required. This should always be the last step. */ + #if !defined(MA_NO_DEVICE_IO) + { + if (engineConfig.noAutoStart == MA_FALSE && pEngine->pDevice != NULL) { + result = ma_engine_start(pEngine); + if (result != MA_SUCCESS) { + goto on_error_4; /* Failed to start the engine. */ + } + } + } + #endif + + return MA_SUCCESS; + +#if !defined(MA_NO_DEVICE_IO) +on_error_4: +#endif +#if !defined(MA_NO_RESOURCE_MANAGER) +on_error_3: + if (pEngine->ownsResourceManager) { + ma_free(pEngine->pResourceManager, &pEngine->allocationCallbacks); + } +#endif /* MA_NO_RESOURCE_MANAGER */ +on_error_2: + for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) { + ma_spatializer_listener_uninit(&pEngine->listeners[iListener], &pEngine->allocationCallbacks); + } + + ma_node_graph_uninit(&pEngine->nodeGraph, &pEngine->allocationCallbacks); +on_error_1: + #if !defined(MA_NO_DEVICE_IO) + { + if (pEngine->ownsDevice) { + ma_device_uninit(pEngine->pDevice); + ma_free(pEngine->pDevice, &pEngine->allocationCallbacks); + } + } + #endif + + return result; +} + +MA_API void ma_engine_uninit(ma_engine* pEngine) +{ + ma_uint32 iListener; + + if (pEngine == NULL) { + return; + } + + /* The device must be uninitialized before the node graph to ensure the audio thread doesn't try accessing it. */ + #if !defined(MA_NO_DEVICE_IO) + { + if (pEngine->ownsDevice) { + ma_device_uninit(pEngine->pDevice); + ma_free(pEngine->pDevice, &pEngine->allocationCallbacks); + } else { + if (pEngine->pDevice != NULL) { + ma_device_stop(pEngine->pDevice); + } + } + } + #endif + + /* + All inlined sounds need to be deleted. I'm going to use a lock here just to future proof in case + I want to do some kind of garbage collection later on. + */ + ma_spinlock_lock(&pEngine->inlinedSoundLock); + { + for (;;) { + ma_sound_inlined* pSoundToDelete = pEngine->pInlinedSoundHead; + if (pSoundToDelete == NULL) { + break; /* Done. */ + } + + pEngine->pInlinedSoundHead = pSoundToDelete->pNext; + + ma_sound_uninit(&pSoundToDelete->sound); + ma_free(pSoundToDelete, &pEngine->allocationCallbacks); + } + } + ma_spinlock_unlock(&pEngine->inlinedSoundLock); + + for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) { + ma_spatializer_listener_uninit(&pEngine->listeners[iListener], &pEngine->allocationCallbacks); + } + + /* Make sure the node graph is uninitialized after the audio thread has been shutdown to prevent accessing of the node graph after being uninitialized. */ + ma_node_graph_uninit(&pEngine->nodeGraph, &pEngine->allocationCallbacks); + + /* Uninitialize the resource manager last to ensure we don't have a thread still trying to access it. */ +#ifndef MA_NO_RESOURCE_MANAGER + if (pEngine->ownsResourceManager) { + ma_resource_manager_uninit(pEngine->pResourceManager); + ma_free(pEngine->pResourceManager, &pEngine->allocationCallbacks); + } +#endif +} + +MA_API ma_result ma_engine_read_pcm_frames(ma_engine* pEngine, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + return ma_node_graph_read_pcm_frames(&pEngine->nodeGraph, pFramesOut, frameCount, pFramesRead); +} + +MA_API ma_node_graph* ma_engine_get_node_graph(ma_engine* pEngine) +{ + if (pEngine == NULL) { + return NULL; + } + + return &pEngine->nodeGraph; +} + +#if !defined(MA_NO_RESOURCE_MANAGER) +MA_API ma_resource_manager* ma_engine_get_resource_manager(ma_engine* pEngine) +{ + if (pEngine == NULL) { + return NULL; + } + + #if !defined(MA_NO_RESOURCE_MANAGER) + { + return pEngine->pResourceManager; + } + #else + { + return NULL; + } + #endif +} +#endif + +MA_API ma_device* ma_engine_get_device(ma_engine* pEngine) +{ + if (pEngine == NULL) { + return NULL; + } + + #if !defined(MA_NO_DEVICE_IO) + { + return pEngine->pDevice; + } + #else + { + return NULL; + } + #endif +} + +MA_API ma_log* ma_engine_get_log(ma_engine* pEngine) +{ + if (pEngine == NULL) { + return NULL; + } + + if (pEngine->pLog != NULL) { + return pEngine->pLog; + } else { + #if !defined(MA_NO_DEVICE_IO) + { + return ma_device_get_log(ma_engine_get_device(pEngine)); + } + #else + { + return NULL; + } + #endif + } +} + +MA_API ma_node* ma_engine_get_endpoint(ma_engine* pEngine) +{ + return ma_node_graph_get_endpoint(&pEngine->nodeGraph); +} + +MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine) +{ + return ma_node_graph_get_time(&pEngine->nodeGraph); +} + +MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime) +{ + return ma_node_graph_set_time(&pEngine->nodeGraph, globalTime); +} + +MA_API ma_uint32 ma_engine_get_channels(const ma_engine* pEngine) +{ + return ma_node_graph_get_channels(&pEngine->nodeGraph); +} + +MA_API ma_uint32 ma_engine_get_sample_rate(const ma_engine* pEngine) +{ + if (pEngine == NULL) { + return 0; + } + + return pEngine->sampleRate; +} + + +MA_API ma_result ma_engine_start(ma_engine* pEngine) +{ + ma_result result; + + if (pEngine == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_DEVICE_IO) + { + if (pEngine->pDevice != NULL) { + result = ma_device_start(pEngine->pDevice); + } else { + result = MA_INVALID_OPERATION; /* The engine is running without a device which means there's no real notion of "starting" the engine. */ + } + } + #else + { + result = MA_INVALID_OPERATION; /* Device IO is disabled, so there's no real notion of "starting" the engine. */ + } + #endif + + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_engine_stop(ma_engine* pEngine) +{ + ma_result result; + + if (pEngine == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_DEVICE_IO) + { + if (pEngine->pDevice != NULL) { + result = ma_device_stop(pEngine->pDevice); + } else { + result = MA_INVALID_OPERATION; /* The engine is running without a device which means there's no real notion of "stopping" the engine. */ + } + } + #else + { + result = MA_INVALID_OPERATION; /* Device IO is disabled, so there's no real notion of "stopping" the engine. */ + } + #endif + + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_engine_set_volume(ma_engine* pEngine, float volume) +{ + if (pEngine == NULL) { + return MA_INVALID_ARGS; + } + + return ma_node_set_output_bus_volume(ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0, volume); +} + +MA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB) +{ + if (pEngine == NULL) { + return MA_INVALID_ARGS; + } + + return ma_node_set_output_bus_volume(ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0, ma_volume_db_to_linear(gainDB)); +} + + +MA_API ma_uint32 ma_engine_get_listener_count(const ma_engine* pEngine) +{ + if (pEngine == NULL) { + return 0; + } + + return pEngine->listenerCount; +} + +MA_API ma_uint32 ma_engine_find_closest_listener(const ma_engine* pEngine, float absolutePosX, float absolutePosY, float absolutePosZ) +{ + ma_uint32 iListener; + ma_uint32 iListenerClosest; + float closestLen2 = MA_FLT_MAX; + + if (pEngine == NULL || pEngine->listenerCount == 1) { + return 0; + } + + iListenerClosest = 0; + for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) { + if (ma_engine_listener_is_enabled(pEngine, iListener)) { + float len2 = ma_vec3f_len2(ma_vec3f_sub(pEngine->listeners[iListener].position, ma_vec3f_init_3f(absolutePosX, absolutePosY, absolutePosZ))); + if (closestLen2 > len2) { + closestLen2 = len2; + iListenerClosest = iListener; + } + } + } + + MA_ASSERT(iListenerClosest < 255); + return iListenerClosest; +} + +MA_API void ma_engine_listener_set_position(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) +{ + if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { + return; + } + + ma_spatializer_listener_set_position(&pEngine->listeners[listenerIndex], x, y, z); +} + +MA_API ma_vec3f ma_engine_listener_get_position(const ma_engine* pEngine, ma_uint32 listenerIndex) +{ + if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { + return ma_vec3f_init_3f(0, 0, 0); + } + + return ma_spatializer_listener_get_position(&pEngine->listeners[listenerIndex]); +} + +MA_API void ma_engine_listener_set_direction(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) +{ + if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { + return; + } + + ma_spatializer_listener_set_direction(&pEngine->listeners[listenerIndex], x, y, z); +} + +MA_API ma_vec3f ma_engine_listener_get_direction(const ma_engine* pEngine, ma_uint32 listenerIndex) +{ + if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { + return ma_vec3f_init_3f(0, 0, -1); + } + + return ma_spatializer_listener_get_direction(&pEngine->listeners[listenerIndex]); +} + +MA_API void ma_engine_listener_set_velocity(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) +{ + if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { + return; + } + + ma_spatializer_listener_set_velocity(&pEngine->listeners[listenerIndex], x, y, z); +} + +MA_API ma_vec3f ma_engine_listener_get_velocity(const ma_engine* pEngine, ma_uint32 listenerIndex) +{ + if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { + return ma_vec3f_init_3f(0, 0, 0); + } + + return ma_spatializer_listener_get_velocity(&pEngine->listeners[listenerIndex]); +} + +MA_API void ma_engine_listener_set_cone(ma_engine* pEngine, ma_uint32 listenerIndex, float innerAngleInRadians, float outerAngleInRadians, float outerGain) +{ + if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { + return; + } + + ma_spatializer_listener_set_cone(&pEngine->listeners[listenerIndex], innerAngleInRadians, outerAngleInRadians, outerGain); +} + +MA_API void ma_engine_listener_get_cone(const ma_engine* pEngine, ma_uint32 listenerIndex, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) +{ + if (pInnerAngleInRadians != NULL) { + *pInnerAngleInRadians = 0; + } + + if (pOuterAngleInRadians != NULL) { + *pOuterAngleInRadians = 0; + } + + if (pOuterGain != NULL) { + *pOuterGain = 0; + } + + ma_spatializer_listener_get_cone(&pEngine->listeners[listenerIndex], pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain); +} + +MA_API void ma_engine_listener_set_world_up(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) +{ + if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { + return; + } + + ma_spatializer_listener_set_world_up(&pEngine->listeners[listenerIndex], x, y, z); +} + +MA_API ma_vec3f ma_engine_listener_get_world_up(const ma_engine* pEngine, ma_uint32 listenerIndex) +{ + if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { + return ma_vec3f_init_3f(0, 1, 0); + } + + return ma_spatializer_listener_get_world_up(&pEngine->listeners[listenerIndex]); +} + +MA_API void ma_engine_listener_set_enabled(ma_engine* pEngine, ma_uint32 listenerIndex, ma_bool32 isEnabled) +{ + if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { + return; + } + + ma_spatializer_listener_set_enabled(&pEngine->listeners[listenerIndex], isEnabled); +} + +MA_API ma_bool32 ma_engine_listener_is_enabled(const ma_engine* pEngine, ma_uint32 listenerIndex) +{ + if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { + return MA_FALSE; + } + + return ma_spatializer_listener_is_enabled(&pEngine->listeners[listenerIndex]); +} + + +#ifndef MA_NO_RESOURCE_MANAGER +MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePath, ma_node* pNode, ma_uint32 nodeInputBusIndex) +{ + ma_result result = MA_SUCCESS; + ma_sound_inlined* pSound = NULL; + ma_sound_inlined* pNextSound = NULL; + + if (pEngine == NULL || pFilePath == NULL) { + return MA_INVALID_ARGS; + } + + /* Attach to the endpoint node if nothing is specicied. */ + if (pNode == NULL) { + pNode = ma_node_graph_get_endpoint(&pEngine->nodeGraph); + nodeInputBusIndex = 0; + } + + /* + We want to check if we can recycle an already-allocated inlined sound. Since this is just a + helper I'm not *too* concerned about performance here and I'm happy to use a lock to keep + the implementation simple. Maybe this can be optimized later if there's enough demand, but + if this function is being used it probably means the caller doesn't really care too much. + + What we do is check the atEnd flag. When this is true, we can recycle the sound. Otherwise + we just keep iterating. If we reach the end without finding a sound to recycle we just + allocate a new one. This doesn't scale well for a massive number of sounds being played + simultaneously as we don't ever actually free the sound objects. Some kind of garbage + collection routine might be valuable for this which I'll think about. + */ + ma_spinlock_lock(&pEngine->inlinedSoundLock); + { + ma_uint32 soundFlags = 0; + + for (pNextSound = pEngine->pInlinedSoundHead; pNextSound != NULL; pNextSound = pNextSound->pNext) { + if (ma_sound_at_end(&pNextSound->sound)) { + /* + The sound is at the end which means it's available for recycling. All we need to do + is uninitialize it and reinitialize it. All we're doing is recycling memory. + */ + pSound = pNextSound; + c89atomic_fetch_sub_32(&pEngine->inlinedSoundCount, 1); + break; + } + } + + if (pSound != NULL) { + /* + We actually want to detach the sound from the list here. The reason is because we want the sound + to be in a consistent state at the non-recycled case to simplify the logic below. + */ + if (pEngine->pInlinedSoundHead == pSound) { + pEngine->pInlinedSoundHead = pSound->pNext; + } + + if (pSound->pPrev != NULL) { + pSound->pPrev->pNext = pSound->pNext; + } + if (pSound->pNext != NULL) { + pSound->pNext->pPrev = pSound->pPrev; + } + + /* Now the previous sound needs to be uninitialized. */ + ma_sound_uninit(&pNextSound->sound); + } else { + /* No sound available for recycling. Allocate one now. */ + pSound = (ma_sound_inlined*)ma_malloc(sizeof(*pSound), &pEngine->allocationCallbacks); + } + + if (pSound != NULL) { /* Safety check for the allocation above. */ + /* + At this point we should have memory allocated for the inlined sound. We just need + to initialize it like a normal sound now. + */ + soundFlags |= MA_SOUND_FLAG_ASYNC; /* For inlined sounds we don't want to be sitting around waiting for stuff to load so force an async load. */ + soundFlags |= MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT; /* We want specific control over where the sound is attached in the graph. We'll attach it manually just before playing the sound. */ + soundFlags |= MA_SOUND_FLAG_NO_PITCH; /* Pitching isn't usable with inlined sounds, so disable it to save on speed. */ + soundFlags |= MA_SOUND_FLAG_NO_SPATIALIZATION; /* Not currently doing spatialization with inlined sounds, but this might actually change later. For now disable spatialization. Will be removed if we ever add support for spatialization here. */ + + result = ma_sound_init_from_file(pEngine, pFilePath, soundFlags, NULL, NULL, &pSound->sound); + if (result == MA_SUCCESS) { + /* Now attach the sound to the graph. */ + result = ma_node_attach_output_bus(pSound, 0, pNode, nodeInputBusIndex); + if (result == MA_SUCCESS) { + /* At this point the sound should be loaded and we can go ahead and add it to the list. The new item becomes the new head. */ + pSound->pNext = pEngine->pInlinedSoundHead; + pSound->pPrev = NULL; + + pEngine->pInlinedSoundHead = pSound; /* <-- This is what attaches the sound to the list. */ + if (pSound->pNext != NULL) { + pSound->pNext->pPrev = pSound; + } + } else { + ma_free(pSound, &pEngine->allocationCallbacks); + } + } else { + ma_free(pSound, &pEngine->allocationCallbacks); + } + } else { + result = MA_OUT_OF_MEMORY; + } + } + ma_spinlock_unlock(&pEngine->inlinedSoundLock); + + if (result != MA_SUCCESS) { + return result; + } + + /* Finally we can start playing the sound. */ + result = ma_sound_start(&pSound->sound); + if (result != MA_SUCCESS) { + /* Failed to start the sound. We need to mark it for recycling and return an error. */ + c89atomic_exchange_32(&pSound->sound.atEnd, MA_TRUE); + return result; + } + + c89atomic_fetch_add_32(&pEngine->inlinedSoundCount, 1); + return result; +} + +MA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ma_sound_group* pGroup) +{ + return ma_engine_play_sound_ex(pEngine, pFilePath, pGroup, 0); +} +#endif + + +static ma_result ma_sound_preinit(ma_engine* pEngine, ma_sound* pSound) +{ + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pSound); + pSound->seekTarget = MA_SEEK_TARGET_NONE; + + if (pEngine == NULL) { + return MA_INVALID_ARGS; + } + + return MA_SUCCESS; +} + +static ma_result ma_sound_init_from_data_source_internal(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound) +{ + ma_result result; + ma_engine_node_config engineNodeConfig; + ma_engine_node_type type; /* Will be set to ma_engine_node_type_group if no data source is specified. */ + + /* Do not clear pSound to zero here - that's done at a higher level with ma_sound_preinit(). */ + MA_ASSERT(pEngine != NULL); + MA_ASSERT(pSound != NULL); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + pSound->pDataSource = pConfig->pDataSource; + + if (pConfig->pDataSource != NULL) { + type = ma_engine_node_type_sound; + } else { + type = ma_engine_node_type_group; + } + + /* + Sounds are engine nodes. Before we can initialize this we need to determine the channel count. + If we can't do this we need to abort. It's up to the caller to ensure they're using a data + source that provides this information upfront. + */ + engineNodeConfig = ma_engine_node_config_init(pEngine, type, pConfig->flags); + engineNodeConfig.channelsIn = pConfig->channelsIn; + engineNodeConfig.channelsOut = pConfig->channelsOut; + + /* If we're loading from a data source the input channel count needs to be the data source's native channel count. */ + if (pConfig->pDataSource != NULL) { + result = ma_data_source_get_data_format(pConfig->pDataSource, NULL, &engineNodeConfig.channelsIn, &engineNodeConfig.sampleRate, NULL, 0); + if (result != MA_SUCCESS) { + return result; /* Failed to retrieve the channel count. */ + } + + if (engineNodeConfig.channelsIn == 0) { + return MA_INVALID_OPERATION; /* Invalid channel count. */ + } + + if (engineNodeConfig.channelsOut == MA_SOUND_SOURCE_CHANNEL_COUNT) { + engineNodeConfig.channelsOut = engineNodeConfig.channelsIn; + } + } + + + /* Getting here means we should have a valid channel count and we can initialize the engine node. */ + result = ma_engine_node_init(&engineNodeConfig, &pEngine->allocationCallbacks, &pSound->engineNode); + if (result != MA_SUCCESS) { + return result; + } + + /* If no attachment is specified, attach the sound straight to the endpoint. */ + if (pConfig->pInitialAttachment == NULL) { + /* No group. Attach straight to the endpoint by default, unless the caller has requested that do not. */ + if ((pConfig->flags & MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT) == 0) { + result = ma_node_attach_output_bus(pSound, 0, ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0); + } + } else { + /* An attachment is specified. Attach to it by default. The sound has only a single output bus, and the config will specify which input bus to attach to. */ + result = ma_node_attach_output_bus(pSound, 0, pConfig->pInitialAttachment, pConfig->initialAttachmentInputBusIndex); + } + + if (result != MA_SUCCESS) { + ma_engine_node_uninit(&pSound->engineNode, &pEngine->allocationCallbacks); + return result; + } + + + /* Apply initial range and looping state to the data source if applicable. */ + if (pConfig->rangeBegInPCMFrames != 0 || pConfig->rangeEndInPCMFrames != ~((ma_uint64)0)) { + ma_data_source_set_range_in_pcm_frames(ma_sound_get_data_source(pSound), pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames); + } + + if (pConfig->loopPointBegInPCMFrames != 0 || pConfig->loopPointEndInPCMFrames != ~((ma_uint64)0)) { + ma_data_source_set_range_in_pcm_frames(ma_sound_get_data_source(pSound), pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); + } + + ma_sound_set_looping(pSound, pConfig->isLooping); + + return MA_SUCCESS; +} + +#ifndef MA_NO_RESOURCE_MANAGER +MA_API ma_result ma_sound_init_from_file_internal(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound) +{ + ma_result result = MA_SUCCESS; + ma_uint32 flags; + ma_sound_config config; + ma_resource_manager_pipeline_notifications notifications; + + /* + The engine requires knowledge of the channel count of the underlying data source before it can + initialize the sound. Therefore, we need to make the resource manager wait until initialization + of the underlying data source to be initialized so we can get access to the channel count. To + do this, the MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT is forced. + + Because we're initializing the data source before the sound, there's a chance the notification + will get triggered before this function returns. This is OK, so long as the caller is aware of + it and can avoid accessing the sound from within the notification. + */ + flags = pConfig->flags | MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT; + + pSound->pResourceManagerDataSource = (ma_resource_manager_data_source*)ma_malloc(sizeof(*pSound->pResourceManagerDataSource), &pEngine->allocationCallbacks); + if (pSound->pResourceManagerDataSource == NULL) { + return MA_OUT_OF_MEMORY; + } + + notifications = ma_resource_manager_pipeline_notifications_init(); + notifications.done.pFence = pConfig->pDoneFence; + + /* + We must wrap everything around the fence if one was specified. This ensures ma_fence_wait() does + not return prematurely before the sound has finished initializing. + */ + if (notifications.done.pFence) { ma_fence_acquire(notifications.done.pFence); } + { + ma_resource_manager_data_source_config resourceManagerDataSourceConfig = ma_resource_manager_data_source_config_init(); + resourceManagerDataSourceConfig.pFilePath = pConfig->pFilePath; + resourceManagerDataSourceConfig.pFilePathW = pConfig->pFilePathW; + resourceManagerDataSourceConfig.flags = flags; + resourceManagerDataSourceConfig.pNotifications = ¬ifications; + resourceManagerDataSourceConfig.initialSeekPointInPCMFrames = pConfig->initialSeekPointInPCMFrames; + resourceManagerDataSourceConfig.rangeBegInPCMFrames = pConfig->rangeBegInPCMFrames; + resourceManagerDataSourceConfig.rangeEndInPCMFrames = pConfig->rangeEndInPCMFrames; + resourceManagerDataSourceConfig.loopPointBegInPCMFrames = pConfig->loopPointBegInPCMFrames; + resourceManagerDataSourceConfig.loopPointEndInPCMFrames = pConfig->loopPointEndInPCMFrames; + resourceManagerDataSourceConfig.isLooping = pConfig->isLooping; + + result = ma_resource_manager_data_source_init_ex(pEngine->pResourceManager, &resourceManagerDataSourceConfig, pSound->pResourceManagerDataSource); + if (result != MA_SUCCESS) { + goto done; + } + + pSound->ownsDataSource = MA_TRUE; /* <-- Important. Not setting this will result in the resource manager data source never getting uninitialized. */ + + /* We need to use a slightly customized version of the config so we'll need to make a copy. */ + config = *pConfig; + config.pFilePath = NULL; + config.pFilePathW = NULL; + config.pDataSource = pSound->pResourceManagerDataSource; + + result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound); + if (result != MA_SUCCESS) { + ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource); + ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks); + MA_ZERO_OBJECT(pSound); + goto done; + } + } +done: + if (notifications.done.pFence) { ma_fence_release(notifications.done.pFence); } + return result; +} + +MA_API ma_result ma_sound_init_from_file(ma_engine* pEngine, const char* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound) +{ + ma_sound_config config = ma_sound_config_init(); + config.pFilePath = pFilePath; + config.flags = flags; + config.pInitialAttachment = pGroup; + config.pDoneFence = pDoneFence; + return ma_sound_init_ex(pEngine, &config, pSound); +} + +MA_API ma_result ma_sound_init_from_file_w(ma_engine* pEngine, const wchar_t* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound) +{ + ma_sound_config config = ma_sound_config_init(); + config.pFilePathW = pFilePath; + config.flags = flags; + config.pInitialAttachment = pGroup; + config.pDoneFence = pDoneFence; + return ma_sound_init_ex(pEngine, &config, pSound); +} + +MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistingSound, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound) +{ + ma_result result; + ma_sound_config config; + + result = ma_sound_preinit(pEngine, pSound); + if (result != MA_SUCCESS) { + return result; + } + + if (pExistingSound == NULL) { + return MA_INVALID_ARGS; + } + + /* Cloning only works for data buffers (not streams) that are loaded from the resource manager. */ + if (pExistingSound->pResourceManagerDataSource == NULL) { + return MA_INVALID_OPERATION; + } + + /* + We need to make a clone of the data source. If the data source is not a data buffer (i.e. a stream) + the this will fail. + */ + pSound->pResourceManagerDataSource = (ma_resource_manager_data_source*)ma_malloc(sizeof(*pSound->pResourceManagerDataSource), &pEngine->allocationCallbacks); + if (pSound->pResourceManagerDataSource == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_resource_manager_data_source_init_copy(pEngine->pResourceManager, pExistingSound->pResourceManagerDataSource, pSound->pResourceManagerDataSource); + if (result != MA_SUCCESS) { + ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks); + return result; + } + + config = ma_sound_config_init(); + config.pDataSource = pSound->pResourceManagerDataSource; + config.flags = flags; + config.pInitialAttachment = pGroup; + + result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound); + if (result != MA_SUCCESS) { + ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource); + ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks); + MA_ZERO_OBJECT(pSound); + return result; + } + + return MA_SUCCESS; +} +#endif + +MA_API ma_result ma_sound_init_from_data_source(ma_engine* pEngine, ma_data_source* pDataSource, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound) +{ + ma_sound_config config = ma_sound_config_init(); + config.pDataSource = pDataSource; + config.flags = flags; + config.pInitialAttachment = pGroup; + return ma_sound_init_ex(pEngine, &config, pSound); +} + +MA_API ma_result ma_sound_init_ex(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound) +{ + ma_result result; + + result = ma_sound_preinit(pEngine, pSound); + if (result != MA_SUCCESS) { + return result; + } + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + /* We need to load the sound differently depending on whether or not we're loading from a file. */ +#ifndef MA_NO_RESOURCE_MANAGER + if (pConfig->pFilePath != NULL || pConfig->pFilePathW != NULL) { + return ma_sound_init_from_file_internal(pEngine, pConfig, pSound); + } else +#endif + { + /* + Getting here means we're not loading from a file. We may be loading from an already-initialized + data source, or none at all. If we aren't specifying any data source, we'll be initializing the + the equivalent to a group. ma_data_source_init_from_data_source_internal() will deal with this + for us, so no special treatment required here. + */ + return ma_sound_init_from_data_source_internal(pEngine, pConfig, pSound); + } +} + +MA_API void ma_sound_uninit(ma_sound* pSound) +{ + if (pSound == NULL) { + return; + } + + /* + Always uninitialize the node first. This ensures it's detached from the graph and does not return until it has done + so which makes thread safety beyond this point trivial. + */ + ma_engine_node_uninit(&pSound->engineNode, &pSound->engineNode.pEngine->allocationCallbacks); + + /* Once the sound is detached from the group we can guarantee that it won't be referenced by the mixer thread which means it's safe for us to destroy the data source. */ +#ifndef MA_NO_RESOURCE_MANAGER + if (pSound->ownsDataSource) { + ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource); + ma_free(pSound->pResourceManagerDataSource, &pSound->engineNode.pEngine->allocationCallbacks); + pSound->pDataSource = NULL; + } +#else + MA_ASSERT(pSound->ownsDataSource == MA_FALSE); +#endif +} + +MA_API ma_engine* ma_sound_get_engine(const ma_sound* pSound) +{ + if (pSound == NULL) { + return NULL; + } + + return pSound->engineNode.pEngine; +} + +MA_API ma_data_source* ma_sound_get_data_source(const ma_sound* pSound) +{ + if (pSound == NULL) { + return NULL; + } + + return pSound->pDataSource; +} + +MA_API ma_result ma_sound_start(ma_sound* pSound) +{ + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + /* If the sound is already playing, do nothing. */ + if (ma_sound_is_playing(pSound)) { + return MA_SUCCESS; + } + + /* If the sound is at the end it means we want to start from the start again. */ + if (ma_sound_at_end(pSound)) { + ma_result result = ma_data_source_seek_to_pcm_frame(pSound->pDataSource, 0); + if (result != MA_SUCCESS && result != MA_NOT_IMPLEMENTED) { + return result; /* Failed to seek back to the start. */ + } + + /* Make sure we clear the end indicator. */ + c89atomic_exchange_32(&pSound->atEnd, MA_FALSE); + } + + /* Make sure the sound is started. If there's a start delay, the sound won't actually start until the start time is reached. */ + ma_node_set_state(pSound, ma_node_state_started); + + return MA_SUCCESS; +} + +MA_API ma_result ma_sound_stop(ma_sound* pSound) +{ + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + /* This will stop the sound immediately. Use ma_sound_set_stop_time() to stop the sound at a specific time. */ + ma_node_set_state(pSound, ma_node_state_stopped); + + return MA_SUCCESS; +} + +MA_API void ma_sound_set_volume(ma_sound* pSound, float volume) +{ + if (pSound == NULL) { + return; + } + + /* The volume is controlled via the output bus. */ + ma_node_set_output_bus_volume(pSound, 0, volume); +} + +MA_API float ma_sound_get_volume(const ma_sound* pSound) +{ + if (pSound == NULL) { + return 0; + } + + return ma_node_get_output_bus_volume(pSound, 0); +} + +MA_API void ma_sound_set_pan(ma_sound* pSound, float pan) +{ + if (pSound == NULL) { + return; + } + + ma_panner_set_pan(&pSound->engineNode.panner, pan); +} + +MA_API float ma_sound_get_pan(const ma_sound* pSound) +{ + if (pSound == NULL) { + return 0; + } + + return ma_panner_get_pan(&pSound->engineNode.panner); +} + +MA_API void ma_sound_set_pan_mode(ma_sound* pSound, ma_pan_mode panMode) +{ + if (pSound == NULL) { + return; + } + + ma_panner_set_mode(&pSound->engineNode.panner, panMode); +} + +MA_API ma_pan_mode ma_sound_get_pan_mode(const ma_sound* pSound) +{ + if (pSound == NULL) { + return ma_pan_mode_balance; + } + + return ma_panner_get_mode(&pSound->engineNode.panner); +} + +MA_API void ma_sound_set_pitch(ma_sound* pSound, float pitch) +{ + if (pSound == NULL) { + return; + } + + if (pitch <= 0) { + return; + } + + c89atomic_exchange_explicit_f32(&pSound->engineNode.pitch, pitch, c89atomic_memory_order_release); +} + +MA_API float ma_sound_get_pitch(const ma_sound* pSound) +{ + if (pSound == NULL) { + return 0; + } + + return c89atomic_load_f32(&pSound->engineNode.pitch); /* Naughty const-cast for this. */ +} + +MA_API void ma_sound_set_spatialization_enabled(ma_sound* pSound, ma_bool32 enabled) +{ + if (pSound == NULL) { + return; + } + + c89atomic_exchange_explicit_32(&pSound->engineNode.isSpatializationDisabled, !enabled, c89atomic_memory_order_release); +} + +MA_API ma_bool32 ma_sound_is_spatialization_enabled(const ma_sound* pSound) +{ + if (pSound == NULL) { + return MA_FALSE; + } + + return ma_engine_node_is_spatialization_enabled(&pSound->engineNode); +} + +MA_API void ma_sound_set_pinned_listener_index(ma_sound* pSound, ma_uint32 listenerIndex) +{ + if (pSound == NULL || listenerIndex >= ma_engine_get_listener_count(ma_sound_get_engine(pSound))) { + return; + } + + c89atomic_exchange_explicit_32(&pSound->engineNode.pinnedListenerIndex, listenerIndex, c89atomic_memory_order_release); +} + +MA_API ma_uint32 ma_sound_get_pinned_listener_index(const ma_sound* pSound) +{ + if (pSound == NULL) { + return MA_LISTENER_INDEX_CLOSEST; + } + + return c89atomic_load_explicit_32(&pSound->engineNode.pinnedListenerIndex, c89atomic_memory_order_acquire); +} + +MA_API ma_uint32 ma_sound_get_listener_index(const ma_sound* pSound) +{ + ma_uint32 listenerIndex; + + if (pSound == NULL) { + return 0; + } + + listenerIndex = ma_sound_get_pinned_listener_index(pSound); + if (listenerIndex == MA_LISTENER_INDEX_CLOSEST) { + ma_vec3f position = ma_sound_get_position(pSound); + return ma_engine_find_closest_listener(ma_sound_get_engine(pSound), position.x, position.y, position.z); + } + + return listenerIndex; +} + +MA_API ma_vec3f ma_sound_get_direction_to_listener(const ma_sound* pSound) +{ + ma_vec3f relativePos; + ma_engine* pEngine; + + if (pSound == NULL) { + return ma_vec3f_init_3f(0, 0, -1); + } + + pEngine = ma_sound_get_engine(pSound); + if (pEngine == NULL) { + return ma_vec3f_init_3f(0, 0, -1); + } + + ma_spatializer_get_relative_position_and_direction(&pSound->engineNode.spatializer, &pEngine->listeners[ma_sound_get_listener_index(pSound)], &relativePos, NULL); + + return ma_vec3f_normalize(ma_vec3f_neg(relativePos)); +} + +MA_API void ma_sound_set_position(ma_sound* pSound, float x, float y, float z) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_position(&pSound->engineNode.spatializer, x, y, z); +} + +MA_API ma_vec3f ma_sound_get_position(const ma_sound* pSound) +{ + if (pSound == NULL) { + return ma_vec3f_init_3f(0, 0, 0); + } + + return ma_spatializer_get_position(&pSound->engineNode.spatializer); +} + +MA_API void ma_sound_set_direction(ma_sound* pSound, float x, float y, float z) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_direction(&pSound->engineNode.spatializer, x, y, z); +} + +MA_API ma_vec3f ma_sound_get_direction(const ma_sound* pSound) +{ + if (pSound == NULL) { + return ma_vec3f_init_3f(0, 0, 0); + } + + return ma_spatializer_get_direction(&pSound->engineNode.spatializer); +} + +MA_API void ma_sound_set_velocity(ma_sound* pSound, float x, float y, float z) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_velocity(&pSound->engineNode.spatializer, x, y, z); +} + +MA_API ma_vec3f ma_sound_get_velocity(const ma_sound* pSound) +{ + if (pSound == NULL) { + return ma_vec3f_init_3f(0, 0, 0); + } + + return ma_spatializer_get_velocity(&pSound->engineNode.spatializer); +} + +MA_API void ma_sound_set_attenuation_model(ma_sound* pSound, ma_attenuation_model attenuationModel) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_attenuation_model(&pSound->engineNode.spatializer, attenuationModel); +} + +MA_API ma_attenuation_model ma_sound_get_attenuation_model(const ma_sound* pSound) +{ + if (pSound == NULL) { + return ma_attenuation_model_none; + } + + return ma_spatializer_get_attenuation_model(&pSound->engineNode.spatializer); +} + +MA_API void ma_sound_set_positioning(ma_sound* pSound, ma_positioning positioning) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_positioning(&pSound->engineNode.spatializer, positioning); +} + +MA_API ma_positioning ma_sound_get_positioning(const ma_sound* pSound) +{ + if (pSound == NULL) { + return ma_positioning_absolute; + } + + return ma_spatializer_get_positioning(&pSound->engineNode.spatializer); +} + +MA_API void ma_sound_set_rolloff(ma_sound* pSound, float rolloff) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_rolloff(&pSound->engineNode.spatializer, rolloff); +} + +MA_API float ma_sound_get_rolloff(const ma_sound* pSound) +{ + if (pSound == NULL) { + return 0; + } + + return ma_spatializer_get_rolloff(&pSound->engineNode.spatializer); +} + +MA_API void ma_sound_set_min_gain(ma_sound* pSound, float minGain) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_min_gain(&pSound->engineNode.spatializer, minGain); +} + +MA_API float ma_sound_get_min_gain(const ma_sound* pSound) +{ + if (pSound == NULL) { + return 0; + } + + return ma_spatializer_get_min_gain(&pSound->engineNode.spatializer); +} + +MA_API void ma_sound_set_max_gain(ma_sound* pSound, float maxGain) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_max_gain(&pSound->engineNode.spatializer, maxGain); +} + +MA_API float ma_sound_get_max_gain(const ma_sound* pSound) +{ + if (pSound == NULL) { + return 0; + } + + return ma_spatializer_get_max_gain(&pSound->engineNode.spatializer); +} + +MA_API void ma_sound_set_min_distance(ma_sound* pSound, float minDistance) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_min_distance(&pSound->engineNode.spatializer, minDistance); +} + +MA_API float ma_sound_get_min_distance(const ma_sound* pSound) +{ + if (pSound == NULL) { + return 0; + } + + return ma_spatializer_get_min_distance(&pSound->engineNode.spatializer); +} + +MA_API void ma_sound_set_max_distance(ma_sound* pSound, float maxDistance) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_max_distance(&pSound->engineNode.spatializer, maxDistance); +} + +MA_API float ma_sound_get_max_distance(const ma_sound* pSound) +{ + if (pSound == NULL) { + return 0; + } + + return ma_spatializer_get_max_distance(&pSound->engineNode.spatializer); +} + +MA_API void ma_sound_set_cone(ma_sound* pSound, float innerAngleInRadians, float outerAngleInRadians, float outerGain) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_cone(&pSound->engineNode.spatializer, innerAngleInRadians, outerAngleInRadians, outerGain); +} + +MA_API void ma_sound_get_cone(const ma_sound* pSound, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) +{ + if (pInnerAngleInRadians != NULL) { + *pInnerAngleInRadians = 0; + } + + if (pOuterAngleInRadians != NULL) { + *pOuterAngleInRadians = 0; + } + + if (pOuterGain != NULL) { + *pOuterGain = 0; + } + + ma_spatializer_get_cone(&pSound->engineNode.spatializer, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain); +} + +MA_API void ma_sound_set_doppler_factor(ma_sound* pSound, float dopplerFactor) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_doppler_factor(&pSound->engineNode.spatializer, dopplerFactor); +} + +MA_API float ma_sound_get_doppler_factor(const ma_sound* pSound) +{ + if (pSound == NULL) { + return 0; + } + + return ma_spatializer_get_doppler_factor(&pSound->engineNode.spatializer); +} + +MA_API void ma_sound_set_directional_attenuation_factor(ma_sound* pSound, float directionalAttenuationFactor) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_directional_attenuation_factor(&pSound->engineNode.spatializer, directionalAttenuationFactor); +} + +MA_API float ma_sound_get_directional_attenuation_factor(const ma_sound* pSound) +{ + if (pSound == NULL) { + return 1; + } + + return ma_spatializer_get_directional_attenuation_factor(&pSound->engineNode.spatializer); +} + + +MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames) +{ + if (pSound == NULL) { + return; + } + + ma_fader_set_fade(&pSound->engineNode.fader, volumeBeg, volumeEnd, fadeLengthInFrames); +} + +MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds) +{ + if (pSound == NULL) { + return; + } + + ma_sound_set_fade_in_pcm_frames(pSound, volumeBeg, volumeEnd, (fadeLengthInMilliseconds * pSound->engineNode.fader.config.sampleRate) / 1000); +} + +MA_API float ma_sound_get_current_fade_volume(ma_sound* pSound) +{ + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + return ma_fader_get_current_volume(&pSound->engineNode.fader); +} + +MA_API void ma_sound_set_start_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames) +{ + if (pSound == NULL) { + return; + } + + ma_node_set_state_time(pSound, ma_node_state_started, absoluteGlobalTimeInFrames); +} + +MA_API void ma_sound_set_start_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds) +{ + if (pSound == NULL) { + return; + } + + ma_sound_set_start_time_in_pcm_frames(pSound, absoluteGlobalTimeInMilliseconds * ma_engine_get_sample_rate(ma_sound_get_engine(pSound)) / 1000); +} + +MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames) +{ + if (pSound == NULL) { + return; + } + + ma_node_set_state_time(pSound, ma_node_state_stopped, absoluteGlobalTimeInFrames); +} + +MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds) +{ + if (pSound == NULL) { + return; + } + + ma_sound_set_stop_time_in_pcm_frames(pSound, absoluteGlobalTimeInMilliseconds * ma_engine_get_sample_rate(ma_sound_get_engine(pSound)) / 1000); +} + +MA_API ma_bool32 ma_sound_is_playing(const ma_sound* pSound) +{ + if (pSound == NULL) { + return MA_FALSE; + } + + return ma_node_get_state_by_time(pSound, ma_engine_get_time(ma_sound_get_engine(pSound))) == ma_node_state_started; +} + +MA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound* pSound) +{ + if (pSound == NULL) { + return 0; + } + + return ma_node_get_time(pSound); +} + +MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping) +{ + if (pSound == NULL) { + return; + } + + /* Looping is only a valid concept if the sound is backed by a data source. */ + if (pSound->pDataSource == NULL) { + return; + } + + /* The looping state needs to be applied to the data source in order for any looping to actually happen. */ + ma_data_source_set_looping(pSound->pDataSource, isLooping); +} + +MA_API ma_bool32 ma_sound_is_looping(const ma_sound* pSound) +{ + if (pSound == NULL) { + return MA_FALSE; + } + + /* There is no notion of looping for sounds that are not backed by a data source. */ + if (pSound->pDataSource == NULL) { + return MA_FALSE; + } + + return ma_data_source_is_looping(pSound->pDataSource); +} + +MA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound) +{ + if (pSound == NULL) { + return MA_FALSE; + } + + /* There is no notion of an end of a sound if it's not backed by a data source. */ + if (pSound->pDataSource == NULL) { + return MA_FALSE; + } + + return c89atomic_load_32(&pSound->atEnd); +} + +MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameIndex) +{ + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + /* Seeking is only valid for sounds that are backed by a data source. */ + if (pSound->pDataSource == NULL) { + return MA_INVALID_OPERATION; + } + + /* We can't be seeking while reading at the same time. We just set the seek target and get the mixing thread to do the actual seek. */ + c89atomic_exchange_64(&pSound->seekTarget, frameIndex); + + return MA_SUCCESS; +} + +MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + /* The data format is retrieved directly from the data source if the sound is backed by one. Otherwise we pull it from the node. */ + if (pSound->pDataSource == NULL) { + ma_uint32 channels; + + if (pFormat != NULL) { + *pFormat = ma_format_f32; + } + + channels = ma_node_get_input_channels(&pSound->engineNode, 0); + if (pChannels != NULL) { + *pChannels = channels; + } + + if (pSampleRate != NULL) { + *pSampleRate = pSound->engineNode.resampler.config.sampleRateIn; + } + + if (pChannelMap != NULL) { + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channels); + } + + return MA_SUCCESS; + } else { + return ma_data_source_get_data_format(pSound->pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); + } +} + +MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor) +{ + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + /* The notion of a cursor is only valid for sounds that are backed by a data source. */ + if (pSound->pDataSource == NULL) { + return MA_INVALID_OPERATION; + } + + return ma_data_source_get_cursor_in_pcm_frames(pSound->pDataSource, pCursor); +} + +MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength) +{ + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + /* The notion of a sound length is only valid for sounds that are backed by a data source. */ + if (pSound->pDataSource == NULL) { + return MA_INVALID_OPERATION; + } + + return ma_data_source_get_length_in_pcm_frames(pSound->pDataSource, pLength); +} + +MA_API ma_result ma_sound_get_cursor_in_seconds(ma_sound* pSound, float* pCursor) +{ + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + /* The notion of a cursor is only valid for sounds that are backed by a data source. */ + if (pSound->pDataSource == NULL) { + return MA_INVALID_OPERATION; + } + + return ma_data_source_get_cursor_in_seconds(pSound->pDataSource, pCursor); +} + +MA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength) +{ + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + /* The notion of a sound length is only valid for sounds that are backed by a data source. */ + if (pSound->pDataSource == NULL) { + return MA_INVALID_OPERATION; + } + + return ma_data_source_get_length_in_seconds(pSound->pDataSource, pLength); +} + + +MA_API ma_result ma_sound_group_init(ma_engine* pEngine, ma_uint32 flags, ma_sound_group* pParentGroup, ma_sound_group* pGroup) +{ + ma_sound_group_config config = ma_sound_group_config_init(); + config.flags = flags; + config.pInitialAttachment = pParentGroup; + return ma_sound_group_init_ex(pEngine, &config, pGroup); +} + +MA_API ma_result ma_sound_group_init_ex(ma_engine* pEngine, const ma_sound_group_config* pConfig, ma_sound_group* pGroup) +{ + ma_sound_config soundConfig; + + if (pGroup == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pGroup); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + /* A sound group is just a sound without a data source. */ + soundConfig = *pConfig; + soundConfig.pFilePath = NULL; + soundConfig.pFilePathW = NULL; + soundConfig.pDataSource = NULL; + + /* + Groups need to have spatialization disabled by default because I think it'll be pretty rare + that programs will want to spatialize groups (but not unheard of). Certainly it feels like + disabling this by default feels like the right option. Spatialization can be enabled with a + call to ma_sound_group_set_spatialization_enabled(). + */ + soundConfig.flags |= MA_SOUND_FLAG_NO_SPATIALIZATION; + + return ma_sound_init_ex(pEngine, &soundConfig, pGroup); +} + +MA_API void ma_sound_group_uninit(ma_sound_group* pGroup) +{ + ma_sound_uninit(pGroup); +} + +MA_API ma_engine* ma_sound_group_get_engine(const ma_sound_group* pGroup) +{ + return ma_sound_get_engine(pGroup); +} + +MA_API ma_result ma_sound_group_start(ma_sound_group* pGroup) +{ + return ma_sound_start(pGroup); +} + +MA_API ma_result ma_sound_group_stop(ma_sound_group* pGroup) +{ + return ma_sound_stop(pGroup); +} + +MA_API void ma_sound_group_set_volume(ma_sound_group* pGroup, float volume) +{ + ma_sound_set_volume(pGroup, volume); +} + +MA_API float ma_sound_group_get_volume(const ma_sound_group* pGroup) +{ + return ma_sound_get_volume(pGroup); +} + +MA_API void ma_sound_group_set_pan(ma_sound_group* pGroup, float pan) +{ + ma_sound_set_pan(pGroup, pan); +} + +MA_API float ma_sound_group_get_pan(const ma_sound_group* pGroup) +{ + return ma_sound_get_pan(pGroup); +} + +MA_API void ma_sound_group_set_pan_mode(ma_sound_group* pGroup, ma_pan_mode panMode) +{ + ma_sound_set_pan_mode(pGroup, panMode); +} + +MA_API ma_pan_mode ma_sound_group_get_pan_mode(const ma_sound_group* pGroup) +{ + return ma_sound_get_pan_mode(pGroup); +} + +MA_API void ma_sound_group_set_pitch(ma_sound_group* pGroup, float pitch) +{ + ma_sound_set_pitch(pGroup, pitch); +} + +MA_API float ma_sound_group_get_pitch(const ma_sound_group* pGroup) +{ + return ma_sound_get_pitch(pGroup); +} + +MA_API void ma_sound_group_set_spatialization_enabled(ma_sound_group* pGroup, ma_bool32 enabled) +{ + ma_sound_set_spatialization_enabled(pGroup, enabled); +} + +MA_API ma_bool32 ma_sound_group_is_spatialization_enabled(const ma_sound_group* pGroup) +{ + return ma_sound_is_spatialization_enabled(pGroup); +} + +MA_API void ma_sound_group_set_pinned_listener_index(ma_sound_group* pGroup, ma_uint32 listenerIndex) +{ + ma_sound_set_pinned_listener_index(pGroup, listenerIndex); +} + +MA_API ma_uint32 ma_sound_group_get_pinned_listener_index(const ma_sound_group* pGroup) +{ + return ma_sound_get_pinned_listener_index(pGroup); +} + +MA_API ma_uint32 ma_sound_group_get_listener_index(const ma_sound_group* pGroup) +{ + return ma_sound_get_listener_index(pGroup); +} + +MA_API ma_vec3f ma_sound_group_get_direction_to_listener(const ma_sound_group* pGroup) +{ + return ma_sound_get_direction_to_listener(pGroup); +} + +MA_API void ma_sound_group_set_position(ma_sound_group* pGroup, float x, float y, float z) +{ + ma_sound_set_position(pGroup, x, y, z); +} + +MA_API ma_vec3f ma_sound_group_get_position(const ma_sound_group* pGroup) +{ + return ma_sound_get_position(pGroup); +} + +MA_API void ma_sound_group_set_direction(ma_sound_group* pGroup, float x, float y, float z) +{ + ma_sound_set_direction(pGroup, x, y, z); +} + +MA_API ma_vec3f ma_sound_group_get_direction(const ma_sound_group* pGroup) +{ + return ma_sound_get_direction(pGroup); +} + +MA_API void ma_sound_group_set_velocity(ma_sound_group* pGroup, float x, float y, float z) +{ + ma_sound_set_velocity(pGroup, x, y, z); +} + +MA_API ma_vec3f ma_sound_group_get_velocity(const ma_sound_group* pGroup) +{ + return ma_sound_get_velocity(pGroup); +} + +MA_API void ma_sound_group_set_attenuation_model(ma_sound_group* pGroup, ma_attenuation_model attenuationModel) +{ + ma_sound_set_attenuation_model(pGroup, attenuationModel); +} + +MA_API ma_attenuation_model ma_sound_group_get_attenuation_model(const ma_sound_group* pGroup) +{ + return ma_sound_get_attenuation_model(pGroup); +} + +MA_API void ma_sound_group_set_positioning(ma_sound_group* pGroup, ma_positioning positioning) +{ + ma_sound_set_positioning(pGroup, positioning); +} + +MA_API ma_positioning ma_sound_group_get_positioning(const ma_sound_group* pGroup) +{ + return ma_sound_get_positioning(pGroup); +} + +MA_API void ma_sound_group_set_rolloff(ma_sound_group* pGroup, float rolloff) +{ + ma_sound_set_rolloff(pGroup, rolloff); +} + +MA_API float ma_sound_group_get_rolloff(const ma_sound_group* pGroup) +{ + return ma_sound_get_rolloff(pGroup); +} + +MA_API void ma_sound_group_set_min_gain(ma_sound_group* pGroup, float minGain) +{ + ma_sound_set_min_gain(pGroup, minGain); +} + +MA_API float ma_sound_group_get_min_gain(const ma_sound_group* pGroup) +{ + return ma_sound_get_min_gain(pGroup); +} + +MA_API void ma_sound_group_set_max_gain(ma_sound_group* pGroup, float maxGain) +{ + ma_sound_set_max_gain(pGroup, maxGain); +} + +MA_API float ma_sound_group_get_max_gain(const ma_sound_group* pGroup) +{ + return ma_sound_get_max_gain(pGroup); +} + +MA_API void ma_sound_group_set_min_distance(ma_sound_group* pGroup, float minDistance) +{ + ma_sound_set_min_distance(pGroup, minDistance); +} + +MA_API float ma_sound_group_get_min_distance(const ma_sound_group* pGroup) +{ + return ma_sound_get_min_distance(pGroup); +} + +MA_API void ma_sound_group_set_max_distance(ma_sound_group* pGroup, float maxDistance) +{ + ma_sound_set_max_distance(pGroup, maxDistance); +} + +MA_API float ma_sound_group_get_max_distance(const ma_sound_group* pGroup) +{ + return ma_sound_get_max_distance(pGroup); +} + +MA_API void ma_sound_group_set_cone(ma_sound_group* pGroup, float innerAngleInRadians, float outerAngleInRadians, float outerGain) +{ + ma_sound_set_cone(pGroup, innerAngleInRadians, outerAngleInRadians, outerGain); +} + +MA_API void ma_sound_group_get_cone(const ma_sound_group* pGroup, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) +{ + ma_sound_get_cone(pGroup, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain); +} + +MA_API void ma_sound_group_set_doppler_factor(ma_sound_group* pGroup, float dopplerFactor) +{ + ma_sound_set_doppler_factor(pGroup, dopplerFactor); +} + +MA_API float ma_sound_group_get_doppler_factor(const ma_sound_group* pGroup) +{ + return ma_sound_get_doppler_factor(pGroup); +} + +MA_API void ma_sound_group_set_directional_attenuation_factor(ma_sound_group* pGroup, float directionalAttenuationFactor) +{ + ma_sound_set_directional_attenuation_factor(pGroup, directionalAttenuationFactor); +} + +MA_API float ma_sound_group_get_directional_attenuation_factor(const ma_sound_group* pGroup) +{ + return ma_sound_get_directional_attenuation_factor(pGroup); +} + +MA_API void ma_sound_group_set_fade_in_pcm_frames(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames) +{ + ma_sound_set_fade_in_pcm_frames(pGroup, volumeBeg, volumeEnd, fadeLengthInFrames); +} + +MA_API void ma_sound_group_set_fade_in_milliseconds(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds) +{ + ma_sound_set_fade_in_milliseconds(pGroup, volumeBeg, volumeEnd, fadeLengthInMilliseconds); +} + +MA_API float ma_sound_group_get_current_fade_volume(ma_sound_group* pGroup) +{ + return ma_sound_get_current_fade_volume(pGroup); +} + +MA_API void ma_sound_group_set_start_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames) +{ + ma_sound_set_start_time_in_pcm_frames(pGroup, absoluteGlobalTimeInFrames); +} + +MA_API void ma_sound_group_set_start_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds) +{ + ma_sound_set_start_time_in_milliseconds(pGroup, absoluteGlobalTimeInMilliseconds); +} + +MA_API void ma_sound_group_set_stop_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames) +{ + ma_sound_set_stop_time_in_pcm_frames(pGroup, absoluteGlobalTimeInFrames); +} + +MA_API void ma_sound_group_set_stop_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds) +{ + ma_sound_set_stop_time_in_milliseconds(pGroup, absoluteGlobalTimeInMilliseconds); +} + +MA_API ma_bool32 ma_sound_group_is_playing(const ma_sound_group* pGroup) +{ + return ma_sound_is_playing(pGroup); +} + +MA_API ma_uint64 ma_sound_group_get_time_in_pcm_frames(const ma_sound_group* pGroup) +{ + return ma_sound_get_time_in_pcm_frames(pGroup); +} +#endif /* MA_NO_ENGINE */ + + + /************************************************************************************************************************************************************** *************************************************************************************************************************************************************** @@ -52852,6 +73812,7 @@ code below please report the bug to the respective repository for the relevant p #define drwav_min(a, b) (((a) < (b)) ? (a) : (b)) #define drwav_max(a, b) (((a) > (b)) ? (a) : (b)) #define drwav_clamp(x, lo, hi) (drwav_max((lo), drwav_min((hi), (x)))) +#define drwav_offset_ptr(p, offset) (((drwav_uint8*)(p)) + (offset)) #define DRWAV_MAX_SIMD_VECTOR_SIZE 64 #if defined(__x86_64__) || defined(_M_X64) #define DRWAV_X64 @@ -52864,9 +73825,14 @@ code below please report the bug to the respective repository for the relevant p #define DRWAV_INLINE __forceinline #elif defined(__GNUC__) #if defined(__STRICT_ANSI__) - #define DRWAV_INLINE __inline__ __attribute__((always_inline)) + #define DRWAV_GNUC_INLINE_HINT __inline__ #else - #define DRWAV_INLINE inline __attribute__((always_inline)) + #define DRWAV_GNUC_INLINE_HINT inline + #endif + #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) + #define DRWAV_INLINE DRWAV_GNUC_INLINE_HINT __attribute__((always_inline)) + #else + #define DRWAV_INLINE DRWAV_GNUC_INLINE_HINT #endif #elif defined(__WATCOMC__) #define DRWAV_INLINE __inline @@ -53095,6 +74061,9 @@ static DRWAV_INLINE void drwav__bswap_samples_pcm(void* pSamples, drwav_uint64 s { switch (bytesPerSample) { + case 1: + { + } break; case 2: { drwav__bswap_samples_s16((drwav_int16*)pSamples, sampleCount); @@ -53353,7 +74322,7 @@ DRWAV_PRIVATE drwav_bool32 drwav__read_fmt(drwav_read_proc onRead, drwav_seek_pr fmtOut->extendedSize = 0; fmtOut->validBitsPerSample = 0; fmtOut->channelMask = 0; - memset(fmtOut->subFormat, 0, sizeof(fmtOut->subFormat)); + DRWAV_ZERO_MEMORY(fmtOut->subFormat, sizeof(fmtOut->subFormat)); if (header.sizeInBytes > 16) { drwav_uint8 fmt_cbSize[2]; int bytesReadSoFar = 0; @@ -53486,7 +74455,7 @@ DRWAV_PRIVATE void drwav__metadata_request_extra_memory_for_stage_2(drwav__metad DRWAV_PRIVATE drwav_result drwav__metadata_alloc(drwav__metadata_parser* pParser, drwav_allocation_callbacks* pAllocationCallbacks) { if (pParser->extraCapacity != 0 || pParser->metadataCount != 0) { - free(pParser->pData); + pAllocationCallbacks->onFree(pParser->pData, pAllocationCallbacks->pUserData); pParser->pData = (drwav_uint8*)pAllocationCallbacks->onMalloc(drwav__metadata_memory_capacity(pParser), pAllocationCallbacks->pUserData); pParser->pDataCursor = pParser->pData; if (pParser->pData == NULL) { @@ -53505,12 +74474,13 @@ DRWAV_PRIVATE size_t drwav__metadata_parser_read(drwav__metadata_parser* pParser return pParser->onRead(pParser->pReadSeekUserData, pBufferOut, bytesToRead); } } -DRWAV_PRIVATE drwav_uint64 drwav__read_smpl_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata) +DRWAV_PRIVATE drwav_uint64 drwav__read_smpl_to_metadata_obj(drwav__metadata_parser* pParser, const drwav_chunk_header* pChunkHeader, drwav_metadata* pMetadata) { drwav_uint8 smplHeaderData[DRWAV_SMPL_BYTES]; drwav_uint64 totalBytesRead = 0; size_t bytesJustRead = drwav__metadata_parser_read(pParser, smplHeaderData, sizeof(smplHeaderData), &totalBytesRead); DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); + DRWAV_ASSERT(pChunkHeader != NULL); if (bytesJustRead == sizeof(smplHeaderData)) { drwav_uint32 iSampleLoop; pMetadata->type = drwav_metadata_type_smpl; @@ -53523,30 +74493,32 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_smpl_to_metadata_obj(drwav__metadata_pars pMetadata->data.smpl.smpteOffset = drwav_bytes_to_u32(smplHeaderData + 24); pMetadata->data.smpl.sampleLoopCount = drwav_bytes_to_u32(smplHeaderData + 28); pMetadata->data.smpl.samplerSpecificDataSizeInBytes = drwav_bytes_to_u32(smplHeaderData + 32); - pMetadata->data.smpl.pLoops = (drwav_smpl_loop*)drwav__metadata_get_memory(pParser, sizeof(drwav_smpl_loop) * pMetadata->data.smpl.sampleLoopCount, DRWAV_METADATA_ALIGNMENT); - for (iSampleLoop = 0; iSampleLoop < pMetadata->data.smpl.sampleLoopCount; ++iSampleLoop) { - drwav_uint8 smplLoopData[DRWAV_SMPL_LOOP_BYTES]; - bytesJustRead = drwav__metadata_parser_read(pParser, smplLoopData, sizeof(smplLoopData), &totalBytesRead); - if (bytesJustRead == sizeof(smplLoopData)) { - pMetadata->data.smpl.pLoops[iSampleLoop].cuePointId = drwav_bytes_to_u32(smplLoopData + 0); - pMetadata->data.smpl.pLoops[iSampleLoop].type = drwav_bytes_to_u32(smplLoopData + 4); - pMetadata->data.smpl.pLoops[iSampleLoop].firstSampleByteOffset = drwav_bytes_to_u32(smplLoopData + 8); - pMetadata->data.smpl.pLoops[iSampleLoop].lastSampleByteOffset = drwav_bytes_to_u32(smplLoopData + 12); - pMetadata->data.smpl.pLoops[iSampleLoop].sampleFraction = drwav_bytes_to_u32(smplLoopData + 16); - pMetadata->data.smpl.pLoops[iSampleLoop].playCount = drwav_bytes_to_u32(smplLoopData + 20); - } else { - break; + if (pMetadata->data.smpl.sampleLoopCount == (pChunkHeader->sizeInBytes - DRWAV_SMPL_BYTES) / DRWAV_SMPL_LOOP_BYTES) { + pMetadata->data.smpl.pLoops = (drwav_smpl_loop*)drwav__metadata_get_memory(pParser, sizeof(drwav_smpl_loop) * pMetadata->data.smpl.sampleLoopCount, DRWAV_METADATA_ALIGNMENT); + for (iSampleLoop = 0; iSampleLoop < pMetadata->data.smpl.sampleLoopCount; ++iSampleLoop) { + drwav_uint8 smplLoopData[DRWAV_SMPL_LOOP_BYTES]; + bytesJustRead = drwav__metadata_parser_read(pParser, smplLoopData, sizeof(smplLoopData), &totalBytesRead); + if (bytesJustRead == sizeof(smplLoopData)) { + pMetadata->data.smpl.pLoops[iSampleLoop].cuePointId = drwav_bytes_to_u32(smplLoopData + 0); + pMetadata->data.smpl.pLoops[iSampleLoop].type = drwav_bytes_to_u32(smplLoopData + 4); + pMetadata->data.smpl.pLoops[iSampleLoop].firstSampleByteOffset = drwav_bytes_to_u32(smplLoopData + 8); + pMetadata->data.smpl.pLoops[iSampleLoop].lastSampleByteOffset = drwav_bytes_to_u32(smplLoopData + 12); + pMetadata->data.smpl.pLoops[iSampleLoop].sampleFraction = drwav_bytes_to_u32(smplLoopData + 16); + pMetadata->data.smpl.pLoops[iSampleLoop].playCount = drwav_bytes_to_u32(smplLoopData + 20); + } else { + break; + } + } + if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) { + pMetadata->data.smpl.pSamplerSpecificData = drwav__metadata_get_memory(pParser, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, 1); + DRWAV_ASSERT(pMetadata->data.smpl.pSamplerSpecificData != NULL); + drwav__metadata_parser_read(pParser, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, &totalBytesRead); } - } - if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) { - pMetadata->data.smpl.pSamplerSpecificData = drwav__metadata_get_memory(pParser, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, 1); - DRWAV_ASSERT(pMetadata->data.smpl.pSamplerSpecificData != NULL); - bytesJustRead = drwav__metadata_parser_read(pParser, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, &totalBytesRead); } } return totalBytesRead; } -DRWAV_PRIVATE drwav_uint64 drwav__read_cue_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata) +DRWAV_PRIVATE drwav_uint64 drwav__read_cue_to_metadata_obj(drwav__metadata_parser* pParser, const drwav_chunk_header* pChunkHeader, drwav_metadata* pMetadata) { drwav_uint8 cueHeaderSectionData[DRWAV_CUE_BYTES]; drwav_uint64 totalBytesRead = 0; @@ -53555,25 +74527,27 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_cue_to_metadata_obj(drwav__metadata_parse if (bytesJustRead == sizeof(cueHeaderSectionData)) { pMetadata->type = drwav_metadata_type_cue; pMetadata->data.cue.cuePointCount = drwav_bytes_to_u32(cueHeaderSectionData); - pMetadata->data.cue.pCuePoints = (drwav_cue_point*)drwav__metadata_get_memory(pParser, sizeof(drwav_cue_point) * pMetadata->data.cue.cuePointCount, DRWAV_METADATA_ALIGNMENT); - DRWAV_ASSERT(pMetadata->data.cue.pCuePoints != NULL); - if (pMetadata->data.cue.cuePointCount > 0) { - drwav_uint32 iCuePoint; - for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) { - drwav_uint8 cuePointData[DRWAV_CUE_POINT_BYTES]; - bytesJustRead = drwav__metadata_parser_read(pParser, cuePointData, sizeof(cuePointData), &totalBytesRead); - if (bytesJustRead == sizeof(cuePointData)) { - pMetadata->data.cue.pCuePoints[iCuePoint].id = drwav_bytes_to_u32(cuePointData + 0); - pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition = drwav_bytes_to_u32(cuePointData + 4); - pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[0] = cuePointData[8]; - pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[1] = cuePointData[9]; - pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[2] = cuePointData[10]; - pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[3] = cuePointData[11]; - pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart = drwav_bytes_to_u32(cuePointData + 12); - pMetadata->data.cue.pCuePoints[iCuePoint].blockStart = drwav_bytes_to_u32(cuePointData + 16); - pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset = drwav_bytes_to_u32(cuePointData + 20); - } else { - break; + if (pMetadata->data.cue.cuePointCount == (pChunkHeader->sizeInBytes - DRWAV_CUE_BYTES) / DRWAV_CUE_POINT_BYTES) { + pMetadata->data.cue.pCuePoints = (drwav_cue_point*)drwav__metadata_get_memory(pParser, sizeof(drwav_cue_point) * pMetadata->data.cue.cuePointCount, DRWAV_METADATA_ALIGNMENT); + DRWAV_ASSERT(pMetadata->data.cue.pCuePoints != NULL); + if (pMetadata->data.cue.cuePointCount > 0) { + drwav_uint32 iCuePoint; + for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) { + drwav_uint8 cuePointData[DRWAV_CUE_POINT_BYTES]; + bytesJustRead = drwav__metadata_parser_read(pParser, cuePointData, sizeof(cuePointData), &totalBytesRead); + if (bytesJustRead == sizeof(cuePointData)) { + pMetadata->data.cue.pCuePoints[iCuePoint].id = drwav_bytes_to_u32(cuePointData + 0); + pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition = drwav_bytes_to_u32(cuePointData + 4); + pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[0] = cuePointData[8]; + pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[1] = cuePointData[9]; + pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[2] = cuePointData[10]; + pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[3] = cuePointData[11]; + pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart = drwav_bytes_to_u32(cuePointData + 12); + pMetadata->data.cue.pCuePoints[iCuePoint].blockStart = drwav_bytes_to_u32(cuePointData + 16); + pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset = drwav_bytes_to_u32(cuePointData + 20); + } else { + break; + } } } } @@ -53615,7 +74589,15 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_acid_to_metadata_obj(drwav__metadata_pars } return bytesRead; } -DRWAV_PRIVATE size_t drwav__strlen_clamped(char* str, size_t maxToRead) +DRWAV_PRIVATE size_t drwav__strlen(const char* str) +{ + size_t result = 0; + while (*str++) { + result += 1; + } + return result; +} +DRWAV_PRIVATE size_t drwav__strlen_clamped(const char* str, size_t maxToRead) { size_t result = 0; while (*str++ && result < maxToRead) { @@ -53623,71 +74605,147 @@ DRWAV_PRIVATE size_t drwav__strlen_clamped(char* str, size_t maxToRead) } return result; } -DRWAV_PRIVATE char* drwav__metadata_copy_string(drwav__metadata_parser* pParser, char* str, size_t maxToRead) +DRWAV_PRIVATE char* drwav__metadata_copy_string(drwav__metadata_parser* pParser, const char* str, size_t maxToRead) { size_t len = drwav__strlen_clamped(str, maxToRead); if (len) { char* result = (char*)drwav__metadata_get_memory(pParser, len + 1, 1); DRWAV_ASSERT(result != NULL); - memcpy(result, str, len); + DRWAV_COPY_MEMORY(result, str, len); result[len] = '\0'; return result; } else { return NULL; } } +typedef struct +{ + const void* pBuffer; + size_t sizeInBytes; + size_t cursor; +} drwav_buffer_reader; +DRWAV_PRIVATE drwav_result drwav_buffer_reader_init(const void* pBuffer, size_t sizeInBytes, drwav_buffer_reader* pReader) +{ + DRWAV_ASSERT(pBuffer != NULL); + DRWAV_ASSERT(pReader != NULL); + DRWAV_ZERO_OBJECT(pReader); + pReader->pBuffer = pBuffer; + pReader->sizeInBytes = sizeInBytes; + pReader->cursor = 0; + return DRWAV_SUCCESS; +} +DRWAV_PRIVATE const void* drwav_buffer_reader_ptr(const drwav_buffer_reader* pReader) +{ + DRWAV_ASSERT(pReader != NULL); + return drwav_offset_ptr(pReader->pBuffer, pReader->cursor); +} +DRWAV_PRIVATE drwav_result drwav_buffer_reader_seek(drwav_buffer_reader* pReader, size_t bytesToSeek) +{ + DRWAV_ASSERT(pReader != NULL); + if (pReader->cursor + bytesToSeek > pReader->sizeInBytes) { + return DRWAV_BAD_SEEK; + } + pReader->cursor += bytesToSeek; + return DRWAV_SUCCESS; +} +DRWAV_PRIVATE drwav_result drwav_buffer_reader_read(drwav_buffer_reader* pReader, void* pDst, size_t bytesToRead, size_t* pBytesRead) +{ + drwav_result result = DRWAV_SUCCESS; + size_t bytesRemaining; + DRWAV_ASSERT(pReader != NULL); + if (pBytesRead != NULL) { + *pBytesRead = 0; + } + bytesRemaining = (pReader->sizeInBytes - pReader->cursor); + if (bytesToRead > bytesRemaining) { + bytesToRead = bytesRemaining; + } + if (pDst == NULL) { + result = drwav_buffer_reader_seek(pReader, bytesToRead); + } else { + DRWAV_COPY_MEMORY(pDst, drwav_buffer_reader_ptr(pReader), bytesToRead); + pReader->cursor += bytesToRead; + } + DRWAV_ASSERT(pReader->cursor <= pReader->sizeInBytes); + if (result == DRWAV_SUCCESS) { + if (pBytesRead != NULL) { + *pBytesRead = bytesToRead; + } + } + return DRWAV_SUCCESS; +} +DRWAV_PRIVATE drwav_result drwav_buffer_reader_read_u16(drwav_buffer_reader* pReader, drwav_uint16* pDst) +{ + drwav_result result; + size_t bytesRead; + drwav_uint8 data[2]; + DRWAV_ASSERT(pReader != NULL); + DRWAV_ASSERT(pDst != NULL); + *pDst = 0; + result = drwav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead); + if (result != DRWAV_SUCCESS || bytesRead != sizeof(*pDst)) { + return result; + } + *pDst = drwav_bytes_to_u16(data); + return DRWAV_SUCCESS; +} +DRWAV_PRIVATE drwav_result drwav_buffer_reader_read_u32(drwav_buffer_reader* pReader, drwav_uint32* pDst) +{ + drwav_result result; + size_t bytesRead; + drwav_uint8 data[4]; + DRWAV_ASSERT(pReader != NULL); + DRWAV_ASSERT(pDst != NULL); + *pDst = 0; + result = drwav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead); + if (result != DRWAV_SUCCESS || bytesRead != sizeof(*pDst)) { + return result; + } + *pDst = drwav_bytes_to_u32(data); + return DRWAV_SUCCESS; +} DRWAV_PRIVATE drwav_uint64 drwav__read_bext_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata, drwav_uint64 chunkSize) { drwav_uint8 bextData[DRWAV_BEXT_BYTES]; - drwav_uint64 bytesRead = drwav__metadata_parser_read(pParser, bextData, sizeof(bextData), NULL); + size_t bytesRead = drwav__metadata_parser_read(pParser, bextData, sizeof(bextData), NULL); DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); if (bytesRead == sizeof(bextData)) { - drwav_uint8* pReadPointer; + drwav_buffer_reader reader; drwav_uint32 timeReferenceLow; drwav_uint32 timeReferenceHigh; size_t extraBytes; pMetadata->type = drwav_metadata_type_bext; - pReadPointer = bextData; - pMetadata->data.bext.pDescription = drwav__metadata_copy_string(pParser, (char*)(pReadPointer), DRWAV_BEXT_DESCRIPTION_BYTES); - pReadPointer += DRWAV_BEXT_DESCRIPTION_BYTES; - pMetadata->data.bext.pOriginatorName = drwav__metadata_copy_string(pParser, (char*)(pReadPointer), DRWAV_BEXT_ORIGINATOR_NAME_BYTES); - pReadPointer += DRWAV_BEXT_ORIGINATOR_NAME_BYTES; - pMetadata->data.bext.pOriginatorReference = drwav__metadata_copy_string(pParser, (char*)(pReadPointer), DRWAV_BEXT_ORIGINATOR_REF_BYTES); - pReadPointer += DRWAV_BEXT_ORIGINATOR_REF_BYTES; - memcpy(pReadPointer, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate)); - pReadPointer += sizeof(pMetadata->data.bext.pOriginationDate); - memcpy(pReadPointer, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime)); - pReadPointer += sizeof(pMetadata->data.bext.pOriginationTime); - timeReferenceLow = drwav_bytes_to_u32(pReadPointer); - pReadPointer += sizeof(drwav_uint32); - timeReferenceHigh = drwav_bytes_to_u32(pReadPointer); - pReadPointer += sizeof(drwav_uint32); - pMetadata->data.bext.timeReference = ((drwav_uint64)timeReferenceHigh << 32) + timeReferenceLow; - pMetadata->data.bext.version = drwav_bytes_to_u16(pReadPointer); - pReadPointer += sizeof(drwav_uint16); - pMetadata->data.bext.pUMID = drwav__metadata_get_memory(pParser, DRWAV_BEXT_UMID_BYTES, 1); - memcpy(pMetadata->data.bext.pUMID, pReadPointer, DRWAV_BEXT_UMID_BYTES); - pReadPointer += DRWAV_BEXT_UMID_BYTES; - pMetadata->data.bext.loudnessValue = drwav_bytes_to_u16(pReadPointer); - pReadPointer += sizeof(drwav_uint16); - pMetadata->data.bext.loudnessRange = drwav_bytes_to_u16(pReadPointer); - pReadPointer += sizeof(drwav_uint16); - pMetadata->data.bext.maxTruePeakLevel = drwav_bytes_to_u16(pReadPointer); - pReadPointer += sizeof(drwav_uint16); - pMetadata->data.bext.maxMomentaryLoudness = drwav_bytes_to_u16(pReadPointer); - pReadPointer += sizeof(drwav_uint16); - pMetadata->data.bext.maxShortTermLoudness = drwav_bytes_to_u16(pReadPointer); - pReadPointer += sizeof(drwav_uint16); - DRWAV_ASSERT((pReadPointer + DRWAV_BEXT_RESERVED_BYTES) == (bextData + DRWAV_BEXT_BYTES)); - extraBytes = (size_t)(chunkSize - DRWAV_BEXT_BYTES); - if (extraBytes > 0) { - pMetadata->data.bext.pCodingHistory = (char*)drwav__metadata_get_memory(pParser, extraBytes + 1, 1); - DRWAV_ASSERT(pMetadata->data.bext.pCodingHistory != NULL); - bytesRead += drwav__metadata_parser_read(pParser, pMetadata->data.bext.pCodingHistory, extraBytes, NULL); - pMetadata->data.bext.codingHistorySize = (drwav_uint32)strlen(pMetadata->data.bext.pCodingHistory); - } else { - pMetadata->data.bext.pCodingHistory = NULL; - pMetadata->data.bext.codingHistorySize = 0; + if (drwav_buffer_reader_init(bextData, bytesRead, &reader) == DRWAV_SUCCESS) { + pMetadata->data.bext.pDescription = drwav__metadata_copy_string(pParser, (const char*)drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_DESCRIPTION_BYTES); + drwav_buffer_reader_seek(&reader, DRWAV_BEXT_DESCRIPTION_BYTES); + pMetadata->data.bext.pOriginatorName = drwav__metadata_copy_string(pParser, (const char*)drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_ORIGINATOR_NAME_BYTES); + drwav_buffer_reader_seek(&reader, DRWAV_BEXT_ORIGINATOR_NAME_BYTES); + pMetadata->data.bext.pOriginatorReference = drwav__metadata_copy_string(pParser, (const char*)drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_ORIGINATOR_REF_BYTES); + drwav_buffer_reader_seek(&reader, DRWAV_BEXT_ORIGINATOR_REF_BYTES); + drwav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate), NULL); + drwav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime), NULL); + drwav_buffer_reader_read_u32(&reader, &timeReferenceLow); + drwav_buffer_reader_read_u32(&reader, &timeReferenceHigh); + pMetadata->data.bext.timeReference = ((drwav_uint64)timeReferenceHigh << 32) + timeReferenceLow; + drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.version); + pMetadata->data.bext.pUMID = drwav__metadata_get_memory(pParser, DRWAV_BEXT_UMID_BYTES, 1); + drwav_buffer_reader_read(&reader, pMetadata->data.bext.pUMID, DRWAV_BEXT_UMID_BYTES, NULL); + drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessValue); + drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessRange); + drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxTruePeakLevel); + drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxMomentaryLoudness); + drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxShortTermLoudness); + DRWAV_ASSERT((drwav_offset_ptr(drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_RESERVED_BYTES)) == (bextData + DRWAV_BEXT_BYTES)); + extraBytes = (size_t)(chunkSize - DRWAV_BEXT_BYTES); + if (extraBytes > 0) { + pMetadata->data.bext.pCodingHistory = (char*)drwav__metadata_get_memory(pParser, extraBytes + 1, 1); + DRWAV_ASSERT(pMetadata->data.bext.pCodingHistory != NULL); + bytesRead += drwav__metadata_parser_read(pParser, pMetadata->data.bext.pCodingHistory, extraBytes, NULL); + pMetadata->data.bext.codingHistorySize = (drwav_uint32)drwav__strlen(pMetadata->data.bext.pCodingHistory); + } else { + pMetadata->data.bext.pCodingHistory = NULL; + pMetadata->data.bext.codingHistorySize = 0; + } } } return bytesRead; @@ -53707,7 +74765,7 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_list_label_or_note_to_metadata_obj(drwav_ pMetadata->data.labelOrNote.stringLength = sizeIncludingNullTerminator - 1; pMetadata->data.labelOrNote.pString = (char*)drwav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1); DRWAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL); - bytesJustRead = drwav__metadata_parser_read(pParser, pMetadata->data.labelOrNote.pString, sizeIncludingNullTerminator, &totalBytesRead); + drwav__metadata_parser_read(pParser, pMetadata->data.labelOrNote.pString, sizeIncludingNullTerminator, &totalBytesRead); } else { pMetadata->data.labelOrNote.stringLength = 0; pMetadata->data.labelOrNote.pString = NULL; @@ -53739,7 +74797,7 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_list_labelled_cue_region_to_metadata_obj( pMetadata->data.labelledCueRegion.stringLength = sizeIncludingNullTerminator - 1; pMetadata->data.labelledCueRegion.pString = (char*)drwav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1); DRWAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL); - bytesJustRead = drwav__metadata_parser_read(pParser, pMetadata->data.labelledCueRegion.pString, sizeIncludingNullTerminator, &totalBytesRead); + drwav__metadata_parser_read(pParser, pMetadata->data.labelledCueRegion.pString, sizeIncludingNullTerminator, &totalBytesRead); } else { pMetadata->data.labelledCueRegion.stringLength = 0; pMetadata->data.labelledCueRegion.pString = NULL; @@ -53805,11 +74863,11 @@ DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_unknown_chunk(drwav__metadata } return bytesRead; } -DRWAV_PRIVATE drwav_bool32 drwav__chunk_matches(drwav_uint64 allowedMetadataTypes, const drwav_uint8* pChunkID, drwav_metadata_type type, const char* pID) +DRWAV_PRIVATE drwav_bool32 drwav__chunk_matches(drwav_metadata_type allowedMetadataTypes, const drwav_uint8* pChunkID, drwav_metadata_type type, const char* pID) { return (allowedMetadataTypes & type) && drwav_fourcc_equal(pChunkID, pID); } -DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* pParser, const drwav_chunk_header* pChunkHeader, drwav_uint64 allowedMetadataTypes) +DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* pParser, const drwav_chunk_header* pChunkHeader, drwav_metadata_type allowedMetadataTypes) { const drwav_uint8 *pChunkID = pChunkHeader->id.fourcc; drwav_uint64 bytesRead = 0; @@ -53825,16 +74883,21 @@ DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* bytesJustRead = drwav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead); if (bytesJustRead == sizeof(buffer)) { drwav_uint32 loopCount = drwav_bytes_to_u32(buffer); - bytesJustRead = drwav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead); - if (bytesJustRead == sizeof(buffer)) { - drwav_uint32 samplerSpecificDataSizeInBytes = drwav_bytes_to_u32(buffer); - pParser->metadataCount += 1; - drwav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(drwav_smpl_loop) * loopCount, DRWAV_METADATA_ALIGNMENT); - drwav__metadata_request_extra_memory_for_stage_2(pParser, samplerSpecificDataSizeInBytes, 1); + drwav_uint64 calculatedLoopCount; + calculatedLoopCount = (pChunkHeader->sizeInBytes - DRWAV_SMPL_BYTES) / DRWAV_SMPL_LOOP_BYTES; + if (calculatedLoopCount == loopCount) { + bytesJustRead = drwav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead); + if (bytesJustRead == sizeof(buffer)) { + drwav_uint32 samplerSpecificDataSizeInBytes = drwav_bytes_to_u32(buffer); + pParser->metadataCount += 1; + drwav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(drwav_smpl_loop) * loopCount, DRWAV_METADATA_ALIGNMENT); + drwav__metadata_request_extra_memory_for_stage_2(pParser, samplerSpecificDataSizeInBytes, 1); + } + } else { } } } else { - bytesRead = drwav__read_smpl_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]); + bytesRead = drwav__read_smpl_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]); if (bytesRead == pChunkHeader->sizeInBytes) { pParser->metadataCursor += 1; } else { @@ -53876,7 +74939,7 @@ DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* cueCount = (size_t)(pChunkHeader->sizeInBytes - DRWAV_CUE_BYTES) / DRWAV_CUE_POINT_BYTES; drwav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(drwav_cue_point) * cueCount, DRWAV_METADATA_ALIGNMENT); } else { - bytesRead = drwav__read_cue_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]); + bytesRead = drwav__read_cue_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]); if (bytesRead == pChunkHeader->sizeInBytes) { pParser->metadataCursor += 1; } else { @@ -53895,19 +74958,19 @@ DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* if (bytesJustRead != DRWAV_BEXT_DESCRIPTION_BYTES) { return bytesRead; } - allocSizeNeeded += strlen(buffer) + 1; + allocSizeNeeded += drwav__strlen(buffer) + 1; buffer[DRWAV_BEXT_ORIGINATOR_NAME_BYTES] = '\0'; bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_ORIGINATOR_NAME_BYTES, &bytesRead); if (bytesJustRead != DRWAV_BEXT_ORIGINATOR_NAME_BYTES) { return bytesRead; } - allocSizeNeeded += strlen(buffer) + 1; + allocSizeNeeded += drwav__strlen(buffer) + 1; buffer[DRWAV_BEXT_ORIGINATOR_REF_BYTES] = '\0'; bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_ORIGINATOR_REF_BYTES, &bytesRead); if (bytesJustRead != DRWAV_BEXT_ORIGINATOR_REF_BYTES) { return bytesRead; } - allocSizeNeeded += strlen(buffer) + 1; + allocSizeNeeded += drwav__strlen(buffer) + 1; allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - DRWAV_BEXT_BYTES; drwav__metadata_request_extra_memory_for_stage_2(pParser, allocSizeNeeded, 1); pParser->metadataCount += 1; @@ -53991,7 +75054,7 @@ DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_album); } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_tracknumber, "ITRK")) { subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_tracknumber); - } else if (allowedMetadataTypes & drwav_metadata_type_unknown) { + } else if ((allowedMetadataTypes & drwav_metadata_type_unknown) != 0) { subchunkBytesRead = drwav__metadata_process_unknown_chunk(pParser, subchunkId, subchunkDataSize, listType); } bytesRead += subchunkBytesRead; @@ -54010,18 +75073,25 @@ DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* bytesRead += 1; } } - } else if (allowedMetadataTypes & drwav_metadata_type_unknown) { + } else if ((allowedMetadataTypes & drwav_metadata_type_unknown) != 0) { bytesRead = drwav__metadata_process_unknown_chunk(pParser, pChunkID, pChunkHeader->sizeInBytes, drwav_metadata_location_top_level); } return bytesRead; } DRWAV_PRIVATE drwav_uint32 drwav_get_bytes_per_pcm_frame(drwav* pWav) { + drwav_uint32 bytesPerFrame; if ((pWav->bitsPerSample & 0x7) == 0) { - return (pWav->bitsPerSample * pWav->fmt.channels) >> 3; + bytesPerFrame = (pWav->bitsPerSample * pWav->fmt.channels) >> 3; } else { - return pWav->fmt.blockAlign; + bytesPerFrame = pWav->fmt.blockAlign; } + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW || pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) { + if (bytesPerFrame != pWav->fmt.channels) { + return 0; + } + } + return bytesPerFrame; } DRWAV_API drwav_uint16 drwav_fmt_get_format(const drwav_fmt* pFMT) { @@ -54167,7 +75237,7 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on if (translatedFormatTag == DR_WAVE_FORMAT_EXTENSIBLE) { translatedFormatTag = drwav_bytes_to_u16(fmt.subFormat + 0); } - memset(&metadataParser, 0, sizeof(metadataParser)); + DRWAV_ZERO_MEMORY(&metadataParser, sizeof(metadataParser)); if (!sequential && pWav->allowedMetadataTypes != drwav_metadata_type_none && (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64)) { drwav_uint64 cursorForMetadata = cursor; metadataParser.onRead = pWav->onRead; @@ -54302,7 +75372,11 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on if (sampleCountFromFactChunk != 0) { pWav->totalPCMFrameCount = sampleCountFromFactChunk; } else { - pWav->totalPCMFrameCount = dataChunkSize / drwav_get_bytes_per_pcm_frame(pWav); + drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return DRWAV_FALSE; + } + pWav->totalPCMFrameCount = dataChunkSize / bytesPerFrame; if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { drwav_uint64 totalBlockHeaderSizeInBytes; drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; @@ -54328,6 +75402,9 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on return DRWAV_FALSE; } } + if (drwav_get_bytes_per_pcm_frame(pWav) == 0) { + return DRWAV_FALSE; + } #ifdef DR_WAV_LIBSNDFILE_COMPAT if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; @@ -54592,7 +75669,7 @@ DRWAV_PRIVATE size_t drwav__write_or_count_metadata(drwav* pWav, drwav_metadata* bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxTruePeakLevel); bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxMomentaryLoudness); bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxShortTermLoudness); - memset(reservedBuf, 0, sizeof(reservedBuf)); + DRWAV_ZERO_MEMORY(reservedBuf, sizeof(reservedBuf)); bytesWritten += drwav__write_or_count(pWav, reservedBuf, sizeof(reservedBuf)); if (pMetadata->data.bext.codingHistorySize > 0) { bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pCodingHistory, pMetadata->data.bext.codingHistorySize); @@ -55829,6 +76906,7 @@ DRWAV_API drwav_result drwav_uninit(drwav* pWav) DRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut) { size_t bytesRead; + drwav_uint32 bytesPerFrame; if (pWav == NULL || bytesToRead == 0) { return 0; } @@ -55838,6 +76916,10 @@ DRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOu if (bytesToRead == 0) { return 0; } + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } if (pBufferOut != NULL) { bytesRead = pWav->onRead(pWav->pUserData, pBufferOut, bytesToRead); } else { @@ -55866,7 +76948,7 @@ DRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOu } } } - pWav->readCursorInPCMFrames += bytesRead / drwav_get_bytes_per_pcm_frame(pWav); + pWav->readCursorInPCMFrames += bytesRead / bytesPerFrame; pWav->bytesRemaining -= bytesRead; return bytesRead; } @@ -55897,7 +76979,11 @@ DRWAV_API drwav_uint64 drwav_read_pcm_frames_be(drwav* pWav, drwav_uint64 frames { drwav_uint64 framesRead = drwav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); if (pBufferOut != NULL) { - drwav__bswap_samples(pBufferOut, framesRead*pWav->channels, drwav_get_bytes_per_pcm_frame(pWav)/pWav->channels, pWav->translatedFormatTag); + drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + drwav__bswap_samples(pBufferOut, framesRead*pWav->channels, bytesPerFrame/pWav->channels, pWav->translatedFormatTag); } return framesRead; } @@ -55941,8 +77027,8 @@ DRWAV_API drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetF if (pWav->totalPCMFrameCount == 0) { return DRWAV_TRUE; } - if (targetFrameIndex >= pWav->totalPCMFrameCount) { - targetFrameIndex = pWav->totalPCMFrameCount - 1; + if (targetFrameIndex > pWav->totalPCMFrameCount) { + targetFrameIndex = pWav->totalPCMFrameCount; } if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) { if (targetFrameIndex < pWav->readCursorInPCMFrames) { @@ -55977,10 +77063,15 @@ DRWAV_API drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetF drwav_uint64 currentBytePos; drwav_uint64 targetBytePos; drwav_uint64 offset; - totalSizeInBytes = pWav->totalPCMFrameCount * drwav_get_bytes_per_pcm_frame(pWav); + drwav_uint32 bytesPerFrame; + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return DRWAV_FALSE; + } + totalSizeInBytes = pWav->totalPCMFrameCount * bytesPerFrame; DRWAV_ASSERT(totalSizeInBytes >= pWav->bytesRemaining); currentBytePos = totalSizeInBytes - pWav->bytesRemaining; - targetBytePos = targetFrameIndex * drwav_get_bytes_per_pcm_frame(pWav); + targetBytePos = targetFrameIndex * bytesPerFrame; if (currentBytePos < targetBytePos) { offset = (targetBytePos - currentBytePos); } else { @@ -55994,7 +77085,7 @@ DRWAV_API drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetF if (!pWav->onSeek(pWav->pUserData, offset32, drwav_seek_origin_current)) { return DRWAV_FALSE; } - pWav->readCursorInPCMFrames += offset32 / drwav_get_bytes_per_pcm_frame(pWav); + pWav->readCursorInPCMFrames += offset32 / bytesPerFrame; pWav->bytesRemaining -= offset32; offset -= offset32; } @@ -56080,6 +77171,9 @@ DRWAV_API drwav_uint64 drwav_write_pcm_frames_be(drwav* pWav, drwav_uint64 frame bytesWritten = 0; pRunningData = (const drwav_uint8*)pData; bytesPerSample = drwav_get_bytes_per_pcm_frame(pWav) / pWav->channels; + if (bytesPerSample == 0) { + return 0; + } while (bytesToWrite > 0) { drwav_uint8 temp[4096]; drwav_uint32 sampleCount; @@ -56278,7 +77372,7 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uin return totalFramesRead; } pWav->ima.predictor[0] = drwav_bytes_to_s16(header + 0); - pWav->ima.stepIndex[0] = header[2]; + pWav->ima.stepIndex[0] = drwav_clamp(header[2], 0, (drwav_int32)drwav_countof(stepTable)-1); pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[0]; pWav->ima.cachedFrameCount = 1; } else { @@ -56293,9 +77387,9 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uin return totalFramesRead; } pWav->ima.predictor[0] = drwav_bytes_to_s16(header + 0); - pWav->ima.stepIndex[0] = header[2]; + pWav->ima.stepIndex[0] = drwav_clamp(header[2], 0, (drwav_int32)drwav_countof(stepTable)-1); pWav->ima.predictor[1] = drwav_bytes_to_s16(header + 4); - pWav->ima.stepIndex[1] = header[6]; + pWav->ima.stepIndex[1] = drwav_clamp(header[6], 0, (drwav_int32)drwav_countof(stepTable)-1); pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 2] = pWav->ima.predictor[0]; pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[1]; pWav->ima.cachedFrameCount = 1; @@ -56409,7 +77503,7 @@ static DRWAV_INLINE drwav_int16 drwav__mulaw_to_s16(drwav_uint8 sampleIn) } DRWAV_PRIVATE void drwav__pcm_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) { - unsigned int i; + size_t i; if (bytesPerSample == 1) { drwav_u8_to_s16(pOut, pIn, totalSampleCount); return; @@ -56461,8 +77555,10 @@ DRWAV_PRIVATE void drwav__ieee_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__pcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) { drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096]; + drwav_uint8 sampleData[4096] = {0}; drwav_uint32 bytesPerFrame; + drwav_uint32 bytesPerSample; + drwav_uint64 samplesRead; if ((pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 16) || pBufferOut == NULL) { return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut); } @@ -56470,14 +77566,25 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__pcm(drwav* pWav, drwav_uin if (bytesPerFrame == 0) { return 0; } + bytesPerSample = bytesPerFrame / pWav->channels; + if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { + return 0; + } totalFramesRead = 0; while (framesToRead > 0) { - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); + drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - drwav__pcm_to_s16(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels), bytesPerFrame/pWav->channels); - pBufferOut += framesRead*pWav->channels; + DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + samplesRead = framesRead * pWav->channels; + if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { + DRWAV_ASSERT(DRWAV_FALSE); + break; + } + drwav__pcm_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); + pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } @@ -56486,8 +77593,10 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__pcm(drwav* pWav, drwav_uin DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ieee(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) { drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096]; + drwav_uint8 sampleData[4096] = {0}; drwav_uint32 bytesPerFrame; + drwav_uint32 bytesPerSample; + drwav_uint64 samplesRead; if (pBufferOut == NULL) { return drwav_read_pcm_frames(pWav, framesToRead, NULL); } @@ -56495,14 +77604,25 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ieee(drwav* pWav, drwav_ui if (bytesPerFrame == 0) { return 0; } + bytesPerSample = bytesPerFrame / pWav->channels; + if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { + return 0; + } totalFramesRead = 0; while (framesToRead > 0) { - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); + drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - drwav__ieee_to_s16(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels), bytesPerFrame/pWav->channels); - pBufferOut += framesRead*pWav->channels; + DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + samplesRead = framesRead * pWav->channels; + if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { + DRWAV_ASSERT(DRWAV_FALSE); + break; + } + drwav__ieee_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); + pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } @@ -56511,8 +77631,10 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ieee(drwav* pWav, drwav_ui DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__alaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) { drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096]; + drwav_uint8 sampleData[4096] = {0}; drwav_uint32 bytesPerFrame; + drwav_uint32 bytesPerSample; + drwav_uint64 samplesRead; if (pBufferOut == NULL) { return drwav_read_pcm_frames(pWav, framesToRead, NULL); } @@ -56520,14 +77642,25 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__alaw(drwav* pWav, drwav_ui if (bytesPerFrame == 0) { return 0; } + bytesPerSample = bytesPerFrame / pWav->channels; + if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { + return 0; + } totalFramesRead = 0; while (framesToRead > 0) { - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); + drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - drwav_alaw_to_s16(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels)); - pBufferOut += framesRead*pWav->channels; + DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + samplesRead = framesRead * pWav->channels; + if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { + DRWAV_ASSERT(DRWAV_FALSE); + break; + } + drwav_alaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead); + pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } @@ -56536,8 +77669,10 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__alaw(drwav* pWav, drwav_ui DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__mulaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) { drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096]; + drwav_uint8 sampleData[4096] = {0}; drwav_uint32 bytesPerFrame; + drwav_uint32 bytesPerSample; + drwav_uint64 samplesRead; if (pBufferOut == NULL) { return drwav_read_pcm_frames(pWav, framesToRead, NULL); } @@ -56545,14 +77680,25 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__mulaw(drwav* pWav, drwav_u if (bytesPerFrame == 0) { return 0; } + bytesPerSample = bytesPerFrame / pWav->channels; + if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { + return 0; + } totalFramesRead = 0; while (framesToRead > 0) { - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); + drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - drwav_mulaw_to_s16(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels)); - pBufferOut += framesRead*pWav->channels; + DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + samplesRead = framesRead * pWav->channels; + if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { + DRWAV_ASSERT(DRWAV_FALSE); + break; + } + drwav_mulaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead); + pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } @@ -56733,49 +77879,50 @@ DRWAV_PRIVATE void drwav__ieee_to_f32(float* pOut, const drwav_uint8* pIn, size_ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__pcm(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) { drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096]; - drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + drwav_uint8 sampleData[4096] = {0}; + drwav_uint32 bytesPerFrame; + drwav_uint32 bytesPerSample; + drwav_uint64 samplesRead; + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } + bytesPerSample = bytesPerFrame / pWav->channels; + if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { + return 0; + } totalFramesRead = 0; while (framesToRead > 0) { - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); + drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - drwav__pcm_to_f32(pBufferOut, sampleData, (size_t)framesRead*pWav->channels, bytesPerFrame/pWav->channels); - pBufferOut += framesRead*pWav->channels; + DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + samplesRead = framesRead * pWav->channels; + if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { + DRWAV_ASSERT(DRWAV_FALSE); + break; + } + drwav__pcm_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); + pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__msadpcm(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__msadpcm_ima(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) { - drwav_uint64 totalFramesRead = 0; + drwav_uint64 totalFramesRead; drwav_int16 samples16[2048]; + totalFramesRead = 0; while (framesToRead > 0) { - drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels), samples16); - if (framesRead == 0) { - break; - } - drwav_s16_to_f32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); - pBufferOut += framesRead*pWav->channels; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__ima(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) -{ - drwav_uint64 totalFramesRead = 0; - drwav_int16 samples16[2048]; - while (framesToRead > 0) { - drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels), samples16); + drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels); + drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16); if (framesRead == 0) { break; } + DRWAV_ASSERT(framesRead <= framesToReadThisIteration); drwav_s16_to_f32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); pBufferOut += framesRead*pWav->channels; framesToRead -= framesRead; @@ -56786,8 +77933,10 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__ima(drwav* pWav, drwav_uin DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__ieee(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) { drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096]; + drwav_uint8 sampleData[4096] = {0}; drwav_uint32 bytesPerFrame; + drwav_uint32 bytesPerSample; + drwav_uint64 samplesRead; if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bitsPerSample == 32) { return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut); } @@ -56795,14 +77944,25 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__ieee(drwav* pWav, drwav_ui if (bytesPerFrame == 0) { return 0; } + bytesPerSample = bytesPerFrame / pWav->channels; + if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { + return 0; + } totalFramesRead = 0; while (framesToRead > 0) { - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); + drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - drwav__ieee_to_f32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels), bytesPerFrame/pWav->channels); - pBufferOut += framesRead*pWav->channels; + DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + samplesRead = framesRead * pWav->channels; + if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { + DRWAV_ASSERT(DRWAV_FALSE); + break; + } + drwav__ieee_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); + pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } @@ -56811,19 +77971,33 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__ieee(drwav* pWav, drwav_ui DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__alaw(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) { drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096]; - drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + drwav_uint8 sampleData[4096] = {0}; + drwav_uint32 bytesPerFrame; + drwav_uint32 bytesPerSample; + drwav_uint64 samplesRead; + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } + bytesPerSample = bytesPerFrame / pWav->channels; + if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { + return 0; + } totalFramesRead = 0; while (framesToRead > 0) { - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); + drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - drwav_alaw_to_f32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels)); - pBufferOut += framesRead*pWav->channels; + DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + samplesRead = framesRead * pWav->channels; + if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { + DRWAV_ASSERT(DRWAV_FALSE); + break; + } + drwav_alaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead); + pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } @@ -56832,19 +78006,33 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__alaw(drwav* pWav, drwav_ui DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__mulaw(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) { drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096]; - drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + drwav_uint8 sampleData[4096] = {0}; + drwav_uint32 bytesPerFrame; + drwav_uint32 bytesPerSample; + drwav_uint64 samplesRead; + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } + bytesPerSample = bytesPerFrame / pWav->channels; + if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { + return 0; + } totalFramesRead = 0; while (framesToRead > 0) { - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); + drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - drwav_mulaw_to_f32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels)); - pBufferOut += framesRead*pWav->channels; + DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + samplesRead = framesRead * pWav->channels; + if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { + DRWAV_ASSERT(DRWAV_FALSE); + break; + } + drwav_mulaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead); + pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } @@ -56864,8 +78052,8 @@ DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 frame if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) { return drwav_read_pcm_frames_f32__pcm(pWav, framesToRead, pBufferOut); } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { - return drwav_read_pcm_frames_f32__msadpcm(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { + return drwav_read_pcm_frames_f32__msadpcm_ima(pWav, framesToRead, pBufferOut); } if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) { return drwav_read_pcm_frames_f32__ieee(pWav, framesToRead, pBufferOut); @@ -56876,9 +78064,6 @@ DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 frame if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) { return drwav_read_pcm_frames_f32__mulaw(pWav, framesToRead, pBufferOut); } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { - return drwav_read_pcm_frames_f32__ima(pWav, framesToRead, pBufferOut); - } return 0; } DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32le(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) @@ -57035,8 +78220,10 @@ DRWAV_PRIVATE void drwav__ieee_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__pcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) { drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096]; + drwav_uint8 sampleData[4096] = {0}; drwav_uint32 bytesPerFrame; + drwav_uint32 bytesPerSample; + drwav_uint64 samplesRead; if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 32) { return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut); } @@ -57044,44 +78231,41 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__pcm(drwav* pWav, drwav_uin if (bytesPerFrame == 0) { return 0; } + bytesPerSample = bytesPerFrame / pWav->channels; + if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { + return 0; + } totalFramesRead = 0; while (framesToRead > 0) { - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); + drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - drwav__pcm_to_s32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels), bytesPerFrame/pWav->channels); - pBufferOut += framesRead*pWav->channels; + DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + samplesRead = framesRead * pWav->channels; + if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { + DRWAV_ASSERT(DRWAV_FALSE); + break; + } + drwav__pcm_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); + pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__msadpcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__msadpcm_ima(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) { drwav_uint64 totalFramesRead = 0; drwav_int16 samples16[2048]; while (framesToRead > 0) { - drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels), samples16); - if (framesRead == 0) { - break; - } - drwav_s16_to_s32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); - pBufferOut += framesRead*pWav->channels; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__ima(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) -{ - drwav_uint64 totalFramesRead = 0; - drwav_int16 samples16[2048]; - while (framesToRead > 0) { - drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels), samples16); + drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels); + drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16); if (framesRead == 0) { break; } + DRWAV_ASSERT(framesRead <= framesToReadThisIteration); drwav_s16_to_s32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); pBufferOut += framesRead*pWav->channels; framesToRead -= framesRead; @@ -57092,19 +78276,33 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__ima(drwav* pWav, drwav_uin DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__ieee(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) { drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096]; - drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + drwav_uint8 sampleData[4096] = {0}; + drwav_uint32 bytesPerFrame; + drwav_uint32 bytesPerSample; + drwav_uint64 samplesRead; + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } + bytesPerSample = bytesPerFrame / pWav->channels; + if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { + return 0; + } totalFramesRead = 0; while (framesToRead > 0) { - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); + drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - drwav__ieee_to_s32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels), bytesPerFrame/pWav->channels); - pBufferOut += framesRead*pWav->channels; + DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + samplesRead = framesRead * pWav->channels; + if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { + DRWAV_ASSERT(DRWAV_FALSE); + break; + } + drwav__ieee_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); + pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } @@ -57113,19 +78311,33 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__ieee(drwav* pWav, drwav_ui DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__alaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) { drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096]; - drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + drwav_uint8 sampleData[4096] = {0}; + drwav_uint32 bytesPerFrame; + drwav_uint32 bytesPerSample; + drwav_uint64 samplesRead; + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } + bytesPerSample = bytesPerFrame / pWav->channels; + if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { + return 0; + } totalFramesRead = 0; while (framesToRead > 0) { - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); + drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - drwav_alaw_to_s32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels)); - pBufferOut += framesRead*pWav->channels; + DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + samplesRead = framesRead * pWav->channels; + if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { + DRWAV_ASSERT(DRWAV_FALSE); + break; + } + drwav_alaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead); + pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } @@ -57134,19 +78346,33 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__alaw(drwav* pWav, drwav_ui DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__mulaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) { drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096]; - drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + drwav_uint8 sampleData[4096] = {0}; + drwav_uint32 bytesPerFrame; + drwav_uint32 bytesPerSample; + drwav_uint64 samplesRead; + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } + bytesPerSample = bytesPerFrame / pWav->channels; + if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { + return 0; + } totalFramesRead = 0; while (framesToRead > 0) { - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); + drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - drwav_mulaw_to_s32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels)); - pBufferOut += framesRead*pWav->channels; + DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + samplesRead = framesRead * pWav->channels; + if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { + DRWAV_ASSERT(DRWAV_FALSE); + break; + } + drwav_mulaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead); + pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } @@ -57166,8 +78392,8 @@ DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32(drwav* pWav, drwav_uint64 frame if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) { return drwav_read_pcm_frames_s32__pcm(pWav, framesToRead, pBufferOut); } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { - return drwav_read_pcm_frames_s32__msadpcm(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { + return drwav_read_pcm_frames_s32__msadpcm_ima(pWav, framesToRead, pBufferOut); } if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) { return drwav_read_pcm_frames_s32__ieee(pWav, framesToRead, pBufferOut); @@ -57178,9 +78404,6 @@ DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32(drwav* pWav, drwav_uint64 frame if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) { return drwav_read_pcm_frames_s32__mulaw(pWav, framesToRead, pBufferOut); } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { - return drwav_read_pcm_frames_s32__ima(pWav, framesToRead, pBufferOut); - } return 0; } DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32le(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) @@ -57677,9 +78900,14 @@ DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b) #define DRFLAC_INLINE __forceinline #elif defined(__GNUC__) #if defined(__STRICT_ANSI__) - #define DRFLAC_INLINE __inline__ __attribute__((always_inline)) + #define DRFLAC_GNUC_INLINE_HINT __inline__ #else - #define DRFLAC_INLINE inline __attribute__((always_inline)) + #define DRFLAC_GNUC_INLINE_HINT inline + #endif + #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) + #define DRFLAC_INLINE DRFLAC_GNUC_INLINE_HINT __attribute__((always_inline)) + #else + #define DRFLAC_INLINE DRFLAC_GNUC_INLINE_HINT #endif #elif defined(__WATCOMC__) #define DRFLAC_INLINE __inline @@ -57690,7 +78918,7 @@ DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b) #define DRFLAC_X64 #elif defined(__i386) || defined(_M_IX86) #define DRFLAC_X86 -#elif defined(__arm__) || defined(_M_ARM) || defined(_M_ARM64) +#elif defined(__arm__) || defined(_M_ARM) || defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) #define DRFLAC_ARM #endif #if !defined(DR_FLAC_NO_SIMD) @@ -57727,13 +78955,6 @@ DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b) #if defined(DRFLAC_ARM) #if !defined(DRFLAC_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) #define DRFLAC_SUPPORT_NEON - #endif - #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include) - #if !defined(DRFLAC_SUPPORT_NEON) && !defined(DRFLAC_NO_NEON) && __has_include() - #define DRFLAC_SUPPORT_NEON - #endif - #endif - #if defined(DRFLAC_SUPPORT_NEON) #include #endif #endif @@ -58136,6 +79357,11 @@ static DRFLAC_INLINE drflac_uint32 drflac__be2host_32(drflac_uint32 n) } return n; } +static DRFLAC_INLINE drflac_uint32 drflac__be2host_32_ptr_unaligned(const void* pData) +{ + const drflac_uint8* pNum = (drflac_uint8*)pData; + return *(pNum) << 24 | *(pNum+1) << 16 | *(pNum+2) << 8 | *(pNum+3); +} static DRFLAC_INLINE drflac_uint64 drflac__be2host_64(drflac_uint64 n) { if (drflac__is_little_endian()) { @@ -58150,6 +79376,11 @@ static DRFLAC_INLINE drflac_uint32 drflac__le2host_32(drflac_uint32 n) } return n; } +static DRFLAC_INLINE drflac_uint32 drflac__le2host_32_ptr_unaligned(const void* pData) +{ + const drflac_uint8* pNum = (drflac_uint8*)pData; + return *pNum | *(pNum+1) << 8 | *(pNum+2) << 16 | *(pNum+3) << 24; +} static DRFLAC_INLINE drflac_uint32 drflac__unsynchsafe_32(drflac_uint32 n) { drflac_uint32 result = 0; @@ -58535,6 +79766,9 @@ static DRFLAC_INLINE drflac_bool32 drflac__read_uint32(drflac_bs* bs, unsigned i if (!drflac__reload_cache(bs)) { return DRFLAC_FALSE; } + if (bitCountLo > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + return DRFLAC_FALSE; + } *pResultOut = (resultHi << bitCountLo) | (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo); bs->consumedBits += bitCountLo; bs->cache <<= bitCountLo; @@ -58876,8 +80110,18 @@ static DRFLAC_INLINE drflac_bool32 drflac__seek_past_next_set_bit(drflac_bs* bs, return DRFLAC_FALSE; } } + if (bs->cache == 1) { + *pOffsetOut = zeroCounter + (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs) - 1; + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; + } + return DRFLAC_TRUE; + } setBitOffsetPlus1 = drflac__clz(bs->cache); setBitOffsetPlus1 += 1; + if (setBitOffsetPlus1 > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + return DRFLAC_FALSE; + } bs->consumedBits += setBitOffsetPlus1; bs->cache <<= setBitOffsetPlus1; *pOffsetOut = zeroCounter + setBitOffsetPlus1 - 1; @@ -58963,6 +80207,24 @@ static drflac_result drflac__read_utf8_coded_number(drflac_bs* bs, drflac_uint64 *pCRCOut = crc; return DRFLAC_SUCCESS; } +static DRFLAC_INLINE drflac_uint32 drflac__ilog2_u32(drflac_uint32 x) +{ +#if 1 + drflac_uint32 result = 0; + while (x > 0) { + result += 1; + x >>= 1; + } + return result; +#endif +} +static DRFLAC_INLINE drflac_bool32 drflac__use_64_bit_prediction(drflac_uint32 bitsPerSample, drflac_uint32 order, drflac_uint32 precision) +{ + return bitsPerSample + precision + drflac__ilog2_u32(order) > 32; +} +#if defined(__clang__) +__attribute__((no_sanitize("signed-integer-overflow"))) +#endif static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_32(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) { drflac_int32 prediction = 0; @@ -59173,7 +80435,7 @@ static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_64(drflac_uint32 return (drflac_int32)(prediction >> shift); } #if 0 -static drflac_bool32 drflac__decode_samples_with_residual__rice__reference(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +static drflac_bool32 drflac__decode_samples_with_residual__rice__reference(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { drflac_uint32 i; DRFLAC_ASSERT(bs != NULL); @@ -59205,10 +80467,10 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__reference(drfla } else { decodedRice = (decodedRice >> 1); } - if (bitsPerSample+shift >= 32) { - pSamplesOut[i] = decodedRice + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + i); + if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + pSamplesOut[i] = decodedRice + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); } else { - pSamplesOut[i] = decodedRice + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + i); + pSamplesOut[i] = decodedRice + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); } } return DRFLAC_TRUE; @@ -59287,6 +80549,9 @@ static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts(drflac_bs* bs, drflac if (!drflac__reload_cache(bs)) { return DRFLAC_FALSE; } + if (bitCountLo > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + return DRFLAC_FALSE; + } } riceParamPart = (drflac_uint32)(resultHi | DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, bitCountLo)); bs->consumedBits += bitCountLo; @@ -59334,6 +80599,9 @@ static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts_x1(drflac_bs* bs, drf if (!drflac__reload_cache(bs)) { return DRFLAC_FALSE; } + if (riceParamPartLoBitCount > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + return DRFLAC_FALSE; + } bs_cache = bs->cache; bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount; } @@ -59403,6 +80671,9 @@ static DRFLAC_INLINE drflac_bool32 drflac__seek_rice_parts(drflac_bs* bs, drflac if (!drflac__reload_cache(bs)) { return DRFLAC_FALSE; } + if (riceParamPartLoBitCount > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + return DRFLAC_FALSE; + } bs_cache = bs->cache; bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount; } @@ -59464,7 +80735,7 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar_zeroorde } return DRFLAC_TRUE; } -static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; drflac_uint32 zeroCountPart0 = 0; @@ -59480,12 +80751,12 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar(drflac_b drflac_uint32 i; DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(pSamplesOut != NULL); - if (order == 0) { - return drflac__decode_samples_with_residual__rice__scalar_zeroorder(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut); + if (lpcOrder == 0) { + return drflac__decode_samples_with_residual__rice__scalar_zeroorder(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); } riceParamMask = (drflac_uint32)~((~0UL) << riceParam); pSamplesOutEnd = pSamplesOut + (count & ~3); - if (bitsPerSample+shift > 32) { + if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { while (pSamplesOut < pSamplesOutEnd) { if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) || @@ -59505,10 +80776,10 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar(drflac_b riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01]; riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01]; riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01]; - pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 0); - pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 1); - pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 2); - pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 3); + pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); + pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); + pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); + pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); pSamplesOut += 4; } } else { @@ -59531,10 +80802,10 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar(drflac_b riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01]; riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01]; riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01]; - pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 0); - pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 1); - pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 2); - pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 3); + pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); + pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); + pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); + pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); pSamplesOut += 4; } } @@ -59546,10 +80817,10 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar(drflac_b riceParamPart0 &= riceParamMask; riceParamPart0 |= (zeroCountPart0 << riceParam); riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; - if (bitsPerSample+shift > 32) { - pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 0); + if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); } else { - pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 0); + pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); } i += 1; pSamplesOut += 1; @@ -59894,18 +81165,18 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_64(drflac } return DRFLAC_TRUE; } -static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(pSamplesOut != NULL); - if (order > 0 && order <= 12) { - if (bitsPerSample+shift > 32) { - return drflac__decode_samples_with_residual__rice__sse41_64(bs, count, riceParam, order, shift, coefficients, pSamplesOut); + if (lpcOrder > 0 && lpcOrder <= 12) { + if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + return drflac__decode_samples_with_residual__rice__sse41_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); } else { - return drflac__decode_samples_with_residual__rice__sse41_32(bs, count, riceParam, order, shift, coefficients, pSamplesOut); + return drflac__decode_samples_with_residual__rice__sse41_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); } } else { - return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut); + return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); } } #endif @@ -60244,37 +81515,37 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_64(drflac_ } return DRFLAC_TRUE; } -static drflac_bool32 drflac__decode_samples_with_residual__rice__neon(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +static drflac_bool32 drflac__decode_samples_with_residual__rice__neon(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(pSamplesOut != NULL); - if (order > 0 && order <= 12) { - if (bitsPerSample+shift > 32) { - return drflac__decode_samples_with_residual__rice__neon_64(bs, count, riceParam, order, shift, coefficients, pSamplesOut); + if (lpcOrder > 0 && lpcOrder <= 12) { + if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + return drflac__decode_samples_with_residual__rice__neon_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); } else { - return drflac__decode_samples_with_residual__rice__neon_32(bs, count, riceParam, order, shift, coefficients, pSamplesOut); + return drflac__decode_samples_with_residual__rice__neon_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); } } else { - return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut); + return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); } } #endif -static drflac_bool32 drflac__decode_samples_with_residual__rice(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +static drflac_bool32 drflac__decode_samples_with_residual__rice(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { #if defined(DRFLAC_SUPPORT_SSE41) if (drflac__gIsSSE41Supported) { - return drflac__decode_samples_with_residual__rice__sse41(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut); + return drflac__decode_samples_with_residual__rice__sse41(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); } else #elif defined(DRFLAC_SUPPORT_NEON) if (drflac__gIsNEONSupported) { - return drflac__decode_samples_with_residual__rice__neon(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut); + return drflac__decode_samples_with_residual__rice__neon(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); } else #endif { #if 0 - return drflac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut); + return drflac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); #else - return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut); + return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); #endif } } @@ -60289,7 +81560,10 @@ static drflac_bool32 drflac__read_and_seek_residual__rice(drflac_bs* bs, drflac_ } return DRFLAC_TRUE; } -static drflac_bool32 drflac__decode_samples_with_residual__unencoded(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 unencodedBitsPerSample, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +#if defined(__clang__) +__attribute__((no_sanitize("signed-integer-overflow"))) +#endif +static drflac_bool32 drflac__decode_samples_with_residual__unencoded(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 unencodedBitsPerSample, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { drflac_uint32 i; DRFLAC_ASSERT(bs != NULL); @@ -60303,15 +81577,15 @@ static drflac_bool32 drflac__decode_samples_with_residual__unencoded(drflac_bs* } else { pSamplesOut[i] = 0; } - if (bitsPerSample >= 24) { - pSamplesOut[i] += drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + i); + if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + pSamplesOut[i] += drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); } else { - pSamplesOut[i] += drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + i); + pSamplesOut[i] += drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); } } return DRFLAC_TRUE; } -static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 blockSize, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) +static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 blockSize, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) { drflac_uint8 residualMethod; drflac_uint8 partitionOrder; @@ -60326,17 +81600,17 @@ static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_ if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { return DRFLAC_FALSE; } - pDecodedSamples += order; + pDecodedSamples += lpcOrder; if (!drflac__read_uint8(bs, 4, &partitionOrder)) { return DRFLAC_FALSE; } if (partitionOrder > 8) { return DRFLAC_FALSE; } - if ((blockSize / (1 << partitionOrder)) < order) { + if ((blockSize / (1 << partitionOrder)) < lpcOrder) { return DRFLAC_FALSE; } - samplesInPartition = (blockSize / (1 << partitionOrder)) - order; + samplesInPartition = (blockSize / (1 << partitionOrder)) - lpcOrder; partitionsRemaining = (1 << partitionOrder); for (;;) { drflac_uint8 riceParam = 0; @@ -60356,7 +81630,7 @@ static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_ } } if (riceParam != 0xFF) { - if (!drflac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, order, shift, coefficients, pDecodedSamples)) { + if (!drflac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { return DRFLAC_FALSE; } } else { @@ -60364,7 +81638,7 @@ static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_ if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) { return DRFLAC_FALSE; } - if (!drflac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, order, shift, coefficients, pDecodedSamples)) { + if (!drflac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { return DRFLAC_FALSE; } } @@ -60484,7 +81758,7 @@ static drflac_bool32 drflac__decode_samples__fixed(drflac_bs* bs, drflac_uint32 } pDecodedSamples[i] = sample; } - if (!drflac__decode_samples_with_residual(bs, subframeBitsPerSample, blockSize, lpcOrder, 0, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) { + if (!drflac__decode_samples_with_residual(bs, subframeBitsPerSample, blockSize, lpcOrder, 0, 4, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) { return DRFLAC_FALSE; } return DRFLAC_TRUE; @@ -60521,7 +81795,7 @@ static drflac_bool32 drflac__decode_samples__lpc(drflac_bs* bs, drflac_uint32 bl return DRFLAC_FALSE; } } - if (!drflac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, coefficients, pDecodedSamples)) { + if (!drflac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { return DRFLAC_FALSE; } return DRFLAC_TRUE; @@ -60630,6 +81904,9 @@ static drflac_bool32 drflac__read_next_flac_frame_header(drflac_bs* bs, drflac_u return DRFLAC_FALSE; } crc8 = drflac_crc8(crc8, header->blockSizeInPCMFrames, 16); + if (header->blockSizeInPCMFrames == 0xFFFF) { + return DRFLAC_FALSE; + } header->blockSizeInPCMFrames += 1; } else { DRFLAC_ASSERT(blockSize >= 8); @@ -60662,6 +81939,9 @@ static drflac_bool32 drflac__read_next_flac_frame_header(drflac_bs* bs, drflac_u if (header->bitsPerSample == 0) { header->bitsPerSample = streaminfoBitsPerSample; } + if (header->bitsPerSample != streaminfoBitsPerSample) { + return DRFLAC_FALSE; + } if (!drflac__read_uint8(bs, 8, &header->crc8)) { return DRFLAC_FALSE; } @@ -60732,6 +82012,9 @@ static drflac_bool32 drflac__decode_subframe(drflac_bs* bs, drflac_frame* frame, } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { subframeBitsPerSample += 1; } + if (subframeBitsPerSample > 32) { + return DRFLAC_FALSE; + } if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) { return DRFLAC_FALSE; } @@ -61215,6 +82498,9 @@ static drflac_bool32 drflac__seek_to_pcm_frame__seek_table(drflac* pFlac, drflac if (pFlac->pSeekpoints == NULL || pFlac->seekpointCount == 0) { return DRFLAC_FALSE; } + if (pFlac->pSeekpoints[0].firstPCMFrame > pcmFrameIndex) { + return DRFLAC_FALSE; + } for (iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) { if (pFlac->pSeekpoints[iSeekpoint].firstPCMFrame >= pcmFrameIndex) { break; @@ -61558,13 +82844,13 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d metadata.rawDataSize = blockSize; pRunningData = (const char*)pRawData; pRunningDataEnd = (const char*)pRawData + blockSize; - metadata.data.vorbis_comment.vendorLength = drflac__le2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.vorbis_comment.vendorLength = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; if ((pRunningDataEnd - pRunningData) - 4 < (drflac_int64)metadata.data.vorbis_comment.vendorLength) { drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; } metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength; - metadata.data.vorbis_comment.commentCount = drflac__le2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.vorbis_comment.commentCount = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; if ((pRunningDataEnd - pRunningData) / sizeof(drflac_uint32) < metadata.data.vorbis_comment.commentCount) { drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; @@ -61576,7 +82862,7 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; } - commentLength = drflac__le2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; + commentLength = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; if (pRunningDataEnd - pRunningData < (drflac_int64)commentLength) { drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; @@ -61660,24 +82946,24 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d metadata.rawDataSize = blockSize; pRunningData = (const char*)pRawData; pRunningDataEnd = (const char*)pRawData + blockSize; - metadata.data.picture.type = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; - metadata.data.picture.mimeLength = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.picture.type = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.mimeLength = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; if ((pRunningDataEnd - pRunningData) - 24 < (drflac_int64)metadata.data.picture.mimeLength) { drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; } metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength; - metadata.data.picture.descriptionLength = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.picture.descriptionLength = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; if ((pRunningDataEnd - pRunningData) - 20 < (drflac_int64)metadata.data.picture.descriptionLength) { drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; } metadata.data.picture.description = pRunningData; pRunningData += metadata.data.picture.descriptionLength; - metadata.data.picture.width = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; - metadata.data.picture.height = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; - metadata.data.picture.colorDepth = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; - metadata.data.picture.indexColorCount = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; - metadata.data.picture.pictureDataSize = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.picture.width = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.height = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.colorDepth = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.indexColorCount = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.pictureDataSize = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; metadata.data.picture.pPictureData = (const drflac_uint8*)pRunningData; if (pRunningDataEnd - pRunningData < (drflac_int64)metadata.data.picture.pictureDataSize) { drflac__free_from_callbacks(pRawData, pAllocationCallbacks); @@ -62563,7 +83849,7 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac #ifndef DR_FLAC_NO_OGG if (init.container == drflac_container_ogg) { drflac_oggbs* pInternalOggbs = (drflac_oggbs*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + seektableSize); - *pInternalOggbs = oggbs; + DRFLAC_COPY_MEMORY(pInternalOggbs, &oggbs, sizeof(oggbs)); pFlac->bs.onRead = drflac__on_read_ogg; pFlac->bs.onSeek = drflac__on_seek_ogg; pFlac->bs.pUserData = (void*)pInternalOggbs; @@ -65798,7 +87084,7 @@ DRFLAC_API const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) { return NULL; } - length = drflac__le2host_32(*(const drflac_uint32*)pIter->pRunningData); + length = drflac__le2host_32_ptr_unaligned(pIter->pRunningData); pIter->pRunningData += 4; pComment = pIter->pRunningData; pIter->pRunningData += length; @@ -67301,7 +88587,11 @@ static void drmp3d_synth(float *xl, drmp3d_sample_t *dstl, int nch, float *lins) vst1_lane_s16(dstl + (49 + i)*nch, pcmb, 2); #endif #else + #if DRMP3_HAVE_SSE static const drmp3_f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f }; + #else + const drmp3_f4 g_scale = vdupq_n_f32(1.0f/32768.0f); + #endif a = DRMP3_VMUL(a, g_scale); b = DRMP3_VMUL(b, g_scale); #if DRMP3_HAVE_SSE @@ -67575,7 +88865,6 @@ DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num } } } -#include #if defined(SIZE_MAX) #define DRMP3_SIZE_MAX SIZE_MAX #else @@ -67621,18 +88910,6 @@ static DRMP3_INLINE drmp3_uint32 drmp3_gcf_u32(drmp3_uint32 a, drmp3_uint32 b) } return a; } -static DRMP3_INLINE double drmp3_sin(double x) -{ - return sin(x); -} -static DRMP3_INLINE double drmp3_exp(double x) -{ - return exp(x); -} -static DRMP3_INLINE double drmp3_cos(double x) -{ - return drmp3_sin((DRMP3_PI_D*0.5) - x); -} static void* drmp3__malloc_default(size_t sz, void* pUserData) { (void)pUserData; @@ -69134,1093 +90411,6 @@ DRMP3_API void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocation #endif /* miniaudio_c */ #endif /* MINIAUDIO_IMPLEMENTATION */ -/* -RELEASE NOTES - VERSION 0.10.x -============================== -Version 0.10 includes major API changes and refactoring, mostly concerned with the data conversion system. Data conversion is performed internally to convert -audio data between the format requested when initializing the `ma_device` object and the format of the internal device used by the backend. The same applies -to the `ma_decoder` object. The previous design has several design flaws and missing features which necessitated a complete redesign. - - -Changes to Data Conversion --------------------------- -The previous data conversion system used callbacks to deliver input data for conversion. This design works well in some specific situations, but in other -situations it has some major readability and maintenance issues. The decision was made to replace this with a more iterative approach where you just pass in a -pointer to the input data directly rather than dealing with a callback. - -The following are the data conversion APIs that have been removed and their replacements: - - - ma_format_converter -> ma_convert_pcm_frames_format() - - ma_channel_router -> ma_channel_converter - - ma_src -> ma_resampler - - ma_pcm_converter -> ma_data_converter - -The previous conversion APIs accepted a callback in their configs. There are no longer any callbacks to deal with. Instead you just pass the data into the -`*_process_pcm_frames()` function as a pointer to a buffer. - -The simplest aspect of data conversion is sample format conversion. To convert between two formats, just call `ma_convert_pcm_frames_format()`. Channel -conversion is also simple which you can do with `ma_channel_converter` via `ma_channel_converter_process_pcm_frames()`. - -Resampling is more complicated because the number of output frames that are processed is different to the number of input frames that are consumed. When you -call `ma_resampler_process_pcm_frames()` you need to pass in the number of input frames available for processing and the number of output frames you want to -output. Upon returning they will receive the number of input frames that were consumed and the number of output frames that were generated. - -The `ma_data_converter` API is a wrapper around format, channel and sample rate conversion and handles all of the data conversion you'll need which probably -makes it the best option if you need to do data conversion. - -In addition to changes to the API design, a few other changes have been made to the data conversion pipeline: - - - The sinc resampler has been removed. This was completely broken and never actually worked properly. - - The linear resampler now uses low-pass filtering to remove aliasing. The quality of the low-pass filter can be controlled via the resampler config with the - `lpfOrder` option, which has a maximum value of MA_MAX_FILTER_ORDER. - - Data conversion now supports s16 natively which runs through a fixed point pipeline. Previously everything needed to be converted to floating point before - processing, whereas now both s16 and f32 are natively supported. Other formats still require conversion to either s16 or f32 prior to processing, however - `ma_data_converter` will handle this for you. - - -Custom Memory Allocators ------------------------- -miniaudio has always supported macro level customization for memory allocation via MA_MALLOC, MA_REALLOC and MA_FREE, however some scenarios require more -flexibility by allowing a user data pointer to be passed to the custom allocation routines. Support for this has been added to version 0.10 via the -`ma_allocation_callbacks` structure. Anything making use of heap allocations has been updated to accept this new structure. - -The `ma_context_config` structure has been updated with a new member called `allocationCallbacks`. Leaving this set to it's defaults returned by -`ma_context_config_init()` will cause it to use MA_MALLOC, MA_REALLOC and MA_FREE. Likewise, The `ma_decoder_config` structure has been updated in the same -way, and leaving everything as-is after `ma_decoder_config_init()` will cause it to use the same defaults. - -The following APIs have been updated to take a pointer to a `ma_allocation_callbacks` object. Setting this parameter to NULL will cause it to use defaults. -Otherwise they will use the relevant callback in the structure. - - - ma_malloc() - - ma_realloc() - - ma_free() - - ma_aligned_malloc() - - ma_aligned_free() - - ma_rb_init() / ma_rb_init_ex() - - ma_pcm_rb_init() / ma_pcm_rb_init_ex() - -Note that you can continue to use MA_MALLOC, MA_REALLOC and MA_FREE as per normal. These will continue to be used by default if you do not specify custom -allocation callbacks. - - -Buffer and Period Configuration Changes ---------------------------------------- -The way in which the size of the internal buffer and periods are specified in the device configuration have changed. In previous versions, the config variables -`bufferSizeInFrames` and `bufferSizeInMilliseconds` defined the size of the entire buffer, with the size of a period being the size of this variable divided by -the period count. This became confusing because people would expect the value of `bufferSizeInFrames` or `bufferSizeInMilliseconds` to independantly determine -latency, when in fact it was that value divided by the period count that determined it. These variables have been removed and replaced with new ones called -`periodSizeInFrames` and `periodSizeInMilliseconds`. - -These new configuration variables work in the same way as their predecessors in that if one is set to 0, the other will be used, but the main difference is -that you now set these to you desired latency rather than the size of the entire buffer. The benefit of this is that it's much easier and less confusing to -configure latency. - -The following unused APIs have been removed: - - ma_get_default_buffer_size_in_milliseconds() - ma_get_default_buffer_size_in_frames() - -The following macros have been removed: - - MA_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY - MA_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE - - -Other API Changes ------------------ -Other less major API changes have also been made in version 0.10. - -`ma_device_set_stop_callback()` has been removed. If you require a stop callback, you must now set it via the device config just like the data callback. - -The `ma_sine_wave` API has been replaced with a more general API called `ma_waveform`. This supports generation of different types of waveforms, including -sine, square, triangle and sawtooth. Use `ma_waveform_init()` in place of `ma_sine_wave_init()` to initialize the waveform object. This takes a configuration -object called `ma_waveform_config` which defines the properties of the waveform. Use `ma_waveform_config_init()` to initialize a `ma_waveform_config` object. -Use `ma_waveform_read_pcm_frames()` in place of `ma_sine_wave_read_f32()` and `ma_sine_wave_read_f32_ex()`. - -`ma_convert_frames()` and `ma_convert_frames_ex()` have been changed. Both of these functions now take a new parameter called `frameCountOut` which specifies -the size of the output buffer in PCM frames. This has been added for safety. In addition to this, the parameters for `ma_convert_frames_ex()` have changed to -take a pointer to a `ma_data_converter_config` object to specify the input and output formats to convert between. This was done to make it more flexible, to -prevent the parameter list getting too long, and to prevent API breakage whenever a new conversion property is added. - -`ma_calculate_frame_count_after_src()` has been renamed to `ma_calculate_frame_count_after_resampling()` for consistency with the new `ma_resampler` API. - - -Filters -------- -The following filters have been added: - - |-------------|-------------------------------------------------------------------| - | API | Description | - |-------------|-------------------------------------------------------------------| - | ma_biquad | Biquad filter (transposed direct form 2) | - | ma_lpf1 | First order low-pass filter | - | ma_lpf2 | Second order low-pass filter | - | ma_lpf | High order low-pass filter (Butterworth) | - | ma_hpf1 | First order high-pass filter | - | ma_hpf2 | Second order high-pass filter | - | ma_hpf | High order high-pass filter (Butterworth) | - | ma_bpf2 | Second order band-pass filter | - | ma_bpf | High order band-pass filter | - | ma_peak2 | Second order peaking filter | - | ma_notch2 | Second order notching filter | - | ma_loshelf2 | Second order low shelf filter | - | ma_hishelf2 | Second order high shelf filter | - |-------------|-------------------------------------------------------------------| - -These filters all support 32-bit floating point and 16-bit signed integer formats natively. Other formats need to be converted beforehand. - - -Sine, Square, Triangle and Sawtooth Waveforms ---------------------------------------------- -Previously miniaudio supported only sine wave generation. This has now been generalized to support sine, square, triangle and sawtooth waveforms. The old -`ma_sine_wave` API has been removed and replaced with the `ma_waveform` API. Use `ma_waveform_config_init()` to initialize a config object, and then pass it -into `ma_waveform_init()`. Then use `ma_waveform_read_pcm_frames()` to read PCM data. - - -Noise Generation ----------------- -A noise generation API has been added. This is used via the `ma_noise` API. Currently white, pink and Brownian noise is supported. The `ma_noise` API is -similar to the waveform API. Use `ma_noise_config_init()` to initialize a config object, and then pass it into `ma_noise_init()` to initialize a `ma_noise` -object. Then use `ma_noise_read_pcm_frames()` to read PCM data. - - -Miscellaneous Changes ---------------------- -The MA_NO_STDIO option has been removed. This would disable file I/O APIs, however this has proven to be too hard to maintain for it's perceived value and was -therefore removed. - -Internal functions have all been made static where possible. If you get warnings about unused functions, please submit a bug report. - -The `ma_device` structure is no longer defined as being aligned to MA_SIMD_ALIGNMENT. This resulted in a possible crash when allocating a `ma_device` object on -the heap, but not aligning it to MA_SIMD_ALIGNMENT. This crash would happen due to the compiler seeing the alignment specified on the structure and assuming it -was always aligned as such and thinking it was safe to emit alignment-dependant SIMD instructions. Since miniaudio's philosophy is for things to just work, -this has been removed from all structures. - -Results codes have been overhauled. Unnecessary result codes have been removed, and some have been renumbered for organisation purposes. If you are are binding -maintainer you will need to update your result codes. Support has also been added for retrieving a human readable description of a given result code via the -`ma_result_description()` API. - -ALSA: The automatic format conversion, channel conversion and resampling performed by ALSA is now disabled by default as they were causing some compatibility -issues with certain devices and configurations. These can be individually enabled via the device config: - - ```c - deviceConfig.alsa.noAutoFormat = MA_TRUE; - deviceConfig.alsa.noAutoChannels = MA_TRUE; - deviceConfig.alsa.noAutoResample = MA_TRUE; - ``` -*/ - -/* -RELEASE NOTES - VERSION 0.9.x -============================= -Version 0.9 includes major API changes, centered mostly around full-duplex and the rebrand to "miniaudio". Before I go into detail about the major changes I -would like to apologize. I know it's annoying dealing with breaking API changes, but I think it's best to get these changes out of the way now while the -library is still relatively young and unknown. - -There's been a lot of refactoring with this release so there's a good chance a few bugs have been introduced. I apologize in advance for this. You may want to -hold off on upgrading for the short term if you're worried. If mini_al v0.8.14 works for you, and you don't need full-duplex support, you can avoid upgrading -(though you won't be getting future bug fixes). - - -Rebranding to "miniaudio" -------------------------- -The decision was made to rename mini_al to miniaudio. Don't worry, it's the same project. The reason for this is simple: - -1) Having the word "audio" in the title makes it immediately clear that the library is related to audio; and -2) I don't like the look of the underscore. - -This rebrand has necessitated a change in namespace from "mal" to "ma". I know this is annoying, and I apologize, but it's better to get this out of the road -now rather than later. Also, since there are necessary API changes for full-duplex support I think it's better to just get the namespace change over and done -with at the same time as the full-duplex changes. I'm hoping this will be the last of the major API changes. Fingers crossed! - -The implementation define is now "#define MINIAUDIO_IMPLEMENTATION". You can also use "#define MA_IMPLEMENTATION" if that's your preference. - - -Full-Duplex Support -------------------- -The major feature added to version 0.9 is full-duplex. This has necessitated a few API changes. - -1) The data callback has now changed. Previously there was one type of callback for playback and another for capture. I wanted to avoid a third callback just - for full-duplex so the decision was made to break this API and unify the callbacks. Now, there is just one callback which is the same for all three modes - (playback, capture, duplex). The new callback looks like the following: - - void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); - - This callback allows you to move data straight out of the input buffer and into the output buffer in full-duplex mode. In playback-only mode, pInput will be - null. Likewise, pOutput will be null in capture-only mode. The sample count is no longer returned from the callback since it's not necessary for miniaudio - anymore. - -2) The device config needed to change in order to support full-duplex. Full-duplex requires the ability to allow the client to choose a different PCM format - for the playback and capture sides. The old ma_device_config object simply did not allow this and needed to change. With these changes you now specify the - device ID, format, channels, channel map and share mode on a per-playback and per-capture basis (see example below). The sample rate must be the same for - playback and capture. - - Since the device config API has changed I have also decided to take the opportunity to simplify device initialization. Now, the device ID, device type and - callback user data are set in the config. ma_device_init() is now simplified down to taking just the context, device config and a pointer to the device - object being initialized. The rationale for this change is that it just makes more sense to me that these are set as part of the config like everything - else. - - Example device initialization: - - ma_device_config config = ma_device_config_init(ma_device_type_duplex); // Or ma_device_type_playback or ma_device_type_capture. - config.playback.pDeviceID = &myPlaybackDeviceID; // Or NULL for the default playback device. - config.playback.format = ma_format_f32; - config.playback.channels = 2; - config.capture.pDeviceID = &myCaptureDeviceID; // Or NULL for the default capture device. - config.capture.format = ma_format_s16; - config.capture.channels = 1; - config.sampleRate = 44100; - config.dataCallback = data_callback; - config.pUserData = &myUserData; - - result = ma_device_init(&myContext, &config, &device); - if (result != MA_SUCCESS) { - ... handle error ... - } - - Note that the "onDataCallback" member of ma_device_config has been renamed to "dataCallback". Also, "onStopCallback" has been renamed to "stopCallback". - -This is the first pass for full-duplex and there is a known bug. You will hear crackling on the following backends when sample rate conversion is required for -the playback device: - - Core Audio - - JACK - - AAudio - - OpenSL - - WebAudio - -In addition to the above, not all platforms have been absolutely thoroughly tested simply because I lack the hardware for such thorough testing. If you -experience a bug, an issue report on GitHub or an email would be greatly appreciated (and a sample program that reproduces the issue if possible). - - -Other API Changes ------------------ -In addition to the above, the following API changes have been made: - -- The log callback is no longer passed to ma_context_config_init(). Instead you need to set it manually after initialization. -- The onLogCallback member of ma_context_config has been renamed to "logCallback". -- The log callback now takes a logLevel parameter. The new callback looks like: void log_callback(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message) - - You can use ma_log_level_to_string() to convert the logLevel to human readable text if you want to log it. -- Some APIs have been renamed: - - mal_decoder_read() -> ma_decoder_read_pcm_frames() - - mal_decoder_seek_to_frame() -> ma_decoder_seek_to_pcm_frame() - - mal_sine_wave_read() -> ma_sine_wave_read_f32() - - mal_sine_wave_read_ex() -> ma_sine_wave_read_f32_ex() -- Some APIs have been removed: - - mal_device_get_buffer_size_in_bytes() - - mal_device_set_recv_callback() - - mal_device_set_send_callback() - - mal_src_set_input_sample_rate() - - mal_src_set_output_sample_rate() -- Error codes have been rearranged. If you're a binding maintainer you will need to update. -- The ma_backend enums have been rearranged to priority order. The rationale for this is to simplify automatic backend selection and to make it easier to see - the priority. If you're a binding maintainer you will need to update. -- ma_dsp has been renamed to ma_pcm_converter. The rationale for this change is that I'm expecting "ma_dsp" to conflict with some future planned high-level - APIs. -- For functions that take a pointer/count combo, such as ma_decoder_read_pcm_frames(), the parameter order has changed so that the pointer comes before the - count. The rationale for this is to keep it consistent with things like memcpy(). - - -Miscellaneous Changes ---------------------- -The following miscellaneous changes have also been made. - -- The AAudio backend has been added for Android 8 and above. This is Android's new "High-Performance Audio" API. (For the record, this is one of the nicest - audio APIs out there, just behind the BSD audio APIs). -- The WebAudio backend has been added. This is based on ScriptProcessorNode. This removes the need for SDL. -- The SDL and OpenAL backends have been removed. These were originally implemented to add support for platforms for which miniaudio was not explicitly - supported. These are no longer needed and have therefore been removed. -- Device initialization now fails if the requested share mode is not supported. If you ask for exclusive mode, you either get an exclusive mode device, or an - error. The rationale for this change is to give the client more control over how to handle cases when the desired shared mode is unavailable. -- A lock-free ring buffer API has been added. There are two varients of this. "ma_rb" operates on bytes, whereas "ma_pcm_rb" operates on PCM frames. -- The library is now licensed as a choice of Public Domain (Unlicense) _or_ MIT-0 (No Attribution) which is the same as MIT, but removes the attribution - requirement. The rationale for this is to support countries that don't recognize public domain. -*/ - -/* -REVISION HISTORY -================ -v0.10.42 - 2021-08-22 - - Fix a possible deadlock when stopping devices. - -v0.10.41 - 2021-08-15 - - Core Audio: Fix some deadlock errors. - -v0.10.40 - 2021-07-23 - - Fix a bug when converting from stereo to mono. - - PulseAudio: Fix a glitch when pausing and resuming a device. - -v0.10.39 - 2021-07-20 - - Core Audio: Fix a deadlock when the default device is changed. - - Core Audio: Fix compilation errors on macOS and iOS. - - PulseAudio: Fix a bug where the stop callback is not fired when a device is unplugged. - - PulseAudio: Fix a null pointer dereference. - -v0.10.38 - 2021-07-14 - - Fix a linking error when MA_DEBUG_OUTPUT is not enabled. - - Fix an error where ma_log_postv() does not return a value. - - OpenSL: Fix a bug with setting of stream types and recording presets. - -0.10.37 - 2021-07-06 - - Fix a bug with log message formatting. - - Fix build when compiling with MA_NO_THREADING. - - Minor updates to channel mapping. - -0.10.36 - 2021-07-03 - - Add support for custom decoding backends. - - Fix some bugs with the Vorbis decoder. - - PulseAudio: Fix a bug with channel mapping. - - PulseAudio: Fix a bug where miniaudio does not fall back to a supported format when PulseAudio - defaults to a format not known to miniaudio. - - OpenSL: Fix a crash when initializing a capture device when a recording preset other than the - default is specified. - - Silence some warnings when compiling with MA_DEBUG_OUTPUT - - Improvements to logging. See the `ma_log` API for details. The logCallback variable used by - ma_context has been deprecated and will be replaced with the new system in version 0.11. - - Initialize an `ma_log` object with `ma_log_init()`. - - Register a callback with `ma_log_register_callback()`. - - In the context config, set `pLog` to your `ma_log` object and stop using `logCallback`. - - Prep work for some upcoming changes to data sources. These changes are still compatible with - existing code, however code will need to be updated in preparation for version 0.11 which will - be breaking. You should make these changes now for any custom data sources: - - Change your base data source object from `ma_data_source_callbacks` to `ma_data_source_base`. - - Call `ma_data_source_init()` for your base object in your custom data source's initialization - routine. This takes a config object which includes a pointer to a vtable which is now where - your custom callbacks are defined. - - Call `ma_data_source_uninit()` in your custom data source's uninitialization routine. This - doesn't currently do anything, but it placeholder in case some future uninitialization code - is required to be added at a later date. - -v0.10.35 - 2021-04-27 - - Fix the C++ build. - -v0.10.34 - 2021-04-26 - - WASAPI: Fix a bug where a result code is not getting checked at initialization time. - - WASAPI: Bug fixes for loopback mode. - - ALSA: Fix a possible deadlock when stopping devices. - - Mark devices as default on the null backend. - -v0.10.33 - 2021-04-04 - - Core Audio: Fix a memory leak. - - Core Audio: Fix a bug where the performance profile is not being used by playback devices. - - JACK: Fix loading of 64-bit JACK on Windows. - - Fix a calculation error and add a safety check to the following APIs to prevent a division by zero: - - ma_calculate_buffer_size_in_milliseconds_from_frames() - - ma_calculate_buffer_size_in_frames_from_milliseconds() - - Fix compilation errors relating to c89atomic. - - Update FLAC decoder. - -v0.10.32 - 2021-02-23 - - WASAPI: Fix a deadlock in exclusive mode. - - WASAPI: No longer return an error from ma_context_get_device_info() when an exclusive mode format - cannot be retrieved. - - WASAPI: Attempt to fix some bugs with device uninitialization. - - PulseAudio: Yet another refactor, this time to remove the dependency on `pa_threaded_mainloop`. - - Web Audio: Fix a bug on Chrome and any other browser using the same engine. - - Web Audio: Automatically start the device on some user input if the device has been started. This - is to work around Google's policy of not starting audio if no user input has yet been performed. - - Fix a bug where thread handles are not being freed. - - Fix some static analysis warnings in FLAC, WAV and MP3 decoders. - - Fix a warning due to referencing _MSC_VER when it is undefined. - - Update to latest version of c89atomic. - - Internal refactoring to migrate over to the new backend callback system for the following backends: - - PulseAudio - - ALSA - - Core Audio - - AAudio - - OpenSL|ES - - OSS - - audio(4) - - sndio - -v0.10.31 - 2021-01-17 - - Make some functions const correct. - - Update ma_data_source_read_pcm_frames() to initialize pFramesRead to 0 for safety. - - Add the MA_ATOMIC annotation for use with variables that should be used atomically and remove unnecessary volatile qualifiers. - - Add support for enabling only specific backends at compile time. This is the reverse of the pre-existing system. With the new - system, all backends are first disabled with `MA_ENABLE_ONLY_SPECIFIC_BACKENDS`, which is then followed with `MA_ENABLE_*`. The - old system where you disable backends with `MA_NO_*` still exists and is still the default. - -v0.10.30 - 2021-01-10 - - Fix a crash in ma_audio_buffer_read_pcm_frames(). - - Update spinlock APIs to take a volatile parameter as input. - - Silence some unused parameter warnings. - - Fix a warning on GCC when compiling as C++. - -v0.10.29 - 2020-12-26 - - Fix some subtle multi-threading bugs on non-x86 platforms. - - Fix a bug resulting in superfluous memory allocations when enumerating devices. - - Core Audio: Fix a compilation error when compiling for iOS. - -v0.10.28 - 2020-12-16 - - Fix a crash when initializing a POSIX thread. - - OpenSL|ES: Respect the MA_NO_RUNTIME_LINKING option. - -v0.10.27 - 2020-12-04 - - Add support for dynamically configuring some properties of `ma_noise` objects post-initialization. - - Add support for configuring the channel mixing mode in the device config. - - Fix a bug with simple channel mixing mode (drop or silence excess channels). - - Fix some bugs with trying to access uninitialized variables. - - Fix some errors with stopping devices for synchronous backends where the backend's stop callback would get fired twice. - - Fix a bug in the decoder due to using an uninitialized variable. - - Fix some data race errors. - -v0.10.26 - 2020-11-24 - - WASAPI: Fix a bug where the exclusive mode format may not be retrieved correctly due to accessing freed memory. - - Fix a bug with ma_waveform where glitching occurs after changing frequency. - - Fix compilation with OpenWatcom. - - Fix compilation with TCC. - - Fix compilation with Digital Mars. - - Fix compilation warnings. - - Remove bitfields from public structures to aid in binding maintenance. - -v0.10.25 - 2020-11-15 - - PulseAudio: Fix a bug where the stop callback isn't fired. - - WebAudio: Fix an error that occurs when Emscripten increases the size of it's heap. - - Custom Backends: Change the onContextInit and onDeviceInit callbacks to take a parameter which is a pointer to the config that was - passed into ma_context_init() and ma_device_init(). This replaces the deviceType parameter of onDeviceInit. - - Fix compilation warnings on older versions of GCC. - -v0.10.24 - 2020-11-10 - - Fix a bug where initialization of a backend can fail due to some bad state being set from a prior failed attempt at initializing a - lower priority backend. - -v0.10.23 - 2020-11-09 - - AAudio: Add support for configuring a playback stream's usage. - - Fix a compilation error when all built-in asynchronous backends are disabled at compile time. - - Fix compilation errors when compiling as C++. - -v0.10.22 - 2020-11-08 - - Add support for custom backends. - - Add support for detecting default devices during device enumeration and with `ma_context_get_device_info()`. - - Refactor to the PulseAudio backend. This simplifies the implementation and fixes a capture bug. - - ALSA: Fix a bug in `ma_context_get_device_info()` where the PCM handle is left open in the event of an error. - - Core Audio: Further improvements to sample rate selection. - - Core Audio: Fix some bugs with capture mode. - - OpenSL: Add support for configuring stream types and recording presets. - - AAudio: Add support for configuring content types and input presets. - - Fix bugs in `ma_decoder_init_file*()` where the file handle is not closed after a decoding error. - - Fix some compilation warnings on GCC and Clang relating to the Speex resampler. - - Fix a compilation error for the Linux build when the ALSA and JACK backends are both disabled. - - Fix a compilation error for the BSD build. - - Fix some compilation errors on older versions of GCC. - - Add documentation for `MA_NO_RUNTIME_LINKING`. - -v0.10.21 - 2020-10-30 - - Add ma_is_backend_enabled() and ma_get_enabled_backends() for retrieving enabled backends at run-time. - - WASAPI: Fix a copy and paste bug relating to loopback mode. - - Core Audio: Fix a bug when using multiple contexts. - - Core Audio: Fix a compilation warning. - - Core Audio: Improvements to sample rate selection. - - Core Audio: Improvements to format/channels/rate selection when requesting defaults. - - Core Audio: Add notes regarding the Apple notarization process. - - Fix some bugs due to null pointer dereferences. - -v0.10.20 - 2020-10-06 - - Fix build errors with UWP. - - Minor documentation updates. - -v0.10.19 - 2020-09-22 - - WASAPI: Return an error when exclusive mode is requested, but the native format is not supported by miniaudio. - - Fix a bug where ma_decoder_seek_to_pcm_frames() never returns MA_SUCCESS even though it was successful. - - Store the sample rate in the `ma_lpf` and `ma_hpf` structures. - -v0.10.18 - 2020-08-30 - - Fix build errors with VC6. - - Fix a bug in channel converter for s32 format. - - Change channel converter configs to use the default channel map instead of a blank channel map when no channel map is specified when initializing the - config. This fixes an issue where the optimized mono expansion path would never get used. - - Use a more appropriate default format for FLAC decoders. This will now use ma_format_s16 when the FLAC is encoded as 16-bit. - - Update FLAC decoder. - - Update links to point to the new repository location (https://github.com/mackron/miniaudio). - -v0.10.17 - 2020-08-28 - - Fix an error where the WAV codec is incorrectly excluded from the build depending on which compile time options are set. - - Fix a bug in ma_audio_buffer_read_pcm_frames() where it isn't returning the correct number of frames processed. - - Fix compilation error on Android. - - Core Audio: Fix a bug with full-duplex mode. - - Add ma_decoder_get_cursor_in_pcm_frames(). - - Update WAV codec. - -v0.10.16 - 2020-08-14 - - WASAPI: Fix a potential crash due to using an uninitialized variable. - - OpenSL: Enable runtime linking. - - OpenSL: Fix a multithreading bug when initializing and uninitializing multiple contexts at the same time. - - iOS: Improvements to device enumeration. - - Fix a crash in ma_data_source_read_pcm_frames() when the output frame count parameter is NULL. - - Fix a bug in ma_data_source_read_pcm_frames() where looping doesn't work. - - Fix some compilation warnings on Windows when both DirectSound and WinMM are disabled. - - Fix some compilation warnings when no decoders are enabled. - - Add ma_audio_buffer_get_available_frames(). - - Add ma_decoder_get_available_frames(). - - Add sample rate to ma_data_source_get_data_format(). - - Change volume APIs to take 64-bit frame counts. - - Updates to documentation. - -v0.10.15 - 2020-07-15 - - Fix a bug when converting bit-masked channel maps to miniaudio channel maps. This affects the WASAPI and OpenSL backends. - -v0.10.14 - 2020-07-14 - - Fix compilation errors on Android. - - Fix compilation errors with -march=armv6. - - Updates to the documentation. - -v0.10.13 - 2020-07-11 - - Fix some potential buffer overflow errors with channel maps when channel counts are greater than MA_MAX_CHANNELS. - - Fix compilation error on Emscripten. - - Silence some unused function warnings. - - Increase the default buffer size on the Web Audio backend. This fixes glitching issues on some browsers. - - Bring FLAC decoder up-to-date with dr_flac. - - Bring MP3 decoder up-to-date with dr_mp3. - -v0.10.12 - 2020-07-04 - - Fix compilation errors on the iOS build. - -v0.10.11 - 2020-06-28 - - Fix some bugs with device tracking on Core Audio. - - Updates to documentation. - -v0.10.10 - 2020-06-26 - - Add include guard for the implementation section. - - Mark ma_device_sink_info_callback() as static. - - Fix compilation errors with MA_NO_DECODING and MA_NO_ENCODING. - - Fix compilation errors with MA_NO_DEVICE_IO - -v0.10.9 - 2020-06-24 - - Amalgamation of dr_wav, dr_flac and dr_mp3. With this change, including the header section of these libraries before the implementation of miniaudio is no - longer required. Decoding of WAV, FLAC and MP3 should be supported seamlessly without any additional libraries. Decoders can be excluded from the build - with the following options: - - MA_NO_WAV - - MA_NO_FLAC - - MA_NO_MP3 - If you get errors about multiple definitions you need to either enable the options above, move the implementation of dr_wav, dr_flac and/or dr_mp3 to before - the implementation of miniaudio, or update dr_wav, dr_flac and/or dr_mp3. - - Changes to the internal atomics library. This has been replaced with c89atomic.h which is embedded within this file. - - Fix a bug when a decoding backend reports configurations outside the limits of miniaudio's decoder abstraction. - - Fix the UWP build. - - Fix the Core Audio build. - - Fix the -std=c89 build on GCC. - -v0.10.8 - 2020-06-22 - - Remove dependency on ma_context from mutexes. - - Change ma_data_source_read_pcm_frames() to return a result code and output the frames read as an output parameter. - - Change ma_data_source_seek_pcm_frames() to return a result code and output the frames seeked as an output parameter. - - Change ma_audio_buffer_unmap() to return MA_AT_END when the end has been reached. This should be considered successful. - - Change playback.pDeviceID and capture.pDeviceID to constant pointers in ma_device_config. - - Add support for initializing decoders from a virtual file system object. This is achieved via the ma_vfs API and allows the application to customize file - IO for the loading and reading of raw audio data. Passing in NULL for the VFS will use defaults. New APIs: - - ma_decoder_init_vfs() - - ma_decoder_init_vfs_wav() - - ma_decoder_init_vfs_flac() - - ma_decoder_init_vfs_mp3() - - ma_decoder_init_vfs_vorbis() - - ma_decoder_init_vfs_w() - - ma_decoder_init_vfs_wav_w() - - ma_decoder_init_vfs_flac_w() - - ma_decoder_init_vfs_mp3_w() - - ma_decoder_init_vfs_vorbis_w() - - Add support for memory mapping to ma_data_source. - - ma_data_source_map() - - ma_data_source_unmap() - - Add ma_offset_pcm_frames_ptr() and ma_offset_pcm_frames_const_ptr() which can be used for offsetting a pointer by a specified number of PCM frames. - - Add initial implementation of ma_yield() which is useful for spin locks which will be used in some upcoming work. - - Add documentation for log levels. - - The ma_event API has been made public in preparation for some uncoming work. - - Fix a bug in ma_decoder_seek_to_pcm_frame() where the internal sample rate is not being taken into account for determining the seek location. - - Fix some bugs with the linear resampler when dynamically changing the sample rate. - - Fix compilation errors with MA_NO_DEVICE_IO. - - Fix some warnings with GCC and -std=c89. - - Fix some formatting warnings with GCC and -Wall and -Wpedantic. - - Fix some warnings with VC6. - - Minor optimization to ma_copy_pcm_frames(). This is now a no-op when the input and output buffers are the same. - -v0.10.7 - 2020-05-25 - - Fix a compilation error in the C++ build. - - Silence a warning. - -v0.10.6 - 2020-05-24 - - Change ma_clip_samples_f32() and ma_clip_pcm_frames_f32() to take a 64-bit sample/frame count. - - Change ma_zero_pcm_frames() to clear to 128 for ma_format_u8. - - Add ma_silence_pcm_frames() which replaces ma_zero_pcm_frames(). ma_zero_pcm_frames() will be removed in version 0.11. - - Add support for u8, s24 and s32 formats to ma_channel_converter. - - Add compile-time and run-time version querying. - - MA_VERSION_MINOR - - MA_VERSION_MAJOR - - MA_VERSION_REVISION - - MA_VERSION_STRING - - ma_version() - - ma_version_string() - - Add ma_audio_buffer for reading raw audio data directly from memory. - - Fix a bug in shuffle mode in ma_channel_converter. - - Fix compilation errors in certain configurations for ALSA and PulseAudio. - - The data callback now initializes the output buffer to 128 when the playback sample format is ma_format_u8. - -v0.10.5 - 2020-05-05 - - Change ma_zero_pcm_frames() to take a 64-bit frame count. - - Add ma_copy_pcm_frames(). - - Add MA_NO_GENERATION build option to exclude the `ma_waveform` and `ma_noise` APIs from the build. - - Add support for formatted logging to the VC6 build. - - Fix a crash in the linear resampler when LPF order is 0. - - Fix compilation errors and warnings with older versions of Visual Studio. - - Minor documentation updates. - -v0.10.4 - 2020-04-12 - - Fix a data conversion bug when converting from the client format to the native device format. - -v0.10.3 - 2020-04-07 - - Bring up to date with breaking changes to dr_mp3. - - Remove MA_NO_STDIO. This was causing compilation errors and the maintenance cost versus practical benefit is no longer worthwhile. - - Fix a bug with data conversion where it was unnecessarily converting to s16 or f32 and then straight back to the original format. - - Fix compilation errors and warnings with Visual Studio 2005. - - ALSA: Disable ALSA's automatic data conversion by default and add configuration options to the device config: - - alsa.noAutoFormat - - alsa.noAutoChannels - - alsa.noAutoResample - - WASAPI: Add some overrun recovery for ma_device_type_capture devices. - -v0.10.2 - 2020-03-22 - - Decorate some APIs with MA_API which were missed in the previous version. - - Fix a bug in ma_linear_resampler_set_rate() and ma_linear_resampler_set_rate_ratio(). - -v0.10.1 - 2020-03-17 - - Add MA_API decoration. This can be customized by defining it before including miniaudio.h. - - Fix a bug where opening a file would return a success code when in fact it failed. - - Fix compilation errors with Visual Studio 6 and 2003. - - Fix warnings on macOS. - -v0.10.0 - 2020-03-07 - - API CHANGE: Refactor data conversion APIs - - ma_format_converter has been removed. Use ma_convert_pcm_frames_format() instead. - - ma_channel_router has been replaced with ma_channel_converter. - - ma_src has been replaced with ma_resampler - - ma_pcm_converter has been replaced with ma_data_converter - - API CHANGE: Add support for custom memory allocation callbacks. The following APIs have been updated to take an extra parameter for the allocation - callbacks: - - ma_malloc() - - ma_realloc() - - ma_free() - - ma_aligned_malloc() - - ma_aligned_free() - - ma_rb_init() / ma_rb_init_ex() - - ma_pcm_rb_init() / ma_pcm_rb_init_ex() - - API CHANGE: Simplify latency specification in device configurations. The bufferSizeInFrames and bufferSizeInMilliseconds parameters have been replaced with - periodSizeInFrames and periodSizeInMilliseconds respectively. The previous variables defined the size of the entire buffer, whereas the new ones define the - size of a period. The following APIs have been removed since they are no longer relevant: - - ma_get_default_buffer_size_in_milliseconds() - - ma_get_default_buffer_size_in_frames() - - API CHANGE: ma_device_set_stop_callback() has been removed. If you require a stop callback, you must now set it via the device config just like the data - callback. - - API CHANGE: The ma_sine_wave API has been replaced with ma_waveform. The following APIs have been removed: - - ma_sine_wave_init() - - ma_sine_wave_read_f32() - - ma_sine_wave_read_f32_ex() - - API CHANGE: ma_convert_frames() has been updated to take an extra parameter which is the size of the output buffer in PCM frames. Parameters have also been - reordered. - - API CHANGE: ma_convert_frames_ex() has been changed to take a pointer to a ma_data_converter_config object to specify the input and output formats to - convert between. - - API CHANGE: ma_calculate_frame_count_after_src() has been renamed to ma_calculate_frame_count_after_resampling(). - - Add support for the following filters: - - Biquad (ma_biquad) - - First order low-pass (ma_lpf1) - - Second order low-pass (ma_lpf2) - - Low-pass with configurable order (ma_lpf) - - First order high-pass (ma_hpf1) - - Second order high-pass (ma_hpf2) - - High-pass with configurable order (ma_hpf) - - Second order band-pass (ma_bpf2) - - Band-pass with configurable order (ma_bpf) - - Second order peaking EQ (ma_peak2) - - Second order notching (ma_notch2) - - Second order low shelf (ma_loshelf2) - - Second order high shelf (ma_hishelf2) - - Add waveform generation API (ma_waveform) with support for the following: - - Sine - - Square - - Triangle - - Sawtooth - - Add noise generation API (ma_noise) with support for the following: - - White - - Pink - - Brownian - - Add encoding API (ma_encoder). This only supports outputting to WAV files via dr_wav. - - Add ma_result_description() which is used to retrieve a human readable description of a given result code. - - Result codes have been changed. Binding maintainers will need to update their result code constants. - - More meaningful result codes are now returned when a file fails to open. - - Internal functions have all been made static where possible. - - Fix potential crash when ma_device object's are not aligned to MA_SIMD_ALIGNMENT. - - Fix a bug in ma_decoder_get_length_in_pcm_frames() where it was returning the length based on the internal sample rate rather than the output sample rate. - - Fix bugs in some backends where the device is not drained properly in ma_device_stop(). - - Improvements to documentation. - -v0.9.10 - 2020-01-15 - - Fix compilation errors due to #if/#endif mismatches. - - WASAPI: Fix a bug where automatic stream routing is being performed for devices that are initialized with an explicit device ID. - - iOS: Fix a crash on device uninitialization. - -v0.9.9 - 2020-01-09 - - Fix compilation errors with MinGW. - - Fix compilation errors when compiling on Apple platforms. - - WASAPI: Add support for disabling hardware offloading. - - WASAPI: Add support for disabling automatic stream routing. - - Core Audio: Fix bugs in the case where the internal device uses deinterleaved buffers. - - Core Audio: Add support for controlling the session category (AVAudioSessionCategory) and options (AVAudioSessionCategoryOptions). - - JACK: Fix bug where incorrect ports are connected. - -v0.9.8 - 2019-10-07 - - WASAPI: Fix a potential deadlock when starting a full-duplex device. - - WASAPI: Enable automatic resampling by default. Disable with config.wasapi.noAutoConvertSRC. - - Core Audio: Fix bugs with automatic stream routing. - - Add support for controlling whether or not the content of the output buffer passed in to the data callback is pre-initialized - to zero. By default it will be initialized to zero, but this can be changed by setting noPreZeroedOutputBuffer in the device - config. Setting noPreZeroedOutputBuffer to true will leave the contents undefined. - - Add support for clipping samples after the data callback has returned. This only applies when the playback sample format is - configured as ma_format_f32. If you are doing clipping yourself, you can disable this overhead by setting noClip to true in - the device config. - - Add support for master volume control for devices. - - Use ma_device_set_master_volume() to set the volume to a factor between 0 and 1, where 0 is silence and 1 is full volume. - - Use ma_device_set_master_gain_db() to set the volume in decibels where 0 is full volume and < 0 reduces the volume. - - Fix warnings emitted by GCC when `__inline__` is undefined or defined as nothing. - -v0.9.7 - 2019-08-28 - - Add support for loopback mode (WASAPI only). - - To use this, set the device type to ma_device_type_loopback, and then fill out the capture section of the device config. - - If you need to capture from a specific output device, set the capture device ID to that of a playback device. - - Fix a crash when an error is posted in ma_device_init(). - - Fix a compilation error when compiling for ARM architectures. - - Fix a bug with the audio(4) backend where the device is incorrectly being opened in non-blocking mode. - - Fix memory leaks in the Core Audio backend. - - Minor refactoring to the WinMM, ALSA, PulseAudio, OSS, audio(4), sndio and null backends. - -v0.9.6 - 2019-08-04 - - Add support for loading decoders using a wchar_t string for file paths. - - Don't trigger an assert when ma_device_start() is called on a device that is already started. This will now log a warning - and return MA_INVALID_OPERATION. The same applies for ma_device_stop(). - - Try fixing an issue with PulseAudio taking a long time to start playback. - - Fix a bug in ma_convert_frames() and ma_convert_frames_ex(). - - Fix memory leaks in the WASAPI backend. - - Fix a compilation error with Visual Studio 2010. - -v0.9.5 - 2019-05-21 - - Add logging to ma_dlopen() and ma_dlsym(). - - Add ma_decoder_get_length_in_pcm_frames(). - - Fix a bug with capture on the OpenSL|ES backend. - - Fix a bug with the ALSA backend where a device would not restart after being stopped. - -v0.9.4 - 2019-05-06 - - Add support for C89. With this change, miniaudio should compile clean with GCC/Clang with "-std=c89 -ansi -pedantic" and - Microsoft compilers back to VC6. Other compilers should also work, but have not been tested. - -v0.9.3 - 2019-04-19 - - Fix compiler errors on GCC when compiling with -std=c99. - -v0.9.2 - 2019-04-08 - - Add support for per-context user data. - - Fix a potential bug with context configs. - - Fix some bugs with PulseAudio. - -v0.9.1 - 2019-03-17 - - Fix a bug where the output buffer is not getting zeroed out before calling the data callback. This happens when - the device is running in passthrough mode (not doing any data conversion). - - Fix an issue where the data callback is getting called too frequently on the WASAPI and DirectSound backends. - - Fix error on the UWP build. - - Fix a build error on Apple platforms. - -v0.9 - 2019-03-06 - - Rebranded to "miniaudio". All namespaces have been renamed from "mal" to "ma". - - API CHANGE: ma_device_init() and ma_device_config_init() have changed significantly: - - The device type, device ID and user data pointer have moved from ma_device_init() to the config. - - All variations of ma_device_config_init_*() have been removed in favor of just ma_device_config_init(). - - ma_device_config_init() now takes only one parameter which is the device type. All other properties need - to be set on the returned object directly. - - The onDataCallback and onStopCallback members of ma_device_config have been renamed to "dataCallback" - and "stopCallback". - - The ID of the physical device is now split into two: one for the playback device and the other for the - capture device. This is required for full-duplex. These are named "pPlaybackDeviceID" and "pCaptureDeviceID". - - API CHANGE: The data callback has changed. It now uses a unified callback for all device types rather than - being separate for each. It now takes two pointers - one containing input data and the other output data. This - design in required for full-duplex. The return value is now void instead of the number of frames written. The - new callback looks like the following: - void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); - - API CHANGE: Remove the log callback parameter from ma_context_config_init(). With this change, - ma_context_config_init() now takes no parameters and the log callback is set via the structure directly. The - new policy for config initialization is that only mandatory settings are passed in to *_config_init(). The - "onLog" member of ma_context_config has been renamed to "logCallback". - - API CHANGE: Remove ma_device_get_buffer_size_in_bytes(). - - API CHANGE: Rename decoding APIs to "pcm_frames" convention. - - mal_decoder_read() -> ma_decoder_read_pcm_frames() - - mal_decoder_seek_to_frame() -> ma_decoder_seek_to_pcm_frame() - - API CHANGE: Rename sine wave reading APIs to f32 convention. - - mal_sine_wave_read() -> ma_sine_wave_read_f32() - - mal_sine_wave_read_ex() -> ma_sine_wave_read_f32_ex() - - API CHANGE: Remove some deprecated APIs - - mal_device_set_recv_callback() - - mal_device_set_send_callback() - - mal_src_set_input_sample_rate() - - mal_src_set_output_sample_rate() - - API CHANGE: Add log level to the log callback. New signature: - - void on_log(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message) - - API CHANGE: Changes to result codes. Constants have changed and unused codes have been removed. If you're - a binding mainainer you will need to update your result code constants. - - API CHANGE: Change the order of the ma_backend enums to priority order. If you are a binding maintainer, you - will need to update. - - API CHANGE: Rename mal_dsp to ma_pcm_converter. All functions have been renamed from mal_dsp_*() to - ma_pcm_converter_*(). All structures have been renamed from mal_dsp* to ma_pcm_converter*. - - API CHANGE: Reorder parameters of ma_decoder_read_pcm_frames() to be consistent with the new parameter order scheme. - - The resampling algorithm has been changed from sinc to linear. The rationale for this is that the sinc implementation - is too inefficient right now. This will hopefully be improved at a later date. - - Device initialization will no longer fall back to shared mode if exclusive mode is requested but is unusable. - With this change, if you request an device in exclusive mode, but exclusive mode is not supported, it will not - automatically fall back to shared mode. The client will need to reinitialize the device in shared mode if that's - what they want. - - Add ring buffer API. This is ma_rb and ma_pcm_rb, the difference being that ma_rb operates on bytes and - ma_pcm_rb operates on PCM frames. - - Add Web Audio backend. This is used when compiling with Emscripten. The SDL backend, which was previously - used for web support, will be removed in a future version. - - Add AAudio backend (Android Audio). This is the new priority backend for Android. Support for AAudio starts - with Android 8. OpenSL|ES is used as a fallback for older versions of Android. - - Remove OpenAL and SDL backends. - - Fix a possible deadlock when rapidly stopping the device after it has started. - - Update documentation. - - Change licensing to a choice of public domain _or_ MIT-0 (No Attribution). - -v0.8.14 - 2018-12-16 - - Core Audio: Fix a bug where the device state is not set correctly after stopping. - - Add support for custom weights to the channel router. - - Update decoders to use updated APIs in dr_flac, dr_mp3 and dr_wav. - -v0.8.13 - 2018-12-04 - - Core Audio: Fix a bug with channel mapping. - - Fix a bug with channel routing where the back/left and back/right channels have the wrong weight. - -v0.8.12 - 2018-11-27 - - Drop support for SDL 1.2. The Emscripten build now requires "-s USE_SDL=2". - - Fix a linking error with ALSA. - - Fix a bug on iOS where the device name is not set correctly. - -v0.8.11 - 2018-11-21 - - iOS bug fixes. - - Minor tweaks to PulseAudio. - -v0.8.10 - 2018-10-21 - - Core Audio: Fix a hang when uninitializing a device. - - Fix a bug where an incorrect value is returned from mal_device_stop(). - -v0.8.9 - 2018-09-28 - - Fix a bug with the SDL backend where device initialization fails. - -v0.8.8 - 2018-09-14 - - Fix Linux build with the ALSA backend. - - Minor documentation fix. - -v0.8.7 - 2018-09-12 - - Fix a bug with UWP detection. - -v0.8.6 - 2018-08-26 - - Automatically switch the internal device when the default device is unplugged. Note that this is still in the - early stages and not all backends handle this the same way. As of this version, this will not detect a default - device switch when changed from the operating system's audio preferences (unless the backend itself handles - this automatically). This is not supported in exclusive mode. - - WASAPI and Core Audio: Add support for stream routing. When the application is using a default device and the - user switches the default device via the operating system's audio preferences, miniaudio will automatically switch - the internal device to the new default. This is not supported in exclusive mode. - - WASAPI: Add support for hardware offloading via IAudioClient2. Only supported on Windows 8 and newer. - - WASAPI: Add support for low-latency shared mode via IAudioClient3. Only supported on Windows 10 and newer. - - Add support for compiling the UWP build as C. - - mal_device_set_recv_callback() and mal_device_set_send_callback() have been deprecated. You must now set this - when the device is initialized with mal_device_init*(). These will be removed in version 0.9.0. - -v0.8.5 - 2018-08-12 - - Add support for specifying the size of a device's buffer in milliseconds. You can still set the buffer size in - frames if that suits you. When bufferSizeInFrames is 0, bufferSizeInMilliseconds will be used. If both are non-0 - then bufferSizeInFrames will take priority. If both are set to 0 the default buffer size is used. - - Add support for the audio(4) backend to OpenBSD. - - Fix a bug with the ALSA backend that was causing problems on Raspberry Pi. This significantly improves the - Raspberry Pi experience. - - Fix a bug where an incorrect number of samples is returned from sinc resampling. - - Add support for setting the value to be passed to internal calls to CoInitializeEx(). - - WASAPI and WinMM: Stop the device when it is unplugged. - -v0.8.4 - 2018-08-06 - - Add sndio backend for OpenBSD. - - Add audio(4) backend for NetBSD. - - Drop support for the OSS backend on everything except FreeBSD and DragonFly BSD. - - Formats are now native-endian (were previously little-endian). - - Mark some APIs as deprecated: - - mal_src_set_input_sample_rate() and mal_src_set_output_sample_rate() are replaced with mal_src_set_sample_rate(). - - mal_dsp_set_input_sample_rate() and mal_dsp_set_output_sample_rate() are replaced with mal_dsp_set_sample_rate(). - - Fix a bug when capturing using the WASAPI backend. - - Fix some aliasing issues with resampling, specifically when increasing the sample rate. - - Fix warnings. - -v0.8.3 - 2018-07-15 - - Fix a crackling bug when resampling in capture mode. - - Core Audio: Fix a bug where capture does not work. - - ALSA: Fix a bug where the worker thread can get stuck in an infinite loop. - - PulseAudio: Fix a bug where mal_context_init() succeeds when PulseAudio is unusable. - - JACK: Fix a bug where mal_context_init() succeeds when JACK is unusable. - -v0.8.2 - 2018-07-07 - - Fix a bug on macOS with Core Audio where the internal callback is not called. - -v0.8.1 - 2018-07-06 - - Fix compilation errors and warnings. - -v0.8 - 2018-07-05 - - Changed MAL_IMPLEMENTATION to MINI_AL_IMPLEMENTATION for consistency with other libraries. The old - way is still supported for now, but you should update as it may be removed in the future. - - API CHANGE: Replace device enumeration APIs. mal_enumerate_devices() has been replaced with - mal_context_get_devices(). An additional low-level device enumration API has been introduced called - mal_context_enumerate_devices() which uses a callback to report devices. - - API CHANGE: Rename mal_get_sample_size_in_bytes() to mal_get_bytes_per_sample() and add - mal_get_bytes_per_frame(). - - API CHANGE: Replace mal_device_config.preferExclusiveMode with mal_device_config.shareMode. - - This new config can be set to mal_share_mode_shared (default) or mal_share_mode_exclusive. - - API CHANGE: Remove excludeNullDevice from mal_context_config.alsa. - - API CHANGE: Rename MAL_MAX_SAMPLE_SIZE_IN_BYTES to MAL_MAX_PCM_SAMPLE_SIZE_IN_BYTES. - - API CHANGE: Change the default channel mapping to the standard Microsoft mapping. - - API CHANGE: Remove backend-specific result codes. - - API CHANGE: Changes to the format conversion APIs (mal_pcm_f32_to_s16(), etc.) - - Add support for Core Audio (Apple). - - Add support for PulseAudio. - - This is the highest priority backend on Linux (higher priority than ALSA) since it is commonly - installed by default on many of the popular distros and offer's more seamless integration on - platforms where PulseAudio is used. In addition, if PulseAudio is installed and running (which - is extremely common), it's better to just use PulseAudio directly rather than going through the - "pulse" ALSA plugin (which is what the "default" ALSA device is likely set to). - - Add support for JACK. - - Remove dependency on asound.h for the ALSA backend. This means the ALSA development packages are no - longer required to build miniaudio. - - Remove dependency on dsound.h for the DirectSound backend. This fixes build issues with some - distributions of MinGW. - - Remove dependency on audioclient.h for the WASAPI backend. This fixes build issues with some - distributions of MinGW. - - Add support for dithering to format conversion. - - Add support for configuring the priority of the worker thread. - - Add a sine wave generator. - - Improve efficiency of sample rate conversion. - - Introduce the notion of standard channel maps. Use mal_get_standard_channel_map(). - - Introduce the notion of default device configurations. A default config uses the same configuration - as the backend's internal device, and as such results in a pass-through data transmission pipeline. - - Add support for passing in NULL for the device config in mal_device_init(), which uses a default - config. This requires manually calling mal_device_set_send/recv_callback(). - - Add support for decoding from raw PCM data (mal_decoder_init_raw(), etc.) - - Make mal_device_init_ex() more robust. - - Make some APIs more const-correct. - - Fix errors with SDL detection on Apple platforms. - - Fix errors with OpenAL detection. - - Fix some memory leaks. - - Fix a bug with opening decoders from memory. - - Early work on SSE2, AVX2 and NEON optimizations. - - Miscellaneous bug fixes. - - Documentation updates. - -v0.7 - 2018-02-25 - - API CHANGE: Change mal_src_read_frames() and mal_dsp_read_frames() to use 64-bit sample counts. - - Add decoder APIs for loading WAV, FLAC, Vorbis and MP3 files. - - Allow opening of devices without a context. - - In this case the context is created and managed internally by the device. - - Change the default channel mapping to the same as that used by FLAC. - - Fix build errors with macOS. - -v0.6c - 2018-02-12 - - Fix build errors with BSD/OSS. - -v0.6b - 2018-02-03 - - Fix some warnings when compiling with Visual C++. - -v0.6a - 2018-01-26 - - Fix errors with channel mixing when increasing the channel count. - - Improvements to the build system for the OpenAL backend. - - Documentation fixes. - -v0.6 - 2017-12-08 - - API CHANGE: Expose and improve mutex APIs. If you were using the mutex APIs before this version you'll - need to update. - - API CHANGE: SRC and DSP callbacks now take a pointer to a mal_src and mal_dsp object respectively. - - API CHANGE: Improvements to event and thread APIs. These changes make these APIs more consistent. - - Add support for SDL and Emscripten. - - Simplify the build system further for when development packages for various backends are not installed. - With this change, when the compiler supports __has_include, backends without the relevant development - packages installed will be ignored. This fixes the build for old versions of MinGW. - - Fixes to the Android build. - - Add mal_convert_frames(). This is a high-level helper API for performing a one-time, bulk conversion of - audio data to a different format. - - Improvements to f32 -> u8/s16/s24/s32 conversion routines. - - Fix a bug where the wrong value is returned from mal_device_start() for the OpenSL backend. - - Fixes and improvements for Raspberry Pi. - - Warning fixes. - -v0.5 - 2017-11-11 - - API CHANGE: The mal_context_init() function now takes a pointer to a mal_context_config object for - configuring the context. The works in the same kind of way as the device config. The rationale for this - change is to give applications better control over context-level properties, add support for backend- - specific configurations, and support extensibility without breaking the API. - - API CHANGE: The alsa.preferPlugHW device config variable has been removed since it's not really useful for - anything anymore. - - ALSA: By default, device enumeration will now only enumerate over unique card/device pairs. Applications - can enable verbose device enumeration by setting the alsa.useVerboseDeviceEnumeration context config - variable. - - ALSA: When opening a device in shared mode (the default), the dmix/dsnoop plugin will be prioritized. If - this fails it will fall back to the hw plugin. With this change the preferExclusiveMode config is now - honored. Note that this does not happen when alsa.useVerboseDeviceEnumeration is set to true (see above) - which is by design. - - ALSA: Add support for excluding the "null" device using the alsa.excludeNullDevice context config variable. - - ALSA: Fix a bug with channel mapping which causes an assertion to fail. - - Fix errors with enumeration when pInfo is set to NULL. - - OSS: Fix a bug when starting a device when the client sends 0 samples for the initial buffer fill. - -v0.4 - 2017-11-05 - - API CHANGE: The log callback is now per-context rather than per-device and as is thus now passed to - mal_context_init(). The rationale for this change is that it allows applications to capture diagnostic - messages at the context level. Previously this was only available at the device level. - - API CHANGE: The device config passed to mal_device_init() is now const. - - Added support for OSS which enables support on BSD platforms. - - Added support for WinMM (waveOut/waveIn). - - Added support for UWP (Universal Windows Platform) applications. Currently C++ only. - - Added support for exclusive mode for selected backends. Currently supported on WASAPI. - - POSIX builds no longer require explicit linking to libpthread (-lpthread). - - ALSA: Explicit linking to libasound (-lasound) is no longer required. - - ALSA: Latency improvements. - - ALSA: Use MMAP mode where available. This can be disabled with the alsa.noMMap config. - - ALSA: Use "hw" devices instead of "plughw" devices by default. This can be disabled with the - alsa.preferPlugHW config. - - WASAPI is now the highest priority backend on Windows platforms. - - Fixed an error with sample rate conversion which was causing crackling when capturing. - - Improved error handling. - - Improved compiler support. - - Miscellaneous bug fixes. - -v0.3 - 2017-06-19 - - API CHANGE: Introduced the notion of a context. The context is the highest level object and is required for - enumerating and creating devices. Now, applications must first create a context, and then use that to - enumerate and create devices. The reason for this change is to ensure device enumeration and creation is - tied to the same backend. In addition, some backends are better suited to this design. - - API CHANGE: Removed the rewinding APIs because they're too inconsistent across the different backends, hard - to test and maintain, and just generally unreliable. - - Added helper APIs for initializing mal_device_config objects. - - Null Backend: Fixed a crash when recording. - - Fixed build for UWP. - - Added support for f32 formats to the OpenSL|ES backend. - - Added initial implementation of the WASAPI backend. - - Added initial implementation of the OpenAL backend. - - Added support for low quality linear sample rate conversion. - - Added early support for basic channel mapping. - -v0.2 - 2016-10-28 - - API CHANGE: Add user data pointer as the last parameter to mal_device_init(). The rationale for this - change is to ensure the logging callback has access to the user data during initialization. - - API CHANGE: Have device configuration properties be passed to mal_device_init() via a structure. Rationale: - 1) The number of parameters is just getting too much. - 2) It makes it a bit easier to add new configuration properties in the future. In particular, there's a - chance there will be support added for backend-specific properties. - - Dropped support for f64, A-law and Mu-law formats since they just aren't common enough to justify the - added maintenance cost. - - DirectSound: Increased the default buffer size for capture devices. - - Added initial implementation of the OpenSL|ES backend. - -v0.1 - 2016-10-21 - - Initial versioned release. -*/ - /* This software is available as a choice of the following licenses. Choose diff --git a/vendor/miniaudio/synchronization.odin b/vendor/miniaudio/synchronization.odin new file mode 100644 index 000000000..7615e8f45 --- /dev/null +++ b/vendor/miniaudio/synchronization.odin @@ -0,0 +1,152 @@ +package miniaudio + +when ODIN_OS == .Windows { + foreign import lib "lib/miniaudio.lib" +} else when ODIN_OS == .Linux { + foreign import lib "lib/miniaudio.a" +} else { + foreign import lib "system:miniaudio" +} + +@(default_calling_convention="c", link_prefix="ma_") +foreign lib { + /* + Locks a spinlock. + */ + spinlock_lock :: proc(/*volatile*/ pSpinlock: ^spinlock) -> result --- + + /* + Locks a spinlock, but does not yield() when looping. + */ + spinlock_lock_noyield :: proc(/*volatile*/ pSpinlock: ^spinlock) -> result --- + + /* + Unlocks a spinlock. + */ + spinlock_unlock :: proc(/*volatile*/ pSpinlock: ^spinlock) -> result --- + +when NO_THREADING { + /* + Creates a mutex. + + A mutex must be created from a valid context. A mutex is initially unlocked. + */ + mutex_init :: proc(pMutex: ^mutex) -> result --- + + /* + Deletes a mutex. + */ + mutex_uninit :: proc(pMutex: ^mutex) --- + + /* + Locks a mutex with an infinite timeout. + */ + mutex_lock :: proc(pMutex: ^mutex) --- + + /* + Unlocks a mutex. + */ + mutex_unlock :: proc(pMutex: ^mutex) --- + + + /* + Initializes an auto-reset event. + */ + event_init :: proc(pEvent: ^event) -> result --- + + /* + Uninitializes an auto-reset event. + */ + event_uninit :: proc(pEvent: ^event) --- + + /* + Waits for the specified auto-reset event to become signalled. + */ + event_wait :: proc(pEvent: ^event) -> result --- + + /* + Signals the specified auto-reset event. + */ + event_signal :: proc(pEvent: ^event) -> result --- +} /* NO_THREADING */ + +} + +/* +Fence +===== +This locks while the counter is larger than 0. Counter can be incremented and decremented by any +thread, but care needs to be taken when waiting. It is possible for one thread to acquire the +fence just as another thread returns from ma_fence_wait(). + +The idea behind a fence is to allow you to wait for a group of operations to complete. When an +operation starts, the counter is incremented which locks the fence. When the operation completes, +the fence will be released which decrements the counter. ma_fence_wait() will block until the +counter hits zero. + +If threading is disabled, ma_fence_wait() will spin on the counter. +*/ +fence :: struct { + e: (struct {} when NO_THREADING else event), + counter: (u32 when NO_THREADING else struct {}), +} + +@(default_calling_convention="c", link_prefix="ma_") +foreign lib { + fence_init :: proc(pFence: ^fence) -> result --- + fence_uninit :: proc(pFence: ^fence) --- + fence_acquire :: proc(pFence: ^fence) -> result --- /* Increment counter. */ + fence_release :: proc(pFence: ^fence) -> result --- /* Decrement counter. */ + fence_wait :: proc(pFence: ^fence) -> result --- /* Wait for counter to reach 0. */ +} + + +/* +Notification callback for asynchronous operations. +*/ +async_notification :: struct {} + +async_notification_callbacks :: struct { + onSignal: proc "c" (pNotification: ^async_notification), +} + +@(default_calling_convention="c", link_prefix="ma_") +foreign lib { + async_notification_signal :: proc(pNotification: ^async_notification) -> result --- +} + + +/* +Simple polling notification. + +This just sets a variable when the notification has been signalled which is then polled with ma_async_notification_poll_is_signalled() +*/ +async_notification_poll :: struct { + cb: async_notification_callbacks, + signalled: b32, +} + +@(default_calling_convention="c", link_prefix="ma_") +foreign lib { + async_notification_poll_init :: proc(pNotificationPoll: ^async_notification_poll) -> result --- + async_notification_poll_is_signalled :: proc(pNotificationPoll: ^async_notification_poll) -> b32 --- +} + + +/* +Event Notification + +This uses an ma_event. If threading is disabled (MA_NO_THREADING), initialization will fail. +*/ +async_notification_event :: struct { + cb: async_notification_callbacks, + e: (struct {} when NO_THREADING else event), +} + +@(default_calling_convention="c", link_prefix="ma_") +foreign lib { + async_notification_event_init :: proc(pNotificationEvent: ^async_notification_event) -> result --- + async_notification_event_uninit :: proc(pNotificationEvent: ^async_notification_event) -> result --- + async_notification_event_wait :: proc(pNotificationEvent: ^async_notification_event) -> result --- + async_notification_event_signal :: proc(pNotificationEvent: ^async_notification_event) -> result --- +} diff --git a/vendor/miniaudio/utilities.odin b/vendor/miniaudio/utilities.odin index 1a94550e4..708cc820e 100644 --- a/vendor/miniaudio/utilities.odin +++ b/vendor/miniaudio/utilities.odin @@ -1,17 +1,17 @@ package miniaudio -when ODIN_OS == "windows" { foreign import lib "lib/miniaudio.lib" } -when ODIN_OS == "linux" { foreign import lib "lib/miniaudio.a" } +import c "core:c/libc" + +when ODIN_OS == .Windows { + foreign import lib "lib/miniaudio.lib" +} else when ODIN_OS == .Linux { + foreign import lib "lib/miniaudio.a" +} else { + foreign import lib "system:miniaudio" +} @(default_calling_convention="c", link_prefix="ma_") foreign lib { - /* - Adjust buffer size based on a scaling factor. - - This just multiplies the base size by the scaling factor, making sure it's a size of at least 1. - */ - scale_buffer_size :: proc(baseBufferSize: u32, scale: f32) -> u32 --- - /* Calculates a buffer size in milliseconds from the specified number of frames and sample rate. */ @@ -46,9 +46,14 @@ foreign lib { /* - Clips f32 samples. + Clips samples. */ - clip_samples_f32 :: proc(p: [^]f32, sampleCount: u64) --- + clip_samples_u8 :: proc(pDst: [^]u8, pSrc: [^]i16, count: u64) --- + clip_samples_s16 :: proc(pDst: [^]i16, pSrc: [^]i32, count: u64) --- + clip_samples_s24 :: proc(pDst: [^]u8, pSrc: [^]i64, count: u64) --- + clip_samples_s32 :: proc(pDst: [^]i32, pSrc: [^]i64, count: u64) --- + clip_samples_f32 :: proc(pDst, pSrc: [^]f32, count: u64) --- + clip_pcm_frames :: proc(pDst, pSrc: rawptr, frameCount: u64, format: format, channels: u32) --- /* Helper for applying a volume factor to samples. @@ -81,20 +86,26 @@ foreign lib { apply_volume_factor_pcm_frames_f32 :: proc(pFrames: [^]f32, frameCount: u64, channels: u32, factor: f32) --- apply_volume_factor_pcm_frames :: proc(pFrames: rawptr, frameCount: u64, format: format, channels: u32, factor: f32) --- + copy_and_apply_volume_factor_per_channel_f32 :: proc(pFramesOut, pFramesIn: [^]f32, frameCount: u64, channels: u32, pChannelGains: [^]f32) --- + + + ma_copy_and_apply_volume_and_clip_samples_u8 :: proc(pDst: [^]u8, pSrc: [^]i16, count: u64, volume: f32) --- + ma_copy_and_apply_volume_and_clip_samples_s16 :: proc(pDst: [^]i16, pSrc: [^]i32, count: u64, volume: f32) --- + ma_copy_and_apply_volume_and_clip_samples_s24 :: proc(pDst: [^]u8, pSrc: [^]i64, count: u64, volume: f32) --- + ma_copy_and_apply_volume_and_clip_samples_s32 :: proc(pDst: [^]i32, pSrc: [^]i64, count: u64, volume: f32) --- + ma_copy_and_apply_volume_and_clip_samples_f32 :: proc(pDst, pSrc: [^]f32, count: u64, volume: f32) --- + ma_copy_and_apply_volume_and_clip_pcm_frames :: proc(pDst, pSrc: rawptr, frameCount: u64, format: format, channels: u32, volume: f32) --- + /* Helper for converting a linear factor to gain in decibels. */ - factor_to_gain_db :: proc(factor: f32) -> f32 --- + volume_linear_to_db :: proc(factor: f32) -> f32 --- /* Helper for converting gain in decibels to a linear factor. */ - gain_db_to_factor :: proc(gain: f32) -> f32 --- -} - -zero_pcm_frames :: #force_inline proc "c" (p: rawptr, frameCount: u64, format: format, channels: u32) { - silence_pcm_frames(p, frameCount, format, channels) + volume_db_to_linear :: proc(gain: f32) -> f32 --- } offset_pcm_frames_ptr_f32 :: #force_inline proc "c" (p: [^]f32, offsetInFrames: u64, channels: u32) -> [^]f32 { @@ -104,23 +115,20 @@ offset_pcm_frames_const_ptr_f32 :: #force_inline proc "c" (p: [^]f32, offsetInFr return cast([^]f32)offset_pcm_frames_ptr(p, offsetInFrames, .f32, channels) } -clip_pcm_frames_f32 :: #force_inline proc "c" (p: [^]f32, frameCount: u64, channels: u32) { - clip_samples_f32(p, frameCount*u64(channels)) -} - data_source :: struct {} +DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT :: 0x00000001 + data_source_vtable :: struct { onRead: proc "c" (pDataSource: ^data_source, pFramesOut: rawptr, frameCount: u64, pFramesRead: ^u64) -> result, onSeek: proc "c" (pDataSource: ^data_source, frameIndex: u64) -> result, - onMap: proc "c" (pDataSource: ^data_source, ppFramesOut: ^rawptr, pFrameCount: ^u64) -> result, /* Returns MA_AT_END if the end has been reached. This should be considered successful. */ - onUnmap: proc "c" (pDataSource: ^data_source, frameCount: u64) -> result, - onGetDataFormat: proc "c" (pDataSource: ^data_source, pFormat: ^format, pChannels: ^u32, pSampleRate: ^u32) -> result, + onGetDataFormat: proc "c" (pDataSource: ^data_source, pFormat: ^format, pChannels: ^u32, pSampleRate: ^u32, pChannelMap: [^]channel, channelMapCap: c.size_t) -> result, onGetCursor: proc "c" (pDataSource: ^data_source, pCursor: ^u64) -> result, onGetLength: proc "c" (pDataSource: ^data_source, pLength: ^u64) -> result, + onSetLooping: proc "c" (pDataSource: ^data_source, isLooping: b32) -> result, + flags: u32, } -data_source_callbacks :: data_source_vtable /* TODO: Remove ma_data_source_callbacks in version 0.11. */ data_source_get_next_proc :: proc "c" (pDataSource: ^data_source) -> ^data_source @@ -129,45 +137,43 @@ data_source_config :: struct { } data_source_base :: struct { - cb: data_source_callbacks, /* TODO: Remove this. */ - - /* Variables below are placeholder and not yet used. */ vtable: ^data_source_vtable, rangeBegInFrames: u64, - rangeEndInFrames: u64, /* Set to -1 for unranged (default). */ - loopBegInFrames: u64, /* Relative to rangeBegInFrames. */ - loopEndInFrames: u64, /* Relative to rangeBegInFrames. Set to -1 for the end of the range. */ - pCurrent: ^data_source, /* When non-NULL, the data source being initialized will act as a proxy and will route all operations to pCurrent. Used in conjunction with pNext/onGetNext for seamless chaining. */ - pNext: ^data_source, /* When set to NULL, onGetNext will be used. */ - onGetNext: ^data_source_get_next_proc, /* Will be used when pNext is NULL. If both are NULL, no next will be used. */ + rangeEndInFrames: u64, /* Set to -1 for unranged (default). */ + loopBegInFrames: u64, /* Relative to rangeBegInFrames. */ + loopEndInFrames: u64, /* Relative to rangeBegInFrames. Set to -1 for the end of the range. */ + pCurrent: ^data_source, /* When non-NULL, the data source being initialized will act as a proxy and will route all operations to pCurrent. Used in conjunction with pNext/onGetNext for seamless chaining. */ + pNext: ^data_source, /* When set to NULL, onGetNext will be used. */ + onGetNext: data_source_get_next_proc, /* Will be used when pNext is NULL. If both are NULL, no next will be used. */ + isLooping: b32, /*atomic*/ } @(default_calling_convention="c", link_prefix="ma_") foreign lib { - ma_data_source_config_init :: proc() -> data_source_config --- + data_source_config_init :: proc() -> data_source_config --- - data_source_init :: proc(pConfig: ^data_source_config, pDataSource: ^data_source) -> result --- - data_source_uninit :: proc(pDataSource: ^data_source) --- - data_source_read_pcm_frames :: proc(pDataSource: ^data_source, pFramesOut: rawptr, frameCount: u64, pFramesRead: ^u64, loop: b32) -> result --- /* Must support pFramesOut = NULL in which case a forward seek should be performed. */ - data_source_seek_pcm_frames :: proc(pDataSource: ^data_source, frameCount: u64, pFramesSeeked: ^u64, loop: b32) -> result --- /* Can only seek forward. Equivalent to ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount); */ - data_source_seek_to_pcm_frame :: proc(pDataSource: ^data_source, frameIndex: u64) -> result --- - data_source_map :: proc(pDataSource: ^data_source, ppFramesOut: ^rawptr, pFrameCount: ^u64) -> result --- /* Returns MA_NOT_IMPLEMENTED if mapping is not supported. */ - data_source_unmap :: proc(pDataSource: ^data_source, frameCount: u64) -> result --- /* Returns MA_AT_END if the end has been reached. */ - data_source_get_data_format :: proc(pDataSource: ^data_source, pFormat: ^format, pChannels: ^u32, pSampleRate: ^u32) -> result --- - data_source_get_cursor_in_pcm_frames :: proc(pDataSource: ^data_source, pCursor: ^u64) -> result --- - data_source_get_length_in_pcm_frames :: proc(pDataSource: ^data_source, pLength: ^u64) -> result --- /* Returns MA_NOT_IMPLEMENTED if the length is unknown or cannot be determined. Decoders can return this. */ - // #if defined(MA_EXPERIMENTAL__DATA_LOOPING_AND_CHAINING) - // MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames); - // MA_API void ma_data_source_get_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames); - // MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames); - // MA_API void ma_data_source_get_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames); - // MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource); - // MA_API ma_data_source* ma_data_source_get_current(ma_data_source* pDataSource); - // MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource); - // MA_API ma_data_source* ma_data_source_get_next(ma_data_source* pDataSource); - // MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext); - // MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(ma_data_source* pDataSource); - // #endif + data_source_init :: proc(pConfig: ^data_source_config, pDataSource: ^data_source) -> result --- + data_source_uninit :: proc(pDataSource: ^data_source) --- + data_source_read_pcm_frames :: proc(pDataSource: ^data_source, pFramesOut: rawptr, frameCount: u64, pFramesRead: ^u64) -> result --- /* Must support pFramesOut = NULL in which case a forward seek should be performed. */ + data_source_seek_pcm_frames :: proc(pDataSource: ^data_source, frameCount: u64, pFramesSeeked: ^u64) -> result --- /* Can only seek forward. Equivalent to ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount); */ + data_source_seek_to_pcm_frame :: proc(pDataSource: ^data_source, frameIndex: u64) -> result --- + data_source_get_data_format :: proc(pDataSource: ^data_source, pFormat: ^format, pChannels: ^u32, pSampleRate: ^u32, pChannelMap: [^]channel, channelMapCap: c.size_t) -> result --- + data_source_get_cursor_in_pcm_frames :: proc(pDataSource: ^data_source, pCursor: ^u64) -> result --- + data_source_get_length_in_pcm_frames :: proc(pDataSource: ^data_source, pLength: ^u64) -> result --- /* Returns MA_NOT_IMPLEMENTED if the length is unknown or cannot be determined. Decoders can return this. */ + data_source_get_cursor_in_seconds :: proc(pDataSource: ^data_source, pCursor: ^f32) -> result --- + data_source_get_length_in_seconds :: proc(pDataSource: ^data_source, pLength: ^f32) -> result --- + data_source_set_looping :: proc(pDataSource: ^data_source, isLooping: b32) -> result --- + data_source_is_looping :: proc(pDataSource: ^data_source) -> b32 --- + data_source_set_range_in_pcm_frames :: proc(pDataSource: ^data_source, rangeBegInFrames: u64, rangeEndInFrames: u64) -> result --- + data_source_get_range_in_pcm_frames :: proc(pDataSource: ^data_source, pRangeBegInFrames: ^u64, pRangeEndInFrames: ^u64) --- + data_source_set_loop_point_in_pcm_frames :: proc(pDataSource: ^data_source, loopBegInFrames: u64, loopEndInFrames: u64) -> result --- + data_source_get_loop_point_in_pcm_frames :: proc(pDataSource: ^data_source, pLoopBegInFrames: ^u64, pLoopEndInFrames: ^u64) --- + data_source_set_current :: proc(pDataSource: ^data_source, pCurrentDataSource: ^data_source) -> result --- + data_source_get_current :: proc(pDataSource: ^data_source) -> ^data_source --- + data_source_set_next :: proc(pDataSource: ^data_source, pNextDataSource: ^data_source) -> result --- + data_source_get_next :: proc(pDataSource: ^data_source) -> ^data_source --- + data_source_set_next_callback :: proc(pDataSource: ^data_source, onGetNext: ^data_source_get_next_proc) -> result --- + data_source_get_next_callback :: proc(pDataSource: ^data_source) -> ^data_source_get_next_proc --- } @@ -175,6 +181,7 @@ audio_buffer_ref :: struct { ds: data_source_base, format: format, channels: u32, + sampleRate: u32, cursor: u64, sizeInFrames: u64, pData: rawptr, @@ -199,6 +206,7 @@ foreign lib { audio_buffer_config :: struct { format: format, channels: u32, + sampleRate: u32, sizeInFrames: u64, pData: rawptr, /* If set to NULL, will allocate a block of memory for you. */ allocationCallbacks: allocation_callbacks, @@ -228,4 +236,66 @@ foreign lib { audio_buffer_get_cursor_in_pcm_frames :: proc(pAudioBuffer: ^audio_buffer, pCursor: ^u64) -> result --- audio_buffer_get_length_in_pcm_frames :: proc(pAudioBuffer: ^audio_buffer, pLength: ^u64) -> result --- audio_buffer_get_available_frames :: proc(pAudioBuffer: ^audio_buffer, pAvailableFrames: ^u64) -> result --- -} \ No newline at end of file +} + +/* +Paged Audio Buffer +================== +A paged audio buffer is made up of a linked list of pages. It's expandable, but not shrinkable. It +can be used for cases where audio data is streamed in asynchronously while allowing data to be read +at the same time. + +This is lock-free, but not 100% thread safe. You can append a page and read from the buffer across +simultaneously across different threads, however only one thread at a time can append, and only one +thread at a time can read and seek. +*/ +paged_audio_buffer_page :: struct { + pNext: ^paged_audio_buffer_page, /*atomic*/ + sizeInFrames: u64, + pAudioData: [1]u8, +} + +paged_audio_buffer_data :: struct { + format: format, + channels: u32, + head: paged_audio_buffer_page, /* Dummy head for the lock-free algorithm. Always has a size of 0. */ + pTail: ^paged_audio_buffer_page, /*atomic*/ /* Never null. Initially set to &head. */ +} + +@(default_calling_convention="c", link_prefix="ma_") +foreign lib { + paged_audio_buffer_data_init :: proc(format: format, channels: u32, pData: ^paged_audio_buffer_data) -> result --- + paged_audio_buffer_data_uninit :: proc(pData: ^paged_audio_buffer_data, pAllocationCallbacks: ^allocation_callbacks) --- + paged_audio_buffer_data_get_head :: proc(pData: ^paged_audio_buffer_data) -> ^paged_audio_buffer_page --- + paged_audio_buffer_data_get_tail :: proc(pData: ^paged_audio_buffer_data) -> ^paged_audio_buffer_page --- + paged_audio_buffer_data_get_length_in_pcm_frames :: proc(pData: ^paged_audio_buffer_data, pLength: ^u64) -> result --- + paged_audio_buffer_data_allocate_page :: proc(pData: ^paged_audio_buffer_data, pageSizeInFrames: u64, pInitialData: rawptr, pAllocationCallbacks: ^allocation_callbacks, ppPage: ^^paged_audio_buffer_page) -> result --- + paged_audio_buffer_data_free_page :: proc(pData: ^paged_audio_buffer_data, pPage: ^paged_audio_buffer_page, pAllocationCallbacks: ^allocation_callbacks) -> result --- + paged_audio_buffer_data_append_page :: proc(pData: ^paged_audio_buffer_data, pPage: ^paged_audio_buffer_page) -> result --- + paged_audio_buffer_data_allocate_and_append_page :: proc(pData: ^paged_audio_buffer_data, pageSizeInFrames: u32, pInitialData: rawptr, pAllocationCallbacks: ^allocation_callbacks) -> result --- +} + + +paged_audio_buffer_config :: struct { + pData: ^paged_audio_buffer_data, /* Must not be null. */ +} + +paged_audio_buffer :: struct { + ds: data_source_base, + pData: ^paged_audio_buffer_data, /* Audio data is read from here. Cannot be null. */ + pCurrent: ^paged_audio_buffer_page, + relativeCursor: u64, /* Relative to the current page. */ + absoluteCursor: u64, +} + +@(default_calling_convention="c", link_prefix="ma_") +foreign lib { + paged_audio_buffer_config_init :: proc(pData: ^paged_audio_buffer_data) -> paged_audio_buffer_config --- + + paged_audio_buffer_init :: proc(pConfig: ^paged_audio_buffer_config, pPagedAudioBuffer: ^paged_audio_buffer) -> result --- + paged_audio_buffer_uninit :: proc(pPagedAudioBuffer: ^paged_audio_buffer) --- + paged_audio_buffer_read_pcm_frames :: proc(pPagedAudioBuffer: ^paged_audio_buffer, pFramesOut: rawptr, frameCount: u64, pFramesRead: ^u64) -> result --- /* Returns MA_AT_END if no more pages available. */ + paged_audio_buffer_seek_to_pcm_frame :: proc(pPagedAudioBuffer: ^paged_audio_buffer, frameIndex: u64) -> result --- + paged_audio_buffer_get_cursor_in_pcm_frames :: proc(pPagedAudioBuffer: ^paged_audio_buffer, pCursor: ^u64) -> result --- + paged_audio_buffer_get_length_in_pcm_frames :: proc(pPagedAudioBuffer: ^paged_audio_buffer, pLength: ^u64) -> result --- +} diff --git a/vendor/miniaudio/vfs.odin b/vendor/miniaudio/vfs.odin index fa18afb6b..9731c713f 100644 --- a/vendor/miniaudio/vfs.odin +++ b/vendor/miniaudio/vfs.odin @@ -2,8 +2,13 @@ package miniaudio import "core:c" -when ODIN_OS == "windows" { foreign import lib "lib/miniaudio.lib" } -when ODIN_OS == "linux" { foreign import lib "lib/miniaudio.a" } +when ODIN_OS == .Windows { + foreign import lib "lib/miniaudio.lib" +} else when ODIN_OS == .Linux { + foreign import lib "lib/miniaudio.a" +} else { + foreign import lib "system:miniaudio" +} /************************************************************************************************************************************************************ @@ -17,8 +22,10 @@ appropriate for a given situation. vfs :: struct {} vfs_file :: distinct handle -OPEN_MODE_READ :: 0x00000001 -OPEN_MODE_WRITE :: 0x00000002 +open_mode_flags :: enum c.int { + READ = 0x00000001, + WRITE = 0x00000002, +} seek_origin :: enum c.int { start, @@ -66,10 +73,6 @@ foreign lib { default_vfs_init :: proc(pVFS: ^default_vfs, pAllocationCallbacks: ^allocation_callbacks) -> result --- } -resource_format :: enum c.int { - wav, -} - encoding_format :: enum c.int { unknown = 0, wav, diff --git a/vendor/portmidi/portmidi.odin b/vendor/portmidi/portmidi.odin index 08f78150c..a3db4191c 100644 --- a/vendor/portmidi/portmidi.odin +++ b/vendor/portmidi/portmidi.odin @@ -3,12 +3,14 @@ package portmidi import "core:c" import "core:strings" -when ODIN_OS == "windows" { +when ODIN_OS == .Windows { foreign import lib { "portmidi_s.lib", "system:Winmm.lib", "system:Advapi32.lib", } +} else { + foreign import lib "system:portmidi" } #assert(size_of(b32) == size_of(c.int)) @@ -519,4 +521,4 @@ foreign lib { WriteSysEx() writes a timestamped system-exclusive midi message. */ WriteSysEx :: proc(stream: Stream, whence: Timestamp, msg: cstring) -> Error --- -} \ No newline at end of file +} diff --git a/vendor/portmidi/util.odin b/vendor/portmidi/util.odin index d02142bd0..caf73f8ad 100644 --- a/vendor/portmidi/util.odin +++ b/vendor/portmidi/util.odin @@ -7,7 +7,11 @@ package portmidi import "core:c" -when ODIN_OS == "windows" { foreign import lib "portmidi_s.lib" } +when ODIN_OS == .Windows { + foreign import lib "portmidi_s.lib" +} else { + foreign import lib "system:portmidi" +} Queue :: distinct rawptr @@ -118,4 +122,4 @@ foreign lib { state, returns .NoError if successfully set overflow state. */ SetOverflow :: proc(queue: Queue) -> Error --- -} \ No newline at end of file +} diff --git a/vendor/raylib/macos-arm64/libraylib.4.0.0.dylib b/vendor/raylib/macos-arm64/libraylib.4.0.0.dylib new file mode 100644 index 000000000..a40219baa Binary files /dev/null and b/vendor/raylib/macos-arm64/libraylib.4.0.0.dylib differ diff --git a/vendor/raylib/macos-arm64/libraylib.400.dylib b/vendor/raylib/macos-arm64/libraylib.400.dylib new file mode 100644 index 000000000..a40219baa Binary files /dev/null and b/vendor/raylib/macos-arm64/libraylib.400.dylib differ diff --git a/vendor/raylib/macos-arm64/libraylib.a b/vendor/raylib/macos-arm64/libraylib.a new file mode 100644 index 000000000..5eddcb8fa Binary files /dev/null and b/vendor/raylib/macos-arm64/libraylib.a differ diff --git a/vendor/raylib/macos-arm64/libraylib.dylib b/vendor/raylib/macos-arm64/libraylib.dylib new file mode 100644 index 000000000..a40219baa Binary files /dev/null and b/vendor/raylib/macos-arm64/libraylib.dylib differ diff --git a/vendor/raylib/raylib.odin b/vendor/raylib/raylib.odin index fb4d7dd92..a694c83a4 100644 --- a/vendor/raylib/raylib.odin +++ b/vendor/raylib/raylib.odin @@ -91,7 +91,7 @@ MAX_TEXT_BUFFER_LENGTH :: #config(RAYLIB_MAX_TEXT_BUFFER_LENGTH, 1024) #assert(size_of(rune) == size_of(c.int)) -when ODIN_OS == "windows" { +when ODIN_OS == .Windows { foreign import lib { "raylib.lib", "system:Winmm.lib", @@ -99,15 +99,31 @@ when ODIN_OS == "windows" { "system:User32.lib", "system:Shell32.lib", } -} -when ODIN_OS == "linux" { +} else when ODIN_OS == .Linux { foreign import lib { "linux/libraylib.a", "system:dl", "system:pthread", } +} else when ODIN_OS == .Darwin { + when ODIN_ARCH == .arm64 { + foreign import lib { + "macos-arm64/libraylib.a", + "system:Cocoa.framework", + "system:OpenGL.framework", + "system:IOKit.framework", + } + } else { + foreign import lib { + "macos/libraylib.a", + "system:Cocoa.framework", + "system:OpenGL.framework", + "system:IOKit.framework", + } + } +} else { + foreign import lib "system:raylib" } -when ODIN_OS == "darwin" { foreign import lib "macos/libraylib.a" } VERSION :: "4.0" @@ -1150,9 +1166,9 @@ foreign lib { DrawRectangleGradientH :: proc(posX, posY, width, height: c.int, color1: Color, color2: Color) --- // Draw a horizontal-gradient-filled rectangle DrawRectangleGradientEx :: proc(rec: Rectangle, col1, col2, col3, col4: Color) --- // Draw a gradient-filled rectangle with custom vertex colors DrawRectangleLines :: proc(posX, posY, width, height: c.int, color: Color) --- // Draw rectangle outline - DrawRectangleLinesEx :: proc(rec: Rectangle, lineThick: c.int, color: Color) --- // Draw rectangle outline with extended parameters + DrawRectangleLinesEx :: proc(rec: Rectangle, lineThick: f32, color: Color) --- // Draw rectangle outline with extended parameters DrawRectangleRounded :: proc(rec: Rectangle, roundness: f32, segments: c.int, color: Color) --- // Draw rectangle with rounded edges - DrawRectangleRoundedLines :: proc(rec: Rectangle, roundness: f32, segments: c.int, lineThick: c.int, color: Color) --- // Draw rectangle with rounded edges outline + DrawRectangleRoundedLines :: proc(rec: Rectangle, roundness: f32, segments: c.int, lineThick: f32, color: Color) --- // Draw rectangle with rounded edges outline DrawTriangle :: proc(v1, v2, v3: Vector2, color: Color) --- // Draw a color-filled triangle (vertex in counter-clockwise order!) DrawTriangleLines :: proc(v1, v2, v3: Vector2, color: Color) --- // Draw triangle outline (vertex in counter-clockwise order!) DrawTriangleFan :: proc(points: [^]Vector2, pointsCount: c.int, color: Color) --- // Draw a triangle fan defined by points (first vertex is the center) diff --git a/vendor/raylib/rlgl.odin b/vendor/raylib/rlgl.odin index 8f91486c1..7e7f2feea 100644 --- a/vendor/raylib/rlgl.odin +++ b/vendor/raylib/rlgl.odin @@ -2,7 +2,7 @@ package raylib import "core:c" -when ODIN_OS == "windows" { +when ODIN_OS == .Windows { foreign import lib { "raylib.lib", "system:Winmm.lib", @@ -10,9 +10,13 @@ when ODIN_OS == "windows" { "system:User32.lib", "system:Shell32.lib", } +} else when ODIN_OS == .Linux { + foreign import lib "linux/libraylib.a" +} else when ODIN_OS == .Darwin { + foreign import lib "macos/libraylib.a" +} else { + foreign import lib "system:raylib" } -when ODIN_OS == "linux" { foreign import lib "linux/libraylib.a" } -when ODIN_OS == "darwin" { foreign import lib "macos/libraylib.a" } GRAPHICS_API_OPENGL_11 :: false GRAPHICS_API_OPENGL_21 :: true @@ -378,4 +382,4 @@ foreign lib { // Quick and dirty cube/quad buffers load->draw->unload rlLoadDrawCube :: proc() --- // Load and draw a cube rlLoadDrawQuad :: proc() --- // Load and draw a quad -} \ No newline at end of file +} diff --git a/vendor/sdl2/image/sdl_image.odin b/vendor/sdl2/image/sdl_image.odin index 1dbe048ed..204ec9a0d 100644 --- a/vendor/sdl2/image/sdl_image.odin +++ b/vendor/sdl2/image/sdl_image.odin @@ -3,10 +3,11 @@ package sdl2_image import "core:c" import SDL ".." -when ODIN_OS == "windows" { foreign import lib "SDL2_image.lib" } -when ODIN_OS == "linux" { foreign import lib "system:SDL2_image" } -when ODIN_OS == "darwin" { foreign import lib "system:SDL2_image" } -when ODIN_OS == "freebsd" { foreign import lib "system:SDL2_image" } +when ODIN_OS == .Windows { + foreign import lib "SDL2_image.lib" +} else { + foreign import lib "system:SDL2_image" +} bool :: SDL.bool @@ -119,4 +120,4 @@ foreign lib { /* Individual loading functions */ LoadGIFAnimation_RW :: proc(src: ^SDL.RWops) -> ^Animation --- -} \ No newline at end of file +} diff --git a/vendor/sdl2/mixer/sdl_mixer.odin b/vendor/sdl2/mixer/sdl_mixer.odin index 90334d91a..b0c16871d 100644 --- a/vendor/sdl2/mixer/sdl_mixer.odin +++ b/vendor/sdl2/mixer/sdl_mixer.odin @@ -3,11 +3,11 @@ package sdl2_mixer import "core:c" import SDL ".." -when ODIN_OS == "windows" { foreign import lib "SDL2_mixer.lib" } -when ODIN_OS == "linux" { foreign import lib "system:SDL2_mixer" } -when ODIN_OS == "darwin" { foreign import lib "system:SDL2_mixer" } -when ODIN_OS == "freebsd" { foreign import lib "system:SDL2_mixer" } - +when ODIN_OS == .Windows { + foreign import lib "SDL2_mixer.lib" +} else { + foreign import lib "system:SDL2_mixer" +} MAJOR_VERSION :: 2 MINOR_VERSION :: 0 diff --git a/vendor/sdl2/net/sdl_net.odin b/vendor/sdl2/net/sdl_net.odin index 49671764a..579f245e5 100644 --- a/vendor/sdl2/net/sdl_net.odin +++ b/vendor/sdl2/net/sdl_net.odin @@ -3,10 +3,11 @@ package sdl2_net import "core:c" import SDL ".." -when ODIN_OS == "windows" { foreign import lib "SDL2_net.lib" } -when ODIN_OS == "linux" { foreign import lib "system:SDL2_net" } -when ODIN_OS == "darwin" { foreign import lib "system:SDL2_net" } -when ODIN_OS == "freebsd" { foreign import lib "system:SDL2_net" } +when ODIN_OS == .Windows { + foreign import lib "SDL2_net.lib" +} else { + foreign import lib "system:SDL2_net" +} bool :: SDL.bool @@ -188,4 +189,4 @@ Read16 :: #force_inline proc "c" (areap: rawptr) -> u16 { Read32 :: #force_inline proc "c" (areap: rawptr) -> u32 { area := (^[4]u8)(areap) return u32(area[0])<<24 | u32(area[1])<<16 | u32(area[2])<<8 | u32(area[3]) -} \ No newline at end of file +} diff --git a/vendor/sdl2/sdl2.odin b/vendor/sdl2/sdl2.odin index 4781483f7..24ecc38e6 100644 --- a/vendor/sdl2/sdl2.odin +++ b/vendor/sdl2/sdl2.odin @@ -25,10 +25,11 @@ package sdl2 import "core:c" import "core:intrinsics" -when ODIN_OS == "windows" { foreign import lib "SDL2.lib" } -when ODIN_OS == "linux" { foreign import lib "system:SDL2" } -when ODIN_OS == "darwin" { foreign import lib "system:SDL2" } -when ODIN_OS == "freebsd" { foreign import lib "system:SDL2" } +when ODIN_OS == .Windows { + foreign import lib "SDL2.lib" +} else { + foreign import lib "system:SDL2" +} version :: struct { major: u8, /**< major version */ @@ -314,4 +315,4 @@ foreign lib { IsShapedWindow :: proc(window: ^Window) -> bool --- SetWindowShape :: proc(window: ^Window, shape: ^Surface, shape_mode: ^WindowShapeMode) -> c.int --- GetShapedWindowMode :: proc(window: ^Window, shape_mode: ^WindowShapeMode) -> c.int --- -} \ No newline at end of file +} diff --git a/vendor/sdl2/sdl_audio.odin b/vendor/sdl2/sdl_audio.odin index 2c5b7fedb..28a59d947 100644 --- a/vendor/sdl2/sdl_audio.odin +++ b/vendor/sdl2/sdl_audio.odin @@ -2,10 +2,11 @@ package sdl2 import "core:c" -when ODIN_OS == "windows" { foreign import lib "SDL2.lib" } -when ODIN_OS == "linux" { foreign import lib "system:SDL2" } -when ODIN_OS == "darwin" { foreign import lib "system:SDL2" } -when ODIN_OS == "freebsd" { foreign import lib "system:SDL2" } +when ODIN_OS == .Windows { + foreign import lib "SDL2.lib" +} else { + foreign import lib "system:SDL2" +} /** * \brief Audio format flags. diff --git a/vendor/sdl2/sdl_blendmode.odin b/vendor/sdl2/sdl_blendmode.odin index 3fb7c2e83..4fde5111b 100644 --- a/vendor/sdl2/sdl_blendmode.odin +++ b/vendor/sdl2/sdl_blendmode.odin @@ -2,10 +2,11 @@ package sdl2 import "core:c" -when ODIN_OS == "windows" { foreign import lib "SDL2.lib" } -when ODIN_OS == "linux" { foreign import lib "system:SDL2" } -when ODIN_OS == "darwin" { foreign import lib "system:SDL2" } -when ODIN_OS == "freebsd" { foreign import lib "system:SDL2" } +when ODIN_OS == .Windows { + foreign import lib "SDL2.lib" +} else { + foreign import lib "system:SDL2" +} /** * \brief The blend mode used in SDL_RenderCopy() and drawing operations. @@ -62,4 +63,4 @@ BlendFactor :: enum c.int { foreign lib { ComposeCustomBlendMode :: proc(srcColorFactor, dstColorFactor: BlendFactor, colorOperation: BlendOperation, srcAlphaFactor, dstAlphaFactor: BlendFactor, alphaOperation: BlendOperation) -> BlendMode --- -} \ No newline at end of file +} diff --git a/vendor/sdl2/sdl_cpuinfo.odin b/vendor/sdl2/sdl_cpuinfo.odin index 5fe5cf16c..c5175e4d5 100644 --- a/vendor/sdl2/sdl_cpuinfo.odin +++ b/vendor/sdl2/sdl_cpuinfo.odin @@ -2,10 +2,11 @@ package sdl2 import "core:c" -when ODIN_OS == "windows" { foreign import lib "SDL2.lib" } -when ODIN_OS == "linux" { foreign import lib "system:SDL2" } -when ODIN_OS == "darwin" { foreign import lib "system:SDL2" } -when ODIN_OS == "freebsd" { foreign import lib "system:SDL2" } +when ODIN_OS == .Windows { + foreign import lib "SDL2.lib" +} else { + foreign import lib "system:SDL2" +} /* This is a guess for the cacheline size used for padding. * Most x86 processors have a 64 byte cache line. @@ -41,4 +42,4 @@ foreign lib { SIMDAlloc :: proc(len: c.size_t) -> rawptr --- SIMDRealloc :: proc(mem: rawptr, len: c.size_t) -> rawptr --- SIMDFree :: proc(ptr: rawptr) --- -} \ No newline at end of file +} diff --git a/vendor/sdl2/sdl_events.odin b/vendor/sdl2/sdl_events.odin index 535269656..60daaea56 100644 --- a/vendor/sdl2/sdl_events.odin +++ b/vendor/sdl2/sdl_events.odin @@ -2,10 +2,11 @@ package sdl2 import "core:c" -when ODIN_OS == "windows" { foreign import lib "SDL2.lib" } -when ODIN_OS == "linux" { foreign import lib "system:SDL2" } -when ODIN_OS == "darwin" { foreign import lib "system:SDL2" } -when ODIN_OS == "freebsd" { foreign import lib "system:SDL2" } +when ODIN_OS == .Windows { + foreign import lib "SDL2.lib" +} else { + foreign import lib "system:SDL2" +} RELEASED :: 0 PRESSED :: 1 @@ -476,7 +477,7 @@ DISABLE :: 0 ENABLE :: 1 -GetEventState :: #force_inline proc "c" (type: EventType) -> u8 { return EventState(type, QUERY) } +GetEventState :: #force_inline proc "c" (type: EventType) -> b8 { return EventState(type, QUERY) } @(default_calling_convention="c", link_prefix="SDL_") @@ -487,15 +488,15 @@ foreign lib { HasEvents :: proc(minType, maxType: EventType) -> bool --- FlushEvent :: proc(type: EventType) --- FlushEvents :: proc(minType, maxType: EventType) --- - PollEvent :: proc(event: ^Event) -> c.int --- - WaitEvent :: proc(event: ^Event) -> c.int --- - WaitEventTimeout :: proc(event: ^Event, timeout: c.int) -> c.int --- - PushEvent :: proc(event: ^Event) -> c.int --- + PollEvent :: proc(event: ^Event) -> bool --- // original return value is c.int + WaitEvent :: proc(event: ^Event) -> bool --- // original return value is c.int + WaitEventTimeout :: proc(event: ^Event, timeout: c.int) -> bool --- // original return value is c.int + PushEvent :: proc(event: ^Event) -> bool --- // original return value is c.int SetEventFilter :: proc(filter: EventFilter, userdata: rawptr) --- GetEventFilter :: proc(filter: ^EventFilter, userdata: ^rawptr) -> bool --- AddEventWatch :: proc(filter: EventFilter, userdata: rawptr) --- DelEventWatch :: proc(filter: EventFilter, userdata: rawptr) --- FilterEvents :: proc(filter: EventFilter, userdata: rawptr) --- - EventState :: proc(type: EventType, state: c.int) -> u8 --- + EventState :: proc(type: EventType, state: c.int) -> b8 --- // original return value is u8 RegisterEvents :: proc(numevents: c.int) -> u32 --- -} \ No newline at end of file +} diff --git a/vendor/sdl2/sdl_gamecontroller.odin b/vendor/sdl2/sdl_gamecontroller.odin index 747cd1af6..8772faa26 100644 --- a/vendor/sdl2/sdl_gamecontroller.odin +++ b/vendor/sdl2/sdl_gamecontroller.odin @@ -2,10 +2,11 @@ package sdl2 import "core:c" -when ODIN_OS == "windows" { foreign import lib "SDL2.lib" } -when ODIN_OS == "linux" { foreign import lib "system:SDL2" } -when ODIN_OS == "darwin" { foreign import lib "system:SDL2" } -when ODIN_OS == "freebsd" { foreign import lib "system:SDL2" } +when ODIN_OS == .Windows { + foreign import lib "SDL2.lib" +} else { + foreign import lib "system:SDL2" +} GameController :: struct {} @@ -117,7 +118,7 @@ foreign lib { GameControllerGetStringForAxis :: proc(axis: GameControllerAxis) -> cstring --- GameControllerGetBindForAxis :: proc(gamecontroller: ^GameController, axis: GameControllerAxis) -> GameControllerButtonBind--- GameControllerHasAxis :: proc(gamecontroller: ^GameController, axis: GameControllerAxis) -> bool --- - GameControllerGetAxis :: proc(gamecontroller: ^GameController, axis: GameControllerAxis) -> i16 --- + GameControllerGetAxis :: proc(gamecontroller: ^GameController, axis: GameControllerAxis) -> i16 --- GameControllerGetButtonFromString :: proc(str: cstring) -> GameControllerButton --- GameControllerGetStringForButton :: proc(button: GameControllerButton) -> cstring --- diff --git a/vendor/sdl2/sdl_gesture_haptic.odin b/vendor/sdl2/sdl_gesture_haptic.odin index 0d257a525..a21e0df06 100644 --- a/vendor/sdl2/sdl_gesture_haptic.odin +++ b/vendor/sdl2/sdl_gesture_haptic.odin @@ -2,10 +2,11 @@ package sdl2 import "core:c" -when ODIN_OS == "windows" { foreign import lib "SDL2.lib" } -when ODIN_OS == "linux" { foreign import lib "system:SDL2" } -when ODIN_OS == "darwin" { foreign import lib "system:SDL2" } -when ODIN_OS == "freebsd" { foreign import lib "system:SDL2" } +when ODIN_OS == .Windows { + foreign import lib "SDL2.lib" +} else { + foreign import lib "system:SDL2" +} // Gesture @@ -259,4 +260,4 @@ foreign lib { HapticRumbleInit :: proc(haptic: ^Haptic) -> c.int --- HapticRumblePlay :: proc(haptic: ^Haptic, strength: f32, length: u32) -> c.int --- HapticRumbleStop :: proc(haptic: ^Haptic) -> c.int --- -} \ No newline at end of file +} diff --git a/vendor/sdl2/sdl_hints.odin b/vendor/sdl2/sdl_hints.odin index b5793bb02..913d4ea12 100644 --- a/vendor/sdl2/sdl_hints.odin +++ b/vendor/sdl2/sdl_hints.odin @@ -2,10 +2,11 @@ package sdl2 import "core:c" -when ODIN_OS == "windows" { foreign import lib "SDL2.lib" } -when ODIN_OS == "linux" { foreign import lib "system:SDL2" } -when ODIN_OS == "darwin" { foreign import lib "system:SDL2" } -when ODIN_OS == "freebsd" { foreign import lib "system:SDL2" } +when ODIN_OS == .Windows { + foreign import lib "SDL2.lib" +} else { + foreign import lib "system:SDL2" +} HINT_ACCELEROMETER_AS_JOYSTICK :: "SDL_ACCELEROMETER_AS_JOYSTICK" HINT_ALLOW_ALT_TAB_WHILE_GRABBED :: "SDL_ALLOW_ALT_TAB_WHILE_GRABBED" @@ -146,4 +147,4 @@ foreign lib { AddHintCallback :: proc(name: cstring, callback: HintCallback, userdata: rawptr) --- DelHintCallback :: proc(name: cstring, callback: HintCallback, userdata: rawptr) --- ClearHints :: proc() --- -} \ No newline at end of file +} diff --git a/vendor/sdl2/sdl_joystick.odin b/vendor/sdl2/sdl_joystick.odin index d2bb624b7..35ca5cdcc 100644 --- a/vendor/sdl2/sdl_joystick.odin +++ b/vendor/sdl2/sdl_joystick.odin @@ -2,10 +2,11 @@ package sdl2 import "core:c" -when ODIN_OS == "windows" { foreign import lib "SDL2.lib" } -when ODIN_OS == "linux" { foreign import lib "system:SDL2" } -when ODIN_OS == "darwin" { foreign import lib "system:SDL2" } -when ODIN_OS == "freebsd" { foreign import lib "system:SDL2" } +when ODIN_OS == .Windows { + foreign import lib "SDL2.lib" +} else { + foreign import lib "system:SDL2" +} Joystick :: struct {} @@ -106,4 +107,4 @@ foreign lib { JoystickSendEffect :: proc(joystick: ^Joystick, data: rawptr, size: c.int) -> c.int --- JoystickClose :: proc(joystick: ^Joystick) --- JoystickCurrentPowerLevel :: proc(joystick: ^Joystick) -> JoystickPowerLevel --- -} \ No newline at end of file +} diff --git a/vendor/sdl2/sdl_keyboard.odin b/vendor/sdl2/sdl_keyboard.odin index 86112863a..f880286aa 100644 --- a/vendor/sdl2/sdl_keyboard.odin +++ b/vendor/sdl2/sdl_keyboard.odin @@ -2,10 +2,11 @@ package sdl2 import "core:c" -when ODIN_OS == "windows" { foreign import lib "SDL2.lib" } -when ODIN_OS == "linux" { foreign import lib "system:SDL2" } -when ODIN_OS == "darwin" { foreign import lib "system:SDL2" } -when ODIN_OS == "freebsd" { foreign import lib "system:SDL2" } +when ODIN_OS == .Windows { + foreign import lib "SDL2.lib" +} else { + foreign import lib "system:SDL2" +} Keysym :: struct { scancode: Scancode, /**< SDL physical key code - see ::SDL_Scancode for details */ diff --git a/vendor/sdl2/sdl_keycode.odin b/vendor/sdl2/sdl_keycode.odin index 73637f072..c03fdc2a9 100644 --- a/vendor/sdl2/sdl_keycode.odin +++ b/vendor/sdl2/sdl_keycode.odin @@ -327,4 +327,4 @@ KMOD_RESERVED :: Keymod{.RESERVED} KMOD_CTRL :: Keymod{.LCTRL, .RCTRL} KMOD_SHIFT :: Keymod{.LSHIFT, .RSHIFT} KMOD_ALT :: Keymod{.LALT, .RALT} -KMOD_GUI :: Keymod{.LGUI, .RGUI}; \ No newline at end of file +KMOD_GUI :: Keymod{.LGUI, .RGUI} diff --git a/vendor/sdl2/sdl_log.odin b/vendor/sdl2/sdl_log.odin index b9f8a8d7d..09b7eaef0 100644 --- a/vendor/sdl2/sdl_log.odin +++ b/vendor/sdl2/sdl_log.odin @@ -2,10 +2,11 @@ package sdl2 import "core:c" -when ODIN_OS == "windows" { foreign import lib "SDL2.lib" } -when ODIN_OS == "linux" { foreign import lib "system:SDL2" } -when ODIN_OS == "darwin" { foreign import lib "system:SDL2" } -when ODIN_OS == "freebsd" { foreign import lib "system:SDL2" } +when ODIN_OS == .Windows { + foreign import lib "SDL2.lib" +} else { + foreign import lib "system:SDL2" +} MAX_LOG_MESSAGE :: 4096 @@ -74,4 +75,4 @@ foreign lib { // LogMessageV :: proc(category: c.int, priority: LogPriority, fmt: cstring, ap: va_list) --- LogGetOutputFunction :: proc(callback: ^LogOutputFunction, userdata: ^rawptr) --- LogSetOutputFunction :: proc(callback: LogOutputFunction, userdata: rawptr) --- -} \ No newline at end of file +} diff --git a/vendor/sdl2/sdl_messagebox.odin b/vendor/sdl2/sdl_messagebox.odin index d8a07c1e2..6228704ac 100644 --- a/vendor/sdl2/sdl_messagebox.odin +++ b/vendor/sdl2/sdl_messagebox.odin @@ -2,10 +2,11 @@ package sdl2 import "core:c" -when ODIN_OS == "windows" { foreign import lib "SDL2.lib" } -when ODIN_OS == "linux" { foreign import lib "system:SDL2" } -when ODIN_OS == "darwin" { foreign import lib "system:SDL2" } -when ODIN_OS == "freebsd" { foreign import lib "system:SDL2" } +when ODIN_OS == .Windows { + foreign import lib "SDL2.lib" +} else { + foreign import lib "system:SDL2" +} MessageBoxFlag :: enum u32 { _ = 0, diff --git a/vendor/sdl2/sdl_metal.odin b/vendor/sdl2/sdl_metal.odin index 87a0313ef..1eccf7f5a 100644 --- a/vendor/sdl2/sdl_metal.odin +++ b/vendor/sdl2/sdl_metal.odin @@ -2,10 +2,11 @@ package sdl2 import "core:c" -when ODIN_OS == "windows" { foreign import lib "SDL2.lib" } -when ODIN_OS == "linux" { foreign import lib "system:SDL2" } -when ODIN_OS == "darwin" { foreign import lib "system:SDL2" } -when ODIN_OS == "freebsd" { foreign import lib "system:SDL2" } +when ODIN_OS == .Windows { + foreign import lib "SDL2.lib" +} else { + foreign import lib "system:SDL2" +} MetalView :: distinct rawptr @@ -15,4 +16,4 @@ foreign lib { Metal_DestroyView :: proc(view: MetalView) --- Metal_GetLayer :: proc(view: MetalView) -> rawptr --- Metal_GetDrawableSize :: proc(window: ^Window, w, h: ^c.int) --- -} \ No newline at end of file +} diff --git a/vendor/sdl2/sdl_mouse.odin b/vendor/sdl2/sdl_mouse.odin index d667addc8..13220c94b 100644 --- a/vendor/sdl2/sdl_mouse.odin +++ b/vendor/sdl2/sdl_mouse.odin @@ -2,10 +2,11 @@ package sdl2 import "core:c" -when ODIN_OS == "windows" { foreign import lib "SDL2.lib" } -when ODIN_OS == "linux" { foreign import lib "system:SDL2" } -when ODIN_OS == "darwin" { foreign import lib "system:SDL2" } -when ODIN_OS == "freebsd" { foreign import lib "system:SDL2" } +when ODIN_OS == .Windows { + foreign import lib "SDL2.lib" +} else { + foreign import lib "system:SDL2" +} Cursor :: struct {} @@ -61,4 +62,4 @@ foreign lib { GetDefaultCursor :: proc() -> ^Cursor --- FreeCursor :: proc(cursor: ^Cursor) --- ShowCursor :: proc(toggle: c.int) -> c.int --- -} \ No newline at end of file +} diff --git a/vendor/sdl2/sdl_mutex.odin b/vendor/sdl2/sdl_mutex.odin index 80d62f7c8..1fd5849e0 100644 --- a/vendor/sdl2/sdl_mutex.odin +++ b/vendor/sdl2/sdl_mutex.odin @@ -2,10 +2,11 @@ package sdl2 import "core:c" -when ODIN_OS == "windows" { foreign import lib "SDL2.lib" } -when ODIN_OS == "linux" { foreign import lib "system:SDL2" } -when ODIN_OS == "darwin" { foreign import lib "system:SDL2" } -when ODIN_OS == "freebsd" { foreign import lib "system:SDL2" } +when ODIN_OS == .Windows { + foreign import lib "SDL2.lib" +} else { + foreign import lib "system:SDL2" +} MUTEX_TIMEDOUT :: 1 MUTEX_MAXWAIT :: ~u32(0) @@ -41,4 +42,4 @@ foreign lib { CondBroadcast :: proc(cv: ^cond) -> c.int --- CondWait :: proc(cv: ^cond, m: ^mutex) -> c.int --- CondWaitTimeout :: proc(cv: ^cond, m: ^mutex, ms: u32) -> c.int --- -} \ No newline at end of file +} diff --git a/vendor/sdl2/sdl_pixels.odin b/vendor/sdl2/sdl_pixels.odin index 22f6db440..8ee06aa1a 100644 --- a/vendor/sdl2/sdl_pixels.odin +++ b/vendor/sdl2/sdl_pixels.odin @@ -2,10 +2,11 @@ package sdl2 import "core:c" -when ODIN_OS == "windows" { foreign import lib "SDL2.lib" } -when ODIN_OS == "linux" { foreign import lib "system:SDL2" } -when ODIN_OS == "darwin" { foreign import lib "system:SDL2" } -when ODIN_OS == "freebsd" { foreign import lib "system:SDL2" } +when ODIN_OS == .Windows { + foreign import lib "SDL2.lib" +} else { + foreign import lib "system:SDL2" +} ALPHA_OPAQUE :: 255 ALPHA_TRANSPARENT :: 0 @@ -234,4 +235,4 @@ foreign lib { GetRGB :: proc(pixel: u32, format: ^PixelFormat, r, g, b: ^u8) --- GetRGBA :: proc(pixel: u32, format: ^PixelFormat, r, g, b, a: ^u8) --- CalculateGammaRamp :: proc(gamma: f32, ramp: ^[256]u16) --- -} \ No newline at end of file +} diff --git a/vendor/sdl2/sdl_rect.odin b/vendor/sdl2/sdl_rect.odin index 929897c26..852309cd2 100644 --- a/vendor/sdl2/sdl_rect.odin +++ b/vendor/sdl2/sdl_rect.odin @@ -2,10 +2,11 @@ package sdl2 import "core:c" -when ODIN_OS == "windows" { foreign import lib "SDL2.lib" } -when ODIN_OS == "linux" { foreign import lib "system:SDL2" } -when ODIN_OS == "darwin" { foreign import lib "system:SDL2" } -when ODIN_OS == "freebsd" { foreign import lib "system:SDL2" } +when ODIN_OS == .Windows { + foreign import lib "SDL2.lib" +} else { + foreign import lib "system:SDL2" +} Point :: struct { x: c.int, @@ -47,4 +48,4 @@ foreign lib { UnionRect :: proc(A, B: ^Rect, result: ^Rect) --- EnclosePoints :: proc(points: [^]Point, count: c.int, clip: ^Rect, result: ^Rect) -> bool --- IntersectRectAndLine :: proc(rect: ^Rect, X1, Y1, X2, Y2: ^c.int) -> bool --- -} \ No newline at end of file +} diff --git a/vendor/sdl2/sdl_render.odin b/vendor/sdl2/sdl_render.odin index c92fd3eda..57845235b 100644 --- a/vendor/sdl2/sdl_render.odin +++ b/vendor/sdl2/sdl_render.odin @@ -2,10 +2,11 @@ package sdl2 import "core:c" -when ODIN_OS == "windows" { foreign import lib "SDL2.lib" } -when ODIN_OS == "linux" { foreign import lib "system:SDL2" } -when ODIN_OS == "darwin" { foreign import lib "system:SDL2" } -when ODIN_OS == "freebsd" { foreign import lib "system:SDL2" } +when ODIN_OS == .Windows { + foreign import lib "SDL2.lib" +} else { + foreign import lib "system:SDL2" +} RendererFlag :: enum u32 { SOFTWARE = 0, /**< The renderer is a software fallback */ @@ -140,4 +141,4 @@ foreign lib { GL_UnbindTexture :: proc(texture: ^Texture) -> c.int --- RenderGetMetalLayer :: proc(renderer: ^Renderer) -> rawptr --- RenderGetMetalCommandEncoder :: proc(renderer: ^Renderer) -> rawptr --- -} \ No newline at end of file +} diff --git a/vendor/sdl2/sdl_rwops.odin b/vendor/sdl2/sdl_rwops.odin index 590815c90..86fb23c75 100644 --- a/vendor/sdl2/sdl_rwops.odin +++ b/vendor/sdl2/sdl_rwops.odin @@ -2,10 +2,11 @@ package sdl2 import "core:c" -when ODIN_OS == "windows" { foreign import lib "SDL2.lib" } -when ODIN_OS == "linux" { foreign import lib "system:SDL2" } -when ODIN_OS == "darwin" { foreign import lib "system:SDL2" } -when ODIN_OS == "freebsd" { foreign import lib "system:SDL2" } +when ODIN_OS == .Windows { + foreign import lib "SDL2.lib" +} else { + foreign import lib "system:SDL2" +} /* RWops Types */ RWOPS_UNKNOWN :: 0 /**< Unknown stream type */ @@ -105,4 +106,4 @@ foreign lib { WriteBE32 :: proc(dst: ^RWops, value: ^u32) -> c.size_t --- WriteLE64 :: proc(dst: ^RWops, value: ^u64) -> c.size_t --- WriteBE64 :: proc(dst: ^RWops, value: ^u64) -> c.size_t --- -} \ No newline at end of file +} diff --git a/vendor/sdl2/sdl_scancode.odin b/vendor/sdl2/sdl_scancode.odin index d27bba7f7..ca2169778 100644 --- a/vendor/sdl2/sdl_scancode.odin +++ b/vendor/sdl2/sdl_scancode.odin @@ -539,4 +539,4 @@ SCANCODE_APP1 :: Scancode.APP1 SCANCODE_APP2 :: Scancode.APP2 SCANCODE_AUDIOREWIND :: Scancode.AUDIOREWIND -SCANCODE_AUDIOFASTFORWARD :: Scancode.AUDIOFASTFORWARD; \ No newline at end of file +SCANCODE_AUDIOFASTFORWARD :: Scancode.AUDIOFASTFORWARD diff --git a/vendor/sdl2/sdl_stdinc.odin b/vendor/sdl2/sdl_stdinc.odin index bf098a591..97722f4fe 100644 --- a/vendor/sdl2/sdl_stdinc.odin +++ b/vendor/sdl2/sdl_stdinc.odin @@ -5,10 +5,11 @@ import "core:intrinsics" import "core:runtime" _, _ :: intrinsics, runtime -when ODIN_OS == "windows" { foreign import lib "SDL2.lib" } -when ODIN_OS == "linux" { foreign import lib "system:SDL2" } -when ODIN_OS == "darwin" { foreign import lib "system:SDL2" } -when ODIN_OS == "freebsd" { foreign import lib "system:SDL2" } +when ODIN_OS == .Windows { + foreign import lib "SDL2.lib" +} else { + foreign import lib "system:SDL2" +} bool :: distinct b32 #assert(size_of(bool) == size_of(c.int)) @@ -160,4 +161,4 @@ iconv_utf8_ucs2 :: proc "c" (s: string) -> [^]u16 { iconv_utf8_utf32 :: iconv_utf8_ucs4 iconv_utf8_ucs4 :: proc "c" (s: string) -> [^]rune { return cast([^]rune)iconv_string("UCS-4-INTERNAL", "UTF-8", cstring(raw_data(s)), len(s)+1) -} \ No newline at end of file +} diff --git a/vendor/sdl2/sdl_surface.odin b/vendor/sdl2/sdl_surface.odin index c0b20be63..f50de35f7 100644 --- a/vendor/sdl2/sdl_surface.odin +++ b/vendor/sdl2/sdl_surface.odin @@ -2,10 +2,11 @@ package sdl2 import "core:c" -when ODIN_OS == "windows" { foreign import lib "SDL2.lib" } -when ODIN_OS == "linux" { foreign import lib "system:SDL2" } -when ODIN_OS == "darwin" { foreign import lib "system:SDL2" } -when ODIN_OS == "freebsd" { foreign import lib "system:SDL2" } +when ODIN_OS == .Windows { + foreign import lib "SDL2.lib" +} else { + foreign import lib "system:SDL2" +} SWSURFACE :: 0 /**< Just here for compatibility */ PREALLOC :: 0x00000001 /**< Surface uses preallocated memory */ @@ -108,4 +109,4 @@ foreign lib { SetYUVConversionMode :: proc(mode: YUV_CONVERSION_MODE) --- GetYUVConversionMode :: proc() -> YUV_CONVERSION_MODE --- GetYUVConversionModeForResolution :: proc(width, height: c.int) -> YUV_CONVERSION_MODE --- -} \ No newline at end of file +} diff --git a/vendor/sdl2/sdl_system.odin b/vendor/sdl2/sdl_system.odin index 31f1b590b..d9b6b98df 100644 --- a/vendor/sdl2/sdl_system.odin +++ b/vendor/sdl2/sdl_system.odin @@ -2,10 +2,11 @@ package sdl2 import "core:c" -when ODIN_OS == "windows" { foreign import lib "SDL2.lib" } -when ODIN_OS == "linux" { foreign import lib "system:SDL2" } -when ODIN_OS == "darwin" { foreign import lib "system:SDL2" } -when ODIN_OS == "freebsd" { foreign import lib "system:SDL2" } +when ODIN_OS == .Windows { + foreign import lib "SDL2.lib" +} else { + foreign import lib "system:SDL2" +} // General @(default_calling_convention="c", link_prefix="SDL_") @@ -122,4 +123,4 @@ foreign lib { AndroidGetExternalStoragePath :: proc() -> cstring --- AndroidRequestPermission :: proc(permission: cstring) -> bool --- AndroidShowToast :: proc(message: cstring, duration, gravity, xoffset, yoffset: c.int) -> c.int --- -} \ No newline at end of file +} diff --git a/vendor/sdl2/sdl_syswm.odin b/vendor/sdl2/sdl_syswm.odin index e374e72b7..62ca9d628 100644 --- a/vendor/sdl2/sdl_syswm.odin +++ b/vendor/sdl2/sdl_syswm.odin @@ -2,10 +2,11 @@ package sdl2 import "core:c" -when ODIN_OS == "windows" { foreign import lib "SDL2.lib" } -when ODIN_OS == "linux" { foreign import lib "system:SDL2" } -when ODIN_OS == "darwin" { foreign import lib "system:SDL2" } -when ODIN_OS == "freebsd" { foreign import lib "system:SDL2" } +when ODIN_OS == .Windows { + foreign import lib "SDL2.lib" +} else { + foreign import lib "system:SDL2" +} SYSWM_TYPE :: enum c.int { UNKNOWN, @@ -105,4 +106,4 @@ SysWMinfo :: struct { @(default_calling_convention="c", link_prefix="SDL_") foreign lib { GetWindowWMInfo :: proc(window: ^Window, info: ^SysWMinfo) -> bool --- -} \ No newline at end of file +} diff --git a/vendor/sdl2/sdl_thread.odin b/vendor/sdl2/sdl_thread.odin index cbaf3ac00..5d1c0bd37 100644 --- a/vendor/sdl2/sdl_thread.odin +++ b/vendor/sdl2/sdl_thread.odin @@ -2,10 +2,11 @@ package sdl2 import "core:c" -when ODIN_OS == "windows" { foreign import lib "SDL2.lib" } -when ODIN_OS == "linux" { foreign import lib "system:SDL2" } -when ODIN_OS == "darwin" { foreign import lib "system:SDL2" } -when ODIN_OS == "freebsd" { foreign import lib "system:SDL2" } +when ODIN_OS == .Windows { + foreign import lib "SDL2.lib" +} else { + foreign import lib "system:SDL2" +} Thread :: struct {} diff --git a/vendor/sdl2/sdl_timer.odin b/vendor/sdl2/sdl_timer.odin index 5b26c7346..d71ed2da5 100644 --- a/vendor/sdl2/sdl_timer.odin +++ b/vendor/sdl2/sdl_timer.odin @@ -2,10 +2,11 @@ package sdl2 import "core:c" -when ODIN_OS == "windows" { foreign import lib "SDL2.lib" } -when ODIN_OS == "linux" { foreign import lib "system:SDL2" } -when ODIN_OS == "darwin" { foreign import lib "system:SDL2" } -when ODIN_OS == "freebsd" { foreign import lib "system:SDL2" } +when ODIN_OS == .Windows { + foreign import lib "SDL2.lib" +} else { + foreign import lib "system:SDL2" +} TimerCallback :: proc "c" (interval: u32, param: rawptr) -> u32 TimerID :: distinct c.int @@ -22,4 +23,4 @@ foreign lib { Delay :: proc(ms: u32) --- AddTimer :: proc(interval: u32, callback: TimerCallback, param: rawptr) -> TimerID --- RemoveTimer :: proc(id: TimerID) -> bool --- -} \ No newline at end of file +} diff --git a/vendor/sdl2/sdl_touch.odin b/vendor/sdl2/sdl_touch.odin index c393b74ef..f2a8cc695 100644 --- a/vendor/sdl2/sdl_touch.odin +++ b/vendor/sdl2/sdl_touch.odin @@ -2,10 +2,11 @@ package sdl2 import "core:c" -when ODIN_OS == "windows" { foreign import lib "SDL2.lib" } -when ODIN_OS == "linux" { foreign import lib "system:SDL2" } -when ODIN_OS == "darwin" { foreign import lib "system:SDL2" } -when ODIN_OS == "freebsd" { foreign import lib "system:SDL2" } +when ODIN_OS == .Windows { + foreign import lib "SDL2.lib" +} else { + foreign import lib "system:SDL2" +} TouchID :: distinct i64 FingerID :: distinct i64 @@ -34,4 +35,4 @@ foreign lib { GetTouchDeviceType :: proc(touchID: TouchID) -> TouchDeviceType --- GetNumTouchFingers :: proc(touchID: TouchID) -> c.int --- GetTouchFinger :: proc(touchID: TouchID, index: c.int) -> ^Finger --- -} \ No newline at end of file +} diff --git a/vendor/sdl2/sdl_video.odin b/vendor/sdl2/sdl_video.odin index 97a134739..86b564541 100644 --- a/vendor/sdl2/sdl_video.odin +++ b/vendor/sdl2/sdl_video.odin @@ -2,10 +2,11 @@ package sdl2 import "core:c" -when ODIN_OS == "windows" { foreign import lib "SDL2.lib" } -when ODIN_OS == "linux" { foreign import lib "system:SDL2" } -when ODIN_OS == "darwin" { foreign import lib "system:SDL2" } -when ODIN_OS == "freebsd" { foreign import lib "system:SDL2" } +when ODIN_OS == .Windows { + foreign import lib "SDL2.lib" +} else { + foreign import lib "system:SDL2" +} DisplayMode :: struct { format: u32, /**< pixel format */ @@ -310,4 +311,4 @@ foreign lib { // Used by vendor:OpenGL gl_set_proc_address :: proc(p: rawptr, name: cstring) { (^rawptr)(p)^ = GL_GetProcAddress(name) -} \ No newline at end of file +} diff --git a/vendor/sdl2/sdl_vulkan.odin b/vendor/sdl2/sdl_vulkan.odin index 97a41bacf..33bb8e51c 100644 --- a/vendor/sdl2/sdl_vulkan.odin +++ b/vendor/sdl2/sdl_vulkan.odin @@ -3,10 +3,11 @@ package sdl2 import "core:c" import vk "vendor:vulkan" -when ODIN_OS == "windows" { foreign import lib "SDL2.lib" } -when ODIN_OS == "linux" { foreign import lib "system:SDL2" } -when ODIN_OS == "darwin" { foreign import lib "system:SDL2" } -when ODIN_OS == "freebsd" { foreign import lib "system:SDL2" } +when ODIN_OS == .Windows { + foreign import lib "SDL2.lib" +} else { + foreign import lib "system:SDL2" +} VkInstance :: vk.Instance VkSurfaceKHR :: vk.SurfaceKHR @@ -22,4 +23,4 @@ foreign lib { Vulkan_GetInstanceExtensions :: proc(window: ^Window, pCount: ^c.uint, pNames: [^]cstring) -> bool --- Vulkan_CreateSurface :: proc(window: ^Window, instance: VkInstance, surface: ^VkSurfaceKHR) -> bool --- Vulkan_GetDrawableSize :: proc(window: ^Window, w, h: ^c.int) --- -} \ No newline at end of file +} diff --git a/vendor/sdl2/ttf/SDL2_ttf.dll b/vendor/sdl2/ttf/SDL2_ttf.dll index 575636a91..2acc0e5bc 100644 Binary files a/vendor/sdl2/ttf/SDL2_ttf.dll and b/vendor/sdl2/ttf/SDL2_ttf.dll differ diff --git a/vendor/sdl2/ttf/SDL2_ttf.lib b/vendor/sdl2/ttf/SDL2_ttf.lib index 28810a7bc..bd53219e9 100644 Binary files a/vendor/sdl2/ttf/SDL2_ttf.lib and b/vendor/sdl2/ttf/SDL2_ttf.lib differ diff --git a/vendor/sdl2/ttf/sdl_ttf.odin b/vendor/sdl2/ttf/sdl_ttf.odin index 6b41f07c2..ca9beded0 100644 --- a/vendor/sdl2/ttf/sdl_ttf.odin +++ b/vendor/sdl2/ttf/sdl_ttf.odin @@ -3,10 +3,11 @@ package sdl2_ttf import "core:c" import SDL ".." -when ODIN_OS == "windows" { foreign import lib "SDL2_ttf.lib" } -when ODIN_OS == "linux" { foreign import lib "system:SDL2_ttf" } -when ODIN_OS == "darwin" { foreign import lib "system:SDL2_ttf" } -when ODIN_OS == "freebsd" { foreign import lib "system:SDL2_ttf" } +when ODIN_OS == .Windows { + foreign import lib "SDL2_ttf.lib" +} else { + foreign import lib "system:SDL2_ttf" +} bool :: SDL.bool @@ -14,7 +15,7 @@ bool :: SDL.bool MAJOR_VERSION :: 2 MINOR_VERSION :: 0 -PATCHLEVEL :: 15 +PATCHLEVEL :: 18 UNICODE_BOM_NATIVE :: 0xFEFF UNICODE_BOM_SWAPPED :: 0xFFFE @@ -163,4 +164,4 @@ foreign lib { SetFontSDF :: proc(font: ^Font, on_off: bool) -> c.int --- GetFontSDF :: proc(font: ^Font) -> bool --- -} \ No newline at end of file +} diff --git a/vendor/stb/easy_font/stb_easy_font.odin b/vendor/stb/easy_font/stb_easy_font.odin index 3a60301e5..0208ca316 100644 --- a/vendor/stb/easy_font/stb_easy_font.odin +++ b/vendor/stb/easy_font/stb_easy_font.odin @@ -1,32 +1,105 @@ package stb_easy_font -// Source port of stb_easy_font.h +/* + Source port of stb_easy_font.h + + Original port: gingerBill + Bugfixes: Florian Behr & Jeroen van Rijn + Additions: Jeroen van Rijn + + Changelog: + 2022-04-03 + Bug fixes + Add `print(x, y, text, color, quad_buffer)` version that takes `[]quad`. + (Same internal memory layout as []u8 API, but more convenient for the caller.) + Add optional `scale := f32(1.0)` param to `print` to embiggen the glyph quads. + + 2021-09-14 + Original Odin version +*/ + +/* + // Example for use with vendor:raylib + + quads: [999]easy_font.Quad = --- + + color := rl.GREEN + c := transmute(easy_font.Color)color + num_quads := easy_font.print(10, 60, TEXT, c, quads[:]) + + for q in quads[:num_quads] { + tl := q.tl.v + br := q.br.v + color = transmute(rl.Color)q.tl.c + + r := rl.Rectangle{x = tl.x, y = tl.y, width = br.x - tl.x, height = br.y - tl.y} + + // Yes, we could just use the `color` from above, but this shows how to get it back from the vertex. + // And in practice this code will likely not live as close to the `easy_font` call. + rl.DrawRectangleRec(r, color) + } +*/ import "core:math" +import "core:mem" -color :: struct { - c: [4]u8, +Color :: [4]u8 + +Vertex :: struct { + v: [3]f32, + c: Color, } +#assert(size_of(Vertex) == 16) -draw_segs :: proc(x, y: f32, segs: []u8, vertical: bool, c: color, vbuf: []byte, offset: int) -> int { - x, y, offset := x, y, offset - for i in 0..>31) & 1) - if n != 0 && offset+64 <= len(vbuf) { - y0 := y + f32(segs[i]>>4) - for j in 0..<4 { - (^f32)(&vbuf[offset+0])^ = x + ((vertical ? 1 : len) if j==1 || j==2 else 0) - (^f32)(&vbuf[offset+4])^ = y0 + ((vertical ? len : 1) if j >= 2 else 0) - (^f32)(&vbuf[offset+8])^ = 0 - (^color)(&vbuf[offset+12])^ = c - offset += 16 - } +Quad :: struct #packed { + tl: Vertex, + tr: Vertex, + br: Vertex, + bl: Vertex, +} +#assert(size_of(Quad) == 64) + +// Same memory layout, but takes a []quad instead of []byte +draw_segs_quad_buffer :: proc(x, y: f32, segs: []u8, vertical: bool, c: Color, buf: []Quad, start_offset: int, scale := f32(1.0)) -> (quads: int) { + x, y := x, y + quads = start_offset + + for seg in segs { + stroke_length := f32(seg & 7) * scale + x += f32((seg >> 3) & 1) * scale + if stroke_length != 0 && quads + 1 <= len(buf) { + y0 := y + (f32(seg >> 4) * scale) + + horz := scale if vertical else stroke_length + vert := stroke_length if vertical else scale + + buf[quads].tl.c = c + buf[quads].tl.v = { x, y0, 0 } + + buf[quads].tr.c = c + buf[quads].tr.v = { x + horz, y0, 0 } + + buf[quads].br.c = c + buf[quads].br.v = { x + horz, y0 + vert, 0 } + + buf[quads].bl.c = c + buf[quads].bl.v = { x, y0 + vert, 0 } + + quads += 1 } } + return quads +} + +// Compatible with original C API +draw_segs_vertex_buffer :: proc(x, y: f32, segs: []u8, vertical: bool, c: Color, vbuf: []byte, start_offset: int, scale := f32(1.0)) -> (offset: int) { + buf := mem.slice_data_cast([]Quad, vbuf) + offset = draw_segs_quad_buffer(x, y, segs, vertical, c, buf, start_offset / size_of(Quad), scale) * size_of(Quad) return offset } +draw_segs :: proc{ draw_segs_quad_buffer, draw_segs_vertex_buffer } + @(private) _spacing_val := f32(0) @@ -34,13 +107,17 @@ font_spacing :: proc(spacing: f32) { _spacing_val = spacing } -print :: proc(x, y: f32, text: string, color: color, vertex_buffer: []byte) -> int { - x, y := x, y +// Same memory layout, but takes a []quad instead of []byte +print_quad_buffer :: proc(x, y: f32, text: string, color: Color, quad_buffer: []Quad, scale := f32(1.0)) -> (quads: int) { + x, y, color := x, y, color text := text start_x := x - offset := 0 - - for len(text) != 0 && offset < len(vertex_buffer) { + + if color == {} { + color = {255, 255, 255, 255} + } + + for len(text) != 0 && quads < len(quad_buffer) { c := text[0] if c == '\n' { y += 12 @@ -52,17 +129,27 @@ print :: proc(x, y: f32, text: string, color: color, vertex_buffer: []byte) -> i v_seg := charinfo[c-32].v_seg num_h := charinfo[c-32 + 1].h_seg - h_seg num_v := charinfo[c-32 + 1].v_seg - v_seg - offset = draw_segs(x, y_ch, hseg[h_seg:][:num_h], false, color, vertex_buffer, offset) - offset = draw_segs(x, y_ch, hseg[v_seg:][:num_v], true, color, vertex_buffer, offset) - x += f32(advance & 15) - x += _spacing_val + + quads = draw_segs(x, y_ch, hseg[h_seg:][:num_h], false, color, quad_buffer, quads, scale) + quads = draw_segs(x, y_ch, vseg[v_seg:][:num_v], true, color, quad_buffer, quads, scale) + + x += f32(advance & 15) * scale + x += _spacing_val * scale } text = text[1:] } - return offset/64 + return } +// Compatible with original C API +print_vertex_buffer :: proc(x, y: f32, text: string, color: Color, vertex_buffer: []byte, scale := f32(1.0)) -> int { + buf := mem.slice_data_cast([]Quad, vertex_buffer) + return print_quad_buffer(x, y, text, color, buf, scale) +} + +print :: proc{ print_quad_buffer, print_vertex_buffer } + width :: proc(text: string) -> int { length := f32(0) max_length := f32(0) diff --git a/vendor/stb/image/stb_image.odin b/vendor/stb/image/stb_image.odin index 27f4e7201..eedce5f04 100644 --- a/vendor/stb/image/stb_image.odin +++ b/vendor/stb/image/stb_image.odin @@ -75,8 +75,9 @@ foreign stbi { info_from_memory :: proc(buffer: [^]byte, len: c.int, x, y, comp: ^c.int) -> c.int --- info_from_callbacks :: proc(clbk: ^Io_Callbacks, user: rawptr, x, y, comp: ^c.int) -> c.int --- - is_16_bit :: proc(filename: cstring) -> b32 --- - is_16_bit_from_file :: proc(f: ^c.FILE) -> b32 --- + is_16_bit :: proc(filename: cstring) -> b32 --- + is_16_bit_from_file :: proc(f: ^c.FILE) -> b32 --- + is_16_bit_from_memory :: proc(buffer: [^]byte, len: c.int) -> c.int --- // for image formats that explicitly notate that they have premultiplied alpha, // we just return the colors as stored in the file. set this flag to force diff --git a/vendor/stb/rect_pack/stb_rect_pack.odin b/vendor/stb/rect_pack/stb_rect_pack.odin index 4142a73ec..c9f999bf7 100644 --- a/vendor/stb/rect_pack/stb_rect_pack.odin +++ b/vendor/stb/rect_pack/stb_rect_pack.odin @@ -4,8 +4,9 @@ import c "core:c/libc" #assert(size_of(b32) == size_of(c.int)) -when ODIN_OS == "windows" { foreign import lib "../lib/stb_rect_pack.lib" } -when ODIN_OS == "linux" { foreign import lib "../lib/stb_rect_pack.a" } +when ODIN_OS == .Windows { foreign import lib "../lib/stb_rect_pack.lib" } +when ODIN_OS == .Linux { foreign import lib "../lib/stb_rect_pack.a" } +when ODIN_OS == .Darwin { foreign import lib "../lib/stb_rect_pack.a" } Coord :: distinct c.int _MAXVAL :: max(Coord) diff --git a/vendor/stb/src/Makefile b/vendor/stb/src/Makefile index c65aa7263..5504fede9 100644 --- a/vendor/stb/src/Makefile +++ b/vendor/stb/src/Makefile @@ -1,16 +1,16 @@ all: mkdir -p ../lib - gcc -c -O2 -Os -fPIC stb_image.c stb_image_write.c stb_image_resize.c stb_truetype.c stb_rect_pack.c stb_vorbis.c - ar rcs ../lib/stb_image.a stb_image.o - ar rcs ../lib/stb_image_write.a stb_image_write.o - ar rcs ../lib/stb_image_resize.a stb_image_resize.o - ar rcs ../lib/stb_truetype.a stb_truetype.o - ar rcs ../lib/stb_rect_pack.a stb_rect_pack.o - #ar rcs ../lib/stb_vorbis_pack.a stb_vorbis_pack.o - #gcc -fPIC -shared -Wl,-soname=stb_image.so -o ../lib/stb_image.so stb_image.o - #gcc -fPIC -shared -Wl,-soname=stb_image_write.so -o ../lib/stb_image_write.so stb_image_write.o - #gcc -fPIC -shared -Wl,-soname=stb_image_resize.so -o ../lib/stb_image_resize.so stb_image_resize.o - #gcc -fPIC -shared -Wl,-soname=stb_truetype.so -o ../lib/stb_truetype.so stb_image_truetype.o - #gcc -fPIC -shared -Wl,-soname=stb_rect_pack.so -o ../lib/stb_rect_pack.so stb_rect_packl.o - #gcc -fPIC -shared -Wl,-soname=stb_vorbis.so -o ../lib/stb_vorbis.so stb_vorbisl.o + $(CC) -c -O2 -Os -fPIC stb_image.c stb_image_write.c stb_image_resize.c stb_truetype.c stb_rect_pack.c stb_vorbis.c + $(AR) rcs ../lib/stb_image.a stb_image.o + $(AR) rcs ../lib/stb_image_write.a stb_image_write.o + $(AR) rcs ../lib/stb_image_resize.a stb_image_resize.o + $(AR) rcs ../lib/stb_truetype.a stb_truetype.o + $(AR) rcs ../lib/stb_rect_pack.a stb_rect_pack.o + #$(AR) rcs ../lib/stb_vorbis_pack.a stb_vorbis_pack.o + #$(CC) -fPIC -shared -Wl,-soname=stb_image.so -o ../lib/stb_image.so stb_image.o + #$(CC) -fPIC -shared -Wl,-soname=stb_image_write.so -o ../lib/stb_image_write.so stb_image_write.o + #$(CC) -fPIC -shared -Wl,-soname=stb_image_resize.so -o ../lib/stb_image_resize.so stb_image_resize.o + #$(CC) -fPIC -shared -Wl,-soname=stb_truetype.so -o ../lib/stb_truetype.so stb_image_truetype.o + #$(CC) -fPIC -shared -Wl,-soname=stb_rect_pack.so -o ../lib/stb_rect_pack.so stb_rect_packl.o + #$(CC) -fPIC -shared -Wl,-soname=stb_vorbis.so -o ../lib/stb_vorbis.so stb_vorbisl.o rm *.o diff --git a/vendor/stb/truetype/stb_truetype.odin b/vendor/stb/truetype/stb_truetype.odin index 3abb187c2..e0c03268e 100644 --- a/vendor/stb/truetype/stb_truetype.odin +++ b/vendor/stb/truetype/stb_truetype.odin @@ -3,8 +3,9 @@ package stb_truetype import c "core:c" import stbrp "vendor:stb/rect_pack" -when ODIN_OS == "windows" { foreign import stbtt "../lib/stb_truetype.lib" } -when ODIN_OS == "linux" { foreign import stbtt "../lib/stb_truetype.a" } +when ODIN_OS == .Windows { foreign import stbtt "../lib/stb_truetype.lib" } +when ODIN_OS == .Linux { foreign import stbtt "../lib/stb_truetype.a" } +when ODIN_OS == .Darwin { foreign import stbtt "../lib/stb_truetype.a" } /////////////////////////////////////////////////////////////////////////////// @@ -225,7 +226,7 @@ foreign stbtt { // (.ttf) files only contain one font. The number of fonts can be used for // indexing with the previous function where the index is between zero and one // less than the total fonts. If an error occurs, -1 is returned. - GetNumberOfFonts :: proc(data: [^]byte) -> b32 --- + GetNumberOfFonts :: proc(data: [^]byte) -> c.int --- // Each .ttf/.ttc file may have more than one font. Each font has a sequential // index number starting from 0. Call this function to get the font offset for diff --git a/vendor/stb/vorbis/stb_vorbis.odin b/vendor/stb/vorbis/stb_vorbis.odin index 7ec248df5..43b9bc715 100644 --- a/vendor/stb/vorbis/stb_vorbis.odin +++ b/vendor/stb/vorbis/stb_vorbis.odin @@ -3,8 +3,9 @@ package stb_vorbis import c "core:c/libc" -when ODIN_OS == "windows" { foreign import lib "../lib/stb_vorbis.lib" } -when ODIN_OS == "linux" { foreign import lib "../lib/stb_vorbis.a" } +when ODIN_OS == .Windows { foreign import lib "../lib/stb_vorbis.lib" } +when ODIN_OS == .Linux { foreign import lib "../lib/stb_vorbis.a" } +when ODIN_OS == .Darwin { foreign import lib "../lib/stb_vorbis.a" } diff --git a/vendor/vulkan/_gen/create_vulkan_odin_wrapper.py b/vendor/vulkan/_gen/create_vulkan_odin_wrapper.py index 1525f4e15..9a32f5796 100644 --- a/vendor/vulkan/_gen/create_vulkan_odin_wrapper.py +++ b/vendor/vulkan/_gen/create_vulkan_odin_wrapper.py @@ -7,14 +7,14 @@ import os.path import math file_and_urls = [ - ("vk_platform.h", 'https://raw.githubusercontent.com/KhronosGroup/Vulkan-Headers/master/include/vulkan/vk_platform.h', True), - ("vulkan_core.h", 'https://raw.githubusercontent.com/KhronosGroup/Vulkan-Headers/master/include/vulkan/vulkan_core.h', False), - ("vk_layer.h", 'https://raw.githubusercontent.com/KhronosGroup/Vulkan-Headers/master/include/vulkan/vk_layer.h', True), - ("vk_icd.h", 'https://raw.githubusercontent.com/KhronosGroup/Vulkan-Headers/master/include/vulkan/vk_icd.h', True), - ("vulkan_win32.h", 'https://raw.githubusercontent.com/KhronosGroup/Vulkan-Headers/master/include/vulkan/vulkan_win32.h', False), - ("vulkan_metal.h", 'https://raw.githubusercontent.com/KhronosGroup/Vulkan-Headers/master/include/vulkan/vulkan_metal.h', False), - ("vulkan_macos.h", 'https://raw.githubusercontent.com/KhronosGroup/Vulkan-Headers/master/include/vulkan/vulkan_macos.h', False), - ("vulkan_ios.h", 'https://raw.githubusercontent.com/KhronosGroup/Vulkan-Headers/master/include/vulkan/vulkan_ios.h', False), + ("vk_platform.h", 'https://raw.githubusercontent.com/KhronosGroup/Vulkan-Headers/main/include/vulkan/vk_platform.h', True), + ("vulkan_core.h", 'https://raw.githubusercontent.com/KhronosGroup/Vulkan-Headers/main/include/vulkan/vulkan_core.h', False), + ("vk_layer.h", 'https://raw.githubusercontent.com/KhronosGroup/Vulkan-Headers/main/include/vulkan/vk_layer.h', True), + ("vk_icd.h", 'https://raw.githubusercontent.com/KhronosGroup/Vulkan-Headers/main/include/vulkan/vk_icd.h', True), + ("vulkan_win32.h", 'https://raw.githubusercontent.com/KhronosGroup/Vulkan-Headers/main/include/vulkan/vulkan_win32.h', False), + ("vulkan_metal.h", 'https://raw.githubusercontent.com/KhronosGroup/Vulkan-Headers/main/include/vulkan/vulkan_metal.h', False), + ("vulkan_macos.h", 'https://raw.githubusercontent.com/KhronosGroup/Vulkan-Headers/main/include/vulkan/vulkan_macos.h', False), + ("vulkan_ios.h", 'https://raw.githubusercontent.com/KhronosGroup/Vulkan-Headers/main/include/vulkan/vulkan_ios.h', False), ] for file, url, _ in file_and_urls: @@ -125,7 +125,7 @@ def to_snake_case(name): s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name) return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower() -ext_suffixes = ["KHR", "EXT", "AMD", "NV", "NVX", "GOOGLE"] +ext_suffixes = ["KHR", "EXT", "AMD", "NV", "NVX", "GOOGLE", "KHX"] ext_suffixes_title = [ext.title() for ext in ext_suffixes] @@ -254,9 +254,19 @@ def parse_constants(f): f.write("{}{} :: {}\n".format(name, "".rjust(max_len-len(name)), value)) f.write("\n// Vendor Constants\n") - data = re.findall(r"#define VK_((?:"+'|'.join(ext_suffixes)+r")\w+)\s*(.*?)\n", src, re.S) + fixes = '|'.join(ext_suffixes) + inner = r"((?:(?:" + fixes + r")\w+)|(?:\w+" + fixes + r"))" + pattern = r"#define\s+VK_" + inner + r"\s*(.*?)\n" + data = re.findall(pattern, src, re.S) + + number_suffix_re = re.compile(r"(\d+)[UuLlFf]") + max_len = max(len(name) for name, value in data) for name, value in data: + value = remove_prefix(value, 'VK_') + v = number_suffix_re.findall(value) + if v: + value = v[0] f.write("{}{} :: {}\n".format(name, "".rjust(max_len-len(name)), value)) f.write("\n") @@ -444,7 +454,7 @@ procedure_map = {} def parse_procedures(f): data = re.findall(r"typedef (\w+\*?) \(\w+ \*(\w+)\)\((.+?)\);", src, re.S) - ff = [] + group_ff = {"Loader":[], "Misc":[], "Instance":[], "Device":[]} for rt, name, fields in data: proc_name = no_vk(name) @@ -464,18 +474,32 @@ def parse_procedures(f): ts += " -> {}".format(rt_str) procedure_map[proc_name] = ts - ff.append( (proc_name, ts) ) - max_len = max(len(n) for n, t in ff) + fields_types_name = [do_type(t) for t in re.findall(r"(?:\s*|)(.+?)\s*\w+(?:,|$)", fields)] + table_name = fields_types_name[0] + nn = (proc_name, ts) + if table_name in ('Device', 'Queue', 'CommandBuffer') and proc_name != 'GetDeviceProcAddr': + group_ff["Device"].append(nn) + elif table_name in ('Instance', 'PhysicalDevice') or proc_name == 'GetDeviceProcAddr': + group_ff["Instance"].append(nn) + elif table_name in ('rawptr', '', 'DebugReportFlagsEXT') or proc_name == 'GetInstanceProcAddr': + group_ff["Misc"].append(nn) + else: + group_ff["Loader"].append(nn) + f.write("import \"core:c\"\n\n") - f.write("// Procedure Types\n\n"); - for n, t in ff: - f.write("{} :: #type {}\n".format(n.ljust(max_len), t.replace('"c"', '"system"'))) + for group_name, ff in group_ff.items(): + ff.sort() + f.write("// {} Procedure Types\n".format(group_name)) + max_len = max(len(n) for n, t in ff) + for n, t in ff: + f.write("{} :: #type {}\n".format(n.ljust(max_len), t.replace('"c"', '"system"'))) + f.write("\n") def group_functions(f): data = re.findall(r"typedef (\w+\*?) \(\w+ \*(\w+)\)\((.+?)\);", src, re.S) - group_map = {"Instance":[], "Device":[], "Loader":[]} + group_map = {"Loader":[], "Instance":[], "Device":[]} for rt, vkname, fields in data: fields_types_name = [do_type(t) for t in re.findall(r"(?:\s*|)(.+?)\s*\w+(?:,|$)", fields)] @@ -493,6 +517,8 @@ def group_functions(f): pass else: group_map["Loader"].append(nn) + for _, group in group_map.items(): + group.sort() for group_name, group_lines in group_map.items(): f.write("// {} Procedures\n".format(group_name)) @@ -502,7 +528,7 @@ def group_functions(f): f.write('{}: {}\n'.format(remove_prefix(name, "Proc"), name.rjust(max_len))) f.write("\n") - f.write("load_proc_addresses :: proc(set_proc_address: SetProcAddressType) {\n") + f.write("load_proc_addresses_custom :: proc(set_proc_address: SetProcAddressType) {\n") for group_name, group_lines in group_map.items(): f.write("\t// {} Procedures\n".format(group_name)) max_len = max(len(name) for name, _ in group_lines) @@ -514,7 +540,77 @@ def group_functions(f): remove_prefix(vk_name, 'Proc'), )) f.write("\n") - f.write("}\n") + f.write("}\n\n") + + f.write("// Device Procedure VTable\n") + f.write("Device_VTable :: struct {\n") + max_len = max(len(name) for name, _ in group_map["Device"]) + for name, vk_name in group_map["Device"]: + f.write('\t{}: {},\n'.format(remove_prefix(name, "Proc"), name.rjust(max_len))) + f.write("}\n\n") + + f.write("load_proc_addresses_device_vtable :: proc(device: Device, vtable: ^Device_VTable) {\n") + for name, vk_name in group_map["Device"]: + k = max_len - len(name) + f.write('\tvtable.{}{} = auto_cast GetDeviceProcAddr(device, "vk{}")\n'.format( + remove_prefix(name, 'Proc'), + "".ljust(k), + remove_prefix(vk_name, 'Proc'), + )) + f.write("}\n\n") + + f.write("load_proc_addresses_device :: proc(device: Device) {\n") + max_len = max(len(name) for name, _ in group_map["Device"]) + for name, vk_name in group_map["Device"]: + k = max_len - len(name) + f.write('\t{}{} = auto_cast GetDeviceProcAddr(device, "vk{}")\n'.format( + remove_prefix(name, 'Proc'), + "".ljust(k), + remove_prefix(vk_name, 'Proc'), + )) + f.write("}\n\n") + + f.write("load_proc_addresses_instance :: proc(instance: Instance) {\n") + max_len = max(len(name) for name, _ in group_map["Instance"]) + for name, vk_name in group_map["Instance"]: + k = max_len - len(name) + f.write('\t{}{} = auto_cast GetInstanceProcAddr(instance, "vk{}")\n'.format( + remove_prefix(name, 'Proc'), + "".ljust(k), + remove_prefix(vk_name, 'Proc'), + )) + f.write("\n\t// Device Procedures (may call into dispatch)\n") + max_len = max(len(name) for name, _ in group_map["Device"]) + for name, vk_name in group_map["Device"]: + k = max_len - len(name) + f.write('\t{}{} = auto_cast GetInstanceProcAddr(instance, "vk{}")\n'.format( + remove_prefix(name, 'Proc'), + "".ljust(k), + remove_prefix(vk_name, 'Proc'), + )) + f.write("}\n\n") + + f.write("load_proc_addresses_global :: proc(vk_get_instance_proc_addr: rawptr) {\n") + f.write("\tGetInstanceProcAddr = auto_cast vk_get_instance_proc_addr\n\n") + max_len = max(len(name) for name, _ in group_map["Loader"]) + for name, vk_name in group_map["Loader"]: + k = max_len - len(name) + f.write('\t{}{} = auto_cast GetInstanceProcAddr(nil, "vk{}")\n'.format( + remove_prefix(name, 'Proc'), + "".ljust(k), + remove_prefix(vk_name, 'Proc'), + )) + f.write("}\n\n") + + f.write(""" +load_proc_addresses :: proc{ +\tload_proc_addresses_global, +\tload_proc_addresses_instance, +\tload_proc_addresses_device, +\tload_proc_addresses_device_vtable, +\tload_proc_addresses_custom, +}\n +"""[1::]) @@ -530,6 +626,9 @@ with open("../core.odin", 'w', encoding='utf-8') as f: f.write(BASE) f.write(""" API_VERSION_1_0 :: (1<<22) | (0<<12) | (0) +API_VERSION_1_1 :: (1<<22) | (1<<12) | (0) +API_VERSION_1_2 :: (1<<22) | (2<<12) | (0) +API_VERSION_1_3 :: (1<<22) | (3<<12) | (0) MAKE_VERSION :: proc(major, minor, patch: u32) -> u32 { return (major<<22) | (minor<<12) | (patch) @@ -566,32 +665,29 @@ MAX_MEMORY_TYPES :: 32 MAX_MEMORY_HEAPS :: 16 MAX_EXTENSION_NAME_SIZE :: 256 MAX_DESCRIPTION_SIZE :: 256 -MAX_DEVICE_GROUP_SIZE_KHX :: 32 MAX_DEVICE_GROUP_SIZE :: 32 LUID_SIZE_KHX :: 8 -LUID_SIZE_KHR :: 8 LUID_SIZE :: 8 -MAX_DRIVER_NAME_SIZE_KHR :: 256 -MAX_DRIVER_INFO_SIZE_KHR :: 256 -MAX_QUEUE_FAMILY_EXTERNAL :: ~u32(0)-1 +MAX_QUEUE_FAMILY_EXTERNAL :: ~u32(1) MAX_GLOBAL_PRIORITY_SIZE_EXT :: 16 +QUEUE_FAMILY_EXTERNAL :: MAX_QUEUE_FAMILY_EXTERNAL """[1::]) parse_constants(f) parse_handles_def(f) f.write("\n\n") parse_flags_def(f) - with open("../enums.odin", 'w', encoding='utf-8') as f: - f.write(BASE) - f.write("\n") - parse_enums(f) - f.write("\n\n") - with open("../structs.odin", 'w', encoding='utf-8') as f: - f.write(BASE) - f.write(""" +with open("../enums.odin", 'w', encoding='utf-8') as f: + f.write(BASE) + f.write("\n") + parse_enums(f) + f.write("\n\n") +with open("../structs.odin", 'w', encoding='utf-8') as f: + f.write(BASE) + f.write(""" import "core:c" -when ODIN_OS == "windows" { +when ODIN_OS == .Windows { \timport win32 "core:sys/windows" \tHINSTANCE :: win32.HINSTANCE @@ -622,13 +718,12 @@ CAMetalLayer :: struct {} /********************************/ """) - f.write("\n") - parse_structs(f) - f.write("\n\n") - with open("../procedures.odin", 'w', encoding='utf-8') as f: - f.write(BASE) - f.write("\n") - parse_procedures(f) - f.write("\n") - group_functions(f) - f.write("\n\n") \ No newline at end of file + f.write("\n") + parse_structs(f) + f.write("\n\n") +with open("../procedures.odin", 'w', encoding='utf-8') as f: + f.write(BASE) + f.write("\n") + parse_procedures(f) + f.write("\n") + group_functions(f) diff --git a/vendor/vulkan/_gen/vk_icd.h b/vendor/vulkan/_gen/vk_icd.h index ae006d06d..41989ee35 100644 --- a/vendor/vulkan/_gen/vk_icd.h +++ b/vendor/vulkan/_gen/vk_icd.h @@ -33,7 +33,7 @@ // Version 2 - Add Loader/ICD Interface version negotiation // via vk_icdNegotiateLoaderICDInterfaceVersion. // Version 3 - Add ICD creation/destruction of KHR_surface objects. -// Version 4 - Add unknown physical device extension qyering via +// Version 4 - Add unknown physical device extension querying via // vk_icdGetPhysicalDeviceProcAddr. // Version 5 - Tells ICDs that the loader is now paying attention to the // application version of Vulkan passed into the ApplicationInfo diff --git a/vendor/vulkan/_gen/vk_platform.h b/vendor/vulkan/_gen/vk_platform.h index 18b913abc..3ff8c5d14 100644 --- a/vendor/vulkan/_gen/vk_platform.h +++ b/vendor/vulkan/_gen/vk_platform.h @@ -2,7 +2,7 @@ // File: vk_platform.h // /* -** Copyright 2014-2021 The Khronos Group Inc. +** Copyright 2014-2022 The Khronos Group Inc. ** ** SPDX-License-Identifier: Apache-2.0 */ @@ -42,7 +42,7 @@ extern "C" #define VKAPI_CALL __stdcall #define VKAPI_PTR VKAPI_CALL #elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH < 7 - #error "Vulkan isn't supported for the 'armeabi' NDK ABI" + #error "Vulkan is not supported for the 'armeabi' NDK ABI" #elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7 && defined(__ARM_32BIT_STATE) // On Android 32-bit ARM targets, Vulkan functions use the "hardfloat" // calling convention, i.e. float parameters are passed in registers. This diff --git a/vendor/vulkan/_gen/vulkan_core.h b/vendor/vulkan/_gen/vulkan_core.h index 18b302fa0..5c8b8461f 100644 --- a/vendor/vulkan/_gen/vulkan_core.h +++ b/vendor/vulkan/_gen/vulkan_core.h @@ -2,7 +2,7 @@ #define VULKAN_CORE_H_ 1 /* -** Copyright 2015-2021 The Khronos Group Inc. +** Copyright 2015-2022 The Khronos Group Inc. ** ** SPDX-License-Identifier: Apache-2.0 */ @@ -72,10 +72,10 @@ extern "C" { #define VK_API_VERSION_1_0 VK_MAKE_API_VERSION(0, 1, 0, 0)// Patch version should always be set to 0 // Version of this file -#define VK_HEADER_VERSION 191 +#define VK_HEADER_VERSION 211 // Complete version of this file -#define VK_HEADER_VERSION_COMPLETE VK_MAKE_API_VERSION(0, 1, 2, VK_HEADER_VERSION) +#define VK_HEADER_VERSION_COMPLETE VK_MAKE_API_VERSION(0, 1, 3, VK_HEADER_VERSION) // DEPRECATED: This define is deprecated. VK_API_VERSION_MAJOR should be used instead. #define VK_VERSION_MAJOR(version) ((uint32_t)(version) >> 22) @@ -160,6 +160,7 @@ typedef enum VkResult { VK_ERROR_INVALID_EXTERNAL_HANDLE = -1000072003, VK_ERROR_FRAGMENTATION = -1000161000, VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS = -1000257000, + VK_PIPELINE_COMPILE_REQUIRED = 1000297000, VK_ERROR_SURFACE_LOST_KHR = -1000000000, VK_ERROR_NATIVE_WINDOW_IN_USE_KHR = -1000000001, VK_SUBOPTIMAL_KHR = 1000001003, @@ -168,19 +169,20 @@ typedef enum VkResult { VK_ERROR_VALIDATION_FAILED_EXT = -1000011001, VK_ERROR_INVALID_SHADER_NV = -1000012000, VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT = -1000158000, - VK_ERROR_NOT_PERMITTED_EXT = -1000174001, + VK_ERROR_NOT_PERMITTED_KHR = -1000174001, VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT = -1000255000, VK_THREAD_IDLE_KHR = 1000268000, VK_THREAD_DONE_KHR = 1000268001, VK_OPERATION_DEFERRED_KHR = 1000268002, VK_OPERATION_NOT_DEFERRED_KHR = 1000268003, - VK_PIPELINE_COMPILE_REQUIRED_EXT = 1000297000, VK_ERROR_OUT_OF_POOL_MEMORY_KHR = VK_ERROR_OUT_OF_POOL_MEMORY, VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR = VK_ERROR_INVALID_EXTERNAL_HANDLE, VK_ERROR_FRAGMENTATION_EXT = VK_ERROR_FRAGMENTATION, + VK_ERROR_NOT_PERMITTED_EXT = VK_ERROR_NOT_PERMITTED_KHR, VK_ERROR_INVALID_DEVICE_ADDRESS_EXT = VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS, VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS_KHR = VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS, - VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT = VK_PIPELINE_COMPILE_REQUIRED_EXT, + VK_PIPELINE_COMPILE_REQUIRED_EXT = VK_PIPELINE_COMPILE_REQUIRED, + VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT = VK_PIPELINE_COMPILE_REQUIRED, VK_RESULT_MAX_ENUM = 0x7FFFFFFF } VkResult; @@ -349,6 +351,58 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_BUFFER_OPAQUE_CAPTURE_ADDRESS_CREATE_INFO = 1000257002, VK_STRUCTURE_TYPE_MEMORY_OPAQUE_CAPTURE_ADDRESS_ALLOCATE_INFO = 1000257003, VK_STRUCTURE_TYPE_DEVICE_MEMORY_OPAQUE_CAPTURE_ADDRESS_INFO = 1000257004, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES = 53, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_PROPERTIES = 54, + VK_STRUCTURE_TYPE_PIPELINE_CREATION_FEEDBACK_CREATE_INFO = 1000192000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_TERMINATE_INVOCATION_FEATURES = 1000215000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TOOL_PROPERTIES = 1000245000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES = 1000276000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIVATE_DATA_FEATURES = 1000295000, + VK_STRUCTURE_TYPE_DEVICE_PRIVATE_DATA_CREATE_INFO = 1000295001, + VK_STRUCTURE_TYPE_PRIVATE_DATA_SLOT_CREATE_INFO = 1000295002, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_CREATION_CACHE_CONTROL_FEATURES = 1000297000, + VK_STRUCTURE_TYPE_MEMORY_BARRIER_2 = 1000314000, + VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2 = 1000314001, + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2 = 1000314002, + VK_STRUCTURE_TYPE_DEPENDENCY_INFO = 1000314003, + VK_STRUCTURE_TYPE_SUBMIT_INFO_2 = 1000314004, + VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO = 1000314005, + VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO = 1000314006, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES = 1000314007, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ZERO_INITIALIZE_WORKGROUP_MEMORY_FEATURES = 1000325000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_ROBUSTNESS_FEATURES = 1000335000, + VK_STRUCTURE_TYPE_COPY_BUFFER_INFO_2 = 1000337000, + VK_STRUCTURE_TYPE_COPY_IMAGE_INFO_2 = 1000337001, + VK_STRUCTURE_TYPE_COPY_BUFFER_TO_IMAGE_INFO_2 = 1000337002, + VK_STRUCTURE_TYPE_COPY_IMAGE_TO_BUFFER_INFO_2 = 1000337003, + VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2 = 1000337004, + VK_STRUCTURE_TYPE_RESOLVE_IMAGE_INFO_2 = 1000337005, + VK_STRUCTURE_TYPE_BUFFER_COPY_2 = 1000337006, + VK_STRUCTURE_TYPE_IMAGE_COPY_2 = 1000337007, + VK_STRUCTURE_TYPE_IMAGE_BLIT_2 = 1000337008, + VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2 = 1000337009, + VK_STRUCTURE_TYPE_IMAGE_RESOLVE_2 = 1000337010, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES = 1000225000, + VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO = 1000225001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES = 1000225002, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES = 1000138000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_PROPERTIES = 1000138001, + VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK = 1000138002, + VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_INLINE_UNIFORM_BLOCK_CREATE_INFO = 1000138003, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXTURE_COMPRESSION_ASTC_HDR_FEATURES = 1000066000, + VK_STRUCTURE_TYPE_RENDERING_INFO = 1000044000, + VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO = 1000044001, + VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO = 1000044002, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES = 1000044003, + VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_RENDERING_INFO = 1000044004, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_FEATURES = 1000280000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_PROPERTIES = 1000280001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_PROPERTIES = 1000281001, + VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_3 = 1000360000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_4_FEATURES = 1000413000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_4_PROPERTIES = 1000413001, + VK_STRUCTURE_TYPE_DEVICE_BUFFER_MEMORY_REQUIREMENTS = 1000413002, + VK_STRUCTURE_TYPE_DEVICE_IMAGE_MEMORY_REQUIREMENTS = 1000413003, VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR = 1000001000, VK_STRUCTURE_TYPE_PRESENT_INFO_KHR = 1000001001, VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_CAPABILITIES_KHR = 1000060007, @@ -418,8 +472,14 @@ typedef enum VkStructureType { #ifdef VK_ENABLE_BETA_EXTENSIONS VK_STRUCTURE_TYPE_VIDEO_FORMAT_PROPERTIES_KHR = 1000023015, #endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_QUEUE_FAMILY_QUERY_RESULT_STATUS_PROPERTIES_2_KHR = 1000023016, +#endif #ifdef VK_ENABLE_BETA_EXTENSIONS VK_STRUCTURE_TYPE_VIDEO_DECODE_INFO_KHR = 1000024000, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_DECODE_CAPABILITIES_KHR = 1000024001, #endif VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_IMAGE_CREATE_INFO_NV = 1000026000, VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_BUFFER_CREATE_INFO_NV = 1000026001, @@ -436,54 +496,94 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_CAPABILITIES_EXT = 1000038000, #endif #ifdef VK_ENABLE_BETA_EXTENSIONS - VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_SESSION_CREATE_INFO_EXT = 1000038001, + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_SESSION_PARAMETERS_CREATE_INFO_EXT = 1000038001, #endif #ifdef VK_ENABLE_BETA_EXTENSIONS - VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_SESSION_PARAMETERS_CREATE_INFO_EXT = 1000038002, + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_SESSION_PARAMETERS_ADD_INFO_EXT = 1000038002, #endif #ifdef VK_ENABLE_BETA_EXTENSIONS - VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_SESSION_PARAMETERS_ADD_INFO_EXT = 1000038003, + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_VCL_FRAME_INFO_EXT = 1000038003, #endif #ifdef VK_ENABLE_BETA_EXTENSIONS - VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_VCL_FRAME_INFO_EXT = 1000038004, + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_DPB_SLOT_INFO_EXT = 1000038004, #endif #ifdef VK_ENABLE_BETA_EXTENSIONS - VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_DPB_SLOT_INFO_EXT = 1000038005, + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_NALU_SLICE_EXT = 1000038005, #endif #ifdef VK_ENABLE_BETA_EXTENSIONS - VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_NALU_SLICE_EXT = 1000038006, + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_EMIT_PICTURE_PARAMETERS_EXT = 1000038006, #endif #ifdef VK_ENABLE_BETA_EXTENSIONS - VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_EMIT_PICTURE_PARAMETERS_EXT = 1000038007, + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_PROFILE_EXT = 1000038007, #endif #ifdef VK_ENABLE_BETA_EXTENSIONS - VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_PROFILE_EXT = 1000038008, + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_RATE_CONTROL_INFO_EXT = 1000038008, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_RATE_CONTROL_LAYER_INFO_EXT = 1000038009, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_REFERENCE_LISTS_EXT = 1000038010, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_CAPABILITIES_EXT = 1000039000, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_SESSION_PARAMETERS_CREATE_INFO_EXT = 1000039001, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_SESSION_PARAMETERS_ADD_INFO_EXT = 1000039002, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_VCL_FRAME_INFO_EXT = 1000039003, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_DPB_SLOT_INFO_EXT = 1000039004, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_NALU_SLICE_SEGMENT_EXT = 1000039005, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_EMIT_PICTURE_PARAMETERS_EXT = 1000039006, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_PROFILE_EXT = 1000039007, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_REFERENCE_LISTS_EXT = 1000039008, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_RATE_CONTROL_INFO_EXT = 1000039009, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_RATE_CONTROL_LAYER_INFO_EXT = 1000039010, #endif #ifdef VK_ENABLE_BETA_EXTENSIONS VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_CAPABILITIES_EXT = 1000040000, #endif #ifdef VK_ENABLE_BETA_EXTENSIONS - VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_SESSION_CREATE_INFO_EXT = 1000040001, + VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_PICTURE_INFO_EXT = 1000040001, #endif #ifdef VK_ENABLE_BETA_EXTENSIONS - VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_PICTURE_INFO_EXT = 1000040002, + VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_MVC_EXT = 1000040002, #endif #ifdef VK_ENABLE_BETA_EXTENSIONS - VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_MVC_EXT = 1000040003, + VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_PROFILE_EXT = 1000040003, #endif #ifdef VK_ENABLE_BETA_EXTENSIONS - VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_PROFILE_EXT = 1000040004, + VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_SESSION_PARAMETERS_CREATE_INFO_EXT = 1000040004, #endif #ifdef VK_ENABLE_BETA_EXTENSIONS - VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_SESSION_PARAMETERS_CREATE_INFO_EXT = 1000040005, + VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_SESSION_PARAMETERS_ADD_INFO_EXT = 1000040005, #endif #ifdef VK_ENABLE_BETA_EXTENSIONS - VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_SESSION_PARAMETERS_ADD_INFO_EXT = 1000040006, -#endif -#ifdef VK_ENABLE_BETA_EXTENSIONS - VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_DPB_SLOT_INFO_EXT = 1000040007, + VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_DPB_SLOT_INFO_EXT = 1000040006, #endif VK_STRUCTURE_TYPE_TEXTURE_LOD_GATHER_FORMAT_PROPERTIES_AMD = 1000041000, + VK_STRUCTURE_TYPE_RENDERING_FRAGMENT_SHADING_RATE_ATTACHMENT_INFO_KHR = 1000044006, + VK_STRUCTURE_TYPE_RENDERING_FRAGMENT_DENSITY_MAP_ATTACHMENT_INFO_EXT = 1000044007, + VK_STRUCTURE_TYPE_ATTACHMENT_SAMPLE_COUNT_INFO_AMD = 1000044008, + VK_STRUCTURE_TYPE_MULTIVIEW_PER_VIEW_ATTRIBUTES_INFO_NVX = 1000044009, VK_STRUCTURE_TYPE_STREAM_DESCRIPTOR_SURFACE_CREATE_INFO_GGP = 1000049000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CORNER_SAMPLED_IMAGE_FEATURES_NV = 1000050000, VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_NV = 1000056000, @@ -493,7 +593,6 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_NV = 1000058000, VK_STRUCTURE_TYPE_VALIDATION_FLAGS_EXT = 1000061000, VK_STRUCTURE_TYPE_VI_SURFACE_CREATE_INFO_NN = 1000062000, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXTURE_COMPRESSION_ASTC_HDR_FEATURES_EXT = 1000066000, VK_STRUCTURE_TYPE_IMAGE_VIEW_ASTC_DECODE_MODE_EXT = 1000067000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ASTC_DECODE_FEATURES_EXT = 1000067001, VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR = 1000073000, @@ -565,10 +664,7 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID = 1000129003, VK_STRUCTURE_TYPE_MEMORY_GET_ANDROID_HARDWARE_BUFFER_INFO_ANDROID = 1000129004, VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID = 1000129005, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES_EXT = 1000138000, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_PROPERTIES_EXT = 1000138001, - VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK_EXT = 1000138002, - VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_INLINE_UNIFORM_BLOCK_CREATE_INFO_EXT = 1000138003, + VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_2_ANDROID = 1000129006, VK_STRUCTURE_TYPE_SAMPLE_LOCATIONS_INFO_EXT = 1000143000, VK_STRUCTURE_TYPE_RENDER_PASS_SAMPLE_LOCATIONS_BEGIN_INFO_EXT = 1000143001, VK_STRUCTURE_TYPE_PIPELINE_SAMPLE_LOCATIONS_STATE_CREATE_INFO_EXT = 1000143002, @@ -607,6 +703,7 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_LIST_CREATE_INFO_EXT = 1000158003, VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT = 1000158004, VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_PROPERTIES_EXT = 1000158005, + VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_2_EXT = 1000158006, VK_STRUCTURE_TYPE_VALIDATION_CACHE_CREATE_INFO_EXT = 1000160000, VK_STRUCTURE_TYPE_SHADER_MODULE_VALIDATION_CACHE_CREATE_INFO_EXT = 1000160001, #ifdef VK_ENABLE_BETA_EXTENSIONS @@ -634,7 +731,6 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_PIPELINE_REPRESENTATIVE_FRAGMENT_TEST_STATE_CREATE_INFO_NV = 1000166001, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_VIEW_IMAGE_FORMAT_INFO_EXT = 1000170000, VK_STRUCTURE_TYPE_FILTER_CUBIC_IMAGE_VIEW_IMAGE_FORMAT_PROPERTIES_EXT = 1000170001, - VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_EXT = 1000174000, VK_STRUCTURE_TYPE_IMPORT_MEMORY_HOST_POINTER_INFO_EXT = 1000178000, VK_STRUCTURE_TYPE_MEMORY_HOST_POINTER_PROPERTIES_EXT = 1000178001, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_MEMORY_HOST_PROPERTIES_EXT = 1000178002, @@ -646,29 +742,28 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_CAPABILITIES_EXT = 1000187000, #endif #ifdef VK_ENABLE_BETA_EXTENSIONS - VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_SESSION_CREATE_INFO_EXT = 1000187001, + VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_SESSION_PARAMETERS_CREATE_INFO_EXT = 1000187001, #endif #ifdef VK_ENABLE_BETA_EXTENSIONS - VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_SESSION_PARAMETERS_CREATE_INFO_EXT = 1000187002, + VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_SESSION_PARAMETERS_ADD_INFO_EXT = 1000187002, #endif #ifdef VK_ENABLE_BETA_EXTENSIONS - VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_SESSION_PARAMETERS_ADD_INFO_EXT = 1000187003, + VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_PROFILE_EXT = 1000187003, #endif #ifdef VK_ENABLE_BETA_EXTENSIONS - VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_PROFILE_EXT = 1000187004, + VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_PICTURE_INFO_EXT = 1000187004, #endif #ifdef VK_ENABLE_BETA_EXTENSIONS - VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_PICTURE_INFO_EXT = 1000187005, -#endif -#ifdef VK_ENABLE_BETA_EXTENSIONS - VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_DPB_SLOT_INFO_EXT = 1000187006, + VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_DPB_SLOT_INFO_EXT = 1000187005, #endif + VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_KHR = 1000174000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GLOBAL_PRIORITY_QUERY_FEATURES_KHR = 1000388000, + VK_STRUCTURE_TYPE_QUEUE_FAMILY_GLOBAL_PRIORITY_PROPERTIES_KHR = 1000388001, VK_STRUCTURE_TYPE_DEVICE_MEMORY_OVERALLOCATION_CREATE_INFO_AMD = 1000189000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_PROPERTIES_EXT = 1000190000, VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT = 1000190001, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_EXT = 1000190002, VK_STRUCTURE_TYPE_PRESENT_FRAME_TOKEN_GGP = 1000191000, - VK_STRUCTURE_TYPE_PIPELINE_CREATION_FEEDBACK_CREATE_INFO_EXT = 1000192000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COMPUTE_SHADER_DERIVATIVES_FEATURES_NV = 1000201000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_NV = 1000202000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_PROPERTIES_NV = 1000202001, @@ -689,14 +784,10 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_DISPLAY_NATIVE_HDR_SURFACE_CAPABILITIES_AMD = 1000213000, VK_STRUCTURE_TYPE_SWAPCHAIN_DISPLAY_NATIVE_HDR_CREATE_INFO_AMD = 1000213001, VK_STRUCTURE_TYPE_IMAGEPIPE_SURFACE_CREATE_INFO_FUCHSIA = 1000214000, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_TERMINATE_INVOCATION_FEATURES_KHR = 1000215000, VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT = 1000217000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_FEATURES_EXT = 1000218000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_PROPERTIES_EXT = 1000218001, VK_STRUCTURE_TYPE_RENDER_PASS_FRAGMENT_DENSITY_MAP_CREATE_INFO_EXT = 1000218002, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES_EXT = 1000225000, - VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO_EXT = 1000225001, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES_EXT = 1000225002, VK_STRUCTURE_TYPE_FRAGMENT_SHADING_RATE_ATTACHMENT_INFO_KHR = 1000226000, VK_STRUCTURE_TYPE_PIPELINE_FRAGMENT_SHADING_RATE_STATE_CREATE_INFO_KHR = 1000226001, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_PROPERTIES_KHR = 1000226002, @@ -712,7 +803,6 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEDICATED_ALLOCATION_IMAGE_ALIASING_FEATURES_NV = 1000240000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_EXT = 1000244000, VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_CREATE_INFO_EXT = 1000244002, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TOOL_PROPERTIES_EXT = 1000245000, VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT = 1000247000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_WAIT_FEATURES_KHR = 1000248000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COOPERATIVE_MATRIX_FEATURES_NV = 1000249000, @@ -743,7 +833,6 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_PIPELINE_EXECUTABLE_STATISTIC_KHR = 1000269004, VK_STRUCTURE_TYPE_PIPELINE_EXECUTABLE_INTERNAL_REPRESENTATION_KHR = 1000269005, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_FLOAT_2_FEATURES_EXT = 1000273000, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES_EXT = 1000276000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEVICE_GENERATED_COMMANDS_PROPERTIES_NV = 1000277000, VK_STRUCTURE_TYPE_GRAPHICS_SHADER_GROUP_CREATE_INFO_NV = 1000277001, VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_SHADER_GROUPS_CREATE_INFO_NV = 1000277002, @@ -754,10 +843,7 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEVICE_GENERATED_COMMANDS_FEATURES_NV = 1000277007, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INHERITED_VIEWPORT_SCISSOR_FEATURES_NV = 1000278000, VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_VIEWPORT_SCISSOR_INFO_NV = 1000278001, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_FEATURES_KHR = 1000280000, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_PROPERTIES_KHR = 1000280001, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_FEATURES_EXT = 1000281000, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_PROPERTIES_EXT = 1000281001, VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_RENDER_PASS_TRANSFORM_INFO_QCOM = 1000282000, VK_STRUCTURE_TYPE_RENDER_PASS_TRANSFORM_BEGIN_INFO_QCOM = 1000282001, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEVICE_MEMORY_REPORT_FEATURES_EXT = 1000284000, @@ -771,30 +857,26 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_PIPELINE_LIBRARY_CREATE_INFO_KHR = 1000290000, VK_STRUCTURE_TYPE_PRESENT_ID_KHR = 1000294000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_ID_FEATURES_KHR = 1000294001, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIVATE_DATA_FEATURES_EXT = 1000295000, - VK_STRUCTURE_TYPE_DEVICE_PRIVATE_DATA_CREATE_INFO_EXT = 1000295001, - VK_STRUCTURE_TYPE_PRIVATE_DATA_SLOT_CREATE_INFO_EXT = 1000295002, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_CREATION_CACHE_CONTROL_FEATURES_EXT = 1000297000, #ifdef VK_ENABLE_BETA_EXTENSIONS VK_STRUCTURE_TYPE_VIDEO_ENCODE_INFO_KHR = 1000299000, #endif #ifdef VK_ENABLE_BETA_EXTENSIONS VK_STRUCTURE_TYPE_VIDEO_ENCODE_RATE_CONTROL_INFO_KHR = 1000299001, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_ENCODE_RATE_CONTROL_LAYER_INFO_KHR = 1000299002, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_ENCODE_CAPABILITIES_KHR = 1000299003, #endif VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DIAGNOSTICS_CONFIG_FEATURES_NV = 1000300000, VK_STRUCTURE_TYPE_DEVICE_DIAGNOSTICS_CONFIG_CREATE_INFO_NV = 1000300001, - VK_STRUCTURE_TYPE_MEMORY_BARRIER_2_KHR = 1000314000, - VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2_KHR = 1000314001, - VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2_KHR = 1000314002, - VK_STRUCTURE_TYPE_DEPENDENCY_INFO_KHR = 1000314003, - VK_STRUCTURE_TYPE_SUBMIT_INFO_2_KHR = 1000314004, - VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO_KHR = 1000314005, - VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO_KHR = 1000314006, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES_KHR = 1000314007, VK_STRUCTURE_TYPE_QUEUE_FAMILY_CHECKPOINT_PROPERTIES_2_NV = 1000314008, VK_STRUCTURE_TYPE_CHECKPOINT_DATA_2_NV = 1000314009, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_FEATURES_EXT = 1000320000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_PROPERTIES_EXT = 1000320001, + VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_LIBRARY_CREATE_INFO_EXT = 1000320002, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_UNIFORM_CONTROL_FLOW_FEATURES_KHR = 1000323000, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ZERO_INITIALIZE_WORKGROUP_MEMORY_FEATURES_KHR = 1000325000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_ENUMS_PROPERTIES_NV = 1000326000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_ENUMS_FEATURES_NV = 1000326001, VK_STRUCTURE_TYPE_PIPELINE_FRAGMENT_SHADING_RATE_ENUM_STATE_CREATE_INFO_NV = 1000326002, @@ -805,20 +887,10 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_2_FEATURES_EXT = 1000332000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_2_PROPERTIES_EXT = 1000332001, VK_STRUCTURE_TYPE_COPY_COMMAND_TRANSFORM_INFO_QCOM = 1000333000, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_ROBUSTNESS_FEATURES_EXT = 1000335000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_FEATURES_KHR = 1000336000, - VK_STRUCTURE_TYPE_COPY_BUFFER_INFO_2_KHR = 1000337000, - VK_STRUCTURE_TYPE_COPY_IMAGE_INFO_2_KHR = 1000337001, - VK_STRUCTURE_TYPE_COPY_BUFFER_TO_IMAGE_INFO_2_KHR = 1000337002, - VK_STRUCTURE_TYPE_COPY_IMAGE_TO_BUFFER_INFO_2_KHR = 1000337003, - VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2_KHR = 1000337004, - VK_STRUCTURE_TYPE_RESOLVE_IMAGE_INFO_2_KHR = 1000337005, - VK_STRUCTURE_TYPE_BUFFER_COPY_2_KHR = 1000337006, - VK_STRUCTURE_TYPE_IMAGE_COPY_2_KHR = 1000337007, - VK_STRUCTURE_TYPE_IMAGE_BLIT_2_KHR = 1000337008, - VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2_KHR = 1000337009, - VK_STRUCTURE_TYPE_IMAGE_RESOLVE_2_KHR = 1000337010, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_4444_FORMATS_FEATURES_EXT = 1000340000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_FEATURES_ARM = 1000342000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RGBA10X6_FORMATS_FEATURES_EXT = 1000344000, VK_STRUCTURE_TYPE_DIRECTFB_SURFACE_CREATE_INFO_EXT = 1000346000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MUTABLE_DESCRIPTOR_TYPE_FEATURES_VALVE = 1000351000, VK_STRUCTURE_TYPE_MUTABLE_DESCRIPTOR_TYPE_CREATE_INFO_VALVE = 1000351002, @@ -826,12 +898,24 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_VERTEX_INPUT_BINDING_DESCRIPTION_2_EXT = 1000352001, VK_STRUCTURE_TYPE_VERTEX_INPUT_ATTRIBUTE_DESCRIPTION_2_EXT = 1000352002, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRM_PROPERTIES_EXT = 1000353000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT = 1000355000, + VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_DEPTH_CLIP_CONTROL_CREATE_INFO_EXT = 1000355001, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT = 1000356000, VK_STRUCTURE_TYPE_IMPORT_MEMORY_ZIRCON_HANDLE_INFO_FUCHSIA = 1000364000, VK_STRUCTURE_TYPE_MEMORY_ZIRCON_HANDLE_PROPERTIES_FUCHSIA = 1000364001, VK_STRUCTURE_TYPE_MEMORY_GET_ZIRCON_HANDLE_INFO_FUCHSIA = 1000364002, VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_ZIRCON_HANDLE_INFO_FUCHSIA = 1000365000, VK_STRUCTURE_TYPE_SEMAPHORE_GET_ZIRCON_HANDLE_INFO_FUCHSIA = 1000365001, + VK_STRUCTURE_TYPE_BUFFER_COLLECTION_CREATE_INFO_FUCHSIA = 1000366000, + VK_STRUCTURE_TYPE_IMPORT_MEMORY_BUFFER_COLLECTION_FUCHSIA = 1000366001, + VK_STRUCTURE_TYPE_BUFFER_COLLECTION_IMAGE_CREATE_INFO_FUCHSIA = 1000366002, + VK_STRUCTURE_TYPE_BUFFER_COLLECTION_PROPERTIES_FUCHSIA = 1000366003, + VK_STRUCTURE_TYPE_BUFFER_CONSTRAINTS_INFO_FUCHSIA = 1000366004, + VK_STRUCTURE_TYPE_BUFFER_COLLECTION_BUFFER_CREATE_INFO_FUCHSIA = 1000366005, + VK_STRUCTURE_TYPE_IMAGE_CONSTRAINTS_INFO_FUCHSIA = 1000366006, + VK_STRUCTURE_TYPE_IMAGE_FORMAT_CONSTRAINTS_INFO_FUCHSIA = 1000366007, + VK_STRUCTURE_TYPE_SYSMEM_COLOR_SPACE_FUCHSIA = 1000366008, + VK_STRUCTURE_TYPE_BUFFER_COLLECTION_CONSTRAINTS_INFO_FUCHSIA = 1000366009, VK_STRUCTURE_TYPE_SUBPASS_SHADING_PIPELINE_CREATE_INFO_HUAWEI = 1000369000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBPASS_SHADING_FEATURES_HUAWEI = 1000369001, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBPASS_SHADING_PROPERTIES_HUAWEI = 1000369002, @@ -842,14 +926,31 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_SCREEN_SURFACE_CREATE_INFO_QNX = 1000378000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COLOR_WRITE_ENABLE_FEATURES_EXT = 1000381000, VK_STRUCTURE_TYPE_PIPELINE_COLOR_WRITE_CREATE_INFO_EXT = 1000381001, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GLOBAL_PRIORITY_QUERY_FEATURES_EXT = 1000388000, - VK_STRUCTURE_TYPE_QUEUE_FAMILY_GLOBAL_PRIORITY_PROPERTIES_EXT = 1000388001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVES_GENERATED_QUERY_FEATURES_EXT = 1000382000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_VIEW_MIN_LOD_FEATURES_EXT = 1000391000, + VK_STRUCTURE_TYPE_IMAGE_VIEW_MIN_LOD_CREATE_INFO_EXT = 1000391001, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTI_DRAW_FEATURES_EXT = 1000392000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTI_DRAW_PROPERTIES_EXT = 1000392001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_2D_VIEW_OF_3D_FEATURES_EXT = 1000393000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BORDER_COLOR_SWIZZLE_FEATURES_EXT = 1000411000, + VK_STRUCTURE_TYPE_SAMPLER_BORDER_COLOR_COMPONENT_MAPPING_CREATE_INFO_EXT = 1000411001, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PAGEABLE_DEVICE_LOCAL_MEMORY_FEATURES_EXT = 1000412000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_SET_HOST_MAPPING_FEATURES_VALVE = 1000420000, + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_BINDING_REFERENCE_VALVE = 1000420001, + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_HOST_MAPPING_INFO_VALVE = 1000420002, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_OFFSET_FEATURES_QCOM = 1000425000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_OFFSET_PROPERTIES_QCOM = 1000425001, + VK_STRUCTURE_TYPE_SUBPASS_FRAGMENT_DENSITY_MAP_OFFSET_END_INFO_QCOM = 1000425002, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINEAR_COLOR_ATTACHMENT_FEATURES_NV = 1000430000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETER_FEATURES = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES, VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT, + VK_STRUCTURE_TYPE_RENDERING_INFO_KHR = VK_STRUCTURE_TYPE_RENDERING_INFO, + VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, + VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES, + VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_RENDERING_INFO_KHR = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_RENDERING_INFO, + VK_STRUCTURE_TYPE_ATTACHMENT_SAMPLE_COUNT_INFO_NV = VK_STRUCTURE_TYPE_ATTACHMENT_SAMPLE_COUNT_INFO_AMD, VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES, @@ -869,6 +970,7 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_DEVICE_GROUP_BIND_SPARSE_INFO_KHR = VK_STRUCTURE_TYPE_DEVICE_GROUP_BIND_SPARSE_INFO, VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_DEVICE_GROUP_INFO_KHR = VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_DEVICE_GROUP_INFO, VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_DEVICE_GROUP_INFO_KHR = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_DEVICE_GROUP_INFO, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXTURE_COMPRESSION_ASTC_HDR_FEATURES_EXT = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXTURE_COMPRESSION_ASTC_HDR_FEATURES, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES, VK_STRUCTURE_TYPE_DEVICE_GROUP_DEVICE_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_DEVICE_GROUP_DEVICE_CREATE_INFO, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO, @@ -911,6 +1013,10 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_FILTER_MINMAX_PROPERTIES_EXT = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_FILTER_MINMAX_PROPERTIES, VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO_EXT = VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES_EXT = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_PROPERTIES_EXT = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_PROPERTIES, + VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK_EXT = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK, + VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_INLINE_UNIFORM_BLOCK_CREATE_INFO_EXT = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_INLINE_UNIFORM_BLOCK_CREATE_INFO, VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR = VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2, VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2, VK_STRUCTURE_TYPE_IMAGE_SPARSE_MEMORY_REQUIREMENTS_INFO_2_KHR = VK_STRUCTURE_TYPE_IMAGE_SPARSE_MEMORY_REQUIREMENTS_INFO_2, @@ -932,9 +1038,11 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_LAYOUT_SUPPORT_EXT = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_LAYOUT_SUPPORT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES, VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_SUPPORT_KHR = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_SUPPORT, + VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_EXT = VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_KHR, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES, + VK_STRUCTURE_TYPE_PIPELINE_CREATION_FEEDBACK_CREATE_INFO_EXT = VK_STRUCTURE_TYPE_PIPELINE_CREATION_FEEDBACK_CREATE_INFO, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_STENCIL_RESOLVE_PROPERTIES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_STENCIL_RESOLVE_PROPERTIES, @@ -947,12 +1055,17 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_SEMAPHORE_SIGNAL_INFO_KHR = VK_STRUCTURE_TYPE_SEMAPHORE_SIGNAL_INFO, VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO_INTEL = VK_STRUCTURE_TYPE_QUERY_POOL_PERFORMANCE_QUERY_CREATE_INFO_INTEL, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_MEMORY_MODEL_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_MEMORY_MODEL_FEATURES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_TERMINATE_INVOCATION_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_TERMINATE_INVOCATION_FEATURES, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES_EXT = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES_EXT = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES, + VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO_EXT = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES_EXT = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SEPARATE_DEPTH_STENCIL_LAYOUTS_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SEPARATE_DEPTH_STENCIL_LAYOUTS_FEATURES, VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_STENCIL_LAYOUT_KHR = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_STENCIL_LAYOUT, VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_STENCIL_LAYOUT_KHR = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_STENCIL_LAYOUT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_ADDRESS_FEATURES_EXT = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_EXT, VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO_EXT = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TOOL_PROPERTIES_EXT = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TOOL_PROPERTIES, VK_STRUCTURE_TYPE_IMAGE_STENCIL_USAGE_CREATE_INFO_EXT = VK_STRUCTURE_TYPE_IMAGE_STENCIL_USAGE_CREATE_INFO, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES, @@ -961,6 +1074,42 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_MEMORY_OPAQUE_CAPTURE_ADDRESS_ALLOCATE_INFO_KHR = VK_STRUCTURE_TYPE_MEMORY_OPAQUE_CAPTURE_ADDRESS_ALLOCATE_INFO, VK_STRUCTURE_TYPE_DEVICE_MEMORY_OPAQUE_CAPTURE_ADDRESS_INFO_KHR = VK_STRUCTURE_TYPE_DEVICE_MEMORY_OPAQUE_CAPTURE_ADDRESS_INFO, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES_EXT = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES_EXT = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_FEATURES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_PROPERTIES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_PROPERTIES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_PROPERTIES_EXT = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_PROPERTIES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIVATE_DATA_FEATURES_EXT = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIVATE_DATA_FEATURES, + VK_STRUCTURE_TYPE_DEVICE_PRIVATE_DATA_CREATE_INFO_EXT = VK_STRUCTURE_TYPE_DEVICE_PRIVATE_DATA_CREATE_INFO, + VK_STRUCTURE_TYPE_PRIVATE_DATA_SLOT_CREATE_INFO_EXT = VK_STRUCTURE_TYPE_PRIVATE_DATA_SLOT_CREATE_INFO, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_CREATION_CACHE_CONTROL_FEATURES_EXT = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_CREATION_CACHE_CONTROL_FEATURES, + VK_STRUCTURE_TYPE_MEMORY_BARRIER_2_KHR = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, + VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2_KHR = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2, + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2_KHR = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + VK_STRUCTURE_TYPE_DEPENDENCY_INFO_KHR = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + VK_STRUCTURE_TYPE_SUBMIT_INFO_2_KHR = VK_STRUCTURE_TYPE_SUBMIT_INFO_2, + VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO_KHR = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO, + VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO_KHR = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ZERO_INITIALIZE_WORKGROUP_MEMORY_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ZERO_INITIALIZE_WORKGROUP_MEMORY_FEATURES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_ROBUSTNESS_FEATURES_EXT = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_ROBUSTNESS_FEATURES, + VK_STRUCTURE_TYPE_COPY_BUFFER_INFO_2_KHR = VK_STRUCTURE_TYPE_COPY_BUFFER_INFO_2, + VK_STRUCTURE_TYPE_COPY_IMAGE_INFO_2_KHR = VK_STRUCTURE_TYPE_COPY_IMAGE_INFO_2, + VK_STRUCTURE_TYPE_COPY_BUFFER_TO_IMAGE_INFO_2_KHR = VK_STRUCTURE_TYPE_COPY_BUFFER_TO_IMAGE_INFO_2, + VK_STRUCTURE_TYPE_COPY_IMAGE_TO_BUFFER_INFO_2_KHR = VK_STRUCTURE_TYPE_COPY_IMAGE_TO_BUFFER_INFO_2, + VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2_KHR = VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2, + VK_STRUCTURE_TYPE_RESOLVE_IMAGE_INFO_2_KHR = VK_STRUCTURE_TYPE_RESOLVE_IMAGE_INFO_2, + VK_STRUCTURE_TYPE_BUFFER_COPY_2_KHR = VK_STRUCTURE_TYPE_BUFFER_COPY_2, + VK_STRUCTURE_TYPE_IMAGE_COPY_2_KHR = VK_STRUCTURE_TYPE_IMAGE_COPY_2, + VK_STRUCTURE_TYPE_IMAGE_BLIT_2_KHR = VK_STRUCTURE_TYPE_IMAGE_BLIT_2, + VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2_KHR = VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2, + VK_STRUCTURE_TYPE_IMAGE_RESOLVE_2_KHR = VK_STRUCTURE_TYPE_IMAGE_RESOLVE_2, + VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_3_KHR = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_3, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GLOBAL_PRIORITY_QUERY_FEATURES_EXT = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GLOBAL_PRIORITY_QUERY_FEATURES_KHR, + VK_STRUCTURE_TYPE_QUEUE_FAMILY_GLOBAL_PRIORITY_PROPERTIES_EXT = VK_STRUCTURE_TYPE_QUEUE_FAMILY_GLOBAL_PRIORITY_PROPERTIES_KHR, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_4_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_4_FEATURES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_4_PROPERTIES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_4_PROPERTIES, + VK_STRUCTURE_TYPE_DEVICE_BUFFER_MEMORY_REQUIREMENTS_KHR = VK_STRUCTURE_TYPE_DEVICE_BUFFER_MEMORY_REQUIREMENTS, + VK_STRUCTURE_TYPE_DEVICE_IMAGE_MEMORY_REQUIREMENTS_KHR = VK_STRUCTURE_TYPE_DEVICE_IMAGE_MEMORY_REQUIREMENTS, VK_STRUCTURE_TYPE_MAX_ENUM = 0x7FFFFFFF } VkStructureType; @@ -980,6 +1129,8 @@ typedef enum VkImageLayout { VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL = 1000241001, VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL = 1000241002, VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL = 1000241003, + VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL = 1000314000, + VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL = 1000314001, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR = 1000001002, #ifdef VK_ENABLE_BETA_EXTENSIONS VK_IMAGE_LAYOUT_VIDEO_DECODE_DST_KHR = 1000024000, @@ -1002,8 +1153,6 @@ typedef enum VkImageLayout { #ifdef VK_ENABLE_BETA_EXTENSIONS VK_IMAGE_LAYOUT_VIDEO_ENCODE_DPB_KHR = 1000299002, #endif - VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL_KHR = 1000314000, - VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR = 1000314001, VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL_KHR = VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL_KHR = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_SHADING_RATE_OPTIMAL_NV = VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR, @@ -1011,6 +1160,8 @@ typedef enum VkImageLayout { VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL_KHR = VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL_KHR = VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL_KHR = VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL, + VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL_KHR = VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL, + VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_MAX_ENUM = 0x7FFFFFFF } VkImageLayout; @@ -1043,6 +1194,7 @@ typedef enum VkObjectType { VK_OBJECT_TYPE_COMMAND_POOL = 25, VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION = 1000156000, VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE = 1000085000, + VK_OBJECT_TYPE_PRIVATE_DATA_SLOT = 1000295000, VK_OBJECT_TYPE_SURFACE_KHR = 1000000000, VK_OBJECT_TYPE_SWAPCHAIN_KHR = 1000001000, VK_OBJECT_TYPE_DISPLAY_KHR = 1000002000, @@ -1063,9 +1215,10 @@ typedef enum VkObjectType { VK_OBJECT_TYPE_PERFORMANCE_CONFIGURATION_INTEL = 1000210000, VK_OBJECT_TYPE_DEFERRED_OPERATION_KHR = 1000268000, VK_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NV = 1000277000, - VK_OBJECT_TYPE_PRIVATE_DATA_SLOT_EXT = 1000295000, + VK_OBJECT_TYPE_BUFFER_COLLECTION_FUCHSIA = 1000366000, VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_KHR = VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE, VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_KHR = VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION, + VK_OBJECT_TYPE_PRIVATE_DATA_SLOT_EXT = VK_OBJECT_TYPE_PRIVATE_DATA_SLOT, VK_OBJECT_TYPE_MAX_ENUM = 0x7FFFFFFF } VkObjectType; @@ -1318,6 +1471,26 @@ typedef enum VkFormat { VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM = 1000156031, VK_FORMAT_G16_B16R16_2PLANE_422_UNORM = 1000156032, VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM = 1000156033, + VK_FORMAT_G8_B8R8_2PLANE_444_UNORM = 1000330000, + VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16 = 1000330001, + VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16 = 1000330002, + VK_FORMAT_G16_B16R16_2PLANE_444_UNORM = 1000330003, + VK_FORMAT_A4R4G4B4_UNORM_PACK16 = 1000340000, + VK_FORMAT_A4B4G4R4_UNORM_PACK16 = 1000340001, + VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK = 1000066000, + VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK = 1000066001, + VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK = 1000066002, + VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK = 1000066003, + VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK = 1000066004, + VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK = 1000066005, + VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK = 1000066006, + VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK = 1000066007, + VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK = 1000066008, + VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK = 1000066009, + VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK = 1000066010, + VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK = 1000066011, + VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK = 1000066012, + VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK = 1000066013, VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG = 1000054000, VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG = 1000054001, VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG = 1000054002, @@ -1326,26 +1499,20 @@ typedef enum VkFormat { VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG = 1000054005, VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG = 1000054006, VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG = 1000054007, - VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT = 1000066000, - VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT = 1000066001, - VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT = 1000066002, - VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT = 1000066003, - VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT = 1000066004, - VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT = 1000066005, - VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT = 1000066006, - VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT = 1000066007, - VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT = 1000066008, - VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT = 1000066009, - VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT = 1000066010, - VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT = 1000066011, - VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT = 1000066012, - VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT = 1000066013, - VK_FORMAT_G8_B8R8_2PLANE_444_UNORM_EXT = 1000330000, - VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16_EXT = 1000330001, - VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16_EXT = 1000330002, - VK_FORMAT_G16_B16R16_2PLANE_444_UNORM_EXT = 1000330003, - VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT = 1000340000, - VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT = 1000340001, + VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK, + VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK, + VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK, + VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK, + VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK, + VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK, + VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK, + VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK, + VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK, + VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK, + VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK, + VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK, + VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK, + VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK, VK_FORMAT_G8B8G8R8_422_UNORM_KHR = VK_FORMAT_G8B8G8R8_422_UNORM, VK_FORMAT_B8G8R8G8_422_UNORM_KHR = VK_FORMAT_B8G8R8G8_422_UNORM, VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM_KHR = VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM, @@ -1380,6 +1547,12 @@ typedef enum VkFormat { VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM_KHR = VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM, VK_FORMAT_G16_B16R16_2PLANE_422_UNORM_KHR = VK_FORMAT_G16_B16R16_2PLANE_422_UNORM, VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM_KHR = VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM, + VK_FORMAT_G8_B8R8_2PLANE_444_UNORM_EXT = VK_FORMAT_G8_B8R8_2PLANE_444_UNORM, + VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16_EXT = VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16, + VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16_EXT = VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16, + VK_FORMAT_G16_B16R16_2PLANE_444_UNORM_EXT = VK_FORMAT_G16_B16R16_2PLANE_444_UNORM, + VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT = VK_FORMAT_A4R4G4B4_UNORM_PACK16, + VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT = VK_FORMAT_A4B4G4R4_UNORM_PACK16, VK_FORMAT_MAX_ENUM = 0x7FFFFFFF } VkFormat; @@ -1422,6 +1595,7 @@ typedef enum VkQueryType { #ifdef VK_ENABLE_BETA_EXTENSIONS VK_QUERY_TYPE_VIDEO_ENCODE_BITSTREAM_BUFFER_RANGE_KHR = 1000299000, #endif + VK_QUERY_TYPE_PRIMITIVES_GENERATED_EXT = 1000382000, VK_QUERY_TYPE_MAX_ENUM = 0x7FFFFFFF } VkQueryType; @@ -1553,6 +1727,21 @@ typedef enum VkDynamicState { VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK = 6, VK_DYNAMIC_STATE_STENCIL_WRITE_MASK = 7, VK_DYNAMIC_STATE_STENCIL_REFERENCE = 8, + VK_DYNAMIC_STATE_CULL_MODE = 1000267000, + VK_DYNAMIC_STATE_FRONT_FACE = 1000267001, + VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY = 1000267002, + VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT = 1000267003, + VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT = 1000267004, + VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE = 1000267005, + VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE = 1000267006, + VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE = 1000267007, + VK_DYNAMIC_STATE_DEPTH_COMPARE_OP = 1000267008, + VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE = 1000267009, + VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE = 1000267010, + VK_DYNAMIC_STATE_STENCIL_OP = 1000267011, + VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE = 1000377001, + VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE = 1000377002, + VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE = 1000377004, VK_DYNAMIC_STATE_VIEWPORT_W_SCALING_NV = 1000087000, VK_DYNAMIC_STATE_DISCARD_RECTANGLE_EXT = 1000099000, VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_EXT = 1000143000, @@ -1562,25 +1751,25 @@ typedef enum VkDynamicState { VK_DYNAMIC_STATE_EXCLUSIVE_SCISSOR_NV = 1000205001, VK_DYNAMIC_STATE_FRAGMENT_SHADING_RATE_KHR = 1000226000, VK_DYNAMIC_STATE_LINE_STIPPLE_EXT = 1000259000, - VK_DYNAMIC_STATE_CULL_MODE_EXT = 1000267000, - VK_DYNAMIC_STATE_FRONT_FACE_EXT = 1000267001, - VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY_EXT = 1000267002, - VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT_EXT = 1000267003, - VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT_EXT = 1000267004, - VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT = 1000267005, - VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT = 1000267006, - VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT = 1000267007, - VK_DYNAMIC_STATE_DEPTH_COMPARE_OP_EXT = 1000267008, - VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE_EXT = 1000267009, - VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT = 1000267010, - VK_DYNAMIC_STATE_STENCIL_OP_EXT = 1000267011, VK_DYNAMIC_STATE_VERTEX_INPUT_EXT = 1000352000, VK_DYNAMIC_STATE_PATCH_CONTROL_POINTS_EXT = 1000377000, - VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE_EXT = 1000377001, - VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE_EXT = 1000377002, VK_DYNAMIC_STATE_LOGIC_OP_EXT = 1000377003, - VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE_EXT = 1000377004, VK_DYNAMIC_STATE_COLOR_WRITE_ENABLE_EXT = 1000381000, + VK_DYNAMIC_STATE_CULL_MODE_EXT = VK_DYNAMIC_STATE_CULL_MODE, + VK_DYNAMIC_STATE_FRONT_FACE_EXT = VK_DYNAMIC_STATE_FRONT_FACE, + VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY_EXT = VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY, + VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT_EXT = VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT, + VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT_EXT = VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT, + VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT = VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE, + VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT = VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE, + VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT = VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE, + VK_DYNAMIC_STATE_DEPTH_COMPARE_OP_EXT = VK_DYNAMIC_STATE_DEPTH_COMPARE_OP, + VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE_EXT = VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE, + VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT = VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE, + VK_DYNAMIC_STATE_STENCIL_OP_EXT = VK_DYNAMIC_STATE_STENCIL_OP, + VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE_EXT = VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE, + VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE_EXT = VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE, + VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE_EXT = VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE, VK_DYNAMIC_STATE_MAX_ENUM = 0x7FFFFFFF } VkDynamicState; @@ -1699,10 +1888,11 @@ typedef enum VkDescriptorType { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC = 8, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC = 9, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT = 10, - VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT = 1000138000, + VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK = 1000138000, VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR = 1000150000, VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV = 1000165000, VK_DESCRIPTOR_TYPE_MUTABLE_VALVE = 1000351000, + VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT = VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK, VK_DESCRIPTOR_TYPE_MAX_ENUM = 0x7FFFFFFF } VkDescriptorType; @@ -1717,8 +1907,10 @@ typedef enum VkAttachmentLoadOp { typedef enum VkAttachmentStoreOp { VK_ATTACHMENT_STORE_OP_STORE = 0, VK_ATTACHMENT_STORE_OP_DONT_CARE = 1, - VK_ATTACHMENT_STORE_OP_NONE_EXT = 1000301000, - VK_ATTACHMENT_STORE_OP_NONE_QCOM = VK_ATTACHMENT_STORE_OP_NONE_EXT, + VK_ATTACHMENT_STORE_OP_NONE = 1000301000, + VK_ATTACHMENT_STORE_OP_NONE_KHR = VK_ATTACHMENT_STORE_OP_NONE, + VK_ATTACHMENT_STORE_OP_NONE_QCOM = VK_ATTACHMENT_STORE_OP_NONE, + VK_ATTACHMENT_STORE_OP_NONE_EXT = VK_ATTACHMENT_STORE_OP_NONE, VK_ATTACHMENT_STORE_OP_MAX_ENUM = 0x7FFFFFFF } VkAttachmentStoreOp; @@ -1770,6 +1962,7 @@ typedef enum VkAccessFlagBits { VK_ACCESS_HOST_WRITE_BIT = 0x00004000, VK_ACCESS_MEMORY_READ_BIT = 0x00008000, VK_ACCESS_MEMORY_WRITE_BIT = 0x00010000, + VK_ACCESS_NONE = 0, VK_ACCESS_TRANSFORM_FEEDBACK_WRITE_BIT_EXT = 0x02000000, VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT = 0x04000000, VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT = 0x08000000, @@ -1781,10 +1974,10 @@ typedef enum VkAccessFlagBits { VK_ACCESS_FRAGMENT_SHADING_RATE_ATTACHMENT_READ_BIT_KHR = 0x00800000, VK_ACCESS_COMMAND_PREPROCESS_READ_BIT_NV = 0x00020000, VK_ACCESS_COMMAND_PREPROCESS_WRITE_BIT_NV = 0x00040000, - VK_ACCESS_NONE_KHR = 0, VK_ACCESS_SHADING_RATE_IMAGE_READ_BIT_NV = VK_ACCESS_FRAGMENT_SHADING_RATE_ATTACHMENT_READ_BIT_KHR, VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_NV = VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR, VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_NV = VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR, + VK_ACCESS_NONE_KHR = VK_ACCESS_NONE, VK_ACCESS_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkAccessFlagBits; typedef VkFlags VkAccessFlags; @@ -1797,6 +1990,7 @@ typedef enum VkImageAspectFlagBits { VK_IMAGE_ASPECT_PLANE_0_BIT = 0x00000010, VK_IMAGE_ASPECT_PLANE_1_BIT = 0x00000020, VK_IMAGE_ASPECT_PLANE_2_BIT = 0x00000040, + VK_IMAGE_ASPECT_NONE = 0, VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT = 0x00000080, VK_IMAGE_ASPECT_MEMORY_PLANE_1_BIT_EXT = 0x00000100, VK_IMAGE_ASPECT_MEMORY_PLANE_2_BIT_EXT = 0x00000200, @@ -1804,6 +1998,7 @@ typedef enum VkImageAspectFlagBits { VK_IMAGE_ASPECT_PLANE_0_BIT_KHR = VK_IMAGE_ASPECT_PLANE_0_BIT, VK_IMAGE_ASPECT_PLANE_1_BIT_KHR = VK_IMAGE_ASPECT_PLANE_1_BIT, VK_IMAGE_ASPECT_PLANE_2_BIT_KHR = VK_IMAGE_ASPECT_PLANE_2_BIT, + VK_IMAGE_ASPECT_NONE_KHR = VK_IMAGE_ASPECT_NONE, VK_IMAGE_ASPECT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkImageAspectFlagBits; typedef VkFlags VkImageAspectFlags; @@ -1879,6 +2074,8 @@ typedef enum VkImageCreateFlagBits { VK_IMAGE_CREATE_CORNER_SAMPLED_BIT_NV = 0x00002000, VK_IMAGE_CREATE_SAMPLE_LOCATIONS_COMPATIBLE_DEPTH_BIT_EXT = 0x00001000, VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT = 0x00004000, + VK_IMAGE_CREATE_2D_VIEW_COMPATIBLE_BIT_EXT = 0x00020000, + VK_IMAGE_CREATE_FRAGMENT_DENSITY_MAP_OFFSET_BIT_QCOM = 0x00008000, VK_IMAGE_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT_KHR = VK_IMAGE_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT, VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT_KHR = VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT, VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT_KHR = VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT, @@ -1935,6 +2132,11 @@ typedef enum VkImageUsageFlagBits { VK_IMAGE_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkImageUsageFlagBits; typedef VkFlags VkImageUsageFlags; + +typedef enum VkInstanceCreateFlagBits { + VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR = 0x00000001, + VK_INSTANCE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkInstanceCreateFlagBits; typedef VkFlags VkInstanceCreateFlags; typedef enum VkMemoryHeapFlagBits { @@ -2000,6 +2202,7 @@ typedef enum VkPipelineStageFlagBits { VK_PIPELINE_STAGE_HOST_BIT = 0x00004000, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT = 0x00008000, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT = 0x00010000, + VK_PIPELINE_STAGE_NONE = 0, VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT = 0x01000000, VK_PIPELINE_STAGE_CONDITIONAL_RENDERING_BIT_EXT = 0x00040000, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR = 0x02000000, @@ -2009,10 +2212,10 @@ typedef enum VkPipelineStageFlagBits { VK_PIPELINE_STAGE_FRAGMENT_DENSITY_PROCESS_BIT_EXT = 0x00800000, VK_PIPELINE_STAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR = 0x00400000, VK_PIPELINE_STAGE_COMMAND_PREPROCESS_BIT_NV = 0x00020000, - VK_PIPELINE_STAGE_NONE_KHR = 0, VK_PIPELINE_STAGE_SHADING_RATE_IMAGE_BIT_NV = VK_PIPELINE_STAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR, VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_NV = VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_NV = VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, + VK_PIPELINE_STAGE_NONE_KHR = VK_PIPELINE_STAGE_NONE, VK_PIPELINE_STAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkPipelineStageFlagBits; typedef VkFlags VkPipelineStageFlags; @@ -2040,7 +2243,8 @@ typedef VkFlags VkFenceCreateFlags; typedef VkFlags VkSemaphoreCreateFlags; typedef enum VkEventCreateFlagBits { - VK_EVENT_CREATE_DEVICE_ONLY_BIT_KHR = 0x00000001, + VK_EVENT_CREATE_DEVICE_ONLY_BIT = 0x00000001, + VK_EVENT_CREATE_DEVICE_ONLY_BIT_KHR = VK_EVENT_CREATE_DEVICE_ONLY_BIT, VK_EVENT_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkEventCreateFlagBits; typedef VkFlags VkEventCreateFlags; @@ -2132,7 +2336,8 @@ typedef VkFlags VkImageViewCreateFlags; typedef VkFlags VkShaderModuleCreateFlags; typedef enum VkPipelineCacheCreateFlagBits { - VK_PIPELINE_CACHE_CREATE_EXTERNALLY_SYNCHRONIZED_BIT_EXT = 0x00000001, + VK_PIPELINE_CACHE_CREATE_EXTERNALLY_SYNCHRONIZED_BIT = 0x00000001, + VK_PIPELINE_CACHE_CREATE_EXTERNALLY_SYNCHRONIZED_BIT_EXT = VK_PIPELINE_CACHE_CREATE_EXTERNALLY_SYNCHRONIZED_BIT, VK_PIPELINE_CACHE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkPipelineCacheCreateFlagBits; typedef VkFlags VkPipelineCacheCreateFlags; @@ -2152,6 +2357,10 @@ typedef enum VkPipelineCreateFlagBits { VK_PIPELINE_CREATE_DERIVATIVE_BIT = 0x00000004, VK_PIPELINE_CREATE_VIEW_INDEX_FROM_DEVICE_INDEX_BIT = 0x00000008, VK_PIPELINE_CREATE_DISPATCH_BASE_BIT = 0x00000010, + VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT = 0x00000100, + VK_PIPELINE_CREATE_EARLY_RETURN_ON_FAILURE_BIT = 0x00000200, + VK_PIPELINE_CREATE_RENDERING_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR = 0x00200000, + VK_PIPELINE_CREATE_RENDERING_FRAGMENT_DENSITY_MAP_ATTACHMENT_BIT_EXT = 0x00400000, VK_PIPELINE_CREATE_RAY_TRACING_NO_NULL_ANY_HIT_SHADERS_BIT_KHR = 0x00004000, VK_PIPELINE_CREATE_RAY_TRACING_NO_NULL_CLOSEST_HIT_SHADERS_BIT_KHR = 0x00008000, VK_PIPELINE_CREATE_RAY_TRACING_NO_NULL_MISS_SHADERS_BIT_KHR = 0x00010000, @@ -2164,19 +2373,25 @@ typedef enum VkPipelineCreateFlagBits { VK_PIPELINE_CREATE_CAPTURE_INTERNAL_REPRESENTATIONS_BIT_KHR = 0x00000080, VK_PIPELINE_CREATE_INDIRECT_BINDABLE_BIT_NV = 0x00040000, VK_PIPELINE_CREATE_LIBRARY_BIT_KHR = 0x00000800, - VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT_EXT = 0x00000100, - VK_PIPELINE_CREATE_EARLY_RETURN_ON_FAILURE_BIT_EXT = 0x00000200, + VK_PIPELINE_CREATE_RETAIN_LINK_TIME_OPTIMIZATION_INFO_BIT_EXT = 0x00800000, + VK_PIPELINE_CREATE_LINK_TIME_OPTIMIZATION_BIT_EXT = 0x00000400, VK_PIPELINE_CREATE_RAY_TRACING_ALLOW_MOTION_BIT_NV = 0x00100000, VK_PIPELINE_CREATE_DISPATCH_BASE = VK_PIPELINE_CREATE_DISPATCH_BASE_BIT, + VK_PIPELINE_RASTERIZATION_STATE_CREATE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR = VK_PIPELINE_CREATE_RENDERING_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR, + VK_PIPELINE_RASTERIZATION_STATE_CREATE_FRAGMENT_DENSITY_MAP_ATTACHMENT_BIT_EXT = VK_PIPELINE_CREATE_RENDERING_FRAGMENT_DENSITY_MAP_ATTACHMENT_BIT_EXT, VK_PIPELINE_CREATE_VIEW_INDEX_FROM_DEVICE_INDEX_BIT_KHR = VK_PIPELINE_CREATE_VIEW_INDEX_FROM_DEVICE_INDEX_BIT, VK_PIPELINE_CREATE_DISPATCH_BASE_KHR = VK_PIPELINE_CREATE_DISPATCH_BASE, + VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT_EXT = VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT, + VK_PIPELINE_CREATE_EARLY_RETURN_ON_FAILURE_BIT_EXT = VK_PIPELINE_CREATE_EARLY_RETURN_ON_FAILURE_BIT, VK_PIPELINE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkPipelineCreateFlagBits; typedef VkFlags VkPipelineCreateFlags; typedef enum VkPipelineShaderStageCreateFlagBits { - VK_PIPELINE_SHADER_STAGE_CREATE_ALLOW_VARYING_SUBGROUP_SIZE_BIT_EXT = 0x00000001, - VK_PIPELINE_SHADER_STAGE_CREATE_REQUIRE_FULL_SUBGROUPS_BIT_EXT = 0x00000002, + VK_PIPELINE_SHADER_STAGE_CREATE_ALLOW_VARYING_SUBGROUP_SIZE_BIT = 0x00000001, + VK_PIPELINE_SHADER_STAGE_CREATE_REQUIRE_FULL_SUBGROUPS_BIT = 0x00000002, + VK_PIPELINE_SHADER_STAGE_CREATE_ALLOW_VARYING_SUBGROUP_SIZE_BIT_EXT = VK_PIPELINE_SHADER_STAGE_CREATE_ALLOW_VARYING_SUBGROUP_SIZE_BIT, + VK_PIPELINE_SHADER_STAGE_CREATE_REQUIRE_FULL_SUBGROUPS_BIT_EXT = VK_PIPELINE_SHADER_STAGE_CREATE_REQUIRE_FULL_SUBGROUPS_BIT, VK_PIPELINE_SHADER_STAGE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkPipelineShaderStageCreateFlagBits; typedef VkFlags VkPipelineShaderStageCreateFlags; @@ -2222,9 +2437,25 @@ typedef VkFlags VkPipelineTessellationStateCreateFlags; typedef VkFlags VkPipelineViewportStateCreateFlags; typedef VkFlags VkPipelineRasterizationStateCreateFlags; typedef VkFlags VkPipelineMultisampleStateCreateFlags; + +typedef enum VkPipelineDepthStencilStateCreateFlagBits { + VK_PIPELINE_DEPTH_STENCIL_STATE_CREATE_RASTERIZATION_ORDER_ATTACHMENT_DEPTH_ACCESS_BIT_ARM = 0x00000001, + VK_PIPELINE_DEPTH_STENCIL_STATE_CREATE_RASTERIZATION_ORDER_ATTACHMENT_STENCIL_ACCESS_BIT_ARM = 0x00000002, + VK_PIPELINE_DEPTH_STENCIL_STATE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkPipelineDepthStencilStateCreateFlagBits; typedef VkFlags VkPipelineDepthStencilStateCreateFlags; + +typedef enum VkPipelineColorBlendStateCreateFlagBits { + VK_PIPELINE_COLOR_BLEND_STATE_CREATE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_BIT_ARM = 0x00000001, + VK_PIPELINE_COLOR_BLEND_STATE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkPipelineColorBlendStateCreateFlagBits; typedef VkFlags VkPipelineColorBlendStateCreateFlags; typedef VkFlags VkPipelineDynamicStateCreateFlags; + +typedef enum VkPipelineLayoutCreateFlagBits { + VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT = 0x00000002, + VK_PIPELINE_LAYOUT_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkPipelineLayoutCreateFlagBits; typedef VkFlags VkPipelineLayoutCreateFlags; typedef VkFlags VkShaderStageFlags; @@ -2288,6 +2519,9 @@ typedef enum VkSubpassDescriptionFlagBits { VK_SUBPASS_DESCRIPTION_PER_VIEW_POSITION_X_ONLY_BIT_NVX = 0x00000002, VK_SUBPASS_DESCRIPTION_FRAGMENT_REGION_BIT_QCOM = 0x00000004, VK_SUBPASS_DESCRIPTION_SHADER_RESOLVE_BIT_QCOM = 0x00000008, + VK_SUBPASS_DESCRIPTION_RASTERIZATION_ORDER_ATTACHMENT_COLOR_ACCESS_BIT_ARM = 0x00000010, + VK_SUBPASS_DESCRIPTION_RASTERIZATION_ORDER_ATTACHMENT_DEPTH_ACCESS_BIT_ARM = 0x00000020, + VK_SUBPASS_DESCRIPTION_RASTERIZATION_ORDER_ATTACHMENT_STENCIL_ACCESS_BIT_ARM = 0x00000040, VK_SUBPASS_DESCRIPTION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkSubpassDescriptionFlagBits; typedef VkFlags VkSubpassDescriptionFlags; @@ -5285,6 +5519,11 @@ typedef enum VkDriverId { VK_DRIVER_ID_COREAVI_PROPRIETARY = 15, VK_DRIVER_ID_JUICE_PROPRIETARY = 16, VK_DRIVER_ID_VERISILICON_PROPRIETARY = 17, + VK_DRIVER_ID_MESA_TURNIP = 18, + VK_DRIVER_ID_MESA_V3DV = 19, + VK_DRIVER_ID_MESA_PANVK = 20, + VK_DRIVER_ID_SAMSUNG_PROPRIETARY = 21, + VK_DRIVER_ID_MESA_VENUS = 22, VK_DRIVER_ID_AMD_PROPRIETARY_KHR = VK_DRIVER_ID_AMD_PROPRIETARY, VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR = VK_DRIVER_ID_AMD_OPEN_SOURCE, VK_DRIVER_ID_MESA_RADV_KHR = VK_DRIVER_ID_MESA_RADV, @@ -6006,6 +6245,1037 @@ VKAPI_ATTR uint64_t VKAPI_CALL vkGetDeviceMemoryOpaqueCaptureAddress( #endif +#define VK_VERSION_1_3 1 +// Vulkan 1.3 version number +#define VK_API_VERSION_1_3 VK_MAKE_API_VERSION(0, 1, 3, 0)// Patch version should always be set to 0 + +typedef uint64_t VkFlags64; +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkPrivateDataSlot) + +typedef enum VkPipelineCreationFeedbackFlagBits { + VK_PIPELINE_CREATION_FEEDBACK_VALID_BIT = 0x00000001, + VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT = 0x00000002, + VK_PIPELINE_CREATION_FEEDBACK_BASE_PIPELINE_ACCELERATION_BIT = 0x00000004, + VK_PIPELINE_CREATION_FEEDBACK_VALID_BIT_EXT = VK_PIPELINE_CREATION_FEEDBACK_VALID_BIT, + VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT_EXT = VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT, + VK_PIPELINE_CREATION_FEEDBACK_BASE_PIPELINE_ACCELERATION_BIT_EXT = VK_PIPELINE_CREATION_FEEDBACK_BASE_PIPELINE_ACCELERATION_BIT, + VK_PIPELINE_CREATION_FEEDBACK_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkPipelineCreationFeedbackFlagBits; +typedef VkFlags VkPipelineCreationFeedbackFlags; + +typedef enum VkToolPurposeFlagBits { + VK_TOOL_PURPOSE_VALIDATION_BIT = 0x00000001, + VK_TOOL_PURPOSE_PROFILING_BIT = 0x00000002, + VK_TOOL_PURPOSE_TRACING_BIT = 0x00000004, + VK_TOOL_PURPOSE_ADDITIONAL_FEATURES_BIT = 0x00000008, + VK_TOOL_PURPOSE_MODIFYING_FEATURES_BIT = 0x00000010, + VK_TOOL_PURPOSE_DEBUG_REPORTING_BIT_EXT = 0x00000020, + VK_TOOL_PURPOSE_DEBUG_MARKERS_BIT_EXT = 0x00000040, + VK_TOOL_PURPOSE_VALIDATION_BIT_EXT = VK_TOOL_PURPOSE_VALIDATION_BIT, + VK_TOOL_PURPOSE_PROFILING_BIT_EXT = VK_TOOL_PURPOSE_PROFILING_BIT, + VK_TOOL_PURPOSE_TRACING_BIT_EXT = VK_TOOL_PURPOSE_TRACING_BIT, + VK_TOOL_PURPOSE_ADDITIONAL_FEATURES_BIT_EXT = VK_TOOL_PURPOSE_ADDITIONAL_FEATURES_BIT, + VK_TOOL_PURPOSE_MODIFYING_FEATURES_BIT_EXT = VK_TOOL_PURPOSE_MODIFYING_FEATURES_BIT, + VK_TOOL_PURPOSE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkToolPurposeFlagBits; +typedef VkFlags VkToolPurposeFlags; +typedef VkFlags VkPrivateDataSlotCreateFlags; +typedef VkFlags64 VkPipelineStageFlags2; + +// Flag bits for VkPipelineStageFlagBits2 +typedef VkFlags64 VkPipelineStageFlagBits2; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_NONE = 0ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_NONE_KHR = 0ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT = 0x00000001ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT_KHR = 0x00000001ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT = 0x00000002ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT_KHR = 0x00000002ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_VERTEX_INPUT_BIT = 0x00000004ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_VERTEX_INPUT_BIT_KHR = 0x00000004ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_VERTEX_SHADER_BIT = 0x00000008ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_VERTEX_SHADER_BIT_KHR = 0x00000008ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_TESSELLATION_CONTROL_SHADER_BIT = 0x00000010ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_TESSELLATION_CONTROL_SHADER_BIT_KHR = 0x00000010ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_TESSELLATION_EVALUATION_SHADER_BIT = 0x00000020ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_TESSELLATION_EVALUATION_SHADER_BIT_KHR = 0x00000020ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_GEOMETRY_SHADER_BIT = 0x00000040ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_GEOMETRY_SHADER_BIT_KHR = 0x00000040ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT = 0x00000080ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT_KHR = 0x00000080ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT = 0x00000100ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT_KHR = 0x00000100ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT = 0x00000200ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT_KHR = 0x00000200ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT = 0x00000400ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT_KHR = 0x00000400ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT = 0x00000800ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT_KHR = 0x00000800ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_ALL_TRANSFER_BIT = 0x00001000ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_ALL_TRANSFER_BIT_KHR = 0x00001000ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_TRANSFER_BIT = 0x00001000ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_TRANSFER_BIT_KHR = 0x00001000ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT = 0x00002000ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT_KHR = 0x00002000ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_HOST_BIT = 0x00004000ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_HOST_BIT_KHR = 0x00004000ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT = 0x00008000ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT_KHR = 0x00008000ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT = 0x00010000ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT_KHR = 0x00010000ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_COPY_BIT = 0x100000000ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_COPY_BIT_KHR = 0x100000000ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_RESOLVE_BIT = 0x200000000ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_RESOLVE_BIT_KHR = 0x200000000ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_BLIT_BIT = 0x400000000ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_BLIT_BIT_KHR = 0x400000000ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_CLEAR_BIT = 0x800000000ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_CLEAR_BIT_KHR = 0x800000000ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_INDEX_INPUT_BIT = 0x1000000000ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_INDEX_INPUT_BIT_KHR = 0x1000000000ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_VERTEX_ATTRIBUTE_INPUT_BIT = 0x2000000000ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_VERTEX_ATTRIBUTE_INPUT_BIT_KHR = 0x2000000000ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_PRE_RASTERIZATION_SHADERS_BIT = 0x4000000000ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_PRE_RASTERIZATION_SHADERS_BIT_KHR = 0x4000000000ULL; +#ifdef VK_ENABLE_BETA_EXTENSIONS +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR = 0x04000000ULL; +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_VIDEO_ENCODE_BIT_KHR = 0x08000000ULL; +#endif +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_TRANSFORM_FEEDBACK_BIT_EXT = 0x01000000ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_CONDITIONAL_RENDERING_BIT_EXT = 0x00040000ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_COMMAND_PREPROCESS_BIT_NV = 0x00020000ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR = 0x00400000ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_SHADING_RATE_IMAGE_BIT_NV = 0x00400000ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR = 0x02000000ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_RAY_TRACING_SHADER_BIT_KHR = 0x00200000ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_RAY_TRACING_SHADER_BIT_NV = 0x00200000ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_NV = 0x02000000ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_FRAGMENT_DENSITY_PROCESS_BIT_EXT = 0x00800000ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_TASK_SHADER_BIT_NV = 0x00080000ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_MESH_SHADER_BIT_NV = 0x00100000ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_SUBPASS_SHADING_BIT_HUAWEI = 0x8000000000ULL; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_INVOCATION_MASK_BIT_HUAWEI = 0x10000000000ULL; + +typedef VkFlags64 VkAccessFlags2; + +// Flag bits for VkAccessFlagBits2 +typedef VkFlags64 VkAccessFlagBits2; +static const VkAccessFlagBits2 VK_ACCESS_2_NONE = 0ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_NONE_KHR = 0ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_INDIRECT_COMMAND_READ_BIT = 0x00000001ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_INDIRECT_COMMAND_READ_BIT_KHR = 0x00000001ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_INDEX_READ_BIT = 0x00000002ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_INDEX_READ_BIT_KHR = 0x00000002ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_VERTEX_ATTRIBUTE_READ_BIT = 0x00000004ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_VERTEX_ATTRIBUTE_READ_BIT_KHR = 0x00000004ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_UNIFORM_READ_BIT = 0x00000008ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_UNIFORM_READ_BIT_KHR = 0x00000008ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_INPUT_ATTACHMENT_READ_BIT = 0x00000010ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_INPUT_ATTACHMENT_READ_BIT_KHR = 0x00000010ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_SHADER_READ_BIT = 0x00000020ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_SHADER_READ_BIT_KHR = 0x00000020ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_SHADER_WRITE_BIT = 0x00000040ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_SHADER_WRITE_BIT_KHR = 0x00000040ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT = 0x00000080ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT_KHR = 0x00000080ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT = 0x00000100ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT_KHR = 0x00000100ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_READ_BIT = 0x00000200ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_READ_BIT_KHR = 0x00000200ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT = 0x00000400ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT_KHR = 0x00000400ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_TRANSFER_READ_BIT = 0x00000800ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_TRANSFER_READ_BIT_KHR = 0x00000800ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_TRANSFER_WRITE_BIT = 0x00001000ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_TRANSFER_WRITE_BIT_KHR = 0x00001000ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_HOST_READ_BIT = 0x00002000ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_HOST_READ_BIT_KHR = 0x00002000ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_HOST_WRITE_BIT = 0x00004000ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_HOST_WRITE_BIT_KHR = 0x00004000ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_MEMORY_READ_BIT = 0x00008000ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_MEMORY_READ_BIT_KHR = 0x00008000ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_MEMORY_WRITE_BIT = 0x00010000ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_MEMORY_WRITE_BIT_KHR = 0x00010000ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_SHADER_SAMPLED_READ_BIT = 0x100000000ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_SHADER_SAMPLED_READ_BIT_KHR = 0x100000000ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_SHADER_STORAGE_READ_BIT = 0x200000000ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_SHADER_STORAGE_READ_BIT_KHR = 0x200000000ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT = 0x400000000ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT_KHR = 0x400000000ULL; +#ifdef VK_ENABLE_BETA_EXTENSIONS +static const VkAccessFlagBits2 VK_ACCESS_2_VIDEO_DECODE_READ_BIT_KHR = 0x800000000ULL; +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS +static const VkAccessFlagBits2 VK_ACCESS_2_VIDEO_DECODE_WRITE_BIT_KHR = 0x1000000000ULL; +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS +static const VkAccessFlagBits2 VK_ACCESS_2_VIDEO_ENCODE_READ_BIT_KHR = 0x2000000000ULL; +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS +static const VkAccessFlagBits2 VK_ACCESS_2_VIDEO_ENCODE_WRITE_BIT_KHR = 0x4000000000ULL; +#endif +static const VkAccessFlagBits2 VK_ACCESS_2_TRANSFORM_FEEDBACK_WRITE_BIT_EXT = 0x02000000ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT = 0x04000000ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT = 0x08000000ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_CONDITIONAL_RENDERING_READ_BIT_EXT = 0x00100000ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_COMMAND_PREPROCESS_READ_BIT_NV = 0x00020000ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_COMMAND_PREPROCESS_WRITE_BIT_NV = 0x00040000ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_FRAGMENT_SHADING_RATE_ATTACHMENT_READ_BIT_KHR = 0x00800000ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_SHADING_RATE_IMAGE_READ_BIT_NV = 0x00800000ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_ACCELERATION_STRUCTURE_READ_BIT_KHR = 0x00200000ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_ACCELERATION_STRUCTURE_WRITE_BIT_KHR = 0x00400000ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_ACCELERATION_STRUCTURE_READ_BIT_NV = 0x00200000ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_ACCELERATION_STRUCTURE_WRITE_BIT_NV = 0x00400000ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_FRAGMENT_DENSITY_MAP_READ_BIT_EXT = 0x01000000ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_COLOR_ATTACHMENT_READ_NONCOHERENT_BIT_EXT = 0x00080000ULL; +static const VkAccessFlagBits2 VK_ACCESS_2_INVOCATION_MASK_READ_BIT_HUAWEI = 0x8000000000ULL; + + +typedef enum VkSubmitFlagBits { + VK_SUBMIT_PROTECTED_BIT = 0x00000001, + VK_SUBMIT_PROTECTED_BIT_KHR = VK_SUBMIT_PROTECTED_BIT, + VK_SUBMIT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkSubmitFlagBits; +typedef VkFlags VkSubmitFlags; + +typedef enum VkRenderingFlagBits { + VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT = 0x00000001, + VK_RENDERING_SUSPENDING_BIT = 0x00000002, + VK_RENDERING_RESUMING_BIT = 0x00000004, + VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT_KHR = VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT, + VK_RENDERING_SUSPENDING_BIT_KHR = VK_RENDERING_SUSPENDING_BIT, + VK_RENDERING_RESUMING_BIT_KHR = VK_RENDERING_RESUMING_BIT, + VK_RENDERING_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkRenderingFlagBits; +typedef VkFlags VkRenderingFlags; +typedef VkFlags64 VkFormatFeatureFlags2; + +// Flag bits for VkFormatFeatureFlagBits2 +typedef VkFlags64 VkFormatFeatureFlagBits2; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_BIT = 0x00000001ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_BIT_KHR = 0x00000001ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_STORAGE_IMAGE_BIT = 0x00000002ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_STORAGE_IMAGE_BIT_KHR = 0x00000002ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_STORAGE_IMAGE_ATOMIC_BIT = 0x00000004ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_STORAGE_IMAGE_ATOMIC_BIT_KHR = 0x00000004ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_UNIFORM_TEXEL_BUFFER_BIT = 0x00000008ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_UNIFORM_TEXEL_BUFFER_BIT_KHR = 0x00000008ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_STORAGE_TEXEL_BUFFER_BIT = 0x00000010ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_STORAGE_TEXEL_BUFFER_BIT_KHR = 0x00000010ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_STORAGE_TEXEL_BUFFER_ATOMIC_BIT = 0x00000020ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_STORAGE_TEXEL_BUFFER_ATOMIC_BIT_KHR = 0x00000020ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_VERTEX_BUFFER_BIT = 0x00000040ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_VERTEX_BUFFER_BIT_KHR = 0x00000040ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT = 0x00000080ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT_KHR = 0x00000080ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BLEND_BIT = 0x00000100ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BLEND_BIT_KHR = 0x00000100ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_DEPTH_STENCIL_ATTACHMENT_BIT = 0x00000200ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_DEPTH_STENCIL_ATTACHMENT_BIT_KHR = 0x00000200ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_BLIT_SRC_BIT = 0x00000400ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_BLIT_SRC_BIT_KHR = 0x00000400ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_BLIT_DST_BIT = 0x00000800ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_BLIT_DST_BIT_KHR = 0x00000800ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_FILTER_LINEAR_BIT = 0x00001000ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_FILTER_LINEAR_BIT_KHR = 0x00001000ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_FILTER_CUBIC_BIT = 0x00002000ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_FILTER_CUBIC_BIT_EXT = 0x00002000ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_TRANSFER_SRC_BIT = 0x00004000ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_TRANSFER_SRC_BIT_KHR = 0x00004000ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_TRANSFER_DST_BIT = 0x00008000ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_TRANSFER_DST_BIT_KHR = 0x00008000ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_FILTER_MINMAX_BIT = 0x00010000ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_FILTER_MINMAX_BIT_KHR = 0x00010000ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_MIDPOINT_CHROMA_SAMPLES_BIT = 0x00020000ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_MIDPOINT_CHROMA_SAMPLES_BIT_KHR = 0x00020000ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT = 0x00040000ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT_KHR = 0x00040000ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT = 0x00080000ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT_KHR = 0x00080000ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_BIT = 0x00100000ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_BIT_KHR = 0x00100000ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_FORCEABLE_BIT = 0x00200000ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_FORCEABLE_BIT_KHR = 0x00200000ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_DISJOINT_BIT = 0x00400000ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_DISJOINT_BIT_KHR = 0x00400000ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_COSITED_CHROMA_SAMPLES_BIT = 0x00800000ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_COSITED_CHROMA_SAMPLES_BIT_KHR = 0x00800000ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_STORAGE_READ_WITHOUT_FORMAT_BIT = 0x80000000ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_STORAGE_READ_WITHOUT_FORMAT_BIT_KHR = 0x80000000ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_STORAGE_WRITE_WITHOUT_FORMAT_BIT = 0x100000000ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_STORAGE_WRITE_WITHOUT_FORMAT_BIT_KHR = 0x100000000ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_DEPTH_COMPARISON_BIT = 0x200000000ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_DEPTH_COMPARISON_BIT_KHR = 0x200000000ULL; +#ifdef VK_ENABLE_BETA_EXTENSIONS +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_VIDEO_DECODE_OUTPUT_BIT_KHR = 0x02000000ULL; +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_VIDEO_DECODE_DPB_BIT_KHR = 0x04000000ULL; +#endif +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_ACCELERATION_STRUCTURE_VERTEX_BUFFER_BIT_KHR = 0x20000000ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_FRAGMENT_DENSITY_MAP_BIT_EXT = 0x01000000ULL; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR = 0x40000000ULL; +#ifdef VK_ENABLE_BETA_EXTENSIONS +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_VIDEO_ENCODE_INPUT_BIT_KHR = 0x08000000ULL; +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_VIDEO_ENCODE_DPB_BIT_KHR = 0x10000000ULL; +#endif +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_LINEAR_COLOR_ATTACHMENT_BIT_NV = 0x4000000000ULL; + +typedef struct VkPhysicalDeviceVulkan13Features { + VkStructureType sType; + void* pNext; + VkBool32 robustImageAccess; + VkBool32 inlineUniformBlock; + VkBool32 descriptorBindingInlineUniformBlockUpdateAfterBind; + VkBool32 pipelineCreationCacheControl; + VkBool32 privateData; + VkBool32 shaderDemoteToHelperInvocation; + VkBool32 shaderTerminateInvocation; + VkBool32 subgroupSizeControl; + VkBool32 computeFullSubgroups; + VkBool32 synchronization2; + VkBool32 textureCompressionASTC_HDR; + VkBool32 shaderZeroInitializeWorkgroupMemory; + VkBool32 dynamicRendering; + VkBool32 shaderIntegerDotProduct; + VkBool32 maintenance4; +} VkPhysicalDeviceVulkan13Features; + +typedef struct VkPhysicalDeviceVulkan13Properties { + VkStructureType sType; + void* pNext; + uint32_t minSubgroupSize; + uint32_t maxSubgroupSize; + uint32_t maxComputeWorkgroupSubgroups; + VkShaderStageFlags requiredSubgroupSizeStages; + uint32_t maxInlineUniformBlockSize; + uint32_t maxPerStageDescriptorInlineUniformBlocks; + uint32_t maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks; + uint32_t maxDescriptorSetInlineUniformBlocks; + uint32_t maxDescriptorSetUpdateAfterBindInlineUniformBlocks; + uint32_t maxInlineUniformTotalSize; + VkBool32 integerDotProduct8BitUnsignedAccelerated; + VkBool32 integerDotProduct8BitSignedAccelerated; + VkBool32 integerDotProduct8BitMixedSignednessAccelerated; + VkBool32 integerDotProduct4x8BitPackedUnsignedAccelerated; + VkBool32 integerDotProduct4x8BitPackedSignedAccelerated; + VkBool32 integerDotProduct4x8BitPackedMixedSignednessAccelerated; + VkBool32 integerDotProduct16BitUnsignedAccelerated; + VkBool32 integerDotProduct16BitSignedAccelerated; + VkBool32 integerDotProduct16BitMixedSignednessAccelerated; + VkBool32 integerDotProduct32BitUnsignedAccelerated; + VkBool32 integerDotProduct32BitSignedAccelerated; + VkBool32 integerDotProduct32BitMixedSignednessAccelerated; + VkBool32 integerDotProduct64BitUnsignedAccelerated; + VkBool32 integerDotProduct64BitSignedAccelerated; + VkBool32 integerDotProduct64BitMixedSignednessAccelerated; + VkBool32 integerDotProductAccumulatingSaturating8BitUnsignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating8BitSignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating8BitMixedSignednessAccelerated; + VkBool32 integerDotProductAccumulatingSaturating4x8BitPackedUnsignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating4x8BitPackedSignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating4x8BitPackedMixedSignednessAccelerated; + VkBool32 integerDotProductAccumulatingSaturating16BitUnsignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating16BitSignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating16BitMixedSignednessAccelerated; + VkBool32 integerDotProductAccumulatingSaturating32BitUnsignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating32BitSignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating32BitMixedSignednessAccelerated; + VkBool32 integerDotProductAccumulatingSaturating64BitUnsignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating64BitSignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating64BitMixedSignednessAccelerated; + VkDeviceSize storageTexelBufferOffsetAlignmentBytes; + VkBool32 storageTexelBufferOffsetSingleTexelAlignment; + VkDeviceSize uniformTexelBufferOffsetAlignmentBytes; + VkBool32 uniformTexelBufferOffsetSingleTexelAlignment; + VkDeviceSize maxBufferSize; +} VkPhysicalDeviceVulkan13Properties; + +typedef struct VkPipelineCreationFeedback { + VkPipelineCreationFeedbackFlags flags; + uint64_t duration; +} VkPipelineCreationFeedback; + +typedef struct VkPipelineCreationFeedbackCreateInfo { + VkStructureType sType; + const void* pNext; + VkPipelineCreationFeedback* pPipelineCreationFeedback; + uint32_t pipelineStageCreationFeedbackCount; + VkPipelineCreationFeedback* pPipelineStageCreationFeedbacks; +} VkPipelineCreationFeedbackCreateInfo; + +typedef struct VkPhysicalDeviceShaderTerminateInvocationFeatures { + VkStructureType sType; + void* pNext; + VkBool32 shaderTerminateInvocation; +} VkPhysicalDeviceShaderTerminateInvocationFeatures; + +typedef struct VkPhysicalDeviceToolProperties { + VkStructureType sType; + void* pNext; + char name[VK_MAX_EXTENSION_NAME_SIZE]; + char version[VK_MAX_EXTENSION_NAME_SIZE]; + VkToolPurposeFlags purposes; + char description[VK_MAX_DESCRIPTION_SIZE]; + char layer[VK_MAX_EXTENSION_NAME_SIZE]; +} VkPhysicalDeviceToolProperties; + +typedef struct VkPhysicalDeviceShaderDemoteToHelperInvocationFeatures { + VkStructureType sType; + void* pNext; + VkBool32 shaderDemoteToHelperInvocation; +} VkPhysicalDeviceShaderDemoteToHelperInvocationFeatures; + +typedef struct VkPhysicalDevicePrivateDataFeatures { + VkStructureType sType; + void* pNext; + VkBool32 privateData; +} VkPhysicalDevicePrivateDataFeatures; + +typedef struct VkDevicePrivateDataCreateInfo { + VkStructureType sType; + const void* pNext; + uint32_t privateDataSlotRequestCount; +} VkDevicePrivateDataCreateInfo; + +typedef struct VkPrivateDataSlotCreateInfo { + VkStructureType sType; + const void* pNext; + VkPrivateDataSlotCreateFlags flags; +} VkPrivateDataSlotCreateInfo; + +typedef struct VkPhysicalDevicePipelineCreationCacheControlFeatures { + VkStructureType sType; + void* pNext; + VkBool32 pipelineCreationCacheControl; +} VkPhysicalDevicePipelineCreationCacheControlFeatures; + +typedef struct VkMemoryBarrier2 { + VkStructureType sType; + const void* pNext; + VkPipelineStageFlags2 srcStageMask; + VkAccessFlags2 srcAccessMask; + VkPipelineStageFlags2 dstStageMask; + VkAccessFlags2 dstAccessMask; +} VkMemoryBarrier2; + +typedef struct VkBufferMemoryBarrier2 { + VkStructureType sType; + const void* pNext; + VkPipelineStageFlags2 srcStageMask; + VkAccessFlags2 srcAccessMask; + VkPipelineStageFlags2 dstStageMask; + VkAccessFlags2 dstAccessMask; + uint32_t srcQueueFamilyIndex; + uint32_t dstQueueFamilyIndex; + VkBuffer buffer; + VkDeviceSize offset; + VkDeviceSize size; +} VkBufferMemoryBarrier2; + +typedef struct VkImageMemoryBarrier2 { + VkStructureType sType; + const void* pNext; + VkPipelineStageFlags2 srcStageMask; + VkAccessFlags2 srcAccessMask; + VkPipelineStageFlags2 dstStageMask; + VkAccessFlags2 dstAccessMask; + VkImageLayout oldLayout; + VkImageLayout newLayout; + uint32_t srcQueueFamilyIndex; + uint32_t dstQueueFamilyIndex; + VkImage image; + VkImageSubresourceRange subresourceRange; +} VkImageMemoryBarrier2; + +typedef struct VkDependencyInfo { + VkStructureType sType; + const void* pNext; + VkDependencyFlags dependencyFlags; + uint32_t memoryBarrierCount; + const VkMemoryBarrier2* pMemoryBarriers; + uint32_t bufferMemoryBarrierCount; + const VkBufferMemoryBarrier2* pBufferMemoryBarriers; + uint32_t imageMemoryBarrierCount; + const VkImageMemoryBarrier2* pImageMemoryBarriers; +} VkDependencyInfo; + +typedef struct VkSemaphoreSubmitInfo { + VkStructureType sType; + const void* pNext; + VkSemaphore semaphore; + uint64_t value; + VkPipelineStageFlags2 stageMask; + uint32_t deviceIndex; +} VkSemaphoreSubmitInfo; + +typedef struct VkCommandBufferSubmitInfo { + VkStructureType sType; + const void* pNext; + VkCommandBuffer commandBuffer; + uint32_t deviceMask; +} VkCommandBufferSubmitInfo; + +typedef struct VkSubmitInfo2 { + VkStructureType sType; + const void* pNext; + VkSubmitFlags flags; + uint32_t waitSemaphoreInfoCount; + const VkSemaphoreSubmitInfo* pWaitSemaphoreInfos; + uint32_t commandBufferInfoCount; + const VkCommandBufferSubmitInfo* pCommandBufferInfos; + uint32_t signalSemaphoreInfoCount; + const VkSemaphoreSubmitInfo* pSignalSemaphoreInfos; +} VkSubmitInfo2; + +typedef struct VkPhysicalDeviceSynchronization2Features { + VkStructureType sType; + void* pNext; + VkBool32 synchronization2; +} VkPhysicalDeviceSynchronization2Features; + +typedef struct VkPhysicalDeviceZeroInitializeWorkgroupMemoryFeatures { + VkStructureType sType; + void* pNext; + VkBool32 shaderZeroInitializeWorkgroupMemory; +} VkPhysicalDeviceZeroInitializeWorkgroupMemoryFeatures; + +typedef struct VkPhysicalDeviceImageRobustnessFeatures { + VkStructureType sType; + void* pNext; + VkBool32 robustImageAccess; +} VkPhysicalDeviceImageRobustnessFeatures; + +typedef struct VkBufferCopy2 { + VkStructureType sType; + const void* pNext; + VkDeviceSize srcOffset; + VkDeviceSize dstOffset; + VkDeviceSize size; +} VkBufferCopy2; + +typedef struct VkCopyBufferInfo2 { + VkStructureType sType; + const void* pNext; + VkBuffer srcBuffer; + VkBuffer dstBuffer; + uint32_t regionCount; + const VkBufferCopy2* pRegions; +} VkCopyBufferInfo2; + +typedef struct VkImageCopy2 { + VkStructureType sType; + const void* pNext; + VkImageSubresourceLayers srcSubresource; + VkOffset3D srcOffset; + VkImageSubresourceLayers dstSubresource; + VkOffset3D dstOffset; + VkExtent3D extent; +} VkImageCopy2; + +typedef struct VkCopyImageInfo2 { + VkStructureType sType; + const void* pNext; + VkImage srcImage; + VkImageLayout srcImageLayout; + VkImage dstImage; + VkImageLayout dstImageLayout; + uint32_t regionCount; + const VkImageCopy2* pRegions; +} VkCopyImageInfo2; + +typedef struct VkBufferImageCopy2 { + VkStructureType sType; + const void* pNext; + VkDeviceSize bufferOffset; + uint32_t bufferRowLength; + uint32_t bufferImageHeight; + VkImageSubresourceLayers imageSubresource; + VkOffset3D imageOffset; + VkExtent3D imageExtent; +} VkBufferImageCopy2; + +typedef struct VkCopyBufferToImageInfo2 { + VkStructureType sType; + const void* pNext; + VkBuffer srcBuffer; + VkImage dstImage; + VkImageLayout dstImageLayout; + uint32_t regionCount; + const VkBufferImageCopy2* pRegions; +} VkCopyBufferToImageInfo2; + +typedef struct VkCopyImageToBufferInfo2 { + VkStructureType sType; + const void* pNext; + VkImage srcImage; + VkImageLayout srcImageLayout; + VkBuffer dstBuffer; + uint32_t regionCount; + const VkBufferImageCopy2* pRegions; +} VkCopyImageToBufferInfo2; + +typedef struct VkImageBlit2 { + VkStructureType sType; + const void* pNext; + VkImageSubresourceLayers srcSubresource; + VkOffset3D srcOffsets[2]; + VkImageSubresourceLayers dstSubresource; + VkOffset3D dstOffsets[2]; +} VkImageBlit2; + +typedef struct VkBlitImageInfo2 { + VkStructureType sType; + const void* pNext; + VkImage srcImage; + VkImageLayout srcImageLayout; + VkImage dstImage; + VkImageLayout dstImageLayout; + uint32_t regionCount; + const VkImageBlit2* pRegions; + VkFilter filter; +} VkBlitImageInfo2; + +typedef struct VkImageResolve2 { + VkStructureType sType; + const void* pNext; + VkImageSubresourceLayers srcSubresource; + VkOffset3D srcOffset; + VkImageSubresourceLayers dstSubresource; + VkOffset3D dstOffset; + VkExtent3D extent; +} VkImageResolve2; + +typedef struct VkResolveImageInfo2 { + VkStructureType sType; + const void* pNext; + VkImage srcImage; + VkImageLayout srcImageLayout; + VkImage dstImage; + VkImageLayout dstImageLayout; + uint32_t regionCount; + const VkImageResolve2* pRegions; +} VkResolveImageInfo2; + +typedef struct VkPhysicalDeviceSubgroupSizeControlFeatures { + VkStructureType sType; + void* pNext; + VkBool32 subgroupSizeControl; + VkBool32 computeFullSubgroups; +} VkPhysicalDeviceSubgroupSizeControlFeatures; + +typedef struct VkPhysicalDeviceSubgroupSizeControlProperties { + VkStructureType sType; + void* pNext; + uint32_t minSubgroupSize; + uint32_t maxSubgroupSize; + uint32_t maxComputeWorkgroupSubgroups; + VkShaderStageFlags requiredSubgroupSizeStages; +} VkPhysicalDeviceSubgroupSizeControlProperties; + +typedef struct VkPipelineShaderStageRequiredSubgroupSizeCreateInfo { + VkStructureType sType; + void* pNext; + uint32_t requiredSubgroupSize; +} VkPipelineShaderStageRequiredSubgroupSizeCreateInfo; + +typedef struct VkPhysicalDeviceInlineUniformBlockFeatures { + VkStructureType sType; + void* pNext; + VkBool32 inlineUniformBlock; + VkBool32 descriptorBindingInlineUniformBlockUpdateAfterBind; +} VkPhysicalDeviceInlineUniformBlockFeatures; + +typedef struct VkPhysicalDeviceInlineUniformBlockProperties { + VkStructureType sType; + void* pNext; + uint32_t maxInlineUniformBlockSize; + uint32_t maxPerStageDescriptorInlineUniformBlocks; + uint32_t maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks; + uint32_t maxDescriptorSetInlineUniformBlocks; + uint32_t maxDescriptorSetUpdateAfterBindInlineUniformBlocks; +} VkPhysicalDeviceInlineUniformBlockProperties; + +typedef struct VkWriteDescriptorSetInlineUniformBlock { + VkStructureType sType; + const void* pNext; + uint32_t dataSize; + const void* pData; +} VkWriteDescriptorSetInlineUniformBlock; + +typedef struct VkDescriptorPoolInlineUniformBlockCreateInfo { + VkStructureType sType; + const void* pNext; + uint32_t maxInlineUniformBlockBindings; +} VkDescriptorPoolInlineUniformBlockCreateInfo; + +typedef struct VkPhysicalDeviceTextureCompressionASTCHDRFeatures { + VkStructureType sType; + void* pNext; + VkBool32 textureCompressionASTC_HDR; +} VkPhysicalDeviceTextureCompressionASTCHDRFeatures; + +typedef struct VkRenderingAttachmentInfo { + VkStructureType sType; + const void* pNext; + VkImageView imageView; + VkImageLayout imageLayout; + VkResolveModeFlagBits resolveMode; + VkImageView resolveImageView; + VkImageLayout resolveImageLayout; + VkAttachmentLoadOp loadOp; + VkAttachmentStoreOp storeOp; + VkClearValue clearValue; +} VkRenderingAttachmentInfo; + +typedef struct VkRenderingInfo { + VkStructureType sType; + const void* pNext; + VkRenderingFlags flags; + VkRect2D renderArea; + uint32_t layerCount; + uint32_t viewMask; + uint32_t colorAttachmentCount; + const VkRenderingAttachmentInfo* pColorAttachments; + const VkRenderingAttachmentInfo* pDepthAttachment; + const VkRenderingAttachmentInfo* pStencilAttachment; +} VkRenderingInfo; + +typedef struct VkPipelineRenderingCreateInfo { + VkStructureType sType; + const void* pNext; + uint32_t viewMask; + uint32_t colorAttachmentCount; + const VkFormat* pColorAttachmentFormats; + VkFormat depthAttachmentFormat; + VkFormat stencilAttachmentFormat; +} VkPipelineRenderingCreateInfo; + +typedef struct VkPhysicalDeviceDynamicRenderingFeatures { + VkStructureType sType; + void* pNext; + VkBool32 dynamicRendering; +} VkPhysicalDeviceDynamicRenderingFeatures; + +typedef struct VkCommandBufferInheritanceRenderingInfo { + VkStructureType sType; + const void* pNext; + VkRenderingFlags flags; + uint32_t viewMask; + uint32_t colorAttachmentCount; + const VkFormat* pColorAttachmentFormats; + VkFormat depthAttachmentFormat; + VkFormat stencilAttachmentFormat; + VkSampleCountFlagBits rasterizationSamples; +} VkCommandBufferInheritanceRenderingInfo; + +typedef struct VkPhysicalDeviceShaderIntegerDotProductFeatures { + VkStructureType sType; + void* pNext; + VkBool32 shaderIntegerDotProduct; +} VkPhysicalDeviceShaderIntegerDotProductFeatures; + +typedef struct VkPhysicalDeviceShaderIntegerDotProductProperties { + VkStructureType sType; + void* pNext; + VkBool32 integerDotProduct8BitUnsignedAccelerated; + VkBool32 integerDotProduct8BitSignedAccelerated; + VkBool32 integerDotProduct8BitMixedSignednessAccelerated; + VkBool32 integerDotProduct4x8BitPackedUnsignedAccelerated; + VkBool32 integerDotProduct4x8BitPackedSignedAccelerated; + VkBool32 integerDotProduct4x8BitPackedMixedSignednessAccelerated; + VkBool32 integerDotProduct16BitUnsignedAccelerated; + VkBool32 integerDotProduct16BitSignedAccelerated; + VkBool32 integerDotProduct16BitMixedSignednessAccelerated; + VkBool32 integerDotProduct32BitUnsignedAccelerated; + VkBool32 integerDotProduct32BitSignedAccelerated; + VkBool32 integerDotProduct32BitMixedSignednessAccelerated; + VkBool32 integerDotProduct64BitUnsignedAccelerated; + VkBool32 integerDotProduct64BitSignedAccelerated; + VkBool32 integerDotProduct64BitMixedSignednessAccelerated; + VkBool32 integerDotProductAccumulatingSaturating8BitUnsignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating8BitSignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating8BitMixedSignednessAccelerated; + VkBool32 integerDotProductAccumulatingSaturating4x8BitPackedUnsignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating4x8BitPackedSignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating4x8BitPackedMixedSignednessAccelerated; + VkBool32 integerDotProductAccumulatingSaturating16BitUnsignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating16BitSignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating16BitMixedSignednessAccelerated; + VkBool32 integerDotProductAccumulatingSaturating32BitUnsignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating32BitSignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating32BitMixedSignednessAccelerated; + VkBool32 integerDotProductAccumulatingSaturating64BitUnsignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating64BitSignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating64BitMixedSignednessAccelerated; +} VkPhysicalDeviceShaderIntegerDotProductProperties; + +typedef struct VkPhysicalDeviceTexelBufferAlignmentProperties { + VkStructureType sType; + void* pNext; + VkDeviceSize storageTexelBufferOffsetAlignmentBytes; + VkBool32 storageTexelBufferOffsetSingleTexelAlignment; + VkDeviceSize uniformTexelBufferOffsetAlignmentBytes; + VkBool32 uniformTexelBufferOffsetSingleTexelAlignment; +} VkPhysicalDeviceTexelBufferAlignmentProperties; + +typedef struct VkFormatProperties3 { + VkStructureType sType; + void* pNext; + VkFormatFeatureFlags2 linearTilingFeatures; + VkFormatFeatureFlags2 optimalTilingFeatures; + VkFormatFeatureFlags2 bufferFeatures; +} VkFormatProperties3; + +typedef struct VkPhysicalDeviceMaintenance4Features { + VkStructureType sType; + void* pNext; + VkBool32 maintenance4; +} VkPhysicalDeviceMaintenance4Features; + +typedef struct VkPhysicalDeviceMaintenance4Properties { + VkStructureType sType; + void* pNext; + VkDeviceSize maxBufferSize; +} VkPhysicalDeviceMaintenance4Properties; + +typedef struct VkDeviceBufferMemoryRequirements { + VkStructureType sType; + const void* pNext; + const VkBufferCreateInfo* pCreateInfo; +} VkDeviceBufferMemoryRequirements; + +typedef struct VkDeviceImageMemoryRequirements { + VkStructureType sType; + const void* pNext; + const VkImageCreateInfo* pCreateInfo; + VkImageAspectFlagBits planeAspect; +} VkDeviceImageMemoryRequirements; + +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceToolProperties)(VkPhysicalDevice physicalDevice, uint32_t* pToolCount, VkPhysicalDeviceToolProperties* pToolProperties); +typedef VkResult (VKAPI_PTR *PFN_vkCreatePrivateDataSlot)(VkDevice device, const VkPrivateDataSlotCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkPrivateDataSlot* pPrivateDataSlot); +typedef void (VKAPI_PTR *PFN_vkDestroyPrivateDataSlot)(VkDevice device, VkPrivateDataSlot privateDataSlot, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkSetPrivateData)(VkDevice device, VkObjectType objectType, uint64_t objectHandle, VkPrivateDataSlot privateDataSlot, uint64_t data); +typedef void (VKAPI_PTR *PFN_vkGetPrivateData)(VkDevice device, VkObjectType objectType, uint64_t objectHandle, VkPrivateDataSlot privateDataSlot, uint64_t* pData); +typedef void (VKAPI_PTR *PFN_vkCmdSetEvent2)(VkCommandBuffer commandBuffer, VkEvent event, const VkDependencyInfo* pDependencyInfo); +typedef void (VKAPI_PTR *PFN_vkCmdResetEvent2)(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags2 stageMask); +typedef void (VKAPI_PTR *PFN_vkCmdWaitEvents2)(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent* pEvents, const VkDependencyInfo* pDependencyInfos); +typedef void (VKAPI_PTR *PFN_vkCmdPipelineBarrier2)(VkCommandBuffer commandBuffer, const VkDependencyInfo* pDependencyInfo); +typedef void (VKAPI_PTR *PFN_vkCmdWriteTimestamp2)(VkCommandBuffer commandBuffer, VkPipelineStageFlags2 stage, VkQueryPool queryPool, uint32_t query); +typedef VkResult (VKAPI_PTR *PFN_vkQueueSubmit2)(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2* pSubmits, VkFence fence); +typedef void (VKAPI_PTR *PFN_vkCmdCopyBuffer2)(VkCommandBuffer commandBuffer, const VkCopyBufferInfo2* pCopyBufferInfo); +typedef void (VKAPI_PTR *PFN_vkCmdCopyImage2)(VkCommandBuffer commandBuffer, const VkCopyImageInfo2* pCopyImageInfo); +typedef void (VKAPI_PTR *PFN_vkCmdCopyBufferToImage2)(VkCommandBuffer commandBuffer, const VkCopyBufferToImageInfo2* pCopyBufferToImageInfo); +typedef void (VKAPI_PTR *PFN_vkCmdCopyImageToBuffer2)(VkCommandBuffer commandBuffer, const VkCopyImageToBufferInfo2* pCopyImageToBufferInfo); +typedef void (VKAPI_PTR *PFN_vkCmdBlitImage2)(VkCommandBuffer commandBuffer, const VkBlitImageInfo2* pBlitImageInfo); +typedef void (VKAPI_PTR *PFN_vkCmdResolveImage2)(VkCommandBuffer commandBuffer, const VkResolveImageInfo2* pResolveImageInfo); +typedef void (VKAPI_PTR *PFN_vkCmdBeginRendering)(VkCommandBuffer commandBuffer, const VkRenderingInfo* pRenderingInfo); +typedef void (VKAPI_PTR *PFN_vkCmdEndRendering)(VkCommandBuffer commandBuffer); +typedef void (VKAPI_PTR *PFN_vkCmdSetCullMode)(VkCommandBuffer commandBuffer, VkCullModeFlags cullMode); +typedef void (VKAPI_PTR *PFN_vkCmdSetFrontFace)(VkCommandBuffer commandBuffer, VkFrontFace frontFace); +typedef void (VKAPI_PTR *PFN_vkCmdSetPrimitiveTopology)(VkCommandBuffer commandBuffer, VkPrimitiveTopology primitiveTopology); +typedef void (VKAPI_PTR *PFN_vkCmdSetViewportWithCount)(VkCommandBuffer commandBuffer, uint32_t viewportCount, const VkViewport* pViewports); +typedef void (VKAPI_PTR *PFN_vkCmdSetScissorWithCount)(VkCommandBuffer commandBuffer, uint32_t scissorCount, const VkRect2D* pScissors); +typedef void (VKAPI_PTR *PFN_vkCmdBindVertexBuffers2)(VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, const VkBuffer* pBuffers, const VkDeviceSize* pOffsets, const VkDeviceSize* pSizes, const VkDeviceSize* pStrides); +typedef void (VKAPI_PTR *PFN_vkCmdSetDepthTestEnable)(VkCommandBuffer commandBuffer, VkBool32 depthTestEnable); +typedef void (VKAPI_PTR *PFN_vkCmdSetDepthWriteEnable)(VkCommandBuffer commandBuffer, VkBool32 depthWriteEnable); +typedef void (VKAPI_PTR *PFN_vkCmdSetDepthCompareOp)(VkCommandBuffer commandBuffer, VkCompareOp depthCompareOp); +typedef void (VKAPI_PTR *PFN_vkCmdSetDepthBoundsTestEnable)(VkCommandBuffer commandBuffer, VkBool32 depthBoundsTestEnable); +typedef void (VKAPI_PTR *PFN_vkCmdSetStencilTestEnable)(VkCommandBuffer commandBuffer, VkBool32 stencilTestEnable); +typedef void (VKAPI_PTR *PFN_vkCmdSetStencilOp)(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, VkStencilOp failOp, VkStencilOp passOp, VkStencilOp depthFailOp, VkCompareOp compareOp); +typedef void (VKAPI_PTR *PFN_vkCmdSetRasterizerDiscardEnable)(VkCommandBuffer commandBuffer, VkBool32 rasterizerDiscardEnable); +typedef void (VKAPI_PTR *PFN_vkCmdSetDepthBiasEnable)(VkCommandBuffer commandBuffer, VkBool32 depthBiasEnable); +typedef void (VKAPI_PTR *PFN_vkCmdSetPrimitiveRestartEnable)(VkCommandBuffer commandBuffer, VkBool32 primitiveRestartEnable); +typedef void (VKAPI_PTR *PFN_vkGetDeviceBufferMemoryRequirements)(VkDevice device, const VkDeviceBufferMemoryRequirements* pInfo, VkMemoryRequirements2* pMemoryRequirements); +typedef void (VKAPI_PTR *PFN_vkGetDeviceImageMemoryRequirements)(VkDevice device, const VkDeviceImageMemoryRequirements* pInfo, VkMemoryRequirements2* pMemoryRequirements); +typedef void (VKAPI_PTR *PFN_vkGetDeviceImageSparseMemoryRequirements)(VkDevice device, const VkDeviceImageMemoryRequirements* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceToolProperties( + VkPhysicalDevice physicalDevice, + uint32_t* pToolCount, + VkPhysicalDeviceToolProperties* pToolProperties); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreatePrivateDataSlot( + VkDevice device, + const VkPrivateDataSlotCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkPrivateDataSlot* pPrivateDataSlot); + +VKAPI_ATTR void VKAPI_CALL vkDestroyPrivateDataSlot( + VkDevice device, + VkPrivateDataSlot privateDataSlot, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkSetPrivateData( + VkDevice device, + VkObjectType objectType, + uint64_t objectHandle, + VkPrivateDataSlot privateDataSlot, + uint64_t data); + +VKAPI_ATTR void VKAPI_CALL vkGetPrivateData( + VkDevice device, + VkObjectType objectType, + uint64_t objectHandle, + VkPrivateDataSlot privateDataSlot, + uint64_t* pData); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetEvent2( + VkCommandBuffer commandBuffer, + VkEvent event, + const VkDependencyInfo* pDependencyInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdResetEvent2( + VkCommandBuffer commandBuffer, + VkEvent event, + VkPipelineStageFlags2 stageMask); + +VKAPI_ATTR void VKAPI_CALL vkCmdWaitEvents2( + VkCommandBuffer commandBuffer, + uint32_t eventCount, + const VkEvent* pEvents, + const VkDependencyInfo* pDependencyInfos); + +VKAPI_ATTR void VKAPI_CALL vkCmdPipelineBarrier2( + VkCommandBuffer commandBuffer, + const VkDependencyInfo* pDependencyInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdWriteTimestamp2( + VkCommandBuffer commandBuffer, + VkPipelineStageFlags2 stage, + VkQueryPool queryPool, + uint32_t query); + +VKAPI_ATTR VkResult VKAPI_CALL vkQueueSubmit2( + VkQueue queue, + uint32_t submitCount, + const VkSubmitInfo2* pSubmits, + VkFence fence); + +VKAPI_ATTR void VKAPI_CALL vkCmdCopyBuffer2( + VkCommandBuffer commandBuffer, + const VkCopyBufferInfo2* pCopyBufferInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdCopyImage2( + VkCommandBuffer commandBuffer, + const VkCopyImageInfo2* pCopyImageInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdCopyBufferToImage2( + VkCommandBuffer commandBuffer, + const VkCopyBufferToImageInfo2* pCopyBufferToImageInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdCopyImageToBuffer2( + VkCommandBuffer commandBuffer, + const VkCopyImageToBufferInfo2* pCopyImageToBufferInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdBlitImage2( + VkCommandBuffer commandBuffer, + const VkBlitImageInfo2* pBlitImageInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdResolveImage2( + VkCommandBuffer commandBuffer, + const VkResolveImageInfo2* pResolveImageInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdBeginRendering( + VkCommandBuffer commandBuffer, + const VkRenderingInfo* pRenderingInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdEndRendering( + VkCommandBuffer commandBuffer); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetCullMode( + VkCommandBuffer commandBuffer, + VkCullModeFlags cullMode); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetFrontFace( + VkCommandBuffer commandBuffer, + VkFrontFace frontFace); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetPrimitiveTopology( + VkCommandBuffer commandBuffer, + VkPrimitiveTopology primitiveTopology); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetViewportWithCount( + VkCommandBuffer commandBuffer, + uint32_t viewportCount, + const VkViewport* pViewports); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetScissorWithCount( + VkCommandBuffer commandBuffer, + uint32_t scissorCount, + const VkRect2D* pScissors); + +VKAPI_ATTR void VKAPI_CALL vkCmdBindVertexBuffers2( + VkCommandBuffer commandBuffer, + uint32_t firstBinding, + uint32_t bindingCount, + const VkBuffer* pBuffers, + const VkDeviceSize* pOffsets, + const VkDeviceSize* pSizes, + const VkDeviceSize* pStrides); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetDepthTestEnable( + VkCommandBuffer commandBuffer, + VkBool32 depthTestEnable); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetDepthWriteEnable( + VkCommandBuffer commandBuffer, + VkBool32 depthWriteEnable); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetDepthCompareOp( + VkCommandBuffer commandBuffer, + VkCompareOp depthCompareOp); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetDepthBoundsTestEnable( + VkCommandBuffer commandBuffer, + VkBool32 depthBoundsTestEnable); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetStencilTestEnable( + VkCommandBuffer commandBuffer, + VkBool32 stencilTestEnable); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetStencilOp( + VkCommandBuffer commandBuffer, + VkStencilFaceFlags faceMask, + VkStencilOp failOp, + VkStencilOp passOp, + VkStencilOp depthFailOp, + VkCompareOp compareOp); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetRasterizerDiscardEnable( + VkCommandBuffer commandBuffer, + VkBool32 rasterizerDiscardEnable); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetDepthBiasEnable( + VkCommandBuffer commandBuffer, + VkBool32 depthBiasEnable); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetPrimitiveRestartEnable( + VkCommandBuffer commandBuffer, + VkBool32 primitiveRestartEnable); + +VKAPI_ATTR void VKAPI_CALL vkGetDeviceBufferMemoryRequirements( + VkDevice device, + const VkDeviceBufferMemoryRequirements* pInfo, + VkMemoryRequirements2* pMemoryRequirements); + +VKAPI_ATTR void VKAPI_CALL vkGetDeviceImageMemoryRequirements( + VkDevice device, + const VkDeviceImageMemoryRequirements* pInfo, + VkMemoryRequirements2* pMemoryRequirements); + +VKAPI_ATTR void VKAPI_CALL vkGetDeviceImageSparseMemoryRequirements( + VkDevice device, + const VkDeviceImageMemoryRequirements* pInfo, + uint32_t* pSparseMemoryRequirementCount, + VkSparseImageMemoryRequirements2* pSparseMemoryRequirements); +#endif + + #define VK_KHR_surface 1 VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSurfaceKHR) #define VK_KHR_SURFACE_SPEC_VERSION 25 @@ -6432,6 +7702,68 @@ VKAPI_ATTR VkResult VKAPI_CALL vkCreateSharedSwapchainsKHR( #define VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME "VK_KHR_sampler_mirror_clamp_to_edge" +#define VK_KHR_dynamic_rendering 1 +#define VK_KHR_DYNAMIC_RENDERING_SPEC_VERSION 1 +#define VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME "VK_KHR_dynamic_rendering" +typedef VkRenderingFlags VkRenderingFlagsKHR; + +typedef VkRenderingFlagBits VkRenderingFlagBitsKHR; + +typedef VkRenderingInfo VkRenderingInfoKHR; + +typedef VkRenderingAttachmentInfo VkRenderingAttachmentInfoKHR; + +typedef VkPipelineRenderingCreateInfo VkPipelineRenderingCreateInfoKHR; + +typedef VkPhysicalDeviceDynamicRenderingFeatures VkPhysicalDeviceDynamicRenderingFeaturesKHR; + +typedef VkCommandBufferInheritanceRenderingInfo VkCommandBufferInheritanceRenderingInfoKHR; + +typedef struct VkRenderingFragmentShadingRateAttachmentInfoKHR { + VkStructureType sType; + const void* pNext; + VkImageView imageView; + VkImageLayout imageLayout; + VkExtent2D shadingRateAttachmentTexelSize; +} VkRenderingFragmentShadingRateAttachmentInfoKHR; + +typedef struct VkRenderingFragmentDensityMapAttachmentInfoEXT { + VkStructureType sType; + const void* pNext; + VkImageView imageView; + VkImageLayout imageLayout; +} VkRenderingFragmentDensityMapAttachmentInfoEXT; + +typedef struct VkAttachmentSampleCountInfoAMD { + VkStructureType sType; + const void* pNext; + uint32_t colorAttachmentCount; + const VkSampleCountFlagBits* pColorAttachmentSamples; + VkSampleCountFlagBits depthStencilAttachmentSamples; +} VkAttachmentSampleCountInfoAMD; + +typedef VkAttachmentSampleCountInfoAMD VkAttachmentSampleCountInfoNV; + +typedef struct VkMultiviewPerViewAttributesInfoNVX { + VkStructureType sType; + const void* pNext; + VkBool32 perViewAttributes; + VkBool32 perViewAttributesPositionXOnly; +} VkMultiviewPerViewAttributesInfoNVX; + +typedef void (VKAPI_PTR *PFN_vkCmdBeginRenderingKHR)(VkCommandBuffer commandBuffer, const VkRenderingInfo* pRenderingInfo); +typedef void (VKAPI_PTR *PFN_vkCmdEndRenderingKHR)(VkCommandBuffer commandBuffer); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdBeginRenderingKHR( + VkCommandBuffer commandBuffer, + const VkRenderingInfo* pRenderingInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdEndRenderingKHR( + VkCommandBuffer commandBuffer); +#endif + + #define VK_KHR_multiview 1 #define VK_KHR_MULTIVIEW_SPEC_VERSION 1 #define VK_KHR_MULTIVIEW_EXTENSION_NAME "VK_KHR_multiview" @@ -6566,8 +7898,10 @@ VKAPI_ATTR void VKAPI_CALL vkCmdDispatchBaseKHR( #define VK_KHR_maintenance1 1 -#define VK_KHR_MAINTENANCE1_SPEC_VERSION 2 -#define VK_KHR_MAINTENANCE1_EXTENSION_NAME "VK_KHR_maintenance1" +#define VK_KHR_MAINTENANCE_1_SPEC_VERSION 2 +#define VK_KHR_MAINTENANCE_1_EXTENSION_NAME "VK_KHR_maintenance1" +#define VK_KHR_MAINTENANCE1_SPEC_VERSION VK_KHR_MAINTENANCE_1_SPEC_VERSION +#define VK_KHR_MAINTENANCE1_EXTENSION_NAME VK_KHR_MAINTENANCE_1_EXTENSION_NAME typedef VkCommandPoolTrimFlags VkCommandPoolTrimFlagsKHR; typedef void (VKAPI_PTR *PFN_vkTrimCommandPoolKHR)(VkDevice device, VkCommandPool commandPool, VkCommandPoolTrimFlags flags); @@ -7147,8 +8481,10 @@ VKAPI_ATTR void VKAPI_CALL vkReleaseProfilingLockKHR( #define VK_KHR_maintenance2 1 -#define VK_KHR_MAINTENANCE2_SPEC_VERSION 1 -#define VK_KHR_MAINTENANCE2_EXTENSION_NAME "VK_KHR_maintenance2" +#define VK_KHR_MAINTENANCE_2_SPEC_VERSION 1 +#define VK_KHR_MAINTENANCE_2_EXTENSION_NAME "VK_KHR_maintenance2" +#define VK_KHR_MAINTENANCE2_SPEC_VERSION VK_KHR_MAINTENANCE_2_SPEC_VERSION +#define VK_KHR_MAINTENANCE2_EXTENSION_NAME VK_KHR_MAINTENANCE_2_EXTENSION_NAME typedef VkPointClippingBehavior VkPointClippingBehaviorKHR; typedef VkTessellationDomainOrigin VkTessellationDomainOriginKHR; @@ -7401,8 +8737,10 @@ VKAPI_ATTR VkResult VKAPI_CALL vkBindImageMemory2KHR( #define VK_KHR_maintenance3 1 -#define VK_KHR_MAINTENANCE3_SPEC_VERSION 1 -#define VK_KHR_MAINTENANCE3_EXTENSION_NAME "VK_KHR_maintenance3" +#define VK_KHR_MAINTENANCE_3_SPEC_VERSION 1 +#define VK_KHR_MAINTENANCE_3_EXTENSION_NAME "VK_KHR_maintenance3" +#define VK_KHR_MAINTENANCE3_SPEC_VERSION VK_KHR_MAINTENANCE_3_SPEC_VERSION +#define VK_KHR_MAINTENANCE3_EXTENSION_NAME VK_KHR_MAINTENANCE_3_EXTENSION_NAME typedef VkPhysicalDeviceMaintenance3Properties VkPhysicalDeviceMaintenance3PropertiesKHR; typedef VkDescriptorSetLayoutSupport VkDescriptorSetLayoutSupportKHR; @@ -7477,6 +8815,43 @@ typedef struct VkPhysicalDeviceShaderClockFeaturesKHR { +#define VK_KHR_global_priority 1 +#define VK_MAX_GLOBAL_PRIORITY_SIZE_KHR 16U +#define VK_KHR_GLOBAL_PRIORITY_SPEC_VERSION 1 +#define VK_KHR_GLOBAL_PRIORITY_EXTENSION_NAME "VK_KHR_global_priority" + +typedef enum VkQueueGlobalPriorityKHR { + VK_QUEUE_GLOBAL_PRIORITY_LOW_KHR = 128, + VK_QUEUE_GLOBAL_PRIORITY_MEDIUM_KHR = 256, + VK_QUEUE_GLOBAL_PRIORITY_HIGH_KHR = 512, + VK_QUEUE_GLOBAL_PRIORITY_REALTIME_KHR = 1024, + VK_QUEUE_GLOBAL_PRIORITY_LOW_EXT = VK_QUEUE_GLOBAL_PRIORITY_LOW_KHR, + VK_QUEUE_GLOBAL_PRIORITY_MEDIUM_EXT = VK_QUEUE_GLOBAL_PRIORITY_MEDIUM_KHR, + VK_QUEUE_GLOBAL_PRIORITY_HIGH_EXT = VK_QUEUE_GLOBAL_PRIORITY_HIGH_KHR, + VK_QUEUE_GLOBAL_PRIORITY_REALTIME_EXT = VK_QUEUE_GLOBAL_PRIORITY_REALTIME_KHR, + VK_QUEUE_GLOBAL_PRIORITY_MAX_ENUM_KHR = 0x7FFFFFFF +} VkQueueGlobalPriorityKHR; +typedef struct VkDeviceQueueGlobalPriorityCreateInfoKHR { + VkStructureType sType; + const void* pNext; + VkQueueGlobalPriorityKHR globalPriority; +} VkDeviceQueueGlobalPriorityCreateInfoKHR; + +typedef struct VkPhysicalDeviceGlobalPriorityQueryFeaturesKHR { + VkStructureType sType; + void* pNext; + VkBool32 globalPriorityQuery; +} VkPhysicalDeviceGlobalPriorityQueryFeaturesKHR; + +typedef struct VkQueueFamilyGlobalPriorityPropertiesKHR { + VkStructureType sType; + void* pNext; + uint32_t priorityCount; + VkQueueGlobalPriorityKHR priorities[VK_MAX_GLOBAL_PRIORITY_SIZE_KHR]; +} VkQueueFamilyGlobalPriorityPropertiesKHR; + + + #define VK_KHR_driver_properties 1 #define VK_KHR_DRIVER_PROPERTIES_SPEC_VERSION 1 #define VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME "VK_KHR_driver_properties" @@ -7569,16 +8944,12 @@ typedef VkPhysicalDeviceVulkanMemoryModelFeatures VkPhysicalDeviceVulkanMemoryMo #define VK_KHR_shader_terminate_invocation 1 #define VK_KHR_SHADER_TERMINATE_INVOCATION_SPEC_VERSION 1 #define VK_KHR_SHADER_TERMINATE_INVOCATION_EXTENSION_NAME "VK_KHR_shader_terminate_invocation" -typedef struct VkPhysicalDeviceShaderTerminateInvocationFeaturesKHR { - VkStructureType sType; - void* pNext; - VkBool32 shaderTerminateInvocation; -} VkPhysicalDeviceShaderTerminateInvocationFeaturesKHR; +typedef VkPhysicalDeviceShaderTerminateInvocationFeatures VkPhysicalDeviceShaderTerminateInvocationFeaturesKHR; #define VK_KHR_fragment_shading_rate 1 -#define VK_KHR_FRAGMENT_SHADING_RATE_SPEC_VERSION 1 +#define VK_KHR_FRAGMENT_SHADING_RATE_SPEC_VERSION 2 #define VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME "VK_KHR_fragment_shading_rate" typedef enum VkFragmentShadingRateCombinerOpKHR { @@ -7870,46 +9241,9 @@ VKAPI_ATTR VkResult VKAPI_CALL vkGetPipelineExecutableInternalRepresentationsKHR #define VK_KHR_shader_integer_dot_product 1 #define VK_KHR_SHADER_INTEGER_DOT_PRODUCT_SPEC_VERSION 1 #define VK_KHR_SHADER_INTEGER_DOT_PRODUCT_EXTENSION_NAME "VK_KHR_shader_integer_dot_product" -typedef struct VkPhysicalDeviceShaderIntegerDotProductFeaturesKHR { - VkStructureType sType; - void* pNext; - VkBool32 shaderIntegerDotProduct; -} VkPhysicalDeviceShaderIntegerDotProductFeaturesKHR; +typedef VkPhysicalDeviceShaderIntegerDotProductFeatures VkPhysicalDeviceShaderIntegerDotProductFeaturesKHR; -typedef struct VkPhysicalDeviceShaderIntegerDotProductPropertiesKHR { - VkStructureType sType; - void* pNext; - VkBool32 integerDotProduct8BitUnsignedAccelerated; - VkBool32 integerDotProduct8BitSignedAccelerated; - VkBool32 integerDotProduct8BitMixedSignednessAccelerated; - VkBool32 integerDotProduct4x8BitPackedUnsignedAccelerated; - VkBool32 integerDotProduct4x8BitPackedSignedAccelerated; - VkBool32 integerDotProduct4x8BitPackedMixedSignednessAccelerated; - VkBool32 integerDotProduct16BitUnsignedAccelerated; - VkBool32 integerDotProduct16BitSignedAccelerated; - VkBool32 integerDotProduct16BitMixedSignednessAccelerated; - VkBool32 integerDotProduct32BitUnsignedAccelerated; - VkBool32 integerDotProduct32BitSignedAccelerated; - VkBool32 integerDotProduct32BitMixedSignednessAccelerated; - VkBool32 integerDotProduct64BitUnsignedAccelerated; - VkBool32 integerDotProduct64BitSignedAccelerated; - VkBool32 integerDotProduct64BitMixedSignednessAccelerated; - VkBool32 integerDotProductAccumulatingSaturating8BitUnsignedAccelerated; - VkBool32 integerDotProductAccumulatingSaturating8BitSignedAccelerated; - VkBool32 integerDotProductAccumulatingSaturating8BitMixedSignednessAccelerated; - VkBool32 integerDotProductAccumulatingSaturating4x8BitPackedUnsignedAccelerated; - VkBool32 integerDotProductAccumulatingSaturating4x8BitPackedSignedAccelerated; - VkBool32 integerDotProductAccumulatingSaturating4x8BitPackedMixedSignednessAccelerated; - VkBool32 integerDotProductAccumulatingSaturating16BitUnsignedAccelerated; - VkBool32 integerDotProductAccumulatingSaturating16BitSignedAccelerated; - VkBool32 integerDotProductAccumulatingSaturating16BitMixedSignednessAccelerated; - VkBool32 integerDotProductAccumulatingSaturating32BitUnsignedAccelerated; - VkBool32 integerDotProductAccumulatingSaturating32BitSignedAccelerated; - VkBool32 integerDotProductAccumulatingSaturating32BitMixedSignednessAccelerated; - VkBool32 integerDotProductAccumulatingSaturating64BitUnsignedAccelerated; - VkBool32 integerDotProductAccumulatingSaturating64BitSignedAccelerated; - VkBool32 integerDotProductAccumulatingSaturating64BitMixedSignednessAccelerated; -} VkPhysicalDeviceShaderIntegerDotProductPropertiesKHR; +typedef VkPhysicalDeviceShaderIntegerDotProductProperties VkPhysicalDeviceShaderIntegerDotProductPropertiesKHR; @@ -7949,261 +9283,94 @@ typedef struct VkPhysicalDevicePresentIdFeaturesKHR { #define VK_KHR_synchronization2 1 -typedef uint64_t VkFlags64; #define VK_KHR_SYNCHRONIZATION_2_SPEC_VERSION 1 #define VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME "VK_KHR_synchronization2" -typedef VkFlags64 VkPipelineStageFlags2KHR; +typedef VkPipelineStageFlags2 VkPipelineStageFlags2KHR; -// Flag bits for VkPipelineStageFlagBits2KHR -typedef VkFlags64 VkPipelineStageFlagBits2KHR; -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_NONE_KHR = 0ULL; -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT_KHR = 0x00000001ULL; -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT_KHR = 0x00000002ULL; -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_VERTEX_INPUT_BIT_KHR = 0x00000004ULL; -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_VERTEX_SHADER_BIT_KHR = 0x00000008ULL; -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_TESSELLATION_CONTROL_SHADER_BIT_KHR = 0x00000010ULL; -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_TESSELLATION_EVALUATION_SHADER_BIT_KHR = 0x00000020ULL; -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_GEOMETRY_SHADER_BIT_KHR = 0x00000040ULL; -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT_KHR = 0x00000080ULL; -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT_KHR = 0x00000100ULL; -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT_KHR = 0x00000200ULL; -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT_KHR = 0x00000400ULL; -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT_KHR = 0x00000800ULL; -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_ALL_TRANSFER_BIT_KHR = 0x00001000ULL; -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_TRANSFER_BIT_KHR = 0x00001000ULL; -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT_KHR = 0x00002000ULL; -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_HOST_BIT_KHR = 0x00004000ULL; -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT_KHR = 0x00008000ULL; -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT_KHR = 0x00010000ULL; -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_COPY_BIT_KHR = 0x100000000ULL; -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_RESOLVE_BIT_KHR = 0x200000000ULL; -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_BLIT_BIT_KHR = 0x400000000ULL; -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_CLEAR_BIT_KHR = 0x800000000ULL; -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_INDEX_INPUT_BIT_KHR = 0x1000000000ULL; -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_VERTEX_ATTRIBUTE_INPUT_BIT_KHR = 0x2000000000ULL; -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_PRE_RASTERIZATION_SHADERS_BIT_KHR = 0x4000000000ULL; -#ifdef VK_ENABLE_BETA_EXTENSIONS -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR = 0x04000000ULL; -#endif -#ifdef VK_ENABLE_BETA_EXTENSIONS -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_VIDEO_ENCODE_BIT_KHR = 0x08000000ULL; -#endif -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_TRANSFORM_FEEDBACK_BIT_EXT = 0x01000000ULL; -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_CONDITIONAL_RENDERING_BIT_EXT = 0x00040000ULL; -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_COMMAND_PREPROCESS_BIT_NV = 0x00020000ULL; -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR = 0x00400000ULL; -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_SHADING_RATE_IMAGE_BIT_NV = 0x00400000ULL; -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR = 0x02000000ULL; -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_RAY_TRACING_SHADER_BIT_KHR = 0x00200000ULL; -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_RAY_TRACING_SHADER_BIT_NV = 0x00200000ULL; -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_NV = 0x02000000ULL; -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_FRAGMENT_DENSITY_PROCESS_BIT_EXT = 0x00800000ULL; -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_TASK_SHADER_BIT_NV = 0x00080000ULL; -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_MESH_SHADER_BIT_NV = 0x00100000ULL; -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_SUBPASS_SHADING_BIT_HUAWEI = 0x8000000000ULL; -static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_INVOCATION_MASK_BIT_HUAWEI = 0x10000000000ULL; +typedef VkPipelineStageFlagBits2 VkPipelineStageFlagBits2KHR; -typedef VkFlags64 VkAccessFlags2KHR; +typedef VkAccessFlags2 VkAccessFlags2KHR; -// Flag bits for VkAccessFlagBits2KHR -typedef VkFlags64 VkAccessFlagBits2KHR; -static const VkAccessFlagBits2KHR VK_ACCESS_2_NONE_KHR = 0ULL; -static const VkAccessFlagBits2KHR VK_ACCESS_2_INDIRECT_COMMAND_READ_BIT_KHR = 0x00000001ULL; -static const VkAccessFlagBits2KHR VK_ACCESS_2_INDEX_READ_BIT_KHR = 0x00000002ULL; -static const VkAccessFlagBits2KHR VK_ACCESS_2_VERTEX_ATTRIBUTE_READ_BIT_KHR = 0x00000004ULL; -static const VkAccessFlagBits2KHR VK_ACCESS_2_UNIFORM_READ_BIT_KHR = 0x00000008ULL; -static const VkAccessFlagBits2KHR VK_ACCESS_2_INPUT_ATTACHMENT_READ_BIT_KHR = 0x00000010ULL; -static const VkAccessFlagBits2KHR VK_ACCESS_2_SHADER_READ_BIT_KHR = 0x00000020ULL; -static const VkAccessFlagBits2KHR VK_ACCESS_2_SHADER_WRITE_BIT_KHR = 0x00000040ULL; -static const VkAccessFlagBits2KHR VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT_KHR = 0x00000080ULL; -static const VkAccessFlagBits2KHR VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT_KHR = 0x00000100ULL; -static const VkAccessFlagBits2KHR VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_READ_BIT_KHR = 0x00000200ULL; -static const VkAccessFlagBits2KHR VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT_KHR = 0x00000400ULL; -static const VkAccessFlagBits2KHR VK_ACCESS_2_TRANSFER_READ_BIT_KHR = 0x00000800ULL; -static const VkAccessFlagBits2KHR VK_ACCESS_2_TRANSFER_WRITE_BIT_KHR = 0x00001000ULL; -static const VkAccessFlagBits2KHR VK_ACCESS_2_HOST_READ_BIT_KHR = 0x00002000ULL; -static const VkAccessFlagBits2KHR VK_ACCESS_2_HOST_WRITE_BIT_KHR = 0x00004000ULL; -static const VkAccessFlagBits2KHR VK_ACCESS_2_MEMORY_READ_BIT_KHR = 0x00008000ULL; -static const VkAccessFlagBits2KHR VK_ACCESS_2_MEMORY_WRITE_BIT_KHR = 0x00010000ULL; -static const VkAccessFlagBits2KHR VK_ACCESS_2_SHADER_SAMPLED_READ_BIT_KHR = 0x100000000ULL; -static const VkAccessFlagBits2KHR VK_ACCESS_2_SHADER_STORAGE_READ_BIT_KHR = 0x200000000ULL; -static const VkAccessFlagBits2KHR VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT_KHR = 0x400000000ULL; -#ifdef VK_ENABLE_BETA_EXTENSIONS -static const VkAccessFlagBits2KHR VK_ACCESS_2_VIDEO_DECODE_READ_BIT_KHR = 0x800000000ULL; -#endif -#ifdef VK_ENABLE_BETA_EXTENSIONS -static const VkAccessFlagBits2KHR VK_ACCESS_2_VIDEO_DECODE_WRITE_BIT_KHR = 0x1000000000ULL; -#endif -#ifdef VK_ENABLE_BETA_EXTENSIONS -static const VkAccessFlagBits2KHR VK_ACCESS_2_VIDEO_ENCODE_READ_BIT_KHR = 0x2000000000ULL; -#endif -#ifdef VK_ENABLE_BETA_EXTENSIONS -static const VkAccessFlagBits2KHR VK_ACCESS_2_VIDEO_ENCODE_WRITE_BIT_KHR = 0x4000000000ULL; -#endif -static const VkAccessFlagBits2KHR VK_ACCESS_2_TRANSFORM_FEEDBACK_WRITE_BIT_EXT = 0x02000000ULL; -static const VkAccessFlagBits2KHR VK_ACCESS_2_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT = 0x04000000ULL; -static const VkAccessFlagBits2KHR VK_ACCESS_2_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT = 0x08000000ULL; -static const VkAccessFlagBits2KHR VK_ACCESS_2_CONDITIONAL_RENDERING_READ_BIT_EXT = 0x00100000ULL; -static const VkAccessFlagBits2KHR VK_ACCESS_2_COMMAND_PREPROCESS_READ_BIT_NV = 0x00020000ULL; -static const VkAccessFlagBits2KHR VK_ACCESS_2_COMMAND_PREPROCESS_WRITE_BIT_NV = 0x00040000ULL; -static const VkAccessFlagBits2KHR VK_ACCESS_2_FRAGMENT_SHADING_RATE_ATTACHMENT_READ_BIT_KHR = 0x00800000ULL; -static const VkAccessFlagBits2KHR VK_ACCESS_2_SHADING_RATE_IMAGE_READ_BIT_NV = 0x00800000ULL; -static const VkAccessFlagBits2KHR VK_ACCESS_2_ACCELERATION_STRUCTURE_READ_BIT_KHR = 0x00200000ULL; -static const VkAccessFlagBits2KHR VK_ACCESS_2_ACCELERATION_STRUCTURE_WRITE_BIT_KHR = 0x00400000ULL; -static const VkAccessFlagBits2KHR VK_ACCESS_2_ACCELERATION_STRUCTURE_READ_BIT_NV = 0x00200000ULL; -static const VkAccessFlagBits2KHR VK_ACCESS_2_ACCELERATION_STRUCTURE_WRITE_BIT_NV = 0x00400000ULL; -static const VkAccessFlagBits2KHR VK_ACCESS_2_FRAGMENT_DENSITY_MAP_READ_BIT_EXT = 0x01000000ULL; -static const VkAccessFlagBits2KHR VK_ACCESS_2_COLOR_ATTACHMENT_READ_NONCOHERENT_BIT_EXT = 0x00080000ULL; -static const VkAccessFlagBits2KHR VK_ACCESS_2_INVOCATION_MASK_READ_BIT_HUAWEI = 0x8000000000ULL; +typedef VkAccessFlagBits2 VkAccessFlagBits2KHR; +typedef VkSubmitFlagBits VkSubmitFlagBitsKHR; -typedef enum VkSubmitFlagBitsKHR { - VK_SUBMIT_PROTECTED_BIT_KHR = 0x00000001, - VK_SUBMIT_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF -} VkSubmitFlagBitsKHR; -typedef VkFlags VkSubmitFlagsKHR; -typedef struct VkMemoryBarrier2KHR { - VkStructureType sType; - const void* pNext; - VkPipelineStageFlags2KHR srcStageMask; - VkAccessFlags2KHR srcAccessMask; - VkPipelineStageFlags2KHR dstStageMask; - VkAccessFlags2KHR dstAccessMask; -} VkMemoryBarrier2KHR; +typedef VkSubmitFlags VkSubmitFlagsKHR; -typedef struct VkBufferMemoryBarrier2KHR { - VkStructureType sType; - const void* pNext; - VkPipelineStageFlags2KHR srcStageMask; - VkAccessFlags2KHR srcAccessMask; - VkPipelineStageFlags2KHR dstStageMask; - VkAccessFlags2KHR dstAccessMask; - uint32_t srcQueueFamilyIndex; - uint32_t dstQueueFamilyIndex; - VkBuffer buffer; - VkDeviceSize offset; - VkDeviceSize size; -} VkBufferMemoryBarrier2KHR; +typedef VkMemoryBarrier2 VkMemoryBarrier2KHR; -typedef struct VkImageMemoryBarrier2KHR { - VkStructureType sType; - const void* pNext; - VkPipelineStageFlags2KHR srcStageMask; - VkAccessFlags2KHR srcAccessMask; - VkPipelineStageFlags2KHR dstStageMask; - VkAccessFlags2KHR dstAccessMask; - VkImageLayout oldLayout; - VkImageLayout newLayout; - uint32_t srcQueueFamilyIndex; - uint32_t dstQueueFamilyIndex; - VkImage image; - VkImageSubresourceRange subresourceRange; -} VkImageMemoryBarrier2KHR; +typedef VkBufferMemoryBarrier2 VkBufferMemoryBarrier2KHR; -typedef struct VkDependencyInfoKHR { - VkStructureType sType; - const void* pNext; - VkDependencyFlags dependencyFlags; - uint32_t memoryBarrierCount; - const VkMemoryBarrier2KHR* pMemoryBarriers; - uint32_t bufferMemoryBarrierCount; - const VkBufferMemoryBarrier2KHR* pBufferMemoryBarriers; - uint32_t imageMemoryBarrierCount; - const VkImageMemoryBarrier2KHR* pImageMemoryBarriers; -} VkDependencyInfoKHR; +typedef VkImageMemoryBarrier2 VkImageMemoryBarrier2KHR; -typedef struct VkSemaphoreSubmitInfoKHR { - VkStructureType sType; - const void* pNext; - VkSemaphore semaphore; - uint64_t value; - VkPipelineStageFlags2KHR stageMask; - uint32_t deviceIndex; -} VkSemaphoreSubmitInfoKHR; +typedef VkDependencyInfo VkDependencyInfoKHR; -typedef struct VkCommandBufferSubmitInfoKHR { - VkStructureType sType; - const void* pNext; - VkCommandBuffer commandBuffer; - uint32_t deviceMask; -} VkCommandBufferSubmitInfoKHR; +typedef VkSubmitInfo2 VkSubmitInfo2KHR; -typedef struct VkSubmitInfo2KHR { - VkStructureType sType; - const void* pNext; - VkSubmitFlagsKHR flags; - uint32_t waitSemaphoreInfoCount; - const VkSemaphoreSubmitInfoKHR* pWaitSemaphoreInfos; - uint32_t commandBufferInfoCount; - const VkCommandBufferSubmitInfoKHR* pCommandBufferInfos; - uint32_t signalSemaphoreInfoCount; - const VkSemaphoreSubmitInfoKHR* pSignalSemaphoreInfos; -} VkSubmitInfo2KHR; +typedef VkSemaphoreSubmitInfo VkSemaphoreSubmitInfoKHR; -typedef struct VkPhysicalDeviceSynchronization2FeaturesKHR { - VkStructureType sType; - void* pNext; - VkBool32 synchronization2; -} VkPhysicalDeviceSynchronization2FeaturesKHR; +typedef VkCommandBufferSubmitInfo VkCommandBufferSubmitInfoKHR; + +typedef VkPhysicalDeviceSynchronization2Features VkPhysicalDeviceSynchronization2FeaturesKHR; typedef struct VkQueueFamilyCheckpointProperties2NV { - VkStructureType sType; - void* pNext; - VkPipelineStageFlags2KHR checkpointExecutionStageMask; + VkStructureType sType; + void* pNext; + VkPipelineStageFlags2 checkpointExecutionStageMask; } VkQueueFamilyCheckpointProperties2NV; typedef struct VkCheckpointData2NV { - VkStructureType sType; - void* pNext; - VkPipelineStageFlags2KHR stage; - void* pCheckpointMarker; + VkStructureType sType; + void* pNext; + VkPipelineStageFlags2 stage; + void* pCheckpointMarker; } VkCheckpointData2NV; -typedef void (VKAPI_PTR *PFN_vkCmdSetEvent2KHR)(VkCommandBuffer commandBuffer, VkEvent event, const VkDependencyInfoKHR* pDependencyInfo); -typedef void (VKAPI_PTR *PFN_vkCmdResetEvent2KHR)(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags2KHR stageMask); -typedef void (VKAPI_PTR *PFN_vkCmdWaitEvents2KHR)(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent* pEvents, const VkDependencyInfoKHR* pDependencyInfos); -typedef void (VKAPI_PTR *PFN_vkCmdPipelineBarrier2KHR)(VkCommandBuffer commandBuffer, const VkDependencyInfoKHR* pDependencyInfo); -typedef void (VKAPI_PTR *PFN_vkCmdWriteTimestamp2KHR)(VkCommandBuffer commandBuffer, VkPipelineStageFlags2KHR stage, VkQueryPool queryPool, uint32_t query); -typedef VkResult (VKAPI_PTR *PFN_vkQueueSubmit2KHR)(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2KHR* pSubmits, VkFence fence); -typedef void (VKAPI_PTR *PFN_vkCmdWriteBufferMarker2AMD)(VkCommandBuffer commandBuffer, VkPipelineStageFlags2KHR stage, VkBuffer dstBuffer, VkDeviceSize dstOffset, uint32_t marker); +typedef void (VKAPI_PTR *PFN_vkCmdSetEvent2KHR)(VkCommandBuffer commandBuffer, VkEvent event, const VkDependencyInfo* pDependencyInfo); +typedef void (VKAPI_PTR *PFN_vkCmdResetEvent2KHR)(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags2 stageMask); +typedef void (VKAPI_PTR *PFN_vkCmdWaitEvents2KHR)(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent* pEvents, const VkDependencyInfo* pDependencyInfos); +typedef void (VKAPI_PTR *PFN_vkCmdPipelineBarrier2KHR)(VkCommandBuffer commandBuffer, const VkDependencyInfo* pDependencyInfo); +typedef void (VKAPI_PTR *PFN_vkCmdWriteTimestamp2KHR)(VkCommandBuffer commandBuffer, VkPipelineStageFlags2 stage, VkQueryPool queryPool, uint32_t query); +typedef VkResult (VKAPI_PTR *PFN_vkQueueSubmit2KHR)(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2* pSubmits, VkFence fence); +typedef void (VKAPI_PTR *PFN_vkCmdWriteBufferMarker2AMD)(VkCommandBuffer commandBuffer, VkPipelineStageFlags2 stage, VkBuffer dstBuffer, VkDeviceSize dstOffset, uint32_t marker); typedef void (VKAPI_PTR *PFN_vkGetQueueCheckpointData2NV)(VkQueue queue, uint32_t* pCheckpointDataCount, VkCheckpointData2NV* pCheckpointData); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR void VKAPI_CALL vkCmdSetEvent2KHR( VkCommandBuffer commandBuffer, VkEvent event, - const VkDependencyInfoKHR* pDependencyInfo); + const VkDependencyInfo* pDependencyInfo); VKAPI_ATTR void VKAPI_CALL vkCmdResetEvent2KHR( VkCommandBuffer commandBuffer, VkEvent event, - VkPipelineStageFlags2KHR stageMask); + VkPipelineStageFlags2 stageMask); VKAPI_ATTR void VKAPI_CALL vkCmdWaitEvents2KHR( VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent* pEvents, - const VkDependencyInfoKHR* pDependencyInfos); + const VkDependencyInfo* pDependencyInfos); VKAPI_ATTR void VKAPI_CALL vkCmdPipelineBarrier2KHR( VkCommandBuffer commandBuffer, - const VkDependencyInfoKHR* pDependencyInfo); + const VkDependencyInfo* pDependencyInfo); VKAPI_ATTR void VKAPI_CALL vkCmdWriteTimestamp2KHR( VkCommandBuffer commandBuffer, - VkPipelineStageFlags2KHR stage, + VkPipelineStageFlags2 stage, VkQueryPool queryPool, uint32_t query); VKAPI_ATTR VkResult VKAPI_CALL vkQueueSubmit2KHR( VkQueue queue, uint32_t submitCount, - const VkSubmitInfo2KHR* pSubmits, + const VkSubmitInfo2* pSubmits, VkFence fence); VKAPI_ATTR void VKAPI_CALL vkCmdWriteBufferMarker2AMD( VkCommandBuffer commandBuffer, - VkPipelineStageFlags2KHR stage, + VkPipelineStageFlags2 stage, VkBuffer dstBuffer, VkDeviceSize dstOffset, uint32_t marker); @@ -8229,11 +9396,7 @@ typedef struct VkPhysicalDeviceShaderSubgroupUniformControlFlowFeaturesKHR { #define VK_KHR_zero_initialize_workgroup_memory 1 #define VK_KHR_ZERO_INITIALIZE_WORKGROUP_MEMORY_SPEC_VERSION 1 #define VK_KHR_ZERO_INITIALIZE_WORKGROUP_MEMORY_EXTENSION_NAME "VK_KHR_zero_initialize_workgroup_memory" -typedef struct VkPhysicalDeviceZeroInitializeWorkgroupMemoryFeaturesKHR { - VkStructureType sType; - void* pNext; - VkBool32 shaderZeroInitializeWorkgroupMemory; -} VkPhysicalDeviceZeroInitializeWorkgroupMemoryFeaturesKHR; +typedef VkPhysicalDeviceZeroInitializeWorkgroupMemoryFeatures VkPhysicalDeviceZeroInitializeWorkgroupMemoryFeaturesKHR; @@ -8254,148 +9417,109 @@ typedef struct VkPhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR { #define VK_KHR_copy_commands2 1 #define VK_KHR_COPY_COMMANDS_2_SPEC_VERSION 1 #define VK_KHR_COPY_COMMANDS_2_EXTENSION_NAME "VK_KHR_copy_commands2" -typedef struct VkBufferCopy2KHR { - VkStructureType sType; - const void* pNext; - VkDeviceSize srcOffset; - VkDeviceSize dstOffset; - VkDeviceSize size; -} VkBufferCopy2KHR; +typedef VkCopyBufferInfo2 VkCopyBufferInfo2KHR; -typedef struct VkCopyBufferInfo2KHR { - VkStructureType sType; - const void* pNext; - VkBuffer srcBuffer; - VkBuffer dstBuffer; - uint32_t regionCount; - const VkBufferCopy2KHR* pRegions; -} VkCopyBufferInfo2KHR; +typedef VkCopyImageInfo2 VkCopyImageInfo2KHR; -typedef struct VkImageCopy2KHR { - VkStructureType sType; - const void* pNext; - VkImageSubresourceLayers srcSubresource; - VkOffset3D srcOffset; - VkImageSubresourceLayers dstSubresource; - VkOffset3D dstOffset; - VkExtent3D extent; -} VkImageCopy2KHR; +typedef VkCopyBufferToImageInfo2 VkCopyBufferToImageInfo2KHR; -typedef struct VkCopyImageInfo2KHR { - VkStructureType sType; - const void* pNext; - VkImage srcImage; - VkImageLayout srcImageLayout; - VkImage dstImage; - VkImageLayout dstImageLayout; - uint32_t regionCount; - const VkImageCopy2KHR* pRegions; -} VkCopyImageInfo2KHR; +typedef VkCopyImageToBufferInfo2 VkCopyImageToBufferInfo2KHR; -typedef struct VkBufferImageCopy2KHR { - VkStructureType sType; - const void* pNext; - VkDeviceSize bufferOffset; - uint32_t bufferRowLength; - uint32_t bufferImageHeight; - VkImageSubresourceLayers imageSubresource; - VkOffset3D imageOffset; - VkExtent3D imageExtent; -} VkBufferImageCopy2KHR; +typedef VkBlitImageInfo2 VkBlitImageInfo2KHR; -typedef struct VkCopyBufferToImageInfo2KHR { - VkStructureType sType; - const void* pNext; - VkBuffer srcBuffer; - VkImage dstImage; - VkImageLayout dstImageLayout; - uint32_t regionCount; - const VkBufferImageCopy2KHR* pRegions; -} VkCopyBufferToImageInfo2KHR; +typedef VkResolveImageInfo2 VkResolveImageInfo2KHR; -typedef struct VkCopyImageToBufferInfo2KHR { - VkStructureType sType; - const void* pNext; - VkImage srcImage; - VkImageLayout srcImageLayout; - VkBuffer dstBuffer; - uint32_t regionCount; - const VkBufferImageCopy2KHR* pRegions; -} VkCopyImageToBufferInfo2KHR; +typedef VkBufferCopy2 VkBufferCopy2KHR; -typedef struct VkImageBlit2KHR { - VkStructureType sType; - const void* pNext; - VkImageSubresourceLayers srcSubresource; - VkOffset3D srcOffsets[2]; - VkImageSubresourceLayers dstSubresource; - VkOffset3D dstOffsets[2]; -} VkImageBlit2KHR; +typedef VkImageCopy2 VkImageCopy2KHR; -typedef struct VkBlitImageInfo2KHR { - VkStructureType sType; - const void* pNext; - VkImage srcImage; - VkImageLayout srcImageLayout; - VkImage dstImage; - VkImageLayout dstImageLayout; - uint32_t regionCount; - const VkImageBlit2KHR* pRegions; - VkFilter filter; -} VkBlitImageInfo2KHR; +typedef VkImageBlit2 VkImageBlit2KHR; -typedef struct VkImageResolve2KHR { - VkStructureType sType; - const void* pNext; - VkImageSubresourceLayers srcSubresource; - VkOffset3D srcOffset; - VkImageSubresourceLayers dstSubresource; - VkOffset3D dstOffset; - VkExtent3D extent; -} VkImageResolve2KHR; +typedef VkBufferImageCopy2 VkBufferImageCopy2KHR; -typedef struct VkResolveImageInfo2KHR { - VkStructureType sType; - const void* pNext; - VkImage srcImage; - VkImageLayout srcImageLayout; - VkImage dstImage; - VkImageLayout dstImageLayout; - uint32_t regionCount; - const VkImageResolve2KHR* pRegions; -} VkResolveImageInfo2KHR; +typedef VkImageResolve2 VkImageResolve2KHR; -typedef void (VKAPI_PTR *PFN_vkCmdCopyBuffer2KHR)(VkCommandBuffer commandBuffer, const VkCopyBufferInfo2KHR* pCopyBufferInfo); -typedef void (VKAPI_PTR *PFN_vkCmdCopyImage2KHR)(VkCommandBuffer commandBuffer, const VkCopyImageInfo2KHR* pCopyImageInfo); -typedef void (VKAPI_PTR *PFN_vkCmdCopyBufferToImage2KHR)(VkCommandBuffer commandBuffer, const VkCopyBufferToImageInfo2KHR* pCopyBufferToImageInfo); -typedef void (VKAPI_PTR *PFN_vkCmdCopyImageToBuffer2KHR)(VkCommandBuffer commandBuffer, const VkCopyImageToBufferInfo2KHR* pCopyImageToBufferInfo); -typedef void (VKAPI_PTR *PFN_vkCmdBlitImage2KHR)(VkCommandBuffer commandBuffer, const VkBlitImageInfo2KHR* pBlitImageInfo); -typedef void (VKAPI_PTR *PFN_vkCmdResolveImage2KHR)(VkCommandBuffer commandBuffer, const VkResolveImageInfo2KHR* pResolveImageInfo); +typedef void (VKAPI_PTR *PFN_vkCmdCopyBuffer2KHR)(VkCommandBuffer commandBuffer, const VkCopyBufferInfo2* pCopyBufferInfo); +typedef void (VKAPI_PTR *PFN_vkCmdCopyImage2KHR)(VkCommandBuffer commandBuffer, const VkCopyImageInfo2* pCopyImageInfo); +typedef void (VKAPI_PTR *PFN_vkCmdCopyBufferToImage2KHR)(VkCommandBuffer commandBuffer, const VkCopyBufferToImageInfo2* pCopyBufferToImageInfo); +typedef void (VKAPI_PTR *PFN_vkCmdCopyImageToBuffer2KHR)(VkCommandBuffer commandBuffer, const VkCopyImageToBufferInfo2* pCopyImageToBufferInfo); +typedef void (VKAPI_PTR *PFN_vkCmdBlitImage2KHR)(VkCommandBuffer commandBuffer, const VkBlitImageInfo2* pBlitImageInfo); +typedef void (VKAPI_PTR *PFN_vkCmdResolveImage2KHR)(VkCommandBuffer commandBuffer, const VkResolveImageInfo2* pResolveImageInfo); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR void VKAPI_CALL vkCmdCopyBuffer2KHR( VkCommandBuffer commandBuffer, - const VkCopyBufferInfo2KHR* pCopyBufferInfo); + const VkCopyBufferInfo2* pCopyBufferInfo); VKAPI_ATTR void VKAPI_CALL vkCmdCopyImage2KHR( VkCommandBuffer commandBuffer, - const VkCopyImageInfo2KHR* pCopyImageInfo); + const VkCopyImageInfo2* pCopyImageInfo); VKAPI_ATTR void VKAPI_CALL vkCmdCopyBufferToImage2KHR( VkCommandBuffer commandBuffer, - const VkCopyBufferToImageInfo2KHR* pCopyBufferToImageInfo); + const VkCopyBufferToImageInfo2* pCopyBufferToImageInfo); VKAPI_ATTR void VKAPI_CALL vkCmdCopyImageToBuffer2KHR( VkCommandBuffer commandBuffer, - const VkCopyImageToBufferInfo2KHR* pCopyImageToBufferInfo); + const VkCopyImageToBufferInfo2* pCopyImageToBufferInfo); VKAPI_ATTR void VKAPI_CALL vkCmdBlitImage2KHR( VkCommandBuffer commandBuffer, - const VkBlitImageInfo2KHR* pBlitImageInfo); + const VkBlitImageInfo2* pBlitImageInfo); VKAPI_ATTR void VKAPI_CALL vkCmdResolveImage2KHR( VkCommandBuffer commandBuffer, - const VkResolveImageInfo2KHR* pResolveImageInfo); + const VkResolveImageInfo2* pResolveImageInfo); +#endif + + +#define VK_KHR_format_feature_flags2 1 +#define VK_KHR_FORMAT_FEATURE_FLAGS_2_SPEC_VERSION 1 +#define VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME "VK_KHR_format_feature_flags2" +typedef VkFormatFeatureFlags2 VkFormatFeatureFlags2KHR; + +typedef VkFormatFeatureFlagBits2 VkFormatFeatureFlagBits2KHR; + +typedef VkFormatProperties3 VkFormatProperties3KHR; + + + +#define VK_KHR_portability_enumeration 1 +#define VK_KHR_PORTABILITY_ENUMERATION_SPEC_VERSION 1 +#define VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME "VK_KHR_portability_enumeration" + + +#define VK_KHR_maintenance4 1 +#define VK_KHR_MAINTENANCE_4_SPEC_VERSION 2 +#define VK_KHR_MAINTENANCE_4_EXTENSION_NAME "VK_KHR_maintenance4" +typedef VkPhysicalDeviceMaintenance4Features VkPhysicalDeviceMaintenance4FeaturesKHR; + +typedef VkPhysicalDeviceMaintenance4Properties VkPhysicalDeviceMaintenance4PropertiesKHR; + +typedef VkDeviceBufferMemoryRequirements VkDeviceBufferMemoryRequirementsKHR; + +typedef VkDeviceImageMemoryRequirements VkDeviceImageMemoryRequirementsKHR; + +typedef void (VKAPI_PTR *PFN_vkGetDeviceBufferMemoryRequirementsKHR)(VkDevice device, const VkDeviceBufferMemoryRequirements* pInfo, VkMemoryRequirements2* pMemoryRequirements); +typedef void (VKAPI_PTR *PFN_vkGetDeviceImageMemoryRequirementsKHR)(VkDevice device, const VkDeviceImageMemoryRequirements* pInfo, VkMemoryRequirements2* pMemoryRequirements); +typedef void (VKAPI_PTR *PFN_vkGetDeviceImageSparseMemoryRequirementsKHR)(VkDevice device, const VkDeviceImageMemoryRequirements* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkGetDeviceBufferMemoryRequirementsKHR( + VkDevice device, + const VkDeviceBufferMemoryRequirements* pInfo, + VkMemoryRequirements2* pMemoryRequirements); + +VKAPI_ATTR void VKAPI_CALL vkGetDeviceImageMemoryRequirementsKHR( + VkDevice device, + const VkDeviceImageMemoryRequirements* pInfo, + VkMemoryRequirements2* pMemoryRequirements); + +VKAPI_ATTR void VKAPI_CALL vkGetDeviceImageSparseMemoryRequirementsKHR( + VkDevice device, + const VkDeviceImageMemoryRequirements* pInfo, + uint32_t* pSparseMemoryRequirementCount, + VkSparseImageMemoryRequirements2* pSparseMemoryRequirements); #endif @@ -8443,6 +9567,7 @@ typedef enum VkDebugReportObjectTypeEXT { VK_DEBUG_REPORT_OBJECT_TYPE_CU_FUNCTION_NVX_EXT = 1000029001, VK_DEBUG_REPORT_OBJECT_TYPE_ACCELERATION_STRUCTURE_KHR_EXT = 1000150000, VK_DEBUG_REPORT_OBJECT_TYPE_ACCELERATION_STRUCTURE_NV_EXT = 1000165000, + VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_COLLECTION_FUCHSIA_EXT = 1000366000, VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_KHR_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_EXT, @@ -9028,11 +10153,7 @@ typedef struct VkValidationFlagsEXT { #define VK_EXT_texture_compression_astc_hdr 1 #define VK_EXT_TEXTURE_COMPRESSION_ASTC_HDR_SPEC_VERSION 1 #define VK_EXT_TEXTURE_COMPRESSION_ASTC_HDR_EXTENSION_NAME "VK_EXT_texture_compression_astc_hdr" -typedef struct VkPhysicalDeviceTextureCompressionASTCHDRFeaturesEXT { - VkStructureType sType; - void* pNext; - VkBool32 textureCompressionASTC_HDR; -} VkPhysicalDeviceTextureCompressionASTCHDRFeaturesEXT; +typedef VkPhysicalDeviceTextureCompressionASTCHDRFeatures VkPhysicalDeviceTextureCompressionASTCHDRFeaturesEXT; @@ -9302,8 +10423,10 @@ VKAPI_ATTR VkResult VKAPI_CALL vkGetPastPresentationTimingGOOGLE( #define VK_NV_viewport_array2 1 -#define VK_NV_VIEWPORT_ARRAY2_SPEC_VERSION 1 -#define VK_NV_VIEWPORT_ARRAY2_EXTENSION_NAME "VK_NV_viewport_array2" +#define VK_NV_VIEWPORT_ARRAY_2_SPEC_VERSION 1 +#define VK_NV_VIEWPORT_ARRAY_2_EXTENSION_NAME "VK_NV_viewport_array2" +#define VK_NV_VIEWPORT_ARRAY2_SPEC_VERSION VK_NV_VIEWPORT_ARRAY_2_SPEC_VERSION +#define VK_NV_VIEWPORT_ARRAY2_EXTENSION_NAME VK_NV_VIEWPORT_ARRAY_2_EXTENSION_NAME #define VK_NVX_multiview_per_view_attributes 1 @@ -9658,35 +10781,13 @@ typedef VkPhysicalDeviceSamplerFilterMinmaxProperties VkPhysicalDeviceSamplerFil #define VK_EXT_inline_uniform_block 1 #define VK_EXT_INLINE_UNIFORM_BLOCK_SPEC_VERSION 1 #define VK_EXT_INLINE_UNIFORM_BLOCK_EXTENSION_NAME "VK_EXT_inline_uniform_block" -typedef struct VkPhysicalDeviceInlineUniformBlockFeaturesEXT { - VkStructureType sType; - void* pNext; - VkBool32 inlineUniformBlock; - VkBool32 descriptorBindingInlineUniformBlockUpdateAfterBind; -} VkPhysicalDeviceInlineUniformBlockFeaturesEXT; +typedef VkPhysicalDeviceInlineUniformBlockFeatures VkPhysicalDeviceInlineUniformBlockFeaturesEXT; -typedef struct VkPhysicalDeviceInlineUniformBlockPropertiesEXT { - VkStructureType sType; - void* pNext; - uint32_t maxInlineUniformBlockSize; - uint32_t maxPerStageDescriptorInlineUniformBlocks; - uint32_t maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks; - uint32_t maxDescriptorSetInlineUniformBlocks; - uint32_t maxDescriptorSetUpdateAfterBindInlineUniformBlocks; -} VkPhysicalDeviceInlineUniformBlockPropertiesEXT; +typedef VkPhysicalDeviceInlineUniformBlockProperties VkPhysicalDeviceInlineUniformBlockPropertiesEXT; -typedef struct VkWriteDescriptorSetInlineUniformBlockEXT { - VkStructureType sType; - const void* pNext; - uint32_t dataSize; - const void* pData; -} VkWriteDescriptorSetInlineUniformBlockEXT; +typedef VkWriteDescriptorSetInlineUniformBlock VkWriteDescriptorSetInlineUniformBlockEXT; -typedef struct VkDescriptorPoolInlineUniformBlockCreateInfoEXT { - VkStructureType sType; - const void* pNext; - uint32_t maxInlineUniformBlockBindings; -} VkDescriptorPoolInlineUniformBlockCreateInfoEXT; +typedef VkDescriptorPoolInlineUniformBlockCreateInfo VkDescriptorPoolInlineUniformBlockCreateInfoEXT; @@ -9873,7 +10974,7 @@ typedef struct VkPhysicalDeviceShaderSMBuiltinsFeaturesNV { #define VK_EXT_image_drm_format_modifier 1 -#define VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_SPEC_VERSION 1 +#define VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_SPEC_VERSION 2 #define VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME "VK_EXT_image_drm_format_modifier" typedef struct VkDrmFormatModifierPropertiesEXT { uint64_t drmFormatModifier; @@ -9918,6 +11019,19 @@ typedef struct VkImageDrmFormatModifierPropertiesEXT { uint64_t drmFormatModifier; } VkImageDrmFormatModifierPropertiesEXT; +typedef struct VkDrmFormatModifierProperties2EXT { + uint64_t drmFormatModifier; + uint32_t drmFormatModifierPlaneCount; + VkFormatFeatureFlags2 drmFormatModifierTilingFeatures; +} VkDrmFormatModifierProperties2EXT; + +typedef struct VkDrmFormatModifierPropertiesList2EXT { + VkStructureType sType; + void* pNext; + uint32_t drmFormatModifierCount; + VkDrmFormatModifierProperties2EXT* pDrmFormatModifierProperties; +} VkDrmFormatModifierPropertiesList2EXT; + typedef VkResult (VKAPI_PTR *PFN_vkGetImageDrmFormatModifierPropertiesEXT)(VkDevice device, VkImage image, VkImageDrmFormatModifierPropertiesEXT* pProperties); #ifndef VK_NO_PROTOTYPES @@ -10519,19 +11633,9 @@ typedef struct VkFilterCubicImageViewImageFormatPropertiesEXT { #define VK_EXT_global_priority 1 #define VK_EXT_GLOBAL_PRIORITY_SPEC_VERSION 2 #define VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME "VK_EXT_global_priority" +typedef VkQueueGlobalPriorityKHR VkQueueGlobalPriorityEXT; -typedef enum VkQueueGlobalPriorityEXT { - VK_QUEUE_GLOBAL_PRIORITY_LOW_EXT = 128, - VK_QUEUE_GLOBAL_PRIORITY_MEDIUM_EXT = 256, - VK_QUEUE_GLOBAL_PRIORITY_HIGH_EXT = 512, - VK_QUEUE_GLOBAL_PRIORITY_REALTIME_EXT = 1024, - VK_QUEUE_GLOBAL_PRIORITY_MAX_ENUM_EXT = 0x7FFFFFFF -} VkQueueGlobalPriorityEXT; -typedef struct VkDeviceQueueGlobalPriorityCreateInfoEXT { - VkStructureType sType; - const void* pNext; - VkQueueGlobalPriorityEXT globalPriority; -} VkDeviceQueueGlobalPriorityCreateInfoEXT; +typedef VkDeviceQueueGlobalPriorityCreateInfoKHR VkDeviceQueueGlobalPriorityCreateInfoEXT; @@ -10709,26 +11813,13 @@ typedef struct VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT { #define VK_EXT_pipeline_creation_feedback 1 #define VK_EXT_PIPELINE_CREATION_FEEDBACK_SPEC_VERSION 1 #define VK_EXT_PIPELINE_CREATION_FEEDBACK_EXTENSION_NAME "VK_EXT_pipeline_creation_feedback" +typedef VkPipelineCreationFeedbackFlagBits VkPipelineCreationFeedbackFlagBitsEXT; -typedef enum VkPipelineCreationFeedbackFlagBitsEXT { - VK_PIPELINE_CREATION_FEEDBACK_VALID_BIT_EXT = 0x00000001, - VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT_EXT = 0x00000002, - VK_PIPELINE_CREATION_FEEDBACK_BASE_PIPELINE_ACCELERATION_BIT_EXT = 0x00000004, - VK_PIPELINE_CREATION_FEEDBACK_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF -} VkPipelineCreationFeedbackFlagBitsEXT; -typedef VkFlags VkPipelineCreationFeedbackFlagsEXT; -typedef struct VkPipelineCreationFeedbackEXT { - VkPipelineCreationFeedbackFlagsEXT flags; - uint64_t duration; -} VkPipelineCreationFeedbackEXT; +typedef VkPipelineCreationFeedbackFlags VkPipelineCreationFeedbackFlagsEXT; -typedef struct VkPipelineCreationFeedbackCreateInfoEXT { - VkStructureType sType; - const void* pNext; - VkPipelineCreationFeedbackEXT* pPipelineCreationFeedback; - uint32_t pipelineStageCreationFeedbackCount; - VkPipelineCreationFeedbackEXT* pPipelineStageCreationFeedbacks; -} VkPipelineCreationFeedbackCreateInfoEXT; +typedef VkPipelineCreationFeedbackCreateInfo VkPipelineCreationFeedbackCreateInfoEXT; + +typedef VkPipelineCreationFeedback VkPipelineCreationFeedbackEXT; @@ -11079,7 +12170,7 @@ VKAPI_ATTR void VKAPI_CALL vkSetLocalDimmingAMD( #define VK_EXT_fragment_density_map 1 -#define VK_EXT_FRAGMENT_DENSITY_MAP_SPEC_VERSION 1 +#define VK_EXT_FRAGMENT_DENSITY_MAP_SPEC_VERSION 2 #define VK_EXT_FRAGMENT_DENSITY_MAP_EXTENSION_NAME "VK_EXT_fragment_density_map" typedef struct VkPhysicalDeviceFragmentDensityMapFeaturesEXT { VkStructureType sType; @@ -11113,8 +12204,10 @@ typedef VkPhysicalDeviceScalarBlockLayoutFeatures VkPhysicalDeviceScalarBlockLay #define VK_GOOGLE_hlsl_functionality1 1 -#define VK_GOOGLE_HLSL_FUNCTIONALITY1_SPEC_VERSION 1 -#define VK_GOOGLE_HLSL_FUNCTIONALITY1_EXTENSION_NAME "VK_GOOGLE_hlsl_functionality1" +#define VK_GOOGLE_HLSL_FUNCTIONALITY_1_SPEC_VERSION 1 +#define VK_GOOGLE_HLSL_FUNCTIONALITY_1_EXTENSION_NAME "VK_GOOGLE_hlsl_functionality1" +#define VK_GOOGLE_HLSL_FUNCTIONALITY1_SPEC_VERSION VK_GOOGLE_HLSL_FUNCTIONALITY_1_SPEC_VERSION +#define VK_GOOGLE_HLSL_FUNCTIONALITY1_EXTENSION_NAME VK_GOOGLE_HLSL_FUNCTIONALITY_1_EXTENSION_NAME #define VK_GOOGLE_decorate_string 1 @@ -11125,27 +12218,11 @@ typedef VkPhysicalDeviceScalarBlockLayoutFeatures VkPhysicalDeviceScalarBlockLay #define VK_EXT_subgroup_size_control 1 #define VK_EXT_SUBGROUP_SIZE_CONTROL_SPEC_VERSION 2 #define VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME "VK_EXT_subgroup_size_control" -typedef struct VkPhysicalDeviceSubgroupSizeControlFeaturesEXT { - VkStructureType sType; - void* pNext; - VkBool32 subgroupSizeControl; - VkBool32 computeFullSubgroups; -} VkPhysicalDeviceSubgroupSizeControlFeaturesEXT; +typedef VkPhysicalDeviceSubgroupSizeControlFeatures VkPhysicalDeviceSubgroupSizeControlFeaturesEXT; -typedef struct VkPhysicalDeviceSubgroupSizeControlPropertiesEXT { - VkStructureType sType; - void* pNext; - uint32_t minSubgroupSize; - uint32_t maxSubgroupSize; - uint32_t maxComputeWorkgroupSubgroups; - VkShaderStageFlags requiredSubgroupSizeStages; -} VkPhysicalDeviceSubgroupSizeControlPropertiesEXT; +typedef VkPhysicalDeviceSubgroupSizeControlProperties VkPhysicalDeviceSubgroupSizeControlPropertiesEXT; -typedef struct VkPipelineShaderStageRequiredSubgroupSizeCreateInfoEXT { - VkStructureType sType; - void* pNext; - uint32_t requiredSubgroupSize; -} VkPipelineShaderStageRequiredSubgroupSizeCreateInfoEXT; +typedef VkPipelineShaderStageRequiredSubgroupSizeCreateInfo VkPipelineShaderStageRequiredSubgroupSizeCreateInfoEXT; @@ -11262,35 +12339,19 @@ VKAPI_ATTR VkDeviceAddress VKAPI_CALL vkGetBufferDeviceAddressEXT( #define VK_EXT_tooling_info 1 #define VK_EXT_TOOLING_INFO_SPEC_VERSION 1 #define VK_EXT_TOOLING_INFO_EXTENSION_NAME "VK_EXT_tooling_info" +typedef VkToolPurposeFlagBits VkToolPurposeFlagBitsEXT; -typedef enum VkToolPurposeFlagBitsEXT { - VK_TOOL_PURPOSE_VALIDATION_BIT_EXT = 0x00000001, - VK_TOOL_PURPOSE_PROFILING_BIT_EXT = 0x00000002, - VK_TOOL_PURPOSE_TRACING_BIT_EXT = 0x00000004, - VK_TOOL_PURPOSE_ADDITIONAL_FEATURES_BIT_EXT = 0x00000008, - VK_TOOL_PURPOSE_MODIFYING_FEATURES_BIT_EXT = 0x00000010, - VK_TOOL_PURPOSE_DEBUG_REPORTING_BIT_EXT = 0x00000020, - VK_TOOL_PURPOSE_DEBUG_MARKERS_BIT_EXT = 0x00000040, - VK_TOOL_PURPOSE_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF -} VkToolPurposeFlagBitsEXT; -typedef VkFlags VkToolPurposeFlagsEXT; -typedef struct VkPhysicalDeviceToolPropertiesEXT { - VkStructureType sType; - void* pNext; - char name[VK_MAX_EXTENSION_NAME_SIZE]; - char version[VK_MAX_EXTENSION_NAME_SIZE]; - VkToolPurposeFlagsEXT purposes; - char description[VK_MAX_DESCRIPTION_SIZE]; - char layer[VK_MAX_EXTENSION_NAME_SIZE]; -} VkPhysicalDeviceToolPropertiesEXT; +typedef VkToolPurposeFlags VkToolPurposeFlagsEXT; -typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceToolPropertiesEXT)(VkPhysicalDevice physicalDevice, uint32_t* pToolCount, VkPhysicalDeviceToolPropertiesEXT* pToolProperties); +typedef VkPhysicalDeviceToolProperties VkPhysicalDeviceToolPropertiesEXT; + +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceToolPropertiesEXT)(VkPhysicalDevice physicalDevice, uint32_t* pToolCount, VkPhysicalDeviceToolProperties* pToolProperties); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceToolPropertiesEXT( VkPhysicalDevice physicalDevice, uint32_t* pToolCount, - VkPhysicalDeviceToolPropertiesEXT* pToolProperties); + VkPhysicalDeviceToolProperties* pToolProperties); #endif @@ -11721,11 +12782,7 @@ typedef struct VkPhysicalDeviceShaderAtomicFloat2FeaturesEXT { #define VK_EXT_shader_demote_to_helper_invocation 1 #define VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_SPEC_VERSION 1 #define VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME "VK_EXT_shader_demote_to_helper_invocation" -typedef struct VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT { - VkStructureType sType; - void* pNext; - VkBool32 shaderDemoteToHelperInvocation; -} VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT; +typedef VkPhysicalDeviceShaderDemoteToHelperInvocationFeatures VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT; @@ -11947,14 +13004,7 @@ typedef struct VkPhysicalDeviceTexelBufferAlignmentFeaturesEXT { VkBool32 texelBufferAlignment; } VkPhysicalDeviceTexelBufferAlignmentFeaturesEXT; -typedef struct VkPhysicalDeviceTexelBufferAlignmentPropertiesEXT { - VkStructureType sType; - void* pNext; - VkDeviceSize storageTexelBufferOffsetAlignmentBytes; - VkBool32 storageTexelBufferOffsetSingleTexelAlignment; - VkDeviceSize uniformTexelBufferOffsetAlignmentBytes; - VkBool32 uniformTexelBufferOffsetSingleTexelAlignment; -} VkPhysicalDeviceTexelBufferAlignmentPropertiesEXT; +typedef VkPhysicalDeviceTexelBufferAlignmentProperties VkPhysicalDeviceTexelBufferAlignmentPropertiesEXT; @@ -12092,61 +13142,47 @@ typedef struct VkPhysicalDeviceCustomBorderColorFeaturesEXT { #define VK_EXT_private_data 1 -VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkPrivateDataSlotEXT) +typedef VkPrivateDataSlot VkPrivateDataSlotEXT; + #define VK_EXT_PRIVATE_DATA_SPEC_VERSION 1 #define VK_EXT_PRIVATE_DATA_EXTENSION_NAME "VK_EXT_private_data" +typedef VkPrivateDataSlotCreateFlags VkPrivateDataSlotCreateFlagsEXT; -typedef enum VkPrivateDataSlotCreateFlagBitsEXT { - VK_PRIVATE_DATA_SLOT_CREATE_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF -} VkPrivateDataSlotCreateFlagBitsEXT; -typedef VkFlags VkPrivateDataSlotCreateFlagsEXT; -typedef struct VkPhysicalDevicePrivateDataFeaturesEXT { - VkStructureType sType; - void* pNext; - VkBool32 privateData; -} VkPhysicalDevicePrivateDataFeaturesEXT; +typedef VkPhysicalDevicePrivateDataFeatures VkPhysicalDevicePrivateDataFeaturesEXT; -typedef struct VkDevicePrivateDataCreateInfoEXT { - VkStructureType sType; - const void* pNext; - uint32_t privateDataSlotRequestCount; -} VkDevicePrivateDataCreateInfoEXT; +typedef VkDevicePrivateDataCreateInfo VkDevicePrivateDataCreateInfoEXT; -typedef struct VkPrivateDataSlotCreateInfoEXT { - VkStructureType sType; - const void* pNext; - VkPrivateDataSlotCreateFlagsEXT flags; -} VkPrivateDataSlotCreateInfoEXT; +typedef VkPrivateDataSlotCreateInfo VkPrivateDataSlotCreateInfoEXT; -typedef VkResult (VKAPI_PTR *PFN_vkCreatePrivateDataSlotEXT)(VkDevice device, const VkPrivateDataSlotCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkPrivateDataSlotEXT* pPrivateDataSlot); -typedef void (VKAPI_PTR *PFN_vkDestroyPrivateDataSlotEXT)(VkDevice device, VkPrivateDataSlotEXT privateDataSlot, const VkAllocationCallbacks* pAllocator); -typedef VkResult (VKAPI_PTR *PFN_vkSetPrivateDataEXT)(VkDevice device, VkObjectType objectType, uint64_t objectHandle, VkPrivateDataSlotEXT privateDataSlot, uint64_t data); -typedef void (VKAPI_PTR *PFN_vkGetPrivateDataEXT)(VkDevice device, VkObjectType objectType, uint64_t objectHandle, VkPrivateDataSlotEXT privateDataSlot, uint64_t* pData); +typedef VkResult (VKAPI_PTR *PFN_vkCreatePrivateDataSlotEXT)(VkDevice device, const VkPrivateDataSlotCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkPrivateDataSlot* pPrivateDataSlot); +typedef void (VKAPI_PTR *PFN_vkDestroyPrivateDataSlotEXT)(VkDevice device, VkPrivateDataSlot privateDataSlot, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkSetPrivateDataEXT)(VkDevice device, VkObjectType objectType, uint64_t objectHandle, VkPrivateDataSlot privateDataSlot, uint64_t data); +typedef void (VKAPI_PTR *PFN_vkGetPrivateDataEXT)(VkDevice device, VkObjectType objectType, uint64_t objectHandle, VkPrivateDataSlot privateDataSlot, uint64_t* pData); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR VkResult VKAPI_CALL vkCreatePrivateDataSlotEXT( VkDevice device, - const VkPrivateDataSlotCreateInfoEXT* pCreateInfo, + const VkPrivateDataSlotCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, - VkPrivateDataSlotEXT* pPrivateDataSlot); + VkPrivateDataSlot* pPrivateDataSlot); VKAPI_ATTR void VKAPI_CALL vkDestroyPrivateDataSlotEXT( VkDevice device, - VkPrivateDataSlotEXT privateDataSlot, + VkPrivateDataSlot privateDataSlot, const VkAllocationCallbacks* pAllocator); VKAPI_ATTR VkResult VKAPI_CALL vkSetPrivateDataEXT( VkDevice device, VkObjectType objectType, uint64_t objectHandle, - VkPrivateDataSlotEXT privateDataSlot, + VkPrivateDataSlot privateDataSlot, uint64_t data); VKAPI_ATTR void VKAPI_CALL vkGetPrivateDataEXT( VkDevice device, VkObjectType objectType, uint64_t objectHandle, - VkPrivateDataSlotEXT privateDataSlot, + VkPrivateDataSlot privateDataSlot, uint64_t* pData); #endif @@ -12154,11 +13190,7 @@ VKAPI_ATTR void VKAPI_CALL vkGetPrivateDataEXT( #define VK_EXT_pipeline_creation_cache_control 1 #define VK_EXT_PIPELINE_CREATION_CACHE_CONTROL_SPEC_VERSION 3 #define VK_EXT_PIPELINE_CREATION_CACHE_CONTROL_EXTENSION_NAME "VK_EXT_pipeline_creation_cache_control" -typedef struct VkPhysicalDevicePipelineCreationCacheControlFeaturesEXT { - VkStructureType sType; - void* pNext; - VkBool32 pipelineCreationCacheControl; -} VkPhysicalDevicePipelineCreationCacheControlFeaturesEXT; +typedef VkPhysicalDevicePipelineCreationCacheControlFeatures VkPhysicalDevicePipelineCreationCacheControlFeaturesEXT; @@ -12192,6 +13224,39 @@ typedef struct VkDeviceDiagnosticsConfigCreateInfoNV { #define VK_QCOM_RENDER_PASS_STORE_OPS_EXTENSION_NAME "VK_QCOM_render_pass_store_ops" +#define VK_EXT_graphics_pipeline_library 1 +#define VK_EXT_GRAPHICS_PIPELINE_LIBRARY_SPEC_VERSION 1 +#define VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME "VK_EXT_graphics_pipeline_library" + +typedef enum VkGraphicsPipelineLibraryFlagBitsEXT { + VK_GRAPHICS_PIPELINE_LIBRARY_VERTEX_INPUT_INTERFACE_BIT_EXT = 0x00000001, + VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT = 0x00000002, + VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT = 0x00000004, + VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT = 0x00000008, + VK_GRAPHICS_PIPELINE_LIBRARY_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF +} VkGraphicsPipelineLibraryFlagBitsEXT; +typedef VkFlags VkGraphicsPipelineLibraryFlagsEXT; +typedef struct VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 graphicsPipelineLibrary; +} VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT; + +typedef struct VkPhysicalDeviceGraphicsPipelineLibraryPropertiesEXT { + VkStructureType sType; + void* pNext; + VkBool32 graphicsPipelineLibraryFastLinking; + VkBool32 graphicsPipelineLibraryIndependentInterpolationDecoration; +} VkPhysicalDeviceGraphicsPipelineLibraryPropertiesEXT; + +typedef struct VkGraphicsPipelineLibraryCreateInfoEXT { + VkStructureType sType; + void* pNext; + VkGraphicsPipelineLibraryFlagsEXT flags; +} VkGraphicsPipelineLibraryCreateInfoEXT; + + + #define VK_NV_fragment_shading_rate_enums 1 #define VK_NV_FRAGMENT_SHADING_RATE_ENUMS_SPEC_VERSION 1 #define VK_NV_FRAGMENT_SHADING_RATE_ENUMS_EXTENSION_NAME "VK_NV_fragment_shading_rate_enums" @@ -12384,11 +13449,7 @@ typedef struct VkCopyCommandTransformInfoQCOM { #define VK_EXT_image_robustness 1 #define VK_EXT_IMAGE_ROBUSTNESS_SPEC_VERSION 1 #define VK_EXT_IMAGE_ROBUSTNESS_EXTENSION_NAME "VK_EXT_image_robustness" -typedef struct VkPhysicalDeviceImageRobustnessFeaturesEXT { - VkStructureType sType; - void* pNext; - VkBool32 robustImageAccess; -} VkPhysicalDeviceImageRobustnessFeaturesEXT; +typedef VkPhysicalDeviceImageRobustnessFeatures VkPhysicalDeviceImageRobustnessFeaturesEXT; @@ -12404,6 +13465,30 @@ typedef struct VkPhysicalDevice4444FormatsFeaturesEXT { +#define VK_ARM_rasterization_order_attachment_access 1 +#define VK_ARM_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_SPEC_VERSION 1 +#define VK_ARM_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_EXTENSION_NAME "VK_ARM_rasterization_order_attachment_access" +typedef struct VkPhysicalDeviceRasterizationOrderAttachmentAccessFeaturesARM { + VkStructureType sType; + void* pNext; + VkBool32 rasterizationOrderColorAttachmentAccess; + VkBool32 rasterizationOrderDepthAttachmentAccess; + VkBool32 rasterizationOrderStencilAttachmentAccess; +} VkPhysicalDeviceRasterizationOrderAttachmentAccessFeaturesARM; + + + +#define VK_EXT_rgba10x6_formats 1 +#define VK_EXT_RGBA10X6_FORMATS_SPEC_VERSION 1 +#define VK_EXT_RGBA10X6_FORMATS_EXTENSION_NAME "VK_EXT_rgba10x6_formats" +typedef struct VkPhysicalDeviceRGBA10X6FormatsFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 formatRgba10x6WithoutYCbCrSampler; +} VkPhysicalDeviceRGBA10X6FormatsFeaturesEXT; + + + #define VK_NV_acquire_winrt_display 1 #define VK_NV_ACQUIRE_WINRT_DISPLAY_SPEC_VERSION 1 #define VK_NV_ACQUIRE_WINRT_DISPLAY_EXTENSION_NAME "VK_NV_acquire_winrt_display" @@ -12500,6 +13585,23 @@ typedef struct VkPhysicalDeviceDrmPropertiesEXT { +#define VK_EXT_depth_clip_control 1 +#define VK_EXT_DEPTH_CLIP_CONTROL_SPEC_VERSION 1 +#define VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME "VK_EXT_depth_clip_control" +typedef struct VkPhysicalDeviceDepthClipControlFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 depthClipControl; +} VkPhysicalDeviceDepthClipControlFeaturesEXT; + +typedef struct VkPipelineViewportDepthClipControlCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkBool32 negativeOneToOne; +} VkPipelineViewportDepthClipControlCreateInfoEXT; + + + #define VK_EXT_primitive_topology_list_restart 1 #define VK_EXT_PRIMITIVE_TOPOLOGY_LIST_RESTART_SPEC_VERSION 1 #define VK_EXT_PRIMITIVE_TOPOLOGY_LIST_RESTART_EXTENSION_NAME "VK_EXT_primitive_topology_list_restart" @@ -12660,22 +13762,43 @@ VKAPI_ATTR void VKAPI_CALL vkCmdSetColorWrite #endif -#define VK_EXT_global_priority_query 1 -#define VK_MAX_GLOBAL_PRIORITY_SIZE_EXT 16U -#define VK_EXT_GLOBAL_PRIORITY_QUERY_SPEC_VERSION 1 -#define VK_EXT_GLOBAL_PRIORITY_QUERY_EXTENSION_NAME "VK_EXT_global_priority_query" -typedef struct VkPhysicalDeviceGlobalPriorityQueryFeaturesEXT { +#define VK_EXT_primitives_generated_query 1 +#define VK_EXT_PRIMITIVES_GENERATED_QUERY_SPEC_VERSION 1 +#define VK_EXT_PRIMITIVES_GENERATED_QUERY_EXTENSION_NAME "VK_EXT_primitives_generated_query" +typedef struct VkPhysicalDevicePrimitivesGeneratedQueryFeaturesEXT { VkStructureType sType; void* pNext; - VkBool32 globalPriorityQuery; -} VkPhysicalDeviceGlobalPriorityQueryFeaturesEXT; + VkBool32 primitivesGeneratedQuery; + VkBool32 primitivesGeneratedQueryWithRasterizerDiscard; + VkBool32 primitivesGeneratedQueryWithNonZeroStreams; +} VkPhysicalDevicePrimitivesGeneratedQueryFeaturesEXT; -typedef struct VkQueueFamilyGlobalPriorityPropertiesEXT { - VkStructureType sType; - void* pNext; - uint32_t priorityCount; - VkQueueGlobalPriorityEXT priorities[VK_MAX_GLOBAL_PRIORITY_SIZE_EXT]; -} VkQueueFamilyGlobalPriorityPropertiesEXT; + + +#define VK_EXT_global_priority_query 1 +#define VK_EXT_GLOBAL_PRIORITY_QUERY_SPEC_VERSION 1 +#define VK_EXT_GLOBAL_PRIORITY_QUERY_EXTENSION_NAME "VK_EXT_global_priority_query" +#define VK_MAX_GLOBAL_PRIORITY_SIZE_EXT VK_MAX_GLOBAL_PRIORITY_SIZE_KHR +typedef VkPhysicalDeviceGlobalPriorityQueryFeaturesKHR VkPhysicalDeviceGlobalPriorityQueryFeaturesEXT; + +typedef VkQueueFamilyGlobalPriorityPropertiesKHR VkQueueFamilyGlobalPriorityPropertiesEXT; + + + +#define VK_EXT_image_view_min_lod 1 +#define VK_EXT_IMAGE_VIEW_MIN_LOD_SPEC_VERSION 1 +#define VK_EXT_IMAGE_VIEW_MIN_LOD_EXTENSION_NAME "VK_EXT_image_view_min_lod" +typedef struct VkPhysicalDeviceImageViewMinLodFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 minLod; +} VkPhysicalDeviceImageViewMinLodFeaturesEXT; + +typedef struct VkImageViewMinLodCreateInfoEXT { + VkStructureType sType; + const void* pNext; + float minLod; +} VkImageViewMinLodCreateInfoEXT; @@ -12728,11 +13851,42 @@ VKAPI_ATTR void VKAPI_CALL vkCmdDrawMultiIndexedEXT( #endif +#define VK_EXT_image_2d_view_of_3d 1 +#define VK_EXT_IMAGE_2D_VIEW_OF_3D_SPEC_VERSION 1 +#define VK_EXT_IMAGE_2D_VIEW_OF_3D_EXTENSION_NAME "VK_EXT_image_2d_view_of_3d" +typedef struct VkPhysicalDeviceImage2DViewOf3DFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 image2DViewOf3D; + VkBool32 sampler2DViewOf3D; +} VkPhysicalDeviceImage2DViewOf3DFeaturesEXT; + + + #define VK_EXT_load_store_op_none 1 #define VK_EXT_LOAD_STORE_OP_NONE_SPEC_VERSION 1 #define VK_EXT_LOAD_STORE_OP_NONE_EXTENSION_NAME "VK_EXT_load_store_op_none" +#define VK_EXT_border_color_swizzle 1 +#define VK_EXT_BORDER_COLOR_SWIZZLE_SPEC_VERSION 1 +#define VK_EXT_BORDER_COLOR_SWIZZLE_EXTENSION_NAME "VK_EXT_border_color_swizzle" +typedef struct VkPhysicalDeviceBorderColorSwizzleFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 borderColorSwizzle; + VkBool32 borderColorSwizzleFromImage; +} VkPhysicalDeviceBorderColorSwizzleFeaturesEXT; + +typedef struct VkSamplerBorderColorComponentMappingCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkComponentMapping components; + VkBool32 srgb; +} VkSamplerBorderColorComponentMappingCreateInfoEXT; + + + #define VK_EXT_pageable_device_local_memory 1 #define VK_EXT_PAGEABLE_DEVICE_LOCAL_MEMORY_SPEC_VERSION 1 #define VK_EXT_PAGEABLE_DEVICE_LOCAL_MEMORY_EXTENSION_NAME "VK_EXT_pageable_device_local_memory" @@ -12752,9 +13906,88 @@ VKAPI_ATTR void VKAPI_CALL vkSetDeviceMemoryPriorityEXT( #endif +#define VK_VALVE_descriptor_set_host_mapping 1 +#define VK_VALVE_DESCRIPTOR_SET_HOST_MAPPING_SPEC_VERSION 1 +#define VK_VALVE_DESCRIPTOR_SET_HOST_MAPPING_EXTENSION_NAME "VK_VALVE_descriptor_set_host_mapping" +typedef struct VkPhysicalDeviceDescriptorSetHostMappingFeaturesVALVE { + VkStructureType sType; + void* pNext; + VkBool32 descriptorSetHostMapping; +} VkPhysicalDeviceDescriptorSetHostMappingFeaturesVALVE; + +typedef struct VkDescriptorSetBindingReferenceVALVE { + VkStructureType sType; + const void* pNext; + VkDescriptorSetLayout descriptorSetLayout; + uint32_t binding; +} VkDescriptorSetBindingReferenceVALVE; + +typedef struct VkDescriptorSetLayoutHostMappingInfoVALVE { + VkStructureType sType; + void* pNext; + size_t descriptorOffset; + uint32_t descriptorSize; +} VkDescriptorSetLayoutHostMappingInfoVALVE; + +typedef void (VKAPI_PTR *PFN_vkGetDescriptorSetLayoutHostMappingInfoVALVE)(VkDevice device, const VkDescriptorSetBindingReferenceVALVE* pBindingReference, VkDescriptorSetLayoutHostMappingInfoVALVE* pHostMapping); +typedef void (VKAPI_PTR *PFN_vkGetDescriptorSetHostMappingVALVE)(VkDevice device, VkDescriptorSet descriptorSet, void** ppData); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkGetDescriptorSetLayoutHostMappingInfoVALVE( + VkDevice device, + const VkDescriptorSetBindingReferenceVALVE* pBindingReference, + VkDescriptorSetLayoutHostMappingInfoVALVE* pHostMapping); + +VKAPI_ATTR void VKAPI_CALL vkGetDescriptorSetHostMappingVALVE( + VkDevice device, + VkDescriptorSet descriptorSet, + void** ppData); +#endif + + +#define VK_QCOM_fragment_density_map_offset 1 +#define VK_QCOM_FRAGMENT_DENSITY_MAP_OFFSET_SPEC_VERSION 1 +#define VK_QCOM_FRAGMENT_DENSITY_MAP_OFFSET_EXTENSION_NAME "VK_QCOM_fragment_density_map_offset" +typedef struct VkPhysicalDeviceFragmentDensityMapOffsetFeaturesQCOM { + VkStructureType sType; + void* pNext; + VkBool32 fragmentDensityMapOffset; +} VkPhysicalDeviceFragmentDensityMapOffsetFeaturesQCOM; + +typedef struct VkPhysicalDeviceFragmentDensityMapOffsetPropertiesQCOM { + VkStructureType sType; + void* pNext; + VkExtent2D fragmentDensityOffsetGranularity; +} VkPhysicalDeviceFragmentDensityMapOffsetPropertiesQCOM; + +typedef struct VkSubpassFragmentDensityMapOffsetEndInfoQCOM { + VkStructureType sType; + const void* pNext; + uint32_t fragmentDensityOffsetCount; + const VkOffset2D* pFragmentDensityOffsets; +} VkSubpassFragmentDensityMapOffsetEndInfoQCOM; + + + +#define VK_NV_linear_color_attachment 1 +#define VK_NV_LINEAR_COLOR_ATTACHMENT_SPEC_VERSION 1 +#define VK_NV_LINEAR_COLOR_ATTACHMENT_EXTENSION_NAME "VK_NV_linear_color_attachment" +typedef struct VkPhysicalDeviceLinearColorAttachmentFeaturesNV { + VkStructureType sType; + void* pNext; + VkBool32 linearColorAttachment; +} VkPhysicalDeviceLinearColorAttachmentFeaturesNV; + + + +#define VK_GOOGLE_surfaceless_query 1 +#define VK_GOOGLE_SURFACELESS_QUERY_SPEC_VERSION 1 +#define VK_GOOGLE_SURFACELESS_QUERY_EXTENSION_NAME "VK_GOOGLE_surfaceless_query" + + #define VK_KHR_acceleration_structure 1 VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkAccelerationStructureKHR) -#define VK_KHR_ACCELERATION_STRUCTURE_SPEC_VERSION 12 +#define VK_KHR_ACCELERATION_STRUCTURE_SPEC_VERSION 13 #define VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME "VK_KHR_acceleration_structure" typedef enum VkBuildAccelerationStructureModeKHR { diff --git a/vendor/vulkan/_gen/vulkan_ios.h b/vendor/vulkan/_gen/vulkan_ios.h index 6e7e6afea..579220543 100644 --- a/vendor/vulkan/_gen/vulkan_ios.h +++ b/vendor/vulkan/_gen/vulkan_ios.h @@ -2,7 +2,7 @@ #define VULKAN_IOS_H_ 1 /* -** Copyright 2015-2021 The Khronos Group Inc. +** Copyright 2015-2022 The Khronos Group Inc. ** ** SPDX-License-Identifier: Apache-2.0 */ diff --git a/vendor/vulkan/_gen/vulkan_macos.h b/vendor/vulkan/_gen/vulkan_macos.h index c49b123d0..8e197c7cf 100644 --- a/vendor/vulkan/_gen/vulkan_macos.h +++ b/vendor/vulkan/_gen/vulkan_macos.h @@ -2,7 +2,7 @@ #define VULKAN_MACOS_H_ 1 /* -** Copyright 2015-2021 The Khronos Group Inc. +** Copyright 2015-2022 The Khronos Group Inc. ** ** SPDX-License-Identifier: Apache-2.0 */ diff --git a/vendor/vulkan/_gen/vulkan_metal.h b/vendor/vulkan/_gen/vulkan_metal.h index 5cf4a703a..3631f1200 100644 --- a/vendor/vulkan/_gen/vulkan_metal.h +++ b/vendor/vulkan/_gen/vulkan_metal.h @@ -2,7 +2,7 @@ #define VULKAN_METAL_H_ 1 /* -** Copyright 2015-2021 The Khronos Group Inc. +** Copyright 2015-2022 The Khronos Group Inc. ** ** SPDX-License-Identifier: Apache-2.0 */ diff --git a/vendor/vulkan/_gen/vulkan_win32.h b/vendor/vulkan/_gen/vulkan_win32.h index 1b680f0b1..affe0c02a 100644 --- a/vendor/vulkan/_gen/vulkan_win32.h +++ b/vendor/vulkan/_gen/vulkan_win32.h @@ -2,7 +2,7 @@ #define VULKAN_WIN32_H_ 1 /* -** Copyright 2015-2021 The Khronos Group Inc. +** Copyright 2015-2022 The Khronos Group Inc. ** ** SPDX-License-Identifier: Apache-2.0 */ diff --git a/vendor/vulkan/core.odin b/vendor/vulkan/core.odin index 67ca47553..b90bfad17 100644 --- a/vendor/vulkan/core.odin +++ b/vendor/vulkan/core.odin @@ -3,6 +3,9 @@ // package vulkan API_VERSION_1_0 :: (1<<22) | (0<<12) | (0) +API_VERSION_1_1 :: (1<<22) | (1<<12) | (0) +API_VERSION_1_2 :: (1<<22) | (2<<12) | (0) +API_VERSION_1_3 :: (1<<22) | (3<<12) | (0) MAKE_VERSION :: proc(major, minor, patch: u32) -> u32 { return (major<<22) | (minor<<12) | (patch) @@ -39,18 +42,15 @@ MAX_MEMORY_TYPES :: 32 MAX_MEMORY_HEAPS :: 16 MAX_EXTENSION_NAME_SIZE :: 256 MAX_DESCRIPTION_SIZE :: 256 -MAX_DEVICE_GROUP_SIZE_KHX :: 32 MAX_DEVICE_GROUP_SIZE :: 32 LUID_SIZE_KHX :: 8 -LUID_SIZE_KHR :: 8 LUID_SIZE :: 8 -MAX_DRIVER_NAME_SIZE_KHR :: 256 -MAX_DRIVER_INFO_SIZE_KHR :: 256 -MAX_QUEUE_FAMILY_EXTERNAL :: ~u32(0)-1 +MAX_QUEUE_FAMILY_EXTERNAL :: ~u32(1) MAX_GLOBAL_PRIORITY_SIZE_EXT :: 16 +QUEUE_FAMILY_EXTERNAL :: MAX_QUEUE_FAMILY_EXTERNAL // General Constants -HEADER_VERSION :: 191 +HEADER_VERSION :: 211 MAX_DRIVER_NAME_SIZE :: 256 MAX_DRIVER_INFO_SIZE :: 256 @@ -70,6 +70,9 @@ KHR_DISPLAY_SWAPCHAIN_EXTENSION_NAME :: "VK_KHR_display_swapc KHR_sampler_mirror_clamp_to_edge :: 1 KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_SPEC_VERSION :: 3 KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME :: "VK_KHR_sampler_mirror_clamp_to_edge" +KHR_dynamic_rendering :: 1 +KHR_DYNAMIC_RENDERING_SPEC_VERSION :: 1 +KHR_DYNAMIC_RENDERING_EXTENSION_NAME :: "VK_KHR_dynamic_rendering" KHR_multiview :: 1 KHR_MULTIVIEW_SPEC_VERSION :: 1 KHR_MULTIVIEW_EXTENSION_NAME :: "VK_KHR_multiview" @@ -83,17 +86,22 @@ KHR_shader_draw_parameters :: 1 KHR_SHADER_DRAW_PARAMETERS_SPEC_VERSION :: 1 KHR_SHADER_DRAW_PARAMETERS_EXTENSION_NAME :: "VK_KHR_shader_draw_parameters" KHR_maintenance1 :: 1 -KHR_MAINTENANCE1_SPEC_VERSION :: 2 -KHR_MAINTENANCE1_EXTENSION_NAME :: "VK_KHR_maintenance1" +KHR_MAINTENANCE_1_SPEC_VERSION :: 2 +KHR_MAINTENANCE_1_EXTENSION_NAME :: "VK_KHR_maintenance1" +KHR_MAINTENANCE1_SPEC_VERSION :: KHR_MAINTENANCE_1_SPEC_VERSION +KHR_MAINTENANCE1_EXTENSION_NAME :: KHR_MAINTENANCE_1_EXTENSION_NAME KHR_device_group_creation :: 1 KHR_DEVICE_GROUP_CREATION_SPEC_VERSION :: 1 KHR_DEVICE_GROUP_CREATION_EXTENSION_NAME :: "VK_KHR_device_group_creation" +MAX_DEVICE_GROUP_SIZE_KHR :: MAX_DEVICE_GROUP_SIZE KHR_external_memory_capabilities :: 1 KHR_EXTERNAL_MEMORY_CAPABILITIES_SPEC_VERSION :: 1 KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME :: "VK_KHR_external_memory_capabilities" +LUID_SIZE_KHR :: LUID_SIZE KHR_external_memory :: 1 KHR_EXTERNAL_MEMORY_SPEC_VERSION :: 1 KHR_EXTERNAL_MEMORY_EXTENSION_NAME :: "VK_KHR_external_memory" +QUEUE_FAMILY_EXTERNAL_KHR :: QUEUE_FAMILY_EXTERNAL KHR_external_memory_fd :: 1 KHR_EXTERNAL_MEMORY_FD_SPEC_VERSION :: 1 KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME :: "VK_KHR_external_memory_fd" @@ -143,8 +151,10 @@ KHR_performance_query :: 1 KHR_PERFORMANCE_QUERY_SPEC_VERSION :: 1 KHR_PERFORMANCE_QUERY_EXTENSION_NAME :: "VK_KHR_performance_query" KHR_maintenance2 :: 1 -KHR_MAINTENANCE2_SPEC_VERSION :: 1 -KHR_MAINTENANCE2_EXTENSION_NAME :: "VK_KHR_maintenance2" +KHR_MAINTENANCE_2_SPEC_VERSION :: 1 +KHR_MAINTENANCE_2_EXTENSION_NAME :: "VK_KHR_maintenance2" +KHR_MAINTENANCE2_SPEC_VERSION :: KHR_MAINTENANCE_2_SPEC_VERSION +KHR_MAINTENANCE2_EXTENSION_NAME :: KHR_MAINTENANCE_2_EXTENSION_NAME KHR_get_surface_capabilities2 :: 1 KHR_GET_SURFACE_CAPABILITIES_2_SPEC_VERSION :: 1 KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME :: "VK_KHR_get_surface_capabilities2" @@ -176,8 +186,10 @@ KHR_bind_memory2 :: 1 KHR_BIND_MEMORY_2_SPEC_VERSION :: 1 KHR_BIND_MEMORY_2_EXTENSION_NAME :: "VK_KHR_bind_memory2" KHR_maintenance3 :: 1 -KHR_MAINTENANCE3_SPEC_VERSION :: 1 -KHR_MAINTENANCE3_EXTENSION_NAME :: "VK_KHR_maintenance3" +KHR_MAINTENANCE_3_SPEC_VERSION :: 1 +KHR_MAINTENANCE_3_EXTENSION_NAME :: "VK_KHR_maintenance3" +KHR_MAINTENANCE3_SPEC_VERSION :: KHR_MAINTENANCE_3_SPEC_VERSION +KHR_MAINTENANCE3_EXTENSION_NAME :: KHR_MAINTENANCE_3_EXTENSION_NAME KHR_draw_indirect_count :: 1 KHR_DRAW_INDIRECT_COUNT_SPEC_VERSION :: 1 KHR_DRAW_INDIRECT_COUNT_EXTENSION_NAME :: "VK_KHR_draw_indirect_count" @@ -193,9 +205,15 @@ KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME :: "VK_KHR_shader_atomic KHR_shader_clock :: 1 KHR_SHADER_CLOCK_SPEC_VERSION :: 1 KHR_SHADER_CLOCK_EXTENSION_NAME :: "VK_KHR_shader_clock" +KHR_global_priority :: 1 +MAX_GLOBAL_PRIORITY_SIZE_KHR :: 16 +KHR_GLOBAL_PRIORITY_SPEC_VERSION :: 1 +KHR_GLOBAL_PRIORITY_EXTENSION_NAME :: "VK_KHR_global_priority" KHR_driver_properties :: 1 KHR_DRIVER_PROPERTIES_SPEC_VERSION :: 1 KHR_DRIVER_PROPERTIES_EXTENSION_NAME :: "VK_KHR_driver_properties" +MAX_DRIVER_NAME_SIZE_KHR :: MAX_DRIVER_NAME_SIZE +MAX_DRIVER_INFO_SIZE_KHR :: MAX_DRIVER_INFO_SIZE KHR_shader_float_controls :: 1 KHR_SHADER_FLOAT_CONTROLS_SPEC_VERSION :: 4 KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME :: "VK_KHR_shader_float_controls" @@ -215,7 +233,7 @@ KHR_shader_terminate_invocation :: 1 KHR_SHADER_TERMINATE_INVOCATION_SPEC_VERSION :: 1 KHR_SHADER_TERMINATE_INVOCATION_EXTENSION_NAME :: "VK_KHR_shader_terminate_invocation" KHR_fragment_shading_rate :: 1 -KHR_FRAGMENT_SHADING_RATE_SPEC_VERSION :: 1 +KHR_FRAGMENT_SHADING_RATE_SPEC_VERSION :: 2 KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME :: "VK_KHR_fragment_shading_rate" KHR_spirv_1_4 :: 1 KHR_SPIRV_1_4_SPEC_VERSION :: 1 @@ -268,6 +286,15 @@ KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME :: "VK_KHR_workgroup_mem KHR_copy_commands2 :: 1 KHR_COPY_COMMANDS_2_SPEC_VERSION :: 1 KHR_COPY_COMMANDS_2_EXTENSION_NAME :: "VK_KHR_copy_commands2" +KHR_format_feature_flags2 :: 1 +KHR_FORMAT_FEATURE_FLAGS_2_SPEC_VERSION :: 1 +KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME :: "VK_KHR_format_feature_flags2" +KHR_portability_enumeration :: 1 +KHR_PORTABILITY_ENUMERATION_SPEC_VERSION :: 1 +KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME :: "VK_KHR_portability_enumeration" +KHR_maintenance4 :: 1 +KHR_MAINTENANCE_4_SPEC_VERSION :: 2 +KHR_MAINTENANCE_4_EXTENSION_NAME :: "VK_KHR_maintenance4" EXT_debug_report :: 1 EXT_DEBUG_REPORT_SPEC_VERSION :: 10 EXT_DEBUG_REPORT_EXTENSION_NAME :: "VK_EXT_debug_report" @@ -374,8 +401,10 @@ NV_geometry_shader_passthrough :: 1 NV_GEOMETRY_SHADER_PASSTHROUGH_SPEC_VERSION :: 1 NV_GEOMETRY_SHADER_PASSTHROUGH_EXTENSION_NAME :: "VK_NV_geometry_shader_passthrough" NV_viewport_array2 :: 1 -NV_VIEWPORT_ARRAY2_SPEC_VERSION :: 1 -NV_VIEWPORT_ARRAY2_EXTENSION_NAME :: "VK_NV_viewport_array2" +NV_VIEWPORT_ARRAY_2_SPEC_VERSION :: 1 +NV_VIEWPORT_ARRAY_2_EXTENSION_NAME :: "VK_NV_viewport_array2" +NV_VIEWPORT_ARRAY2_SPEC_VERSION :: NV_VIEWPORT_ARRAY_2_SPEC_VERSION +NV_VIEWPORT_ARRAY2_EXTENSION_NAME :: NV_VIEWPORT_ARRAY_2_EXTENSION_NAME NVX_multiview_per_view_attributes :: 1 NVX_MULTIVIEW_PER_VIEW_ATTRIBUTES_SPEC_VERSION :: 1 NVX_MULTIVIEW_PER_VIEW_ATTRIBUTES_EXTENSION_NAME :: "VK_NVX_multiview_per_view_attributes" @@ -446,7 +475,7 @@ EXT_post_depth_coverage :: 1 EXT_POST_DEPTH_COVERAGE_SPEC_VERSION :: 1 EXT_POST_DEPTH_COVERAGE_EXTENSION_NAME :: "VK_EXT_post_depth_coverage" EXT_image_drm_format_modifier :: 1 -EXT_IMAGE_DRM_FORMAT_MODIFIER_SPEC_VERSION :: 1 +EXT_IMAGE_DRM_FORMAT_MODIFIER_SPEC_VERSION :: 2 EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME :: "VK_EXT_image_drm_format_modifier" EXT_validation_cache :: 1 EXT_VALIDATION_CACHE_SPEC_VERSION :: 1 @@ -463,6 +492,7 @@ NV_SHADING_RATE_IMAGE_EXTENSION_NAME :: "VK_NV_shading_rate_i NV_ray_tracing :: 1 NV_RAY_TRACING_SPEC_VERSION :: 3 NV_RAY_TRACING_EXTENSION_NAME :: "VK_NV_ray_tracing" +SHADER_UNUSED_KHR :: 0 NV_representative_fragment_test :: 1 NV_REPRESENTATIVE_FRAGMENT_TEST_SPEC_VERSION :: 2 NV_REPRESENTATIVE_FRAGMENT_TEST_EXTENSION_NAME :: "VK_NV_representative_fragment_test" @@ -524,14 +554,16 @@ AMD_display_native_hdr :: 1 AMD_DISPLAY_NATIVE_HDR_SPEC_VERSION :: 1 AMD_DISPLAY_NATIVE_HDR_EXTENSION_NAME :: "VK_AMD_display_native_hdr" EXT_fragment_density_map :: 1 -EXT_FRAGMENT_DENSITY_MAP_SPEC_VERSION :: 1 +EXT_FRAGMENT_DENSITY_MAP_SPEC_VERSION :: 2 EXT_FRAGMENT_DENSITY_MAP_EXTENSION_NAME :: "VK_EXT_fragment_density_map" EXT_scalar_block_layout :: 1 EXT_SCALAR_BLOCK_LAYOUT_SPEC_VERSION :: 1 EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME :: "VK_EXT_scalar_block_layout" GOOGLE_hlsl_functionality1 :: 1 -GOOGLE_HLSL_FUNCTIONALITY1_SPEC_VERSION :: 1 -GOOGLE_HLSL_FUNCTIONALITY1_EXTENSION_NAME :: "VK_GOOGLE_hlsl_functionality1" +GOOGLE_HLSL_FUNCTIONALITY_1_SPEC_VERSION :: 1 +GOOGLE_HLSL_FUNCTIONALITY_1_EXTENSION_NAME :: "VK_GOOGLE_hlsl_functionality1" +GOOGLE_HLSL_FUNCTIONALITY1_SPEC_VERSION :: GOOGLE_HLSL_FUNCTIONALITY_1_SPEC_VERSION +GOOGLE_HLSL_FUNCTIONALITY1_EXTENSION_NAME :: GOOGLE_HLSL_FUNCTIONALITY_1_EXTENSION_NAME GOOGLE_decorate_string :: 1 GOOGLE_DECORATE_STRING_SPEC_VERSION :: 1 GOOGLE_DECORATE_STRING_EXTENSION_NAME :: "VK_GOOGLE_decorate_string" @@ -640,6 +672,9 @@ EXT_PIPELINE_CREATION_CACHE_CONTROL_EXTENSION_NAME :: "VK_EXT_pipeline_crea NV_device_diagnostics_config :: 1 NV_DEVICE_DIAGNOSTICS_CONFIG_SPEC_VERSION :: 1 NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME :: "VK_NV_device_diagnostics_config" +EXT_graphics_pipeline_library :: 1 +EXT_GRAPHICS_PIPELINE_LIBRARY_SPEC_VERSION :: 1 +EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME :: "VK_EXT_graphics_pipeline_library" NV_fragment_shading_rate_enums :: 1 NV_FRAGMENT_SHADING_RATE_ENUMS_SPEC_VERSION :: 1 NV_FRAGMENT_SHADING_RATE_ENUMS_EXTENSION_NAME :: "VK_NV_fragment_shading_rate_enums" @@ -658,6 +693,9 @@ EXT_IMAGE_ROBUSTNESS_EXTENSION_NAME :: "VK_EXT_image_robustn EXT_4444_formats :: 1 EXT_4444_FORMATS_SPEC_VERSION :: 1 EXT_4444_FORMATS_EXTENSION_NAME :: "VK_EXT_4444_formats" +EXT_rgba10x6_formats :: 1 +EXT_RGBA10X6_FORMATS_SPEC_VERSION :: 1 +EXT_RGBA10X6_FORMATS_EXTENSION_NAME :: "VK_EXT_rgba10x6_formats" NV_acquire_winrt_display :: 1 NV_ACQUIRE_WINRT_DISPLAY_SPEC_VERSION :: 1 NV_ACQUIRE_WINRT_DISPLAY_EXTENSION_NAME :: "VK_NV_acquire_winrt_display" @@ -667,6 +705,9 @@ EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME :: "VK_EXT_vertex_input_ EXT_physical_device_drm :: 1 EXT_PHYSICAL_DEVICE_DRM_SPEC_VERSION :: 1 EXT_PHYSICAL_DEVICE_DRM_EXTENSION_NAME :: "VK_EXT_physical_device_drm" +EXT_depth_clip_control :: 1 +EXT_DEPTH_CLIP_CONTROL_SPEC_VERSION :: 1 +EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME :: "VK_EXT_depth_clip_control" EXT_primitive_topology_list_restart :: 1 EXT_PRIMITIVE_TOPOLOGY_LIST_RESTART_SPEC_VERSION :: 1 EXT_PRIMITIVE_TOPOLOGY_LIST_RESTART_EXTENSION_NAME :: "VK_EXT_primitive_topology_list_restart" @@ -679,20 +720,38 @@ EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME :: "VK_EXT_extended_dyna EXT_color_write_enable :: 1 EXT_COLOR_WRITE_ENABLE_SPEC_VERSION :: 1 EXT_COLOR_WRITE_ENABLE_EXTENSION_NAME :: "VK_EXT_color_write_enable" +EXT_primitives_generated_query :: 1 +EXT_PRIMITIVES_GENERATED_QUERY_SPEC_VERSION :: 1 +EXT_PRIMITIVES_GENERATED_QUERY_EXTENSION_NAME :: "VK_EXT_primitives_generated_query" EXT_global_priority_query :: 1 EXT_GLOBAL_PRIORITY_QUERY_SPEC_VERSION :: 1 EXT_GLOBAL_PRIORITY_QUERY_EXTENSION_NAME :: "VK_EXT_global_priority_query" +EXT_image_view_min_lod :: 1 +EXT_IMAGE_VIEW_MIN_LOD_SPEC_VERSION :: 1 +EXT_IMAGE_VIEW_MIN_LOD_EXTENSION_NAME :: "VK_EXT_image_view_min_lod" EXT_multi_draw :: 1 EXT_MULTI_DRAW_SPEC_VERSION :: 1 EXT_MULTI_DRAW_EXTENSION_NAME :: "VK_EXT_multi_draw" +EXT_image_2d_view_of_3d :: 1 +EXT_IMAGE_2D_VIEW_OF_3D_SPEC_VERSION :: 1 +EXT_IMAGE_2D_VIEW_OF_3D_EXTENSION_NAME :: "VK_EXT_image_2d_view_of_3d" EXT_load_store_op_none :: 1 EXT_LOAD_STORE_OP_NONE_SPEC_VERSION :: 1 EXT_LOAD_STORE_OP_NONE_EXTENSION_NAME :: "VK_EXT_load_store_op_none" +EXT_border_color_swizzle :: 1 +EXT_BORDER_COLOR_SWIZZLE_SPEC_VERSION :: 1 +EXT_BORDER_COLOR_SWIZZLE_EXTENSION_NAME :: "VK_EXT_border_color_swizzle" EXT_pageable_device_local_memory :: 1 EXT_PAGEABLE_DEVICE_LOCAL_MEMORY_SPEC_VERSION :: 1 EXT_PAGEABLE_DEVICE_LOCAL_MEMORY_EXTENSION_NAME :: "VK_EXT_pageable_device_local_memory" +NV_linear_color_attachment :: 1 +NV_LINEAR_COLOR_ATTACHMENT_SPEC_VERSION :: 1 +NV_LINEAR_COLOR_ATTACHMENT_EXTENSION_NAME :: "VK_NV_linear_color_attachment" +GOOGLE_surfaceless_query :: 1 +GOOGLE_SURFACELESS_QUERY_SPEC_VERSION :: 1 +GOOGLE_SURFACELESS_QUERY_EXTENSION_NAME :: "VK_GOOGLE_surfaceless_query" KHR_acceleration_structure :: 1 -KHR_ACCELERATION_STRUCTURE_SPEC_VERSION :: 12 +KHR_ACCELERATION_STRUCTURE_SPEC_VERSION :: 13 KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME :: "VK_KHR_acceleration_structure" KHR_ray_tracing_pipeline :: 1 KHR_RAY_TRACING_PIPELINE_SPEC_VERSION :: 1 @@ -756,6 +815,7 @@ Framebuffer :: distinct NonDispatchableHandle CommandPool :: distinct NonDispatchableHandle SamplerYcbcrConversion :: distinct NonDispatchableHandle DescriptorUpdateTemplate :: distinct NonDispatchableHandle +PrivateDataSlot :: distinct NonDispatchableHandle SurfaceKHR :: distinct NonDispatchableHandle SwapchainKHR :: distinct NonDispatchableHandle DisplayKHR :: distinct NonDispatchableHandle @@ -769,7 +829,6 @@ ValidationCacheEXT :: distinct NonDispatchableHandle AccelerationStructureNV :: distinct NonDispatchableHandle PerformanceConfigurationINTEL :: distinct NonDispatchableHandle IndirectCommandsLayoutNV :: distinct NonDispatchableHandle -PrivateDataSlotEXT :: distinct NonDispatchableHandle AccelerationStructureKHR :: distinct NonDispatchableHandle diff --git a/vendor/vulkan/enums.odin b/vendor/vulkan/enums.odin index be6691ab4..b2eec06ab 100644 --- a/vendor/vulkan/enums.odin +++ b/vendor/vulkan/enums.odin @@ -78,7 +78,7 @@ AccessFlag :: enum Flags { ACCELERATION_STRUCTURE_WRITE_NV = ACCELERATION_STRUCTURE_WRITE_KHR, } -AccessFlags_NONE_KHR :: AccessFlags{} +AccessFlags_NONE :: AccessFlags{} AcquireProfilingLockFlagsKHR :: distinct bit_set[AcquireProfilingLockFlagKHR; Flags] @@ -100,8 +100,7 @@ AttachmentLoadOp :: enum c.int { AttachmentStoreOp :: enum c.int { STORE = 0, DONT_CARE = 1, - NONE_EXT = 1000301000, - NONE_QCOM = NONE_EXT, + NONE = 1000301000, } BlendFactor :: enum c.int { @@ -460,6 +459,7 @@ DebugReportObjectTypeEXT :: enum c.int { CU_FUNCTION_NVX = 1000029001, ACCELERATION_STRUCTURE_KHR = 1000150000, ACCELERATION_STRUCTURE_NV = 1000165000, + BUFFER_COLLECTION_FUCHSIA = 1000366000, DEBUG_REPORT = DEBUG_REPORT_CALLBACK_EXT, VALIDATION_CACHE = VALIDATION_CACHE_EXT, DESCRIPTOR_UPDATE_TEMPLATE_KHR = DESCRIPTOR_UPDATE_TEMPLATE, @@ -530,10 +530,11 @@ DescriptorType :: enum c.int { UNIFORM_BUFFER_DYNAMIC = 8, STORAGE_BUFFER_DYNAMIC = 9, INPUT_ATTACHMENT = 10, - INLINE_UNIFORM_BLOCK_EXT = 1000138000, + INLINE_UNIFORM_BLOCK = 1000138000, ACCELERATION_STRUCTURE_KHR = 1000150000, ACCELERATION_STRUCTURE_NV = 1000165000, MUTABLE_VALVE = 1000351000, + INLINE_UNIFORM_BLOCK_EXT = INLINE_UNIFORM_BLOCK, } DescriptorUpdateTemplateType :: enum c.int { @@ -615,6 +616,11 @@ DriverId :: enum c.int { COREAVI_PROPRIETARY = 15, JUICE_PROPRIETARY = 16, VERISILICON_PROPRIETARY = 17, + MESA_TURNIP = 18, + MESA_V3DV = 19, + MESA_PANVK = 20, + SAMSUNG_PROPRIETARY = 21, + MESA_VENUS = 22, AMD_PROPRIETARY_KHR = AMD_PROPRIETARY, AMD_OPEN_SOURCE_KHR = AMD_OPEN_SOURCE, MESA_RADV_KHR = MESA_RADV, @@ -639,6 +645,21 @@ DynamicState :: enum c.int { STENCIL_COMPARE_MASK = 6, STENCIL_WRITE_MASK = 7, STENCIL_REFERENCE = 8, + CULL_MODE = 1000267000, + FRONT_FACE = 1000267001, + PRIMITIVE_TOPOLOGY = 1000267002, + VIEWPORT_WITH_COUNT = 1000267003, + SCISSOR_WITH_COUNT = 1000267004, + VERTEX_INPUT_BINDING_STRIDE = 1000267005, + DEPTH_TEST_ENABLE = 1000267006, + DEPTH_WRITE_ENABLE = 1000267007, + DEPTH_COMPARE_OP = 1000267008, + DEPTH_BOUNDS_TEST_ENABLE = 1000267009, + STENCIL_TEST_ENABLE = 1000267010, + STENCIL_OP = 1000267011, + RASTERIZER_DISCARD_ENABLE = 1000377001, + DEPTH_BIAS_ENABLE = 1000377002, + PRIMITIVE_RESTART_ENABLE = 1000377004, VIEWPORT_W_SCALING_NV = 1000087000, DISCARD_RECTANGLE_EXT = 1000099000, SAMPLE_LOCATIONS_EXT = 1000143000, @@ -648,30 +669,31 @@ DynamicState :: enum c.int { EXCLUSIVE_SCISSOR_NV = 1000205001, FRAGMENT_SHADING_RATE_KHR = 1000226000, LINE_STIPPLE_EXT = 1000259000, - CULL_MODE_EXT = 1000267000, - FRONT_FACE_EXT = 1000267001, - PRIMITIVE_TOPOLOGY_EXT = 1000267002, - VIEWPORT_WITH_COUNT_EXT = 1000267003, - SCISSOR_WITH_COUNT_EXT = 1000267004, - VERTEX_INPUT_BINDING_STRIDE_EXT = 1000267005, - DEPTH_TEST_ENABLE_EXT = 1000267006, - DEPTH_WRITE_ENABLE_EXT = 1000267007, - DEPTH_COMPARE_OP_EXT = 1000267008, - DEPTH_BOUNDS_TEST_ENABLE_EXT = 1000267009, - STENCIL_TEST_ENABLE_EXT = 1000267010, - STENCIL_OP_EXT = 1000267011, VERTEX_INPUT_EXT = 1000352000, PATCH_CONTROL_POINTS_EXT = 1000377000, - RASTERIZER_DISCARD_ENABLE_EXT = 1000377001, - DEPTH_BIAS_ENABLE_EXT = 1000377002, LOGIC_OP_EXT = 1000377003, - PRIMITIVE_RESTART_ENABLE_EXT = 1000377004, COLOR_WRITE_ENABLE_EXT = 1000381000, + CULL_MODE_EXT = CULL_MODE, + FRONT_FACE_EXT = FRONT_FACE, + PRIMITIVE_TOPOLOGY_EXT = PRIMITIVE_TOPOLOGY, + VIEWPORT_WITH_COUNT_EXT = VIEWPORT_WITH_COUNT, + SCISSOR_WITH_COUNT_EXT = SCISSOR_WITH_COUNT, + VERTEX_INPUT_BINDING_STRIDE_EXT = VERTEX_INPUT_BINDING_STRIDE, + DEPTH_TEST_ENABLE_EXT = DEPTH_TEST_ENABLE, + DEPTH_WRITE_ENABLE_EXT = DEPTH_WRITE_ENABLE, + DEPTH_COMPARE_OP_EXT = DEPTH_COMPARE_OP, + DEPTH_BOUNDS_TEST_ENABLE_EXT = DEPTH_BOUNDS_TEST_ENABLE, + STENCIL_TEST_ENABLE_EXT = STENCIL_TEST_ENABLE, + STENCIL_OP_EXT = STENCIL_OP, + RASTERIZER_DISCARD_ENABLE_EXT = RASTERIZER_DISCARD_ENABLE, + DEPTH_BIAS_ENABLE_EXT = DEPTH_BIAS_ENABLE, + PRIMITIVE_RESTART_ENABLE_EXT = PRIMITIVE_RESTART_ENABLE, } EventCreateFlags :: distinct bit_set[EventCreateFlag; Flags] EventCreateFlag :: enum Flags { - DEVICE_ONLY_KHR = 0, + DEVICE_ONLY = 0, + DEVICE_ONLY_KHR = DEVICE_ONLY, } ExternalFenceFeatureFlags :: distinct bit_set[ExternalFenceFeatureFlag; Flags] @@ -1005,6 +1027,26 @@ Format :: enum c.int { G16_B16_R16_3PLANE_422_UNORM = 1000156031, G16_B16R16_2PLANE_422_UNORM = 1000156032, G16_B16_R16_3PLANE_444_UNORM = 1000156033, + G8_B8R8_2PLANE_444_UNORM = 1000330000, + G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16 = 1000330001, + G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16 = 1000330002, + G16_B16R16_2PLANE_444_UNORM = 1000330003, + A4R4G4B4_UNORM_PACK16 = 1000340000, + A4B4G4R4_UNORM_PACK16 = 1000340001, + ASTC_4x4_SFLOAT_BLOCK = 1000066000, + ASTC_5x4_SFLOAT_BLOCK = 1000066001, + ASTC_5x5_SFLOAT_BLOCK = 1000066002, + ASTC_6x5_SFLOAT_BLOCK = 1000066003, + ASTC_6x6_SFLOAT_BLOCK = 1000066004, + ASTC_8x5_SFLOAT_BLOCK = 1000066005, + ASTC_8x6_SFLOAT_BLOCK = 1000066006, + ASTC_8x8_SFLOAT_BLOCK = 1000066007, + ASTC_10x5_SFLOAT_BLOCK = 1000066008, + ASTC_10x6_SFLOAT_BLOCK = 1000066009, + ASTC_10x8_SFLOAT_BLOCK = 1000066010, + ASTC_10x10_SFLOAT_BLOCK = 1000066011, + ASTC_12x10_SFLOAT_BLOCK = 1000066012, + ASTC_12x12_SFLOAT_BLOCK = 1000066013, PVRTC1_2BPP_UNORM_BLOCK_IMG = 1000054000, PVRTC1_4BPP_UNORM_BLOCK_IMG = 1000054001, PVRTC2_2BPP_UNORM_BLOCK_IMG = 1000054002, @@ -1013,26 +1055,20 @@ Format :: enum c.int { PVRTC1_4BPP_SRGB_BLOCK_IMG = 1000054005, PVRTC2_2BPP_SRGB_BLOCK_IMG = 1000054006, PVRTC2_4BPP_SRGB_BLOCK_IMG = 1000054007, - ASTC_4x4_SFLOAT_BLOCK_EXT = 1000066000, - ASTC_5x4_SFLOAT_BLOCK_EXT = 1000066001, - ASTC_5x5_SFLOAT_BLOCK_EXT = 1000066002, - ASTC_6x5_SFLOAT_BLOCK_EXT = 1000066003, - ASTC_6x6_SFLOAT_BLOCK_EXT = 1000066004, - ASTC_8x5_SFLOAT_BLOCK_EXT = 1000066005, - ASTC_8x6_SFLOAT_BLOCK_EXT = 1000066006, - ASTC_8x8_SFLOAT_BLOCK_EXT = 1000066007, - ASTC_10x5_SFLOAT_BLOCK_EXT = 1000066008, - ASTC_10x6_SFLOAT_BLOCK_EXT = 1000066009, - ASTC_10x8_SFLOAT_BLOCK_EXT = 1000066010, - ASTC_10x10_SFLOAT_BLOCK_EXT = 1000066011, - ASTC_12x10_SFLOAT_BLOCK_EXT = 1000066012, - ASTC_12x12_SFLOAT_BLOCK_EXT = 1000066013, - G8_B8R8_2PLANE_444_UNORM_EXT = 1000330000, - G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16_EXT = 1000330001, - G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16_EXT = 1000330002, - G16_B16R16_2PLANE_444_UNORM_EXT = 1000330003, - A4R4G4B4_UNORM_PACK16_EXT = 1000340000, - A4B4G4R4_UNORM_PACK16_EXT = 1000340001, + ASTC_4x4_SFLOAT_BLOCK_EXT = ASTC_4x4_SFLOAT_BLOCK, + ASTC_5x4_SFLOAT_BLOCK_EXT = ASTC_5x4_SFLOAT_BLOCK, + ASTC_5x5_SFLOAT_BLOCK_EXT = ASTC_5x5_SFLOAT_BLOCK, + ASTC_6x5_SFLOAT_BLOCK_EXT = ASTC_6x5_SFLOAT_BLOCK, + ASTC_6x6_SFLOAT_BLOCK_EXT = ASTC_6x6_SFLOAT_BLOCK, + ASTC_8x5_SFLOAT_BLOCK_EXT = ASTC_8x5_SFLOAT_BLOCK, + ASTC_8x6_SFLOAT_BLOCK_EXT = ASTC_8x6_SFLOAT_BLOCK, + ASTC_8x8_SFLOAT_BLOCK_EXT = ASTC_8x8_SFLOAT_BLOCK, + ASTC_10x5_SFLOAT_BLOCK_EXT = ASTC_10x5_SFLOAT_BLOCK, + ASTC_10x6_SFLOAT_BLOCK_EXT = ASTC_10x6_SFLOAT_BLOCK, + ASTC_10x8_SFLOAT_BLOCK_EXT = ASTC_10x8_SFLOAT_BLOCK, + ASTC_10x10_SFLOAT_BLOCK_EXT = ASTC_10x10_SFLOAT_BLOCK, + ASTC_12x10_SFLOAT_BLOCK_EXT = ASTC_12x10_SFLOAT_BLOCK, + ASTC_12x12_SFLOAT_BLOCK_EXT = ASTC_12x12_SFLOAT_BLOCK, G8B8G8R8_422_UNORM_KHR = G8B8G8R8_422_UNORM, B8G8R8G8_422_UNORM_KHR = B8G8R8G8_422_UNORM, G8_B8_R8_3PLANE_420_UNORM_KHR = G8_B8_R8_3PLANE_420_UNORM, @@ -1067,6 +1103,12 @@ Format :: enum c.int { G16_B16_R16_3PLANE_422_UNORM_KHR = G16_B16_R16_3PLANE_422_UNORM, G16_B16R16_2PLANE_422_UNORM_KHR = G16_B16R16_2PLANE_422_UNORM, G16_B16_R16_3PLANE_444_UNORM_KHR = G16_B16_R16_3PLANE_444_UNORM, + G8_B8R8_2PLANE_444_UNORM_EXT = G8_B8R8_2PLANE_444_UNORM, + G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16_EXT = G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16, + G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16_EXT = G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16, + G16_B16R16_2PLANE_444_UNORM_EXT = G16_B16R16_2PLANE_444_UNORM, + A4R4G4B4_UNORM_PACK16_EXT = A4R4G4B4_UNORM_PACK16, + A4B4G4R4_UNORM_PACK16_EXT = A4B4G4R4_UNORM_PACK16, } FormatFeatureFlags :: distinct bit_set[FormatFeatureFlag; Flags] @@ -1190,6 +1232,14 @@ GeometryTypeKHR :: enum c.int { AABBS_NV = AABBS, } +GraphicsPipelineLibraryFlagsEXT :: distinct bit_set[GraphicsPipelineLibraryFlagEXT; Flags] +GraphicsPipelineLibraryFlagEXT :: enum Flags { + VERTEX_INPUT_INTERFACE = 0, + PRE_RASTERIZATION_SHADERS = 1, + FRAGMENT_SHADER = 2, + FRAGMENT_OUTPUT_INTERFACE = 3, +} + ImageAspectFlags :: distinct bit_set[ImageAspectFlag; Flags] ImageAspectFlag :: enum Flags { COLOR = 0, @@ -1208,6 +1258,9 @@ ImageAspectFlag :: enum Flags { PLANE_2_KHR = PLANE_2, } +ImageAspectFlags_NONE :: ImageAspectFlags{} + + ImageCreateFlags :: distinct bit_set[ImageCreateFlag; Flags] ImageCreateFlag :: enum Flags { SPARSE_BINDING = 0, @@ -1225,6 +1278,8 @@ ImageCreateFlag :: enum Flags { CORNER_SAMPLED_NV = 13, SAMPLE_LOCATIONS_COMPATIBLE_DEPTH_EXT = 12, SUBSAMPLED_EXT = 14, + D2_VIEW_COMPATIBLE_EXT = 17, + FRAGMENT_DENSITY_MAP_OFFSET_QCOM = 15, SPLIT_INSTANCE_BIND_REGIONS_KHR = SPLIT_INSTANCE_BIND_REGIONS, D2_ARRAY_COMPATIBLE_KHR = D2_ARRAY_COMPATIBLE, BLOCK_TEXEL_VIEW_COMPATIBLE_KHR = BLOCK_TEXEL_VIEW_COMPATIBLE, @@ -1249,6 +1304,8 @@ ImageLayout :: enum c.int { DEPTH_READ_ONLY_OPTIMAL = 1000241001, STENCIL_ATTACHMENT_OPTIMAL = 1000241002, STENCIL_READ_ONLY_OPTIMAL = 1000241003, + READ_ONLY_OPTIMAL = 1000314000, + ATTACHMENT_OPTIMAL = 1000314001, PRESENT_SRC_KHR = 1000001002, VIDEO_DECODE_DST_KHR = 1000024000, VIDEO_DECODE_SRC_KHR = 1000024001, @@ -1259,8 +1316,6 @@ ImageLayout :: enum c.int { VIDEO_ENCODE_DST_KHR = 1000299000, VIDEO_ENCODE_SRC_KHR = 1000299001, VIDEO_ENCODE_DPB_KHR = 1000299002, - READ_ONLY_OPTIMAL_KHR = 1000314000, - ATTACHMENT_OPTIMAL_KHR = 1000314001, DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL_KHR = DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL, DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL_KHR = DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL, SHADING_RATE_OPTIMAL_NV = FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR, @@ -1268,6 +1323,8 @@ ImageLayout :: enum c.int { DEPTH_READ_ONLY_OPTIMAL_KHR = DEPTH_READ_ONLY_OPTIMAL, STENCIL_ATTACHMENT_OPTIMAL_KHR = STENCIL_ATTACHMENT_OPTIMAL, STENCIL_READ_ONLY_OPTIMAL_KHR = STENCIL_READ_ONLY_OPTIMAL, + READ_ONLY_OPTIMAL_KHR = READ_ONLY_OPTIMAL, + ATTACHMENT_OPTIMAL_KHR = ATTACHMENT_OPTIMAL, } ImageTiling :: enum c.int { @@ -1351,6 +1408,11 @@ IndirectStateFlagNV :: enum Flags { FLAG_FRONTFACE = 0, } +InstanceCreateFlags :: distinct bit_set[InstanceCreateFlag; Flags] +InstanceCreateFlag :: enum Flags { + ENUMERATE_PORTABILITY_KHR = 0, +} + InternalAllocationType :: enum c.int { EXECUTABLE = 0, } @@ -1446,6 +1508,7 @@ ObjectType :: enum c.int { COMMAND_POOL = 25, SAMPLER_YCBCR_CONVERSION = 1000156000, DESCRIPTOR_UPDATE_TEMPLATE = 1000085000, + PRIVATE_DATA_SLOT = 1000295000, SURFACE_KHR = 1000000000, SWAPCHAIN_KHR = 1000001000, DISPLAY_KHR = 1000002000, @@ -1462,9 +1525,10 @@ ObjectType :: enum c.int { PERFORMANCE_CONFIGURATION_INTEL = 1000210000, DEFERRED_OPERATION_KHR = 1000268000, INDIRECT_COMMANDS_LAYOUT_NV = 1000277000, - PRIVATE_DATA_SLOT_EXT = 1000295000, + BUFFER_COLLECTION_FUCHSIA = 1000366000, DESCRIPTOR_UPDATE_TEMPLATE_KHR = DESCRIPTOR_UPDATE_TEMPLATE, SAMPLER_YCBCR_CONVERSION_KHR = SAMPLER_YCBCR_CONVERSION, + PRIVATE_DATA_SLOT_EXT = PRIVATE_DATA_SLOT, } PeerMemoryFeatureFlags :: distinct bit_set[PeerMemoryFeatureFlag; Flags] @@ -1557,48 +1621,71 @@ PipelineBindPoint :: enum c.int { PipelineCacheCreateFlags :: distinct bit_set[PipelineCacheCreateFlag; Flags] PipelineCacheCreateFlag :: enum Flags { - EXTERNALLY_SYNCHRONIZED_EXT = 0, + EXTERNALLY_SYNCHRONIZED = 0, + EXTERNALLY_SYNCHRONIZED_EXT = EXTERNALLY_SYNCHRONIZED, } PipelineCacheHeaderVersion :: enum c.int { ONE = 1, } +PipelineColorBlendStateCreateFlags :: distinct bit_set[PipelineColorBlendStateCreateFlag; Flags] +PipelineColorBlendStateCreateFlag :: enum Flags { + RASTERIZATION_ORDER_ATTACHMENT_ACCESS_ARM = 0, +} + PipelineCompilerControlFlagsAMD :: distinct bit_set[PipelineCompilerControlFlagAMD; Flags] PipelineCompilerControlFlagAMD :: enum Flags { } PipelineCreateFlags :: distinct bit_set[PipelineCreateFlag; Flags] PipelineCreateFlag :: enum Flags { - DISABLE_OPTIMIZATION = 0, - ALLOW_DERIVATIVES = 1, - DERIVATIVE = 2, - VIEW_INDEX_FROM_DEVICE_INDEX = 3, - DISPATCH_BASE = 4, - RAY_TRACING_NO_NULL_ANY_HIT_SHADERS_KHR = 14, - RAY_TRACING_NO_NULL_CLOSEST_HIT_SHADERS_KHR = 15, - RAY_TRACING_NO_NULL_MISS_SHADERS_KHR = 16, - RAY_TRACING_NO_NULL_INTERSECTION_SHADERS_KHR = 17, - RAY_TRACING_SKIP_TRIANGLES_KHR = 12, - RAY_TRACING_SKIP_AABBS_KHR = 13, - RAY_TRACING_SHADER_GROUP_HANDLE_CAPTURE_REPLAY_KHR = 19, - DEFER_COMPILE_NV = 5, - CAPTURE_STATISTICS_KHR = 6, - CAPTURE_INTERNAL_REPRESENTATIONS_KHR = 7, - INDIRECT_BINDABLE_NV = 18, - LIBRARY_KHR = 11, - FAIL_ON_PIPELINE_COMPILE_REQUIRED_EXT = 8, - EARLY_RETURN_ON_FAILURE_EXT = 9, - RAY_TRACING_ALLOW_MOTION_NV = 20, - VIEW_INDEX_FROM_DEVICE_INDEX_KHR = VIEW_INDEX_FROM_DEVICE_INDEX, - DISPATCH_BASE_KHR = DISPATCH_BASE, + DISABLE_OPTIMIZATION = 0, + ALLOW_DERIVATIVES = 1, + DERIVATIVE = 2, + VIEW_INDEX_FROM_DEVICE_INDEX = 3, + DISPATCH_BASE = 4, + FAIL_ON_PIPELINE_COMPILE_REQUIRED = 8, + EARLY_RETURN_ON_FAILURE = 9, + RENDERING_FRAGMENT_SHADING_RATE_ATTACHMENT_KHR = 21, + RENDERING_FRAGMENT_DENSITY_MAP_ATTACHMENT_EXT = 22, + RAY_TRACING_NO_NULL_ANY_HIT_SHADERS_KHR = 14, + RAY_TRACING_NO_NULL_CLOSEST_HIT_SHADERS_KHR = 15, + RAY_TRACING_NO_NULL_MISS_SHADERS_KHR = 16, + RAY_TRACING_NO_NULL_INTERSECTION_SHADERS_KHR = 17, + RAY_TRACING_SKIP_TRIANGLES_KHR = 12, + RAY_TRACING_SKIP_AABBS_KHR = 13, + RAY_TRACING_SHADER_GROUP_HANDLE_CAPTURE_REPLAY_KHR = 19, + DEFER_COMPILE_NV = 5, + CAPTURE_STATISTICS_KHR = 6, + CAPTURE_INTERNAL_REPRESENTATIONS_KHR = 7, + INDIRECT_BINDABLE_NV = 18, + LIBRARY_KHR = 11, + RETAIN_LINK_TIME_OPTIMIZATION_INFO_EXT = 23, + LINK_TIME_OPTIMIZATION_EXT = 10, + RAY_TRACING_ALLOW_MOTION_NV = 20, + PIPELINE_RASTERIZATION_STATE_CREATE_FRAGMENT_SHADING_RATE_ATTACHMENT_KHR = RENDERING_FRAGMENT_SHADING_RATE_ATTACHMENT_KHR, + PIPELINE_RASTERIZATION_STATE_CREATE_FRAGMENT_DENSITY_MAP_ATTACHMENT_EXT = RENDERING_FRAGMENT_DENSITY_MAP_ATTACHMENT_EXT, + VIEW_INDEX_FROM_DEVICE_INDEX_KHR = VIEW_INDEX_FROM_DEVICE_INDEX, + DISPATCH_BASE_KHR = DISPATCH_BASE, + FAIL_ON_PIPELINE_COMPILE_REQUIRED_EXT = FAIL_ON_PIPELINE_COMPILE_REQUIRED, + EARLY_RETURN_ON_FAILURE_EXT = EARLY_RETURN_ON_FAILURE, } -PipelineCreationFeedbackFlagsEXT :: distinct bit_set[PipelineCreationFeedbackFlagEXT; Flags] -PipelineCreationFeedbackFlagEXT :: enum Flags { - VALID = 0, - APPLICATION_PIPELINE_CACHE_HIT = 1, - BASE_PIPELINE_ACCELERATION = 2, +PipelineCreationFeedbackFlags :: distinct bit_set[PipelineCreationFeedbackFlag; Flags] +PipelineCreationFeedbackFlag :: enum Flags { + VALID = 0, + APPLICATION_PIPELINE_CACHE_HIT = 1, + BASE_PIPELINE_ACCELERATION = 2, + VALID_EXT = VALID, + APPLICATION_PIPELINE_CACHE_HIT_EXT = APPLICATION_PIPELINE_CACHE_HIT, + BASE_PIPELINE_ACCELERATION_EXT = BASE_PIPELINE_ACCELERATION, +} + +PipelineDepthStencilStateCreateFlags :: distinct bit_set[PipelineDepthStencilStateCreateFlag; Flags] +PipelineDepthStencilStateCreateFlag :: enum Flags { + RASTERIZATION_ORDER_ATTACHMENT_DEPTH_ACCESS_ARM = 0, + RASTERIZATION_ORDER_ATTACHMENT_STENCIL_ACCESS_ARM = 1, } PipelineExecutableStatisticFormatKHR :: enum c.int { @@ -1608,10 +1695,17 @@ PipelineExecutableStatisticFormatKHR :: enum c.int { FLOAT64 = 3, } +PipelineLayoutCreateFlags :: distinct bit_set[PipelineLayoutCreateFlag; Flags] +PipelineLayoutCreateFlag :: enum Flags { + INDEPENDENT_SETS_EXT = 1, +} + PipelineShaderStageCreateFlags :: distinct bit_set[PipelineShaderStageCreateFlag; Flags] PipelineShaderStageCreateFlag :: enum Flags { - ALLOW_VARYING_SUBGROUP_SIZE_EXT = 0, - REQUIRE_FULL_SUBGROUPS_EXT = 1, + ALLOW_VARYING_SUBGROUP_SIZE = 0, + REQUIRE_FULL_SUBGROUPS = 1, + ALLOW_VARYING_SUBGROUP_SIZE_EXT = ALLOW_VARYING_SUBGROUP_SIZE, + REQUIRE_FULL_SUBGROUPS_EXT = REQUIRE_FULL_SUBGROUPS, } PipelineStageFlags :: distinct bit_set[PipelineStageFlag; Flags] @@ -1647,7 +1741,7 @@ PipelineStageFlag :: enum Flags { ACCELERATION_STRUCTURE_BUILD_NV = ACCELERATION_STRUCTURE_BUILD_KHR, } -PipelineStageFlags_NONE_KHR :: PipelineStageFlags{} +PipelineStageFlags_NONE :: PipelineStageFlags{} PointClippingBehavior :: enum c.int { @@ -1687,10 +1781,6 @@ PrimitiveTopology :: enum c.int { PATCH_LIST = 10, } -PrivateDataSlotCreateFlagsEXT :: distinct bit_set[PrivateDataSlotCreateFlagEXT; Flags] -PrivateDataSlotCreateFlagEXT :: enum Flags { -} - ProvokingVertexModeEXT :: enum c.int { FIRST_VERTEX = 0, LAST_VERTEX = 1, @@ -1741,6 +1831,7 @@ QueryType :: enum c.int { ACCELERATION_STRUCTURE_COMPACTED_SIZE_NV = 1000165000, PERFORMANCE_QUERY_INTEL = 1000210000, VIDEO_ENCODE_BITSTREAM_BUFFER_RANGE_KHR = 1000299000, + PRIMITIVES_GENERATED_EXT = 1000382000, } QueueFlags :: distinct bit_set[QueueFlag; Flags] @@ -1754,11 +1845,15 @@ QueueFlag :: enum Flags { VIDEO_ENCODE_KHR = 6, } -QueueGlobalPriorityEXT :: enum c.int { - LOW = 128, - MEDIUM = 256, - HIGH = 512, - REALTIME = 1024, +QueueGlobalPriorityKHR :: enum c.int { + LOW = 128, + MEDIUM = 256, + HIGH = 512, + REALTIME = 1024, + LOW_EXT = LOW, + MEDIUM_EXT = MEDIUM, + HIGH_EXT = HIGH, + REALTIME_EXT = REALTIME, } RasterizationOrderAMD :: enum c.int { @@ -1780,6 +1875,16 @@ RenderPassCreateFlag :: enum Flags { TRANSFORM_QCOM = 1, } +RenderingFlags :: distinct bit_set[RenderingFlag; Flags] +RenderingFlag :: enum Flags { + CONTENTS_SECONDARY_COMMAND_BUFFERS = 0, + SUSPENDING = 1, + RESUMING = 2, + CONTENTS_SECONDARY_COMMAND_BUFFERS_KHR = CONTENTS_SECONDARY_COMMAND_BUFFERS, + SUSPENDING_KHR = SUSPENDING, + RESUMING_KHR = RESUMING, +} + ResolveModeFlags :: distinct bit_set[ResolveModeFlag; Flags] ResolveModeFlag :: enum Flags { SAMPLE_ZERO = 0, @@ -1819,6 +1924,7 @@ Result :: enum c.int { ERROR_INVALID_EXTERNAL_HANDLE = -1000072003, ERROR_FRAGMENTATION = -1000161000, ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS = -1000257000, + PIPELINE_COMPILE_REQUIRED = 1000297000, ERROR_SURFACE_LOST_KHR = -1000000000, ERROR_NATIVE_WINDOW_IN_USE_KHR = -1000000001, SUBOPTIMAL_KHR = 1000001003, @@ -1827,19 +1933,20 @@ Result :: enum c.int { ERROR_VALIDATION_FAILED_EXT = -1000011001, ERROR_INVALID_SHADER_NV = -1000012000, ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT = -1000158000, - ERROR_NOT_PERMITTED_EXT = -1000174001, + ERROR_NOT_PERMITTED_KHR = -1000174001, ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT = -1000255000, THREAD_IDLE_KHR = 1000268000, THREAD_DONE_KHR = 1000268001, OPERATION_DEFERRED_KHR = 1000268002, OPERATION_NOT_DEFERRED_KHR = 1000268003, - PIPELINE_COMPILE_REQUIRED_EXT = 1000297000, ERROR_OUT_OF_POOL_MEMORY_KHR = ERROR_OUT_OF_POOL_MEMORY, ERROR_INVALID_EXTERNAL_HANDLE_KHR = ERROR_INVALID_EXTERNAL_HANDLE, ERROR_FRAGMENTATION_EXT = ERROR_FRAGMENTATION, + ERROR_NOT_PERMITTED_EXT = ERROR_NOT_PERMITTED_KHR, ERROR_INVALID_DEVICE_ADDRESS_EXT = ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS, ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS_KHR = ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS, - ERROR_PIPELINE_COMPILE_REQUIRED_EXT = PIPELINE_COMPILE_REQUIRED_EXT, + PIPELINE_COMPILE_REQUIRED_EXT = PIPELINE_COMPILE_REQUIRED, + ERROR_PIPELINE_COMPILE_REQUIRED_EXT = PIPELINE_COMPILE_REQUIRED, } SampleCountFlags :: distinct bit_set[SampleCountFlag; Flags] @@ -2036,692 +2143,809 @@ StencilOp :: enum c.int { } StructureType :: enum c.int { - APPLICATION_INFO = 0, - INSTANCE_CREATE_INFO = 1, - DEVICE_QUEUE_CREATE_INFO = 2, - DEVICE_CREATE_INFO = 3, - SUBMIT_INFO = 4, - MEMORY_ALLOCATE_INFO = 5, - MAPPED_MEMORY_RANGE = 6, - BIND_SPARSE_INFO = 7, - FENCE_CREATE_INFO = 8, - SEMAPHORE_CREATE_INFO = 9, - EVENT_CREATE_INFO = 10, - QUERY_POOL_CREATE_INFO = 11, - BUFFER_CREATE_INFO = 12, - BUFFER_VIEW_CREATE_INFO = 13, - IMAGE_CREATE_INFO = 14, - IMAGE_VIEW_CREATE_INFO = 15, - SHADER_MODULE_CREATE_INFO = 16, - PIPELINE_CACHE_CREATE_INFO = 17, - PIPELINE_SHADER_STAGE_CREATE_INFO = 18, - PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO = 19, - PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO = 20, - PIPELINE_TESSELLATION_STATE_CREATE_INFO = 21, - PIPELINE_VIEWPORT_STATE_CREATE_INFO = 22, - PIPELINE_RASTERIZATION_STATE_CREATE_INFO = 23, - PIPELINE_MULTISAMPLE_STATE_CREATE_INFO = 24, - PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO = 25, - PIPELINE_COLOR_BLEND_STATE_CREATE_INFO = 26, - PIPELINE_DYNAMIC_STATE_CREATE_INFO = 27, - GRAPHICS_PIPELINE_CREATE_INFO = 28, - COMPUTE_PIPELINE_CREATE_INFO = 29, - PIPELINE_LAYOUT_CREATE_INFO = 30, - SAMPLER_CREATE_INFO = 31, - DESCRIPTOR_SET_LAYOUT_CREATE_INFO = 32, - DESCRIPTOR_POOL_CREATE_INFO = 33, - DESCRIPTOR_SET_ALLOCATE_INFO = 34, - WRITE_DESCRIPTOR_SET = 35, - COPY_DESCRIPTOR_SET = 36, - FRAMEBUFFER_CREATE_INFO = 37, - RENDER_PASS_CREATE_INFO = 38, - COMMAND_POOL_CREATE_INFO = 39, - COMMAND_BUFFER_ALLOCATE_INFO = 40, - COMMAND_BUFFER_INHERITANCE_INFO = 41, - COMMAND_BUFFER_BEGIN_INFO = 42, - RENDER_PASS_BEGIN_INFO = 43, - BUFFER_MEMORY_BARRIER = 44, - IMAGE_MEMORY_BARRIER = 45, - MEMORY_BARRIER = 46, - LOADER_INSTANCE_CREATE_INFO = 47, - LOADER_DEVICE_CREATE_INFO = 48, - PHYSICAL_DEVICE_SUBGROUP_PROPERTIES = 1000094000, - BIND_BUFFER_MEMORY_INFO = 1000157000, - BIND_IMAGE_MEMORY_INFO = 1000157001, - PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES = 1000083000, - MEMORY_DEDICATED_REQUIREMENTS = 1000127000, - MEMORY_DEDICATED_ALLOCATE_INFO = 1000127001, - MEMORY_ALLOCATE_FLAGS_INFO = 1000060000, - DEVICE_GROUP_RENDER_PASS_BEGIN_INFO = 1000060003, - DEVICE_GROUP_COMMAND_BUFFER_BEGIN_INFO = 1000060004, - DEVICE_GROUP_SUBMIT_INFO = 1000060005, - DEVICE_GROUP_BIND_SPARSE_INFO = 1000060006, - BIND_BUFFER_MEMORY_DEVICE_GROUP_INFO = 1000060013, - BIND_IMAGE_MEMORY_DEVICE_GROUP_INFO = 1000060014, - PHYSICAL_DEVICE_GROUP_PROPERTIES = 1000070000, - DEVICE_GROUP_DEVICE_CREATE_INFO = 1000070001, - BUFFER_MEMORY_REQUIREMENTS_INFO_2 = 1000146000, - IMAGE_MEMORY_REQUIREMENTS_INFO_2 = 1000146001, - IMAGE_SPARSE_MEMORY_REQUIREMENTS_INFO_2 = 1000146002, - MEMORY_REQUIREMENTS_2 = 1000146003, - SPARSE_IMAGE_MEMORY_REQUIREMENTS_2 = 1000146004, - PHYSICAL_DEVICE_FEATURES_2 = 1000059000, - PHYSICAL_DEVICE_PROPERTIES_2 = 1000059001, - FORMAT_PROPERTIES_2 = 1000059002, - IMAGE_FORMAT_PROPERTIES_2 = 1000059003, - PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2 = 1000059004, - QUEUE_FAMILY_PROPERTIES_2 = 1000059005, - PHYSICAL_DEVICE_MEMORY_PROPERTIES_2 = 1000059006, - SPARSE_IMAGE_FORMAT_PROPERTIES_2 = 1000059007, - PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2 = 1000059008, - PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES = 1000117000, - RENDER_PASS_INPUT_ATTACHMENT_ASPECT_CREATE_INFO = 1000117001, - IMAGE_VIEW_USAGE_CREATE_INFO = 1000117002, - PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO = 1000117003, - RENDER_PASS_MULTIVIEW_CREATE_INFO = 1000053000, - PHYSICAL_DEVICE_MULTIVIEW_FEATURES = 1000053001, - PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES = 1000053002, - PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES = 1000120000, - PROTECTED_SUBMIT_INFO = 1000145000, - PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES = 1000145001, - PHYSICAL_DEVICE_PROTECTED_MEMORY_PROPERTIES = 1000145002, - DEVICE_QUEUE_INFO_2 = 1000145003, - SAMPLER_YCBCR_CONVERSION_CREATE_INFO = 1000156000, - SAMPLER_YCBCR_CONVERSION_INFO = 1000156001, - BIND_IMAGE_PLANE_MEMORY_INFO = 1000156002, - IMAGE_PLANE_MEMORY_REQUIREMENTS_INFO = 1000156003, - PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES = 1000156004, - SAMPLER_YCBCR_CONVERSION_IMAGE_FORMAT_PROPERTIES = 1000156005, - DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO = 1000085000, - PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO = 1000071000, - EXTERNAL_IMAGE_FORMAT_PROPERTIES = 1000071001, - PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO = 1000071002, - EXTERNAL_BUFFER_PROPERTIES = 1000071003, - PHYSICAL_DEVICE_ID_PROPERTIES = 1000071004, - EXTERNAL_MEMORY_BUFFER_CREATE_INFO = 1000072000, - EXTERNAL_MEMORY_IMAGE_CREATE_INFO = 1000072001, - EXPORT_MEMORY_ALLOCATE_INFO = 1000072002, - PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO = 1000112000, - EXTERNAL_FENCE_PROPERTIES = 1000112001, - EXPORT_FENCE_CREATE_INFO = 1000113000, - EXPORT_SEMAPHORE_CREATE_INFO = 1000077000, - PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO = 1000076000, - EXTERNAL_SEMAPHORE_PROPERTIES = 1000076001, - PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES = 1000168000, - DESCRIPTOR_SET_LAYOUT_SUPPORT = 1000168001, - PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES = 1000063000, - PHYSICAL_DEVICE_VULKAN_1_1_FEATURES = 49, - PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES = 50, - PHYSICAL_DEVICE_VULKAN_1_2_FEATURES = 51, - PHYSICAL_DEVICE_VULKAN_1_2_PROPERTIES = 52, - IMAGE_FORMAT_LIST_CREATE_INFO = 1000147000, - ATTACHMENT_DESCRIPTION_2 = 1000109000, - ATTACHMENT_REFERENCE_2 = 1000109001, - SUBPASS_DESCRIPTION_2 = 1000109002, - SUBPASS_DEPENDENCY_2 = 1000109003, - RENDER_PASS_CREATE_INFO_2 = 1000109004, - SUBPASS_BEGIN_INFO = 1000109005, - SUBPASS_END_INFO = 1000109006, - PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES = 1000177000, - PHYSICAL_DEVICE_DRIVER_PROPERTIES = 1000196000, - PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES = 1000180000, - PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES = 1000082000, - PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES = 1000197000, - DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO = 1000161000, - PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES = 1000161001, - PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES = 1000161002, - DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO = 1000161003, - DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_LAYOUT_SUPPORT = 1000161004, - PHYSICAL_DEVICE_DEPTH_STENCIL_RESOLVE_PROPERTIES = 1000199000, - SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE = 1000199001, - PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES = 1000221000, - IMAGE_STENCIL_USAGE_CREATE_INFO = 1000246000, - PHYSICAL_DEVICE_SAMPLER_FILTER_MINMAX_PROPERTIES = 1000130000, - SAMPLER_REDUCTION_MODE_CREATE_INFO = 1000130001, - PHYSICAL_DEVICE_VULKAN_MEMORY_MODEL_FEATURES = 1000211000, - PHYSICAL_DEVICE_IMAGELESS_FRAMEBUFFER_FEATURES = 1000108000, - FRAMEBUFFER_ATTACHMENTS_CREATE_INFO = 1000108001, - FRAMEBUFFER_ATTACHMENT_IMAGE_INFO = 1000108002, - RENDER_PASS_ATTACHMENT_BEGIN_INFO = 1000108003, - PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES = 1000253000, - PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES = 1000175000, - PHYSICAL_DEVICE_SEPARATE_DEPTH_STENCIL_LAYOUTS_FEATURES = 1000241000, - ATTACHMENT_REFERENCE_STENCIL_LAYOUT = 1000241001, - ATTACHMENT_DESCRIPTION_STENCIL_LAYOUT = 1000241002, - PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES = 1000261000, - PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES = 1000207000, - PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_PROPERTIES = 1000207001, - SEMAPHORE_TYPE_CREATE_INFO = 1000207002, - TIMELINE_SEMAPHORE_SUBMIT_INFO = 1000207003, - SEMAPHORE_WAIT_INFO = 1000207004, - SEMAPHORE_SIGNAL_INFO = 1000207005, - PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES = 1000257000, - BUFFER_DEVICE_ADDRESS_INFO = 1000244001, - BUFFER_OPAQUE_CAPTURE_ADDRESS_CREATE_INFO = 1000257002, - MEMORY_OPAQUE_CAPTURE_ADDRESS_ALLOCATE_INFO = 1000257003, - DEVICE_MEMORY_OPAQUE_CAPTURE_ADDRESS_INFO = 1000257004, - SWAPCHAIN_CREATE_INFO_KHR = 1000001000, - PRESENT_INFO_KHR = 1000001001, - DEVICE_GROUP_PRESENT_CAPABILITIES_KHR = 1000060007, - IMAGE_SWAPCHAIN_CREATE_INFO_KHR = 1000060008, - BIND_IMAGE_MEMORY_SWAPCHAIN_INFO_KHR = 1000060009, - ACQUIRE_NEXT_IMAGE_INFO_KHR = 1000060010, - DEVICE_GROUP_PRESENT_INFO_KHR = 1000060011, - DEVICE_GROUP_SWAPCHAIN_CREATE_INFO_KHR = 1000060012, - DISPLAY_MODE_CREATE_INFO_KHR = 1000002000, - DISPLAY_SURFACE_CREATE_INFO_KHR = 1000002001, - DISPLAY_PRESENT_INFO_KHR = 1000003000, - XLIB_SURFACE_CREATE_INFO_KHR = 1000004000, - XCB_SURFACE_CREATE_INFO_KHR = 1000005000, - WAYLAND_SURFACE_CREATE_INFO_KHR = 1000006000, - ANDROID_SURFACE_CREATE_INFO_KHR = 1000008000, - WIN32_SURFACE_CREATE_INFO_KHR = 1000009000, - DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT = 1000011000, - PIPELINE_RASTERIZATION_STATE_RASTERIZATION_ORDER_AMD = 1000018000, - DEBUG_MARKER_OBJECT_NAME_INFO_EXT = 1000022000, - DEBUG_MARKER_OBJECT_TAG_INFO_EXT = 1000022001, - DEBUG_MARKER_MARKER_INFO_EXT = 1000022002, - VIDEO_PROFILE_KHR = 1000023000, - VIDEO_CAPABILITIES_KHR = 1000023001, - VIDEO_PICTURE_RESOURCE_KHR = 1000023002, - VIDEO_GET_MEMORY_PROPERTIES_KHR = 1000023003, - VIDEO_BIND_MEMORY_KHR = 1000023004, - VIDEO_SESSION_CREATE_INFO_KHR = 1000023005, - VIDEO_SESSION_PARAMETERS_CREATE_INFO_KHR = 1000023006, - VIDEO_SESSION_PARAMETERS_UPDATE_INFO_KHR = 1000023007, - VIDEO_BEGIN_CODING_INFO_KHR = 1000023008, - VIDEO_END_CODING_INFO_KHR = 1000023009, - VIDEO_CODING_CONTROL_INFO_KHR = 1000023010, - VIDEO_REFERENCE_SLOT_KHR = 1000023011, - VIDEO_QUEUE_FAMILY_PROPERTIES_2_KHR = 1000023012, - VIDEO_PROFILES_KHR = 1000023013, - PHYSICAL_DEVICE_VIDEO_FORMAT_INFO_KHR = 1000023014, - VIDEO_FORMAT_PROPERTIES_KHR = 1000023015, - VIDEO_DECODE_INFO_KHR = 1000024000, - DEDICATED_ALLOCATION_IMAGE_CREATE_INFO_NV = 1000026000, - DEDICATED_ALLOCATION_BUFFER_CREATE_INFO_NV = 1000026001, - DEDICATED_ALLOCATION_MEMORY_ALLOCATE_INFO_NV = 1000026002, - PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT = 1000028000, - PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_PROPERTIES_EXT = 1000028001, - PIPELINE_RASTERIZATION_STATE_STREAM_CREATE_INFO_EXT = 1000028002, - CU_MODULE_CREATE_INFO_NVX = 1000029000, - CU_FUNCTION_CREATE_INFO_NVX = 1000029001, - CU_LAUNCH_INFO_NVX = 1000029002, - IMAGE_VIEW_HANDLE_INFO_NVX = 1000030000, - IMAGE_VIEW_ADDRESS_PROPERTIES_NVX = 1000030001, - VIDEO_ENCODE_H264_CAPABILITIES_EXT = 1000038000, - VIDEO_ENCODE_H264_SESSION_CREATE_INFO_EXT = 1000038001, - VIDEO_ENCODE_H264_SESSION_PARAMETERS_CREATE_INFO_EXT = 1000038002, - VIDEO_ENCODE_H264_SESSION_PARAMETERS_ADD_INFO_EXT = 1000038003, - VIDEO_ENCODE_H264_VCL_FRAME_INFO_EXT = 1000038004, - VIDEO_ENCODE_H264_DPB_SLOT_INFO_EXT = 1000038005, - VIDEO_ENCODE_H264_NALU_SLICE_EXT = 1000038006, - VIDEO_ENCODE_H264_EMIT_PICTURE_PARAMETERS_EXT = 1000038007, - VIDEO_ENCODE_H264_PROFILE_EXT = 1000038008, - VIDEO_DECODE_H264_CAPABILITIES_EXT = 1000040000, - VIDEO_DECODE_H264_SESSION_CREATE_INFO_EXT = 1000040001, - VIDEO_DECODE_H264_PICTURE_INFO_EXT = 1000040002, - VIDEO_DECODE_H264_MVC_EXT = 1000040003, - VIDEO_DECODE_H264_PROFILE_EXT = 1000040004, - VIDEO_DECODE_H264_SESSION_PARAMETERS_CREATE_INFO_EXT = 1000040005, - VIDEO_DECODE_H264_SESSION_PARAMETERS_ADD_INFO_EXT = 1000040006, - VIDEO_DECODE_H264_DPB_SLOT_INFO_EXT = 1000040007, - TEXTURE_LOD_GATHER_FORMAT_PROPERTIES_AMD = 1000041000, - STREAM_DESCRIPTOR_SURFACE_CREATE_INFO_GGP = 1000049000, - PHYSICAL_DEVICE_CORNER_SAMPLED_IMAGE_FEATURES_NV = 1000050000, - EXTERNAL_MEMORY_IMAGE_CREATE_INFO_NV = 1000056000, - EXPORT_MEMORY_ALLOCATE_INFO_NV = 1000056001, - IMPORT_MEMORY_WIN32_HANDLE_INFO_NV = 1000057000, - EXPORT_MEMORY_WIN32_HANDLE_INFO_NV = 1000057001, - WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_NV = 1000058000, - VALIDATION_FLAGS_EXT = 1000061000, - VI_SURFACE_CREATE_INFO_NN = 1000062000, - PHYSICAL_DEVICE_TEXTURE_COMPRESSION_ASTC_HDR_FEATURES_EXT = 1000066000, - IMAGE_VIEW_ASTC_DECODE_MODE_EXT = 1000067000, - PHYSICAL_DEVICE_ASTC_DECODE_FEATURES_EXT = 1000067001, - IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR = 1000073000, - EXPORT_MEMORY_WIN32_HANDLE_INFO_KHR = 1000073001, - MEMORY_WIN32_HANDLE_PROPERTIES_KHR = 1000073002, - MEMORY_GET_WIN32_HANDLE_INFO_KHR = 1000073003, - IMPORT_MEMORY_FD_INFO_KHR = 1000074000, - MEMORY_FD_PROPERTIES_KHR = 1000074001, - MEMORY_GET_FD_INFO_KHR = 1000074002, - WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_KHR = 1000075000, - IMPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHR = 1000078000, - EXPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHR = 1000078001, - D3D12_FENCE_SUBMIT_INFO_KHR = 1000078002, - SEMAPHORE_GET_WIN32_HANDLE_INFO_KHR = 1000078003, - IMPORT_SEMAPHORE_FD_INFO_KHR = 1000079000, - SEMAPHORE_GET_FD_INFO_KHR = 1000079001, - PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR = 1000080000, - COMMAND_BUFFER_INHERITANCE_CONDITIONAL_RENDERING_INFO_EXT = 1000081000, - PHYSICAL_DEVICE_CONDITIONAL_RENDERING_FEATURES_EXT = 1000081001, - CONDITIONAL_RENDERING_BEGIN_INFO_EXT = 1000081002, - PRESENT_REGIONS_KHR = 1000084000, - PIPELINE_VIEWPORT_W_SCALING_STATE_CREATE_INFO_NV = 1000087000, - SURFACE_CAPABILITIES_2_EXT = 1000090000, - DISPLAY_POWER_INFO_EXT = 1000091000, - DEVICE_EVENT_INFO_EXT = 1000091001, - DISPLAY_EVENT_INFO_EXT = 1000091002, - SWAPCHAIN_COUNTER_CREATE_INFO_EXT = 1000091003, - PRESENT_TIMES_INFO_GOOGLE = 1000092000, - PHYSICAL_DEVICE_MULTIVIEW_PER_VIEW_ATTRIBUTES_PROPERTIES_NVX = 1000097000, - PIPELINE_VIEWPORT_SWIZZLE_STATE_CREATE_INFO_NV = 1000098000, - PHYSICAL_DEVICE_DISCARD_RECTANGLE_PROPERTIES_EXT = 1000099000, - PIPELINE_DISCARD_RECTANGLE_STATE_CREATE_INFO_EXT = 1000099001, - PHYSICAL_DEVICE_CONSERVATIVE_RASTERIZATION_PROPERTIES_EXT = 1000101000, - PIPELINE_RASTERIZATION_CONSERVATIVE_STATE_CREATE_INFO_EXT = 1000101001, - PHYSICAL_DEVICE_DEPTH_CLIP_ENABLE_FEATURES_EXT = 1000102000, - PIPELINE_RASTERIZATION_DEPTH_CLIP_STATE_CREATE_INFO_EXT = 1000102001, - HDR_METADATA_EXT = 1000105000, - SHARED_PRESENT_SURFACE_CAPABILITIES_KHR = 1000111000, - IMPORT_FENCE_WIN32_HANDLE_INFO_KHR = 1000114000, - EXPORT_FENCE_WIN32_HANDLE_INFO_KHR = 1000114001, - FENCE_GET_WIN32_HANDLE_INFO_KHR = 1000114002, - IMPORT_FENCE_FD_INFO_KHR = 1000115000, - FENCE_GET_FD_INFO_KHR = 1000115001, - PHYSICAL_DEVICE_PERFORMANCE_QUERY_FEATURES_KHR = 1000116000, - PHYSICAL_DEVICE_PERFORMANCE_QUERY_PROPERTIES_KHR = 1000116001, - QUERY_POOL_PERFORMANCE_CREATE_INFO_KHR = 1000116002, - PERFORMANCE_QUERY_SUBMIT_INFO_KHR = 1000116003, - ACQUIRE_PROFILING_LOCK_INFO_KHR = 1000116004, - PERFORMANCE_COUNTER_KHR = 1000116005, - PERFORMANCE_COUNTER_DESCRIPTION_KHR = 1000116006, - PHYSICAL_DEVICE_SURFACE_INFO_2_KHR = 1000119000, - SURFACE_CAPABILITIES_2_KHR = 1000119001, - SURFACE_FORMAT_2_KHR = 1000119002, - DISPLAY_PROPERTIES_2_KHR = 1000121000, - DISPLAY_PLANE_PROPERTIES_2_KHR = 1000121001, - DISPLAY_MODE_PROPERTIES_2_KHR = 1000121002, - DISPLAY_PLANE_INFO_2_KHR = 1000121003, - DISPLAY_PLANE_CAPABILITIES_2_KHR = 1000121004, - IOS_SURFACE_CREATE_INFO_MVK = 1000122000, - MACOS_SURFACE_CREATE_INFO_MVK = 1000123000, - DEBUG_UTILS_OBJECT_NAME_INFO_EXT = 1000128000, - DEBUG_UTILS_OBJECT_TAG_INFO_EXT = 1000128001, - DEBUG_UTILS_LABEL_EXT = 1000128002, - DEBUG_UTILS_MESSENGER_CALLBACK_DATA_EXT = 1000128003, - DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT = 1000128004, - ANDROID_HARDWARE_BUFFER_USAGE_ANDROID = 1000129000, - ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID = 1000129001, - ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID = 1000129002, - IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID = 1000129003, - MEMORY_GET_ANDROID_HARDWARE_BUFFER_INFO_ANDROID = 1000129004, - EXTERNAL_FORMAT_ANDROID = 1000129005, - PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES_EXT = 1000138000, - PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_PROPERTIES_EXT = 1000138001, - WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK_EXT = 1000138002, - DESCRIPTOR_POOL_INLINE_UNIFORM_BLOCK_CREATE_INFO_EXT = 1000138003, - SAMPLE_LOCATIONS_INFO_EXT = 1000143000, - RENDER_PASS_SAMPLE_LOCATIONS_BEGIN_INFO_EXT = 1000143001, - PIPELINE_SAMPLE_LOCATIONS_STATE_CREATE_INFO_EXT = 1000143002, - PHYSICAL_DEVICE_SAMPLE_LOCATIONS_PROPERTIES_EXT = 1000143003, - MULTISAMPLE_PROPERTIES_EXT = 1000143004, - PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_FEATURES_EXT = 1000148000, - PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_PROPERTIES_EXT = 1000148001, - PIPELINE_COLOR_BLEND_ADVANCED_STATE_CREATE_INFO_EXT = 1000148002, - PIPELINE_COVERAGE_TO_COLOR_STATE_CREATE_INFO_NV = 1000149000, - WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR = 1000150007, - ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR = 1000150000, - ACCELERATION_STRUCTURE_DEVICE_ADDRESS_INFO_KHR = 1000150002, - ACCELERATION_STRUCTURE_GEOMETRY_AABBS_DATA_KHR = 1000150003, - ACCELERATION_STRUCTURE_GEOMETRY_INSTANCES_DATA_KHR = 1000150004, - ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR = 1000150005, - ACCELERATION_STRUCTURE_GEOMETRY_KHR = 1000150006, - ACCELERATION_STRUCTURE_VERSION_INFO_KHR = 1000150009, - COPY_ACCELERATION_STRUCTURE_INFO_KHR = 1000150010, - COPY_ACCELERATION_STRUCTURE_TO_MEMORY_INFO_KHR = 1000150011, - COPY_MEMORY_TO_ACCELERATION_STRUCTURE_INFO_KHR = 1000150012, - PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR = 1000150013, - PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_PROPERTIES_KHR = 1000150014, - ACCELERATION_STRUCTURE_CREATE_INFO_KHR = 1000150017, - ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR = 1000150020, - PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR = 1000347000, - PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_PROPERTIES_KHR = 1000347001, - RAY_TRACING_PIPELINE_CREATE_INFO_KHR = 1000150015, - RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR = 1000150016, - RAY_TRACING_PIPELINE_INTERFACE_CREATE_INFO_KHR = 1000150018, - PHYSICAL_DEVICE_RAY_QUERY_FEATURES_KHR = 1000348013, - PIPELINE_COVERAGE_MODULATION_STATE_CREATE_INFO_NV = 1000152000, - PHYSICAL_DEVICE_SHADER_SM_BUILTINS_FEATURES_NV = 1000154000, - PHYSICAL_DEVICE_SHADER_SM_BUILTINS_PROPERTIES_NV = 1000154001, - DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT = 1000158000, - PHYSICAL_DEVICE_IMAGE_DRM_FORMAT_MODIFIER_INFO_EXT = 1000158002, - IMAGE_DRM_FORMAT_MODIFIER_LIST_CREATE_INFO_EXT = 1000158003, - IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT = 1000158004, - IMAGE_DRM_FORMAT_MODIFIER_PROPERTIES_EXT = 1000158005, - VALIDATION_CACHE_CREATE_INFO_EXT = 1000160000, - SHADER_MODULE_VALIDATION_CACHE_CREATE_INFO_EXT = 1000160001, - PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_KHR = 1000163000, - PHYSICAL_DEVICE_PORTABILITY_SUBSET_PROPERTIES_KHR = 1000163001, - PIPELINE_VIEWPORT_SHADING_RATE_IMAGE_STATE_CREATE_INFO_NV = 1000164000, - PHYSICAL_DEVICE_SHADING_RATE_IMAGE_FEATURES_NV = 1000164001, - PHYSICAL_DEVICE_SHADING_RATE_IMAGE_PROPERTIES_NV = 1000164002, - PIPELINE_VIEWPORT_COARSE_SAMPLE_ORDER_STATE_CREATE_INFO_NV = 1000164005, - RAY_TRACING_PIPELINE_CREATE_INFO_NV = 1000165000, - ACCELERATION_STRUCTURE_CREATE_INFO_NV = 1000165001, - GEOMETRY_NV = 1000165003, - GEOMETRY_TRIANGLES_NV = 1000165004, - GEOMETRY_AABB_NV = 1000165005, - BIND_ACCELERATION_STRUCTURE_MEMORY_INFO_NV = 1000165006, - WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_NV = 1000165007, - ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_INFO_NV = 1000165008, - PHYSICAL_DEVICE_RAY_TRACING_PROPERTIES_NV = 1000165009, - RAY_TRACING_SHADER_GROUP_CREATE_INFO_NV = 1000165011, - ACCELERATION_STRUCTURE_INFO_NV = 1000165012, - PHYSICAL_DEVICE_REPRESENTATIVE_FRAGMENT_TEST_FEATURES_NV = 1000166000, - PIPELINE_REPRESENTATIVE_FRAGMENT_TEST_STATE_CREATE_INFO_NV = 1000166001, - PHYSICAL_DEVICE_IMAGE_VIEW_IMAGE_FORMAT_INFO_EXT = 1000170000, - FILTER_CUBIC_IMAGE_VIEW_IMAGE_FORMAT_PROPERTIES_EXT = 1000170001, - DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_EXT = 1000174000, - IMPORT_MEMORY_HOST_POINTER_INFO_EXT = 1000178000, - MEMORY_HOST_POINTER_PROPERTIES_EXT = 1000178001, - PHYSICAL_DEVICE_EXTERNAL_MEMORY_HOST_PROPERTIES_EXT = 1000178002, - PHYSICAL_DEVICE_SHADER_CLOCK_FEATURES_KHR = 1000181000, - PIPELINE_COMPILER_CONTROL_CREATE_INFO_AMD = 1000183000, - CALIBRATED_TIMESTAMP_INFO_EXT = 1000184000, - PHYSICAL_DEVICE_SHADER_CORE_PROPERTIES_AMD = 1000185000, - VIDEO_DECODE_H265_CAPABILITIES_EXT = 1000187000, - VIDEO_DECODE_H265_SESSION_CREATE_INFO_EXT = 1000187001, - VIDEO_DECODE_H265_SESSION_PARAMETERS_CREATE_INFO_EXT = 1000187002, - VIDEO_DECODE_H265_SESSION_PARAMETERS_ADD_INFO_EXT = 1000187003, - VIDEO_DECODE_H265_PROFILE_EXT = 1000187004, - VIDEO_DECODE_H265_PICTURE_INFO_EXT = 1000187005, - VIDEO_DECODE_H265_DPB_SLOT_INFO_EXT = 1000187006, - DEVICE_MEMORY_OVERALLOCATION_CREATE_INFO_AMD = 1000189000, - PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_PROPERTIES_EXT = 1000190000, - PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT = 1000190001, - PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_EXT = 1000190002, - PRESENT_FRAME_TOKEN_GGP = 1000191000, - PIPELINE_CREATION_FEEDBACK_CREATE_INFO_EXT = 1000192000, - PHYSICAL_DEVICE_COMPUTE_SHADER_DERIVATIVES_FEATURES_NV = 1000201000, - PHYSICAL_DEVICE_MESH_SHADER_FEATURES_NV = 1000202000, - PHYSICAL_DEVICE_MESH_SHADER_PROPERTIES_NV = 1000202001, - PHYSICAL_DEVICE_FRAGMENT_SHADER_BARYCENTRIC_FEATURES_NV = 1000203000, - PHYSICAL_DEVICE_SHADER_IMAGE_FOOTPRINT_FEATURES_NV = 1000204000, - PIPELINE_VIEWPORT_EXCLUSIVE_SCISSOR_STATE_CREATE_INFO_NV = 1000205000, - PHYSICAL_DEVICE_EXCLUSIVE_SCISSOR_FEATURES_NV = 1000205002, - CHECKPOINT_DATA_NV = 1000206000, - QUEUE_FAMILY_CHECKPOINT_PROPERTIES_NV = 1000206001, - PHYSICAL_DEVICE_SHADER_INTEGER_FUNCTIONS_2_FEATURES_INTEL = 1000209000, - QUERY_POOL_PERFORMANCE_QUERY_CREATE_INFO_INTEL = 1000210000, - INITIALIZE_PERFORMANCE_API_INFO_INTEL = 1000210001, - PERFORMANCE_MARKER_INFO_INTEL = 1000210002, - PERFORMANCE_STREAM_MARKER_INFO_INTEL = 1000210003, - PERFORMANCE_OVERRIDE_INFO_INTEL = 1000210004, - PERFORMANCE_CONFIGURATION_ACQUIRE_INFO_INTEL = 1000210005, - PHYSICAL_DEVICE_PCI_BUS_INFO_PROPERTIES_EXT = 1000212000, - DISPLAY_NATIVE_HDR_SURFACE_CAPABILITIES_AMD = 1000213000, - SWAPCHAIN_DISPLAY_NATIVE_HDR_CREATE_INFO_AMD = 1000213001, - IMAGEPIPE_SURFACE_CREATE_INFO_FUCHSIA = 1000214000, - PHYSICAL_DEVICE_SHADER_TERMINATE_INVOCATION_FEATURES_KHR = 1000215000, - METAL_SURFACE_CREATE_INFO_EXT = 1000217000, - PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_FEATURES_EXT = 1000218000, - PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_PROPERTIES_EXT = 1000218001, - RENDER_PASS_FRAGMENT_DENSITY_MAP_CREATE_INFO_EXT = 1000218002, - PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES_EXT = 1000225000, - PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO_EXT = 1000225001, - PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES_EXT = 1000225002, - FRAGMENT_SHADING_RATE_ATTACHMENT_INFO_KHR = 1000226000, - PIPELINE_FRAGMENT_SHADING_RATE_STATE_CREATE_INFO_KHR = 1000226001, - PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_PROPERTIES_KHR = 1000226002, - PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_FEATURES_KHR = 1000226003, - PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_KHR = 1000226004, - PHYSICAL_DEVICE_SHADER_CORE_PROPERTIES_2_AMD = 1000227000, - PHYSICAL_DEVICE_COHERENT_MEMORY_FEATURES_AMD = 1000229000, - PHYSICAL_DEVICE_SHADER_IMAGE_ATOMIC_INT64_FEATURES_EXT = 1000234000, - PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT = 1000237000, - PHYSICAL_DEVICE_MEMORY_PRIORITY_FEATURES_EXT = 1000238000, - MEMORY_PRIORITY_ALLOCATE_INFO_EXT = 1000238001, - SURFACE_PROTECTED_CAPABILITIES_KHR = 1000239000, - PHYSICAL_DEVICE_DEDICATED_ALLOCATION_IMAGE_ALIASING_FEATURES_NV = 1000240000, - PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_EXT = 1000244000, - BUFFER_DEVICE_ADDRESS_CREATE_INFO_EXT = 1000244002, - PHYSICAL_DEVICE_TOOL_PROPERTIES_EXT = 1000245000, - VALIDATION_FEATURES_EXT = 1000247000, - PHYSICAL_DEVICE_PRESENT_WAIT_FEATURES_KHR = 1000248000, - PHYSICAL_DEVICE_COOPERATIVE_MATRIX_FEATURES_NV = 1000249000, - COOPERATIVE_MATRIX_PROPERTIES_NV = 1000249001, - PHYSICAL_DEVICE_COOPERATIVE_MATRIX_PROPERTIES_NV = 1000249002, - PHYSICAL_DEVICE_COVERAGE_REDUCTION_MODE_FEATURES_NV = 1000250000, - PIPELINE_COVERAGE_REDUCTION_STATE_CREATE_INFO_NV = 1000250001, - FRAMEBUFFER_MIXED_SAMPLES_COMBINATION_NV = 1000250002, - PHYSICAL_DEVICE_FRAGMENT_SHADER_INTERLOCK_FEATURES_EXT = 1000251000, - PHYSICAL_DEVICE_YCBCR_IMAGE_ARRAYS_FEATURES_EXT = 1000252000, - PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT = 1000254000, - PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT = 1000254001, - PHYSICAL_DEVICE_PROVOKING_VERTEX_PROPERTIES_EXT = 1000254002, - SURFACE_FULL_SCREEN_EXCLUSIVE_INFO_EXT = 1000255000, - SURFACE_CAPABILITIES_FULL_SCREEN_EXCLUSIVE_EXT = 1000255002, - SURFACE_FULL_SCREEN_EXCLUSIVE_WIN32_INFO_EXT = 1000255001, - HEADLESS_SURFACE_CREATE_INFO_EXT = 1000256000, - PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT = 1000259000, - PIPELINE_RASTERIZATION_LINE_STATE_CREATE_INFO_EXT = 1000259001, - PHYSICAL_DEVICE_LINE_RASTERIZATION_PROPERTIES_EXT = 1000259002, - PHYSICAL_DEVICE_SHADER_ATOMIC_FLOAT_FEATURES_EXT = 1000260000, - PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES_EXT = 1000265000, - PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT = 1000267000, - PHYSICAL_DEVICE_PIPELINE_EXECUTABLE_PROPERTIES_FEATURES_KHR = 1000269000, - PIPELINE_INFO_KHR = 1000269001, - PIPELINE_EXECUTABLE_PROPERTIES_KHR = 1000269002, - PIPELINE_EXECUTABLE_INFO_KHR = 1000269003, - PIPELINE_EXECUTABLE_STATISTIC_KHR = 1000269004, - PIPELINE_EXECUTABLE_INTERNAL_REPRESENTATION_KHR = 1000269005, - PHYSICAL_DEVICE_SHADER_ATOMIC_FLOAT_2_FEATURES_EXT = 1000273000, - PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES_EXT = 1000276000, - PHYSICAL_DEVICE_DEVICE_GENERATED_COMMANDS_PROPERTIES_NV = 1000277000, - GRAPHICS_SHADER_GROUP_CREATE_INFO_NV = 1000277001, - GRAPHICS_PIPELINE_SHADER_GROUPS_CREATE_INFO_NV = 1000277002, - INDIRECT_COMMANDS_LAYOUT_TOKEN_NV = 1000277003, - INDIRECT_COMMANDS_LAYOUT_CREATE_INFO_NV = 1000277004, - GENERATED_COMMANDS_INFO_NV = 1000277005, - GENERATED_COMMANDS_MEMORY_REQUIREMENTS_INFO_NV = 1000277006, - PHYSICAL_DEVICE_DEVICE_GENERATED_COMMANDS_FEATURES_NV = 1000277007, - PHYSICAL_DEVICE_INHERITED_VIEWPORT_SCISSOR_FEATURES_NV = 1000278000, - COMMAND_BUFFER_INHERITANCE_VIEWPORT_SCISSOR_INFO_NV = 1000278001, - PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_FEATURES_KHR = 1000280000, - PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_PROPERTIES_KHR = 1000280001, - PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_FEATURES_EXT = 1000281000, - PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_PROPERTIES_EXT = 1000281001, - COMMAND_BUFFER_INHERITANCE_RENDER_PASS_TRANSFORM_INFO_QCOM = 1000282000, - RENDER_PASS_TRANSFORM_BEGIN_INFO_QCOM = 1000282001, - PHYSICAL_DEVICE_DEVICE_MEMORY_REPORT_FEATURES_EXT = 1000284000, - DEVICE_DEVICE_MEMORY_REPORT_CREATE_INFO_EXT = 1000284001, - DEVICE_MEMORY_REPORT_CALLBACK_DATA_EXT = 1000284002, - PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT = 1000286000, - PHYSICAL_DEVICE_ROBUSTNESS_2_PROPERTIES_EXT = 1000286001, - SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT = 1000287000, - PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_PROPERTIES_EXT = 1000287001, - PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT = 1000287002, - PIPELINE_LIBRARY_CREATE_INFO_KHR = 1000290000, - PRESENT_ID_KHR = 1000294000, - PHYSICAL_DEVICE_PRESENT_ID_FEATURES_KHR = 1000294001, - PHYSICAL_DEVICE_PRIVATE_DATA_FEATURES_EXT = 1000295000, - DEVICE_PRIVATE_DATA_CREATE_INFO_EXT = 1000295001, - PRIVATE_DATA_SLOT_CREATE_INFO_EXT = 1000295002, - PHYSICAL_DEVICE_PIPELINE_CREATION_CACHE_CONTROL_FEATURES_EXT = 1000297000, - VIDEO_ENCODE_INFO_KHR = 1000299000, - VIDEO_ENCODE_RATE_CONTROL_INFO_KHR = 1000299001, - PHYSICAL_DEVICE_DIAGNOSTICS_CONFIG_FEATURES_NV = 1000300000, - DEVICE_DIAGNOSTICS_CONFIG_CREATE_INFO_NV = 1000300001, - MEMORY_BARRIER_2_KHR = 1000314000, - BUFFER_MEMORY_BARRIER_2_KHR = 1000314001, - IMAGE_MEMORY_BARRIER_2_KHR = 1000314002, - DEPENDENCY_INFO_KHR = 1000314003, - SUBMIT_INFO_2_KHR = 1000314004, - SEMAPHORE_SUBMIT_INFO_KHR = 1000314005, - COMMAND_BUFFER_SUBMIT_INFO_KHR = 1000314006, - PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES_KHR = 1000314007, - QUEUE_FAMILY_CHECKPOINT_PROPERTIES_2_NV = 1000314008, - CHECKPOINT_DATA_2_NV = 1000314009, - PHYSICAL_DEVICE_SHADER_SUBGROUP_UNIFORM_CONTROL_FLOW_FEATURES_KHR = 1000323000, - PHYSICAL_DEVICE_ZERO_INITIALIZE_WORKGROUP_MEMORY_FEATURES_KHR = 1000325000, - PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_ENUMS_PROPERTIES_NV = 1000326000, - PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_ENUMS_FEATURES_NV = 1000326001, - PIPELINE_FRAGMENT_SHADING_RATE_ENUM_STATE_CREATE_INFO_NV = 1000326002, - ACCELERATION_STRUCTURE_GEOMETRY_MOTION_TRIANGLES_DATA_NV = 1000327000, - PHYSICAL_DEVICE_RAY_TRACING_MOTION_BLUR_FEATURES_NV = 1000327001, - ACCELERATION_STRUCTURE_MOTION_INFO_NV = 1000327002, - PHYSICAL_DEVICE_YCBCR_2_PLANE_444_FORMATS_FEATURES_EXT = 1000330000, - PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_2_FEATURES_EXT = 1000332000, - PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_2_PROPERTIES_EXT = 1000332001, - COPY_COMMAND_TRANSFORM_INFO_QCOM = 1000333000, - PHYSICAL_DEVICE_IMAGE_ROBUSTNESS_FEATURES_EXT = 1000335000, - PHYSICAL_DEVICE_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_FEATURES_KHR = 1000336000, - COPY_BUFFER_INFO_2_KHR = 1000337000, - COPY_IMAGE_INFO_2_KHR = 1000337001, - COPY_BUFFER_TO_IMAGE_INFO_2_KHR = 1000337002, - COPY_IMAGE_TO_BUFFER_INFO_2_KHR = 1000337003, - BLIT_IMAGE_INFO_2_KHR = 1000337004, - RESOLVE_IMAGE_INFO_2_KHR = 1000337005, - BUFFER_COPY_2_KHR = 1000337006, - IMAGE_COPY_2_KHR = 1000337007, - IMAGE_BLIT_2_KHR = 1000337008, - BUFFER_IMAGE_COPY_2_KHR = 1000337009, - IMAGE_RESOLVE_2_KHR = 1000337010, - PHYSICAL_DEVICE_4444_FORMATS_FEATURES_EXT = 1000340000, - DIRECTFB_SURFACE_CREATE_INFO_EXT = 1000346000, - PHYSICAL_DEVICE_MUTABLE_DESCRIPTOR_TYPE_FEATURES_VALVE = 1000351000, - MUTABLE_DESCRIPTOR_TYPE_CREATE_INFO_VALVE = 1000351002, - PHYSICAL_DEVICE_VERTEX_INPUT_DYNAMIC_STATE_FEATURES_EXT = 1000352000, - VERTEX_INPUT_BINDING_DESCRIPTION_2_EXT = 1000352001, - VERTEX_INPUT_ATTRIBUTE_DESCRIPTION_2_EXT = 1000352002, - PHYSICAL_DEVICE_DRM_PROPERTIES_EXT = 1000353000, - PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT = 1000356000, - IMPORT_MEMORY_ZIRCON_HANDLE_INFO_FUCHSIA = 1000364000, - MEMORY_ZIRCON_HANDLE_PROPERTIES_FUCHSIA = 1000364001, - MEMORY_GET_ZIRCON_HANDLE_INFO_FUCHSIA = 1000364002, - IMPORT_SEMAPHORE_ZIRCON_HANDLE_INFO_FUCHSIA = 1000365000, - SEMAPHORE_GET_ZIRCON_HANDLE_INFO_FUCHSIA = 1000365001, - SUBPASS_SHADING_PIPELINE_CREATE_INFO_HUAWEI = 1000369000, - PHYSICAL_DEVICE_SUBPASS_SHADING_FEATURES_HUAWEI = 1000369001, - PHYSICAL_DEVICE_SUBPASS_SHADING_PROPERTIES_HUAWEI = 1000369002, - PHYSICAL_DEVICE_INVOCATION_MASK_FEATURES_HUAWEI = 1000370000, - MEMORY_GET_REMOTE_ADDRESS_INFO_NV = 1000371000, - PHYSICAL_DEVICE_EXTERNAL_MEMORY_RDMA_FEATURES_NV = 1000371001, - PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_2_FEATURES_EXT = 1000377000, - SCREEN_SURFACE_CREATE_INFO_QNX = 1000378000, - PHYSICAL_DEVICE_COLOR_WRITE_ENABLE_FEATURES_EXT = 1000381000, - PIPELINE_COLOR_WRITE_CREATE_INFO_EXT = 1000381001, - PHYSICAL_DEVICE_GLOBAL_PRIORITY_QUERY_FEATURES_EXT = 1000388000, - QUEUE_FAMILY_GLOBAL_PRIORITY_PROPERTIES_EXT = 1000388001, - PHYSICAL_DEVICE_MULTI_DRAW_FEATURES_EXT = 1000392000, - PHYSICAL_DEVICE_MULTI_DRAW_PROPERTIES_EXT = 1000392001, - PHYSICAL_DEVICE_PAGEABLE_DEVICE_LOCAL_MEMORY_FEATURES_EXT = 1000412000, - PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES = PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES, - PHYSICAL_DEVICE_SHADER_DRAW_PARAMETER_FEATURES = PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES, - DEBUG_REPORT_CREATE_INFO_EXT = DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT, - RENDER_PASS_MULTIVIEW_CREATE_INFO_KHR = RENDER_PASS_MULTIVIEW_CREATE_INFO, - PHYSICAL_DEVICE_MULTIVIEW_FEATURES_KHR = PHYSICAL_DEVICE_MULTIVIEW_FEATURES, - PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES_KHR = PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES, - PHYSICAL_DEVICE_FEATURES_2_KHR = PHYSICAL_DEVICE_FEATURES_2, - PHYSICAL_DEVICE_PROPERTIES_2_KHR = PHYSICAL_DEVICE_PROPERTIES_2, - FORMAT_PROPERTIES_2_KHR = FORMAT_PROPERTIES_2, - IMAGE_FORMAT_PROPERTIES_2_KHR = IMAGE_FORMAT_PROPERTIES_2, - PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHR = PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2, - QUEUE_FAMILY_PROPERTIES_2_KHR = QUEUE_FAMILY_PROPERTIES_2, - PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR = PHYSICAL_DEVICE_MEMORY_PROPERTIES_2, - SPARSE_IMAGE_FORMAT_PROPERTIES_2_KHR = SPARSE_IMAGE_FORMAT_PROPERTIES_2, - PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2_KHR = PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2, - MEMORY_ALLOCATE_FLAGS_INFO_KHR = MEMORY_ALLOCATE_FLAGS_INFO, - DEVICE_GROUP_RENDER_PASS_BEGIN_INFO_KHR = DEVICE_GROUP_RENDER_PASS_BEGIN_INFO, - DEVICE_GROUP_COMMAND_BUFFER_BEGIN_INFO_KHR = DEVICE_GROUP_COMMAND_BUFFER_BEGIN_INFO, - DEVICE_GROUP_SUBMIT_INFO_KHR = DEVICE_GROUP_SUBMIT_INFO, - DEVICE_GROUP_BIND_SPARSE_INFO_KHR = DEVICE_GROUP_BIND_SPARSE_INFO, - BIND_BUFFER_MEMORY_DEVICE_GROUP_INFO_KHR = BIND_BUFFER_MEMORY_DEVICE_GROUP_INFO, - BIND_IMAGE_MEMORY_DEVICE_GROUP_INFO_KHR = BIND_IMAGE_MEMORY_DEVICE_GROUP_INFO, - PHYSICAL_DEVICE_GROUP_PROPERTIES_KHR = PHYSICAL_DEVICE_GROUP_PROPERTIES, - DEVICE_GROUP_DEVICE_CREATE_INFO_KHR = DEVICE_GROUP_DEVICE_CREATE_INFO, - PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO_KHR = PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO, - EXTERNAL_IMAGE_FORMAT_PROPERTIES_KHR = EXTERNAL_IMAGE_FORMAT_PROPERTIES, - PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO_KHR = PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO, - EXTERNAL_BUFFER_PROPERTIES_KHR = EXTERNAL_BUFFER_PROPERTIES, - PHYSICAL_DEVICE_ID_PROPERTIES_KHR = PHYSICAL_DEVICE_ID_PROPERTIES, - EXTERNAL_MEMORY_BUFFER_CREATE_INFO_KHR = EXTERNAL_MEMORY_BUFFER_CREATE_INFO, - EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHR = EXTERNAL_MEMORY_IMAGE_CREATE_INFO, - EXPORT_MEMORY_ALLOCATE_INFO_KHR = EXPORT_MEMORY_ALLOCATE_INFO, - PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO_KHR = PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO, - EXTERNAL_SEMAPHORE_PROPERTIES_KHR = EXTERNAL_SEMAPHORE_PROPERTIES, - EXPORT_SEMAPHORE_CREATE_INFO_KHR = EXPORT_SEMAPHORE_CREATE_INFO, - PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR = PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES, - PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR = PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES, - PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR = PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES, - DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO_KHR = DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO, - SURFACE_CAPABILITIES2_EXT = SURFACE_CAPABILITIES_2_EXT, - PHYSICAL_DEVICE_IMAGELESS_FRAMEBUFFER_FEATURES_KHR = PHYSICAL_DEVICE_IMAGELESS_FRAMEBUFFER_FEATURES, - FRAMEBUFFER_ATTACHMENTS_CREATE_INFO_KHR = FRAMEBUFFER_ATTACHMENTS_CREATE_INFO, - FRAMEBUFFER_ATTACHMENT_IMAGE_INFO_KHR = FRAMEBUFFER_ATTACHMENT_IMAGE_INFO, - RENDER_PASS_ATTACHMENT_BEGIN_INFO_KHR = RENDER_PASS_ATTACHMENT_BEGIN_INFO, - ATTACHMENT_DESCRIPTION_2_KHR = ATTACHMENT_DESCRIPTION_2, - ATTACHMENT_REFERENCE_2_KHR = ATTACHMENT_REFERENCE_2, - SUBPASS_DESCRIPTION_2_KHR = SUBPASS_DESCRIPTION_2, - SUBPASS_DEPENDENCY_2_KHR = SUBPASS_DEPENDENCY_2, - RENDER_PASS_CREATE_INFO_2_KHR = RENDER_PASS_CREATE_INFO_2, - SUBPASS_BEGIN_INFO_KHR = SUBPASS_BEGIN_INFO, - SUBPASS_END_INFO_KHR = SUBPASS_END_INFO, - PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO_KHR = PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO, - EXTERNAL_FENCE_PROPERTIES_KHR = EXTERNAL_FENCE_PROPERTIES, - EXPORT_FENCE_CREATE_INFO_KHR = EXPORT_FENCE_CREATE_INFO, - PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES_KHR = PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES, - RENDER_PASS_INPUT_ATTACHMENT_ASPECT_CREATE_INFO_KHR = RENDER_PASS_INPUT_ATTACHMENT_ASPECT_CREATE_INFO, - IMAGE_VIEW_USAGE_CREATE_INFO_KHR = IMAGE_VIEW_USAGE_CREATE_INFO, - PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO_KHR = PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO, - PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES_KHR = PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES, - PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES_KHR = PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES_KHR, - MEMORY_DEDICATED_REQUIREMENTS_KHR = MEMORY_DEDICATED_REQUIREMENTS, - MEMORY_DEDICATED_ALLOCATE_INFO_KHR = MEMORY_DEDICATED_ALLOCATE_INFO, - PHYSICAL_DEVICE_SAMPLER_FILTER_MINMAX_PROPERTIES_EXT = PHYSICAL_DEVICE_SAMPLER_FILTER_MINMAX_PROPERTIES, - SAMPLER_REDUCTION_MODE_CREATE_INFO_EXT = SAMPLER_REDUCTION_MODE_CREATE_INFO, - BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR = BUFFER_MEMORY_REQUIREMENTS_INFO_2, - IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR = IMAGE_MEMORY_REQUIREMENTS_INFO_2, - IMAGE_SPARSE_MEMORY_REQUIREMENTS_INFO_2_KHR = IMAGE_SPARSE_MEMORY_REQUIREMENTS_INFO_2, - MEMORY_REQUIREMENTS_2_KHR = MEMORY_REQUIREMENTS_2, - SPARSE_IMAGE_MEMORY_REQUIREMENTS_2_KHR = SPARSE_IMAGE_MEMORY_REQUIREMENTS_2, - IMAGE_FORMAT_LIST_CREATE_INFO_KHR = IMAGE_FORMAT_LIST_CREATE_INFO, - SAMPLER_YCBCR_CONVERSION_CREATE_INFO_KHR = SAMPLER_YCBCR_CONVERSION_CREATE_INFO, - SAMPLER_YCBCR_CONVERSION_INFO_KHR = SAMPLER_YCBCR_CONVERSION_INFO, - BIND_IMAGE_PLANE_MEMORY_INFO_KHR = BIND_IMAGE_PLANE_MEMORY_INFO, - IMAGE_PLANE_MEMORY_REQUIREMENTS_INFO_KHR = IMAGE_PLANE_MEMORY_REQUIREMENTS_INFO, - PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES_KHR = PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES, - SAMPLER_YCBCR_CONVERSION_IMAGE_FORMAT_PROPERTIES_KHR = SAMPLER_YCBCR_CONVERSION_IMAGE_FORMAT_PROPERTIES, - BIND_BUFFER_MEMORY_INFO_KHR = BIND_BUFFER_MEMORY_INFO, - BIND_IMAGE_MEMORY_INFO_KHR = BIND_IMAGE_MEMORY_INFO, - DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO_EXT = DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO, - PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT = PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES, - PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES_EXT = PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES, - DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO_EXT = DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO, - DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_LAYOUT_SUPPORT_EXT = DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_LAYOUT_SUPPORT, - PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES_KHR = PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES, - DESCRIPTOR_SET_LAYOUT_SUPPORT_KHR = DESCRIPTOR_SET_LAYOUT_SUPPORT, - PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES_KHR = PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES, - PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES_KHR = PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES, - PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES_KHR = PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES, - PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR = PHYSICAL_DEVICE_DRIVER_PROPERTIES, - PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES_KHR = PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES, - PHYSICAL_DEVICE_DEPTH_STENCIL_RESOLVE_PROPERTIES_KHR = PHYSICAL_DEVICE_DEPTH_STENCIL_RESOLVE_PROPERTIES, - SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE_KHR = SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE, - PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES_KHR = PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES, - PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_PROPERTIES_KHR = PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_PROPERTIES, - SEMAPHORE_TYPE_CREATE_INFO_KHR = SEMAPHORE_TYPE_CREATE_INFO, - TIMELINE_SEMAPHORE_SUBMIT_INFO_KHR = TIMELINE_SEMAPHORE_SUBMIT_INFO, - SEMAPHORE_WAIT_INFO_KHR = SEMAPHORE_WAIT_INFO, - SEMAPHORE_SIGNAL_INFO_KHR = SEMAPHORE_SIGNAL_INFO, - QUERY_POOL_CREATE_INFO_INTEL = QUERY_POOL_PERFORMANCE_QUERY_CREATE_INFO_INTEL, - PHYSICAL_DEVICE_VULKAN_MEMORY_MODEL_FEATURES_KHR = PHYSICAL_DEVICE_VULKAN_MEMORY_MODEL_FEATURES, - PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES_EXT = PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES, - PHYSICAL_DEVICE_SEPARATE_DEPTH_STENCIL_LAYOUTS_FEATURES_KHR = PHYSICAL_DEVICE_SEPARATE_DEPTH_STENCIL_LAYOUTS_FEATURES, - ATTACHMENT_REFERENCE_STENCIL_LAYOUT_KHR = ATTACHMENT_REFERENCE_STENCIL_LAYOUT, - ATTACHMENT_DESCRIPTION_STENCIL_LAYOUT_KHR = ATTACHMENT_DESCRIPTION_STENCIL_LAYOUT, - PHYSICAL_DEVICE_BUFFER_ADDRESS_FEATURES_EXT = PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_EXT, - BUFFER_DEVICE_ADDRESS_INFO_EXT = BUFFER_DEVICE_ADDRESS_INFO, - IMAGE_STENCIL_USAGE_CREATE_INFO_EXT = IMAGE_STENCIL_USAGE_CREATE_INFO, - PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES_KHR = PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES, - PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_KHR = PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES, - BUFFER_DEVICE_ADDRESS_INFO_KHR = BUFFER_DEVICE_ADDRESS_INFO, - BUFFER_OPAQUE_CAPTURE_ADDRESS_CREATE_INFO_KHR = BUFFER_OPAQUE_CAPTURE_ADDRESS_CREATE_INFO, - MEMORY_OPAQUE_CAPTURE_ADDRESS_ALLOCATE_INFO_KHR = MEMORY_OPAQUE_CAPTURE_ADDRESS_ALLOCATE_INFO, - DEVICE_MEMORY_OPAQUE_CAPTURE_ADDRESS_INFO_KHR = DEVICE_MEMORY_OPAQUE_CAPTURE_ADDRESS_INFO, - PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES_EXT = PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES, + APPLICATION_INFO = 0, + INSTANCE_CREATE_INFO = 1, + DEVICE_QUEUE_CREATE_INFO = 2, + DEVICE_CREATE_INFO = 3, + SUBMIT_INFO = 4, + MEMORY_ALLOCATE_INFO = 5, + MAPPED_MEMORY_RANGE = 6, + BIND_SPARSE_INFO = 7, + FENCE_CREATE_INFO = 8, + SEMAPHORE_CREATE_INFO = 9, + EVENT_CREATE_INFO = 10, + QUERY_POOL_CREATE_INFO = 11, + BUFFER_CREATE_INFO = 12, + BUFFER_VIEW_CREATE_INFO = 13, + IMAGE_CREATE_INFO = 14, + IMAGE_VIEW_CREATE_INFO = 15, + SHADER_MODULE_CREATE_INFO = 16, + PIPELINE_CACHE_CREATE_INFO = 17, + PIPELINE_SHADER_STAGE_CREATE_INFO = 18, + PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO = 19, + PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO = 20, + PIPELINE_TESSELLATION_STATE_CREATE_INFO = 21, + PIPELINE_VIEWPORT_STATE_CREATE_INFO = 22, + PIPELINE_RASTERIZATION_STATE_CREATE_INFO = 23, + PIPELINE_MULTISAMPLE_STATE_CREATE_INFO = 24, + PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO = 25, + PIPELINE_COLOR_BLEND_STATE_CREATE_INFO = 26, + PIPELINE_DYNAMIC_STATE_CREATE_INFO = 27, + GRAPHICS_PIPELINE_CREATE_INFO = 28, + COMPUTE_PIPELINE_CREATE_INFO = 29, + PIPELINE_LAYOUT_CREATE_INFO = 30, + SAMPLER_CREATE_INFO = 31, + DESCRIPTOR_SET_LAYOUT_CREATE_INFO = 32, + DESCRIPTOR_POOL_CREATE_INFO = 33, + DESCRIPTOR_SET_ALLOCATE_INFO = 34, + WRITE_DESCRIPTOR_SET = 35, + COPY_DESCRIPTOR_SET = 36, + FRAMEBUFFER_CREATE_INFO = 37, + RENDER_PASS_CREATE_INFO = 38, + COMMAND_POOL_CREATE_INFO = 39, + COMMAND_BUFFER_ALLOCATE_INFO = 40, + COMMAND_BUFFER_INHERITANCE_INFO = 41, + COMMAND_BUFFER_BEGIN_INFO = 42, + RENDER_PASS_BEGIN_INFO = 43, + BUFFER_MEMORY_BARRIER = 44, + IMAGE_MEMORY_BARRIER = 45, + MEMORY_BARRIER = 46, + LOADER_INSTANCE_CREATE_INFO = 47, + LOADER_DEVICE_CREATE_INFO = 48, + PHYSICAL_DEVICE_SUBGROUP_PROPERTIES = 1000094000, + BIND_BUFFER_MEMORY_INFO = 1000157000, + BIND_IMAGE_MEMORY_INFO = 1000157001, + PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES = 1000083000, + MEMORY_DEDICATED_REQUIREMENTS = 1000127000, + MEMORY_DEDICATED_ALLOCATE_INFO = 1000127001, + MEMORY_ALLOCATE_FLAGS_INFO = 1000060000, + DEVICE_GROUP_RENDER_PASS_BEGIN_INFO = 1000060003, + DEVICE_GROUP_COMMAND_BUFFER_BEGIN_INFO = 1000060004, + DEVICE_GROUP_SUBMIT_INFO = 1000060005, + DEVICE_GROUP_BIND_SPARSE_INFO = 1000060006, + BIND_BUFFER_MEMORY_DEVICE_GROUP_INFO = 1000060013, + BIND_IMAGE_MEMORY_DEVICE_GROUP_INFO = 1000060014, + PHYSICAL_DEVICE_GROUP_PROPERTIES = 1000070000, + DEVICE_GROUP_DEVICE_CREATE_INFO = 1000070001, + BUFFER_MEMORY_REQUIREMENTS_INFO_2 = 1000146000, + IMAGE_MEMORY_REQUIREMENTS_INFO_2 = 1000146001, + IMAGE_SPARSE_MEMORY_REQUIREMENTS_INFO_2 = 1000146002, + MEMORY_REQUIREMENTS_2 = 1000146003, + SPARSE_IMAGE_MEMORY_REQUIREMENTS_2 = 1000146004, + PHYSICAL_DEVICE_FEATURES_2 = 1000059000, + PHYSICAL_DEVICE_PROPERTIES_2 = 1000059001, + FORMAT_PROPERTIES_2 = 1000059002, + IMAGE_FORMAT_PROPERTIES_2 = 1000059003, + PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2 = 1000059004, + QUEUE_FAMILY_PROPERTIES_2 = 1000059005, + PHYSICAL_DEVICE_MEMORY_PROPERTIES_2 = 1000059006, + SPARSE_IMAGE_FORMAT_PROPERTIES_2 = 1000059007, + PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2 = 1000059008, + PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES = 1000117000, + RENDER_PASS_INPUT_ATTACHMENT_ASPECT_CREATE_INFO = 1000117001, + IMAGE_VIEW_USAGE_CREATE_INFO = 1000117002, + PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO = 1000117003, + RENDER_PASS_MULTIVIEW_CREATE_INFO = 1000053000, + PHYSICAL_DEVICE_MULTIVIEW_FEATURES = 1000053001, + PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES = 1000053002, + PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES = 1000120000, + PROTECTED_SUBMIT_INFO = 1000145000, + PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES = 1000145001, + PHYSICAL_DEVICE_PROTECTED_MEMORY_PROPERTIES = 1000145002, + DEVICE_QUEUE_INFO_2 = 1000145003, + SAMPLER_YCBCR_CONVERSION_CREATE_INFO = 1000156000, + SAMPLER_YCBCR_CONVERSION_INFO = 1000156001, + BIND_IMAGE_PLANE_MEMORY_INFO = 1000156002, + IMAGE_PLANE_MEMORY_REQUIREMENTS_INFO = 1000156003, + PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES = 1000156004, + SAMPLER_YCBCR_CONVERSION_IMAGE_FORMAT_PROPERTIES = 1000156005, + DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO = 1000085000, + PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO = 1000071000, + EXTERNAL_IMAGE_FORMAT_PROPERTIES = 1000071001, + PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO = 1000071002, + EXTERNAL_BUFFER_PROPERTIES = 1000071003, + PHYSICAL_DEVICE_ID_PROPERTIES = 1000071004, + EXTERNAL_MEMORY_BUFFER_CREATE_INFO = 1000072000, + EXTERNAL_MEMORY_IMAGE_CREATE_INFO = 1000072001, + EXPORT_MEMORY_ALLOCATE_INFO = 1000072002, + PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO = 1000112000, + EXTERNAL_FENCE_PROPERTIES = 1000112001, + EXPORT_FENCE_CREATE_INFO = 1000113000, + EXPORT_SEMAPHORE_CREATE_INFO = 1000077000, + PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO = 1000076000, + EXTERNAL_SEMAPHORE_PROPERTIES = 1000076001, + PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES = 1000168000, + DESCRIPTOR_SET_LAYOUT_SUPPORT = 1000168001, + PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES = 1000063000, + PHYSICAL_DEVICE_VULKAN_1_1_FEATURES = 49, + PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES = 50, + PHYSICAL_DEVICE_VULKAN_1_2_FEATURES = 51, + PHYSICAL_DEVICE_VULKAN_1_2_PROPERTIES = 52, + IMAGE_FORMAT_LIST_CREATE_INFO = 1000147000, + ATTACHMENT_DESCRIPTION_2 = 1000109000, + ATTACHMENT_REFERENCE_2 = 1000109001, + SUBPASS_DESCRIPTION_2 = 1000109002, + SUBPASS_DEPENDENCY_2 = 1000109003, + RENDER_PASS_CREATE_INFO_2 = 1000109004, + SUBPASS_BEGIN_INFO = 1000109005, + SUBPASS_END_INFO = 1000109006, + PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES = 1000177000, + PHYSICAL_DEVICE_DRIVER_PROPERTIES = 1000196000, + PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES = 1000180000, + PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES = 1000082000, + PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES = 1000197000, + DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO = 1000161000, + PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES = 1000161001, + PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES = 1000161002, + DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO = 1000161003, + DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_LAYOUT_SUPPORT = 1000161004, + PHYSICAL_DEVICE_DEPTH_STENCIL_RESOLVE_PROPERTIES = 1000199000, + SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE = 1000199001, + PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES = 1000221000, + IMAGE_STENCIL_USAGE_CREATE_INFO = 1000246000, + PHYSICAL_DEVICE_SAMPLER_FILTER_MINMAX_PROPERTIES = 1000130000, + SAMPLER_REDUCTION_MODE_CREATE_INFO = 1000130001, + PHYSICAL_DEVICE_VULKAN_MEMORY_MODEL_FEATURES = 1000211000, + PHYSICAL_DEVICE_IMAGELESS_FRAMEBUFFER_FEATURES = 1000108000, + FRAMEBUFFER_ATTACHMENTS_CREATE_INFO = 1000108001, + FRAMEBUFFER_ATTACHMENT_IMAGE_INFO = 1000108002, + RENDER_PASS_ATTACHMENT_BEGIN_INFO = 1000108003, + PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES = 1000253000, + PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES = 1000175000, + PHYSICAL_DEVICE_SEPARATE_DEPTH_STENCIL_LAYOUTS_FEATURES = 1000241000, + ATTACHMENT_REFERENCE_STENCIL_LAYOUT = 1000241001, + ATTACHMENT_DESCRIPTION_STENCIL_LAYOUT = 1000241002, + PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES = 1000261000, + PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES = 1000207000, + PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_PROPERTIES = 1000207001, + SEMAPHORE_TYPE_CREATE_INFO = 1000207002, + TIMELINE_SEMAPHORE_SUBMIT_INFO = 1000207003, + SEMAPHORE_WAIT_INFO = 1000207004, + SEMAPHORE_SIGNAL_INFO = 1000207005, + PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES = 1000257000, + BUFFER_DEVICE_ADDRESS_INFO = 1000244001, + BUFFER_OPAQUE_CAPTURE_ADDRESS_CREATE_INFO = 1000257002, + MEMORY_OPAQUE_CAPTURE_ADDRESS_ALLOCATE_INFO = 1000257003, + DEVICE_MEMORY_OPAQUE_CAPTURE_ADDRESS_INFO = 1000257004, + PHYSICAL_DEVICE_VULKAN_1_3_FEATURES = 53, + PHYSICAL_DEVICE_VULKAN_1_3_PROPERTIES = 54, + PIPELINE_CREATION_FEEDBACK_CREATE_INFO = 1000192000, + PHYSICAL_DEVICE_SHADER_TERMINATE_INVOCATION_FEATURES = 1000215000, + PHYSICAL_DEVICE_TOOL_PROPERTIES = 1000245000, + PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES = 1000276000, + PHYSICAL_DEVICE_PRIVATE_DATA_FEATURES = 1000295000, + DEVICE_PRIVATE_DATA_CREATE_INFO = 1000295001, + PRIVATE_DATA_SLOT_CREATE_INFO = 1000295002, + PHYSICAL_DEVICE_PIPELINE_CREATION_CACHE_CONTROL_FEATURES = 1000297000, + MEMORY_BARRIER_2 = 1000314000, + BUFFER_MEMORY_BARRIER_2 = 1000314001, + IMAGE_MEMORY_BARRIER_2 = 1000314002, + DEPENDENCY_INFO = 1000314003, + SUBMIT_INFO_2 = 1000314004, + SEMAPHORE_SUBMIT_INFO = 1000314005, + COMMAND_BUFFER_SUBMIT_INFO = 1000314006, + PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES = 1000314007, + PHYSICAL_DEVICE_ZERO_INITIALIZE_WORKGROUP_MEMORY_FEATURES = 1000325000, + PHYSICAL_DEVICE_IMAGE_ROBUSTNESS_FEATURES = 1000335000, + COPY_BUFFER_INFO_2 = 1000337000, + COPY_IMAGE_INFO_2 = 1000337001, + COPY_BUFFER_TO_IMAGE_INFO_2 = 1000337002, + COPY_IMAGE_TO_BUFFER_INFO_2 = 1000337003, + BLIT_IMAGE_INFO_2 = 1000337004, + RESOLVE_IMAGE_INFO_2 = 1000337005, + BUFFER_COPY_2 = 1000337006, + IMAGE_COPY_2 = 1000337007, + IMAGE_BLIT_2 = 1000337008, + BUFFER_IMAGE_COPY_2 = 1000337009, + IMAGE_RESOLVE_2 = 1000337010, + PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES = 1000225000, + PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO = 1000225001, + PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES = 1000225002, + PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES = 1000138000, + PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_PROPERTIES = 1000138001, + WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK = 1000138002, + DESCRIPTOR_POOL_INLINE_UNIFORM_BLOCK_CREATE_INFO = 1000138003, + PHYSICAL_DEVICE_TEXTURE_COMPRESSION_ASTC_HDR_FEATURES = 1000066000, + RENDERING_INFO = 1000044000, + RENDERING_ATTACHMENT_INFO = 1000044001, + PIPELINE_RENDERING_CREATE_INFO = 1000044002, + PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES = 1000044003, + COMMAND_BUFFER_INHERITANCE_RENDERING_INFO = 1000044004, + PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_FEATURES = 1000280000, + PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_PROPERTIES = 1000280001, + PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_PROPERTIES = 1000281001, + FORMAT_PROPERTIES_3 = 1000360000, + PHYSICAL_DEVICE_MAINTENANCE_4_FEATURES = 1000413000, + PHYSICAL_DEVICE_MAINTENANCE_4_PROPERTIES = 1000413001, + DEVICE_BUFFER_MEMORY_REQUIREMENTS = 1000413002, + DEVICE_IMAGE_MEMORY_REQUIREMENTS = 1000413003, + SWAPCHAIN_CREATE_INFO_KHR = 1000001000, + PRESENT_INFO_KHR = 1000001001, + DEVICE_GROUP_PRESENT_CAPABILITIES_KHR = 1000060007, + IMAGE_SWAPCHAIN_CREATE_INFO_KHR = 1000060008, + BIND_IMAGE_MEMORY_SWAPCHAIN_INFO_KHR = 1000060009, + ACQUIRE_NEXT_IMAGE_INFO_KHR = 1000060010, + DEVICE_GROUP_PRESENT_INFO_KHR = 1000060011, + DEVICE_GROUP_SWAPCHAIN_CREATE_INFO_KHR = 1000060012, + DISPLAY_MODE_CREATE_INFO_KHR = 1000002000, + DISPLAY_SURFACE_CREATE_INFO_KHR = 1000002001, + DISPLAY_PRESENT_INFO_KHR = 1000003000, + XLIB_SURFACE_CREATE_INFO_KHR = 1000004000, + XCB_SURFACE_CREATE_INFO_KHR = 1000005000, + WAYLAND_SURFACE_CREATE_INFO_KHR = 1000006000, + ANDROID_SURFACE_CREATE_INFO_KHR = 1000008000, + WIN32_SURFACE_CREATE_INFO_KHR = 1000009000, + DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT = 1000011000, + PIPELINE_RASTERIZATION_STATE_RASTERIZATION_ORDER_AMD = 1000018000, + DEBUG_MARKER_OBJECT_NAME_INFO_EXT = 1000022000, + DEBUG_MARKER_OBJECT_TAG_INFO_EXT = 1000022001, + DEBUG_MARKER_MARKER_INFO_EXT = 1000022002, + VIDEO_PROFILE_KHR = 1000023000, + VIDEO_CAPABILITIES_KHR = 1000023001, + VIDEO_PICTURE_RESOURCE_KHR = 1000023002, + VIDEO_GET_MEMORY_PROPERTIES_KHR = 1000023003, + VIDEO_BIND_MEMORY_KHR = 1000023004, + VIDEO_SESSION_CREATE_INFO_KHR = 1000023005, + VIDEO_SESSION_PARAMETERS_CREATE_INFO_KHR = 1000023006, + VIDEO_SESSION_PARAMETERS_UPDATE_INFO_KHR = 1000023007, + VIDEO_BEGIN_CODING_INFO_KHR = 1000023008, + VIDEO_END_CODING_INFO_KHR = 1000023009, + VIDEO_CODING_CONTROL_INFO_KHR = 1000023010, + VIDEO_REFERENCE_SLOT_KHR = 1000023011, + VIDEO_QUEUE_FAMILY_PROPERTIES_2_KHR = 1000023012, + VIDEO_PROFILES_KHR = 1000023013, + PHYSICAL_DEVICE_VIDEO_FORMAT_INFO_KHR = 1000023014, + VIDEO_FORMAT_PROPERTIES_KHR = 1000023015, + QUEUE_FAMILY_QUERY_RESULT_STATUS_PROPERTIES_2_KHR = 1000023016, + VIDEO_DECODE_INFO_KHR = 1000024000, + VIDEO_DECODE_CAPABILITIES_KHR = 1000024001, + DEDICATED_ALLOCATION_IMAGE_CREATE_INFO_NV = 1000026000, + DEDICATED_ALLOCATION_BUFFER_CREATE_INFO_NV = 1000026001, + DEDICATED_ALLOCATION_MEMORY_ALLOCATE_INFO_NV = 1000026002, + PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT = 1000028000, + PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_PROPERTIES_EXT = 1000028001, + PIPELINE_RASTERIZATION_STATE_STREAM_CREATE_INFO_EXT = 1000028002, + CU_MODULE_CREATE_INFO_NVX = 1000029000, + CU_FUNCTION_CREATE_INFO_NVX = 1000029001, + CU_LAUNCH_INFO_NVX = 1000029002, + IMAGE_VIEW_HANDLE_INFO_NVX = 1000030000, + IMAGE_VIEW_ADDRESS_PROPERTIES_NVX = 1000030001, + VIDEO_ENCODE_H264_CAPABILITIES_EXT = 1000038000, + VIDEO_ENCODE_H264_SESSION_PARAMETERS_CREATE_INFO_EXT = 1000038001, + VIDEO_ENCODE_H264_SESSION_PARAMETERS_ADD_INFO_EXT = 1000038002, + VIDEO_ENCODE_H264_VCL_FRAME_INFO_EXT = 1000038003, + VIDEO_ENCODE_H264_DPB_SLOT_INFO_EXT = 1000038004, + VIDEO_ENCODE_H264_NALU_SLICE_EXT = 1000038005, + VIDEO_ENCODE_H264_EMIT_PICTURE_PARAMETERS_EXT = 1000038006, + VIDEO_ENCODE_H264_PROFILE_EXT = 1000038007, + VIDEO_ENCODE_H264_RATE_CONTROL_INFO_EXT = 1000038008, + VIDEO_ENCODE_H264_RATE_CONTROL_LAYER_INFO_EXT = 1000038009, + VIDEO_ENCODE_H264_REFERENCE_LISTS_EXT = 1000038010, + VIDEO_ENCODE_H265_CAPABILITIES_EXT = 1000039000, + VIDEO_ENCODE_H265_SESSION_PARAMETERS_CREATE_INFO_EXT = 1000039001, + VIDEO_ENCODE_H265_SESSION_PARAMETERS_ADD_INFO_EXT = 1000039002, + VIDEO_ENCODE_H265_VCL_FRAME_INFO_EXT = 1000039003, + VIDEO_ENCODE_H265_DPB_SLOT_INFO_EXT = 1000039004, + VIDEO_ENCODE_H265_NALU_SLICE_SEGMENT_EXT = 1000039005, + VIDEO_ENCODE_H265_EMIT_PICTURE_PARAMETERS_EXT = 1000039006, + VIDEO_ENCODE_H265_PROFILE_EXT = 1000039007, + VIDEO_ENCODE_H265_REFERENCE_LISTS_EXT = 1000039008, + VIDEO_ENCODE_H265_RATE_CONTROL_INFO_EXT = 1000039009, + VIDEO_ENCODE_H265_RATE_CONTROL_LAYER_INFO_EXT = 1000039010, + VIDEO_DECODE_H264_CAPABILITIES_EXT = 1000040000, + VIDEO_DECODE_H264_PICTURE_INFO_EXT = 1000040001, + VIDEO_DECODE_H264_MVC_EXT = 1000040002, + VIDEO_DECODE_H264_PROFILE_EXT = 1000040003, + VIDEO_DECODE_H264_SESSION_PARAMETERS_CREATE_INFO_EXT = 1000040004, + VIDEO_DECODE_H264_SESSION_PARAMETERS_ADD_INFO_EXT = 1000040005, + VIDEO_DECODE_H264_DPB_SLOT_INFO_EXT = 1000040006, + TEXTURE_LOD_GATHER_FORMAT_PROPERTIES_AMD = 1000041000, + RENDERING_FRAGMENT_SHADING_RATE_ATTACHMENT_INFO_KHR = 1000044006, + RENDERING_FRAGMENT_DENSITY_MAP_ATTACHMENT_INFO_EXT = 1000044007, + ATTACHMENT_SAMPLE_COUNT_INFO_AMD = 1000044008, + MULTIVIEW_PER_VIEW_ATTRIBUTES_INFO_NVX = 1000044009, + STREAM_DESCRIPTOR_SURFACE_CREATE_INFO_GGP = 1000049000, + PHYSICAL_DEVICE_CORNER_SAMPLED_IMAGE_FEATURES_NV = 1000050000, + EXTERNAL_MEMORY_IMAGE_CREATE_INFO_NV = 1000056000, + EXPORT_MEMORY_ALLOCATE_INFO_NV = 1000056001, + IMPORT_MEMORY_WIN32_HANDLE_INFO_NV = 1000057000, + EXPORT_MEMORY_WIN32_HANDLE_INFO_NV = 1000057001, + WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_NV = 1000058000, + VALIDATION_FLAGS_EXT = 1000061000, + VI_SURFACE_CREATE_INFO_NN = 1000062000, + IMAGE_VIEW_ASTC_DECODE_MODE_EXT = 1000067000, + PHYSICAL_DEVICE_ASTC_DECODE_FEATURES_EXT = 1000067001, + IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR = 1000073000, + EXPORT_MEMORY_WIN32_HANDLE_INFO_KHR = 1000073001, + MEMORY_WIN32_HANDLE_PROPERTIES_KHR = 1000073002, + MEMORY_GET_WIN32_HANDLE_INFO_KHR = 1000073003, + IMPORT_MEMORY_FD_INFO_KHR = 1000074000, + MEMORY_FD_PROPERTIES_KHR = 1000074001, + MEMORY_GET_FD_INFO_KHR = 1000074002, + WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_KHR = 1000075000, + IMPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHR = 1000078000, + EXPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHR = 1000078001, + D3D12_FENCE_SUBMIT_INFO_KHR = 1000078002, + SEMAPHORE_GET_WIN32_HANDLE_INFO_KHR = 1000078003, + IMPORT_SEMAPHORE_FD_INFO_KHR = 1000079000, + SEMAPHORE_GET_FD_INFO_KHR = 1000079001, + PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR = 1000080000, + COMMAND_BUFFER_INHERITANCE_CONDITIONAL_RENDERING_INFO_EXT = 1000081000, + PHYSICAL_DEVICE_CONDITIONAL_RENDERING_FEATURES_EXT = 1000081001, + CONDITIONAL_RENDERING_BEGIN_INFO_EXT = 1000081002, + PRESENT_REGIONS_KHR = 1000084000, + PIPELINE_VIEWPORT_W_SCALING_STATE_CREATE_INFO_NV = 1000087000, + SURFACE_CAPABILITIES_2_EXT = 1000090000, + DISPLAY_POWER_INFO_EXT = 1000091000, + DEVICE_EVENT_INFO_EXT = 1000091001, + DISPLAY_EVENT_INFO_EXT = 1000091002, + SWAPCHAIN_COUNTER_CREATE_INFO_EXT = 1000091003, + PRESENT_TIMES_INFO_GOOGLE = 1000092000, + PHYSICAL_DEVICE_MULTIVIEW_PER_VIEW_ATTRIBUTES_PROPERTIES_NVX = 1000097000, + PIPELINE_VIEWPORT_SWIZZLE_STATE_CREATE_INFO_NV = 1000098000, + PHYSICAL_DEVICE_DISCARD_RECTANGLE_PROPERTIES_EXT = 1000099000, + PIPELINE_DISCARD_RECTANGLE_STATE_CREATE_INFO_EXT = 1000099001, + PHYSICAL_DEVICE_CONSERVATIVE_RASTERIZATION_PROPERTIES_EXT = 1000101000, + PIPELINE_RASTERIZATION_CONSERVATIVE_STATE_CREATE_INFO_EXT = 1000101001, + PHYSICAL_DEVICE_DEPTH_CLIP_ENABLE_FEATURES_EXT = 1000102000, + PIPELINE_RASTERIZATION_DEPTH_CLIP_STATE_CREATE_INFO_EXT = 1000102001, + HDR_METADATA_EXT = 1000105000, + SHARED_PRESENT_SURFACE_CAPABILITIES_KHR = 1000111000, + IMPORT_FENCE_WIN32_HANDLE_INFO_KHR = 1000114000, + EXPORT_FENCE_WIN32_HANDLE_INFO_KHR = 1000114001, + FENCE_GET_WIN32_HANDLE_INFO_KHR = 1000114002, + IMPORT_FENCE_FD_INFO_KHR = 1000115000, + FENCE_GET_FD_INFO_KHR = 1000115001, + PHYSICAL_DEVICE_PERFORMANCE_QUERY_FEATURES_KHR = 1000116000, + PHYSICAL_DEVICE_PERFORMANCE_QUERY_PROPERTIES_KHR = 1000116001, + QUERY_POOL_PERFORMANCE_CREATE_INFO_KHR = 1000116002, + PERFORMANCE_QUERY_SUBMIT_INFO_KHR = 1000116003, + ACQUIRE_PROFILING_LOCK_INFO_KHR = 1000116004, + PERFORMANCE_COUNTER_KHR = 1000116005, + PERFORMANCE_COUNTER_DESCRIPTION_KHR = 1000116006, + PHYSICAL_DEVICE_SURFACE_INFO_2_KHR = 1000119000, + SURFACE_CAPABILITIES_2_KHR = 1000119001, + SURFACE_FORMAT_2_KHR = 1000119002, + DISPLAY_PROPERTIES_2_KHR = 1000121000, + DISPLAY_PLANE_PROPERTIES_2_KHR = 1000121001, + DISPLAY_MODE_PROPERTIES_2_KHR = 1000121002, + DISPLAY_PLANE_INFO_2_KHR = 1000121003, + DISPLAY_PLANE_CAPABILITIES_2_KHR = 1000121004, + IOS_SURFACE_CREATE_INFO_MVK = 1000122000, + MACOS_SURFACE_CREATE_INFO_MVK = 1000123000, + DEBUG_UTILS_OBJECT_NAME_INFO_EXT = 1000128000, + DEBUG_UTILS_OBJECT_TAG_INFO_EXT = 1000128001, + DEBUG_UTILS_LABEL_EXT = 1000128002, + DEBUG_UTILS_MESSENGER_CALLBACK_DATA_EXT = 1000128003, + DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT = 1000128004, + ANDROID_HARDWARE_BUFFER_USAGE_ANDROID = 1000129000, + ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID = 1000129001, + ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID = 1000129002, + IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID = 1000129003, + MEMORY_GET_ANDROID_HARDWARE_BUFFER_INFO_ANDROID = 1000129004, + EXTERNAL_FORMAT_ANDROID = 1000129005, + ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_2_ANDROID = 1000129006, + SAMPLE_LOCATIONS_INFO_EXT = 1000143000, + RENDER_PASS_SAMPLE_LOCATIONS_BEGIN_INFO_EXT = 1000143001, + PIPELINE_SAMPLE_LOCATIONS_STATE_CREATE_INFO_EXT = 1000143002, + PHYSICAL_DEVICE_SAMPLE_LOCATIONS_PROPERTIES_EXT = 1000143003, + MULTISAMPLE_PROPERTIES_EXT = 1000143004, + PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_FEATURES_EXT = 1000148000, + PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_PROPERTIES_EXT = 1000148001, + PIPELINE_COLOR_BLEND_ADVANCED_STATE_CREATE_INFO_EXT = 1000148002, + PIPELINE_COVERAGE_TO_COLOR_STATE_CREATE_INFO_NV = 1000149000, + WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR = 1000150007, + ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR = 1000150000, + ACCELERATION_STRUCTURE_DEVICE_ADDRESS_INFO_KHR = 1000150002, + ACCELERATION_STRUCTURE_GEOMETRY_AABBS_DATA_KHR = 1000150003, + ACCELERATION_STRUCTURE_GEOMETRY_INSTANCES_DATA_KHR = 1000150004, + ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR = 1000150005, + ACCELERATION_STRUCTURE_GEOMETRY_KHR = 1000150006, + ACCELERATION_STRUCTURE_VERSION_INFO_KHR = 1000150009, + COPY_ACCELERATION_STRUCTURE_INFO_KHR = 1000150010, + COPY_ACCELERATION_STRUCTURE_TO_MEMORY_INFO_KHR = 1000150011, + COPY_MEMORY_TO_ACCELERATION_STRUCTURE_INFO_KHR = 1000150012, + PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR = 1000150013, + PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_PROPERTIES_KHR = 1000150014, + ACCELERATION_STRUCTURE_CREATE_INFO_KHR = 1000150017, + ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR = 1000150020, + PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR = 1000347000, + PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_PROPERTIES_KHR = 1000347001, + RAY_TRACING_PIPELINE_CREATE_INFO_KHR = 1000150015, + RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR = 1000150016, + RAY_TRACING_PIPELINE_INTERFACE_CREATE_INFO_KHR = 1000150018, + PHYSICAL_DEVICE_RAY_QUERY_FEATURES_KHR = 1000348013, + PIPELINE_COVERAGE_MODULATION_STATE_CREATE_INFO_NV = 1000152000, + PHYSICAL_DEVICE_SHADER_SM_BUILTINS_FEATURES_NV = 1000154000, + PHYSICAL_DEVICE_SHADER_SM_BUILTINS_PROPERTIES_NV = 1000154001, + DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT = 1000158000, + PHYSICAL_DEVICE_IMAGE_DRM_FORMAT_MODIFIER_INFO_EXT = 1000158002, + IMAGE_DRM_FORMAT_MODIFIER_LIST_CREATE_INFO_EXT = 1000158003, + IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT = 1000158004, + IMAGE_DRM_FORMAT_MODIFIER_PROPERTIES_EXT = 1000158005, + DRM_FORMAT_MODIFIER_PROPERTIES_LIST_2_EXT = 1000158006, + VALIDATION_CACHE_CREATE_INFO_EXT = 1000160000, + SHADER_MODULE_VALIDATION_CACHE_CREATE_INFO_EXT = 1000160001, + PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_KHR = 1000163000, + PHYSICAL_DEVICE_PORTABILITY_SUBSET_PROPERTIES_KHR = 1000163001, + PIPELINE_VIEWPORT_SHADING_RATE_IMAGE_STATE_CREATE_INFO_NV = 1000164000, + PHYSICAL_DEVICE_SHADING_RATE_IMAGE_FEATURES_NV = 1000164001, + PHYSICAL_DEVICE_SHADING_RATE_IMAGE_PROPERTIES_NV = 1000164002, + PIPELINE_VIEWPORT_COARSE_SAMPLE_ORDER_STATE_CREATE_INFO_NV = 1000164005, + RAY_TRACING_PIPELINE_CREATE_INFO_NV = 1000165000, + ACCELERATION_STRUCTURE_CREATE_INFO_NV = 1000165001, + GEOMETRY_NV = 1000165003, + GEOMETRY_TRIANGLES_NV = 1000165004, + GEOMETRY_AABB_NV = 1000165005, + BIND_ACCELERATION_STRUCTURE_MEMORY_INFO_NV = 1000165006, + WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_NV = 1000165007, + ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_INFO_NV = 1000165008, + PHYSICAL_DEVICE_RAY_TRACING_PROPERTIES_NV = 1000165009, + RAY_TRACING_SHADER_GROUP_CREATE_INFO_NV = 1000165011, + ACCELERATION_STRUCTURE_INFO_NV = 1000165012, + PHYSICAL_DEVICE_REPRESENTATIVE_FRAGMENT_TEST_FEATURES_NV = 1000166000, + PIPELINE_REPRESENTATIVE_FRAGMENT_TEST_STATE_CREATE_INFO_NV = 1000166001, + PHYSICAL_DEVICE_IMAGE_VIEW_IMAGE_FORMAT_INFO_EXT = 1000170000, + FILTER_CUBIC_IMAGE_VIEW_IMAGE_FORMAT_PROPERTIES_EXT = 1000170001, + IMPORT_MEMORY_HOST_POINTER_INFO_EXT = 1000178000, + MEMORY_HOST_POINTER_PROPERTIES_EXT = 1000178001, + PHYSICAL_DEVICE_EXTERNAL_MEMORY_HOST_PROPERTIES_EXT = 1000178002, + PHYSICAL_DEVICE_SHADER_CLOCK_FEATURES_KHR = 1000181000, + PIPELINE_COMPILER_CONTROL_CREATE_INFO_AMD = 1000183000, + CALIBRATED_TIMESTAMP_INFO_EXT = 1000184000, + PHYSICAL_DEVICE_SHADER_CORE_PROPERTIES_AMD = 1000185000, + VIDEO_DECODE_H265_CAPABILITIES_EXT = 1000187000, + VIDEO_DECODE_H265_SESSION_PARAMETERS_CREATE_INFO_EXT = 1000187001, + VIDEO_DECODE_H265_SESSION_PARAMETERS_ADD_INFO_EXT = 1000187002, + VIDEO_DECODE_H265_PROFILE_EXT = 1000187003, + VIDEO_DECODE_H265_PICTURE_INFO_EXT = 1000187004, + VIDEO_DECODE_H265_DPB_SLOT_INFO_EXT = 1000187005, + DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_KHR = 1000174000, + PHYSICAL_DEVICE_GLOBAL_PRIORITY_QUERY_FEATURES_KHR = 1000388000, + QUEUE_FAMILY_GLOBAL_PRIORITY_PROPERTIES_KHR = 1000388001, + DEVICE_MEMORY_OVERALLOCATION_CREATE_INFO_AMD = 1000189000, + PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_PROPERTIES_EXT = 1000190000, + PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT = 1000190001, + PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_EXT = 1000190002, + PRESENT_FRAME_TOKEN_GGP = 1000191000, + PHYSICAL_DEVICE_COMPUTE_SHADER_DERIVATIVES_FEATURES_NV = 1000201000, + PHYSICAL_DEVICE_MESH_SHADER_FEATURES_NV = 1000202000, + PHYSICAL_DEVICE_MESH_SHADER_PROPERTIES_NV = 1000202001, + PHYSICAL_DEVICE_FRAGMENT_SHADER_BARYCENTRIC_FEATURES_NV = 1000203000, + PHYSICAL_DEVICE_SHADER_IMAGE_FOOTPRINT_FEATURES_NV = 1000204000, + PIPELINE_VIEWPORT_EXCLUSIVE_SCISSOR_STATE_CREATE_INFO_NV = 1000205000, + PHYSICAL_DEVICE_EXCLUSIVE_SCISSOR_FEATURES_NV = 1000205002, + CHECKPOINT_DATA_NV = 1000206000, + QUEUE_FAMILY_CHECKPOINT_PROPERTIES_NV = 1000206001, + PHYSICAL_DEVICE_SHADER_INTEGER_FUNCTIONS_2_FEATURES_INTEL = 1000209000, + QUERY_POOL_PERFORMANCE_QUERY_CREATE_INFO_INTEL = 1000210000, + INITIALIZE_PERFORMANCE_API_INFO_INTEL = 1000210001, + PERFORMANCE_MARKER_INFO_INTEL = 1000210002, + PERFORMANCE_STREAM_MARKER_INFO_INTEL = 1000210003, + PERFORMANCE_OVERRIDE_INFO_INTEL = 1000210004, + PERFORMANCE_CONFIGURATION_ACQUIRE_INFO_INTEL = 1000210005, + PHYSICAL_DEVICE_PCI_BUS_INFO_PROPERTIES_EXT = 1000212000, + DISPLAY_NATIVE_HDR_SURFACE_CAPABILITIES_AMD = 1000213000, + SWAPCHAIN_DISPLAY_NATIVE_HDR_CREATE_INFO_AMD = 1000213001, + IMAGEPIPE_SURFACE_CREATE_INFO_FUCHSIA = 1000214000, + METAL_SURFACE_CREATE_INFO_EXT = 1000217000, + PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_FEATURES_EXT = 1000218000, + PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_PROPERTIES_EXT = 1000218001, + RENDER_PASS_FRAGMENT_DENSITY_MAP_CREATE_INFO_EXT = 1000218002, + FRAGMENT_SHADING_RATE_ATTACHMENT_INFO_KHR = 1000226000, + PIPELINE_FRAGMENT_SHADING_RATE_STATE_CREATE_INFO_KHR = 1000226001, + PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_PROPERTIES_KHR = 1000226002, + PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_FEATURES_KHR = 1000226003, + PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_KHR = 1000226004, + PHYSICAL_DEVICE_SHADER_CORE_PROPERTIES_2_AMD = 1000227000, + PHYSICAL_DEVICE_COHERENT_MEMORY_FEATURES_AMD = 1000229000, + PHYSICAL_DEVICE_SHADER_IMAGE_ATOMIC_INT64_FEATURES_EXT = 1000234000, + PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT = 1000237000, + PHYSICAL_DEVICE_MEMORY_PRIORITY_FEATURES_EXT = 1000238000, + MEMORY_PRIORITY_ALLOCATE_INFO_EXT = 1000238001, + SURFACE_PROTECTED_CAPABILITIES_KHR = 1000239000, + PHYSICAL_DEVICE_DEDICATED_ALLOCATION_IMAGE_ALIASING_FEATURES_NV = 1000240000, + PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_EXT = 1000244000, + BUFFER_DEVICE_ADDRESS_CREATE_INFO_EXT = 1000244002, + VALIDATION_FEATURES_EXT = 1000247000, + PHYSICAL_DEVICE_PRESENT_WAIT_FEATURES_KHR = 1000248000, + PHYSICAL_DEVICE_COOPERATIVE_MATRIX_FEATURES_NV = 1000249000, + COOPERATIVE_MATRIX_PROPERTIES_NV = 1000249001, + PHYSICAL_DEVICE_COOPERATIVE_MATRIX_PROPERTIES_NV = 1000249002, + PHYSICAL_DEVICE_COVERAGE_REDUCTION_MODE_FEATURES_NV = 1000250000, + PIPELINE_COVERAGE_REDUCTION_STATE_CREATE_INFO_NV = 1000250001, + FRAMEBUFFER_MIXED_SAMPLES_COMBINATION_NV = 1000250002, + PHYSICAL_DEVICE_FRAGMENT_SHADER_INTERLOCK_FEATURES_EXT = 1000251000, + PHYSICAL_DEVICE_YCBCR_IMAGE_ARRAYS_FEATURES_EXT = 1000252000, + PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT = 1000254000, + PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT = 1000254001, + PHYSICAL_DEVICE_PROVOKING_VERTEX_PROPERTIES_EXT = 1000254002, + SURFACE_FULL_SCREEN_EXCLUSIVE_INFO_EXT = 1000255000, + SURFACE_CAPABILITIES_FULL_SCREEN_EXCLUSIVE_EXT = 1000255002, + SURFACE_FULL_SCREEN_EXCLUSIVE_WIN32_INFO_EXT = 1000255001, + HEADLESS_SURFACE_CREATE_INFO_EXT = 1000256000, + PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT = 1000259000, + PIPELINE_RASTERIZATION_LINE_STATE_CREATE_INFO_EXT = 1000259001, + PHYSICAL_DEVICE_LINE_RASTERIZATION_PROPERTIES_EXT = 1000259002, + PHYSICAL_DEVICE_SHADER_ATOMIC_FLOAT_FEATURES_EXT = 1000260000, + PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES_EXT = 1000265000, + PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT = 1000267000, + PHYSICAL_DEVICE_PIPELINE_EXECUTABLE_PROPERTIES_FEATURES_KHR = 1000269000, + PIPELINE_INFO_KHR = 1000269001, + PIPELINE_EXECUTABLE_PROPERTIES_KHR = 1000269002, + PIPELINE_EXECUTABLE_INFO_KHR = 1000269003, + PIPELINE_EXECUTABLE_STATISTIC_KHR = 1000269004, + PIPELINE_EXECUTABLE_INTERNAL_REPRESENTATION_KHR = 1000269005, + PHYSICAL_DEVICE_SHADER_ATOMIC_FLOAT_2_FEATURES_EXT = 1000273000, + PHYSICAL_DEVICE_DEVICE_GENERATED_COMMANDS_PROPERTIES_NV = 1000277000, + GRAPHICS_SHADER_GROUP_CREATE_INFO_NV = 1000277001, + GRAPHICS_PIPELINE_SHADER_GROUPS_CREATE_INFO_NV = 1000277002, + INDIRECT_COMMANDS_LAYOUT_TOKEN_NV = 1000277003, + INDIRECT_COMMANDS_LAYOUT_CREATE_INFO_NV = 1000277004, + GENERATED_COMMANDS_INFO_NV = 1000277005, + GENERATED_COMMANDS_MEMORY_REQUIREMENTS_INFO_NV = 1000277006, + PHYSICAL_DEVICE_DEVICE_GENERATED_COMMANDS_FEATURES_NV = 1000277007, + PHYSICAL_DEVICE_INHERITED_VIEWPORT_SCISSOR_FEATURES_NV = 1000278000, + COMMAND_BUFFER_INHERITANCE_VIEWPORT_SCISSOR_INFO_NV = 1000278001, + PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_FEATURES_EXT = 1000281000, + COMMAND_BUFFER_INHERITANCE_RENDER_PASS_TRANSFORM_INFO_QCOM = 1000282000, + RENDER_PASS_TRANSFORM_BEGIN_INFO_QCOM = 1000282001, + PHYSICAL_DEVICE_DEVICE_MEMORY_REPORT_FEATURES_EXT = 1000284000, + DEVICE_DEVICE_MEMORY_REPORT_CREATE_INFO_EXT = 1000284001, + DEVICE_MEMORY_REPORT_CALLBACK_DATA_EXT = 1000284002, + PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT = 1000286000, + PHYSICAL_DEVICE_ROBUSTNESS_2_PROPERTIES_EXT = 1000286001, + SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT = 1000287000, + PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_PROPERTIES_EXT = 1000287001, + PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT = 1000287002, + PIPELINE_LIBRARY_CREATE_INFO_KHR = 1000290000, + PRESENT_ID_KHR = 1000294000, + PHYSICAL_DEVICE_PRESENT_ID_FEATURES_KHR = 1000294001, + VIDEO_ENCODE_INFO_KHR = 1000299000, + VIDEO_ENCODE_RATE_CONTROL_INFO_KHR = 1000299001, + VIDEO_ENCODE_RATE_CONTROL_LAYER_INFO_KHR = 1000299002, + VIDEO_ENCODE_CAPABILITIES_KHR = 1000299003, + PHYSICAL_DEVICE_DIAGNOSTICS_CONFIG_FEATURES_NV = 1000300000, + DEVICE_DIAGNOSTICS_CONFIG_CREATE_INFO_NV = 1000300001, + QUEUE_FAMILY_CHECKPOINT_PROPERTIES_2_NV = 1000314008, + CHECKPOINT_DATA_2_NV = 1000314009, + PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_FEATURES_EXT = 1000320000, + PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_PROPERTIES_EXT = 1000320001, + GRAPHICS_PIPELINE_LIBRARY_CREATE_INFO_EXT = 1000320002, + PHYSICAL_DEVICE_SHADER_SUBGROUP_UNIFORM_CONTROL_FLOW_FEATURES_KHR = 1000323000, + PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_ENUMS_PROPERTIES_NV = 1000326000, + PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_ENUMS_FEATURES_NV = 1000326001, + PIPELINE_FRAGMENT_SHADING_RATE_ENUM_STATE_CREATE_INFO_NV = 1000326002, + ACCELERATION_STRUCTURE_GEOMETRY_MOTION_TRIANGLES_DATA_NV = 1000327000, + PHYSICAL_DEVICE_RAY_TRACING_MOTION_BLUR_FEATURES_NV = 1000327001, + ACCELERATION_STRUCTURE_MOTION_INFO_NV = 1000327002, + PHYSICAL_DEVICE_YCBCR_2_PLANE_444_FORMATS_FEATURES_EXT = 1000330000, + PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_2_FEATURES_EXT = 1000332000, + PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_2_PROPERTIES_EXT = 1000332001, + COPY_COMMAND_TRANSFORM_INFO_QCOM = 1000333000, + PHYSICAL_DEVICE_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_FEATURES_KHR = 1000336000, + PHYSICAL_DEVICE_4444_FORMATS_FEATURES_EXT = 1000340000, + PHYSICAL_DEVICE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_FEATURES_ARM = 1000342000, + PHYSICAL_DEVICE_RGBA10X6_FORMATS_FEATURES_EXT = 1000344000, + DIRECTFB_SURFACE_CREATE_INFO_EXT = 1000346000, + PHYSICAL_DEVICE_MUTABLE_DESCRIPTOR_TYPE_FEATURES_VALVE = 1000351000, + MUTABLE_DESCRIPTOR_TYPE_CREATE_INFO_VALVE = 1000351002, + PHYSICAL_DEVICE_VERTEX_INPUT_DYNAMIC_STATE_FEATURES_EXT = 1000352000, + VERTEX_INPUT_BINDING_DESCRIPTION_2_EXT = 1000352001, + VERTEX_INPUT_ATTRIBUTE_DESCRIPTION_2_EXT = 1000352002, + PHYSICAL_DEVICE_DRM_PROPERTIES_EXT = 1000353000, + PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT = 1000355000, + PIPELINE_VIEWPORT_DEPTH_CLIP_CONTROL_CREATE_INFO_EXT = 1000355001, + PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT = 1000356000, + IMPORT_MEMORY_ZIRCON_HANDLE_INFO_FUCHSIA = 1000364000, + MEMORY_ZIRCON_HANDLE_PROPERTIES_FUCHSIA = 1000364001, + MEMORY_GET_ZIRCON_HANDLE_INFO_FUCHSIA = 1000364002, + IMPORT_SEMAPHORE_ZIRCON_HANDLE_INFO_FUCHSIA = 1000365000, + SEMAPHORE_GET_ZIRCON_HANDLE_INFO_FUCHSIA = 1000365001, + BUFFER_COLLECTION_CREATE_INFO_FUCHSIA = 1000366000, + IMPORT_MEMORY_BUFFER_COLLECTION_FUCHSIA = 1000366001, + BUFFER_COLLECTION_IMAGE_CREATE_INFO_FUCHSIA = 1000366002, + BUFFER_COLLECTION_PROPERTIES_FUCHSIA = 1000366003, + BUFFER_CONSTRAINTS_INFO_FUCHSIA = 1000366004, + BUFFER_COLLECTION_BUFFER_CREATE_INFO_FUCHSIA = 1000366005, + IMAGE_CONSTRAINTS_INFO_FUCHSIA = 1000366006, + IMAGE_FORMAT_CONSTRAINTS_INFO_FUCHSIA = 1000366007, + SYSMEM_COLOR_SPACE_FUCHSIA = 1000366008, + BUFFER_COLLECTION_CONSTRAINTS_INFO_FUCHSIA = 1000366009, + SUBPASS_SHADING_PIPELINE_CREATE_INFO_HUAWEI = 1000369000, + PHYSICAL_DEVICE_SUBPASS_SHADING_FEATURES_HUAWEI = 1000369001, + PHYSICAL_DEVICE_SUBPASS_SHADING_PROPERTIES_HUAWEI = 1000369002, + PHYSICAL_DEVICE_INVOCATION_MASK_FEATURES_HUAWEI = 1000370000, + MEMORY_GET_REMOTE_ADDRESS_INFO_NV = 1000371000, + PHYSICAL_DEVICE_EXTERNAL_MEMORY_RDMA_FEATURES_NV = 1000371001, + PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_2_FEATURES_EXT = 1000377000, + SCREEN_SURFACE_CREATE_INFO_QNX = 1000378000, + PHYSICAL_DEVICE_COLOR_WRITE_ENABLE_FEATURES_EXT = 1000381000, + PIPELINE_COLOR_WRITE_CREATE_INFO_EXT = 1000381001, + PHYSICAL_DEVICE_PRIMITIVES_GENERATED_QUERY_FEATURES_EXT = 1000382000, + PHYSICAL_DEVICE_IMAGE_VIEW_MIN_LOD_FEATURES_EXT = 1000391000, + IMAGE_VIEW_MIN_LOD_CREATE_INFO_EXT = 1000391001, + PHYSICAL_DEVICE_MULTI_DRAW_FEATURES_EXT = 1000392000, + PHYSICAL_DEVICE_MULTI_DRAW_PROPERTIES_EXT = 1000392001, + PHYSICAL_DEVICE_IMAGE_2D_VIEW_OF_3D_FEATURES_EXT = 1000393000, + PHYSICAL_DEVICE_BORDER_COLOR_SWIZZLE_FEATURES_EXT = 1000411000, + SAMPLER_BORDER_COLOR_COMPONENT_MAPPING_CREATE_INFO_EXT = 1000411001, + PHYSICAL_DEVICE_PAGEABLE_DEVICE_LOCAL_MEMORY_FEATURES_EXT = 1000412000, + PHYSICAL_DEVICE_DESCRIPTOR_SET_HOST_MAPPING_FEATURES_VALVE = 1000420000, + DESCRIPTOR_SET_BINDING_REFERENCE_VALVE = 1000420001, + DESCRIPTOR_SET_LAYOUT_HOST_MAPPING_INFO_VALVE = 1000420002, + PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_OFFSET_FEATURES_QCOM = 1000425000, + PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_OFFSET_PROPERTIES_QCOM = 1000425001, + SUBPASS_FRAGMENT_DENSITY_MAP_OFFSET_END_INFO_QCOM = 1000425002, + PHYSICAL_DEVICE_LINEAR_COLOR_ATTACHMENT_FEATURES_NV = 1000430000, + PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES = PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES, + PHYSICAL_DEVICE_SHADER_DRAW_PARAMETER_FEATURES = PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES, + DEBUG_REPORT_CREATE_INFO_EXT = DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT, + RENDERING_INFO_KHR = RENDERING_INFO, + RENDERING_ATTACHMENT_INFO_KHR = RENDERING_ATTACHMENT_INFO, + PIPELINE_RENDERING_CREATE_INFO_KHR = PIPELINE_RENDERING_CREATE_INFO, + PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES_KHR = PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES, + COMMAND_BUFFER_INHERITANCE_RENDERING_INFO_KHR = COMMAND_BUFFER_INHERITANCE_RENDERING_INFO, + ATTACHMENT_SAMPLE_COUNT_INFO_NV = ATTACHMENT_SAMPLE_COUNT_INFO_AMD, + RENDER_PASS_MULTIVIEW_CREATE_INFO_KHR = RENDER_PASS_MULTIVIEW_CREATE_INFO, + PHYSICAL_DEVICE_MULTIVIEW_FEATURES_KHR = PHYSICAL_DEVICE_MULTIVIEW_FEATURES, + PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES_KHR = PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES, + PHYSICAL_DEVICE_FEATURES_2_KHR = PHYSICAL_DEVICE_FEATURES_2, + PHYSICAL_DEVICE_PROPERTIES_2_KHR = PHYSICAL_DEVICE_PROPERTIES_2, + FORMAT_PROPERTIES_2_KHR = FORMAT_PROPERTIES_2, + IMAGE_FORMAT_PROPERTIES_2_KHR = IMAGE_FORMAT_PROPERTIES_2, + PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHR = PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2, + QUEUE_FAMILY_PROPERTIES_2_KHR = QUEUE_FAMILY_PROPERTIES_2, + PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR = PHYSICAL_DEVICE_MEMORY_PROPERTIES_2, + SPARSE_IMAGE_FORMAT_PROPERTIES_2_KHR = SPARSE_IMAGE_FORMAT_PROPERTIES_2, + PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2_KHR = PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2, + MEMORY_ALLOCATE_FLAGS_INFO_KHR = MEMORY_ALLOCATE_FLAGS_INFO, + DEVICE_GROUP_RENDER_PASS_BEGIN_INFO_KHR = DEVICE_GROUP_RENDER_PASS_BEGIN_INFO, + DEVICE_GROUP_COMMAND_BUFFER_BEGIN_INFO_KHR = DEVICE_GROUP_COMMAND_BUFFER_BEGIN_INFO, + DEVICE_GROUP_SUBMIT_INFO_KHR = DEVICE_GROUP_SUBMIT_INFO, + DEVICE_GROUP_BIND_SPARSE_INFO_KHR = DEVICE_GROUP_BIND_SPARSE_INFO, + BIND_BUFFER_MEMORY_DEVICE_GROUP_INFO_KHR = BIND_BUFFER_MEMORY_DEVICE_GROUP_INFO, + BIND_IMAGE_MEMORY_DEVICE_GROUP_INFO_KHR = BIND_IMAGE_MEMORY_DEVICE_GROUP_INFO, + PHYSICAL_DEVICE_TEXTURE_COMPRESSION_ASTC_HDR_FEATURES_EXT = PHYSICAL_DEVICE_TEXTURE_COMPRESSION_ASTC_HDR_FEATURES, + PHYSICAL_DEVICE_GROUP_PROPERTIES_KHR = PHYSICAL_DEVICE_GROUP_PROPERTIES, + DEVICE_GROUP_DEVICE_CREATE_INFO_KHR = DEVICE_GROUP_DEVICE_CREATE_INFO, + PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO_KHR = PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO, + EXTERNAL_IMAGE_FORMAT_PROPERTIES_KHR = EXTERNAL_IMAGE_FORMAT_PROPERTIES, + PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO_KHR = PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO, + EXTERNAL_BUFFER_PROPERTIES_KHR = EXTERNAL_BUFFER_PROPERTIES, + PHYSICAL_DEVICE_ID_PROPERTIES_KHR = PHYSICAL_DEVICE_ID_PROPERTIES, + EXTERNAL_MEMORY_BUFFER_CREATE_INFO_KHR = EXTERNAL_MEMORY_BUFFER_CREATE_INFO, + EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHR = EXTERNAL_MEMORY_IMAGE_CREATE_INFO, + EXPORT_MEMORY_ALLOCATE_INFO_KHR = EXPORT_MEMORY_ALLOCATE_INFO, + PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO_KHR = PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO, + EXTERNAL_SEMAPHORE_PROPERTIES_KHR = EXTERNAL_SEMAPHORE_PROPERTIES, + EXPORT_SEMAPHORE_CREATE_INFO_KHR = EXPORT_SEMAPHORE_CREATE_INFO, + PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR = PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES, + PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR = PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES, + PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR = PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES, + DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO_KHR = DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO, + SURFACE_CAPABILITIES2_EXT = SURFACE_CAPABILITIES_2_EXT, + PHYSICAL_DEVICE_IMAGELESS_FRAMEBUFFER_FEATURES_KHR = PHYSICAL_DEVICE_IMAGELESS_FRAMEBUFFER_FEATURES, + FRAMEBUFFER_ATTACHMENTS_CREATE_INFO_KHR = FRAMEBUFFER_ATTACHMENTS_CREATE_INFO, + FRAMEBUFFER_ATTACHMENT_IMAGE_INFO_KHR = FRAMEBUFFER_ATTACHMENT_IMAGE_INFO, + RENDER_PASS_ATTACHMENT_BEGIN_INFO_KHR = RENDER_PASS_ATTACHMENT_BEGIN_INFO, + ATTACHMENT_DESCRIPTION_2_KHR = ATTACHMENT_DESCRIPTION_2, + ATTACHMENT_REFERENCE_2_KHR = ATTACHMENT_REFERENCE_2, + SUBPASS_DESCRIPTION_2_KHR = SUBPASS_DESCRIPTION_2, + SUBPASS_DEPENDENCY_2_KHR = SUBPASS_DEPENDENCY_2, + RENDER_PASS_CREATE_INFO_2_KHR = RENDER_PASS_CREATE_INFO_2, + SUBPASS_BEGIN_INFO_KHR = SUBPASS_BEGIN_INFO, + SUBPASS_END_INFO_KHR = SUBPASS_END_INFO, + PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO_KHR = PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO, + EXTERNAL_FENCE_PROPERTIES_KHR = EXTERNAL_FENCE_PROPERTIES, + EXPORT_FENCE_CREATE_INFO_KHR = EXPORT_FENCE_CREATE_INFO, + PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES_KHR = PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES, + RENDER_PASS_INPUT_ATTACHMENT_ASPECT_CREATE_INFO_KHR = RENDER_PASS_INPUT_ATTACHMENT_ASPECT_CREATE_INFO, + IMAGE_VIEW_USAGE_CREATE_INFO_KHR = IMAGE_VIEW_USAGE_CREATE_INFO, + PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO_KHR = PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO, + PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES_KHR = PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES, + PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES_KHR = PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES_KHR, + MEMORY_DEDICATED_REQUIREMENTS_KHR = MEMORY_DEDICATED_REQUIREMENTS, + MEMORY_DEDICATED_ALLOCATE_INFO_KHR = MEMORY_DEDICATED_ALLOCATE_INFO, + PHYSICAL_DEVICE_SAMPLER_FILTER_MINMAX_PROPERTIES_EXT = PHYSICAL_DEVICE_SAMPLER_FILTER_MINMAX_PROPERTIES, + SAMPLER_REDUCTION_MODE_CREATE_INFO_EXT = SAMPLER_REDUCTION_MODE_CREATE_INFO, + PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES_EXT = PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES, + PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_PROPERTIES_EXT = PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_PROPERTIES, + WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK_EXT = WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK, + DESCRIPTOR_POOL_INLINE_UNIFORM_BLOCK_CREATE_INFO_EXT = DESCRIPTOR_POOL_INLINE_UNIFORM_BLOCK_CREATE_INFO, + BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR = BUFFER_MEMORY_REQUIREMENTS_INFO_2, + IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR = IMAGE_MEMORY_REQUIREMENTS_INFO_2, + IMAGE_SPARSE_MEMORY_REQUIREMENTS_INFO_2_KHR = IMAGE_SPARSE_MEMORY_REQUIREMENTS_INFO_2, + MEMORY_REQUIREMENTS_2_KHR = MEMORY_REQUIREMENTS_2, + SPARSE_IMAGE_MEMORY_REQUIREMENTS_2_KHR = SPARSE_IMAGE_MEMORY_REQUIREMENTS_2, + IMAGE_FORMAT_LIST_CREATE_INFO_KHR = IMAGE_FORMAT_LIST_CREATE_INFO, + SAMPLER_YCBCR_CONVERSION_CREATE_INFO_KHR = SAMPLER_YCBCR_CONVERSION_CREATE_INFO, + SAMPLER_YCBCR_CONVERSION_INFO_KHR = SAMPLER_YCBCR_CONVERSION_INFO, + BIND_IMAGE_PLANE_MEMORY_INFO_KHR = BIND_IMAGE_PLANE_MEMORY_INFO, + IMAGE_PLANE_MEMORY_REQUIREMENTS_INFO_KHR = IMAGE_PLANE_MEMORY_REQUIREMENTS_INFO, + PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES_KHR = PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES, + SAMPLER_YCBCR_CONVERSION_IMAGE_FORMAT_PROPERTIES_KHR = SAMPLER_YCBCR_CONVERSION_IMAGE_FORMAT_PROPERTIES, + BIND_BUFFER_MEMORY_INFO_KHR = BIND_BUFFER_MEMORY_INFO, + BIND_IMAGE_MEMORY_INFO_KHR = BIND_IMAGE_MEMORY_INFO, + DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO_EXT = DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO, + PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT = PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES, + PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES_EXT = PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES, + DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO_EXT = DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO, + DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_LAYOUT_SUPPORT_EXT = DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_LAYOUT_SUPPORT, + PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES_KHR = PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES, + DESCRIPTOR_SET_LAYOUT_SUPPORT_KHR = DESCRIPTOR_SET_LAYOUT_SUPPORT, + DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_EXT = DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_KHR, + PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES_KHR = PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES, + PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES_KHR = PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES, + PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES_KHR = PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES, + PIPELINE_CREATION_FEEDBACK_CREATE_INFO_EXT = PIPELINE_CREATION_FEEDBACK_CREATE_INFO, + PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR = PHYSICAL_DEVICE_DRIVER_PROPERTIES, + PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES_KHR = PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES, + PHYSICAL_DEVICE_DEPTH_STENCIL_RESOLVE_PROPERTIES_KHR = PHYSICAL_DEVICE_DEPTH_STENCIL_RESOLVE_PROPERTIES, + SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE_KHR = SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE, + PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES_KHR = PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES, + PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_PROPERTIES_KHR = PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_PROPERTIES, + SEMAPHORE_TYPE_CREATE_INFO_KHR = SEMAPHORE_TYPE_CREATE_INFO, + TIMELINE_SEMAPHORE_SUBMIT_INFO_KHR = TIMELINE_SEMAPHORE_SUBMIT_INFO, + SEMAPHORE_WAIT_INFO_KHR = SEMAPHORE_WAIT_INFO, + SEMAPHORE_SIGNAL_INFO_KHR = SEMAPHORE_SIGNAL_INFO, + QUERY_POOL_CREATE_INFO_INTEL = QUERY_POOL_PERFORMANCE_QUERY_CREATE_INFO_INTEL, + PHYSICAL_DEVICE_VULKAN_MEMORY_MODEL_FEATURES_KHR = PHYSICAL_DEVICE_VULKAN_MEMORY_MODEL_FEATURES, + PHYSICAL_DEVICE_SHADER_TERMINATE_INVOCATION_FEATURES_KHR = PHYSICAL_DEVICE_SHADER_TERMINATE_INVOCATION_FEATURES, + PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES_EXT = PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES, + PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES_EXT = PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES, + PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO_EXT = PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO, + PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES_EXT = PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES, + PHYSICAL_DEVICE_SEPARATE_DEPTH_STENCIL_LAYOUTS_FEATURES_KHR = PHYSICAL_DEVICE_SEPARATE_DEPTH_STENCIL_LAYOUTS_FEATURES, + ATTACHMENT_REFERENCE_STENCIL_LAYOUT_KHR = ATTACHMENT_REFERENCE_STENCIL_LAYOUT, + ATTACHMENT_DESCRIPTION_STENCIL_LAYOUT_KHR = ATTACHMENT_DESCRIPTION_STENCIL_LAYOUT, + PHYSICAL_DEVICE_BUFFER_ADDRESS_FEATURES_EXT = PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_EXT, + BUFFER_DEVICE_ADDRESS_INFO_EXT = BUFFER_DEVICE_ADDRESS_INFO, + PHYSICAL_DEVICE_TOOL_PROPERTIES_EXT = PHYSICAL_DEVICE_TOOL_PROPERTIES, + IMAGE_STENCIL_USAGE_CREATE_INFO_EXT = IMAGE_STENCIL_USAGE_CREATE_INFO, + PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES_KHR = PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES, + PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_KHR = PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES, + BUFFER_DEVICE_ADDRESS_INFO_KHR = BUFFER_DEVICE_ADDRESS_INFO, + BUFFER_OPAQUE_CAPTURE_ADDRESS_CREATE_INFO_KHR = BUFFER_OPAQUE_CAPTURE_ADDRESS_CREATE_INFO, + MEMORY_OPAQUE_CAPTURE_ADDRESS_ALLOCATE_INFO_KHR = MEMORY_OPAQUE_CAPTURE_ADDRESS_ALLOCATE_INFO, + DEVICE_MEMORY_OPAQUE_CAPTURE_ADDRESS_INFO_KHR = DEVICE_MEMORY_OPAQUE_CAPTURE_ADDRESS_INFO, + PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES_EXT = PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES, + PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES_EXT = PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES, + PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_FEATURES_KHR = PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_FEATURES, + PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_PROPERTIES_KHR = PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_PROPERTIES, + PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_PROPERTIES_EXT = PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_PROPERTIES, + PHYSICAL_DEVICE_PRIVATE_DATA_FEATURES_EXT = PHYSICAL_DEVICE_PRIVATE_DATA_FEATURES, + DEVICE_PRIVATE_DATA_CREATE_INFO_EXT = DEVICE_PRIVATE_DATA_CREATE_INFO, + PRIVATE_DATA_SLOT_CREATE_INFO_EXT = PRIVATE_DATA_SLOT_CREATE_INFO, + PHYSICAL_DEVICE_PIPELINE_CREATION_CACHE_CONTROL_FEATURES_EXT = PHYSICAL_DEVICE_PIPELINE_CREATION_CACHE_CONTROL_FEATURES, + MEMORY_BARRIER_2_KHR = MEMORY_BARRIER_2, + BUFFER_MEMORY_BARRIER_2_KHR = BUFFER_MEMORY_BARRIER_2, + IMAGE_MEMORY_BARRIER_2_KHR = IMAGE_MEMORY_BARRIER_2, + DEPENDENCY_INFO_KHR = DEPENDENCY_INFO, + SUBMIT_INFO_2_KHR = SUBMIT_INFO_2, + SEMAPHORE_SUBMIT_INFO_KHR = SEMAPHORE_SUBMIT_INFO, + COMMAND_BUFFER_SUBMIT_INFO_KHR = COMMAND_BUFFER_SUBMIT_INFO, + PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES_KHR = PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES, + PHYSICAL_DEVICE_ZERO_INITIALIZE_WORKGROUP_MEMORY_FEATURES_KHR = PHYSICAL_DEVICE_ZERO_INITIALIZE_WORKGROUP_MEMORY_FEATURES, + PHYSICAL_DEVICE_IMAGE_ROBUSTNESS_FEATURES_EXT = PHYSICAL_DEVICE_IMAGE_ROBUSTNESS_FEATURES, + COPY_BUFFER_INFO_2_KHR = COPY_BUFFER_INFO_2, + COPY_IMAGE_INFO_2_KHR = COPY_IMAGE_INFO_2, + COPY_BUFFER_TO_IMAGE_INFO_2_KHR = COPY_BUFFER_TO_IMAGE_INFO_2, + COPY_IMAGE_TO_BUFFER_INFO_2_KHR = COPY_IMAGE_TO_BUFFER_INFO_2, + BLIT_IMAGE_INFO_2_KHR = BLIT_IMAGE_INFO_2, + RESOLVE_IMAGE_INFO_2_KHR = RESOLVE_IMAGE_INFO_2, + BUFFER_COPY_2_KHR = BUFFER_COPY_2, + IMAGE_COPY_2_KHR = IMAGE_COPY_2, + IMAGE_BLIT_2_KHR = IMAGE_BLIT_2, + BUFFER_IMAGE_COPY_2_KHR = BUFFER_IMAGE_COPY_2, + IMAGE_RESOLVE_2_KHR = IMAGE_RESOLVE_2, + FORMAT_PROPERTIES_3_KHR = FORMAT_PROPERTIES_3, + PHYSICAL_DEVICE_GLOBAL_PRIORITY_QUERY_FEATURES_EXT = PHYSICAL_DEVICE_GLOBAL_PRIORITY_QUERY_FEATURES_KHR, + QUEUE_FAMILY_GLOBAL_PRIORITY_PROPERTIES_EXT = QUEUE_FAMILY_GLOBAL_PRIORITY_PROPERTIES_KHR, + PHYSICAL_DEVICE_MAINTENANCE_4_FEATURES_KHR = PHYSICAL_DEVICE_MAINTENANCE_4_FEATURES, + PHYSICAL_DEVICE_MAINTENANCE_4_PROPERTIES_KHR = PHYSICAL_DEVICE_MAINTENANCE_4_PROPERTIES, + DEVICE_BUFFER_MEMORY_REQUIREMENTS_KHR = DEVICE_BUFFER_MEMORY_REQUIREMENTS, + DEVICE_IMAGE_MEMORY_REQUIREMENTS_KHR = DEVICE_IMAGE_MEMORY_REQUIREMENTS, } SubgroupFeatureFlags :: distinct bit_set[SubgroupFeatureFlag; Flags] @@ -2737,9 +2961,10 @@ SubgroupFeatureFlag :: enum Flags { PARTITIONED_NV = 8, } -SubmitFlagsKHR :: distinct bit_set[SubmitFlagKHR; Flags] -SubmitFlagKHR :: enum Flags { - PROTECTED = 0, +SubmitFlags :: distinct bit_set[SubmitFlag; Flags] +SubmitFlag :: enum Flags { + PROTECTED = 0, + PROTECTED_KHR = PROTECTED, } SubpassContents :: enum c.int { @@ -2749,10 +2974,13 @@ SubpassContents :: enum c.int { SubpassDescriptionFlags :: distinct bit_set[SubpassDescriptionFlag; Flags] SubpassDescriptionFlag :: enum Flags { - PER_VIEW_ATTRIBUTES_NVX = 0, - PER_VIEW_POSITION_X_ONLY_NVX = 1, - FRAGMENT_REGION_QCOM = 2, - SHADER_RESOLVE_QCOM = 3, + PER_VIEW_ATTRIBUTES_NVX = 0, + PER_VIEW_POSITION_X_ONLY_NVX = 1, + FRAGMENT_REGION_QCOM = 2, + SHADER_RESOLVE_QCOM = 3, + RASTERIZATION_ORDER_ATTACHMENT_COLOR_ACCESS_ARM = 4, + RASTERIZATION_ORDER_ATTACHMENT_DEPTH_ACCESS_ARM = 5, + RASTERIZATION_ORDER_ATTACHMENT_STENCIL_ACCESS_ARM = 6, } SurfaceCounterFlagsEXT :: distinct bit_set[SurfaceCounterFlagEXT; Flags] @@ -2802,15 +3030,20 @@ TimeDomainEXT :: enum c.int { QUERY_PERFORMANCE_COUNTER = 3, } -ToolPurposeFlagsEXT :: distinct bit_set[ToolPurposeFlagEXT; Flags] -ToolPurposeFlagEXT :: enum Flags { - VALIDATION = 0, - PROFILING = 1, - TRACING = 2, - ADDITIONAL_FEATURES = 3, - MODIFYING_FEATURES = 4, - DEBUG_REPORTING = 5, - DEBUG_MARKERS = 6, +ToolPurposeFlags :: distinct bit_set[ToolPurposeFlag; Flags] +ToolPurposeFlag :: enum Flags { + VALIDATION = 0, + PROFILING = 1, + TRACING = 2, + ADDITIONAL_FEATURES = 3, + MODIFYING_FEATURES = 4, + DEBUG_REPORTING_EXT = 5, + DEBUG_MARKERS_EXT = 6, + VALIDATION_EXT = VALIDATION, + PROFILING_EXT = PROFILING, + TRACING_EXT = TRACING, + ADDITIONAL_FEATURES_EXT = ADDITIONAL_FEATURES, + MODIFYING_FEATURES_EXT = MODIFYING_FEATURES, } ValidationCacheHeaderVersionEXT :: enum c.int { @@ -2894,32 +3127,24 @@ HeadlessSurfaceCreateFlagsEXT :: distinct bit_set[Headles HeadlessSurfaceCreateFlagEXT :: enum u32 {} IOSSurfaceCreateFlagsMVK :: distinct bit_set[IOSSurfaceCreateFlagMVK; Flags] IOSSurfaceCreateFlagMVK :: enum u32 {} -InstanceCreateFlags :: distinct bit_set[InstanceCreateFlag; Flags] -InstanceCreateFlag :: enum u32 {} MacOSSurfaceCreateFlagsMVK :: distinct bit_set[MacOSSurfaceCreateFlagMVK; Flags] MacOSSurfaceCreateFlagMVK :: enum u32 {} MemoryMapFlags :: distinct bit_set[MemoryMapFlag; Flags] MemoryMapFlag :: enum u32 {} MetalSurfaceCreateFlagsEXT :: distinct bit_set[MetalSurfaceCreateFlagEXT; Flags] MetalSurfaceCreateFlagEXT :: enum u32 {} -PipelineColorBlendStateCreateFlags :: distinct bit_set[PipelineColorBlendStateCreateFlag; Flags] -PipelineColorBlendStateCreateFlag :: enum u32 {} PipelineCoverageModulationStateCreateFlagsNV :: distinct bit_set[PipelineCoverageModulationStateCreateFlagNV; Flags] PipelineCoverageModulationStateCreateFlagNV :: enum u32 {} PipelineCoverageReductionStateCreateFlagsNV :: distinct bit_set[PipelineCoverageReductionStateCreateFlagNV; Flags] PipelineCoverageReductionStateCreateFlagNV :: enum u32 {} PipelineCoverageToColorStateCreateFlagsNV :: distinct bit_set[PipelineCoverageToColorStateCreateFlagNV; Flags] PipelineCoverageToColorStateCreateFlagNV :: enum u32 {} -PipelineDepthStencilStateCreateFlags :: distinct bit_set[PipelineDepthStencilStateCreateFlag; Flags] -PipelineDepthStencilStateCreateFlag :: enum u32 {} PipelineDiscardRectangleStateCreateFlagsEXT :: distinct bit_set[PipelineDiscardRectangleStateCreateFlagEXT; Flags] PipelineDiscardRectangleStateCreateFlagEXT :: enum u32 {} PipelineDynamicStateCreateFlags :: distinct bit_set[PipelineDynamicStateCreateFlag; Flags] PipelineDynamicStateCreateFlag :: enum u32 {} PipelineInputAssemblyStateCreateFlags :: distinct bit_set[PipelineInputAssemblyStateCreateFlag; Flags] PipelineInputAssemblyStateCreateFlag :: enum u32 {} -PipelineLayoutCreateFlags :: distinct bit_set[PipelineLayoutCreateFlag; Flags] -PipelineLayoutCreateFlag :: enum u32 {} PipelineMultisampleStateCreateFlags :: distinct bit_set[PipelineMultisampleStateCreateFlag; Flags] PipelineMultisampleStateCreateFlag :: enum u32 {} PipelineRasterizationConservativeStateCreateFlagsEXT :: distinct bit_set[PipelineRasterizationConservativeStateCreateFlagEXT; Flags] @@ -2938,6 +3163,8 @@ PipelineViewportStateCreateFlags :: distinct bit_set[Pipelin PipelineViewportStateCreateFlag :: enum u32 {} PipelineViewportSwizzleStateCreateFlagsNV :: distinct bit_set[PipelineViewportSwizzleStateCreateFlagNV; Flags] PipelineViewportSwizzleStateCreateFlagNV :: enum u32 {} +PrivateDataSlotCreateFlags :: distinct bit_set[PrivateDataSlotCreateFlag; Flags] +PrivateDataSlotCreateFlag :: enum u32 {} QueryPoolCreateFlags :: distinct bit_set[QueryPoolCreateFlag; Flags] QueryPoolCreateFlag :: enum u32 {} SemaphoreCreateFlags :: distinct bit_set[SemaphoreCreateFlag; Flags] diff --git a/vendor/vulkan/procedures.odin b/vendor/vulkan/procedures.odin index b40523b6d..227f02a87 100644 --- a/vendor/vulkan/procedures.odin +++ b/vendor/vulkan/procedures.odin @@ -5,1412 +5,3330 @@ package vulkan import "core:c" -// Procedure Types +// Loader Procedure Types +ProcCreateInstance :: #type proc "system" (pCreateInfo: ^InstanceCreateInfo, pAllocator: ^AllocationCallbacks, pInstance: ^Instance) -> Result +ProcDebugUtilsMessengerCallbackEXT :: #type proc "system" (messageSeverity: DebugUtilsMessageSeverityFlagsEXT, messageTypes: DebugUtilsMessageTypeFlagsEXT, pCallbackData: ^DebugUtilsMessengerCallbackDataEXT, pUserData: rawptr) -> b32 +ProcDeviceMemoryReportCallbackEXT :: #type proc "system" (pCallbackData: ^DeviceMemoryReportCallbackDataEXT, pUserData: rawptr) +ProcEnumerateInstanceExtensionProperties :: #type proc "system" (pLayerName: cstring, pPropertyCount: ^u32, pProperties: [^]ExtensionProperties) -> Result +ProcEnumerateInstanceLayerProperties :: #type proc "system" (pPropertyCount: ^u32, pProperties: [^]LayerProperties) -> Result +ProcEnumerateInstanceVersion :: #type proc "system" (pApiVersion: ^u32) -> Result -ProcAllocationFunction :: #type proc "system" (pUserData: rawptr, size: int, alignment: int, allocationScope: SystemAllocationScope) -> rawptr -ProcFreeFunction :: #type proc "system" (pUserData: rawptr, pMemory: rawptr) -ProcInternalAllocationNotification :: #type proc "system" (pUserData: rawptr, size: int, allocationType: InternalAllocationType, allocationScope: SystemAllocationScope) -ProcInternalFreeNotification :: #type proc "system" (pUserData: rawptr, size: int, allocationType: InternalAllocationType, allocationScope: SystemAllocationScope) -ProcReallocationFunction :: #type proc "system" (pUserData: rawptr, pOriginal: rawptr, size: int, alignment: int, allocationScope: SystemAllocationScope) -> rawptr -ProcVoidFunction :: #type proc "system" () -ProcCreateInstance :: #type proc "system" (pCreateInfo: ^InstanceCreateInfo, pAllocator: ^AllocationCallbacks, pInstance: ^Instance) -> Result -ProcDestroyInstance :: #type proc "system" (instance: Instance, pAllocator: ^AllocationCallbacks) -ProcEnumeratePhysicalDevices :: #type proc "system" (instance: Instance, pPhysicalDeviceCount: ^u32, pPhysicalDevices: [^]PhysicalDevice) -> Result -ProcGetPhysicalDeviceFeatures :: #type proc "system" (physicalDevice: PhysicalDevice, pFeatures: [^]PhysicalDeviceFeatures) -ProcGetPhysicalDeviceFormatProperties :: #type proc "system" (physicalDevice: PhysicalDevice, format: Format, pFormatProperties: [^]FormatProperties) -ProcGetPhysicalDeviceImageFormatProperties :: #type proc "system" (physicalDevice: PhysicalDevice, format: Format, type: ImageType, tiling: ImageTiling, usage: ImageUsageFlags, flags: ImageCreateFlags, pImageFormatProperties: [^]ImageFormatProperties) -> Result -ProcGetPhysicalDeviceProperties :: #type proc "system" (physicalDevice: PhysicalDevice, pProperties: [^]PhysicalDeviceProperties) -ProcGetPhysicalDeviceQueueFamilyProperties :: #type proc "system" (physicalDevice: PhysicalDevice, pQueueFamilyPropertyCount: ^u32, pQueueFamilyProperties: [^]QueueFamilyProperties) -ProcGetPhysicalDeviceMemoryProperties :: #type proc "system" (physicalDevice: PhysicalDevice, pMemoryProperties: [^]PhysicalDeviceMemoryProperties) -ProcGetInstanceProcAddr :: #type proc "system" (instance: Instance, pName: cstring) -> ProcVoidFunction -ProcGetDeviceProcAddr :: #type proc "system" (device: Device, pName: cstring) -> ProcVoidFunction -ProcCreateDevice :: #type proc "system" (physicalDevice: PhysicalDevice, pCreateInfo: ^DeviceCreateInfo, pAllocator: ^AllocationCallbacks, pDevice: ^Device) -> Result -ProcDestroyDevice :: #type proc "system" (device: Device, pAllocator: ^AllocationCallbacks) -ProcEnumerateInstanceExtensionProperties :: #type proc "system" (pLayerName: cstring, pPropertyCount: ^u32, pProperties: [^]ExtensionProperties) -> Result -ProcEnumerateDeviceExtensionProperties :: #type proc "system" (physicalDevice: PhysicalDevice, pLayerName: cstring, pPropertyCount: ^u32, pProperties: [^]ExtensionProperties) -> Result -ProcEnumerateInstanceLayerProperties :: #type proc "system" (pPropertyCount: ^u32, pProperties: [^]LayerProperties) -> Result -ProcEnumerateDeviceLayerProperties :: #type proc "system" (physicalDevice: PhysicalDevice, pPropertyCount: ^u32, pProperties: [^]LayerProperties) -> Result -ProcGetDeviceQueue :: #type proc "system" (device: Device, queueFamilyIndex: u32, queueIndex: u32, pQueue: ^Queue) -ProcQueueSubmit :: #type proc "system" (queue: Queue, submitCount: u32, pSubmits: [^]SubmitInfo, fence: Fence) -> Result -ProcQueueWaitIdle :: #type proc "system" (queue: Queue) -> Result -ProcDeviceWaitIdle :: #type proc "system" (device: Device) -> Result -ProcAllocateMemory :: #type proc "system" (device: Device, pAllocateInfo: ^MemoryAllocateInfo, pAllocator: ^AllocationCallbacks, pMemory: ^DeviceMemory) -> Result -ProcFreeMemory :: #type proc "system" (device: Device, memory: DeviceMemory, pAllocator: ^AllocationCallbacks) -ProcMapMemory :: #type proc "system" (device: Device, memory: DeviceMemory, offset: DeviceSize, size: DeviceSize, flags: MemoryMapFlags, ppData: ^rawptr) -> Result -ProcUnmapMemory :: #type proc "system" (device: Device, memory: DeviceMemory) -ProcFlushMappedMemoryRanges :: #type proc "system" (device: Device, memoryRangeCount: u32, pMemoryRanges: [^]MappedMemoryRange) -> Result -ProcInvalidateMappedMemoryRanges :: #type proc "system" (device: Device, memoryRangeCount: u32, pMemoryRanges: [^]MappedMemoryRange) -> Result -ProcGetDeviceMemoryCommitment :: #type proc "system" (device: Device, memory: DeviceMemory, pCommittedMemoryInBytes: [^]DeviceSize) -ProcBindBufferMemory :: #type proc "system" (device: Device, buffer: Buffer, memory: DeviceMemory, memoryOffset: DeviceSize) -> Result -ProcBindImageMemory :: #type proc "system" (device: Device, image: Image, memory: DeviceMemory, memoryOffset: DeviceSize) -> Result -ProcGetBufferMemoryRequirements :: #type proc "system" (device: Device, buffer: Buffer, pMemoryRequirements: [^]MemoryRequirements) -ProcGetImageMemoryRequirements :: #type proc "system" (device: Device, image: Image, pMemoryRequirements: [^]MemoryRequirements) -ProcGetImageSparseMemoryRequirements :: #type proc "system" (device: Device, image: Image, pSparseMemoryRequirementCount: ^u32, pSparseMemoryRequirements: [^]SparseImageMemoryRequirements) -ProcGetPhysicalDeviceSparseImageFormatProperties :: #type proc "system" (physicalDevice: PhysicalDevice, format: Format, type: ImageType, samples: SampleCountFlags, usage: ImageUsageFlags, tiling: ImageTiling, pPropertyCount: ^u32, pProperties: [^]SparseImageFormatProperties) -ProcQueueBindSparse :: #type proc "system" (queue: Queue, bindInfoCount: u32, pBindInfo: ^BindSparseInfo, fence: Fence) -> Result -ProcCreateFence :: #type proc "system" (device: Device, pCreateInfo: ^FenceCreateInfo, pAllocator: ^AllocationCallbacks, pFence: ^Fence) -> Result -ProcDestroyFence :: #type proc "system" (device: Device, fence: Fence, pAllocator: ^AllocationCallbacks) -ProcResetFences :: #type proc "system" (device: Device, fenceCount: u32, pFences: [^]Fence) -> Result -ProcGetFenceStatus :: #type proc "system" (device: Device, fence: Fence) -> Result -ProcWaitForFences :: #type proc "system" (device: Device, fenceCount: u32, pFences: [^]Fence, waitAll: b32, timeout: u64) -> Result -ProcCreateSemaphore :: #type proc "system" (device: Device, pCreateInfo: ^SemaphoreCreateInfo, pAllocator: ^AllocationCallbacks, pSemaphore: ^Semaphore) -> Result -ProcDestroySemaphore :: #type proc "system" (device: Device, semaphore: Semaphore, pAllocator: ^AllocationCallbacks) -ProcCreateEvent :: #type proc "system" (device: Device, pCreateInfo: ^EventCreateInfo, pAllocator: ^AllocationCallbacks, pEvent: ^Event) -> Result -ProcDestroyEvent :: #type proc "system" (device: Device, event: Event, pAllocator: ^AllocationCallbacks) -ProcGetEventStatus :: #type proc "system" (device: Device, event: Event) -> Result -ProcSetEvent :: #type proc "system" (device: Device, event: Event) -> Result -ProcResetEvent :: #type proc "system" (device: Device, event: Event) -> Result -ProcCreateQueryPool :: #type proc "system" (device: Device, pCreateInfo: ^QueryPoolCreateInfo, pAllocator: ^AllocationCallbacks, pQueryPool: ^QueryPool) -> Result -ProcDestroyQueryPool :: #type proc "system" (device: Device, queryPool: QueryPool, pAllocator: ^AllocationCallbacks) -ProcGetQueryPoolResults :: #type proc "system" (device: Device, queryPool: QueryPool, firstQuery: u32, queryCount: u32, dataSize: int, pData: rawptr, stride: DeviceSize, flags: QueryResultFlags) -> Result -ProcCreateBuffer :: #type proc "system" (device: Device, pCreateInfo: ^BufferCreateInfo, pAllocator: ^AllocationCallbacks, pBuffer: ^Buffer) -> Result -ProcDestroyBuffer :: #type proc "system" (device: Device, buffer: Buffer, pAllocator: ^AllocationCallbacks) -ProcCreateBufferView :: #type proc "system" (device: Device, pCreateInfo: ^BufferViewCreateInfo, pAllocator: ^AllocationCallbacks, pView: ^BufferView) -> Result -ProcDestroyBufferView :: #type proc "system" (device: Device, bufferView: BufferView, pAllocator: ^AllocationCallbacks) -ProcCreateImage :: #type proc "system" (device: Device, pCreateInfo: ^ImageCreateInfo, pAllocator: ^AllocationCallbacks, pImage: ^Image) -> Result -ProcDestroyImage :: #type proc "system" (device: Device, image: Image, pAllocator: ^AllocationCallbacks) -ProcGetImageSubresourceLayout :: #type proc "system" (device: Device, image: Image, pSubresource: ^ImageSubresource, pLayout: ^SubresourceLayout) -ProcCreateImageView :: #type proc "system" (device: Device, pCreateInfo: ^ImageViewCreateInfo, pAllocator: ^AllocationCallbacks, pView: ^ImageView) -> Result -ProcDestroyImageView :: #type proc "system" (device: Device, imageView: ImageView, pAllocator: ^AllocationCallbacks) -ProcCreateShaderModule :: #type proc "system" (device: Device, pCreateInfo: ^ShaderModuleCreateInfo, pAllocator: ^AllocationCallbacks, pShaderModule: ^ShaderModule) -> Result -ProcDestroyShaderModule :: #type proc "system" (device: Device, shaderModule: ShaderModule, pAllocator: ^AllocationCallbacks) -ProcCreatePipelineCache :: #type proc "system" (device: Device, pCreateInfo: ^PipelineCacheCreateInfo, pAllocator: ^AllocationCallbacks, pPipelineCache: ^PipelineCache) -> Result -ProcDestroyPipelineCache :: #type proc "system" (device: Device, pipelineCache: PipelineCache, pAllocator: ^AllocationCallbacks) -ProcGetPipelineCacheData :: #type proc "system" (device: Device, pipelineCache: PipelineCache, pDataSize: ^int, pData: rawptr) -> Result -ProcMergePipelineCaches :: #type proc "system" (device: Device, dstCache: PipelineCache, srcCacheCount: u32, pSrcCaches: [^]PipelineCache) -> Result -ProcCreateGraphicsPipelines :: #type proc "system" (device: Device, pipelineCache: PipelineCache, createInfoCount: u32, pCreateInfos: [^]GraphicsPipelineCreateInfo, pAllocator: ^AllocationCallbacks, pPipelines: [^]Pipeline) -> Result -ProcCreateComputePipelines :: #type proc "system" (device: Device, pipelineCache: PipelineCache, createInfoCount: u32, pCreateInfos: [^]ComputePipelineCreateInfo, pAllocator: ^AllocationCallbacks, pPipelines: [^]Pipeline) -> Result -ProcDestroyPipeline :: #type proc "system" (device: Device, pipeline: Pipeline, pAllocator: ^AllocationCallbacks) -ProcCreatePipelineLayout :: #type proc "system" (device: Device, pCreateInfo: ^PipelineLayoutCreateInfo, pAllocator: ^AllocationCallbacks, pPipelineLayout: ^PipelineLayout) -> Result -ProcDestroyPipelineLayout :: #type proc "system" (device: Device, pipelineLayout: PipelineLayout, pAllocator: ^AllocationCallbacks) -ProcCreateSampler :: #type proc "system" (device: Device, pCreateInfo: ^SamplerCreateInfo, pAllocator: ^AllocationCallbacks, pSampler: ^Sampler) -> Result -ProcDestroySampler :: #type proc "system" (device: Device, sampler: Sampler, pAllocator: ^AllocationCallbacks) -ProcCreateDescriptorSetLayout :: #type proc "system" (device: Device, pCreateInfo: ^DescriptorSetLayoutCreateInfo, pAllocator: ^AllocationCallbacks, pSetLayout: ^DescriptorSetLayout) -> Result -ProcDestroyDescriptorSetLayout :: #type proc "system" (device: Device, descriptorSetLayout: DescriptorSetLayout, pAllocator: ^AllocationCallbacks) -ProcCreateDescriptorPool :: #type proc "system" (device: Device, pCreateInfo: ^DescriptorPoolCreateInfo, pAllocator: ^AllocationCallbacks, pDescriptorPool: ^DescriptorPool) -> Result -ProcDestroyDescriptorPool :: #type proc "system" (device: Device, descriptorPool: DescriptorPool, pAllocator: ^AllocationCallbacks) -ProcResetDescriptorPool :: #type proc "system" (device: Device, descriptorPool: DescriptorPool, flags: DescriptorPoolResetFlags) -> Result -ProcAllocateDescriptorSets :: #type proc "system" (device: Device, pAllocateInfo: ^DescriptorSetAllocateInfo, pDescriptorSets: [^]DescriptorSet) -> Result -ProcFreeDescriptorSets :: #type proc "system" (device: Device, descriptorPool: DescriptorPool, descriptorSetCount: u32, pDescriptorSets: [^]DescriptorSet) -> Result -ProcUpdateDescriptorSets :: #type proc "system" (device: Device, descriptorWriteCount: u32, pDescriptorWrites: [^]WriteDescriptorSet, descriptorCopyCount: u32, pDescriptorCopies: [^]CopyDescriptorSet) -ProcCreateFramebuffer :: #type proc "system" (device: Device, pCreateInfo: ^FramebufferCreateInfo, pAllocator: ^AllocationCallbacks, pFramebuffer: ^Framebuffer) -> Result -ProcDestroyFramebuffer :: #type proc "system" (device: Device, framebuffer: Framebuffer, pAllocator: ^AllocationCallbacks) -ProcCreateRenderPass :: #type proc "system" (device: Device, pCreateInfo: ^RenderPassCreateInfo, pAllocator: ^AllocationCallbacks, pRenderPass: [^]RenderPass) -> Result -ProcDestroyRenderPass :: #type proc "system" (device: Device, renderPass: RenderPass, pAllocator: ^AllocationCallbacks) -ProcGetRenderAreaGranularity :: #type proc "system" (device: Device, renderPass: RenderPass, pGranularity: ^Extent2D) -ProcCreateCommandPool :: #type proc "system" (device: Device, pCreateInfo: ^CommandPoolCreateInfo, pAllocator: ^AllocationCallbacks, pCommandPool: ^CommandPool) -> Result -ProcDestroyCommandPool :: #type proc "system" (device: Device, commandPool: CommandPool, pAllocator: ^AllocationCallbacks) -ProcResetCommandPool :: #type proc "system" (device: Device, commandPool: CommandPool, flags: CommandPoolResetFlags) -> Result -ProcAllocateCommandBuffers :: #type proc "system" (device: Device, pAllocateInfo: ^CommandBufferAllocateInfo, pCommandBuffers: [^]CommandBuffer) -> Result -ProcFreeCommandBuffers :: #type proc "system" (device: Device, commandPool: CommandPool, commandBufferCount: u32, pCommandBuffers: [^]CommandBuffer) -ProcBeginCommandBuffer :: #type proc "system" (commandBuffer: CommandBuffer, pBeginInfo: ^CommandBufferBeginInfo) -> Result -ProcEndCommandBuffer :: #type proc "system" (commandBuffer: CommandBuffer) -> Result -ProcResetCommandBuffer :: #type proc "system" (commandBuffer: CommandBuffer, flags: CommandBufferResetFlags) -> Result -ProcCmdBindPipeline :: #type proc "system" (commandBuffer: CommandBuffer, pipelineBindPoint: PipelineBindPoint, pipeline: Pipeline) -ProcCmdSetViewport :: #type proc "system" (commandBuffer: CommandBuffer, firstViewport: u32, viewportCount: u32, pViewports: [^]Viewport) -ProcCmdSetScissor :: #type proc "system" (commandBuffer: CommandBuffer, firstScissor: u32, scissorCount: u32, pScissors: [^]Rect2D) -ProcCmdSetLineWidth :: #type proc "system" (commandBuffer: CommandBuffer, lineWidth: f32) -ProcCmdSetDepthBias :: #type proc "system" (commandBuffer: CommandBuffer, depthBiasConstantFactor: f32, depthBiasClamp: f32, depthBiasSlopeFactor: f32) -ProcCmdSetBlendConstants :: #type proc "system" (commandBuffer: CommandBuffer) -ProcCmdSetDepthBounds :: #type proc "system" (commandBuffer: CommandBuffer, minDepthBounds: f32, maxDepthBounds: f32) -ProcCmdSetStencilCompareMask :: #type proc "system" (commandBuffer: CommandBuffer, faceMask: StencilFaceFlags, compareMask: u32) -ProcCmdSetStencilWriteMask :: #type proc "system" (commandBuffer: CommandBuffer, faceMask: StencilFaceFlags, writeMask: u32) -ProcCmdSetStencilReference :: #type proc "system" (commandBuffer: CommandBuffer, faceMask: StencilFaceFlags, reference: u32) -ProcCmdBindDescriptorSets :: #type proc "system" (commandBuffer: CommandBuffer, pipelineBindPoint: PipelineBindPoint, layout: PipelineLayout, firstSet: u32, descriptorSetCount: u32, pDescriptorSets: [^]DescriptorSet, dynamicOffsetCount: u32, pDynamicOffsets: [^]u32) -ProcCmdBindIndexBuffer :: #type proc "system" (commandBuffer: CommandBuffer, buffer: Buffer, offset: DeviceSize, indexType: IndexType) -ProcCmdBindVertexBuffers :: #type proc "system" (commandBuffer: CommandBuffer, firstBinding: u32, bindingCount: u32, pBuffers: [^]Buffer, pOffsets: [^]DeviceSize) -ProcCmdDraw :: #type proc "system" (commandBuffer: CommandBuffer, vertexCount: u32, instanceCount: u32, firstVertex: u32, firstInstance: u32) -ProcCmdDrawIndexed :: #type proc "system" (commandBuffer: CommandBuffer, indexCount: u32, instanceCount: u32, firstIndex: u32, vertexOffset: i32, firstInstance: u32) -ProcCmdDrawIndirect :: #type proc "system" (commandBuffer: CommandBuffer, buffer: Buffer, offset: DeviceSize, drawCount: u32, stride: u32) -ProcCmdDrawIndexedIndirect :: #type proc "system" (commandBuffer: CommandBuffer, buffer: Buffer, offset: DeviceSize, drawCount: u32, stride: u32) -ProcCmdDispatch :: #type proc "system" (commandBuffer: CommandBuffer, groupCountX: u32, groupCountY: u32, groupCountZ: u32) -ProcCmdDispatchIndirect :: #type proc "system" (commandBuffer: CommandBuffer, buffer: Buffer, offset: DeviceSize) -ProcCmdCopyBuffer :: #type proc "system" (commandBuffer: CommandBuffer, srcBuffer: Buffer, dstBuffer: Buffer, regionCount: u32, pRegions: [^]BufferCopy) -ProcCmdCopyImage :: #type proc "system" (commandBuffer: CommandBuffer, srcImage: Image, srcImageLayout: ImageLayout, dstImage: Image, dstImageLayout: ImageLayout, regionCount: u32, pRegions: [^]ImageCopy) -ProcCmdBlitImage :: #type proc "system" (commandBuffer: CommandBuffer, srcImage: Image, srcImageLayout: ImageLayout, dstImage: Image, dstImageLayout: ImageLayout, regionCount: u32, pRegions: [^]ImageBlit, filter: Filter) -ProcCmdCopyBufferToImage :: #type proc "system" (commandBuffer: CommandBuffer, srcBuffer: Buffer, dstImage: Image, dstImageLayout: ImageLayout, regionCount: u32, pRegions: [^]BufferImageCopy) -ProcCmdCopyImageToBuffer :: #type proc "system" (commandBuffer: CommandBuffer, srcImage: Image, srcImageLayout: ImageLayout, dstBuffer: Buffer, regionCount: u32, pRegions: [^]BufferImageCopy) -ProcCmdUpdateBuffer :: #type proc "system" (commandBuffer: CommandBuffer, dstBuffer: Buffer, dstOffset: DeviceSize, dataSize: DeviceSize, pData: rawptr) -ProcCmdFillBuffer :: #type proc "system" (commandBuffer: CommandBuffer, dstBuffer: Buffer, dstOffset: DeviceSize, size: DeviceSize, data: u32) -ProcCmdClearColorImage :: #type proc "system" (commandBuffer: CommandBuffer, image: Image, imageLayout: ImageLayout, pColor: ^ClearColorValue, rangeCount: u32, pRanges: [^]ImageSubresourceRange) -ProcCmdClearDepthStencilImage :: #type proc "system" (commandBuffer: CommandBuffer, image: Image, imageLayout: ImageLayout, pDepthStencil: ^ClearDepthStencilValue, rangeCount: u32, pRanges: [^]ImageSubresourceRange) -ProcCmdClearAttachments :: #type proc "system" (commandBuffer: CommandBuffer, attachmentCount: u32, pAttachments: [^]ClearAttachment, rectCount: u32, pRects: [^]ClearRect) -ProcCmdResolveImage :: #type proc "system" (commandBuffer: CommandBuffer, srcImage: Image, srcImageLayout: ImageLayout, dstImage: Image, dstImageLayout: ImageLayout, regionCount: u32, pRegions: [^]ImageResolve) -ProcCmdSetEvent :: #type proc "system" (commandBuffer: CommandBuffer, event: Event, stageMask: PipelineStageFlags) -ProcCmdResetEvent :: #type proc "system" (commandBuffer: CommandBuffer, event: Event, stageMask: PipelineStageFlags) -ProcCmdWaitEvents :: #type proc "system" (commandBuffer: CommandBuffer, eventCount: u32, pEvents: [^]Event, srcStageMask: PipelineStageFlags, dstStageMask: PipelineStageFlags, memoryBarrierCount: u32, pMemoryBarriers: [^]MemoryBarrier, bufferMemoryBarrierCount: u32, pBufferMemoryBarriers: [^]BufferMemoryBarrier, imageMemoryBarrierCount: u32, pImageMemoryBarriers: [^]ImageMemoryBarrier) -ProcCmdPipelineBarrier :: #type proc "system" (commandBuffer: CommandBuffer, srcStageMask: PipelineStageFlags, dstStageMask: PipelineStageFlags, dependencyFlags: DependencyFlags, memoryBarrierCount: u32, pMemoryBarriers: [^]MemoryBarrier, bufferMemoryBarrierCount: u32, pBufferMemoryBarriers: [^]BufferMemoryBarrier, imageMemoryBarrierCount: u32, pImageMemoryBarriers: [^]ImageMemoryBarrier) -ProcCmdBeginQuery :: #type proc "system" (commandBuffer: CommandBuffer, queryPool: QueryPool, query: u32, flags: QueryControlFlags) -ProcCmdEndQuery :: #type proc "system" (commandBuffer: CommandBuffer, queryPool: QueryPool, query: u32) -ProcCmdResetQueryPool :: #type proc "system" (commandBuffer: CommandBuffer, queryPool: QueryPool, firstQuery: u32, queryCount: u32) -ProcCmdWriteTimestamp :: #type proc "system" (commandBuffer: CommandBuffer, pipelineStage: PipelineStageFlags, queryPool: QueryPool, query: u32) -ProcCmdCopyQueryPoolResults :: #type proc "system" (commandBuffer: CommandBuffer, queryPool: QueryPool, firstQuery: u32, queryCount: u32, dstBuffer: Buffer, dstOffset: DeviceSize, stride: DeviceSize, flags: QueryResultFlags) -ProcCmdPushConstants :: #type proc "system" (commandBuffer: CommandBuffer, layout: PipelineLayout, stageFlags: ShaderStageFlags, offset: u32, size: u32, pValues: rawptr) -ProcCmdBeginRenderPass :: #type proc "system" (commandBuffer: CommandBuffer, pRenderPassBegin: ^RenderPassBeginInfo, contents: SubpassContents) -ProcCmdNextSubpass :: #type proc "system" (commandBuffer: CommandBuffer, contents: SubpassContents) -ProcCmdEndRenderPass :: #type proc "system" (commandBuffer: CommandBuffer) -ProcCmdExecuteCommands :: #type proc "system" (commandBuffer: CommandBuffer, commandBufferCount: u32, pCommandBuffers: [^]CommandBuffer) -ProcEnumerateInstanceVersion :: #type proc "system" (pApiVersion: ^u32) -> Result -ProcBindBufferMemory2 :: #type proc "system" (device: Device, bindInfoCount: u32, pBindInfos: [^]BindBufferMemoryInfo) -> Result -ProcBindImageMemory2 :: #type proc "system" (device: Device, bindInfoCount: u32, pBindInfos: [^]BindImageMemoryInfo) -> Result -ProcGetDeviceGroupPeerMemoryFeatures :: #type proc "system" (device: Device, heapIndex: u32, localDeviceIndex: u32, remoteDeviceIndex: u32, pPeerMemoryFeatures: [^]PeerMemoryFeatureFlags) -ProcCmdSetDeviceMask :: #type proc "system" (commandBuffer: CommandBuffer, deviceMask: u32) -ProcCmdDispatchBase :: #type proc "system" (commandBuffer: CommandBuffer, baseGroupX: u32, baseGroupY: u32, baseGroupZ: u32, groupCountX: u32, groupCountY: u32, groupCountZ: u32) -ProcEnumeratePhysicalDeviceGroups :: #type proc "system" (instance: Instance, pPhysicalDeviceGroupCount: ^u32, pPhysicalDeviceGroupProperties: [^]PhysicalDeviceGroupProperties) -> Result -ProcGetImageMemoryRequirements2 :: #type proc "system" (device: Device, pInfo: ^ImageMemoryRequirementsInfo2, pMemoryRequirements: [^]MemoryRequirements2) -ProcGetBufferMemoryRequirements2 :: #type proc "system" (device: Device, pInfo: ^BufferMemoryRequirementsInfo2, pMemoryRequirements: [^]MemoryRequirements2) -ProcGetImageSparseMemoryRequirements2 :: #type proc "system" (device: Device, pInfo: ^ImageSparseMemoryRequirementsInfo2, pSparseMemoryRequirementCount: ^u32, pSparseMemoryRequirements: [^]SparseImageMemoryRequirements2) -ProcGetPhysicalDeviceFeatures2 :: #type proc "system" (physicalDevice: PhysicalDevice, pFeatures: [^]PhysicalDeviceFeatures2) -ProcGetPhysicalDeviceProperties2 :: #type proc "system" (physicalDevice: PhysicalDevice, pProperties: [^]PhysicalDeviceProperties2) -ProcGetPhysicalDeviceFormatProperties2 :: #type proc "system" (physicalDevice: PhysicalDevice, format: Format, pFormatProperties: [^]FormatProperties2) -ProcGetPhysicalDeviceImageFormatProperties2 :: #type proc "system" (physicalDevice: PhysicalDevice, pImageFormatInfo: ^PhysicalDeviceImageFormatInfo2, pImageFormatProperties: [^]ImageFormatProperties2) -> Result -ProcGetPhysicalDeviceQueueFamilyProperties2 :: #type proc "system" (physicalDevice: PhysicalDevice, pQueueFamilyPropertyCount: ^u32, pQueueFamilyProperties: [^]QueueFamilyProperties2) -ProcGetPhysicalDeviceMemoryProperties2 :: #type proc "system" (physicalDevice: PhysicalDevice, pMemoryProperties: [^]PhysicalDeviceMemoryProperties2) -ProcGetPhysicalDeviceSparseImageFormatProperties2 :: #type proc "system" (physicalDevice: PhysicalDevice, pFormatInfo: ^PhysicalDeviceSparseImageFormatInfo2, pPropertyCount: ^u32, pProperties: [^]SparseImageFormatProperties2) -ProcTrimCommandPool :: #type proc "system" (device: Device, commandPool: CommandPool, flags: CommandPoolTrimFlags) -ProcGetDeviceQueue2 :: #type proc "system" (device: Device, pQueueInfo: ^DeviceQueueInfo2, pQueue: ^Queue) -ProcCreateSamplerYcbcrConversion :: #type proc "system" (device: Device, pCreateInfo: ^SamplerYcbcrConversionCreateInfo, pAllocator: ^AllocationCallbacks, pYcbcrConversion: ^SamplerYcbcrConversion) -> Result -ProcDestroySamplerYcbcrConversion :: #type proc "system" (device: Device, ycbcrConversion: SamplerYcbcrConversion, pAllocator: ^AllocationCallbacks) -ProcCreateDescriptorUpdateTemplate :: #type proc "system" (device: Device, pCreateInfo: ^DescriptorUpdateTemplateCreateInfo, pAllocator: ^AllocationCallbacks, pDescriptorUpdateTemplate: ^DescriptorUpdateTemplate) -> Result -ProcDestroyDescriptorUpdateTemplate :: #type proc "system" (device: Device, descriptorUpdateTemplate: DescriptorUpdateTemplate, pAllocator: ^AllocationCallbacks) -ProcUpdateDescriptorSetWithTemplate :: #type proc "system" (device: Device, descriptorSet: DescriptorSet, descriptorUpdateTemplate: DescriptorUpdateTemplate, pData: rawptr) -ProcGetPhysicalDeviceExternalBufferProperties :: #type proc "system" (physicalDevice: PhysicalDevice, pExternalBufferInfo: ^PhysicalDeviceExternalBufferInfo, pExternalBufferProperties: [^]ExternalBufferProperties) -ProcGetPhysicalDeviceExternalFenceProperties :: #type proc "system" (physicalDevice: PhysicalDevice, pExternalFenceInfo: ^PhysicalDeviceExternalFenceInfo, pExternalFenceProperties: [^]ExternalFenceProperties) -ProcGetPhysicalDeviceExternalSemaphoreProperties :: #type proc "system" (physicalDevice: PhysicalDevice, pExternalSemaphoreInfo: ^PhysicalDeviceExternalSemaphoreInfo, pExternalSemaphoreProperties: [^]ExternalSemaphoreProperties) -ProcGetDescriptorSetLayoutSupport :: #type proc "system" (device: Device, pCreateInfo: ^DescriptorSetLayoutCreateInfo, pSupport: ^DescriptorSetLayoutSupport) -ProcCmdDrawIndirectCount :: #type proc "system" (commandBuffer: CommandBuffer, buffer: Buffer, offset: DeviceSize, countBuffer: Buffer, countBufferOffset: DeviceSize, maxDrawCount: u32, stride: u32) -ProcCmdDrawIndexedIndirectCount :: #type proc "system" (commandBuffer: CommandBuffer, buffer: Buffer, offset: DeviceSize, countBuffer: Buffer, countBufferOffset: DeviceSize, maxDrawCount: u32, stride: u32) -ProcCreateRenderPass2 :: #type proc "system" (device: Device, pCreateInfo: ^RenderPassCreateInfo2, pAllocator: ^AllocationCallbacks, pRenderPass: [^]RenderPass) -> Result -ProcCmdBeginRenderPass2 :: #type proc "system" (commandBuffer: CommandBuffer, pRenderPassBegin: ^RenderPassBeginInfo, pSubpassBeginInfo: ^SubpassBeginInfo) -ProcCmdNextSubpass2 :: #type proc "system" (commandBuffer: CommandBuffer, pSubpassBeginInfo: ^SubpassBeginInfo, pSubpassEndInfo: ^SubpassEndInfo) -ProcCmdEndRenderPass2 :: #type proc "system" (commandBuffer: CommandBuffer, pSubpassEndInfo: ^SubpassEndInfo) -ProcResetQueryPool :: #type proc "system" (device: Device, queryPool: QueryPool, firstQuery: u32, queryCount: u32) -ProcGetSemaphoreCounterValue :: #type proc "system" (device: Device, semaphore: Semaphore, pValue: ^u64) -> Result -ProcWaitSemaphores :: #type proc "system" (device: Device, pWaitInfo: ^SemaphoreWaitInfo, timeout: u64) -> Result -ProcSignalSemaphore :: #type proc "system" (device: Device, pSignalInfo: ^SemaphoreSignalInfo) -> Result -ProcGetBufferDeviceAddress :: #type proc "system" (device: Device, pInfo: ^BufferDeviceAddressInfo) -> DeviceAddress -ProcGetBufferOpaqueCaptureAddress :: #type proc "system" (device: Device, pInfo: ^BufferDeviceAddressInfo) -> u64 -ProcGetDeviceMemoryOpaqueCaptureAddress :: #type proc "system" (device: Device, pInfo: ^DeviceMemoryOpaqueCaptureAddressInfo) -> u64 -ProcDestroySurfaceKHR :: #type proc "system" (instance: Instance, surface: SurfaceKHR, pAllocator: ^AllocationCallbacks) -ProcGetPhysicalDeviceSurfaceSupportKHR :: #type proc "system" (physicalDevice: PhysicalDevice, queueFamilyIndex: u32, surface: SurfaceKHR, pSupported: ^b32) -> Result -ProcGetPhysicalDeviceSurfaceCapabilitiesKHR :: #type proc "system" (physicalDevice: PhysicalDevice, surface: SurfaceKHR, pSurfaceCapabilities: [^]SurfaceCapabilitiesKHR) -> Result -ProcGetPhysicalDeviceSurfaceFormatsKHR :: #type proc "system" (physicalDevice: PhysicalDevice, surface: SurfaceKHR, pSurfaceFormatCount: ^u32, pSurfaceFormats: [^]SurfaceFormatKHR) -> Result -ProcGetPhysicalDeviceSurfacePresentModesKHR :: #type proc "system" (physicalDevice: PhysicalDevice, surface: SurfaceKHR, pPresentModeCount: ^u32, pPresentModes: [^]PresentModeKHR) -> Result -ProcCreateSwapchainKHR :: #type proc "system" (device: Device, pCreateInfo: ^SwapchainCreateInfoKHR, pAllocator: ^AllocationCallbacks, pSwapchain: ^SwapchainKHR) -> Result -ProcDestroySwapchainKHR :: #type proc "system" (device: Device, swapchain: SwapchainKHR, pAllocator: ^AllocationCallbacks) -ProcGetSwapchainImagesKHR :: #type proc "system" (device: Device, swapchain: SwapchainKHR, pSwapchainImageCount: ^u32, pSwapchainImages: [^]Image) -> Result -ProcAcquireNextImageKHR :: #type proc "system" (device: Device, swapchain: SwapchainKHR, timeout: u64, semaphore: Semaphore, fence: Fence, pImageIndex: ^u32) -> Result -ProcQueuePresentKHR :: #type proc "system" (queue: Queue, pPresentInfo: ^PresentInfoKHR) -> Result -ProcGetDeviceGroupPresentCapabilitiesKHR :: #type proc "system" (device: Device, pDeviceGroupPresentCapabilities: [^]DeviceGroupPresentCapabilitiesKHR) -> Result -ProcGetDeviceGroupSurfacePresentModesKHR :: #type proc "system" (device: Device, surface: SurfaceKHR, pModes: [^]DeviceGroupPresentModeFlagsKHR) -> Result -ProcGetPhysicalDevicePresentRectanglesKHR :: #type proc "system" (physicalDevice: PhysicalDevice, surface: SurfaceKHR, pRectCount: ^u32, pRects: [^]Rect2D) -> Result -ProcAcquireNextImage2KHR :: #type proc "system" (device: Device, pAcquireInfo: ^AcquireNextImageInfoKHR, pImageIndex: ^u32) -> Result -ProcGetPhysicalDeviceDisplayPropertiesKHR :: #type proc "system" (physicalDevice: PhysicalDevice, pPropertyCount: ^u32, pProperties: [^]DisplayPropertiesKHR) -> Result -ProcGetPhysicalDeviceDisplayPlanePropertiesKHR :: #type proc "system" (physicalDevice: PhysicalDevice, pPropertyCount: ^u32, pProperties: [^]DisplayPlanePropertiesKHR) -> Result -ProcGetDisplayPlaneSupportedDisplaysKHR :: #type proc "system" (physicalDevice: PhysicalDevice, planeIndex: u32, pDisplayCount: ^u32, pDisplays: [^]DisplayKHR) -> Result -ProcGetDisplayModePropertiesKHR :: #type proc "system" (physicalDevice: PhysicalDevice, display: DisplayKHR, pPropertyCount: ^u32, pProperties: [^]DisplayModePropertiesKHR) -> Result -ProcCreateDisplayModeKHR :: #type proc "system" (physicalDevice: PhysicalDevice, display: DisplayKHR, pCreateInfo: ^DisplayModeCreateInfoKHR, pAllocator: ^AllocationCallbacks, pMode: ^DisplayModeKHR) -> Result -ProcGetDisplayPlaneCapabilitiesKHR :: #type proc "system" (physicalDevice: PhysicalDevice, mode: DisplayModeKHR, planeIndex: u32, pCapabilities: [^]DisplayPlaneCapabilitiesKHR) -> Result -ProcCreateDisplayPlaneSurfaceKHR :: #type proc "system" (instance: Instance, pCreateInfo: ^DisplaySurfaceCreateInfoKHR, pAllocator: ^AllocationCallbacks, pSurface: ^SurfaceKHR) -> Result -ProcCreateSharedSwapchainsKHR :: #type proc "system" (device: Device, swapchainCount: u32, pCreateInfos: [^]SwapchainCreateInfoKHR, pAllocator: ^AllocationCallbacks, pSwapchains: [^]SwapchainKHR) -> Result -ProcGetPhysicalDeviceFeatures2KHR :: #type proc "system" (physicalDevice: PhysicalDevice, pFeatures: [^]PhysicalDeviceFeatures2) -ProcGetPhysicalDeviceProperties2KHR :: #type proc "system" (physicalDevice: PhysicalDevice, pProperties: [^]PhysicalDeviceProperties2) -ProcGetPhysicalDeviceFormatProperties2KHR :: #type proc "system" (physicalDevice: PhysicalDevice, format: Format, pFormatProperties: [^]FormatProperties2) -ProcGetPhysicalDeviceImageFormatProperties2KHR :: #type proc "system" (physicalDevice: PhysicalDevice, pImageFormatInfo: ^PhysicalDeviceImageFormatInfo2, pImageFormatProperties: [^]ImageFormatProperties2) -> Result -ProcGetPhysicalDeviceQueueFamilyProperties2KHR :: #type proc "system" (physicalDevice: PhysicalDevice, pQueueFamilyPropertyCount: ^u32, pQueueFamilyProperties: [^]QueueFamilyProperties2) -ProcGetPhysicalDeviceMemoryProperties2KHR :: #type proc "system" (physicalDevice: PhysicalDevice, pMemoryProperties: [^]PhysicalDeviceMemoryProperties2) -ProcGetPhysicalDeviceSparseImageFormatProperties2KHR :: #type proc "system" (physicalDevice: PhysicalDevice, pFormatInfo: ^PhysicalDeviceSparseImageFormatInfo2, pPropertyCount: ^u32, pProperties: [^]SparseImageFormatProperties2) -ProcGetDeviceGroupPeerMemoryFeaturesKHR :: #type proc "system" (device: Device, heapIndex: u32, localDeviceIndex: u32, remoteDeviceIndex: u32, pPeerMemoryFeatures: [^]PeerMemoryFeatureFlags) -ProcCmdSetDeviceMaskKHR :: #type proc "system" (commandBuffer: CommandBuffer, deviceMask: u32) -ProcCmdDispatchBaseKHR :: #type proc "system" (commandBuffer: CommandBuffer, baseGroupX: u32, baseGroupY: u32, baseGroupZ: u32, groupCountX: u32, groupCountY: u32, groupCountZ: u32) -ProcTrimCommandPoolKHR :: #type proc "system" (device: Device, commandPool: CommandPool, flags: CommandPoolTrimFlags) -ProcEnumeratePhysicalDeviceGroupsKHR :: #type proc "system" (instance: Instance, pPhysicalDeviceGroupCount: ^u32, pPhysicalDeviceGroupProperties: [^]PhysicalDeviceGroupProperties) -> Result -ProcGetPhysicalDeviceExternalBufferPropertiesKHR :: #type proc "system" (physicalDevice: PhysicalDevice, pExternalBufferInfo: ^PhysicalDeviceExternalBufferInfo, pExternalBufferProperties: [^]ExternalBufferProperties) -ProcGetMemoryFdKHR :: #type proc "system" (device: Device, pGetFdInfo: ^MemoryGetFdInfoKHR, pFd: ^c.int) -> Result -ProcGetMemoryFdPropertiesKHR :: #type proc "system" (device: Device, handleType: ExternalMemoryHandleTypeFlags, fd: c.int, pMemoryFdProperties: [^]MemoryFdPropertiesKHR) -> Result -ProcGetPhysicalDeviceExternalSemaphorePropertiesKHR :: #type proc "system" (physicalDevice: PhysicalDevice, pExternalSemaphoreInfo: ^PhysicalDeviceExternalSemaphoreInfo, pExternalSemaphoreProperties: [^]ExternalSemaphoreProperties) -ProcImportSemaphoreFdKHR :: #type proc "system" (device: Device, pImportSemaphoreFdInfo: ^ImportSemaphoreFdInfoKHR) -> Result -ProcGetSemaphoreFdKHR :: #type proc "system" (device: Device, pGetFdInfo: ^SemaphoreGetFdInfoKHR, pFd: ^c.int) -> Result -ProcCmdPushDescriptorSetKHR :: #type proc "system" (commandBuffer: CommandBuffer, pipelineBindPoint: PipelineBindPoint, layout: PipelineLayout, set: u32, descriptorWriteCount: u32, pDescriptorWrites: [^]WriteDescriptorSet) -ProcCmdPushDescriptorSetWithTemplateKHR :: #type proc "system" (commandBuffer: CommandBuffer, descriptorUpdateTemplate: DescriptorUpdateTemplate, layout: PipelineLayout, set: u32, pData: rawptr) -ProcCreateDescriptorUpdateTemplateKHR :: #type proc "system" (device: Device, pCreateInfo: ^DescriptorUpdateTemplateCreateInfo, pAllocator: ^AllocationCallbacks, pDescriptorUpdateTemplate: ^DescriptorUpdateTemplate) -> Result -ProcDestroyDescriptorUpdateTemplateKHR :: #type proc "system" (device: Device, descriptorUpdateTemplate: DescriptorUpdateTemplate, pAllocator: ^AllocationCallbacks) -ProcUpdateDescriptorSetWithTemplateKHR :: #type proc "system" (device: Device, descriptorSet: DescriptorSet, descriptorUpdateTemplate: DescriptorUpdateTemplate, pData: rawptr) -ProcCreateRenderPass2KHR :: #type proc "system" (device: Device, pCreateInfo: ^RenderPassCreateInfo2, pAllocator: ^AllocationCallbacks, pRenderPass: [^]RenderPass) -> Result -ProcCmdBeginRenderPass2KHR :: #type proc "system" (commandBuffer: CommandBuffer, pRenderPassBegin: ^RenderPassBeginInfo, pSubpassBeginInfo: ^SubpassBeginInfo) -ProcCmdNextSubpass2KHR :: #type proc "system" (commandBuffer: CommandBuffer, pSubpassBeginInfo: ^SubpassBeginInfo, pSubpassEndInfo: ^SubpassEndInfo) -ProcCmdEndRenderPass2KHR :: #type proc "system" (commandBuffer: CommandBuffer, pSubpassEndInfo: ^SubpassEndInfo) -ProcGetSwapchainStatusKHR :: #type proc "system" (device: Device, swapchain: SwapchainKHR) -> Result -ProcGetPhysicalDeviceExternalFencePropertiesKHR :: #type proc "system" (physicalDevice: PhysicalDevice, pExternalFenceInfo: ^PhysicalDeviceExternalFenceInfo, pExternalFenceProperties: [^]ExternalFenceProperties) -ProcImportFenceFdKHR :: #type proc "system" (device: Device, pImportFenceFdInfo: ^ImportFenceFdInfoKHR) -> Result -ProcGetFenceFdKHR :: #type proc "system" (device: Device, pGetFdInfo: ^FenceGetFdInfoKHR, pFd: ^c.int) -> Result -ProcEnumeratePhysicalDeviceQueueFamilyPerformanceQueryCountersKHR :: #type proc "system" (physicalDevice: PhysicalDevice, queueFamilyIndex: u32, pCounterCount: ^u32, pCounters: [^]PerformanceCounterKHR, pCounterDescriptions: [^]PerformanceCounterDescriptionKHR) -> Result -ProcGetPhysicalDeviceQueueFamilyPerformanceQueryPassesKHR :: #type proc "system" (physicalDevice: PhysicalDevice, pPerformanceQueryCreateInfo: ^QueryPoolPerformanceCreateInfoKHR, pNumPasses: [^]u32) -ProcAcquireProfilingLockKHR :: #type proc "system" (device: Device, pInfo: ^AcquireProfilingLockInfoKHR) -> Result -ProcReleaseProfilingLockKHR :: #type proc "system" (device: Device) -ProcGetPhysicalDeviceSurfaceCapabilities2KHR :: #type proc "system" (physicalDevice: PhysicalDevice, pSurfaceInfo: ^PhysicalDeviceSurfaceInfo2KHR, pSurfaceCapabilities: [^]SurfaceCapabilities2KHR) -> Result -ProcGetPhysicalDeviceSurfaceFormats2KHR :: #type proc "system" (physicalDevice: PhysicalDevice, pSurfaceInfo: ^PhysicalDeviceSurfaceInfo2KHR, pSurfaceFormatCount: ^u32, pSurfaceFormats: [^]SurfaceFormat2KHR) -> Result -ProcGetPhysicalDeviceDisplayProperties2KHR :: #type proc "system" (physicalDevice: PhysicalDevice, pPropertyCount: ^u32, pProperties: [^]DisplayProperties2KHR) -> Result -ProcGetPhysicalDeviceDisplayPlaneProperties2KHR :: #type proc "system" (physicalDevice: PhysicalDevice, pPropertyCount: ^u32, pProperties: [^]DisplayPlaneProperties2KHR) -> Result -ProcGetDisplayModeProperties2KHR :: #type proc "system" (physicalDevice: PhysicalDevice, display: DisplayKHR, pPropertyCount: ^u32, pProperties: [^]DisplayModeProperties2KHR) -> Result -ProcGetDisplayPlaneCapabilities2KHR :: #type proc "system" (physicalDevice: PhysicalDevice, pDisplayPlaneInfo: ^DisplayPlaneInfo2KHR, pCapabilities: [^]DisplayPlaneCapabilities2KHR) -> Result -ProcGetImageMemoryRequirements2KHR :: #type proc "system" (device: Device, pInfo: ^ImageMemoryRequirementsInfo2, pMemoryRequirements: [^]MemoryRequirements2) -ProcGetBufferMemoryRequirements2KHR :: #type proc "system" (device: Device, pInfo: ^BufferMemoryRequirementsInfo2, pMemoryRequirements: [^]MemoryRequirements2) -ProcGetImageSparseMemoryRequirements2KHR :: #type proc "system" (device: Device, pInfo: ^ImageSparseMemoryRequirementsInfo2, pSparseMemoryRequirementCount: ^u32, pSparseMemoryRequirements: [^]SparseImageMemoryRequirements2) -ProcCreateSamplerYcbcrConversionKHR :: #type proc "system" (device: Device, pCreateInfo: ^SamplerYcbcrConversionCreateInfo, pAllocator: ^AllocationCallbacks, pYcbcrConversion: ^SamplerYcbcrConversion) -> Result -ProcDestroySamplerYcbcrConversionKHR :: #type proc "system" (device: Device, ycbcrConversion: SamplerYcbcrConversion, pAllocator: ^AllocationCallbacks) -ProcBindBufferMemory2KHR :: #type proc "system" (device: Device, bindInfoCount: u32, pBindInfos: [^]BindBufferMemoryInfo) -> Result -ProcBindImageMemory2KHR :: #type proc "system" (device: Device, bindInfoCount: u32, pBindInfos: [^]BindImageMemoryInfo) -> Result -ProcGetDescriptorSetLayoutSupportKHR :: #type proc "system" (device: Device, pCreateInfo: ^DescriptorSetLayoutCreateInfo, pSupport: ^DescriptorSetLayoutSupport) -ProcCmdDrawIndirectCountKHR :: #type proc "system" (commandBuffer: CommandBuffer, buffer: Buffer, offset: DeviceSize, countBuffer: Buffer, countBufferOffset: DeviceSize, maxDrawCount: u32, stride: u32) -ProcCmdDrawIndexedIndirectCountKHR :: #type proc "system" (commandBuffer: CommandBuffer, buffer: Buffer, offset: DeviceSize, countBuffer: Buffer, countBufferOffset: DeviceSize, maxDrawCount: u32, stride: u32) -ProcGetSemaphoreCounterValueKHR :: #type proc "system" (device: Device, semaphore: Semaphore, pValue: ^u64) -> Result -ProcWaitSemaphoresKHR :: #type proc "system" (device: Device, pWaitInfo: ^SemaphoreWaitInfo, timeout: u64) -> Result -ProcSignalSemaphoreKHR :: #type proc "system" (device: Device, pSignalInfo: ^SemaphoreSignalInfo) -> Result -ProcGetPhysicalDeviceFragmentShadingRatesKHR :: #type proc "system" (physicalDevice: PhysicalDevice, pFragmentShadingRateCount: ^u32, pFragmentShadingRates: [^]PhysicalDeviceFragmentShadingRateKHR) -> Result -ProcCmdSetFragmentShadingRateKHR :: #type proc "system" (commandBuffer: CommandBuffer, pFragmentSize: ^Extent2D) -ProcWaitForPresentKHR :: #type proc "system" (device: Device, swapchain: SwapchainKHR, presentId: u64, timeout: u64) -> Result -ProcGetBufferDeviceAddressKHR :: #type proc "system" (device: Device, pInfo: ^BufferDeviceAddressInfo) -> DeviceAddress -ProcGetBufferOpaqueCaptureAddressKHR :: #type proc "system" (device: Device, pInfo: ^BufferDeviceAddressInfo) -> u64 -ProcGetDeviceMemoryOpaqueCaptureAddressKHR :: #type proc "system" (device: Device, pInfo: ^DeviceMemoryOpaqueCaptureAddressInfo) -> u64 -ProcCreateDeferredOperationKHR :: #type proc "system" (device: Device, pAllocator: ^AllocationCallbacks, pDeferredOperation: ^DeferredOperationKHR) -> Result -ProcDestroyDeferredOperationKHR :: #type proc "system" (device: Device, operation: DeferredOperationKHR, pAllocator: ^AllocationCallbacks) -ProcGetDeferredOperationMaxConcurrencyKHR :: #type proc "system" (device: Device, operation: DeferredOperationKHR) -> u32 -ProcGetDeferredOperationResultKHR :: #type proc "system" (device: Device, operation: DeferredOperationKHR) -> Result -ProcDeferredOperationJoinKHR :: #type proc "system" (device: Device, operation: DeferredOperationKHR) -> Result -ProcGetPipelineExecutablePropertiesKHR :: #type proc "system" (device: Device, pPipelineInfo: ^PipelineInfoKHR, pExecutableCount: ^u32, pProperties: [^]PipelineExecutablePropertiesKHR) -> Result -ProcGetPipelineExecutableStatisticsKHR :: #type proc "system" (device: Device, pExecutableInfo: ^PipelineExecutableInfoKHR, pStatisticCount: ^u32, pStatistics: [^]PipelineExecutableStatisticKHR) -> Result -ProcGetPipelineExecutableInternalRepresentationsKHR :: #type proc "system" (device: Device, pExecutableInfo: ^PipelineExecutableInfoKHR, pInternalRepresentationCount: ^u32, pInternalRepresentations: [^]PipelineExecutableInternalRepresentationKHR) -> Result -ProcCmdSetEvent2KHR :: #type proc "system" (commandBuffer: CommandBuffer, event: Event, pDependencyInfo: ^DependencyInfoKHR) -ProcCmdResetEvent2KHR :: #type proc "system" (commandBuffer: CommandBuffer, event: Event, stageMask: PipelineStageFlags2KHR) -ProcCmdWaitEvents2KHR :: #type proc "system" (commandBuffer: CommandBuffer, eventCount: u32, pEvents: [^]Event, pDependencyInfos: [^]DependencyInfoKHR) -ProcCmdPipelineBarrier2KHR :: #type proc "system" (commandBuffer: CommandBuffer, pDependencyInfo: ^DependencyInfoKHR) -ProcCmdWriteTimestamp2KHR :: #type proc "system" (commandBuffer: CommandBuffer, stage: PipelineStageFlags2KHR, queryPool: QueryPool, query: u32) -ProcQueueSubmit2KHR :: #type proc "system" (queue: Queue, submitCount: u32, pSubmits: [^]SubmitInfo2KHR, fence: Fence) -> Result -ProcCmdWriteBufferMarker2AMD :: #type proc "system" (commandBuffer: CommandBuffer, stage: PipelineStageFlags2KHR, dstBuffer: Buffer, dstOffset: DeviceSize, marker: u32) -ProcGetQueueCheckpointData2NV :: #type proc "system" (queue: Queue, pCheckpointDataCount: ^u32, pCheckpointData: ^CheckpointData2NV) -ProcCmdCopyBuffer2KHR :: #type proc "system" (commandBuffer: CommandBuffer, pCopyBufferInfo: ^CopyBufferInfo2KHR) -ProcCmdCopyImage2KHR :: #type proc "system" (commandBuffer: CommandBuffer, pCopyImageInfo: ^CopyImageInfo2KHR) -ProcCmdCopyBufferToImage2KHR :: #type proc "system" (commandBuffer: CommandBuffer, pCopyBufferToImageInfo: ^CopyBufferToImageInfo2KHR) -ProcCmdCopyImageToBuffer2KHR :: #type proc "system" (commandBuffer: CommandBuffer, pCopyImageToBufferInfo: ^CopyImageToBufferInfo2KHR) -ProcCmdBlitImage2KHR :: #type proc "system" (commandBuffer: CommandBuffer, pBlitImageInfo: ^BlitImageInfo2KHR) -ProcCmdResolveImage2KHR :: #type proc "system" (commandBuffer: CommandBuffer, pResolveImageInfo: ^ResolveImageInfo2KHR) -ProcDebugReportCallbackEXT :: #type proc "system" (flags: DebugReportFlagsEXT, objectType: DebugReportObjectTypeEXT, object: u64, location: int, messageCode: i32, pLayerPrefix: cstring, pMessage: cstring, pUserData: rawptr) -> b32 -ProcCreateDebugReportCallbackEXT :: #type proc "system" (instance: Instance, pCreateInfo: ^DebugReportCallbackCreateInfoEXT, pAllocator: ^AllocationCallbacks, pCallback: ^DebugReportCallbackEXT) -> Result -ProcDestroyDebugReportCallbackEXT :: #type proc "system" (instance: Instance, callback: DebugReportCallbackEXT, pAllocator: ^AllocationCallbacks) -ProcDebugReportMessageEXT :: #type proc "system" (instance: Instance, flags: DebugReportFlagsEXT, objectType: DebugReportObjectTypeEXT, object: u64, location: int, messageCode: i32, pLayerPrefix: cstring, pMessage: cstring) -ProcDebugMarkerSetObjectTagEXT :: #type proc "system" (device: Device, pTagInfo: ^DebugMarkerObjectTagInfoEXT) -> Result -ProcDebugMarkerSetObjectNameEXT :: #type proc "system" (device: Device, pNameInfo: ^DebugMarkerObjectNameInfoEXT) -> Result -ProcCmdDebugMarkerBeginEXT :: #type proc "system" (commandBuffer: CommandBuffer, pMarkerInfo: ^DebugMarkerMarkerInfoEXT) -ProcCmdDebugMarkerEndEXT :: #type proc "system" (commandBuffer: CommandBuffer) -ProcCmdDebugMarkerInsertEXT :: #type proc "system" (commandBuffer: CommandBuffer, pMarkerInfo: ^DebugMarkerMarkerInfoEXT) -ProcCmdBindTransformFeedbackBuffersEXT :: #type proc "system" (commandBuffer: CommandBuffer, firstBinding: u32, bindingCount: u32, pBuffers: [^]Buffer, pOffsets: [^]DeviceSize, pSizes: [^]DeviceSize) -ProcCmdBeginTransformFeedbackEXT :: #type proc "system" (commandBuffer: CommandBuffer, firstCounterBuffer: u32, counterBufferCount: u32, pCounterBuffers: [^]Buffer, pCounterBufferOffsets: [^]DeviceSize) -ProcCmdEndTransformFeedbackEXT :: #type proc "system" (commandBuffer: CommandBuffer, firstCounterBuffer: u32, counterBufferCount: u32, pCounterBuffers: [^]Buffer, pCounterBufferOffsets: [^]DeviceSize) -ProcCmdBeginQueryIndexedEXT :: #type proc "system" (commandBuffer: CommandBuffer, queryPool: QueryPool, query: u32, flags: QueryControlFlags, index: u32) -ProcCmdEndQueryIndexedEXT :: #type proc "system" (commandBuffer: CommandBuffer, queryPool: QueryPool, query: u32, index: u32) -ProcCmdDrawIndirectByteCountEXT :: #type proc "system" (commandBuffer: CommandBuffer, instanceCount: u32, firstInstance: u32, counterBuffer: Buffer, counterBufferOffset: DeviceSize, counterOffset: u32, vertexStride: u32) -ProcCreateCuModuleNVX :: #type proc "system" (device: Device, pCreateInfo: ^CuModuleCreateInfoNVX, pAllocator: ^AllocationCallbacks, pModule: ^CuModuleNVX) -> Result -ProcCreateCuFunctionNVX :: #type proc "system" (device: Device, pCreateInfo: ^CuFunctionCreateInfoNVX, pAllocator: ^AllocationCallbacks, pFunction: ^CuFunctionNVX) -> Result -ProcDestroyCuModuleNVX :: #type proc "system" (device: Device, module: CuModuleNVX, pAllocator: ^AllocationCallbacks) -ProcDestroyCuFunctionNVX :: #type proc "system" (device: Device, function: CuFunctionNVX, pAllocator: ^AllocationCallbacks) -ProcCmdCuLaunchKernelNVX :: #type proc "system" (commandBuffer: CommandBuffer, pLaunchInfo: ^CuLaunchInfoNVX) -ProcGetImageViewHandleNVX :: #type proc "system" (device: Device, pInfo: ^ImageViewHandleInfoNVX) -> u32 -ProcGetImageViewAddressNVX :: #type proc "system" (device: Device, imageView: ImageView, pProperties: [^]ImageViewAddressPropertiesNVX) -> Result -ProcCmdDrawIndirectCountAMD :: #type proc "system" (commandBuffer: CommandBuffer, buffer: Buffer, offset: DeviceSize, countBuffer: Buffer, countBufferOffset: DeviceSize, maxDrawCount: u32, stride: u32) -ProcCmdDrawIndexedIndirectCountAMD :: #type proc "system" (commandBuffer: CommandBuffer, buffer: Buffer, offset: DeviceSize, countBuffer: Buffer, countBufferOffset: DeviceSize, maxDrawCount: u32, stride: u32) -ProcGetShaderInfoAMD :: #type proc "system" (device: Device, pipeline: Pipeline, shaderStage: ShaderStageFlags, infoType: ShaderInfoTypeAMD, pInfoSize: ^int, pInfo: rawptr) -> Result -ProcGetPhysicalDeviceExternalImageFormatPropertiesNV :: #type proc "system" (physicalDevice: PhysicalDevice, format: Format, type: ImageType, tiling: ImageTiling, usage: ImageUsageFlags, flags: ImageCreateFlags, externalHandleType: ExternalMemoryHandleTypeFlagsNV, pExternalImageFormatProperties: [^]ExternalImageFormatPropertiesNV) -> Result -ProcCmdBeginConditionalRenderingEXT :: #type proc "system" (commandBuffer: CommandBuffer, pConditionalRenderingBegin: ^ConditionalRenderingBeginInfoEXT) -ProcCmdEndConditionalRenderingEXT :: #type proc "system" (commandBuffer: CommandBuffer) -ProcCmdSetViewportWScalingNV :: #type proc "system" (commandBuffer: CommandBuffer, firstViewport: u32, viewportCount: u32, pViewportWScalings: [^]ViewportWScalingNV) -ProcReleaseDisplayEXT :: #type proc "system" (physicalDevice: PhysicalDevice, display: DisplayKHR) -> Result -ProcGetPhysicalDeviceSurfaceCapabilities2EXT :: #type proc "system" (physicalDevice: PhysicalDevice, surface: SurfaceKHR, pSurfaceCapabilities: [^]SurfaceCapabilities2EXT) -> Result -ProcDisplayPowerControlEXT :: #type proc "system" (device: Device, display: DisplayKHR, pDisplayPowerInfo: ^DisplayPowerInfoEXT) -> Result -ProcRegisterDeviceEventEXT :: #type proc "system" (device: Device, pDeviceEventInfo: ^DeviceEventInfoEXT, pAllocator: ^AllocationCallbacks, pFence: ^Fence) -> Result -ProcRegisterDisplayEventEXT :: #type proc "system" (device: Device, display: DisplayKHR, pDisplayEventInfo: ^DisplayEventInfoEXT, pAllocator: ^AllocationCallbacks, pFence: ^Fence) -> Result -ProcGetSwapchainCounterEXT :: #type proc "system" (device: Device, swapchain: SwapchainKHR, counter: SurfaceCounterFlagsEXT, pCounterValue: ^u64) -> Result -ProcGetRefreshCycleDurationGOOGLE :: #type proc "system" (device: Device, swapchain: SwapchainKHR, pDisplayTimingProperties: [^]RefreshCycleDurationGOOGLE) -> Result -ProcGetPastPresentationTimingGOOGLE :: #type proc "system" (device: Device, swapchain: SwapchainKHR, pPresentationTimingCount: ^u32, pPresentationTimings: [^]PastPresentationTimingGOOGLE) -> Result -ProcCmdSetDiscardRectangleEXT :: #type proc "system" (commandBuffer: CommandBuffer, firstDiscardRectangle: u32, discardRectangleCount: u32, pDiscardRectangles: [^]Rect2D) -ProcSetHdrMetadataEXT :: #type proc "system" (device: Device, swapchainCount: u32, pSwapchains: [^]SwapchainKHR, pMetadata: ^HdrMetadataEXT) -ProcDebugUtilsMessengerCallbackEXT :: #type proc "system" (messageSeverity: DebugUtilsMessageSeverityFlagsEXT, messageTypes: DebugUtilsMessageTypeFlagsEXT, pCallbackData: ^DebugUtilsMessengerCallbackDataEXT, pUserData: rawptr) -> b32 -ProcSetDebugUtilsObjectNameEXT :: #type proc "system" (device: Device, pNameInfo: ^DebugUtilsObjectNameInfoEXT) -> Result -ProcSetDebugUtilsObjectTagEXT :: #type proc "system" (device: Device, pTagInfo: ^DebugUtilsObjectTagInfoEXT) -> Result -ProcQueueBeginDebugUtilsLabelEXT :: #type proc "system" (queue: Queue, pLabelInfo: ^DebugUtilsLabelEXT) -ProcQueueEndDebugUtilsLabelEXT :: #type proc "system" (queue: Queue) -ProcQueueInsertDebugUtilsLabelEXT :: #type proc "system" (queue: Queue, pLabelInfo: ^DebugUtilsLabelEXT) -ProcCmdBeginDebugUtilsLabelEXT :: #type proc "system" (commandBuffer: CommandBuffer, pLabelInfo: ^DebugUtilsLabelEXT) -ProcCmdEndDebugUtilsLabelEXT :: #type proc "system" (commandBuffer: CommandBuffer) -ProcCmdInsertDebugUtilsLabelEXT :: #type proc "system" (commandBuffer: CommandBuffer, pLabelInfo: ^DebugUtilsLabelEXT) -ProcCreateDebugUtilsMessengerEXT :: #type proc "system" (instance: Instance, pCreateInfo: ^DebugUtilsMessengerCreateInfoEXT, pAllocator: ^AllocationCallbacks, pMessenger: ^DebugUtilsMessengerEXT) -> Result -ProcDestroyDebugUtilsMessengerEXT :: #type proc "system" (instance: Instance, messenger: DebugUtilsMessengerEXT, pAllocator: ^AllocationCallbacks) -ProcSubmitDebugUtilsMessageEXT :: #type proc "system" (instance: Instance, messageSeverity: DebugUtilsMessageSeverityFlagsEXT, messageTypes: DebugUtilsMessageTypeFlagsEXT, pCallbackData: ^DebugUtilsMessengerCallbackDataEXT) -ProcCmdSetSampleLocationsEXT :: #type proc "system" (commandBuffer: CommandBuffer, pSampleLocationsInfo: ^SampleLocationsInfoEXT) -ProcGetPhysicalDeviceMultisamplePropertiesEXT :: #type proc "system" (physicalDevice: PhysicalDevice, samples: SampleCountFlags, pMultisampleProperties: [^]MultisamplePropertiesEXT) -ProcGetImageDrmFormatModifierPropertiesEXT :: #type proc "system" (device: Device, image: Image, pProperties: [^]ImageDrmFormatModifierPropertiesEXT) -> Result -ProcCreateValidationCacheEXT :: #type proc "system" (device: Device, pCreateInfo: ^ValidationCacheCreateInfoEXT, pAllocator: ^AllocationCallbacks, pValidationCache: ^ValidationCacheEXT) -> Result -ProcDestroyValidationCacheEXT :: #type proc "system" (device: Device, validationCache: ValidationCacheEXT, pAllocator: ^AllocationCallbacks) -ProcMergeValidationCachesEXT :: #type proc "system" (device: Device, dstCache: ValidationCacheEXT, srcCacheCount: u32, pSrcCaches: [^]ValidationCacheEXT) -> Result -ProcGetValidationCacheDataEXT :: #type proc "system" (device: Device, validationCache: ValidationCacheEXT, pDataSize: ^int, pData: rawptr) -> Result -ProcCmdBindShadingRateImageNV :: #type proc "system" (commandBuffer: CommandBuffer, imageView: ImageView, imageLayout: ImageLayout) -ProcCmdSetViewportShadingRatePaletteNV :: #type proc "system" (commandBuffer: CommandBuffer, firstViewport: u32, viewportCount: u32, pShadingRatePalettes: [^]ShadingRatePaletteNV) -ProcCmdSetCoarseSampleOrderNV :: #type proc "system" (commandBuffer: CommandBuffer, sampleOrderType: CoarseSampleOrderTypeNV, customSampleOrderCount: u32, pCustomSampleOrders: [^]CoarseSampleOrderCustomNV) -ProcCreateAccelerationStructureNV :: #type proc "system" (device: Device, pCreateInfo: ^AccelerationStructureCreateInfoNV, pAllocator: ^AllocationCallbacks, pAccelerationStructure: ^AccelerationStructureNV) -> Result -ProcDestroyAccelerationStructureNV :: #type proc "system" (device: Device, accelerationStructure: AccelerationStructureNV, pAllocator: ^AllocationCallbacks) -ProcGetAccelerationStructureMemoryRequirementsNV :: #type proc "system" (device: Device, pInfo: ^AccelerationStructureMemoryRequirementsInfoNV, pMemoryRequirements: [^]MemoryRequirements2KHR) -ProcBindAccelerationStructureMemoryNV :: #type proc "system" (device: Device, bindInfoCount: u32, pBindInfos: [^]BindAccelerationStructureMemoryInfoNV) -> Result -ProcCmdBuildAccelerationStructureNV :: #type proc "system" (commandBuffer: CommandBuffer, pInfo: ^AccelerationStructureInfoNV, instanceData: Buffer, instanceOffset: DeviceSize, update: b32, dst: AccelerationStructureNV, src: AccelerationStructureNV, scratch: Buffer, scratchOffset: DeviceSize) -ProcCmdCopyAccelerationStructureNV :: #type proc "system" (commandBuffer: CommandBuffer, dst: AccelerationStructureNV, src: AccelerationStructureNV, mode: CopyAccelerationStructureModeKHR) -ProcCmdTraceRaysNV :: #type proc "system" (commandBuffer: CommandBuffer, raygenShaderBindingTableBuffer: Buffer, raygenShaderBindingOffset: DeviceSize, missShaderBindingTableBuffer: Buffer, missShaderBindingOffset: DeviceSize, missShaderBindingStride: DeviceSize, hitShaderBindingTableBuffer: Buffer, hitShaderBindingOffset: DeviceSize, hitShaderBindingStride: DeviceSize, callableShaderBindingTableBuffer: Buffer, callableShaderBindingOffset: DeviceSize, callableShaderBindingStride: DeviceSize, width: u32, height: u32, depth: u32) -ProcCreateRayTracingPipelinesNV :: #type proc "system" (device: Device, pipelineCache: PipelineCache, createInfoCount: u32, pCreateInfos: [^]RayTracingPipelineCreateInfoNV, pAllocator: ^AllocationCallbacks, pPipelines: [^]Pipeline) -> Result -ProcGetRayTracingShaderGroupHandlesKHR :: #type proc "system" (device: Device, pipeline: Pipeline, firstGroup: u32, groupCount: u32, dataSize: int, pData: rawptr) -> Result -ProcGetRayTracingShaderGroupHandlesNV :: #type proc "system" (device: Device, pipeline: Pipeline, firstGroup: u32, groupCount: u32, dataSize: int, pData: rawptr) -> Result -ProcGetAccelerationStructureHandleNV :: #type proc "system" (device: Device, accelerationStructure: AccelerationStructureNV, dataSize: int, pData: rawptr) -> Result -ProcCmdWriteAccelerationStructuresPropertiesNV :: #type proc "system" (commandBuffer: CommandBuffer, accelerationStructureCount: u32, pAccelerationStructures: [^]AccelerationStructureNV, queryType: QueryType, queryPool: QueryPool, firstQuery: u32) -ProcCompileDeferredNV :: #type proc "system" (device: Device, pipeline: Pipeline, shader: u32) -> Result -ProcGetMemoryHostPointerPropertiesEXT :: #type proc "system" (device: Device, handleType: ExternalMemoryHandleTypeFlags, pHostPointer: rawptr, pMemoryHostPointerProperties: [^]MemoryHostPointerPropertiesEXT) -> Result -ProcCmdWriteBufferMarkerAMD :: #type proc "system" (commandBuffer: CommandBuffer, pipelineStage: PipelineStageFlags, dstBuffer: Buffer, dstOffset: DeviceSize, marker: u32) -ProcGetPhysicalDeviceCalibrateableTimeDomainsEXT :: #type proc "system" (physicalDevice: PhysicalDevice, pTimeDomainCount: ^u32, pTimeDomains: [^]TimeDomainEXT) -> Result -ProcGetCalibratedTimestampsEXT :: #type proc "system" (device: Device, timestampCount: u32, pTimestampInfos: [^]CalibratedTimestampInfoEXT, pTimestamps: [^]u64, pMaxDeviation: ^u64) -> Result -ProcCmdDrawMeshTasksNV :: #type proc "system" (commandBuffer: CommandBuffer, taskCount: u32, firstTask: u32) -ProcCmdDrawMeshTasksIndirectNV :: #type proc "system" (commandBuffer: CommandBuffer, buffer: Buffer, offset: DeviceSize, drawCount: u32, stride: u32) -ProcCmdDrawMeshTasksIndirectCountNV :: #type proc "system" (commandBuffer: CommandBuffer, buffer: Buffer, offset: DeviceSize, countBuffer: Buffer, countBufferOffset: DeviceSize, maxDrawCount: u32, stride: u32) -ProcCmdSetExclusiveScissorNV :: #type proc "system" (commandBuffer: CommandBuffer, firstExclusiveScissor: u32, exclusiveScissorCount: u32, pExclusiveScissors: [^]Rect2D) -ProcCmdSetCheckpointNV :: #type proc "system" (commandBuffer: CommandBuffer, pCheckpointMarker: rawptr) -ProcGetQueueCheckpointDataNV :: #type proc "system" (queue: Queue, pCheckpointDataCount: ^u32, pCheckpointData: ^CheckpointDataNV) -ProcInitializePerformanceApiINTEL :: #type proc "system" (device: Device, pInitializeInfo: ^InitializePerformanceApiInfoINTEL) -> Result -ProcUninitializePerformanceApiINTEL :: #type proc "system" (device: Device) -ProcCmdSetPerformanceMarkerINTEL :: #type proc "system" (commandBuffer: CommandBuffer, pMarkerInfo: ^PerformanceMarkerInfoINTEL) -> Result -ProcCmdSetPerformanceStreamMarkerINTEL :: #type proc "system" (commandBuffer: CommandBuffer, pMarkerInfo: ^PerformanceStreamMarkerInfoINTEL) -> Result -ProcCmdSetPerformanceOverrideINTEL :: #type proc "system" (commandBuffer: CommandBuffer, pOverrideInfo: ^PerformanceOverrideInfoINTEL) -> Result -ProcAcquirePerformanceConfigurationINTEL :: #type proc "system" (device: Device, pAcquireInfo: ^PerformanceConfigurationAcquireInfoINTEL, pConfiguration: ^PerformanceConfigurationINTEL) -> Result -ProcReleasePerformanceConfigurationINTEL :: #type proc "system" (device: Device, configuration: PerformanceConfigurationINTEL) -> Result -ProcQueueSetPerformanceConfigurationINTEL :: #type proc "system" (queue: Queue, configuration: PerformanceConfigurationINTEL) -> Result -ProcGetPerformanceParameterINTEL :: #type proc "system" (device: Device, parameter: PerformanceParameterTypeINTEL, pValue: ^PerformanceValueINTEL) -> Result -ProcSetLocalDimmingAMD :: #type proc "system" (device: Device, swapChain: SwapchainKHR, localDimmingEnable: b32) -ProcGetBufferDeviceAddressEXT :: #type proc "system" (device: Device, pInfo: ^BufferDeviceAddressInfo) -> DeviceAddress -ProcGetPhysicalDeviceToolPropertiesEXT :: #type proc "system" (physicalDevice: PhysicalDevice, pToolCount: ^u32, pToolProperties: [^]PhysicalDeviceToolPropertiesEXT) -> Result -ProcGetPhysicalDeviceCooperativeMatrixPropertiesNV :: #type proc "system" (physicalDevice: PhysicalDevice, pPropertyCount: ^u32, pProperties: [^]CooperativeMatrixPropertiesNV) -> Result -ProcGetPhysicalDeviceSupportedFramebufferMixedSamplesCombinationsNV :: #type proc "system" (physicalDevice: PhysicalDevice, pCombinationCount: ^u32, pCombinations: [^]FramebufferMixedSamplesCombinationNV) -> Result -ProcCreateHeadlessSurfaceEXT :: #type proc "system" (instance: Instance, pCreateInfo: ^HeadlessSurfaceCreateInfoEXT, pAllocator: ^AllocationCallbacks, pSurface: ^SurfaceKHR) -> Result -ProcCmdSetLineStippleEXT :: #type proc "system" (commandBuffer: CommandBuffer, lineStippleFactor: u32, lineStipplePattern: u16) -ProcResetQueryPoolEXT :: #type proc "system" (device: Device, queryPool: QueryPool, firstQuery: u32, queryCount: u32) -ProcCmdSetCullModeEXT :: #type proc "system" (commandBuffer: CommandBuffer, cullMode: CullModeFlags) -ProcCmdSetFrontFaceEXT :: #type proc "system" (commandBuffer: CommandBuffer, frontFace: FrontFace) -ProcCmdSetPrimitiveTopologyEXT :: #type proc "system" (commandBuffer: CommandBuffer, primitiveTopology: PrimitiveTopology) -ProcCmdSetViewportWithCountEXT :: #type proc "system" (commandBuffer: CommandBuffer, viewportCount: u32, pViewports: [^]Viewport) -ProcCmdSetScissorWithCountEXT :: #type proc "system" (commandBuffer: CommandBuffer, scissorCount: u32, pScissors: [^]Rect2D) -ProcCmdBindVertexBuffers2EXT :: #type proc "system" (commandBuffer: CommandBuffer, firstBinding: u32, bindingCount: u32, pBuffers: [^]Buffer, pOffsets: [^]DeviceSize, pSizes: [^]DeviceSize, pStrides: [^]DeviceSize) -ProcCmdSetDepthTestEnableEXT :: #type proc "system" (commandBuffer: CommandBuffer, depthTestEnable: b32) -ProcCmdSetDepthWriteEnableEXT :: #type proc "system" (commandBuffer: CommandBuffer, depthWriteEnable: b32) -ProcCmdSetDepthCompareOpEXT :: #type proc "system" (commandBuffer: CommandBuffer, depthCompareOp: CompareOp) -ProcCmdSetDepthBoundsTestEnableEXT :: #type proc "system" (commandBuffer: CommandBuffer, depthBoundsTestEnable: b32) -ProcCmdSetStencilTestEnableEXT :: #type proc "system" (commandBuffer: CommandBuffer, stencilTestEnable: b32) -ProcCmdSetStencilOpEXT :: #type proc "system" (commandBuffer: CommandBuffer, faceMask: StencilFaceFlags, failOp: StencilOp, passOp: StencilOp, depthFailOp: StencilOp, compareOp: CompareOp) -ProcGetGeneratedCommandsMemoryRequirementsNV :: #type proc "system" (device: Device, pInfo: ^GeneratedCommandsMemoryRequirementsInfoNV, pMemoryRequirements: [^]MemoryRequirements2) -ProcCmdPreprocessGeneratedCommandsNV :: #type proc "system" (commandBuffer: CommandBuffer, pGeneratedCommandsInfo: ^GeneratedCommandsInfoNV) -ProcCmdExecuteGeneratedCommandsNV :: #type proc "system" (commandBuffer: CommandBuffer, isPreprocessed: b32, pGeneratedCommandsInfo: ^GeneratedCommandsInfoNV) -ProcCmdBindPipelineShaderGroupNV :: #type proc "system" (commandBuffer: CommandBuffer, pipelineBindPoint: PipelineBindPoint, pipeline: Pipeline, groupIndex: u32) -ProcCreateIndirectCommandsLayoutNV :: #type proc "system" (device: Device, pCreateInfo: ^IndirectCommandsLayoutCreateInfoNV, pAllocator: ^AllocationCallbacks, pIndirectCommandsLayout: ^IndirectCommandsLayoutNV) -> Result -ProcDestroyIndirectCommandsLayoutNV :: #type proc "system" (device: Device, indirectCommandsLayout: IndirectCommandsLayoutNV, pAllocator: ^AllocationCallbacks) -ProcDeviceMemoryReportCallbackEXT :: #type proc "system" (pCallbackData: ^DeviceMemoryReportCallbackDataEXT, pUserData: rawptr) +// Misc Procedure Types +ProcAllocationFunction :: #type proc "system" (pUserData: rawptr, size: int, alignment: int, allocationScope: SystemAllocationScope) -> rawptr +ProcDebugReportCallbackEXT :: #type proc "system" (flags: DebugReportFlagsEXT, objectType: DebugReportObjectTypeEXT, object: u64, location: int, messageCode: i32, pLayerPrefix: cstring, pMessage: cstring, pUserData: rawptr) -> b32 +ProcFreeFunction :: #type proc "system" (pUserData: rawptr, pMemory: rawptr) +ProcInternalAllocationNotification :: #type proc "system" (pUserData: rawptr, size: int, allocationType: InternalAllocationType, allocationScope: SystemAllocationScope) +ProcInternalFreeNotification :: #type proc "system" (pUserData: rawptr, size: int, allocationType: InternalAllocationType, allocationScope: SystemAllocationScope) +ProcReallocationFunction :: #type proc "system" (pUserData: rawptr, pOriginal: rawptr, size: int, alignment: int, allocationScope: SystemAllocationScope) -> rawptr +ProcVoidFunction :: #type proc "system" () + +// Instance Procedure Types ProcAcquireDrmDisplayEXT :: #type proc "system" (physicalDevice: PhysicalDevice, drmFd: i32, display: DisplayKHR) -> Result -ProcGetDrmDisplayEXT :: #type proc "system" (physicalDevice: PhysicalDevice, drmFd: i32, connectorId: u32, display: ^DisplayKHR) -> Result -ProcCreatePrivateDataSlotEXT :: #type proc "system" (device: Device, pCreateInfo: ^PrivateDataSlotCreateInfoEXT, pAllocator: ^AllocationCallbacks, pPrivateDataSlot: ^PrivateDataSlotEXT) -> Result -ProcDestroyPrivateDataSlotEXT :: #type proc "system" (device: Device, privateDataSlot: PrivateDataSlotEXT, pAllocator: ^AllocationCallbacks) -ProcSetPrivateDataEXT :: #type proc "system" (device: Device, objectType: ObjectType, objectHandle: u64, privateDataSlot: PrivateDataSlotEXT, data: u64) -> Result -ProcGetPrivateDataEXT :: #type proc "system" (device: Device, objectType: ObjectType, objectHandle: u64, privateDataSlot: PrivateDataSlotEXT, pData: ^u64) -ProcCmdSetFragmentShadingRateEnumNV :: #type proc "system" (commandBuffer: CommandBuffer, shadingRate: FragmentShadingRateNV) ProcAcquireWinrtDisplayNV :: #type proc "system" (physicalDevice: PhysicalDevice, display: DisplayKHR) -> Result -ProcGetWinrtDisplayNV :: #type proc "system" (physicalDevice: PhysicalDevice, deviceRelativeId: u32, pDisplay: ^DisplayKHR) -> Result -ProcCmdSetVertexInputEXT :: #type proc "system" (commandBuffer: CommandBuffer, vertexBindingDescriptionCount: u32, pVertexBindingDescriptions: [^]VertexInputBindingDescription2EXT, vertexAttributeDescriptionCount: u32, pVertexAttributeDescriptions: [^]VertexInputAttributeDescription2EXT) -ProcGetDeviceSubpassShadingMaxWorkgroupSizeHUAWEI :: #type proc "system" (device: Device, renderpass: RenderPass, pMaxWorkgroupSize: ^Extent2D) -> Result -ProcCmdSubpassShadingHUAWEI :: #type proc "system" (commandBuffer: CommandBuffer) -ProcCmdBindInvocationMaskHUAWEI :: #type proc "system" (commandBuffer: CommandBuffer, imageView: ImageView, imageLayout: ImageLayout) -ProcGetMemoryRemoteAddressNV :: #type proc "system" (device: Device, pMemoryGetRemoteAddressInfo: ^MemoryGetRemoteAddressInfoNV, pAddress: [^]RemoteAddressNV) -> Result -ProcCmdSetPatchControlPointsEXT :: #type proc "system" (commandBuffer: CommandBuffer, patchControlPoints: u32) -ProcCmdSetRasterizerDiscardEnableEXT :: #type proc "system" (commandBuffer: CommandBuffer, rasterizerDiscardEnable: b32) -ProcCmdSetDepthBiasEnableEXT :: #type proc "system" (commandBuffer: CommandBuffer, depthBiasEnable: b32) -ProcCmdSetLogicOpEXT :: #type proc "system" (commandBuffer: CommandBuffer, logicOp: LogicOp) -ProcCmdSetPrimitiveRestartEnableEXT :: #type proc "system" (commandBuffer: CommandBuffer, primitiveRestartEnable: b32) -ProcCmdDrawMultiEXT :: #type proc "system" (commandBuffer: CommandBuffer, drawCount: u32, pVertexInfo: ^MultiDrawInfoEXT, instanceCount: u32, firstInstance: u32, stride: u32) -ProcCmdDrawMultiIndexedEXT :: #type proc "system" (commandBuffer: CommandBuffer, drawCount: u32, pIndexInfo: ^MultiDrawIndexedInfoEXT, instanceCount: u32, firstInstance: u32, stride: u32, pVertexOffset: ^i32) -ProcSetDeviceMemoryPriorityEXT :: #type proc "system" (device: Device, memory: DeviceMemory, priority: f32) -ProcCreateAccelerationStructureKHR :: #type proc "system" (device: Device, pCreateInfo: ^AccelerationStructureCreateInfoKHR, pAllocator: ^AllocationCallbacks, pAccelerationStructure: ^AccelerationStructureKHR) -> Result -ProcDestroyAccelerationStructureKHR :: #type proc "system" (device: Device, accelerationStructure: AccelerationStructureKHR, pAllocator: ^AllocationCallbacks) -ProcCmdBuildAccelerationStructuresKHR :: #type proc "system" (commandBuffer: CommandBuffer, infoCount: u32, pInfos: [^]AccelerationStructureBuildGeometryInfoKHR, ppBuildRangeInfos: ^[^]AccelerationStructureBuildRangeInfoKHR) -ProcCmdBuildAccelerationStructuresIndirectKHR :: #type proc "system" (commandBuffer: CommandBuffer, infoCount: u32, pInfos: [^]AccelerationStructureBuildGeometryInfoKHR, pIndirectDeviceAddresses: [^]DeviceAddress, pIndirectStrides: [^]u32, ppMaxPrimitiveCounts: ^[^]u32) -ProcBuildAccelerationStructuresKHR :: #type proc "system" (device: Device, deferredOperation: DeferredOperationKHR, infoCount: u32, pInfos: [^]AccelerationStructureBuildGeometryInfoKHR, ppBuildRangeInfos: ^[^]AccelerationStructureBuildRangeInfoKHR) -> Result -ProcCopyAccelerationStructureKHR :: #type proc "system" (device: Device, deferredOperation: DeferredOperationKHR, pInfo: ^CopyAccelerationStructureInfoKHR) -> Result -ProcCopyAccelerationStructureToMemoryKHR :: #type proc "system" (device: Device, deferredOperation: DeferredOperationKHR, pInfo: ^CopyAccelerationStructureToMemoryInfoKHR) -> Result -ProcCopyMemoryToAccelerationStructureKHR :: #type proc "system" (device: Device, deferredOperation: DeferredOperationKHR, pInfo: ^CopyMemoryToAccelerationStructureInfoKHR) -> Result -ProcWriteAccelerationStructuresPropertiesKHR :: #type proc "system" (device: Device, accelerationStructureCount: u32, pAccelerationStructures: [^]AccelerationStructureKHR, queryType: QueryType, dataSize: int, pData: rawptr, stride: int) -> Result -ProcCmdCopyAccelerationStructureKHR :: #type proc "system" (commandBuffer: CommandBuffer, pInfo: ^CopyAccelerationStructureInfoKHR) -ProcCmdCopyAccelerationStructureToMemoryKHR :: #type proc "system" (commandBuffer: CommandBuffer, pInfo: ^CopyAccelerationStructureToMemoryInfoKHR) -ProcCmdCopyMemoryToAccelerationStructureKHR :: #type proc "system" (commandBuffer: CommandBuffer, pInfo: ^CopyMemoryToAccelerationStructureInfoKHR) -ProcGetAccelerationStructureDeviceAddressKHR :: #type proc "system" (device: Device, pInfo: ^AccelerationStructureDeviceAddressInfoKHR) -> DeviceAddress -ProcCmdWriteAccelerationStructuresPropertiesKHR :: #type proc "system" (commandBuffer: CommandBuffer, accelerationStructureCount: u32, pAccelerationStructures: [^]AccelerationStructureKHR, queryType: QueryType, queryPool: QueryPool, firstQuery: u32) -ProcGetDeviceAccelerationStructureCompatibilityKHR :: #type proc "system" (device: Device, pVersionInfo: ^AccelerationStructureVersionInfoKHR, pCompatibility: ^AccelerationStructureCompatibilityKHR) -ProcGetAccelerationStructureBuildSizesKHR :: #type proc "system" (device: Device, buildType: AccelerationStructureBuildTypeKHR, pBuildInfo: ^AccelerationStructureBuildGeometryInfoKHR, pMaxPrimitiveCounts: [^]u32, pSizeInfo: ^AccelerationStructureBuildSizesInfoKHR) -ProcCmdTraceRaysKHR :: #type proc "system" (commandBuffer: CommandBuffer, pRaygenShaderBindingTable: [^]StridedDeviceAddressRegionKHR, pMissShaderBindingTable: [^]StridedDeviceAddressRegionKHR, pHitShaderBindingTable: [^]StridedDeviceAddressRegionKHR, pCallableShaderBindingTable: [^]StridedDeviceAddressRegionKHR, width: u32, height: u32, depth: u32) -ProcCreateRayTracingPipelinesKHR :: #type proc "system" (device: Device, deferredOperation: DeferredOperationKHR, pipelineCache: PipelineCache, createInfoCount: u32, pCreateInfos: [^]RayTracingPipelineCreateInfoKHR, pAllocator: ^AllocationCallbacks, pPipelines: [^]Pipeline) -> Result -ProcGetRayTracingCaptureReplayShaderGroupHandlesKHR :: #type proc "system" (device: Device, pipeline: Pipeline, firstGroup: u32, groupCount: u32, dataSize: int, pData: rawptr) -> Result -ProcCmdTraceRaysIndirectKHR :: #type proc "system" (commandBuffer: CommandBuffer, pRaygenShaderBindingTable: [^]StridedDeviceAddressRegionKHR, pMissShaderBindingTable: [^]StridedDeviceAddressRegionKHR, pHitShaderBindingTable: [^]StridedDeviceAddressRegionKHR, pCallableShaderBindingTable: [^]StridedDeviceAddressRegionKHR, indirectDeviceAddress: DeviceAddress) -ProcGetRayTracingShaderGroupStackSizeKHR :: #type proc "system" (device: Device, pipeline: Pipeline, group: u32, groupShader: ShaderGroupShaderKHR) -> DeviceSize -ProcCmdSetRayTracingPipelineStackSizeKHR :: #type proc "system" (commandBuffer: CommandBuffer, pipelineStackSize: u32) -ProcCreateWin32SurfaceKHR :: #type proc "system" (instance: Instance, pCreateInfo: ^Win32SurfaceCreateInfoKHR, pAllocator: ^AllocationCallbacks, pSurface: ^SurfaceKHR) -> Result -ProcGetPhysicalDeviceWin32PresentationSupportKHR :: #type proc "system" (physicalDevice: PhysicalDevice, queueFamilyIndex: u32) -> b32 -ProcGetMemoryWin32HandleKHR :: #type proc "system" (device: Device, pGetWin32HandleInfo: ^MemoryGetWin32HandleInfoKHR, pHandle: ^HANDLE) -> Result -ProcGetMemoryWin32HandlePropertiesKHR :: #type proc "system" (device: Device, handleType: ExternalMemoryHandleTypeFlags, handle: HANDLE, pMemoryWin32HandleProperties: [^]MemoryWin32HandlePropertiesKHR) -> Result -ProcImportSemaphoreWin32HandleKHR :: #type proc "system" (device: Device, pImportSemaphoreWin32HandleInfo: ^ImportSemaphoreWin32HandleInfoKHR) -> Result -ProcGetSemaphoreWin32HandleKHR :: #type proc "system" (device: Device, pGetWin32HandleInfo: ^SemaphoreGetWin32HandleInfoKHR, pHandle: ^HANDLE) -> Result -ProcImportFenceWin32HandleKHR :: #type proc "system" (device: Device, pImportFenceWin32HandleInfo: ^ImportFenceWin32HandleInfoKHR) -> Result -ProcGetFenceWin32HandleKHR :: #type proc "system" (device: Device, pGetWin32HandleInfo: ^FenceGetWin32HandleInfoKHR, pHandle: ^HANDLE) -> Result -ProcGetMemoryWin32HandleNV :: #type proc "system" (device: Device, memory: DeviceMemory, handleType: ExternalMemoryHandleTypeFlagsNV, pHandle: ^HANDLE) -> Result -ProcGetPhysicalDeviceSurfacePresentModes2EXT :: #type proc "system" (physicalDevice: PhysicalDevice, pSurfaceInfo: ^PhysicalDeviceSurfaceInfo2KHR, pPresentModeCount: ^u32, pPresentModes: [^]PresentModeKHR) -> Result -ProcAcquireFullScreenExclusiveModeEXT :: #type proc "system" (device: Device, swapchain: SwapchainKHR) -> Result -ProcReleaseFullScreenExclusiveModeEXT :: #type proc "system" (device: Device, swapchain: SwapchainKHR) -> Result -ProcGetDeviceGroupSurfacePresentModes2EXT :: #type proc "system" (device: Device, pSurfaceInfo: ^PhysicalDeviceSurfaceInfo2KHR, pModes: [^]DeviceGroupPresentModeFlagsKHR) -> Result -ProcCreateMetalSurfaceEXT :: #type proc "system" (instance: Instance, pCreateInfo: ^MetalSurfaceCreateInfoEXT, pAllocator: ^AllocationCallbacks, pSurface: ^SurfaceKHR) -> Result -ProcCreateMacOSSurfaceMVK :: #type proc "system" (instance: Instance, pCreateInfo: ^MacOSSurfaceCreateInfoMVK, pAllocator: ^AllocationCallbacks, pSurface: ^SurfaceKHR) -> Result +ProcCreateDebugReportCallbackEXT :: #type proc "system" (instance: Instance, pCreateInfo: ^DebugReportCallbackCreateInfoEXT, pAllocator: ^AllocationCallbacks, pCallback: ^DebugReportCallbackEXT) -> Result +ProcCreateDebugUtilsMessengerEXT :: #type proc "system" (instance: Instance, pCreateInfo: ^DebugUtilsMessengerCreateInfoEXT, pAllocator: ^AllocationCallbacks, pMessenger: ^DebugUtilsMessengerEXT) -> Result +ProcCreateDevice :: #type proc "system" (physicalDevice: PhysicalDevice, pCreateInfo: ^DeviceCreateInfo, pAllocator: ^AllocationCallbacks, pDevice: ^Device) -> Result +ProcCreateDisplayModeKHR :: #type proc "system" (physicalDevice: PhysicalDevice, display: DisplayKHR, pCreateInfo: ^DisplayModeCreateInfoKHR, pAllocator: ^AllocationCallbacks, pMode: ^DisplayModeKHR) -> Result +ProcCreateDisplayPlaneSurfaceKHR :: #type proc "system" (instance: Instance, pCreateInfo: ^DisplaySurfaceCreateInfoKHR, pAllocator: ^AllocationCallbacks, pSurface: ^SurfaceKHR) -> Result +ProcCreateHeadlessSurfaceEXT :: #type proc "system" (instance: Instance, pCreateInfo: ^HeadlessSurfaceCreateInfoEXT, pAllocator: ^AllocationCallbacks, pSurface: ^SurfaceKHR) -> Result ProcCreateIOSSurfaceMVK :: #type proc "system" (instance: Instance, pCreateInfo: ^IOSSurfaceCreateInfoMVK, pAllocator: ^AllocationCallbacks, pSurface: ^SurfaceKHR) -> Result +ProcCreateMacOSSurfaceMVK :: #type proc "system" (instance: Instance, pCreateInfo: ^MacOSSurfaceCreateInfoMVK, pAllocator: ^AllocationCallbacks, pSurface: ^SurfaceKHR) -> Result +ProcCreateMetalSurfaceEXT :: #type proc "system" (instance: Instance, pCreateInfo: ^MetalSurfaceCreateInfoEXT, pAllocator: ^AllocationCallbacks, pSurface: ^SurfaceKHR) -> Result +ProcCreateWin32SurfaceKHR :: #type proc "system" (instance: Instance, pCreateInfo: ^Win32SurfaceCreateInfoKHR, pAllocator: ^AllocationCallbacks, pSurface: ^SurfaceKHR) -> Result +ProcDebugReportMessageEXT :: #type proc "system" (instance: Instance, flags: DebugReportFlagsEXT, objectType: DebugReportObjectTypeEXT, object: u64, location: int, messageCode: i32, pLayerPrefix: cstring, pMessage: cstring) +ProcDestroyDebugReportCallbackEXT :: #type proc "system" (instance: Instance, callback: DebugReportCallbackEXT, pAllocator: ^AllocationCallbacks) +ProcDestroyDebugUtilsMessengerEXT :: #type proc "system" (instance: Instance, messenger: DebugUtilsMessengerEXT, pAllocator: ^AllocationCallbacks) +ProcDestroyInstance :: #type proc "system" (instance: Instance, pAllocator: ^AllocationCallbacks) +ProcDestroySurfaceKHR :: #type proc "system" (instance: Instance, surface: SurfaceKHR, pAllocator: ^AllocationCallbacks) +ProcEnumerateDeviceExtensionProperties :: #type proc "system" (physicalDevice: PhysicalDevice, pLayerName: cstring, pPropertyCount: ^u32, pProperties: [^]ExtensionProperties) -> Result +ProcEnumerateDeviceLayerProperties :: #type proc "system" (physicalDevice: PhysicalDevice, pPropertyCount: ^u32, pProperties: [^]LayerProperties) -> Result +ProcEnumeratePhysicalDeviceGroups :: #type proc "system" (instance: Instance, pPhysicalDeviceGroupCount: ^u32, pPhysicalDeviceGroupProperties: [^]PhysicalDeviceGroupProperties) -> Result +ProcEnumeratePhysicalDeviceGroupsKHR :: #type proc "system" (instance: Instance, pPhysicalDeviceGroupCount: ^u32, pPhysicalDeviceGroupProperties: [^]PhysicalDeviceGroupProperties) -> Result +ProcEnumeratePhysicalDeviceQueueFamilyPerformanceQueryCountersKHR :: #type proc "system" (physicalDevice: PhysicalDevice, queueFamilyIndex: u32, pCounterCount: ^u32, pCounters: [^]PerformanceCounterKHR, pCounterDescriptions: [^]PerformanceCounterDescriptionKHR) -> Result +ProcEnumeratePhysicalDevices :: #type proc "system" (instance: Instance, pPhysicalDeviceCount: ^u32, pPhysicalDevices: [^]PhysicalDevice) -> Result +ProcGetDisplayModeProperties2KHR :: #type proc "system" (physicalDevice: PhysicalDevice, display: DisplayKHR, pPropertyCount: ^u32, pProperties: [^]DisplayModeProperties2KHR) -> Result +ProcGetDisplayModePropertiesKHR :: #type proc "system" (physicalDevice: PhysicalDevice, display: DisplayKHR, pPropertyCount: ^u32, pProperties: [^]DisplayModePropertiesKHR) -> Result +ProcGetDisplayPlaneCapabilities2KHR :: #type proc "system" (physicalDevice: PhysicalDevice, pDisplayPlaneInfo: ^DisplayPlaneInfo2KHR, pCapabilities: [^]DisplayPlaneCapabilities2KHR) -> Result +ProcGetDisplayPlaneCapabilitiesKHR :: #type proc "system" (physicalDevice: PhysicalDevice, mode: DisplayModeKHR, planeIndex: u32, pCapabilities: [^]DisplayPlaneCapabilitiesKHR) -> Result +ProcGetDisplayPlaneSupportedDisplaysKHR :: #type proc "system" (physicalDevice: PhysicalDevice, planeIndex: u32, pDisplayCount: ^u32, pDisplays: [^]DisplayKHR) -> Result +ProcGetDrmDisplayEXT :: #type proc "system" (physicalDevice: PhysicalDevice, drmFd: i32, connectorId: u32, display: ^DisplayKHR) -> Result +ProcGetInstanceProcAddr :: #type proc "system" (instance: Instance, pName: cstring) -> ProcVoidFunction +ProcGetPhysicalDeviceCalibrateableTimeDomainsEXT :: #type proc "system" (physicalDevice: PhysicalDevice, pTimeDomainCount: ^u32, pTimeDomains: [^]TimeDomainEXT) -> Result +ProcGetPhysicalDeviceCooperativeMatrixPropertiesNV :: #type proc "system" (physicalDevice: PhysicalDevice, pPropertyCount: ^u32, pProperties: [^]CooperativeMatrixPropertiesNV) -> Result +ProcGetPhysicalDeviceDisplayPlaneProperties2KHR :: #type proc "system" (physicalDevice: PhysicalDevice, pPropertyCount: ^u32, pProperties: [^]DisplayPlaneProperties2KHR) -> Result +ProcGetPhysicalDeviceDisplayPlanePropertiesKHR :: #type proc "system" (physicalDevice: PhysicalDevice, pPropertyCount: ^u32, pProperties: [^]DisplayPlanePropertiesKHR) -> Result +ProcGetPhysicalDeviceDisplayProperties2KHR :: #type proc "system" (physicalDevice: PhysicalDevice, pPropertyCount: ^u32, pProperties: [^]DisplayProperties2KHR) -> Result +ProcGetPhysicalDeviceDisplayPropertiesKHR :: #type proc "system" (physicalDevice: PhysicalDevice, pPropertyCount: ^u32, pProperties: [^]DisplayPropertiesKHR) -> Result +ProcGetPhysicalDeviceExternalBufferProperties :: #type proc "system" (physicalDevice: PhysicalDevice, pExternalBufferInfo: ^PhysicalDeviceExternalBufferInfo, pExternalBufferProperties: [^]ExternalBufferProperties) +ProcGetPhysicalDeviceExternalBufferPropertiesKHR :: #type proc "system" (physicalDevice: PhysicalDevice, pExternalBufferInfo: ^PhysicalDeviceExternalBufferInfo, pExternalBufferProperties: [^]ExternalBufferProperties) +ProcGetPhysicalDeviceExternalFenceProperties :: #type proc "system" (physicalDevice: PhysicalDevice, pExternalFenceInfo: ^PhysicalDeviceExternalFenceInfo, pExternalFenceProperties: [^]ExternalFenceProperties) +ProcGetPhysicalDeviceExternalFencePropertiesKHR :: #type proc "system" (physicalDevice: PhysicalDevice, pExternalFenceInfo: ^PhysicalDeviceExternalFenceInfo, pExternalFenceProperties: [^]ExternalFenceProperties) +ProcGetPhysicalDeviceExternalImageFormatPropertiesNV :: #type proc "system" (physicalDevice: PhysicalDevice, format: Format, type: ImageType, tiling: ImageTiling, usage: ImageUsageFlags, flags: ImageCreateFlags, externalHandleType: ExternalMemoryHandleTypeFlagsNV, pExternalImageFormatProperties: [^]ExternalImageFormatPropertiesNV) -> Result +ProcGetPhysicalDeviceExternalSemaphoreProperties :: #type proc "system" (physicalDevice: PhysicalDevice, pExternalSemaphoreInfo: ^PhysicalDeviceExternalSemaphoreInfo, pExternalSemaphoreProperties: [^]ExternalSemaphoreProperties) +ProcGetPhysicalDeviceExternalSemaphorePropertiesKHR :: #type proc "system" (physicalDevice: PhysicalDevice, pExternalSemaphoreInfo: ^PhysicalDeviceExternalSemaphoreInfo, pExternalSemaphoreProperties: [^]ExternalSemaphoreProperties) +ProcGetPhysicalDeviceFeatures :: #type proc "system" (physicalDevice: PhysicalDevice, pFeatures: [^]PhysicalDeviceFeatures) +ProcGetPhysicalDeviceFeatures2 :: #type proc "system" (physicalDevice: PhysicalDevice, pFeatures: [^]PhysicalDeviceFeatures2) +ProcGetPhysicalDeviceFeatures2KHR :: #type proc "system" (physicalDevice: PhysicalDevice, pFeatures: [^]PhysicalDeviceFeatures2) +ProcGetPhysicalDeviceFormatProperties :: #type proc "system" (physicalDevice: PhysicalDevice, format: Format, pFormatProperties: [^]FormatProperties) +ProcGetPhysicalDeviceFormatProperties2 :: #type proc "system" (physicalDevice: PhysicalDevice, format: Format, pFormatProperties: [^]FormatProperties2) +ProcGetPhysicalDeviceFormatProperties2KHR :: #type proc "system" (physicalDevice: PhysicalDevice, format: Format, pFormatProperties: [^]FormatProperties2) +ProcGetPhysicalDeviceFragmentShadingRatesKHR :: #type proc "system" (physicalDevice: PhysicalDevice, pFragmentShadingRateCount: ^u32, pFragmentShadingRates: [^]PhysicalDeviceFragmentShadingRateKHR) -> Result +ProcGetPhysicalDeviceImageFormatProperties :: #type proc "system" (physicalDevice: PhysicalDevice, format: Format, type: ImageType, tiling: ImageTiling, usage: ImageUsageFlags, flags: ImageCreateFlags, pImageFormatProperties: [^]ImageFormatProperties) -> Result +ProcGetPhysicalDeviceImageFormatProperties2 :: #type proc "system" (physicalDevice: PhysicalDevice, pImageFormatInfo: ^PhysicalDeviceImageFormatInfo2, pImageFormatProperties: [^]ImageFormatProperties2) -> Result +ProcGetPhysicalDeviceImageFormatProperties2KHR :: #type proc "system" (physicalDevice: PhysicalDevice, pImageFormatInfo: ^PhysicalDeviceImageFormatInfo2, pImageFormatProperties: [^]ImageFormatProperties2) -> Result +ProcGetPhysicalDeviceMemoryProperties :: #type proc "system" (physicalDevice: PhysicalDevice, pMemoryProperties: [^]PhysicalDeviceMemoryProperties) +ProcGetPhysicalDeviceMemoryProperties2 :: #type proc "system" (physicalDevice: PhysicalDevice, pMemoryProperties: [^]PhysicalDeviceMemoryProperties2) +ProcGetPhysicalDeviceMemoryProperties2KHR :: #type proc "system" (physicalDevice: PhysicalDevice, pMemoryProperties: [^]PhysicalDeviceMemoryProperties2) +ProcGetPhysicalDeviceMultisamplePropertiesEXT :: #type proc "system" (physicalDevice: PhysicalDevice, samples: SampleCountFlags, pMultisampleProperties: [^]MultisamplePropertiesEXT) +ProcGetPhysicalDevicePresentRectanglesKHR :: #type proc "system" (physicalDevice: PhysicalDevice, surface: SurfaceKHR, pRectCount: ^u32, pRects: [^]Rect2D) -> Result +ProcGetPhysicalDeviceProperties :: #type proc "system" (physicalDevice: PhysicalDevice, pProperties: [^]PhysicalDeviceProperties) +ProcGetPhysicalDeviceProperties2 :: #type proc "system" (physicalDevice: PhysicalDevice, pProperties: [^]PhysicalDeviceProperties2) +ProcGetPhysicalDeviceProperties2KHR :: #type proc "system" (physicalDevice: PhysicalDevice, pProperties: [^]PhysicalDeviceProperties2) +ProcGetPhysicalDeviceQueueFamilyPerformanceQueryPassesKHR :: #type proc "system" (physicalDevice: PhysicalDevice, pPerformanceQueryCreateInfo: ^QueryPoolPerformanceCreateInfoKHR, pNumPasses: [^]u32) +ProcGetPhysicalDeviceQueueFamilyProperties :: #type proc "system" (physicalDevice: PhysicalDevice, pQueueFamilyPropertyCount: ^u32, pQueueFamilyProperties: [^]QueueFamilyProperties) +ProcGetPhysicalDeviceQueueFamilyProperties2 :: #type proc "system" (physicalDevice: PhysicalDevice, pQueueFamilyPropertyCount: ^u32, pQueueFamilyProperties: [^]QueueFamilyProperties2) +ProcGetPhysicalDeviceQueueFamilyProperties2KHR :: #type proc "system" (physicalDevice: PhysicalDevice, pQueueFamilyPropertyCount: ^u32, pQueueFamilyProperties: [^]QueueFamilyProperties2) +ProcGetPhysicalDeviceSparseImageFormatProperties :: #type proc "system" (physicalDevice: PhysicalDevice, format: Format, type: ImageType, samples: SampleCountFlags, usage: ImageUsageFlags, tiling: ImageTiling, pPropertyCount: ^u32, pProperties: [^]SparseImageFormatProperties) +ProcGetPhysicalDeviceSparseImageFormatProperties2 :: #type proc "system" (physicalDevice: PhysicalDevice, pFormatInfo: ^PhysicalDeviceSparseImageFormatInfo2, pPropertyCount: ^u32, pProperties: [^]SparseImageFormatProperties2) +ProcGetPhysicalDeviceSparseImageFormatProperties2KHR :: #type proc "system" (physicalDevice: PhysicalDevice, pFormatInfo: ^PhysicalDeviceSparseImageFormatInfo2, pPropertyCount: ^u32, pProperties: [^]SparseImageFormatProperties2) +ProcGetPhysicalDeviceSupportedFramebufferMixedSamplesCombinationsNV :: #type proc "system" (physicalDevice: PhysicalDevice, pCombinationCount: ^u32, pCombinations: [^]FramebufferMixedSamplesCombinationNV) -> Result +ProcGetPhysicalDeviceSurfaceCapabilities2EXT :: #type proc "system" (physicalDevice: PhysicalDevice, surface: SurfaceKHR, pSurfaceCapabilities: [^]SurfaceCapabilities2EXT) -> Result +ProcGetPhysicalDeviceSurfaceCapabilities2KHR :: #type proc "system" (physicalDevice: PhysicalDevice, pSurfaceInfo: ^PhysicalDeviceSurfaceInfo2KHR, pSurfaceCapabilities: [^]SurfaceCapabilities2KHR) -> Result +ProcGetPhysicalDeviceSurfaceCapabilitiesKHR :: #type proc "system" (physicalDevice: PhysicalDevice, surface: SurfaceKHR, pSurfaceCapabilities: [^]SurfaceCapabilitiesKHR) -> Result +ProcGetPhysicalDeviceSurfaceFormats2KHR :: #type proc "system" (physicalDevice: PhysicalDevice, pSurfaceInfo: ^PhysicalDeviceSurfaceInfo2KHR, pSurfaceFormatCount: ^u32, pSurfaceFormats: [^]SurfaceFormat2KHR) -> Result +ProcGetPhysicalDeviceSurfaceFormatsKHR :: #type proc "system" (physicalDevice: PhysicalDevice, surface: SurfaceKHR, pSurfaceFormatCount: ^u32, pSurfaceFormats: [^]SurfaceFormatKHR) -> Result +ProcGetPhysicalDeviceSurfacePresentModes2EXT :: #type proc "system" (physicalDevice: PhysicalDevice, pSurfaceInfo: ^PhysicalDeviceSurfaceInfo2KHR, pPresentModeCount: ^u32, pPresentModes: [^]PresentModeKHR) -> Result +ProcGetPhysicalDeviceSurfacePresentModesKHR :: #type proc "system" (physicalDevice: PhysicalDevice, surface: SurfaceKHR, pPresentModeCount: ^u32, pPresentModes: [^]PresentModeKHR) -> Result +ProcGetPhysicalDeviceSurfaceSupportKHR :: #type proc "system" (physicalDevice: PhysicalDevice, queueFamilyIndex: u32, surface: SurfaceKHR, pSupported: ^b32) -> Result +ProcGetPhysicalDeviceToolProperties :: #type proc "system" (physicalDevice: PhysicalDevice, pToolCount: ^u32, pToolProperties: [^]PhysicalDeviceToolProperties) -> Result +ProcGetPhysicalDeviceToolPropertiesEXT :: #type proc "system" (physicalDevice: PhysicalDevice, pToolCount: ^u32, pToolProperties: [^]PhysicalDeviceToolProperties) -> Result +ProcGetPhysicalDeviceWin32PresentationSupportKHR :: #type proc "system" (physicalDevice: PhysicalDevice, queueFamilyIndex: u32) -> b32 +ProcGetWinrtDisplayNV :: #type proc "system" (physicalDevice: PhysicalDevice, deviceRelativeId: u32, pDisplay: ^DisplayKHR) -> Result +ProcReleaseDisplayEXT :: #type proc "system" (physicalDevice: PhysicalDevice, display: DisplayKHR) -> Result +ProcSubmitDebugUtilsMessageEXT :: #type proc "system" (instance: Instance, messageSeverity: DebugUtilsMessageSeverityFlagsEXT, messageTypes: DebugUtilsMessageTypeFlagsEXT, pCallbackData: ^DebugUtilsMessengerCallbackDataEXT) -// Instance Procedures -DestroyInstance: ProcDestroyInstance -EnumeratePhysicalDevices: ProcEnumeratePhysicalDevices -GetPhysicalDeviceFeatures: ProcGetPhysicalDeviceFeatures -GetPhysicalDeviceFormatProperties: ProcGetPhysicalDeviceFormatProperties -GetPhysicalDeviceImageFormatProperties: ProcGetPhysicalDeviceImageFormatProperties -GetPhysicalDeviceProperties: ProcGetPhysicalDeviceProperties -GetPhysicalDeviceQueueFamilyProperties: ProcGetPhysicalDeviceQueueFamilyProperties -GetPhysicalDeviceMemoryProperties: ProcGetPhysicalDeviceMemoryProperties -GetInstanceProcAddr: ProcGetInstanceProcAddr -CreateDevice: ProcCreateDevice -EnumerateDeviceExtensionProperties: ProcEnumerateDeviceExtensionProperties -EnumerateDeviceLayerProperties: ProcEnumerateDeviceLayerProperties -GetPhysicalDeviceSparseImageFormatProperties: ProcGetPhysicalDeviceSparseImageFormatProperties -EnumeratePhysicalDeviceGroups: ProcEnumeratePhysicalDeviceGroups -GetPhysicalDeviceFeatures2: ProcGetPhysicalDeviceFeatures2 -GetPhysicalDeviceProperties2: ProcGetPhysicalDeviceProperties2 -GetPhysicalDeviceFormatProperties2: ProcGetPhysicalDeviceFormatProperties2 -GetPhysicalDeviceImageFormatProperties2: ProcGetPhysicalDeviceImageFormatProperties2 -GetPhysicalDeviceQueueFamilyProperties2: ProcGetPhysicalDeviceQueueFamilyProperties2 -GetPhysicalDeviceMemoryProperties2: ProcGetPhysicalDeviceMemoryProperties2 -GetPhysicalDeviceSparseImageFormatProperties2: ProcGetPhysicalDeviceSparseImageFormatProperties2 -GetPhysicalDeviceExternalBufferProperties: ProcGetPhysicalDeviceExternalBufferProperties -GetPhysicalDeviceExternalFenceProperties: ProcGetPhysicalDeviceExternalFenceProperties -GetPhysicalDeviceExternalSemaphoreProperties: ProcGetPhysicalDeviceExternalSemaphoreProperties -DestroySurfaceKHR: ProcDestroySurfaceKHR -GetPhysicalDeviceSurfaceSupportKHR: ProcGetPhysicalDeviceSurfaceSupportKHR -GetPhysicalDeviceSurfaceCapabilitiesKHR: ProcGetPhysicalDeviceSurfaceCapabilitiesKHR -GetPhysicalDeviceSurfaceFormatsKHR: ProcGetPhysicalDeviceSurfaceFormatsKHR -GetPhysicalDeviceSurfacePresentModesKHR: ProcGetPhysicalDeviceSurfacePresentModesKHR -GetPhysicalDevicePresentRectanglesKHR: ProcGetPhysicalDevicePresentRectanglesKHR -GetPhysicalDeviceDisplayPropertiesKHR: ProcGetPhysicalDeviceDisplayPropertiesKHR -GetPhysicalDeviceDisplayPlanePropertiesKHR: ProcGetPhysicalDeviceDisplayPlanePropertiesKHR -GetDisplayPlaneSupportedDisplaysKHR: ProcGetDisplayPlaneSupportedDisplaysKHR -GetDisplayModePropertiesKHR: ProcGetDisplayModePropertiesKHR -CreateDisplayModeKHR: ProcCreateDisplayModeKHR -GetDisplayPlaneCapabilitiesKHR: ProcGetDisplayPlaneCapabilitiesKHR -CreateDisplayPlaneSurfaceKHR: ProcCreateDisplayPlaneSurfaceKHR -GetPhysicalDeviceFeatures2KHR: ProcGetPhysicalDeviceFeatures2KHR -GetPhysicalDeviceProperties2KHR: ProcGetPhysicalDeviceProperties2KHR -GetPhysicalDeviceFormatProperties2KHR: ProcGetPhysicalDeviceFormatProperties2KHR -GetPhysicalDeviceImageFormatProperties2KHR: ProcGetPhysicalDeviceImageFormatProperties2KHR -GetPhysicalDeviceQueueFamilyProperties2KHR: ProcGetPhysicalDeviceQueueFamilyProperties2KHR -GetPhysicalDeviceMemoryProperties2KHR: ProcGetPhysicalDeviceMemoryProperties2KHR -GetPhysicalDeviceSparseImageFormatProperties2KHR: ProcGetPhysicalDeviceSparseImageFormatProperties2KHR -EnumeratePhysicalDeviceGroupsKHR: ProcEnumeratePhysicalDeviceGroupsKHR -GetPhysicalDeviceExternalBufferPropertiesKHR: ProcGetPhysicalDeviceExternalBufferPropertiesKHR -GetPhysicalDeviceExternalSemaphorePropertiesKHR: ProcGetPhysicalDeviceExternalSemaphorePropertiesKHR -GetPhysicalDeviceExternalFencePropertiesKHR: ProcGetPhysicalDeviceExternalFencePropertiesKHR -EnumeratePhysicalDeviceQueueFamilyPerformanceQueryCountersKHR: ProcEnumeratePhysicalDeviceQueueFamilyPerformanceQueryCountersKHR -GetPhysicalDeviceQueueFamilyPerformanceQueryPassesKHR: ProcGetPhysicalDeviceQueueFamilyPerformanceQueryPassesKHR -GetPhysicalDeviceSurfaceCapabilities2KHR: ProcGetPhysicalDeviceSurfaceCapabilities2KHR -GetPhysicalDeviceSurfaceFormats2KHR: ProcGetPhysicalDeviceSurfaceFormats2KHR -GetPhysicalDeviceDisplayProperties2KHR: ProcGetPhysicalDeviceDisplayProperties2KHR -GetPhysicalDeviceDisplayPlaneProperties2KHR: ProcGetPhysicalDeviceDisplayPlaneProperties2KHR -GetDisplayModeProperties2KHR: ProcGetDisplayModeProperties2KHR -GetDisplayPlaneCapabilities2KHR: ProcGetDisplayPlaneCapabilities2KHR -GetPhysicalDeviceFragmentShadingRatesKHR: ProcGetPhysicalDeviceFragmentShadingRatesKHR -CreateDebugReportCallbackEXT: ProcCreateDebugReportCallbackEXT -DestroyDebugReportCallbackEXT: ProcDestroyDebugReportCallbackEXT -DebugReportMessageEXT: ProcDebugReportMessageEXT -GetPhysicalDeviceExternalImageFormatPropertiesNV: ProcGetPhysicalDeviceExternalImageFormatPropertiesNV -ReleaseDisplayEXT: ProcReleaseDisplayEXT -GetPhysicalDeviceSurfaceCapabilities2EXT: ProcGetPhysicalDeviceSurfaceCapabilities2EXT -CreateDebugUtilsMessengerEXT: ProcCreateDebugUtilsMessengerEXT -DestroyDebugUtilsMessengerEXT: ProcDestroyDebugUtilsMessengerEXT -SubmitDebugUtilsMessageEXT: ProcSubmitDebugUtilsMessageEXT -GetPhysicalDeviceMultisamplePropertiesEXT: ProcGetPhysicalDeviceMultisamplePropertiesEXT -GetPhysicalDeviceCalibrateableTimeDomainsEXT: ProcGetPhysicalDeviceCalibrateableTimeDomainsEXT -GetPhysicalDeviceToolPropertiesEXT: ProcGetPhysicalDeviceToolPropertiesEXT -GetPhysicalDeviceCooperativeMatrixPropertiesNV: ProcGetPhysicalDeviceCooperativeMatrixPropertiesNV -GetPhysicalDeviceSupportedFramebufferMixedSamplesCombinationsNV: ProcGetPhysicalDeviceSupportedFramebufferMixedSamplesCombinationsNV -CreateHeadlessSurfaceEXT: ProcCreateHeadlessSurfaceEXT -AcquireDrmDisplayEXT: ProcAcquireDrmDisplayEXT -GetDrmDisplayEXT: ProcGetDrmDisplayEXT -AcquireWinrtDisplayNV: ProcAcquireWinrtDisplayNV -GetWinrtDisplayNV: ProcGetWinrtDisplayNV -CreateWin32SurfaceKHR: ProcCreateWin32SurfaceKHR -GetPhysicalDeviceWin32PresentationSupportKHR: ProcGetPhysicalDeviceWin32PresentationSupportKHR -GetPhysicalDeviceSurfacePresentModes2EXT: ProcGetPhysicalDeviceSurfacePresentModes2EXT -CreateMetalSurfaceEXT: ProcCreateMetalSurfaceEXT -CreateMacOSSurfaceMVK: ProcCreateMacOSSurfaceMVK -CreateIOSSurfaceMVK: ProcCreateIOSSurfaceMVK +// Device Procedure Types +ProcAcquireFullScreenExclusiveModeEXT :: #type proc "system" (device: Device, swapchain: SwapchainKHR) -> Result +ProcAcquireNextImage2KHR :: #type proc "system" (device: Device, pAcquireInfo: ^AcquireNextImageInfoKHR, pImageIndex: ^u32) -> Result +ProcAcquireNextImageKHR :: #type proc "system" (device: Device, swapchain: SwapchainKHR, timeout: u64, semaphore: Semaphore, fence: Fence, pImageIndex: ^u32) -> Result +ProcAcquirePerformanceConfigurationINTEL :: #type proc "system" (device: Device, pAcquireInfo: ^PerformanceConfigurationAcquireInfoINTEL, pConfiguration: ^PerformanceConfigurationINTEL) -> Result +ProcAcquireProfilingLockKHR :: #type proc "system" (device: Device, pInfo: ^AcquireProfilingLockInfoKHR) -> Result +ProcAllocateCommandBuffers :: #type proc "system" (device: Device, pAllocateInfo: ^CommandBufferAllocateInfo, pCommandBuffers: [^]CommandBuffer) -> Result +ProcAllocateDescriptorSets :: #type proc "system" (device: Device, pAllocateInfo: ^DescriptorSetAllocateInfo, pDescriptorSets: [^]DescriptorSet) -> Result +ProcAllocateMemory :: #type proc "system" (device: Device, pAllocateInfo: ^MemoryAllocateInfo, pAllocator: ^AllocationCallbacks, pMemory: ^DeviceMemory) -> Result +ProcBeginCommandBuffer :: #type proc "system" (commandBuffer: CommandBuffer, pBeginInfo: ^CommandBufferBeginInfo) -> Result +ProcBindAccelerationStructureMemoryNV :: #type proc "system" (device: Device, bindInfoCount: u32, pBindInfos: [^]BindAccelerationStructureMemoryInfoNV) -> Result +ProcBindBufferMemory :: #type proc "system" (device: Device, buffer: Buffer, memory: DeviceMemory, memoryOffset: DeviceSize) -> Result +ProcBindBufferMemory2 :: #type proc "system" (device: Device, bindInfoCount: u32, pBindInfos: [^]BindBufferMemoryInfo) -> Result +ProcBindBufferMemory2KHR :: #type proc "system" (device: Device, bindInfoCount: u32, pBindInfos: [^]BindBufferMemoryInfo) -> Result +ProcBindImageMemory :: #type proc "system" (device: Device, image: Image, memory: DeviceMemory, memoryOffset: DeviceSize) -> Result +ProcBindImageMemory2 :: #type proc "system" (device: Device, bindInfoCount: u32, pBindInfos: [^]BindImageMemoryInfo) -> Result +ProcBindImageMemory2KHR :: #type proc "system" (device: Device, bindInfoCount: u32, pBindInfos: [^]BindImageMemoryInfo) -> Result +ProcBuildAccelerationStructuresKHR :: #type proc "system" (device: Device, deferredOperation: DeferredOperationKHR, infoCount: u32, pInfos: [^]AccelerationStructureBuildGeometryInfoKHR, ppBuildRangeInfos: ^[^]AccelerationStructureBuildRangeInfoKHR) -> Result +ProcCmdBeginConditionalRenderingEXT :: #type proc "system" (commandBuffer: CommandBuffer, pConditionalRenderingBegin: ^ConditionalRenderingBeginInfoEXT) +ProcCmdBeginDebugUtilsLabelEXT :: #type proc "system" (commandBuffer: CommandBuffer, pLabelInfo: ^DebugUtilsLabelEXT) +ProcCmdBeginQuery :: #type proc "system" (commandBuffer: CommandBuffer, queryPool: QueryPool, query: u32, flags: QueryControlFlags) +ProcCmdBeginQueryIndexedEXT :: #type proc "system" (commandBuffer: CommandBuffer, queryPool: QueryPool, query: u32, flags: QueryControlFlags, index: u32) +ProcCmdBeginRenderPass :: #type proc "system" (commandBuffer: CommandBuffer, pRenderPassBegin: ^RenderPassBeginInfo, contents: SubpassContents) +ProcCmdBeginRenderPass2 :: #type proc "system" (commandBuffer: CommandBuffer, pRenderPassBegin: ^RenderPassBeginInfo, pSubpassBeginInfo: ^SubpassBeginInfo) +ProcCmdBeginRenderPass2KHR :: #type proc "system" (commandBuffer: CommandBuffer, pRenderPassBegin: ^RenderPassBeginInfo, pSubpassBeginInfo: ^SubpassBeginInfo) +ProcCmdBeginRendering :: #type proc "system" (commandBuffer: CommandBuffer, pRenderingInfo: ^RenderingInfo) +ProcCmdBeginRenderingKHR :: #type proc "system" (commandBuffer: CommandBuffer, pRenderingInfo: ^RenderingInfo) +ProcCmdBeginTransformFeedbackEXT :: #type proc "system" (commandBuffer: CommandBuffer, firstCounterBuffer: u32, counterBufferCount: u32, pCounterBuffers: [^]Buffer, pCounterBufferOffsets: [^]DeviceSize) +ProcCmdBindDescriptorSets :: #type proc "system" (commandBuffer: CommandBuffer, pipelineBindPoint: PipelineBindPoint, layout: PipelineLayout, firstSet: u32, descriptorSetCount: u32, pDescriptorSets: [^]DescriptorSet, dynamicOffsetCount: u32, pDynamicOffsets: [^]u32) +ProcCmdBindIndexBuffer :: #type proc "system" (commandBuffer: CommandBuffer, buffer: Buffer, offset: DeviceSize, indexType: IndexType) +ProcCmdBindInvocationMaskHUAWEI :: #type proc "system" (commandBuffer: CommandBuffer, imageView: ImageView, imageLayout: ImageLayout) +ProcCmdBindPipeline :: #type proc "system" (commandBuffer: CommandBuffer, pipelineBindPoint: PipelineBindPoint, pipeline: Pipeline) +ProcCmdBindPipelineShaderGroupNV :: #type proc "system" (commandBuffer: CommandBuffer, pipelineBindPoint: PipelineBindPoint, pipeline: Pipeline, groupIndex: u32) +ProcCmdBindShadingRateImageNV :: #type proc "system" (commandBuffer: CommandBuffer, imageView: ImageView, imageLayout: ImageLayout) +ProcCmdBindTransformFeedbackBuffersEXT :: #type proc "system" (commandBuffer: CommandBuffer, firstBinding: u32, bindingCount: u32, pBuffers: [^]Buffer, pOffsets: [^]DeviceSize, pSizes: [^]DeviceSize) +ProcCmdBindVertexBuffers :: #type proc "system" (commandBuffer: CommandBuffer, firstBinding: u32, bindingCount: u32, pBuffers: [^]Buffer, pOffsets: [^]DeviceSize) +ProcCmdBindVertexBuffers2 :: #type proc "system" (commandBuffer: CommandBuffer, firstBinding: u32, bindingCount: u32, pBuffers: [^]Buffer, pOffsets: [^]DeviceSize, pSizes: [^]DeviceSize, pStrides: [^]DeviceSize) +ProcCmdBindVertexBuffers2EXT :: #type proc "system" (commandBuffer: CommandBuffer, firstBinding: u32, bindingCount: u32, pBuffers: [^]Buffer, pOffsets: [^]DeviceSize, pSizes: [^]DeviceSize, pStrides: [^]DeviceSize) +ProcCmdBlitImage :: #type proc "system" (commandBuffer: CommandBuffer, srcImage: Image, srcImageLayout: ImageLayout, dstImage: Image, dstImageLayout: ImageLayout, regionCount: u32, pRegions: [^]ImageBlit, filter: Filter) +ProcCmdBlitImage2 :: #type proc "system" (commandBuffer: CommandBuffer, pBlitImageInfo: ^BlitImageInfo2) +ProcCmdBlitImage2KHR :: #type proc "system" (commandBuffer: CommandBuffer, pBlitImageInfo: ^BlitImageInfo2) +ProcCmdBuildAccelerationStructureNV :: #type proc "system" (commandBuffer: CommandBuffer, pInfo: ^AccelerationStructureInfoNV, instanceData: Buffer, instanceOffset: DeviceSize, update: b32, dst: AccelerationStructureNV, src: AccelerationStructureNV, scratch: Buffer, scratchOffset: DeviceSize) +ProcCmdBuildAccelerationStructuresIndirectKHR :: #type proc "system" (commandBuffer: CommandBuffer, infoCount: u32, pInfos: [^]AccelerationStructureBuildGeometryInfoKHR, pIndirectDeviceAddresses: [^]DeviceAddress, pIndirectStrides: [^]u32, ppMaxPrimitiveCounts: ^[^]u32) +ProcCmdBuildAccelerationStructuresKHR :: #type proc "system" (commandBuffer: CommandBuffer, infoCount: u32, pInfos: [^]AccelerationStructureBuildGeometryInfoKHR, ppBuildRangeInfos: ^[^]AccelerationStructureBuildRangeInfoKHR) +ProcCmdClearAttachments :: #type proc "system" (commandBuffer: CommandBuffer, attachmentCount: u32, pAttachments: [^]ClearAttachment, rectCount: u32, pRects: [^]ClearRect) +ProcCmdClearColorImage :: #type proc "system" (commandBuffer: CommandBuffer, image: Image, imageLayout: ImageLayout, pColor: ^ClearColorValue, rangeCount: u32, pRanges: [^]ImageSubresourceRange) +ProcCmdClearDepthStencilImage :: #type proc "system" (commandBuffer: CommandBuffer, image: Image, imageLayout: ImageLayout, pDepthStencil: ^ClearDepthStencilValue, rangeCount: u32, pRanges: [^]ImageSubresourceRange) +ProcCmdCopyAccelerationStructureKHR :: #type proc "system" (commandBuffer: CommandBuffer, pInfo: ^CopyAccelerationStructureInfoKHR) +ProcCmdCopyAccelerationStructureNV :: #type proc "system" (commandBuffer: CommandBuffer, dst: AccelerationStructureNV, src: AccelerationStructureNV, mode: CopyAccelerationStructureModeKHR) +ProcCmdCopyAccelerationStructureToMemoryKHR :: #type proc "system" (commandBuffer: CommandBuffer, pInfo: ^CopyAccelerationStructureToMemoryInfoKHR) +ProcCmdCopyBuffer :: #type proc "system" (commandBuffer: CommandBuffer, srcBuffer: Buffer, dstBuffer: Buffer, regionCount: u32, pRegions: [^]BufferCopy) +ProcCmdCopyBuffer2 :: #type proc "system" (commandBuffer: CommandBuffer, pCopyBufferInfo: ^CopyBufferInfo2) +ProcCmdCopyBuffer2KHR :: #type proc "system" (commandBuffer: CommandBuffer, pCopyBufferInfo: ^CopyBufferInfo2) +ProcCmdCopyBufferToImage :: #type proc "system" (commandBuffer: CommandBuffer, srcBuffer: Buffer, dstImage: Image, dstImageLayout: ImageLayout, regionCount: u32, pRegions: [^]BufferImageCopy) +ProcCmdCopyBufferToImage2 :: #type proc "system" (commandBuffer: CommandBuffer, pCopyBufferToImageInfo: ^CopyBufferToImageInfo2) +ProcCmdCopyBufferToImage2KHR :: #type proc "system" (commandBuffer: CommandBuffer, pCopyBufferToImageInfo: ^CopyBufferToImageInfo2) +ProcCmdCopyImage :: #type proc "system" (commandBuffer: CommandBuffer, srcImage: Image, srcImageLayout: ImageLayout, dstImage: Image, dstImageLayout: ImageLayout, regionCount: u32, pRegions: [^]ImageCopy) +ProcCmdCopyImage2 :: #type proc "system" (commandBuffer: CommandBuffer, pCopyImageInfo: ^CopyImageInfo2) +ProcCmdCopyImage2KHR :: #type proc "system" (commandBuffer: CommandBuffer, pCopyImageInfo: ^CopyImageInfo2) +ProcCmdCopyImageToBuffer :: #type proc "system" (commandBuffer: CommandBuffer, srcImage: Image, srcImageLayout: ImageLayout, dstBuffer: Buffer, regionCount: u32, pRegions: [^]BufferImageCopy) +ProcCmdCopyImageToBuffer2 :: #type proc "system" (commandBuffer: CommandBuffer, pCopyImageToBufferInfo: ^CopyImageToBufferInfo2) +ProcCmdCopyImageToBuffer2KHR :: #type proc "system" (commandBuffer: CommandBuffer, pCopyImageToBufferInfo: ^CopyImageToBufferInfo2) +ProcCmdCopyMemoryToAccelerationStructureKHR :: #type proc "system" (commandBuffer: CommandBuffer, pInfo: ^CopyMemoryToAccelerationStructureInfoKHR) +ProcCmdCopyQueryPoolResults :: #type proc "system" (commandBuffer: CommandBuffer, queryPool: QueryPool, firstQuery: u32, queryCount: u32, dstBuffer: Buffer, dstOffset: DeviceSize, stride: DeviceSize, flags: QueryResultFlags) +ProcCmdCuLaunchKernelNVX :: #type proc "system" (commandBuffer: CommandBuffer, pLaunchInfo: ^CuLaunchInfoNVX) +ProcCmdDebugMarkerBeginEXT :: #type proc "system" (commandBuffer: CommandBuffer, pMarkerInfo: ^DebugMarkerMarkerInfoEXT) +ProcCmdDebugMarkerEndEXT :: #type proc "system" (commandBuffer: CommandBuffer) +ProcCmdDebugMarkerInsertEXT :: #type proc "system" (commandBuffer: CommandBuffer, pMarkerInfo: ^DebugMarkerMarkerInfoEXT) +ProcCmdDispatch :: #type proc "system" (commandBuffer: CommandBuffer, groupCountX: u32, groupCountY: u32, groupCountZ: u32) +ProcCmdDispatchBase :: #type proc "system" (commandBuffer: CommandBuffer, baseGroupX: u32, baseGroupY: u32, baseGroupZ: u32, groupCountX: u32, groupCountY: u32, groupCountZ: u32) +ProcCmdDispatchBaseKHR :: #type proc "system" (commandBuffer: CommandBuffer, baseGroupX: u32, baseGroupY: u32, baseGroupZ: u32, groupCountX: u32, groupCountY: u32, groupCountZ: u32) +ProcCmdDispatchIndirect :: #type proc "system" (commandBuffer: CommandBuffer, buffer: Buffer, offset: DeviceSize) +ProcCmdDraw :: #type proc "system" (commandBuffer: CommandBuffer, vertexCount: u32, instanceCount: u32, firstVertex: u32, firstInstance: u32) +ProcCmdDrawIndexed :: #type proc "system" (commandBuffer: CommandBuffer, indexCount: u32, instanceCount: u32, firstIndex: u32, vertexOffset: i32, firstInstance: u32) +ProcCmdDrawIndexedIndirect :: #type proc "system" (commandBuffer: CommandBuffer, buffer: Buffer, offset: DeviceSize, drawCount: u32, stride: u32) +ProcCmdDrawIndexedIndirectCount :: #type proc "system" (commandBuffer: CommandBuffer, buffer: Buffer, offset: DeviceSize, countBuffer: Buffer, countBufferOffset: DeviceSize, maxDrawCount: u32, stride: u32) +ProcCmdDrawIndexedIndirectCountAMD :: #type proc "system" (commandBuffer: CommandBuffer, buffer: Buffer, offset: DeviceSize, countBuffer: Buffer, countBufferOffset: DeviceSize, maxDrawCount: u32, stride: u32) +ProcCmdDrawIndexedIndirectCountKHR :: #type proc "system" (commandBuffer: CommandBuffer, buffer: Buffer, offset: DeviceSize, countBuffer: Buffer, countBufferOffset: DeviceSize, maxDrawCount: u32, stride: u32) +ProcCmdDrawIndirect :: #type proc "system" (commandBuffer: CommandBuffer, buffer: Buffer, offset: DeviceSize, drawCount: u32, stride: u32) +ProcCmdDrawIndirectByteCountEXT :: #type proc "system" (commandBuffer: CommandBuffer, instanceCount: u32, firstInstance: u32, counterBuffer: Buffer, counterBufferOffset: DeviceSize, counterOffset: u32, vertexStride: u32) +ProcCmdDrawIndirectCount :: #type proc "system" (commandBuffer: CommandBuffer, buffer: Buffer, offset: DeviceSize, countBuffer: Buffer, countBufferOffset: DeviceSize, maxDrawCount: u32, stride: u32) +ProcCmdDrawIndirectCountAMD :: #type proc "system" (commandBuffer: CommandBuffer, buffer: Buffer, offset: DeviceSize, countBuffer: Buffer, countBufferOffset: DeviceSize, maxDrawCount: u32, stride: u32) +ProcCmdDrawIndirectCountKHR :: #type proc "system" (commandBuffer: CommandBuffer, buffer: Buffer, offset: DeviceSize, countBuffer: Buffer, countBufferOffset: DeviceSize, maxDrawCount: u32, stride: u32) +ProcCmdDrawMeshTasksIndirectCountNV :: #type proc "system" (commandBuffer: CommandBuffer, buffer: Buffer, offset: DeviceSize, countBuffer: Buffer, countBufferOffset: DeviceSize, maxDrawCount: u32, stride: u32) +ProcCmdDrawMeshTasksIndirectNV :: #type proc "system" (commandBuffer: CommandBuffer, buffer: Buffer, offset: DeviceSize, drawCount: u32, stride: u32) +ProcCmdDrawMeshTasksNV :: #type proc "system" (commandBuffer: CommandBuffer, taskCount: u32, firstTask: u32) +ProcCmdDrawMultiEXT :: #type proc "system" (commandBuffer: CommandBuffer, drawCount: u32, pVertexInfo: ^MultiDrawInfoEXT, instanceCount: u32, firstInstance: u32, stride: u32) +ProcCmdDrawMultiIndexedEXT :: #type proc "system" (commandBuffer: CommandBuffer, drawCount: u32, pIndexInfo: ^MultiDrawIndexedInfoEXT, instanceCount: u32, firstInstance: u32, stride: u32, pVertexOffset: ^i32) +ProcCmdEndConditionalRenderingEXT :: #type proc "system" (commandBuffer: CommandBuffer) +ProcCmdEndDebugUtilsLabelEXT :: #type proc "system" (commandBuffer: CommandBuffer) +ProcCmdEndQuery :: #type proc "system" (commandBuffer: CommandBuffer, queryPool: QueryPool, query: u32) +ProcCmdEndQueryIndexedEXT :: #type proc "system" (commandBuffer: CommandBuffer, queryPool: QueryPool, query: u32, index: u32) +ProcCmdEndRenderPass :: #type proc "system" (commandBuffer: CommandBuffer) +ProcCmdEndRenderPass2 :: #type proc "system" (commandBuffer: CommandBuffer, pSubpassEndInfo: ^SubpassEndInfo) +ProcCmdEndRenderPass2KHR :: #type proc "system" (commandBuffer: CommandBuffer, pSubpassEndInfo: ^SubpassEndInfo) +ProcCmdEndRendering :: #type proc "system" (commandBuffer: CommandBuffer) +ProcCmdEndRenderingKHR :: #type proc "system" (commandBuffer: CommandBuffer) +ProcCmdEndTransformFeedbackEXT :: #type proc "system" (commandBuffer: CommandBuffer, firstCounterBuffer: u32, counterBufferCount: u32, pCounterBuffers: [^]Buffer, pCounterBufferOffsets: [^]DeviceSize) +ProcCmdExecuteCommands :: #type proc "system" (commandBuffer: CommandBuffer, commandBufferCount: u32, pCommandBuffers: [^]CommandBuffer) +ProcCmdExecuteGeneratedCommandsNV :: #type proc "system" (commandBuffer: CommandBuffer, isPreprocessed: b32, pGeneratedCommandsInfo: ^GeneratedCommandsInfoNV) +ProcCmdFillBuffer :: #type proc "system" (commandBuffer: CommandBuffer, dstBuffer: Buffer, dstOffset: DeviceSize, size: DeviceSize, data: u32) +ProcCmdInsertDebugUtilsLabelEXT :: #type proc "system" (commandBuffer: CommandBuffer, pLabelInfo: ^DebugUtilsLabelEXT) +ProcCmdNextSubpass :: #type proc "system" (commandBuffer: CommandBuffer, contents: SubpassContents) +ProcCmdNextSubpass2 :: #type proc "system" (commandBuffer: CommandBuffer, pSubpassBeginInfo: ^SubpassBeginInfo, pSubpassEndInfo: ^SubpassEndInfo) +ProcCmdNextSubpass2KHR :: #type proc "system" (commandBuffer: CommandBuffer, pSubpassBeginInfo: ^SubpassBeginInfo, pSubpassEndInfo: ^SubpassEndInfo) +ProcCmdPipelineBarrier :: #type proc "system" (commandBuffer: CommandBuffer, srcStageMask: PipelineStageFlags, dstStageMask: PipelineStageFlags, dependencyFlags: DependencyFlags, memoryBarrierCount: u32, pMemoryBarriers: [^]MemoryBarrier, bufferMemoryBarrierCount: u32, pBufferMemoryBarriers: [^]BufferMemoryBarrier, imageMemoryBarrierCount: u32, pImageMemoryBarriers: [^]ImageMemoryBarrier) +ProcCmdPipelineBarrier2 :: #type proc "system" (commandBuffer: CommandBuffer, pDependencyInfo: ^DependencyInfo) +ProcCmdPipelineBarrier2KHR :: #type proc "system" (commandBuffer: CommandBuffer, pDependencyInfo: ^DependencyInfo) +ProcCmdPreprocessGeneratedCommandsNV :: #type proc "system" (commandBuffer: CommandBuffer, pGeneratedCommandsInfo: ^GeneratedCommandsInfoNV) +ProcCmdPushConstants :: #type proc "system" (commandBuffer: CommandBuffer, layout: PipelineLayout, stageFlags: ShaderStageFlags, offset: u32, size: u32, pValues: rawptr) +ProcCmdPushDescriptorSetKHR :: #type proc "system" (commandBuffer: CommandBuffer, pipelineBindPoint: PipelineBindPoint, layout: PipelineLayout, set: u32, descriptorWriteCount: u32, pDescriptorWrites: [^]WriteDescriptorSet) +ProcCmdPushDescriptorSetWithTemplateKHR :: #type proc "system" (commandBuffer: CommandBuffer, descriptorUpdateTemplate: DescriptorUpdateTemplate, layout: PipelineLayout, set: u32, pData: rawptr) +ProcCmdResetEvent :: #type proc "system" (commandBuffer: CommandBuffer, event: Event, stageMask: PipelineStageFlags) +ProcCmdResetEvent2 :: #type proc "system" (commandBuffer: CommandBuffer, event: Event, stageMask: PipelineStageFlags2) +ProcCmdResetEvent2KHR :: #type proc "system" (commandBuffer: CommandBuffer, event: Event, stageMask: PipelineStageFlags2) +ProcCmdResetQueryPool :: #type proc "system" (commandBuffer: CommandBuffer, queryPool: QueryPool, firstQuery: u32, queryCount: u32) +ProcCmdResolveImage :: #type proc "system" (commandBuffer: CommandBuffer, srcImage: Image, srcImageLayout: ImageLayout, dstImage: Image, dstImageLayout: ImageLayout, regionCount: u32, pRegions: [^]ImageResolve) +ProcCmdResolveImage2 :: #type proc "system" (commandBuffer: CommandBuffer, pResolveImageInfo: ^ResolveImageInfo2) +ProcCmdResolveImage2KHR :: #type proc "system" (commandBuffer: CommandBuffer, pResolveImageInfo: ^ResolveImageInfo2) +ProcCmdSetBlendConstants :: #type proc "system" (commandBuffer: CommandBuffer) +ProcCmdSetCheckpointNV :: #type proc "system" (commandBuffer: CommandBuffer, pCheckpointMarker: rawptr) +ProcCmdSetCoarseSampleOrderNV :: #type proc "system" (commandBuffer: CommandBuffer, sampleOrderType: CoarseSampleOrderTypeNV, customSampleOrderCount: u32, pCustomSampleOrders: [^]CoarseSampleOrderCustomNV) +ProcCmdSetCullMode :: #type proc "system" (commandBuffer: CommandBuffer, cullMode: CullModeFlags) +ProcCmdSetCullModeEXT :: #type proc "system" (commandBuffer: CommandBuffer, cullMode: CullModeFlags) +ProcCmdSetDepthBias :: #type proc "system" (commandBuffer: CommandBuffer, depthBiasConstantFactor: f32, depthBiasClamp: f32, depthBiasSlopeFactor: f32) +ProcCmdSetDepthBiasEnable :: #type proc "system" (commandBuffer: CommandBuffer, depthBiasEnable: b32) +ProcCmdSetDepthBiasEnableEXT :: #type proc "system" (commandBuffer: CommandBuffer, depthBiasEnable: b32) +ProcCmdSetDepthBounds :: #type proc "system" (commandBuffer: CommandBuffer, minDepthBounds: f32, maxDepthBounds: f32) +ProcCmdSetDepthBoundsTestEnable :: #type proc "system" (commandBuffer: CommandBuffer, depthBoundsTestEnable: b32) +ProcCmdSetDepthBoundsTestEnableEXT :: #type proc "system" (commandBuffer: CommandBuffer, depthBoundsTestEnable: b32) +ProcCmdSetDepthCompareOp :: #type proc "system" (commandBuffer: CommandBuffer, depthCompareOp: CompareOp) +ProcCmdSetDepthCompareOpEXT :: #type proc "system" (commandBuffer: CommandBuffer, depthCompareOp: CompareOp) +ProcCmdSetDepthTestEnable :: #type proc "system" (commandBuffer: CommandBuffer, depthTestEnable: b32) +ProcCmdSetDepthTestEnableEXT :: #type proc "system" (commandBuffer: CommandBuffer, depthTestEnable: b32) +ProcCmdSetDepthWriteEnable :: #type proc "system" (commandBuffer: CommandBuffer, depthWriteEnable: b32) +ProcCmdSetDepthWriteEnableEXT :: #type proc "system" (commandBuffer: CommandBuffer, depthWriteEnable: b32) +ProcCmdSetDeviceMask :: #type proc "system" (commandBuffer: CommandBuffer, deviceMask: u32) +ProcCmdSetDeviceMaskKHR :: #type proc "system" (commandBuffer: CommandBuffer, deviceMask: u32) +ProcCmdSetDiscardRectangleEXT :: #type proc "system" (commandBuffer: CommandBuffer, firstDiscardRectangle: u32, discardRectangleCount: u32, pDiscardRectangles: [^]Rect2D) +ProcCmdSetEvent :: #type proc "system" (commandBuffer: CommandBuffer, event: Event, stageMask: PipelineStageFlags) +ProcCmdSetEvent2 :: #type proc "system" (commandBuffer: CommandBuffer, event: Event, pDependencyInfo: ^DependencyInfo) +ProcCmdSetEvent2KHR :: #type proc "system" (commandBuffer: CommandBuffer, event: Event, pDependencyInfo: ^DependencyInfo) +ProcCmdSetExclusiveScissorNV :: #type proc "system" (commandBuffer: CommandBuffer, firstExclusiveScissor: u32, exclusiveScissorCount: u32, pExclusiveScissors: [^]Rect2D) +ProcCmdSetFragmentShadingRateEnumNV :: #type proc "system" (commandBuffer: CommandBuffer, shadingRate: FragmentShadingRateNV) +ProcCmdSetFragmentShadingRateKHR :: #type proc "system" (commandBuffer: CommandBuffer, pFragmentSize: ^Extent2D) +ProcCmdSetFrontFace :: #type proc "system" (commandBuffer: CommandBuffer, frontFace: FrontFace) +ProcCmdSetFrontFaceEXT :: #type proc "system" (commandBuffer: CommandBuffer, frontFace: FrontFace) +ProcCmdSetLineStippleEXT :: #type proc "system" (commandBuffer: CommandBuffer, lineStippleFactor: u32, lineStipplePattern: u16) +ProcCmdSetLineWidth :: #type proc "system" (commandBuffer: CommandBuffer, lineWidth: f32) +ProcCmdSetLogicOpEXT :: #type proc "system" (commandBuffer: CommandBuffer, logicOp: LogicOp) +ProcCmdSetPatchControlPointsEXT :: #type proc "system" (commandBuffer: CommandBuffer, patchControlPoints: u32) +ProcCmdSetPerformanceMarkerINTEL :: #type proc "system" (commandBuffer: CommandBuffer, pMarkerInfo: ^PerformanceMarkerInfoINTEL) -> Result +ProcCmdSetPerformanceOverrideINTEL :: #type proc "system" (commandBuffer: CommandBuffer, pOverrideInfo: ^PerformanceOverrideInfoINTEL) -> Result +ProcCmdSetPerformanceStreamMarkerINTEL :: #type proc "system" (commandBuffer: CommandBuffer, pMarkerInfo: ^PerformanceStreamMarkerInfoINTEL) -> Result +ProcCmdSetPrimitiveRestartEnable :: #type proc "system" (commandBuffer: CommandBuffer, primitiveRestartEnable: b32) +ProcCmdSetPrimitiveRestartEnableEXT :: #type proc "system" (commandBuffer: CommandBuffer, primitiveRestartEnable: b32) +ProcCmdSetPrimitiveTopology :: #type proc "system" (commandBuffer: CommandBuffer, primitiveTopology: PrimitiveTopology) +ProcCmdSetPrimitiveTopologyEXT :: #type proc "system" (commandBuffer: CommandBuffer, primitiveTopology: PrimitiveTopology) +ProcCmdSetRasterizerDiscardEnable :: #type proc "system" (commandBuffer: CommandBuffer, rasterizerDiscardEnable: b32) +ProcCmdSetRasterizerDiscardEnableEXT :: #type proc "system" (commandBuffer: CommandBuffer, rasterizerDiscardEnable: b32) +ProcCmdSetRayTracingPipelineStackSizeKHR :: #type proc "system" (commandBuffer: CommandBuffer, pipelineStackSize: u32) +ProcCmdSetSampleLocationsEXT :: #type proc "system" (commandBuffer: CommandBuffer, pSampleLocationsInfo: ^SampleLocationsInfoEXT) +ProcCmdSetScissor :: #type proc "system" (commandBuffer: CommandBuffer, firstScissor: u32, scissorCount: u32, pScissors: [^]Rect2D) +ProcCmdSetScissorWithCount :: #type proc "system" (commandBuffer: CommandBuffer, scissorCount: u32, pScissors: [^]Rect2D) +ProcCmdSetScissorWithCountEXT :: #type proc "system" (commandBuffer: CommandBuffer, scissorCount: u32, pScissors: [^]Rect2D) +ProcCmdSetStencilCompareMask :: #type proc "system" (commandBuffer: CommandBuffer, faceMask: StencilFaceFlags, compareMask: u32) +ProcCmdSetStencilOp :: #type proc "system" (commandBuffer: CommandBuffer, faceMask: StencilFaceFlags, failOp: StencilOp, passOp: StencilOp, depthFailOp: StencilOp, compareOp: CompareOp) +ProcCmdSetStencilOpEXT :: #type proc "system" (commandBuffer: CommandBuffer, faceMask: StencilFaceFlags, failOp: StencilOp, passOp: StencilOp, depthFailOp: StencilOp, compareOp: CompareOp) +ProcCmdSetStencilReference :: #type proc "system" (commandBuffer: CommandBuffer, faceMask: StencilFaceFlags, reference: u32) +ProcCmdSetStencilTestEnable :: #type proc "system" (commandBuffer: CommandBuffer, stencilTestEnable: b32) +ProcCmdSetStencilTestEnableEXT :: #type proc "system" (commandBuffer: CommandBuffer, stencilTestEnable: b32) +ProcCmdSetStencilWriteMask :: #type proc "system" (commandBuffer: CommandBuffer, faceMask: StencilFaceFlags, writeMask: u32) +ProcCmdSetVertexInputEXT :: #type proc "system" (commandBuffer: CommandBuffer, vertexBindingDescriptionCount: u32, pVertexBindingDescriptions: [^]VertexInputBindingDescription2EXT, vertexAttributeDescriptionCount: u32, pVertexAttributeDescriptions: [^]VertexInputAttributeDescription2EXT) +ProcCmdSetViewport :: #type proc "system" (commandBuffer: CommandBuffer, firstViewport: u32, viewportCount: u32, pViewports: [^]Viewport) +ProcCmdSetViewportShadingRatePaletteNV :: #type proc "system" (commandBuffer: CommandBuffer, firstViewport: u32, viewportCount: u32, pShadingRatePalettes: [^]ShadingRatePaletteNV) +ProcCmdSetViewportWScalingNV :: #type proc "system" (commandBuffer: CommandBuffer, firstViewport: u32, viewportCount: u32, pViewportWScalings: [^]ViewportWScalingNV) +ProcCmdSetViewportWithCount :: #type proc "system" (commandBuffer: CommandBuffer, viewportCount: u32, pViewports: [^]Viewport) +ProcCmdSetViewportWithCountEXT :: #type proc "system" (commandBuffer: CommandBuffer, viewportCount: u32, pViewports: [^]Viewport) +ProcCmdSubpassShadingHUAWEI :: #type proc "system" (commandBuffer: CommandBuffer) +ProcCmdTraceRaysIndirectKHR :: #type proc "system" (commandBuffer: CommandBuffer, pRaygenShaderBindingTable: [^]StridedDeviceAddressRegionKHR, pMissShaderBindingTable: [^]StridedDeviceAddressRegionKHR, pHitShaderBindingTable: [^]StridedDeviceAddressRegionKHR, pCallableShaderBindingTable: [^]StridedDeviceAddressRegionKHR, indirectDeviceAddress: DeviceAddress) +ProcCmdTraceRaysKHR :: #type proc "system" (commandBuffer: CommandBuffer, pRaygenShaderBindingTable: [^]StridedDeviceAddressRegionKHR, pMissShaderBindingTable: [^]StridedDeviceAddressRegionKHR, pHitShaderBindingTable: [^]StridedDeviceAddressRegionKHR, pCallableShaderBindingTable: [^]StridedDeviceAddressRegionKHR, width: u32, height: u32, depth: u32) +ProcCmdTraceRaysNV :: #type proc "system" (commandBuffer: CommandBuffer, raygenShaderBindingTableBuffer: Buffer, raygenShaderBindingOffset: DeviceSize, missShaderBindingTableBuffer: Buffer, missShaderBindingOffset: DeviceSize, missShaderBindingStride: DeviceSize, hitShaderBindingTableBuffer: Buffer, hitShaderBindingOffset: DeviceSize, hitShaderBindingStride: DeviceSize, callableShaderBindingTableBuffer: Buffer, callableShaderBindingOffset: DeviceSize, callableShaderBindingStride: DeviceSize, width: u32, height: u32, depth: u32) +ProcCmdUpdateBuffer :: #type proc "system" (commandBuffer: CommandBuffer, dstBuffer: Buffer, dstOffset: DeviceSize, dataSize: DeviceSize, pData: rawptr) +ProcCmdWaitEvents :: #type proc "system" (commandBuffer: CommandBuffer, eventCount: u32, pEvents: [^]Event, srcStageMask: PipelineStageFlags, dstStageMask: PipelineStageFlags, memoryBarrierCount: u32, pMemoryBarriers: [^]MemoryBarrier, bufferMemoryBarrierCount: u32, pBufferMemoryBarriers: [^]BufferMemoryBarrier, imageMemoryBarrierCount: u32, pImageMemoryBarriers: [^]ImageMemoryBarrier) +ProcCmdWaitEvents2 :: #type proc "system" (commandBuffer: CommandBuffer, eventCount: u32, pEvents: [^]Event, pDependencyInfos: [^]DependencyInfo) +ProcCmdWaitEvents2KHR :: #type proc "system" (commandBuffer: CommandBuffer, eventCount: u32, pEvents: [^]Event, pDependencyInfos: [^]DependencyInfo) +ProcCmdWriteAccelerationStructuresPropertiesKHR :: #type proc "system" (commandBuffer: CommandBuffer, accelerationStructureCount: u32, pAccelerationStructures: [^]AccelerationStructureKHR, queryType: QueryType, queryPool: QueryPool, firstQuery: u32) +ProcCmdWriteAccelerationStructuresPropertiesNV :: #type proc "system" (commandBuffer: CommandBuffer, accelerationStructureCount: u32, pAccelerationStructures: [^]AccelerationStructureNV, queryType: QueryType, queryPool: QueryPool, firstQuery: u32) +ProcCmdWriteBufferMarker2AMD :: #type proc "system" (commandBuffer: CommandBuffer, stage: PipelineStageFlags2, dstBuffer: Buffer, dstOffset: DeviceSize, marker: u32) +ProcCmdWriteBufferMarkerAMD :: #type proc "system" (commandBuffer: CommandBuffer, pipelineStage: PipelineStageFlags, dstBuffer: Buffer, dstOffset: DeviceSize, marker: u32) +ProcCmdWriteTimestamp :: #type proc "system" (commandBuffer: CommandBuffer, pipelineStage: PipelineStageFlags, queryPool: QueryPool, query: u32) +ProcCmdWriteTimestamp2 :: #type proc "system" (commandBuffer: CommandBuffer, stage: PipelineStageFlags2, queryPool: QueryPool, query: u32) +ProcCmdWriteTimestamp2KHR :: #type proc "system" (commandBuffer: CommandBuffer, stage: PipelineStageFlags2, queryPool: QueryPool, query: u32) +ProcCompileDeferredNV :: #type proc "system" (device: Device, pipeline: Pipeline, shader: u32) -> Result +ProcCopyAccelerationStructureKHR :: #type proc "system" (device: Device, deferredOperation: DeferredOperationKHR, pInfo: ^CopyAccelerationStructureInfoKHR) -> Result +ProcCopyAccelerationStructureToMemoryKHR :: #type proc "system" (device: Device, deferredOperation: DeferredOperationKHR, pInfo: ^CopyAccelerationStructureToMemoryInfoKHR) -> Result +ProcCopyMemoryToAccelerationStructureKHR :: #type proc "system" (device: Device, deferredOperation: DeferredOperationKHR, pInfo: ^CopyMemoryToAccelerationStructureInfoKHR) -> Result +ProcCreateAccelerationStructureKHR :: #type proc "system" (device: Device, pCreateInfo: ^AccelerationStructureCreateInfoKHR, pAllocator: ^AllocationCallbacks, pAccelerationStructure: ^AccelerationStructureKHR) -> Result +ProcCreateAccelerationStructureNV :: #type proc "system" (device: Device, pCreateInfo: ^AccelerationStructureCreateInfoNV, pAllocator: ^AllocationCallbacks, pAccelerationStructure: ^AccelerationStructureNV) -> Result +ProcCreateBuffer :: #type proc "system" (device: Device, pCreateInfo: ^BufferCreateInfo, pAllocator: ^AllocationCallbacks, pBuffer: ^Buffer) -> Result +ProcCreateBufferView :: #type proc "system" (device: Device, pCreateInfo: ^BufferViewCreateInfo, pAllocator: ^AllocationCallbacks, pView: ^BufferView) -> Result +ProcCreateCommandPool :: #type proc "system" (device: Device, pCreateInfo: ^CommandPoolCreateInfo, pAllocator: ^AllocationCallbacks, pCommandPool: ^CommandPool) -> Result +ProcCreateComputePipelines :: #type proc "system" (device: Device, pipelineCache: PipelineCache, createInfoCount: u32, pCreateInfos: [^]ComputePipelineCreateInfo, pAllocator: ^AllocationCallbacks, pPipelines: [^]Pipeline) -> Result +ProcCreateCuFunctionNVX :: #type proc "system" (device: Device, pCreateInfo: ^CuFunctionCreateInfoNVX, pAllocator: ^AllocationCallbacks, pFunction: ^CuFunctionNVX) -> Result +ProcCreateCuModuleNVX :: #type proc "system" (device: Device, pCreateInfo: ^CuModuleCreateInfoNVX, pAllocator: ^AllocationCallbacks, pModule: ^CuModuleNVX) -> Result +ProcCreateDeferredOperationKHR :: #type proc "system" (device: Device, pAllocator: ^AllocationCallbacks, pDeferredOperation: ^DeferredOperationKHR) -> Result +ProcCreateDescriptorPool :: #type proc "system" (device: Device, pCreateInfo: ^DescriptorPoolCreateInfo, pAllocator: ^AllocationCallbacks, pDescriptorPool: ^DescriptorPool) -> Result +ProcCreateDescriptorSetLayout :: #type proc "system" (device: Device, pCreateInfo: ^DescriptorSetLayoutCreateInfo, pAllocator: ^AllocationCallbacks, pSetLayout: ^DescriptorSetLayout) -> Result +ProcCreateDescriptorUpdateTemplate :: #type proc "system" (device: Device, pCreateInfo: ^DescriptorUpdateTemplateCreateInfo, pAllocator: ^AllocationCallbacks, pDescriptorUpdateTemplate: ^DescriptorUpdateTemplate) -> Result +ProcCreateDescriptorUpdateTemplateKHR :: #type proc "system" (device: Device, pCreateInfo: ^DescriptorUpdateTemplateCreateInfo, pAllocator: ^AllocationCallbacks, pDescriptorUpdateTemplate: ^DescriptorUpdateTemplate) -> Result +ProcCreateEvent :: #type proc "system" (device: Device, pCreateInfo: ^EventCreateInfo, pAllocator: ^AllocationCallbacks, pEvent: ^Event) -> Result +ProcCreateFence :: #type proc "system" (device: Device, pCreateInfo: ^FenceCreateInfo, pAllocator: ^AllocationCallbacks, pFence: ^Fence) -> Result +ProcCreateFramebuffer :: #type proc "system" (device: Device, pCreateInfo: ^FramebufferCreateInfo, pAllocator: ^AllocationCallbacks, pFramebuffer: ^Framebuffer) -> Result +ProcCreateGraphicsPipelines :: #type proc "system" (device: Device, pipelineCache: PipelineCache, createInfoCount: u32, pCreateInfos: [^]GraphicsPipelineCreateInfo, pAllocator: ^AllocationCallbacks, pPipelines: [^]Pipeline) -> Result +ProcCreateImage :: #type proc "system" (device: Device, pCreateInfo: ^ImageCreateInfo, pAllocator: ^AllocationCallbacks, pImage: ^Image) -> Result +ProcCreateImageView :: #type proc "system" (device: Device, pCreateInfo: ^ImageViewCreateInfo, pAllocator: ^AllocationCallbacks, pView: ^ImageView) -> Result +ProcCreateIndirectCommandsLayoutNV :: #type proc "system" (device: Device, pCreateInfo: ^IndirectCommandsLayoutCreateInfoNV, pAllocator: ^AllocationCallbacks, pIndirectCommandsLayout: ^IndirectCommandsLayoutNV) -> Result +ProcCreatePipelineCache :: #type proc "system" (device: Device, pCreateInfo: ^PipelineCacheCreateInfo, pAllocator: ^AllocationCallbacks, pPipelineCache: ^PipelineCache) -> Result +ProcCreatePipelineLayout :: #type proc "system" (device: Device, pCreateInfo: ^PipelineLayoutCreateInfo, pAllocator: ^AllocationCallbacks, pPipelineLayout: ^PipelineLayout) -> Result +ProcCreatePrivateDataSlot :: #type proc "system" (device: Device, pCreateInfo: ^PrivateDataSlotCreateInfo, pAllocator: ^AllocationCallbacks, pPrivateDataSlot: ^PrivateDataSlot) -> Result +ProcCreatePrivateDataSlotEXT :: #type proc "system" (device: Device, pCreateInfo: ^PrivateDataSlotCreateInfo, pAllocator: ^AllocationCallbacks, pPrivateDataSlot: ^PrivateDataSlot) -> Result +ProcCreateQueryPool :: #type proc "system" (device: Device, pCreateInfo: ^QueryPoolCreateInfo, pAllocator: ^AllocationCallbacks, pQueryPool: ^QueryPool) -> Result +ProcCreateRayTracingPipelinesKHR :: #type proc "system" (device: Device, deferredOperation: DeferredOperationKHR, pipelineCache: PipelineCache, createInfoCount: u32, pCreateInfos: [^]RayTracingPipelineCreateInfoKHR, pAllocator: ^AllocationCallbacks, pPipelines: [^]Pipeline) -> Result +ProcCreateRayTracingPipelinesNV :: #type proc "system" (device: Device, pipelineCache: PipelineCache, createInfoCount: u32, pCreateInfos: [^]RayTracingPipelineCreateInfoNV, pAllocator: ^AllocationCallbacks, pPipelines: [^]Pipeline) -> Result +ProcCreateRenderPass :: #type proc "system" (device: Device, pCreateInfo: ^RenderPassCreateInfo, pAllocator: ^AllocationCallbacks, pRenderPass: [^]RenderPass) -> Result +ProcCreateRenderPass2 :: #type proc "system" (device: Device, pCreateInfo: ^RenderPassCreateInfo2, pAllocator: ^AllocationCallbacks, pRenderPass: [^]RenderPass) -> Result +ProcCreateRenderPass2KHR :: #type proc "system" (device: Device, pCreateInfo: ^RenderPassCreateInfo2, pAllocator: ^AllocationCallbacks, pRenderPass: [^]RenderPass) -> Result +ProcCreateSampler :: #type proc "system" (device: Device, pCreateInfo: ^SamplerCreateInfo, pAllocator: ^AllocationCallbacks, pSampler: ^Sampler) -> Result +ProcCreateSamplerYcbcrConversion :: #type proc "system" (device: Device, pCreateInfo: ^SamplerYcbcrConversionCreateInfo, pAllocator: ^AllocationCallbacks, pYcbcrConversion: ^SamplerYcbcrConversion) -> Result +ProcCreateSamplerYcbcrConversionKHR :: #type proc "system" (device: Device, pCreateInfo: ^SamplerYcbcrConversionCreateInfo, pAllocator: ^AllocationCallbacks, pYcbcrConversion: ^SamplerYcbcrConversion) -> Result +ProcCreateSemaphore :: #type proc "system" (device: Device, pCreateInfo: ^SemaphoreCreateInfo, pAllocator: ^AllocationCallbacks, pSemaphore: ^Semaphore) -> Result +ProcCreateShaderModule :: #type proc "system" (device: Device, pCreateInfo: ^ShaderModuleCreateInfo, pAllocator: ^AllocationCallbacks, pShaderModule: ^ShaderModule) -> Result +ProcCreateSharedSwapchainsKHR :: #type proc "system" (device: Device, swapchainCount: u32, pCreateInfos: [^]SwapchainCreateInfoKHR, pAllocator: ^AllocationCallbacks, pSwapchains: [^]SwapchainKHR) -> Result +ProcCreateSwapchainKHR :: #type proc "system" (device: Device, pCreateInfo: ^SwapchainCreateInfoKHR, pAllocator: ^AllocationCallbacks, pSwapchain: ^SwapchainKHR) -> Result +ProcCreateValidationCacheEXT :: #type proc "system" (device: Device, pCreateInfo: ^ValidationCacheCreateInfoEXT, pAllocator: ^AllocationCallbacks, pValidationCache: ^ValidationCacheEXT) -> Result +ProcDebugMarkerSetObjectNameEXT :: #type proc "system" (device: Device, pNameInfo: ^DebugMarkerObjectNameInfoEXT) -> Result +ProcDebugMarkerSetObjectTagEXT :: #type proc "system" (device: Device, pTagInfo: ^DebugMarkerObjectTagInfoEXT) -> Result +ProcDeferredOperationJoinKHR :: #type proc "system" (device: Device, operation: DeferredOperationKHR) -> Result +ProcDestroyAccelerationStructureKHR :: #type proc "system" (device: Device, accelerationStructure: AccelerationStructureKHR, pAllocator: ^AllocationCallbacks) +ProcDestroyAccelerationStructureNV :: #type proc "system" (device: Device, accelerationStructure: AccelerationStructureNV, pAllocator: ^AllocationCallbacks) +ProcDestroyBuffer :: #type proc "system" (device: Device, buffer: Buffer, pAllocator: ^AllocationCallbacks) +ProcDestroyBufferView :: #type proc "system" (device: Device, bufferView: BufferView, pAllocator: ^AllocationCallbacks) +ProcDestroyCommandPool :: #type proc "system" (device: Device, commandPool: CommandPool, pAllocator: ^AllocationCallbacks) +ProcDestroyCuFunctionNVX :: #type proc "system" (device: Device, function: CuFunctionNVX, pAllocator: ^AllocationCallbacks) +ProcDestroyCuModuleNVX :: #type proc "system" (device: Device, module: CuModuleNVX, pAllocator: ^AllocationCallbacks) +ProcDestroyDeferredOperationKHR :: #type proc "system" (device: Device, operation: DeferredOperationKHR, pAllocator: ^AllocationCallbacks) +ProcDestroyDescriptorPool :: #type proc "system" (device: Device, descriptorPool: DescriptorPool, pAllocator: ^AllocationCallbacks) +ProcDestroyDescriptorSetLayout :: #type proc "system" (device: Device, descriptorSetLayout: DescriptorSetLayout, pAllocator: ^AllocationCallbacks) +ProcDestroyDescriptorUpdateTemplate :: #type proc "system" (device: Device, descriptorUpdateTemplate: DescriptorUpdateTemplate, pAllocator: ^AllocationCallbacks) +ProcDestroyDescriptorUpdateTemplateKHR :: #type proc "system" (device: Device, descriptorUpdateTemplate: DescriptorUpdateTemplate, pAllocator: ^AllocationCallbacks) +ProcDestroyDevice :: #type proc "system" (device: Device, pAllocator: ^AllocationCallbacks) +ProcDestroyEvent :: #type proc "system" (device: Device, event: Event, pAllocator: ^AllocationCallbacks) +ProcDestroyFence :: #type proc "system" (device: Device, fence: Fence, pAllocator: ^AllocationCallbacks) +ProcDestroyFramebuffer :: #type proc "system" (device: Device, framebuffer: Framebuffer, pAllocator: ^AllocationCallbacks) +ProcDestroyImage :: #type proc "system" (device: Device, image: Image, pAllocator: ^AllocationCallbacks) +ProcDestroyImageView :: #type proc "system" (device: Device, imageView: ImageView, pAllocator: ^AllocationCallbacks) +ProcDestroyIndirectCommandsLayoutNV :: #type proc "system" (device: Device, indirectCommandsLayout: IndirectCommandsLayoutNV, pAllocator: ^AllocationCallbacks) +ProcDestroyPipeline :: #type proc "system" (device: Device, pipeline: Pipeline, pAllocator: ^AllocationCallbacks) +ProcDestroyPipelineCache :: #type proc "system" (device: Device, pipelineCache: PipelineCache, pAllocator: ^AllocationCallbacks) +ProcDestroyPipelineLayout :: #type proc "system" (device: Device, pipelineLayout: PipelineLayout, pAllocator: ^AllocationCallbacks) +ProcDestroyPrivateDataSlot :: #type proc "system" (device: Device, privateDataSlot: PrivateDataSlot, pAllocator: ^AllocationCallbacks) +ProcDestroyPrivateDataSlotEXT :: #type proc "system" (device: Device, privateDataSlot: PrivateDataSlot, pAllocator: ^AllocationCallbacks) +ProcDestroyQueryPool :: #type proc "system" (device: Device, queryPool: QueryPool, pAllocator: ^AllocationCallbacks) +ProcDestroyRenderPass :: #type proc "system" (device: Device, renderPass: RenderPass, pAllocator: ^AllocationCallbacks) +ProcDestroySampler :: #type proc "system" (device: Device, sampler: Sampler, pAllocator: ^AllocationCallbacks) +ProcDestroySamplerYcbcrConversion :: #type proc "system" (device: Device, ycbcrConversion: SamplerYcbcrConversion, pAllocator: ^AllocationCallbacks) +ProcDestroySamplerYcbcrConversionKHR :: #type proc "system" (device: Device, ycbcrConversion: SamplerYcbcrConversion, pAllocator: ^AllocationCallbacks) +ProcDestroySemaphore :: #type proc "system" (device: Device, semaphore: Semaphore, pAllocator: ^AllocationCallbacks) +ProcDestroyShaderModule :: #type proc "system" (device: Device, shaderModule: ShaderModule, pAllocator: ^AllocationCallbacks) +ProcDestroySwapchainKHR :: #type proc "system" (device: Device, swapchain: SwapchainKHR, pAllocator: ^AllocationCallbacks) +ProcDestroyValidationCacheEXT :: #type proc "system" (device: Device, validationCache: ValidationCacheEXT, pAllocator: ^AllocationCallbacks) +ProcDeviceWaitIdle :: #type proc "system" (device: Device) -> Result +ProcDisplayPowerControlEXT :: #type proc "system" (device: Device, display: DisplayKHR, pDisplayPowerInfo: ^DisplayPowerInfoEXT) -> Result +ProcEndCommandBuffer :: #type proc "system" (commandBuffer: CommandBuffer) -> Result +ProcFlushMappedMemoryRanges :: #type proc "system" (device: Device, memoryRangeCount: u32, pMemoryRanges: [^]MappedMemoryRange) -> Result +ProcFreeCommandBuffers :: #type proc "system" (device: Device, commandPool: CommandPool, commandBufferCount: u32, pCommandBuffers: [^]CommandBuffer) +ProcFreeDescriptorSets :: #type proc "system" (device: Device, descriptorPool: DescriptorPool, descriptorSetCount: u32, pDescriptorSets: [^]DescriptorSet) -> Result +ProcFreeMemory :: #type proc "system" (device: Device, memory: DeviceMemory, pAllocator: ^AllocationCallbacks) +ProcGetAccelerationStructureBuildSizesKHR :: #type proc "system" (device: Device, buildType: AccelerationStructureBuildTypeKHR, pBuildInfo: ^AccelerationStructureBuildGeometryInfoKHR, pMaxPrimitiveCounts: [^]u32, pSizeInfo: ^AccelerationStructureBuildSizesInfoKHR) +ProcGetAccelerationStructureDeviceAddressKHR :: #type proc "system" (device: Device, pInfo: ^AccelerationStructureDeviceAddressInfoKHR) -> DeviceAddress +ProcGetAccelerationStructureHandleNV :: #type proc "system" (device: Device, accelerationStructure: AccelerationStructureNV, dataSize: int, pData: rawptr) -> Result +ProcGetAccelerationStructureMemoryRequirementsNV :: #type proc "system" (device: Device, pInfo: ^AccelerationStructureMemoryRequirementsInfoNV, pMemoryRequirements: [^]MemoryRequirements2KHR) +ProcGetBufferDeviceAddress :: #type proc "system" (device: Device, pInfo: ^BufferDeviceAddressInfo) -> DeviceAddress +ProcGetBufferDeviceAddressEXT :: #type proc "system" (device: Device, pInfo: ^BufferDeviceAddressInfo) -> DeviceAddress +ProcGetBufferDeviceAddressKHR :: #type proc "system" (device: Device, pInfo: ^BufferDeviceAddressInfo) -> DeviceAddress +ProcGetBufferMemoryRequirements :: #type proc "system" (device: Device, buffer: Buffer, pMemoryRequirements: [^]MemoryRequirements) +ProcGetBufferMemoryRequirements2 :: #type proc "system" (device: Device, pInfo: ^BufferMemoryRequirementsInfo2, pMemoryRequirements: [^]MemoryRequirements2) +ProcGetBufferMemoryRequirements2KHR :: #type proc "system" (device: Device, pInfo: ^BufferMemoryRequirementsInfo2, pMemoryRequirements: [^]MemoryRequirements2) +ProcGetBufferOpaqueCaptureAddress :: #type proc "system" (device: Device, pInfo: ^BufferDeviceAddressInfo) -> u64 +ProcGetBufferOpaqueCaptureAddressKHR :: #type proc "system" (device: Device, pInfo: ^BufferDeviceAddressInfo) -> u64 +ProcGetCalibratedTimestampsEXT :: #type proc "system" (device: Device, timestampCount: u32, pTimestampInfos: [^]CalibratedTimestampInfoEXT, pTimestamps: [^]u64, pMaxDeviation: ^u64) -> Result +ProcGetDeferredOperationMaxConcurrencyKHR :: #type proc "system" (device: Device, operation: DeferredOperationKHR) -> u32 +ProcGetDeferredOperationResultKHR :: #type proc "system" (device: Device, operation: DeferredOperationKHR) -> Result +ProcGetDescriptorSetHostMappingVALVE :: #type proc "system" (device: Device, descriptorSet: DescriptorSet, ppData: ^rawptr) +ProcGetDescriptorSetLayoutHostMappingInfoVALVE :: #type proc "system" (device: Device, pBindingReference: ^DescriptorSetBindingReferenceVALVE, pHostMapping: ^DescriptorSetLayoutHostMappingInfoVALVE) +ProcGetDescriptorSetLayoutSupport :: #type proc "system" (device: Device, pCreateInfo: ^DescriptorSetLayoutCreateInfo, pSupport: ^DescriptorSetLayoutSupport) +ProcGetDescriptorSetLayoutSupportKHR :: #type proc "system" (device: Device, pCreateInfo: ^DescriptorSetLayoutCreateInfo, pSupport: ^DescriptorSetLayoutSupport) +ProcGetDeviceAccelerationStructureCompatibilityKHR :: #type proc "system" (device: Device, pVersionInfo: ^AccelerationStructureVersionInfoKHR, pCompatibility: ^AccelerationStructureCompatibilityKHR) +ProcGetDeviceBufferMemoryRequirements :: #type proc "system" (device: Device, pInfo: ^DeviceBufferMemoryRequirements, pMemoryRequirements: [^]MemoryRequirements2) +ProcGetDeviceBufferMemoryRequirementsKHR :: #type proc "system" (device: Device, pInfo: ^DeviceBufferMemoryRequirements, pMemoryRequirements: [^]MemoryRequirements2) +ProcGetDeviceGroupPeerMemoryFeatures :: #type proc "system" (device: Device, heapIndex: u32, localDeviceIndex: u32, remoteDeviceIndex: u32, pPeerMemoryFeatures: [^]PeerMemoryFeatureFlags) +ProcGetDeviceGroupPeerMemoryFeaturesKHR :: #type proc "system" (device: Device, heapIndex: u32, localDeviceIndex: u32, remoteDeviceIndex: u32, pPeerMemoryFeatures: [^]PeerMemoryFeatureFlags) +ProcGetDeviceGroupPresentCapabilitiesKHR :: #type proc "system" (device: Device, pDeviceGroupPresentCapabilities: [^]DeviceGroupPresentCapabilitiesKHR) -> Result +ProcGetDeviceGroupSurfacePresentModes2EXT :: #type proc "system" (device: Device, pSurfaceInfo: ^PhysicalDeviceSurfaceInfo2KHR, pModes: [^]DeviceGroupPresentModeFlagsKHR) -> Result +ProcGetDeviceGroupSurfacePresentModesKHR :: #type proc "system" (device: Device, surface: SurfaceKHR, pModes: [^]DeviceGroupPresentModeFlagsKHR) -> Result +ProcGetDeviceImageMemoryRequirements :: #type proc "system" (device: Device, pInfo: ^DeviceImageMemoryRequirements, pMemoryRequirements: [^]MemoryRequirements2) +ProcGetDeviceImageMemoryRequirementsKHR :: #type proc "system" (device: Device, pInfo: ^DeviceImageMemoryRequirements, pMemoryRequirements: [^]MemoryRequirements2) +ProcGetDeviceImageSparseMemoryRequirements :: #type proc "system" (device: Device, pInfo: ^DeviceImageMemoryRequirements, pSparseMemoryRequirementCount: ^u32, pSparseMemoryRequirements: [^]SparseImageMemoryRequirements2) +ProcGetDeviceImageSparseMemoryRequirementsKHR :: #type proc "system" (device: Device, pInfo: ^DeviceImageMemoryRequirements, pSparseMemoryRequirementCount: ^u32, pSparseMemoryRequirements: [^]SparseImageMemoryRequirements2) +ProcGetDeviceMemoryCommitment :: #type proc "system" (device: Device, memory: DeviceMemory, pCommittedMemoryInBytes: [^]DeviceSize) +ProcGetDeviceMemoryOpaqueCaptureAddress :: #type proc "system" (device: Device, pInfo: ^DeviceMemoryOpaqueCaptureAddressInfo) -> u64 +ProcGetDeviceMemoryOpaqueCaptureAddressKHR :: #type proc "system" (device: Device, pInfo: ^DeviceMemoryOpaqueCaptureAddressInfo) -> u64 +ProcGetDeviceProcAddr :: #type proc "system" (device: Device, pName: cstring) -> ProcVoidFunction +ProcGetDeviceQueue :: #type proc "system" (device: Device, queueFamilyIndex: u32, queueIndex: u32, pQueue: ^Queue) +ProcGetDeviceQueue2 :: #type proc "system" (device: Device, pQueueInfo: ^DeviceQueueInfo2, pQueue: ^Queue) +ProcGetDeviceSubpassShadingMaxWorkgroupSizeHUAWEI :: #type proc "system" (device: Device, renderpass: RenderPass, pMaxWorkgroupSize: ^Extent2D) -> Result +ProcGetEventStatus :: #type proc "system" (device: Device, event: Event) -> Result +ProcGetFenceFdKHR :: #type proc "system" (device: Device, pGetFdInfo: ^FenceGetFdInfoKHR, pFd: ^c.int) -> Result +ProcGetFenceStatus :: #type proc "system" (device: Device, fence: Fence) -> Result +ProcGetFenceWin32HandleKHR :: #type proc "system" (device: Device, pGetWin32HandleInfo: ^FenceGetWin32HandleInfoKHR, pHandle: ^HANDLE) -> Result +ProcGetGeneratedCommandsMemoryRequirementsNV :: #type proc "system" (device: Device, pInfo: ^GeneratedCommandsMemoryRequirementsInfoNV, pMemoryRequirements: [^]MemoryRequirements2) +ProcGetImageDrmFormatModifierPropertiesEXT :: #type proc "system" (device: Device, image: Image, pProperties: [^]ImageDrmFormatModifierPropertiesEXT) -> Result +ProcGetImageMemoryRequirements :: #type proc "system" (device: Device, image: Image, pMemoryRequirements: [^]MemoryRequirements) +ProcGetImageMemoryRequirements2 :: #type proc "system" (device: Device, pInfo: ^ImageMemoryRequirementsInfo2, pMemoryRequirements: [^]MemoryRequirements2) +ProcGetImageMemoryRequirements2KHR :: #type proc "system" (device: Device, pInfo: ^ImageMemoryRequirementsInfo2, pMemoryRequirements: [^]MemoryRequirements2) +ProcGetImageSparseMemoryRequirements :: #type proc "system" (device: Device, image: Image, pSparseMemoryRequirementCount: ^u32, pSparseMemoryRequirements: [^]SparseImageMemoryRequirements) +ProcGetImageSparseMemoryRequirements2 :: #type proc "system" (device: Device, pInfo: ^ImageSparseMemoryRequirementsInfo2, pSparseMemoryRequirementCount: ^u32, pSparseMemoryRequirements: [^]SparseImageMemoryRequirements2) +ProcGetImageSparseMemoryRequirements2KHR :: #type proc "system" (device: Device, pInfo: ^ImageSparseMemoryRequirementsInfo2, pSparseMemoryRequirementCount: ^u32, pSparseMemoryRequirements: [^]SparseImageMemoryRequirements2) +ProcGetImageSubresourceLayout :: #type proc "system" (device: Device, image: Image, pSubresource: ^ImageSubresource, pLayout: ^SubresourceLayout) +ProcGetImageViewAddressNVX :: #type proc "system" (device: Device, imageView: ImageView, pProperties: [^]ImageViewAddressPropertiesNVX) -> Result +ProcGetImageViewHandleNVX :: #type proc "system" (device: Device, pInfo: ^ImageViewHandleInfoNVX) -> u32 +ProcGetMemoryFdKHR :: #type proc "system" (device: Device, pGetFdInfo: ^MemoryGetFdInfoKHR, pFd: ^c.int) -> Result +ProcGetMemoryFdPropertiesKHR :: #type proc "system" (device: Device, handleType: ExternalMemoryHandleTypeFlags, fd: c.int, pMemoryFdProperties: [^]MemoryFdPropertiesKHR) -> Result +ProcGetMemoryHostPointerPropertiesEXT :: #type proc "system" (device: Device, handleType: ExternalMemoryHandleTypeFlags, pHostPointer: rawptr, pMemoryHostPointerProperties: [^]MemoryHostPointerPropertiesEXT) -> Result +ProcGetMemoryRemoteAddressNV :: #type proc "system" (device: Device, pMemoryGetRemoteAddressInfo: ^MemoryGetRemoteAddressInfoNV, pAddress: [^]RemoteAddressNV) -> Result +ProcGetMemoryWin32HandleKHR :: #type proc "system" (device: Device, pGetWin32HandleInfo: ^MemoryGetWin32HandleInfoKHR, pHandle: ^HANDLE) -> Result +ProcGetMemoryWin32HandleNV :: #type proc "system" (device: Device, memory: DeviceMemory, handleType: ExternalMemoryHandleTypeFlagsNV, pHandle: ^HANDLE) -> Result +ProcGetMemoryWin32HandlePropertiesKHR :: #type proc "system" (device: Device, handleType: ExternalMemoryHandleTypeFlags, handle: HANDLE, pMemoryWin32HandleProperties: [^]MemoryWin32HandlePropertiesKHR) -> Result +ProcGetPastPresentationTimingGOOGLE :: #type proc "system" (device: Device, swapchain: SwapchainKHR, pPresentationTimingCount: ^u32, pPresentationTimings: [^]PastPresentationTimingGOOGLE) -> Result +ProcGetPerformanceParameterINTEL :: #type proc "system" (device: Device, parameter: PerformanceParameterTypeINTEL, pValue: ^PerformanceValueINTEL) -> Result +ProcGetPipelineCacheData :: #type proc "system" (device: Device, pipelineCache: PipelineCache, pDataSize: ^int, pData: rawptr) -> Result +ProcGetPipelineExecutableInternalRepresentationsKHR :: #type proc "system" (device: Device, pExecutableInfo: ^PipelineExecutableInfoKHR, pInternalRepresentationCount: ^u32, pInternalRepresentations: [^]PipelineExecutableInternalRepresentationKHR) -> Result +ProcGetPipelineExecutablePropertiesKHR :: #type proc "system" (device: Device, pPipelineInfo: ^PipelineInfoKHR, pExecutableCount: ^u32, pProperties: [^]PipelineExecutablePropertiesKHR) -> Result +ProcGetPipelineExecutableStatisticsKHR :: #type proc "system" (device: Device, pExecutableInfo: ^PipelineExecutableInfoKHR, pStatisticCount: ^u32, pStatistics: [^]PipelineExecutableStatisticKHR) -> Result +ProcGetPrivateData :: #type proc "system" (device: Device, objectType: ObjectType, objectHandle: u64, privateDataSlot: PrivateDataSlot, pData: ^u64) +ProcGetPrivateDataEXT :: #type proc "system" (device: Device, objectType: ObjectType, objectHandle: u64, privateDataSlot: PrivateDataSlot, pData: ^u64) +ProcGetQueryPoolResults :: #type proc "system" (device: Device, queryPool: QueryPool, firstQuery: u32, queryCount: u32, dataSize: int, pData: rawptr, stride: DeviceSize, flags: QueryResultFlags) -> Result +ProcGetQueueCheckpointData2NV :: #type proc "system" (queue: Queue, pCheckpointDataCount: ^u32, pCheckpointData: ^CheckpointData2NV) +ProcGetQueueCheckpointDataNV :: #type proc "system" (queue: Queue, pCheckpointDataCount: ^u32, pCheckpointData: ^CheckpointDataNV) +ProcGetRayTracingCaptureReplayShaderGroupHandlesKHR :: #type proc "system" (device: Device, pipeline: Pipeline, firstGroup: u32, groupCount: u32, dataSize: int, pData: rawptr) -> Result +ProcGetRayTracingShaderGroupHandlesKHR :: #type proc "system" (device: Device, pipeline: Pipeline, firstGroup: u32, groupCount: u32, dataSize: int, pData: rawptr) -> Result +ProcGetRayTracingShaderGroupHandlesNV :: #type proc "system" (device: Device, pipeline: Pipeline, firstGroup: u32, groupCount: u32, dataSize: int, pData: rawptr) -> Result +ProcGetRayTracingShaderGroupStackSizeKHR :: #type proc "system" (device: Device, pipeline: Pipeline, group: u32, groupShader: ShaderGroupShaderKHR) -> DeviceSize +ProcGetRefreshCycleDurationGOOGLE :: #type proc "system" (device: Device, swapchain: SwapchainKHR, pDisplayTimingProperties: [^]RefreshCycleDurationGOOGLE) -> Result +ProcGetRenderAreaGranularity :: #type proc "system" (device: Device, renderPass: RenderPass, pGranularity: ^Extent2D) +ProcGetSemaphoreCounterValue :: #type proc "system" (device: Device, semaphore: Semaphore, pValue: ^u64) -> Result +ProcGetSemaphoreCounterValueKHR :: #type proc "system" (device: Device, semaphore: Semaphore, pValue: ^u64) -> Result +ProcGetSemaphoreFdKHR :: #type proc "system" (device: Device, pGetFdInfo: ^SemaphoreGetFdInfoKHR, pFd: ^c.int) -> Result +ProcGetSemaphoreWin32HandleKHR :: #type proc "system" (device: Device, pGetWin32HandleInfo: ^SemaphoreGetWin32HandleInfoKHR, pHandle: ^HANDLE) -> Result +ProcGetShaderInfoAMD :: #type proc "system" (device: Device, pipeline: Pipeline, shaderStage: ShaderStageFlags, infoType: ShaderInfoTypeAMD, pInfoSize: ^int, pInfo: rawptr) -> Result +ProcGetSwapchainCounterEXT :: #type proc "system" (device: Device, swapchain: SwapchainKHR, counter: SurfaceCounterFlagsEXT, pCounterValue: ^u64) -> Result +ProcGetSwapchainImagesKHR :: #type proc "system" (device: Device, swapchain: SwapchainKHR, pSwapchainImageCount: ^u32, pSwapchainImages: [^]Image) -> Result +ProcGetSwapchainStatusKHR :: #type proc "system" (device: Device, swapchain: SwapchainKHR) -> Result +ProcGetValidationCacheDataEXT :: #type proc "system" (device: Device, validationCache: ValidationCacheEXT, pDataSize: ^int, pData: rawptr) -> Result +ProcImportFenceFdKHR :: #type proc "system" (device: Device, pImportFenceFdInfo: ^ImportFenceFdInfoKHR) -> Result +ProcImportFenceWin32HandleKHR :: #type proc "system" (device: Device, pImportFenceWin32HandleInfo: ^ImportFenceWin32HandleInfoKHR) -> Result +ProcImportSemaphoreFdKHR :: #type proc "system" (device: Device, pImportSemaphoreFdInfo: ^ImportSemaphoreFdInfoKHR) -> Result +ProcImportSemaphoreWin32HandleKHR :: #type proc "system" (device: Device, pImportSemaphoreWin32HandleInfo: ^ImportSemaphoreWin32HandleInfoKHR) -> Result +ProcInitializePerformanceApiINTEL :: #type proc "system" (device: Device, pInitializeInfo: ^InitializePerformanceApiInfoINTEL) -> Result +ProcInvalidateMappedMemoryRanges :: #type proc "system" (device: Device, memoryRangeCount: u32, pMemoryRanges: [^]MappedMemoryRange) -> Result +ProcMapMemory :: #type proc "system" (device: Device, memory: DeviceMemory, offset: DeviceSize, size: DeviceSize, flags: MemoryMapFlags, ppData: ^rawptr) -> Result +ProcMergePipelineCaches :: #type proc "system" (device: Device, dstCache: PipelineCache, srcCacheCount: u32, pSrcCaches: [^]PipelineCache) -> Result +ProcMergeValidationCachesEXT :: #type proc "system" (device: Device, dstCache: ValidationCacheEXT, srcCacheCount: u32, pSrcCaches: [^]ValidationCacheEXT) -> Result +ProcQueueBeginDebugUtilsLabelEXT :: #type proc "system" (queue: Queue, pLabelInfo: ^DebugUtilsLabelEXT) +ProcQueueBindSparse :: #type proc "system" (queue: Queue, bindInfoCount: u32, pBindInfo: ^BindSparseInfo, fence: Fence) -> Result +ProcQueueEndDebugUtilsLabelEXT :: #type proc "system" (queue: Queue) +ProcQueueInsertDebugUtilsLabelEXT :: #type proc "system" (queue: Queue, pLabelInfo: ^DebugUtilsLabelEXT) +ProcQueuePresentKHR :: #type proc "system" (queue: Queue, pPresentInfo: ^PresentInfoKHR) -> Result +ProcQueueSetPerformanceConfigurationINTEL :: #type proc "system" (queue: Queue, configuration: PerformanceConfigurationINTEL) -> Result +ProcQueueSubmit :: #type proc "system" (queue: Queue, submitCount: u32, pSubmits: [^]SubmitInfo, fence: Fence) -> Result +ProcQueueSubmit2 :: #type proc "system" (queue: Queue, submitCount: u32, pSubmits: [^]SubmitInfo2, fence: Fence) -> Result +ProcQueueSubmit2KHR :: #type proc "system" (queue: Queue, submitCount: u32, pSubmits: [^]SubmitInfo2, fence: Fence) -> Result +ProcQueueWaitIdle :: #type proc "system" (queue: Queue) -> Result +ProcRegisterDeviceEventEXT :: #type proc "system" (device: Device, pDeviceEventInfo: ^DeviceEventInfoEXT, pAllocator: ^AllocationCallbacks, pFence: ^Fence) -> Result +ProcRegisterDisplayEventEXT :: #type proc "system" (device: Device, display: DisplayKHR, pDisplayEventInfo: ^DisplayEventInfoEXT, pAllocator: ^AllocationCallbacks, pFence: ^Fence) -> Result +ProcReleaseFullScreenExclusiveModeEXT :: #type proc "system" (device: Device, swapchain: SwapchainKHR) -> Result +ProcReleasePerformanceConfigurationINTEL :: #type proc "system" (device: Device, configuration: PerformanceConfigurationINTEL) -> Result +ProcReleaseProfilingLockKHR :: #type proc "system" (device: Device) +ProcResetCommandBuffer :: #type proc "system" (commandBuffer: CommandBuffer, flags: CommandBufferResetFlags) -> Result +ProcResetCommandPool :: #type proc "system" (device: Device, commandPool: CommandPool, flags: CommandPoolResetFlags) -> Result +ProcResetDescriptorPool :: #type proc "system" (device: Device, descriptorPool: DescriptorPool, flags: DescriptorPoolResetFlags) -> Result +ProcResetEvent :: #type proc "system" (device: Device, event: Event) -> Result +ProcResetFences :: #type proc "system" (device: Device, fenceCount: u32, pFences: [^]Fence) -> Result +ProcResetQueryPool :: #type proc "system" (device: Device, queryPool: QueryPool, firstQuery: u32, queryCount: u32) +ProcResetQueryPoolEXT :: #type proc "system" (device: Device, queryPool: QueryPool, firstQuery: u32, queryCount: u32) +ProcSetDebugUtilsObjectNameEXT :: #type proc "system" (device: Device, pNameInfo: ^DebugUtilsObjectNameInfoEXT) -> Result +ProcSetDebugUtilsObjectTagEXT :: #type proc "system" (device: Device, pTagInfo: ^DebugUtilsObjectTagInfoEXT) -> Result +ProcSetDeviceMemoryPriorityEXT :: #type proc "system" (device: Device, memory: DeviceMemory, priority: f32) +ProcSetEvent :: #type proc "system" (device: Device, event: Event) -> Result +ProcSetHdrMetadataEXT :: #type proc "system" (device: Device, swapchainCount: u32, pSwapchains: [^]SwapchainKHR, pMetadata: ^HdrMetadataEXT) +ProcSetLocalDimmingAMD :: #type proc "system" (device: Device, swapChain: SwapchainKHR, localDimmingEnable: b32) +ProcSetPrivateData :: #type proc "system" (device: Device, objectType: ObjectType, objectHandle: u64, privateDataSlot: PrivateDataSlot, data: u64) -> Result +ProcSetPrivateDataEXT :: #type proc "system" (device: Device, objectType: ObjectType, objectHandle: u64, privateDataSlot: PrivateDataSlot, data: u64) -> Result +ProcSignalSemaphore :: #type proc "system" (device: Device, pSignalInfo: ^SemaphoreSignalInfo) -> Result +ProcSignalSemaphoreKHR :: #type proc "system" (device: Device, pSignalInfo: ^SemaphoreSignalInfo) -> Result +ProcTrimCommandPool :: #type proc "system" (device: Device, commandPool: CommandPool, flags: CommandPoolTrimFlags) +ProcTrimCommandPoolKHR :: #type proc "system" (device: Device, commandPool: CommandPool, flags: CommandPoolTrimFlags) +ProcUninitializePerformanceApiINTEL :: #type proc "system" (device: Device) +ProcUnmapMemory :: #type proc "system" (device: Device, memory: DeviceMemory) +ProcUpdateDescriptorSetWithTemplate :: #type proc "system" (device: Device, descriptorSet: DescriptorSet, descriptorUpdateTemplate: DescriptorUpdateTemplate, pData: rawptr) +ProcUpdateDescriptorSetWithTemplateKHR :: #type proc "system" (device: Device, descriptorSet: DescriptorSet, descriptorUpdateTemplate: DescriptorUpdateTemplate, pData: rawptr) +ProcUpdateDescriptorSets :: #type proc "system" (device: Device, descriptorWriteCount: u32, pDescriptorWrites: [^]WriteDescriptorSet, descriptorCopyCount: u32, pDescriptorCopies: [^]CopyDescriptorSet) +ProcWaitForFences :: #type proc "system" (device: Device, fenceCount: u32, pFences: [^]Fence, waitAll: b32, timeout: u64) -> Result +ProcWaitForPresentKHR :: #type proc "system" (device: Device, swapchain: SwapchainKHR, presentId: u64, timeout: u64) -> Result +ProcWaitSemaphores :: #type proc "system" (device: Device, pWaitInfo: ^SemaphoreWaitInfo, timeout: u64) -> Result +ProcWaitSemaphoresKHR :: #type proc "system" (device: Device, pWaitInfo: ^SemaphoreWaitInfo, timeout: u64) -> Result +ProcWriteAccelerationStructuresPropertiesKHR :: #type proc "system" (device: Device, accelerationStructureCount: u32, pAccelerationStructures: [^]AccelerationStructureKHR, queryType: QueryType, dataSize: int, pData: rawptr, stride: int) -> Result -// Device Procedures -GetDeviceProcAddr: ProcGetDeviceProcAddr -DestroyDevice: ProcDestroyDevice -GetDeviceQueue: ProcGetDeviceQueue -QueueSubmit: ProcQueueSubmit -QueueWaitIdle: ProcQueueWaitIdle -DeviceWaitIdle: ProcDeviceWaitIdle -AllocateMemory: ProcAllocateMemory -FreeMemory: ProcFreeMemory -MapMemory: ProcMapMemory -UnmapMemory: ProcUnmapMemory -FlushMappedMemoryRanges: ProcFlushMappedMemoryRanges -InvalidateMappedMemoryRanges: ProcInvalidateMappedMemoryRanges -GetDeviceMemoryCommitment: ProcGetDeviceMemoryCommitment -BindBufferMemory: ProcBindBufferMemory -BindImageMemory: ProcBindImageMemory -GetBufferMemoryRequirements: ProcGetBufferMemoryRequirements -GetImageMemoryRequirements: ProcGetImageMemoryRequirements -GetImageSparseMemoryRequirements: ProcGetImageSparseMemoryRequirements -QueueBindSparse: ProcQueueBindSparse -CreateFence: ProcCreateFence -DestroyFence: ProcDestroyFence -ResetFences: ProcResetFences -GetFenceStatus: ProcGetFenceStatus -WaitForFences: ProcWaitForFences -CreateSemaphore: ProcCreateSemaphore -DestroySemaphore: ProcDestroySemaphore -CreateEvent: ProcCreateEvent -DestroyEvent: ProcDestroyEvent -GetEventStatus: ProcGetEventStatus -SetEvent: ProcSetEvent -ResetEvent: ProcResetEvent -CreateQueryPool: ProcCreateQueryPool -DestroyQueryPool: ProcDestroyQueryPool -GetQueryPoolResults: ProcGetQueryPoolResults -CreateBuffer: ProcCreateBuffer -DestroyBuffer: ProcDestroyBuffer -CreateBufferView: ProcCreateBufferView -DestroyBufferView: ProcDestroyBufferView -CreateImage: ProcCreateImage -DestroyImage: ProcDestroyImage -GetImageSubresourceLayout: ProcGetImageSubresourceLayout -CreateImageView: ProcCreateImageView -DestroyImageView: ProcDestroyImageView -CreateShaderModule: ProcCreateShaderModule -DestroyShaderModule: ProcDestroyShaderModule -CreatePipelineCache: ProcCreatePipelineCache -DestroyPipelineCache: ProcDestroyPipelineCache -GetPipelineCacheData: ProcGetPipelineCacheData -MergePipelineCaches: ProcMergePipelineCaches -CreateGraphicsPipelines: ProcCreateGraphicsPipelines -CreateComputePipelines: ProcCreateComputePipelines -DestroyPipeline: ProcDestroyPipeline -CreatePipelineLayout: ProcCreatePipelineLayout -DestroyPipelineLayout: ProcDestroyPipelineLayout -CreateSampler: ProcCreateSampler -DestroySampler: ProcDestroySampler -CreateDescriptorSetLayout: ProcCreateDescriptorSetLayout -DestroyDescriptorSetLayout: ProcDestroyDescriptorSetLayout -CreateDescriptorPool: ProcCreateDescriptorPool -DestroyDescriptorPool: ProcDestroyDescriptorPool -ResetDescriptorPool: ProcResetDescriptorPool -AllocateDescriptorSets: ProcAllocateDescriptorSets -FreeDescriptorSets: ProcFreeDescriptorSets -UpdateDescriptorSets: ProcUpdateDescriptorSets -CreateFramebuffer: ProcCreateFramebuffer -DestroyFramebuffer: ProcDestroyFramebuffer -CreateRenderPass: ProcCreateRenderPass -DestroyRenderPass: ProcDestroyRenderPass -GetRenderAreaGranularity: ProcGetRenderAreaGranularity -CreateCommandPool: ProcCreateCommandPool -DestroyCommandPool: ProcDestroyCommandPool -ResetCommandPool: ProcResetCommandPool -AllocateCommandBuffers: ProcAllocateCommandBuffers -FreeCommandBuffers: ProcFreeCommandBuffers -BeginCommandBuffer: ProcBeginCommandBuffer -EndCommandBuffer: ProcEndCommandBuffer -ResetCommandBuffer: ProcResetCommandBuffer -CmdBindPipeline: ProcCmdBindPipeline -CmdSetViewport: ProcCmdSetViewport -CmdSetScissor: ProcCmdSetScissor -CmdSetLineWidth: ProcCmdSetLineWidth -CmdSetDepthBias: ProcCmdSetDepthBias -CmdSetBlendConstants: ProcCmdSetBlendConstants -CmdSetDepthBounds: ProcCmdSetDepthBounds -CmdSetStencilCompareMask: ProcCmdSetStencilCompareMask -CmdSetStencilWriteMask: ProcCmdSetStencilWriteMask -CmdSetStencilReference: ProcCmdSetStencilReference -CmdBindDescriptorSets: ProcCmdBindDescriptorSets -CmdBindIndexBuffer: ProcCmdBindIndexBuffer -CmdBindVertexBuffers: ProcCmdBindVertexBuffers -CmdDraw: ProcCmdDraw -CmdDrawIndexed: ProcCmdDrawIndexed -CmdDrawIndirect: ProcCmdDrawIndirect -CmdDrawIndexedIndirect: ProcCmdDrawIndexedIndirect -CmdDispatch: ProcCmdDispatch -CmdDispatchIndirect: ProcCmdDispatchIndirect -CmdCopyBuffer: ProcCmdCopyBuffer -CmdCopyImage: ProcCmdCopyImage -CmdBlitImage: ProcCmdBlitImage -CmdCopyBufferToImage: ProcCmdCopyBufferToImage -CmdCopyImageToBuffer: ProcCmdCopyImageToBuffer -CmdUpdateBuffer: ProcCmdUpdateBuffer -CmdFillBuffer: ProcCmdFillBuffer -CmdClearColorImage: ProcCmdClearColorImage -CmdClearDepthStencilImage: ProcCmdClearDepthStencilImage -CmdClearAttachments: ProcCmdClearAttachments -CmdResolveImage: ProcCmdResolveImage -CmdSetEvent: ProcCmdSetEvent -CmdResetEvent: ProcCmdResetEvent -CmdWaitEvents: ProcCmdWaitEvents -CmdPipelineBarrier: ProcCmdPipelineBarrier -CmdBeginQuery: ProcCmdBeginQuery -CmdEndQuery: ProcCmdEndQuery -CmdResetQueryPool: ProcCmdResetQueryPool -CmdWriteTimestamp: ProcCmdWriteTimestamp -CmdCopyQueryPoolResults: ProcCmdCopyQueryPoolResults -CmdPushConstants: ProcCmdPushConstants -CmdBeginRenderPass: ProcCmdBeginRenderPass -CmdNextSubpass: ProcCmdNextSubpass -CmdEndRenderPass: ProcCmdEndRenderPass -CmdExecuteCommands: ProcCmdExecuteCommands -BindBufferMemory2: ProcBindBufferMemory2 -BindImageMemory2: ProcBindImageMemory2 -GetDeviceGroupPeerMemoryFeatures: ProcGetDeviceGroupPeerMemoryFeatures -CmdSetDeviceMask: ProcCmdSetDeviceMask -CmdDispatchBase: ProcCmdDispatchBase -GetImageMemoryRequirements2: ProcGetImageMemoryRequirements2 -GetBufferMemoryRequirements2: ProcGetBufferMemoryRequirements2 -GetImageSparseMemoryRequirements2: ProcGetImageSparseMemoryRequirements2 -TrimCommandPool: ProcTrimCommandPool -GetDeviceQueue2: ProcGetDeviceQueue2 -CreateSamplerYcbcrConversion: ProcCreateSamplerYcbcrConversion -DestroySamplerYcbcrConversion: ProcDestroySamplerYcbcrConversion -CreateDescriptorUpdateTemplate: ProcCreateDescriptorUpdateTemplate -DestroyDescriptorUpdateTemplate: ProcDestroyDescriptorUpdateTemplate -UpdateDescriptorSetWithTemplate: ProcUpdateDescriptorSetWithTemplate -GetDescriptorSetLayoutSupport: ProcGetDescriptorSetLayoutSupport -CmdDrawIndirectCount: ProcCmdDrawIndirectCount -CmdDrawIndexedIndirectCount: ProcCmdDrawIndexedIndirectCount -CreateRenderPass2: ProcCreateRenderPass2 -CmdBeginRenderPass2: ProcCmdBeginRenderPass2 -CmdNextSubpass2: ProcCmdNextSubpass2 -CmdEndRenderPass2: ProcCmdEndRenderPass2 -ResetQueryPool: ProcResetQueryPool -GetSemaphoreCounterValue: ProcGetSemaphoreCounterValue -WaitSemaphores: ProcWaitSemaphores -SignalSemaphore: ProcSignalSemaphore -GetBufferDeviceAddress: ProcGetBufferDeviceAddress -GetBufferOpaqueCaptureAddress: ProcGetBufferOpaqueCaptureAddress -GetDeviceMemoryOpaqueCaptureAddress: ProcGetDeviceMemoryOpaqueCaptureAddress -CreateSwapchainKHR: ProcCreateSwapchainKHR -DestroySwapchainKHR: ProcDestroySwapchainKHR -GetSwapchainImagesKHR: ProcGetSwapchainImagesKHR -AcquireNextImageKHR: ProcAcquireNextImageKHR -QueuePresentKHR: ProcQueuePresentKHR -GetDeviceGroupPresentCapabilitiesKHR: ProcGetDeviceGroupPresentCapabilitiesKHR -GetDeviceGroupSurfacePresentModesKHR: ProcGetDeviceGroupSurfacePresentModesKHR -AcquireNextImage2KHR: ProcAcquireNextImage2KHR -CreateSharedSwapchainsKHR: ProcCreateSharedSwapchainsKHR -GetDeviceGroupPeerMemoryFeaturesKHR: ProcGetDeviceGroupPeerMemoryFeaturesKHR -CmdSetDeviceMaskKHR: ProcCmdSetDeviceMaskKHR -CmdDispatchBaseKHR: ProcCmdDispatchBaseKHR -TrimCommandPoolKHR: ProcTrimCommandPoolKHR -GetMemoryFdKHR: ProcGetMemoryFdKHR -GetMemoryFdPropertiesKHR: ProcGetMemoryFdPropertiesKHR -ImportSemaphoreFdKHR: ProcImportSemaphoreFdKHR -GetSemaphoreFdKHR: ProcGetSemaphoreFdKHR -CmdPushDescriptorSetKHR: ProcCmdPushDescriptorSetKHR -CmdPushDescriptorSetWithTemplateKHR: ProcCmdPushDescriptorSetWithTemplateKHR -CreateDescriptorUpdateTemplateKHR: ProcCreateDescriptorUpdateTemplateKHR -DestroyDescriptorUpdateTemplateKHR: ProcDestroyDescriptorUpdateTemplateKHR -UpdateDescriptorSetWithTemplateKHR: ProcUpdateDescriptorSetWithTemplateKHR -CreateRenderPass2KHR: ProcCreateRenderPass2KHR -CmdBeginRenderPass2KHR: ProcCmdBeginRenderPass2KHR -CmdNextSubpass2KHR: ProcCmdNextSubpass2KHR -CmdEndRenderPass2KHR: ProcCmdEndRenderPass2KHR -GetSwapchainStatusKHR: ProcGetSwapchainStatusKHR -ImportFenceFdKHR: ProcImportFenceFdKHR -GetFenceFdKHR: ProcGetFenceFdKHR -AcquireProfilingLockKHR: ProcAcquireProfilingLockKHR -ReleaseProfilingLockKHR: ProcReleaseProfilingLockKHR -GetImageMemoryRequirements2KHR: ProcGetImageMemoryRequirements2KHR -GetBufferMemoryRequirements2KHR: ProcGetBufferMemoryRequirements2KHR -GetImageSparseMemoryRequirements2KHR: ProcGetImageSparseMemoryRequirements2KHR -CreateSamplerYcbcrConversionKHR: ProcCreateSamplerYcbcrConversionKHR -DestroySamplerYcbcrConversionKHR: ProcDestroySamplerYcbcrConversionKHR -BindBufferMemory2KHR: ProcBindBufferMemory2KHR -BindImageMemory2KHR: ProcBindImageMemory2KHR -GetDescriptorSetLayoutSupportKHR: ProcGetDescriptorSetLayoutSupportKHR -CmdDrawIndirectCountKHR: ProcCmdDrawIndirectCountKHR -CmdDrawIndexedIndirectCountKHR: ProcCmdDrawIndexedIndirectCountKHR -GetSemaphoreCounterValueKHR: ProcGetSemaphoreCounterValueKHR -WaitSemaphoresKHR: ProcWaitSemaphoresKHR -SignalSemaphoreKHR: ProcSignalSemaphoreKHR -CmdSetFragmentShadingRateKHR: ProcCmdSetFragmentShadingRateKHR -WaitForPresentKHR: ProcWaitForPresentKHR -GetBufferDeviceAddressKHR: ProcGetBufferDeviceAddressKHR -GetBufferOpaqueCaptureAddressKHR: ProcGetBufferOpaqueCaptureAddressKHR -GetDeviceMemoryOpaqueCaptureAddressKHR: ProcGetDeviceMemoryOpaqueCaptureAddressKHR -CreateDeferredOperationKHR: ProcCreateDeferredOperationKHR -DestroyDeferredOperationKHR: ProcDestroyDeferredOperationKHR -GetDeferredOperationMaxConcurrencyKHR: ProcGetDeferredOperationMaxConcurrencyKHR -GetDeferredOperationResultKHR: ProcGetDeferredOperationResultKHR -DeferredOperationJoinKHR: ProcDeferredOperationJoinKHR -GetPipelineExecutablePropertiesKHR: ProcGetPipelineExecutablePropertiesKHR -GetPipelineExecutableStatisticsKHR: ProcGetPipelineExecutableStatisticsKHR -GetPipelineExecutableInternalRepresentationsKHR: ProcGetPipelineExecutableInternalRepresentationsKHR -CmdSetEvent2KHR: ProcCmdSetEvent2KHR -CmdResetEvent2KHR: ProcCmdResetEvent2KHR -CmdWaitEvents2KHR: ProcCmdWaitEvents2KHR -CmdPipelineBarrier2KHR: ProcCmdPipelineBarrier2KHR -CmdWriteTimestamp2KHR: ProcCmdWriteTimestamp2KHR -QueueSubmit2KHR: ProcQueueSubmit2KHR -CmdWriteBufferMarker2AMD: ProcCmdWriteBufferMarker2AMD -GetQueueCheckpointData2NV: ProcGetQueueCheckpointData2NV -CmdCopyBuffer2KHR: ProcCmdCopyBuffer2KHR -CmdCopyImage2KHR: ProcCmdCopyImage2KHR -CmdCopyBufferToImage2KHR: ProcCmdCopyBufferToImage2KHR -CmdCopyImageToBuffer2KHR: ProcCmdCopyImageToBuffer2KHR -CmdBlitImage2KHR: ProcCmdBlitImage2KHR -CmdResolveImage2KHR: ProcCmdResolveImage2KHR -DebugMarkerSetObjectTagEXT: ProcDebugMarkerSetObjectTagEXT -DebugMarkerSetObjectNameEXT: ProcDebugMarkerSetObjectNameEXT -CmdDebugMarkerBeginEXT: ProcCmdDebugMarkerBeginEXT -CmdDebugMarkerEndEXT: ProcCmdDebugMarkerEndEXT -CmdDebugMarkerInsertEXT: ProcCmdDebugMarkerInsertEXT -CmdBindTransformFeedbackBuffersEXT: ProcCmdBindTransformFeedbackBuffersEXT -CmdBeginTransformFeedbackEXT: ProcCmdBeginTransformFeedbackEXT -CmdEndTransformFeedbackEXT: ProcCmdEndTransformFeedbackEXT -CmdBeginQueryIndexedEXT: ProcCmdBeginQueryIndexedEXT -CmdEndQueryIndexedEXT: ProcCmdEndQueryIndexedEXT -CmdDrawIndirectByteCountEXT: ProcCmdDrawIndirectByteCountEXT -CreateCuModuleNVX: ProcCreateCuModuleNVX -CreateCuFunctionNVX: ProcCreateCuFunctionNVX -DestroyCuModuleNVX: ProcDestroyCuModuleNVX -DestroyCuFunctionNVX: ProcDestroyCuFunctionNVX -CmdCuLaunchKernelNVX: ProcCmdCuLaunchKernelNVX -GetImageViewHandleNVX: ProcGetImageViewHandleNVX -GetImageViewAddressNVX: ProcGetImageViewAddressNVX -CmdDrawIndirectCountAMD: ProcCmdDrawIndirectCountAMD -CmdDrawIndexedIndirectCountAMD: ProcCmdDrawIndexedIndirectCountAMD -GetShaderInfoAMD: ProcGetShaderInfoAMD -CmdBeginConditionalRenderingEXT: ProcCmdBeginConditionalRenderingEXT -CmdEndConditionalRenderingEXT: ProcCmdEndConditionalRenderingEXT -CmdSetViewportWScalingNV: ProcCmdSetViewportWScalingNV -DisplayPowerControlEXT: ProcDisplayPowerControlEXT -RegisterDeviceEventEXT: ProcRegisterDeviceEventEXT -RegisterDisplayEventEXT: ProcRegisterDisplayEventEXT -GetSwapchainCounterEXT: ProcGetSwapchainCounterEXT -GetRefreshCycleDurationGOOGLE: ProcGetRefreshCycleDurationGOOGLE -GetPastPresentationTimingGOOGLE: ProcGetPastPresentationTimingGOOGLE -CmdSetDiscardRectangleEXT: ProcCmdSetDiscardRectangleEXT -SetHdrMetadataEXT: ProcSetHdrMetadataEXT -SetDebugUtilsObjectNameEXT: ProcSetDebugUtilsObjectNameEXT -SetDebugUtilsObjectTagEXT: ProcSetDebugUtilsObjectTagEXT -QueueBeginDebugUtilsLabelEXT: ProcQueueBeginDebugUtilsLabelEXT -QueueEndDebugUtilsLabelEXT: ProcQueueEndDebugUtilsLabelEXT -QueueInsertDebugUtilsLabelEXT: ProcQueueInsertDebugUtilsLabelEXT -CmdBeginDebugUtilsLabelEXT: ProcCmdBeginDebugUtilsLabelEXT -CmdEndDebugUtilsLabelEXT: ProcCmdEndDebugUtilsLabelEXT -CmdInsertDebugUtilsLabelEXT: ProcCmdInsertDebugUtilsLabelEXT -CmdSetSampleLocationsEXT: ProcCmdSetSampleLocationsEXT -GetImageDrmFormatModifierPropertiesEXT: ProcGetImageDrmFormatModifierPropertiesEXT -CreateValidationCacheEXT: ProcCreateValidationCacheEXT -DestroyValidationCacheEXT: ProcDestroyValidationCacheEXT -MergeValidationCachesEXT: ProcMergeValidationCachesEXT -GetValidationCacheDataEXT: ProcGetValidationCacheDataEXT -CmdBindShadingRateImageNV: ProcCmdBindShadingRateImageNV -CmdSetViewportShadingRatePaletteNV: ProcCmdSetViewportShadingRatePaletteNV -CmdSetCoarseSampleOrderNV: ProcCmdSetCoarseSampleOrderNV -CreateAccelerationStructureNV: ProcCreateAccelerationStructureNV -DestroyAccelerationStructureNV: ProcDestroyAccelerationStructureNV -GetAccelerationStructureMemoryRequirementsNV: ProcGetAccelerationStructureMemoryRequirementsNV -BindAccelerationStructureMemoryNV: ProcBindAccelerationStructureMemoryNV -CmdBuildAccelerationStructureNV: ProcCmdBuildAccelerationStructureNV -CmdCopyAccelerationStructureNV: ProcCmdCopyAccelerationStructureNV -CmdTraceRaysNV: ProcCmdTraceRaysNV -CreateRayTracingPipelinesNV: ProcCreateRayTracingPipelinesNV -GetRayTracingShaderGroupHandlesKHR: ProcGetRayTracingShaderGroupHandlesKHR -GetRayTracingShaderGroupHandlesNV: ProcGetRayTracingShaderGroupHandlesNV -GetAccelerationStructureHandleNV: ProcGetAccelerationStructureHandleNV -CmdWriteAccelerationStructuresPropertiesNV: ProcCmdWriteAccelerationStructuresPropertiesNV -CompileDeferredNV: ProcCompileDeferredNV -GetMemoryHostPointerPropertiesEXT: ProcGetMemoryHostPointerPropertiesEXT -CmdWriteBufferMarkerAMD: ProcCmdWriteBufferMarkerAMD -GetCalibratedTimestampsEXT: ProcGetCalibratedTimestampsEXT -CmdDrawMeshTasksNV: ProcCmdDrawMeshTasksNV -CmdDrawMeshTasksIndirectNV: ProcCmdDrawMeshTasksIndirectNV -CmdDrawMeshTasksIndirectCountNV: ProcCmdDrawMeshTasksIndirectCountNV -CmdSetExclusiveScissorNV: ProcCmdSetExclusiveScissorNV -CmdSetCheckpointNV: ProcCmdSetCheckpointNV -GetQueueCheckpointDataNV: ProcGetQueueCheckpointDataNV -InitializePerformanceApiINTEL: ProcInitializePerformanceApiINTEL -UninitializePerformanceApiINTEL: ProcUninitializePerformanceApiINTEL -CmdSetPerformanceMarkerINTEL: ProcCmdSetPerformanceMarkerINTEL -CmdSetPerformanceStreamMarkerINTEL: ProcCmdSetPerformanceStreamMarkerINTEL -CmdSetPerformanceOverrideINTEL: ProcCmdSetPerformanceOverrideINTEL -AcquirePerformanceConfigurationINTEL: ProcAcquirePerformanceConfigurationINTEL -ReleasePerformanceConfigurationINTEL: ProcReleasePerformanceConfigurationINTEL -QueueSetPerformanceConfigurationINTEL: ProcQueueSetPerformanceConfigurationINTEL -GetPerformanceParameterINTEL: ProcGetPerformanceParameterINTEL -SetLocalDimmingAMD: ProcSetLocalDimmingAMD -GetBufferDeviceAddressEXT: ProcGetBufferDeviceAddressEXT -CmdSetLineStippleEXT: ProcCmdSetLineStippleEXT -ResetQueryPoolEXT: ProcResetQueryPoolEXT -CmdSetCullModeEXT: ProcCmdSetCullModeEXT -CmdSetFrontFaceEXT: ProcCmdSetFrontFaceEXT -CmdSetPrimitiveTopologyEXT: ProcCmdSetPrimitiveTopologyEXT -CmdSetViewportWithCountEXT: ProcCmdSetViewportWithCountEXT -CmdSetScissorWithCountEXT: ProcCmdSetScissorWithCountEXT -CmdBindVertexBuffers2EXT: ProcCmdBindVertexBuffers2EXT -CmdSetDepthTestEnableEXT: ProcCmdSetDepthTestEnableEXT -CmdSetDepthWriteEnableEXT: ProcCmdSetDepthWriteEnableEXT -CmdSetDepthCompareOpEXT: ProcCmdSetDepthCompareOpEXT -CmdSetDepthBoundsTestEnableEXT: ProcCmdSetDepthBoundsTestEnableEXT -CmdSetStencilTestEnableEXT: ProcCmdSetStencilTestEnableEXT -CmdSetStencilOpEXT: ProcCmdSetStencilOpEXT -GetGeneratedCommandsMemoryRequirementsNV: ProcGetGeneratedCommandsMemoryRequirementsNV -CmdPreprocessGeneratedCommandsNV: ProcCmdPreprocessGeneratedCommandsNV -CmdExecuteGeneratedCommandsNV: ProcCmdExecuteGeneratedCommandsNV -CmdBindPipelineShaderGroupNV: ProcCmdBindPipelineShaderGroupNV -CreateIndirectCommandsLayoutNV: ProcCreateIndirectCommandsLayoutNV -DestroyIndirectCommandsLayoutNV: ProcDestroyIndirectCommandsLayoutNV -CreatePrivateDataSlotEXT: ProcCreatePrivateDataSlotEXT -DestroyPrivateDataSlotEXT: ProcDestroyPrivateDataSlotEXT -SetPrivateDataEXT: ProcSetPrivateDataEXT -GetPrivateDataEXT: ProcGetPrivateDataEXT -CmdSetFragmentShadingRateEnumNV: ProcCmdSetFragmentShadingRateEnumNV -CmdSetVertexInputEXT: ProcCmdSetVertexInputEXT -GetDeviceSubpassShadingMaxWorkgroupSizeHUAWEI: ProcGetDeviceSubpassShadingMaxWorkgroupSizeHUAWEI -CmdSubpassShadingHUAWEI: ProcCmdSubpassShadingHUAWEI -CmdBindInvocationMaskHUAWEI: ProcCmdBindInvocationMaskHUAWEI -GetMemoryRemoteAddressNV: ProcGetMemoryRemoteAddressNV -CmdSetPatchControlPointsEXT: ProcCmdSetPatchControlPointsEXT -CmdSetRasterizerDiscardEnableEXT: ProcCmdSetRasterizerDiscardEnableEXT -CmdSetDepthBiasEnableEXT: ProcCmdSetDepthBiasEnableEXT -CmdSetLogicOpEXT: ProcCmdSetLogicOpEXT -CmdSetPrimitiveRestartEnableEXT: ProcCmdSetPrimitiveRestartEnableEXT -CmdDrawMultiEXT: ProcCmdDrawMultiEXT -CmdDrawMultiIndexedEXT: ProcCmdDrawMultiIndexedEXT -SetDeviceMemoryPriorityEXT: ProcSetDeviceMemoryPriorityEXT -CreateAccelerationStructureKHR: ProcCreateAccelerationStructureKHR -DestroyAccelerationStructureKHR: ProcDestroyAccelerationStructureKHR -CmdBuildAccelerationStructuresKHR: ProcCmdBuildAccelerationStructuresKHR -CmdBuildAccelerationStructuresIndirectKHR: ProcCmdBuildAccelerationStructuresIndirectKHR -BuildAccelerationStructuresKHR: ProcBuildAccelerationStructuresKHR -CopyAccelerationStructureKHR: ProcCopyAccelerationStructureKHR -CopyAccelerationStructureToMemoryKHR: ProcCopyAccelerationStructureToMemoryKHR -CopyMemoryToAccelerationStructureKHR: ProcCopyMemoryToAccelerationStructureKHR -WriteAccelerationStructuresPropertiesKHR: ProcWriteAccelerationStructuresPropertiesKHR -CmdCopyAccelerationStructureKHR: ProcCmdCopyAccelerationStructureKHR -CmdCopyAccelerationStructureToMemoryKHR: ProcCmdCopyAccelerationStructureToMemoryKHR -CmdCopyMemoryToAccelerationStructureKHR: ProcCmdCopyMemoryToAccelerationStructureKHR -GetAccelerationStructureDeviceAddressKHR: ProcGetAccelerationStructureDeviceAddressKHR -CmdWriteAccelerationStructuresPropertiesKHR: ProcCmdWriteAccelerationStructuresPropertiesKHR -GetDeviceAccelerationStructureCompatibilityKHR: ProcGetDeviceAccelerationStructureCompatibilityKHR -GetAccelerationStructureBuildSizesKHR: ProcGetAccelerationStructureBuildSizesKHR -CmdTraceRaysKHR: ProcCmdTraceRaysKHR -CreateRayTracingPipelinesKHR: ProcCreateRayTracingPipelinesKHR -GetRayTracingCaptureReplayShaderGroupHandlesKHR: ProcGetRayTracingCaptureReplayShaderGroupHandlesKHR -CmdTraceRaysIndirectKHR: ProcCmdTraceRaysIndirectKHR -GetRayTracingShaderGroupStackSizeKHR: ProcGetRayTracingShaderGroupStackSizeKHR -CmdSetRayTracingPipelineStackSizeKHR: ProcCmdSetRayTracingPipelineStackSizeKHR -GetMemoryWin32HandleKHR: ProcGetMemoryWin32HandleKHR -GetMemoryWin32HandlePropertiesKHR: ProcGetMemoryWin32HandlePropertiesKHR -ImportSemaphoreWin32HandleKHR: ProcImportSemaphoreWin32HandleKHR -GetSemaphoreWin32HandleKHR: ProcGetSemaphoreWin32HandleKHR -ImportFenceWin32HandleKHR: ProcImportFenceWin32HandleKHR -GetFenceWin32HandleKHR: ProcGetFenceWin32HandleKHR -GetMemoryWin32HandleNV: ProcGetMemoryWin32HandleNV -AcquireFullScreenExclusiveModeEXT: ProcAcquireFullScreenExclusiveModeEXT -ReleaseFullScreenExclusiveModeEXT: ProcReleaseFullScreenExclusiveModeEXT -GetDeviceGroupSurfacePresentModes2EXT: ProcGetDeviceGroupSurfacePresentModes2EXT // Loader Procedures CreateInstance: ProcCreateInstance +DebugUtilsMessengerCallbackEXT: ProcDebugUtilsMessengerCallbackEXT +DeviceMemoryReportCallbackEXT: ProcDeviceMemoryReportCallbackEXT EnumerateInstanceExtensionProperties: ProcEnumerateInstanceExtensionProperties EnumerateInstanceLayerProperties: ProcEnumerateInstanceLayerProperties EnumerateInstanceVersion: ProcEnumerateInstanceVersion -DebugUtilsMessengerCallbackEXT: ProcDebugUtilsMessengerCallbackEXT -DeviceMemoryReportCallbackEXT: ProcDeviceMemoryReportCallbackEXT -load_proc_addresses :: proc(set_proc_address: SetProcAddressType) { - // Instance Procedures - set_proc_address(&DestroyInstance, "vkDestroyInstance") - set_proc_address(&EnumeratePhysicalDevices, "vkEnumeratePhysicalDevices") - set_proc_address(&GetPhysicalDeviceFeatures, "vkGetPhysicalDeviceFeatures") - set_proc_address(&GetPhysicalDeviceFormatProperties, "vkGetPhysicalDeviceFormatProperties") - set_proc_address(&GetPhysicalDeviceImageFormatProperties, "vkGetPhysicalDeviceImageFormatProperties") - set_proc_address(&GetPhysicalDeviceProperties, "vkGetPhysicalDeviceProperties") - set_proc_address(&GetPhysicalDeviceQueueFamilyProperties, "vkGetPhysicalDeviceQueueFamilyProperties") - set_proc_address(&GetPhysicalDeviceMemoryProperties, "vkGetPhysicalDeviceMemoryProperties") - set_proc_address(&GetInstanceProcAddr, "vkGetInstanceProcAddr") - set_proc_address(&CreateDevice, "vkCreateDevice") - set_proc_address(&EnumerateDeviceExtensionProperties, "vkEnumerateDeviceExtensionProperties") - set_proc_address(&EnumerateDeviceLayerProperties, "vkEnumerateDeviceLayerProperties") - set_proc_address(&GetPhysicalDeviceSparseImageFormatProperties, "vkGetPhysicalDeviceSparseImageFormatProperties") - set_proc_address(&EnumeratePhysicalDeviceGroups, "vkEnumeratePhysicalDeviceGroups") - set_proc_address(&GetPhysicalDeviceFeatures2, "vkGetPhysicalDeviceFeatures2") - set_proc_address(&GetPhysicalDeviceProperties2, "vkGetPhysicalDeviceProperties2") - set_proc_address(&GetPhysicalDeviceFormatProperties2, "vkGetPhysicalDeviceFormatProperties2") - set_proc_address(&GetPhysicalDeviceImageFormatProperties2, "vkGetPhysicalDeviceImageFormatProperties2") - set_proc_address(&GetPhysicalDeviceQueueFamilyProperties2, "vkGetPhysicalDeviceQueueFamilyProperties2") - set_proc_address(&GetPhysicalDeviceMemoryProperties2, "vkGetPhysicalDeviceMemoryProperties2") - set_proc_address(&GetPhysicalDeviceSparseImageFormatProperties2, "vkGetPhysicalDeviceSparseImageFormatProperties2") - set_proc_address(&GetPhysicalDeviceExternalBufferProperties, "vkGetPhysicalDeviceExternalBufferProperties") - set_proc_address(&GetPhysicalDeviceExternalFenceProperties, "vkGetPhysicalDeviceExternalFenceProperties") - set_proc_address(&GetPhysicalDeviceExternalSemaphoreProperties, "vkGetPhysicalDeviceExternalSemaphoreProperties") - set_proc_address(&DestroySurfaceKHR, "vkDestroySurfaceKHR") - set_proc_address(&GetPhysicalDeviceSurfaceSupportKHR, "vkGetPhysicalDeviceSurfaceSupportKHR") - set_proc_address(&GetPhysicalDeviceSurfaceCapabilitiesKHR, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR") - set_proc_address(&GetPhysicalDeviceSurfaceFormatsKHR, "vkGetPhysicalDeviceSurfaceFormatsKHR") - set_proc_address(&GetPhysicalDeviceSurfacePresentModesKHR, "vkGetPhysicalDeviceSurfacePresentModesKHR") - set_proc_address(&GetPhysicalDevicePresentRectanglesKHR, "vkGetPhysicalDevicePresentRectanglesKHR") - set_proc_address(&GetPhysicalDeviceDisplayPropertiesKHR, "vkGetPhysicalDeviceDisplayPropertiesKHR") - set_proc_address(&GetPhysicalDeviceDisplayPlanePropertiesKHR, "vkGetPhysicalDeviceDisplayPlanePropertiesKHR") - set_proc_address(&GetDisplayPlaneSupportedDisplaysKHR, "vkGetDisplayPlaneSupportedDisplaysKHR") - set_proc_address(&GetDisplayModePropertiesKHR, "vkGetDisplayModePropertiesKHR") - set_proc_address(&CreateDisplayModeKHR, "vkCreateDisplayModeKHR") - set_proc_address(&GetDisplayPlaneCapabilitiesKHR, "vkGetDisplayPlaneCapabilitiesKHR") - set_proc_address(&CreateDisplayPlaneSurfaceKHR, "vkCreateDisplayPlaneSurfaceKHR") - set_proc_address(&GetPhysicalDeviceFeatures2KHR, "vkGetPhysicalDeviceFeatures2KHR") - set_proc_address(&GetPhysicalDeviceProperties2KHR, "vkGetPhysicalDeviceProperties2KHR") - set_proc_address(&GetPhysicalDeviceFormatProperties2KHR, "vkGetPhysicalDeviceFormatProperties2KHR") - set_proc_address(&GetPhysicalDeviceImageFormatProperties2KHR, "vkGetPhysicalDeviceImageFormatProperties2KHR") - set_proc_address(&GetPhysicalDeviceQueueFamilyProperties2KHR, "vkGetPhysicalDeviceQueueFamilyProperties2KHR") - set_proc_address(&GetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2KHR") - set_proc_address(&GetPhysicalDeviceSparseImageFormatProperties2KHR, "vkGetPhysicalDeviceSparseImageFormatProperties2KHR") - set_proc_address(&EnumeratePhysicalDeviceGroupsKHR, "vkEnumeratePhysicalDeviceGroupsKHR") - set_proc_address(&GetPhysicalDeviceExternalBufferPropertiesKHR, "vkGetPhysicalDeviceExternalBufferPropertiesKHR") - set_proc_address(&GetPhysicalDeviceExternalSemaphorePropertiesKHR, "vkGetPhysicalDeviceExternalSemaphorePropertiesKHR") - set_proc_address(&GetPhysicalDeviceExternalFencePropertiesKHR, "vkGetPhysicalDeviceExternalFencePropertiesKHR") - set_proc_address(&EnumeratePhysicalDeviceQueueFamilyPerformanceQueryCountersKHR, "vkEnumeratePhysicalDeviceQueueFamilyPerformanceQueryCountersKHR") - set_proc_address(&GetPhysicalDeviceQueueFamilyPerformanceQueryPassesKHR, "vkGetPhysicalDeviceQueueFamilyPerformanceQueryPassesKHR") - set_proc_address(&GetPhysicalDeviceSurfaceCapabilities2KHR, "vkGetPhysicalDeviceSurfaceCapabilities2KHR") - set_proc_address(&GetPhysicalDeviceSurfaceFormats2KHR, "vkGetPhysicalDeviceSurfaceFormats2KHR") - set_proc_address(&GetPhysicalDeviceDisplayProperties2KHR, "vkGetPhysicalDeviceDisplayProperties2KHR") - set_proc_address(&GetPhysicalDeviceDisplayPlaneProperties2KHR, "vkGetPhysicalDeviceDisplayPlaneProperties2KHR") - set_proc_address(&GetDisplayModeProperties2KHR, "vkGetDisplayModeProperties2KHR") - set_proc_address(&GetDisplayPlaneCapabilities2KHR, "vkGetDisplayPlaneCapabilities2KHR") - set_proc_address(&GetPhysicalDeviceFragmentShadingRatesKHR, "vkGetPhysicalDeviceFragmentShadingRatesKHR") - set_proc_address(&CreateDebugReportCallbackEXT, "vkCreateDebugReportCallbackEXT") - set_proc_address(&DestroyDebugReportCallbackEXT, "vkDestroyDebugReportCallbackEXT") - set_proc_address(&DebugReportMessageEXT, "vkDebugReportMessageEXT") - set_proc_address(&GetPhysicalDeviceExternalImageFormatPropertiesNV, "vkGetPhysicalDeviceExternalImageFormatPropertiesNV") - set_proc_address(&ReleaseDisplayEXT, "vkReleaseDisplayEXT") - set_proc_address(&GetPhysicalDeviceSurfaceCapabilities2EXT, "vkGetPhysicalDeviceSurfaceCapabilities2EXT") - set_proc_address(&CreateDebugUtilsMessengerEXT, "vkCreateDebugUtilsMessengerEXT") - set_proc_address(&DestroyDebugUtilsMessengerEXT, "vkDestroyDebugUtilsMessengerEXT") - set_proc_address(&SubmitDebugUtilsMessageEXT, "vkSubmitDebugUtilsMessageEXT") - set_proc_address(&GetPhysicalDeviceMultisamplePropertiesEXT, "vkGetPhysicalDeviceMultisamplePropertiesEXT") - set_proc_address(&GetPhysicalDeviceCalibrateableTimeDomainsEXT, "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT") - set_proc_address(&GetPhysicalDeviceToolPropertiesEXT, "vkGetPhysicalDeviceToolPropertiesEXT") - set_proc_address(&GetPhysicalDeviceCooperativeMatrixPropertiesNV, "vkGetPhysicalDeviceCooperativeMatrixPropertiesNV") - set_proc_address(&GetPhysicalDeviceSupportedFramebufferMixedSamplesCombinationsNV, "vkGetPhysicalDeviceSupportedFramebufferMixedSamplesCombinationsNV") - set_proc_address(&CreateHeadlessSurfaceEXT, "vkCreateHeadlessSurfaceEXT") - set_proc_address(&AcquireDrmDisplayEXT, "vkAcquireDrmDisplayEXT") - set_proc_address(&GetDrmDisplayEXT, "vkGetDrmDisplayEXT") - set_proc_address(&AcquireWinrtDisplayNV, "vkAcquireWinrtDisplayNV") - set_proc_address(&GetWinrtDisplayNV, "vkGetWinrtDisplayNV") - set_proc_address(&CreateWin32SurfaceKHR, "vkCreateWin32SurfaceKHR") - set_proc_address(&GetPhysicalDeviceWin32PresentationSupportKHR, "vkGetPhysicalDeviceWin32PresentationSupportKHR") - set_proc_address(&GetPhysicalDeviceSurfacePresentModes2EXT, "vkGetPhysicalDeviceSurfacePresentModes2EXT") - set_proc_address(&CreateMetalSurfaceEXT, "vkCreateMetalSurfaceEXT") - set_proc_address(&CreateMacOSSurfaceMVK, "vkCreateMacOSSurfaceMVK") - set_proc_address(&CreateIOSSurfaceMVK, "vkCreateIOSSurfaceMVK") +// Instance Procedures +AcquireDrmDisplayEXT: ProcAcquireDrmDisplayEXT +AcquireWinrtDisplayNV: ProcAcquireWinrtDisplayNV +CreateDebugReportCallbackEXT: ProcCreateDebugReportCallbackEXT +CreateDebugUtilsMessengerEXT: ProcCreateDebugUtilsMessengerEXT +CreateDevice: ProcCreateDevice +CreateDisplayModeKHR: ProcCreateDisplayModeKHR +CreateDisplayPlaneSurfaceKHR: ProcCreateDisplayPlaneSurfaceKHR +CreateHeadlessSurfaceEXT: ProcCreateHeadlessSurfaceEXT +CreateIOSSurfaceMVK: ProcCreateIOSSurfaceMVK +CreateMacOSSurfaceMVK: ProcCreateMacOSSurfaceMVK +CreateMetalSurfaceEXT: ProcCreateMetalSurfaceEXT +CreateWin32SurfaceKHR: ProcCreateWin32SurfaceKHR +DebugReportMessageEXT: ProcDebugReportMessageEXT +DestroyDebugReportCallbackEXT: ProcDestroyDebugReportCallbackEXT +DestroyDebugUtilsMessengerEXT: ProcDestroyDebugUtilsMessengerEXT +DestroyInstance: ProcDestroyInstance +DestroySurfaceKHR: ProcDestroySurfaceKHR +EnumerateDeviceExtensionProperties: ProcEnumerateDeviceExtensionProperties +EnumerateDeviceLayerProperties: ProcEnumerateDeviceLayerProperties +EnumeratePhysicalDeviceGroups: ProcEnumeratePhysicalDeviceGroups +EnumeratePhysicalDeviceGroupsKHR: ProcEnumeratePhysicalDeviceGroupsKHR +EnumeratePhysicalDeviceQueueFamilyPerformanceQueryCountersKHR: ProcEnumeratePhysicalDeviceQueueFamilyPerformanceQueryCountersKHR +EnumeratePhysicalDevices: ProcEnumeratePhysicalDevices +GetDisplayModeProperties2KHR: ProcGetDisplayModeProperties2KHR +GetDisplayModePropertiesKHR: ProcGetDisplayModePropertiesKHR +GetDisplayPlaneCapabilities2KHR: ProcGetDisplayPlaneCapabilities2KHR +GetDisplayPlaneCapabilitiesKHR: ProcGetDisplayPlaneCapabilitiesKHR +GetDisplayPlaneSupportedDisplaysKHR: ProcGetDisplayPlaneSupportedDisplaysKHR +GetDrmDisplayEXT: ProcGetDrmDisplayEXT +GetInstanceProcAddr: ProcGetInstanceProcAddr +GetPhysicalDeviceCalibrateableTimeDomainsEXT: ProcGetPhysicalDeviceCalibrateableTimeDomainsEXT +GetPhysicalDeviceCooperativeMatrixPropertiesNV: ProcGetPhysicalDeviceCooperativeMatrixPropertiesNV +GetPhysicalDeviceDisplayPlaneProperties2KHR: ProcGetPhysicalDeviceDisplayPlaneProperties2KHR +GetPhysicalDeviceDisplayPlanePropertiesKHR: ProcGetPhysicalDeviceDisplayPlanePropertiesKHR +GetPhysicalDeviceDisplayProperties2KHR: ProcGetPhysicalDeviceDisplayProperties2KHR +GetPhysicalDeviceDisplayPropertiesKHR: ProcGetPhysicalDeviceDisplayPropertiesKHR +GetPhysicalDeviceExternalBufferProperties: ProcGetPhysicalDeviceExternalBufferProperties +GetPhysicalDeviceExternalBufferPropertiesKHR: ProcGetPhysicalDeviceExternalBufferPropertiesKHR +GetPhysicalDeviceExternalFenceProperties: ProcGetPhysicalDeviceExternalFenceProperties +GetPhysicalDeviceExternalFencePropertiesKHR: ProcGetPhysicalDeviceExternalFencePropertiesKHR +GetPhysicalDeviceExternalImageFormatPropertiesNV: ProcGetPhysicalDeviceExternalImageFormatPropertiesNV +GetPhysicalDeviceExternalSemaphoreProperties: ProcGetPhysicalDeviceExternalSemaphoreProperties +GetPhysicalDeviceExternalSemaphorePropertiesKHR: ProcGetPhysicalDeviceExternalSemaphorePropertiesKHR +GetPhysicalDeviceFeatures: ProcGetPhysicalDeviceFeatures +GetPhysicalDeviceFeatures2: ProcGetPhysicalDeviceFeatures2 +GetPhysicalDeviceFeatures2KHR: ProcGetPhysicalDeviceFeatures2KHR +GetPhysicalDeviceFormatProperties: ProcGetPhysicalDeviceFormatProperties +GetPhysicalDeviceFormatProperties2: ProcGetPhysicalDeviceFormatProperties2 +GetPhysicalDeviceFormatProperties2KHR: ProcGetPhysicalDeviceFormatProperties2KHR +GetPhysicalDeviceFragmentShadingRatesKHR: ProcGetPhysicalDeviceFragmentShadingRatesKHR +GetPhysicalDeviceImageFormatProperties: ProcGetPhysicalDeviceImageFormatProperties +GetPhysicalDeviceImageFormatProperties2: ProcGetPhysicalDeviceImageFormatProperties2 +GetPhysicalDeviceImageFormatProperties2KHR: ProcGetPhysicalDeviceImageFormatProperties2KHR +GetPhysicalDeviceMemoryProperties: ProcGetPhysicalDeviceMemoryProperties +GetPhysicalDeviceMemoryProperties2: ProcGetPhysicalDeviceMemoryProperties2 +GetPhysicalDeviceMemoryProperties2KHR: ProcGetPhysicalDeviceMemoryProperties2KHR +GetPhysicalDeviceMultisamplePropertiesEXT: ProcGetPhysicalDeviceMultisamplePropertiesEXT +GetPhysicalDevicePresentRectanglesKHR: ProcGetPhysicalDevicePresentRectanglesKHR +GetPhysicalDeviceProperties: ProcGetPhysicalDeviceProperties +GetPhysicalDeviceProperties2: ProcGetPhysicalDeviceProperties2 +GetPhysicalDeviceProperties2KHR: ProcGetPhysicalDeviceProperties2KHR +GetPhysicalDeviceQueueFamilyPerformanceQueryPassesKHR: ProcGetPhysicalDeviceQueueFamilyPerformanceQueryPassesKHR +GetPhysicalDeviceQueueFamilyProperties: ProcGetPhysicalDeviceQueueFamilyProperties +GetPhysicalDeviceQueueFamilyProperties2: ProcGetPhysicalDeviceQueueFamilyProperties2 +GetPhysicalDeviceQueueFamilyProperties2KHR: ProcGetPhysicalDeviceQueueFamilyProperties2KHR +GetPhysicalDeviceSparseImageFormatProperties: ProcGetPhysicalDeviceSparseImageFormatProperties +GetPhysicalDeviceSparseImageFormatProperties2: ProcGetPhysicalDeviceSparseImageFormatProperties2 +GetPhysicalDeviceSparseImageFormatProperties2KHR: ProcGetPhysicalDeviceSparseImageFormatProperties2KHR +GetPhysicalDeviceSupportedFramebufferMixedSamplesCombinationsNV: ProcGetPhysicalDeviceSupportedFramebufferMixedSamplesCombinationsNV +GetPhysicalDeviceSurfaceCapabilities2EXT: ProcGetPhysicalDeviceSurfaceCapabilities2EXT +GetPhysicalDeviceSurfaceCapabilities2KHR: ProcGetPhysicalDeviceSurfaceCapabilities2KHR +GetPhysicalDeviceSurfaceCapabilitiesKHR: ProcGetPhysicalDeviceSurfaceCapabilitiesKHR +GetPhysicalDeviceSurfaceFormats2KHR: ProcGetPhysicalDeviceSurfaceFormats2KHR +GetPhysicalDeviceSurfaceFormatsKHR: ProcGetPhysicalDeviceSurfaceFormatsKHR +GetPhysicalDeviceSurfacePresentModes2EXT: ProcGetPhysicalDeviceSurfacePresentModes2EXT +GetPhysicalDeviceSurfacePresentModesKHR: ProcGetPhysicalDeviceSurfacePresentModesKHR +GetPhysicalDeviceSurfaceSupportKHR: ProcGetPhysicalDeviceSurfaceSupportKHR +GetPhysicalDeviceToolProperties: ProcGetPhysicalDeviceToolProperties +GetPhysicalDeviceToolPropertiesEXT: ProcGetPhysicalDeviceToolPropertiesEXT +GetPhysicalDeviceWin32PresentationSupportKHR: ProcGetPhysicalDeviceWin32PresentationSupportKHR +GetWinrtDisplayNV: ProcGetWinrtDisplayNV +ReleaseDisplayEXT: ProcReleaseDisplayEXT +SubmitDebugUtilsMessageEXT: ProcSubmitDebugUtilsMessageEXT - // Device Procedures - set_proc_address(&GetDeviceProcAddr, "vkGetDeviceProcAddr") - set_proc_address(&DestroyDevice, "vkDestroyDevice") - set_proc_address(&GetDeviceQueue, "vkGetDeviceQueue") - set_proc_address(&QueueSubmit, "vkQueueSubmit") - set_proc_address(&QueueWaitIdle, "vkQueueWaitIdle") - set_proc_address(&DeviceWaitIdle, "vkDeviceWaitIdle") - set_proc_address(&AllocateMemory, "vkAllocateMemory") - set_proc_address(&FreeMemory, "vkFreeMemory") - set_proc_address(&MapMemory, "vkMapMemory") - set_proc_address(&UnmapMemory, "vkUnmapMemory") - set_proc_address(&FlushMappedMemoryRanges, "vkFlushMappedMemoryRanges") - set_proc_address(&InvalidateMappedMemoryRanges, "vkInvalidateMappedMemoryRanges") - set_proc_address(&GetDeviceMemoryCommitment, "vkGetDeviceMemoryCommitment") - set_proc_address(&BindBufferMemory, "vkBindBufferMemory") - set_proc_address(&BindImageMemory, "vkBindImageMemory") - set_proc_address(&GetBufferMemoryRequirements, "vkGetBufferMemoryRequirements") - set_proc_address(&GetImageMemoryRequirements, "vkGetImageMemoryRequirements") - set_proc_address(&GetImageSparseMemoryRequirements, "vkGetImageSparseMemoryRequirements") - set_proc_address(&QueueBindSparse, "vkQueueBindSparse") - set_proc_address(&CreateFence, "vkCreateFence") - set_proc_address(&DestroyFence, "vkDestroyFence") - set_proc_address(&ResetFences, "vkResetFences") - set_proc_address(&GetFenceStatus, "vkGetFenceStatus") - set_proc_address(&WaitForFences, "vkWaitForFences") - set_proc_address(&CreateSemaphore, "vkCreateSemaphore") - set_proc_address(&DestroySemaphore, "vkDestroySemaphore") - set_proc_address(&CreateEvent, "vkCreateEvent") - set_proc_address(&DestroyEvent, "vkDestroyEvent") - set_proc_address(&GetEventStatus, "vkGetEventStatus") - set_proc_address(&SetEvent, "vkSetEvent") - set_proc_address(&ResetEvent, "vkResetEvent") - set_proc_address(&CreateQueryPool, "vkCreateQueryPool") - set_proc_address(&DestroyQueryPool, "vkDestroyQueryPool") - set_proc_address(&GetQueryPoolResults, "vkGetQueryPoolResults") - set_proc_address(&CreateBuffer, "vkCreateBuffer") - set_proc_address(&DestroyBuffer, "vkDestroyBuffer") - set_proc_address(&CreateBufferView, "vkCreateBufferView") - set_proc_address(&DestroyBufferView, "vkDestroyBufferView") - set_proc_address(&CreateImage, "vkCreateImage") - set_proc_address(&DestroyImage, "vkDestroyImage") - set_proc_address(&GetImageSubresourceLayout, "vkGetImageSubresourceLayout") - set_proc_address(&CreateImageView, "vkCreateImageView") - set_proc_address(&DestroyImageView, "vkDestroyImageView") - set_proc_address(&CreateShaderModule, "vkCreateShaderModule") - set_proc_address(&DestroyShaderModule, "vkDestroyShaderModule") - set_proc_address(&CreatePipelineCache, "vkCreatePipelineCache") - set_proc_address(&DestroyPipelineCache, "vkDestroyPipelineCache") - set_proc_address(&GetPipelineCacheData, "vkGetPipelineCacheData") - set_proc_address(&MergePipelineCaches, "vkMergePipelineCaches") - set_proc_address(&CreateGraphicsPipelines, "vkCreateGraphicsPipelines") - set_proc_address(&CreateComputePipelines, "vkCreateComputePipelines") - set_proc_address(&DestroyPipeline, "vkDestroyPipeline") - set_proc_address(&CreatePipelineLayout, "vkCreatePipelineLayout") - set_proc_address(&DestroyPipelineLayout, "vkDestroyPipelineLayout") - set_proc_address(&CreateSampler, "vkCreateSampler") - set_proc_address(&DestroySampler, "vkDestroySampler") - set_proc_address(&CreateDescriptorSetLayout, "vkCreateDescriptorSetLayout") - set_proc_address(&DestroyDescriptorSetLayout, "vkDestroyDescriptorSetLayout") - set_proc_address(&CreateDescriptorPool, "vkCreateDescriptorPool") - set_proc_address(&DestroyDescriptorPool, "vkDestroyDescriptorPool") - set_proc_address(&ResetDescriptorPool, "vkResetDescriptorPool") - set_proc_address(&AllocateDescriptorSets, "vkAllocateDescriptorSets") - set_proc_address(&FreeDescriptorSets, "vkFreeDescriptorSets") - set_proc_address(&UpdateDescriptorSets, "vkUpdateDescriptorSets") - set_proc_address(&CreateFramebuffer, "vkCreateFramebuffer") - set_proc_address(&DestroyFramebuffer, "vkDestroyFramebuffer") - set_proc_address(&CreateRenderPass, "vkCreateRenderPass") - set_proc_address(&DestroyRenderPass, "vkDestroyRenderPass") - set_proc_address(&GetRenderAreaGranularity, "vkGetRenderAreaGranularity") - set_proc_address(&CreateCommandPool, "vkCreateCommandPool") - set_proc_address(&DestroyCommandPool, "vkDestroyCommandPool") - set_proc_address(&ResetCommandPool, "vkResetCommandPool") - set_proc_address(&AllocateCommandBuffers, "vkAllocateCommandBuffers") - set_proc_address(&FreeCommandBuffers, "vkFreeCommandBuffers") - set_proc_address(&BeginCommandBuffer, "vkBeginCommandBuffer") - set_proc_address(&EndCommandBuffer, "vkEndCommandBuffer") - set_proc_address(&ResetCommandBuffer, "vkResetCommandBuffer") - set_proc_address(&CmdBindPipeline, "vkCmdBindPipeline") - set_proc_address(&CmdSetViewport, "vkCmdSetViewport") - set_proc_address(&CmdSetScissor, "vkCmdSetScissor") - set_proc_address(&CmdSetLineWidth, "vkCmdSetLineWidth") - set_proc_address(&CmdSetDepthBias, "vkCmdSetDepthBias") - set_proc_address(&CmdSetBlendConstants, "vkCmdSetBlendConstants") - set_proc_address(&CmdSetDepthBounds, "vkCmdSetDepthBounds") - set_proc_address(&CmdSetStencilCompareMask, "vkCmdSetStencilCompareMask") - set_proc_address(&CmdSetStencilWriteMask, "vkCmdSetStencilWriteMask") - set_proc_address(&CmdSetStencilReference, "vkCmdSetStencilReference") - set_proc_address(&CmdBindDescriptorSets, "vkCmdBindDescriptorSets") - set_proc_address(&CmdBindIndexBuffer, "vkCmdBindIndexBuffer") - set_proc_address(&CmdBindVertexBuffers, "vkCmdBindVertexBuffers") - set_proc_address(&CmdDraw, "vkCmdDraw") - set_proc_address(&CmdDrawIndexed, "vkCmdDrawIndexed") - set_proc_address(&CmdDrawIndirect, "vkCmdDrawIndirect") - set_proc_address(&CmdDrawIndexedIndirect, "vkCmdDrawIndexedIndirect") - set_proc_address(&CmdDispatch, "vkCmdDispatch") - set_proc_address(&CmdDispatchIndirect, "vkCmdDispatchIndirect") - set_proc_address(&CmdCopyBuffer, "vkCmdCopyBuffer") - set_proc_address(&CmdCopyImage, "vkCmdCopyImage") - set_proc_address(&CmdBlitImage, "vkCmdBlitImage") - set_proc_address(&CmdCopyBufferToImage, "vkCmdCopyBufferToImage") - set_proc_address(&CmdCopyImageToBuffer, "vkCmdCopyImageToBuffer") - set_proc_address(&CmdUpdateBuffer, "vkCmdUpdateBuffer") - set_proc_address(&CmdFillBuffer, "vkCmdFillBuffer") - set_proc_address(&CmdClearColorImage, "vkCmdClearColorImage") - set_proc_address(&CmdClearDepthStencilImage, "vkCmdClearDepthStencilImage") - set_proc_address(&CmdClearAttachments, "vkCmdClearAttachments") - set_proc_address(&CmdResolveImage, "vkCmdResolveImage") - set_proc_address(&CmdSetEvent, "vkCmdSetEvent") - set_proc_address(&CmdResetEvent, "vkCmdResetEvent") - set_proc_address(&CmdWaitEvents, "vkCmdWaitEvents") - set_proc_address(&CmdPipelineBarrier, "vkCmdPipelineBarrier") - set_proc_address(&CmdBeginQuery, "vkCmdBeginQuery") - set_proc_address(&CmdEndQuery, "vkCmdEndQuery") - set_proc_address(&CmdResetQueryPool, "vkCmdResetQueryPool") - set_proc_address(&CmdWriteTimestamp, "vkCmdWriteTimestamp") - set_proc_address(&CmdCopyQueryPoolResults, "vkCmdCopyQueryPoolResults") - set_proc_address(&CmdPushConstants, "vkCmdPushConstants") - set_proc_address(&CmdBeginRenderPass, "vkCmdBeginRenderPass") - set_proc_address(&CmdNextSubpass, "vkCmdNextSubpass") - set_proc_address(&CmdEndRenderPass, "vkCmdEndRenderPass") - set_proc_address(&CmdExecuteCommands, "vkCmdExecuteCommands") - set_proc_address(&BindBufferMemory2, "vkBindBufferMemory2") - set_proc_address(&BindImageMemory2, "vkBindImageMemory2") - set_proc_address(&GetDeviceGroupPeerMemoryFeatures, "vkGetDeviceGroupPeerMemoryFeatures") - set_proc_address(&CmdSetDeviceMask, "vkCmdSetDeviceMask") - set_proc_address(&CmdDispatchBase, "vkCmdDispatchBase") - set_proc_address(&GetImageMemoryRequirements2, "vkGetImageMemoryRequirements2") - set_proc_address(&GetBufferMemoryRequirements2, "vkGetBufferMemoryRequirements2") - set_proc_address(&GetImageSparseMemoryRequirements2, "vkGetImageSparseMemoryRequirements2") - set_proc_address(&TrimCommandPool, "vkTrimCommandPool") - set_proc_address(&GetDeviceQueue2, "vkGetDeviceQueue2") - set_proc_address(&CreateSamplerYcbcrConversion, "vkCreateSamplerYcbcrConversion") - set_proc_address(&DestroySamplerYcbcrConversion, "vkDestroySamplerYcbcrConversion") - set_proc_address(&CreateDescriptorUpdateTemplate, "vkCreateDescriptorUpdateTemplate") - set_proc_address(&DestroyDescriptorUpdateTemplate, "vkDestroyDescriptorUpdateTemplate") - set_proc_address(&UpdateDescriptorSetWithTemplate, "vkUpdateDescriptorSetWithTemplate") - set_proc_address(&GetDescriptorSetLayoutSupport, "vkGetDescriptorSetLayoutSupport") - set_proc_address(&CmdDrawIndirectCount, "vkCmdDrawIndirectCount") - set_proc_address(&CmdDrawIndexedIndirectCount, "vkCmdDrawIndexedIndirectCount") - set_proc_address(&CreateRenderPass2, "vkCreateRenderPass2") - set_proc_address(&CmdBeginRenderPass2, "vkCmdBeginRenderPass2") - set_proc_address(&CmdNextSubpass2, "vkCmdNextSubpass2") - set_proc_address(&CmdEndRenderPass2, "vkCmdEndRenderPass2") - set_proc_address(&ResetQueryPool, "vkResetQueryPool") - set_proc_address(&GetSemaphoreCounterValue, "vkGetSemaphoreCounterValue") - set_proc_address(&WaitSemaphores, "vkWaitSemaphores") - set_proc_address(&SignalSemaphore, "vkSignalSemaphore") - set_proc_address(&GetBufferDeviceAddress, "vkGetBufferDeviceAddress") - set_proc_address(&GetBufferOpaqueCaptureAddress, "vkGetBufferOpaqueCaptureAddress") - set_proc_address(&GetDeviceMemoryOpaqueCaptureAddress, "vkGetDeviceMemoryOpaqueCaptureAddress") - set_proc_address(&CreateSwapchainKHR, "vkCreateSwapchainKHR") - set_proc_address(&DestroySwapchainKHR, "vkDestroySwapchainKHR") - set_proc_address(&GetSwapchainImagesKHR, "vkGetSwapchainImagesKHR") - set_proc_address(&AcquireNextImageKHR, "vkAcquireNextImageKHR") - set_proc_address(&QueuePresentKHR, "vkQueuePresentKHR") - set_proc_address(&GetDeviceGroupPresentCapabilitiesKHR, "vkGetDeviceGroupPresentCapabilitiesKHR") - set_proc_address(&GetDeviceGroupSurfacePresentModesKHR, "vkGetDeviceGroupSurfacePresentModesKHR") - set_proc_address(&AcquireNextImage2KHR, "vkAcquireNextImage2KHR") - set_proc_address(&CreateSharedSwapchainsKHR, "vkCreateSharedSwapchainsKHR") - set_proc_address(&GetDeviceGroupPeerMemoryFeaturesKHR, "vkGetDeviceGroupPeerMemoryFeaturesKHR") - set_proc_address(&CmdSetDeviceMaskKHR, "vkCmdSetDeviceMaskKHR") - set_proc_address(&CmdDispatchBaseKHR, "vkCmdDispatchBaseKHR") - set_proc_address(&TrimCommandPoolKHR, "vkTrimCommandPoolKHR") - set_proc_address(&GetMemoryFdKHR, "vkGetMemoryFdKHR") - set_proc_address(&GetMemoryFdPropertiesKHR, "vkGetMemoryFdPropertiesKHR") - set_proc_address(&ImportSemaphoreFdKHR, "vkImportSemaphoreFdKHR") - set_proc_address(&GetSemaphoreFdKHR, "vkGetSemaphoreFdKHR") - set_proc_address(&CmdPushDescriptorSetKHR, "vkCmdPushDescriptorSetKHR") - set_proc_address(&CmdPushDescriptorSetWithTemplateKHR, "vkCmdPushDescriptorSetWithTemplateKHR") - set_proc_address(&CreateDescriptorUpdateTemplateKHR, "vkCreateDescriptorUpdateTemplateKHR") - set_proc_address(&DestroyDescriptorUpdateTemplateKHR, "vkDestroyDescriptorUpdateTemplateKHR") - set_proc_address(&UpdateDescriptorSetWithTemplateKHR, "vkUpdateDescriptorSetWithTemplateKHR") - set_proc_address(&CreateRenderPass2KHR, "vkCreateRenderPass2KHR") - set_proc_address(&CmdBeginRenderPass2KHR, "vkCmdBeginRenderPass2KHR") - set_proc_address(&CmdNextSubpass2KHR, "vkCmdNextSubpass2KHR") - set_proc_address(&CmdEndRenderPass2KHR, "vkCmdEndRenderPass2KHR") - set_proc_address(&GetSwapchainStatusKHR, "vkGetSwapchainStatusKHR") - set_proc_address(&ImportFenceFdKHR, "vkImportFenceFdKHR") - set_proc_address(&GetFenceFdKHR, "vkGetFenceFdKHR") - set_proc_address(&AcquireProfilingLockKHR, "vkAcquireProfilingLockKHR") - set_proc_address(&ReleaseProfilingLockKHR, "vkReleaseProfilingLockKHR") - set_proc_address(&GetImageMemoryRequirements2KHR, "vkGetImageMemoryRequirements2KHR") - set_proc_address(&GetBufferMemoryRequirements2KHR, "vkGetBufferMemoryRequirements2KHR") - set_proc_address(&GetImageSparseMemoryRequirements2KHR, "vkGetImageSparseMemoryRequirements2KHR") - set_proc_address(&CreateSamplerYcbcrConversionKHR, "vkCreateSamplerYcbcrConversionKHR") - set_proc_address(&DestroySamplerYcbcrConversionKHR, "vkDestroySamplerYcbcrConversionKHR") - set_proc_address(&BindBufferMemory2KHR, "vkBindBufferMemory2KHR") - set_proc_address(&BindImageMemory2KHR, "vkBindImageMemory2KHR") - set_proc_address(&GetDescriptorSetLayoutSupportKHR, "vkGetDescriptorSetLayoutSupportKHR") - set_proc_address(&CmdDrawIndirectCountKHR, "vkCmdDrawIndirectCountKHR") - set_proc_address(&CmdDrawIndexedIndirectCountKHR, "vkCmdDrawIndexedIndirectCountKHR") - set_proc_address(&GetSemaphoreCounterValueKHR, "vkGetSemaphoreCounterValueKHR") - set_proc_address(&WaitSemaphoresKHR, "vkWaitSemaphoresKHR") - set_proc_address(&SignalSemaphoreKHR, "vkSignalSemaphoreKHR") - set_proc_address(&CmdSetFragmentShadingRateKHR, "vkCmdSetFragmentShadingRateKHR") - set_proc_address(&WaitForPresentKHR, "vkWaitForPresentKHR") - set_proc_address(&GetBufferDeviceAddressKHR, "vkGetBufferDeviceAddressKHR") - set_proc_address(&GetBufferOpaqueCaptureAddressKHR, "vkGetBufferOpaqueCaptureAddressKHR") - set_proc_address(&GetDeviceMemoryOpaqueCaptureAddressKHR, "vkGetDeviceMemoryOpaqueCaptureAddressKHR") - set_proc_address(&CreateDeferredOperationKHR, "vkCreateDeferredOperationKHR") - set_proc_address(&DestroyDeferredOperationKHR, "vkDestroyDeferredOperationKHR") - set_proc_address(&GetDeferredOperationMaxConcurrencyKHR, "vkGetDeferredOperationMaxConcurrencyKHR") - set_proc_address(&GetDeferredOperationResultKHR, "vkGetDeferredOperationResultKHR") - set_proc_address(&DeferredOperationJoinKHR, "vkDeferredOperationJoinKHR") - set_proc_address(&GetPipelineExecutablePropertiesKHR, "vkGetPipelineExecutablePropertiesKHR") - set_proc_address(&GetPipelineExecutableStatisticsKHR, "vkGetPipelineExecutableStatisticsKHR") - set_proc_address(&GetPipelineExecutableInternalRepresentationsKHR, "vkGetPipelineExecutableInternalRepresentationsKHR") - set_proc_address(&CmdSetEvent2KHR, "vkCmdSetEvent2KHR") - set_proc_address(&CmdResetEvent2KHR, "vkCmdResetEvent2KHR") - set_proc_address(&CmdWaitEvents2KHR, "vkCmdWaitEvents2KHR") - set_proc_address(&CmdPipelineBarrier2KHR, "vkCmdPipelineBarrier2KHR") - set_proc_address(&CmdWriteTimestamp2KHR, "vkCmdWriteTimestamp2KHR") - set_proc_address(&QueueSubmit2KHR, "vkQueueSubmit2KHR") - set_proc_address(&CmdWriteBufferMarker2AMD, "vkCmdWriteBufferMarker2AMD") - set_proc_address(&GetQueueCheckpointData2NV, "vkGetQueueCheckpointData2NV") - set_proc_address(&CmdCopyBuffer2KHR, "vkCmdCopyBuffer2KHR") - set_proc_address(&CmdCopyImage2KHR, "vkCmdCopyImage2KHR") - set_proc_address(&CmdCopyBufferToImage2KHR, "vkCmdCopyBufferToImage2KHR") - set_proc_address(&CmdCopyImageToBuffer2KHR, "vkCmdCopyImageToBuffer2KHR") - set_proc_address(&CmdBlitImage2KHR, "vkCmdBlitImage2KHR") - set_proc_address(&CmdResolveImage2KHR, "vkCmdResolveImage2KHR") - set_proc_address(&DebugMarkerSetObjectTagEXT, "vkDebugMarkerSetObjectTagEXT") - set_proc_address(&DebugMarkerSetObjectNameEXT, "vkDebugMarkerSetObjectNameEXT") - set_proc_address(&CmdDebugMarkerBeginEXT, "vkCmdDebugMarkerBeginEXT") - set_proc_address(&CmdDebugMarkerEndEXT, "vkCmdDebugMarkerEndEXT") - set_proc_address(&CmdDebugMarkerInsertEXT, "vkCmdDebugMarkerInsertEXT") - set_proc_address(&CmdBindTransformFeedbackBuffersEXT, "vkCmdBindTransformFeedbackBuffersEXT") - set_proc_address(&CmdBeginTransformFeedbackEXT, "vkCmdBeginTransformFeedbackEXT") - set_proc_address(&CmdEndTransformFeedbackEXT, "vkCmdEndTransformFeedbackEXT") - set_proc_address(&CmdBeginQueryIndexedEXT, "vkCmdBeginQueryIndexedEXT") - set_proc_address(&CmdEndQueryIndexedEXT, "vkCmdEndQueryIndexedEXT") - set_proc_address(&CmdDrawIndirectByteCountEXT, "vkCmdDrawIndirectByteCountEXT") - set_proc_address(&CreateCuModuleNVX, "vkCreateCuModuleNVX") - set_proc_address(&CreateCuFunctionNVX, "vkCreateCuFunctionNVX") - set_proc_address(&DestroyCuModuleNVX, "vkDestroyCuModuleNVX") - set_proc_address(&DestroyCuFunctionNVX, "vkDestroyCuFunctionNVX") - set_proc_address(&CmdCuLaunchKernelNVX, "vkCmdCuLaunchKernelNVX") - set_proc_address(&GetImageViewHandleNVX, "vkGetImageViewHandleNVX") - set_proc_address(&GetImageViewAddressNVX, "vkGetImageViewAddressNVX") - set_proc_address(&CmdDrawIndirectCountAMD, "vkCmdDrawIndirectCountAMD") - set_proc_address(&CmdDrawIndexedIndirectCountAMD, "vkCmdDrawIndexedIndirectCountAMD") - set_proc_address(&GetShaderInfoAMD, "vkGetShaderInfoAMD") - set_proc_address(&CmdBeginConditionalRenderingEXT, "vkCmdBeginConditionalRenderingEXT") - set_proc_address(&CmdEndConditionalRenderingEXT, "vkCmdEndConditionalRenderingEXT") - set_proc_address(&CmdSetViewportWScalingNV, "vkCmdSetViewportWScalingNV") - set_proc_address(&DisplayPowerControlEXT, "vkDisplayPowerControlEXT") - set_proc_address(&RegisterDeviceEventEXT, "vkRegisterDeviceEventEXT") - set_proc_address(&RegisterDisplayEventEXT, "vkRegisterDisplayEventEXT") - set_proc_address(&GetSwapchainCounterEXT, "vkGetSwapchainCounterEXT") - set_proc_address(&GetRefreshCycleDurationGOOGLE, "vkGetRefreshCycleDurationGOOGLE") - set_proc_address(&GetPastPresentationTimingGOOGLE, "vkGetPastPresentationTimingGOOGLE") - set_proc_address(&CmdSetDiscardRectangleEXT, "vkCmdSetDiscardRectangleEXT") - set_proc_address(&SetHdrMetadataEXT, "vkSetHdrMetadataEXT") - set_proc_address(&SetDebugUtilsObjectNameEXT, "vkSetDebugUtilsObjectNameEXT") - set_proc_address(&SetDebugUtilsObjectTagEXT, "vkSetDebugUtilsObjectTagEXT") - set_proc_address(&QueueBeginDebugUtilsLabelEXT, "vkQueueBeginDebugUtilsLabelEXT") - set_proc_address(&QueueEndDebugUtilsLabelEXT, "vkQueueEndDebugUtilsLabelEXT") - set_proc_address(&QueueInsertDebugUtilsLabelEXT, "vkQueueInsertDebugUtilsLabelEXT") - set_proc_address(&CmdBeginDebugUtilsLabelEXT, "vkCmdBeginDebugUtilsLabelEXT") - set_proc_address(&CmdEndDebugUtilsLabelEXT, "vkCmdEndDebugUtilsLabelEXT") - set_proc_address(&CmdInsertDebugUtilsLabelEXT, "vkCmdInsertDebugUtilsLabelEXT") - set_proc_address(&CmdSetSampleLocationsEXT, "vkCmdSetSampleLocationsEXT") - set_proc_address(&GetImageDrmFormatModifierPropertiesEXT, "vkGetImageDrmFormatModifierPropertiesEXT") - set_proc_address(&CreateValidationCacheEXT, "vkCreateValidationCacheEXT") - set_proc_address(&DestroyValidationCacheEXT, "vkDestroyValidationCacheEXT") - set_proc_address(&MergeValidationCachesEXT, "vkMergeValidationCachesEXT") - set_proc_address(&GetValidationCacheDataEXT, "vkGetValidationCacheDataEXT") - set_proc_address(&CmdBindShadingRateImageNV, "vkCmdBindShadingRateImageNV") - set_proc_address(&CmdSetViewportShadingRatePaletteNV, "vkCmdSetViewportShadingRatePaletteNV") - set_proc_address(&CmdSetCoarseSampleOrderNV, "vkCmdSetCoarseSampleOrderNV") - set_proc_address(&CreateAccelerationStructureNV, "vkCreateAccelerationStructureNV") - set_proc_address(&DestroyAccelerationStructureNV, "vkDestroyAccelerationStructureNV") - set_proc_address(&GetAccelerationStructureMemoryRequirementsNV, "vkGetAccelerationStructureMemoryRequirementsNV") - set_proc_address(&BindAccelerationStructureMemoryNV, "vkBindAccelerationStructureMemoryNV") - set_proc_address(&CmdBuildAccelerationStructureNV, "vkCmdBuildAccelerationStructureNV") - set_proc_address(&CmdCopyAccelerationStructureNV, "vkCmdCopyAccelerationStructureNV") - set_proc_address(&CmdTraceRaysNV, "vkCmdTraceRaysNV") - set_proc_address(&CreateRayTracingPipelinesNV, "vkCreateRayTracingPipelinesNV") - set_proc_address(&GetRayTracingShaderGroupHandlesKHR, "vkGetRayTracingShaderGroupHandlesKHR") - set_proc_address(&GetRayTracingShaderGroupHandlesNV, "vkGetRayTracingShaderGroupHandlesNV") - set_proc_address(&GetAccelerationStructureHandleNV, "vkGetAccelerationStructureHandleNV") - set_proc_address(&CmdWriteAccelerationStructuresPropertiesNV, "vkCmdWriteAccelerationStructuresPropertiesNV") - set_proc_address(&CompileDeferredNV, "vkCompileDeferredNV") - set_proc_address(&GetMemoryHostPointerPropertiesEXT, "vkGetMemoryHostPointerPropertiesEXT") - set_proc_address(&CmdWriteBufferMarkerAMD, "vkCmdWriteBufferMarkerAMD") - set_proc_address(&GetCalibratedTimestampsEXT, "vkGetCalibratedTimestampsEXT") - set_proc_address(&CmdDrawMeshTasksNV, "vkCmdDrawMeshTasksNV") - set_proc_address(&CmdDrawMeshTasksIndirectNV, "vkCmdDrawMeshTasksIndirectNV") - set_proc_address(&CmdDrawMeshTasksIndirectCountNV, "vkCmdDrawMeshTasksIndirectCountNV") - set_proc_address(&CmdSetExclusiveScissorNV, "vkCmdSetExclusiveScissorNV") - set_proc_address(&CmdSetCheckpointNV, "vkCmdSetCheckpointNV") - set_proc_address(&GetQueueCheckpointDataNV, "vkGetQueueCheckpointDataNV") - set_proc_address(&InitializePerformanceApiINTEL, "vkInitializePerformanceApiINTEL") - set_proc_address(&UninitializePerformanceApiINTEL, "vkUninitializePerformanceApiINTEL") - set_proc_address(&CmdSetPerformanceMarkerINTEL, "vkCmdSetPerformanceMarkerINTEL") - set_proc_address(&CmdSetPerformanceStreamMarkerINTEL, "vkCmdSetPerformanceStreamMarkerINTEL") - set_proc_address(&CmdSetPerformanceOverrideINTEL, "vkCmdSetPerformanceOverrideINTEL") - set_proc_address(&AcquirePerformanceConfigurationINTEL, "vkAcquirePerformanceConfigurationINTEL") - set_proc_address(&ReleasePerformanceConfigurationINTEL, "vkReleasePerformanceConfigurationINTEL") - set_proc_address(&QueueSetPerformanceConfigurationINTEL, "vkQueueSetPerformanceConfigurationINTEL") - set_proc_address(&GetPerformanceParameterINTEL, "vkGetPerformanceParameterINTEL") - set_proc_address(&SetLocalDimmingAMD, "vkSetLocalDimmingAMD") - set_proc_address(&GetBufferDeviceAddressEXT, "vkGetBufferDeviceAddressEXT") - set_proc_address(&CmdSetLineStippleEXT, "vkCmdSetLineStippleEXT") - set_proc_address(&ResetQueryPoolEXT, "vkResetQueryPoolEXT") - set_proc_address(&CmdSetCullModeEXT, "vkCmdSetCullModeEXT") - set_proc_address(&CmdSetFrontFaceEXT, "vkCmdSetFrontFaceEXT") - set_proc_address(&CmdSetPrimitiveTopologyEXT, "vkCmdSetPrimitiveTopologyEXT") - set_proc_address(&CmdSetViewportWithCountEXT, "vkCmdSetViewportWithCountEXT") - set_proc_address(&CmdSetScissorWithCountEXT, "vkCmdSetScissorWithCountEXT") - set_proc_address(&CmdBindVertexBuffers2EXT, "vkCmdBindVertexBuffers2EXT") - set_proc_address(&CmdSetDepthTestEnableEXT, "vkCmdSetDepthTestEnableEXT") - set_proc_address(&CmdSetDepthWriteEnableEXT, "vkCmdSetDepthWriteEnableEXT") - set_proc_address(&CmdSetDepthCompareOpEXT, "vkCmdSetDepthCompareOpEXT") - set_proc_address(&CmdSetDepthBoundsTestEnableEXT, "vkCmdSetDepthBoundsTestEnableEXT") - set_proc_address(&CmdSetStencilTestEnableEXT, "vkCmdSetStencilTestEnableEXT") - set_proc_address(&CmdSetStencilOpEXT, "vkCmdSetStencilOpEXT") - set_proc_address(&GetGeneratedCommandsMemoryRequirementsNV, "vkGetGeneratedCommandsMemoryRequirementsNV") - set_proc_address(&CmdPreprocessGeneratedCommandsNV, "vkCmdPreprocessGeneratedCommandsNV") - set_proc_address(&CmdExecuteGeneratedCommandsNV, "vkCmdExecuteGeneratedCommandsNV") - set_proc_address(&CmdBindPipelineShaderGroupNV, "vkCmdBindPipelineShaderGroupNV") - set_proc_address(&CreateIndirectCommandsLayoutNV, "vkCreateIndirectCommandsLayoutNV") - set_proc_address(&DestroyIndirectCommandsLayoutNV, "vkDestroyIndirectCommandsLayoutNV") - set_proc_address(&CreatePrivateDataSlotEXT, "vkCreatePrivateDataSlotEXT") - set_proc_address(&DestroyPrivateDataSlotEXT, "vkDestroyPrivateDataSlotEXT") - set_proc_address(&SetPrivateDataEXT, "vkSetPrivateDataEXT") - set_proc_address(&GetPrivateDataEXT, "vkGetPrivateDataEXT") - set_proc_address(&CmdSetFragmentShadingRateEnumNV, "vkCmdSetFragmentShadingRateEnumNV") - set_proc_address(&CmdSetVertexInputEXT, "vkCmdSetVertexInputEXT") - set_proc_address(&GetDeviceSubpassShadingMaxWorkgroupSizeHUAWEI, "vkGetDeviceSubpassShadingMaxWorkgroupSizeHUAWEI") - set_proc_address(&CmdSubpassShadingHUAWEI, "vkCmdSubpassShadingHUAWEI") - set_proc_address(&CmdBindInvocationMaskHUAWEI, "vkCmdBindInvocationMaskHUAWEI") - set_proc_address(&GetMemoryRemoteAddressNV, "vkGetMemoryRemoteAddressNV") - set_proc_address(&CmdSetPatchControlPointsEXT, "vkCmdSetPatchControlPointsEXT") - set_proc_address(&CmdSetRasterizerDiscardEnableEXT, "vkCmdSetRasterizerDiscardEnableEXT") - set_proc_address(&CmdSetDepthBiasEnableEXT, "vkCmdSetDepthBiasEnableEXT") - set_proc_address(&CmdSetLogicOpEXT, "vkCmdSetLogicOpEXT") - set_proc_address(&CmdSetPrimitiveRestartEnableEXT, "vkCmdSetPrimitiveRestartEnableEXT") - set_proc_address(&CmdDrawMultiEXT, "vkCmdDrawMultiEXT") - set_proc_address(&CmdDrawMultiIndexedEXT, "vkCmdDrawMultiIndexedEXT") - set_proc_address(&SetDeviceMemoryPriorityEXT, "vkSetDeviceMemoryPriorityEXT") - set_proc_address(&CreateAccelerationStructureKHR, "vkCreateAccelerationStructureKHR") - set_proc_address(&DestroyAccelerationStructureKHR, "vkDestroyAccelerationStructureKHR") - set_proc_address(&CmdBuildAccelerationStructuresKHR, "vkCmdBuildAccelerationStructuresKHR") - set_proc_address(&CmdBuildAccelerationStructuresIndirectKHR, "vkCmdBuildAccelerationStructuresIndirectKHR") - set_proc_address(&BuildAccelerationStructuresKHR, "vkBuildAccelerationStructuresKHR") - set_proc_address(&CopyAccelerationStructureKHR, "vkCopyAccelerationStructureKHR") - set_proc_address(&CopyAccelerationStructureToMemoryKHR, "vkCopyAccelerationStructureToMemoryKHR") - set_proc_address(&CopyMemoryToAccelerationStructureKHR, "vkCopyMemoryToAccelerationStructureKHR") - set_proc_address(&WriteAccelerationStructuresPropertiesKHR, "vkWriteAccelerationStructuresPropertiesKHR") - set_proc_address(&CmdCopyAccelerationStructureKHR, "vkCmdCopyAccelerationStructureKHR") - set_proc_address(&CmdCopyAccelerationStructureToMemoryKHR, "vkCmdCopyAccelerationStructureToMemoryKHR") - set_proc_address(&CmdCopyMemoryToAccelerationStructureKHR, "vkCmdCopyMemoryToAccelerationStructureKHR") - set_proc_address(&GetAccelerationStructureDeviceAddressKHR, "vkGetAccelerationStructureDeviceAddressKHR") - set_proc_address(&CmdWriteAccelerationStructuresPropertiesKHR, "vkCmdWriteAccelerationStructuresPropertiesKHR") - set_proc_address(&GetDeviceAccelerationStructureCompatibilityKHR, "vkGetDeviceAccelerationStructureCompatibilityKHR") - set_proc_address(&GetAccelerationStructureBuildSizesKHR, "vkGetAccelerationStructureBuildSizesKHR") - set_proc_address(&CmdTraceRaysKHR, "vkCmdTraceRaysKHR") - set_proc_address(&CreateRayTracingPipelinesKHR, "vkCreateRayTracingPipelinesKHR") - set_proc_address(&GetRayTracingCaptureReplayShaderGroupHandlesKHR, "vkGetRayTracingCaptureReplayShaderGroupHandlesKHR") - set_proc_address(&CmdTraceRaysIndirectKHR, "vkCmdTraceRaysIndirectKHR") - set_proc_address(&GetRayTracingShaderGroupStackSizeKHR, "vkGetRayTracingShaderGroupStackSizeKHR") - set_proc_address(&CmdSetRayTracingPipelineStackSizeKHR, "vkCmdSetRayTracingPipelineStackSizeKHR") - set_proc_address(&GetMemoryWin32HandleKHR, "vkGetMemoryWin32HandleKHR") - set_proc_address(&GetMemoryWin32HandlePropertiesKHR, "vkGetMemoryWin32HandlePropertiesKHR") - set_proc_address(&ImportSemaphoreWin32HandleKHR, "vkImportSemaphoreWin32HandleKHR") - set_proc_address(&GetSemaphoreWin32HandleKHR, "vkGetSemaphoreWin32HandleKHR") - set_proc_address(&ImportFenceWin32HandleKHR, "vkImportFenceWin32HandleKHR") - set_proc_address(&GetFenceWin32HandleKHR, "vkGetFenceWin32HandleKHR") - set_proc_address(&GetMemoryWin32HandleNV, "vkGetMemoryWin32HandleNV") - set_proc_address(&AcquireFullScreenExclusiveModeEXT, "vkAcquireFullScreenExclusiveModeEXT") - set_proc_address(&ReleaseFullScreenExclusiveModeEXT, "vkReleaseFullScreenExclusiveModeEXT") - set_proc_address(&GetDeviceGroupSurfacePresentModes2EXT, "vkGetDeviceGroupSurfacePresentModes2EXT") +// Device Procedures +AcquireFullScreenExclusiveModeEXT: ProcAcquireFullScreenExclusiveModeEXT +AcquireNextImage2KHR: ProcAcquireNextImage2KHR +AcquireNextImageKHR: ProcAcquireNextImageKHR +AcquirePerformanceConfigurationINTEL: ProcAcquirePerformanceConfigurationINTEL +AcquireProfilingLockKHR: ProcAcquireProfilingLockKHR +AllocateCommandBuffers: ProcAllocateCommandBuffers +AllocateDescriptorSets: ProcAllocateDescriptorSets +AllocateMemory: ProcAllocateMemory +BeginCommandBuffer: ProcBeginCommandBuffer +BindAccelerationStructureMemoryNV: ProcBindAccelerationStructureMemoryNV +BindBufferMemory: ProcBindBufferMemory +BindBufferMemory2: ProcBindBufferMemory2 +BindBufferMemory2KHR: ProcBindBufferMemory2KHR +BindImageMemory: ProcBindImageMemory +BindImageMemory2: ProcBindImageMemory2 +BindImageMemory2KHR: ProcBindImageMemory2KHR +BuildAccelerationStructuresKHR: ProcBuildAccelerationStructuresKHR +CmdBeginConditionalRenderingEXT: ProcCmdBeginConditionalRenderingEXT +CmdBeginDebugUtilsLabelEXT: ProcCmdBeginDebugUtilsLabelEXT +CmdBeginQuery: ProcCmdBeginQuery +CmdBeginQueryIndexedEXT: ProcCmdBeginQueryIndexedEXT +CmdBeginRenderPass: ProcCmdBeginRenderPass +CmdBeginRenderPass2: ProcCmdBeginRenderPass2 +CmdBeginRenderPass2KHR: ProcCmdBeginRenderPass2KHR +CmdBeginRendering: ProcCmdBeginRendering +CmdBeginRenderingKHR: ProcCmdBeginRenderingKHR +CmdBeginTransformFeedbackEXT: ProcCmdBeginTransformFeedbackEXT +CmdBindDescriptorSets: ProcCmdBindDescriptorSets +CmdBindIndexBuffer: ProcCmdBindIndexBuffer +CmdBindInvocationMaskHUAWEI: ProcCmdBindInvocationMaskHUAWEI +CmdBindPipeline: ProcCmdBindPipeline +CmdBindPipelineShaderGroupNV: ProcCmdBindPipelineShaderGroupNV +CmdBindShadingRateImageNV: ProcCmdBindShadingRateImageNV +CmdBindTransformFeedbackBuffersEXT: ProcCmdBindTransformFeedbackBuffersEXT +CmdBindVertexBuffers: ProcCmdBindVertexBuffers +CmdBindVertexBuffers2: ProcCmdBindVertexBuffers2 +CmdBindVertexBuffers2EXT: ProcCmdBindVertexBuffers2EXT +CmdBlitImage: ProcCmdBlitImage +CmdBlitImage2: ProcCmdBlitImage2 +CmdBlitImage2KHR: ProcCmdBlitImage2KHR +CmdBuildAccelerationStructureNV: ProcCmdBuildAccelerationStructureNV +CmdBuildAccelerationStructuresIndirectKHR: ProcCmdBuildAccelerationStructuresIndirectKHR +CmdBuildAccelerationStructuresKHR: ProcCmdBuildAccelerationStructuresKHR +CmdClearAttachments: ProcCmdClearAttachments +CmdClearColorImage: ProcCmdClearColorImage +CmdClearDepthStencilImage: ProcCmdClearDepthStencilImage +CmdCopyAccelerationStructureKHR: ProcCmdCopyAccelerationStructureKHR +CmdCopyAccelerationStructureNV: ProcCmdCopyAccelerationStructureNV +CmdCopyAccelerationStructureToMemoryKHR: ProcCmdCopyAccelerationStructureToMemoryKHR +CmdCopyBuffer: ProcCmdCopyBuffer +CmdCopyBuffer2: ProcCmdCopyBuffer2 +CmdCopyBuffer2KHR: ProcCmdCopyBuffer2KHR +CmdCopyBufferToImage: ProcCmdCopyBufferToImage +CmdCopyBufferToImage2: ProcCmdCopyBufferToImage2 +CmdCopyBufferToImage2KHR: ProcCmdCopyBufferToImage2KHR +CmdCopyImage: ProcCmdCopyImage +CmdCopyImage2: ProcCmdCopyImage2 +CmdCopyImage2KHR: ProcCmdCopyImage2KHR +CmdCopyImageToBuffer: ProcCmdCopyImageToBuffer +CmdCopyImageToBuffer2: ProcCmdCopyImageToBuffer2 +CmdCopyImageToBuffer2KHR: ProcCmdCopyImageToBuffer2KHR +CmdCopyMemoryToAccelerationStructureKHR: ProcCmdCopyMemoryToAccelerationStructureKHR +CmdCopyQueryPoolResults: ProcCmdCopyQueryPoolResults +CmdCuLaunchKernelNVX: ProcCmdCuLaunchKernelNVX +CmdDebugMarkerBeginEXT: ProcCmdDebugMarkerBeginEXT +CmdDebugMarkerEndEXT: ProcCmdDebugMarkerEndEXT +CmdDebugMarkerInsertEXT: ProcCmdDebugMarkerInsertEXT +CmdDispatch: ProcCmdDispatch +CmdDispatchBase: ProcCmdDispatchBase +CmdDispatchBaseKHR: ProcCmdDispatchBaseKHR +CmdDispatchIndirect: ProcCmdDispatchIndirect +CmdDraw: ProcCmdDraw +CmdDrawIndexed: ProcCmdDrawIndexed +CmdDrawIndexedIndirect: ProcCmdDrawIndexedIndirect +CmdDrawIndexedIndirectCount: ProcCmdDrawIndexedIndirectCount +CmdDrawIndexedIndirectCountAMD: ProcCmdDrawIndexedIndirectCountAMD +CmdDrawIndexedIndirectCountKHR: ProcCmdDrawIndexedIndirectCountKHR +CmdDrawIndirect: ProcCmdDrawIndirect +CmdDrawIndirectByteCountEXT: ProcCmdDrawIndirectByteCountEXT +CmdDrawIndirectCount: ProcCmdDrawIndirectCount +CmdDrawIndirectCountAMD: ProcCmdDrawIndirectCountAMD +CmdDrawIndirectCountKHR: ProcCmdDrawIndirectCountKHR +CmdDrawMeshTasksIndirectCountNV: ProcCmdDrawMeshTasksIndirectCountNV +CmdDrawMeshTasksIndirectNV: ProcCmdDrawMeshTasksIndirectNV +CmdDrawMeshTasksNV: ProcCmdDrawMeshTasksNV +CmdDrawMultiEXT: ProcCmdDrawMultiEXT +CmdDrawMultiIndexedEXT: ProcCmdDrawMultiIndexedEXT +CmdEndConditionalRenderingEXT: ProcCmdEndConditionalRenderingEXT +CmdEndDebugUtilsLabelEXT: ProcCmdEndDebugUtilsLabelEXT +CmdEndQuery: ProcCmdEndQuery +CmdEndQueryIndexedEXT: ProcCmdEndQueryIndexedEXT +CmdEndRenderPass: ProcCmdEndRenderPass +CmdEndRenderPass2: ProcCmdEndRenderPass2 +CmdEndRenderPass2KHR: ProcCmdEndRenderPass2KHR +CmdEndRendering: ProcCmdEndRendering +CmdEndRenderingKHR: ProcCmdEndRenderingKHR +CmdEndTransformFeedbackEXT: ProcCmdEndTransformFeedbackEXT +CmdExecuteCommands: ProcCmdExecuteCommands +CmdExecuteGeneratedCommandsNV: ProcCmdExecuteGeneratedCommandsNV +CmdFillBuffer: ProcCmdFillBuffer +CmdInsertDebugUtilsLabelEXT: ProcCmdInsertDebugUtilsLabelEXT +CmdNextSubpass: ProcCmdNextSubpass +CmdNextSubpass2: ProcCmdNextSubpass2 +CmdNextSubpass2KHR: ProcCmdNextSubpass2KHR +CmdPipelineBarrier: ProcCmdPipelineBarrier +CmdPipelineBarrier2: ProcCmdPipelineBarrier2 +CmdPipelineBarrier2KHR: ProcCmdPipelineBarrier2KHR +CmdPreprocessGeneratedCommandsNV: ProcCmdPreprocessGeneratedCommandsNV +CmdPushConstants: ProcCmdPushConstants +CmdPushDescriptorSetKHR: ProcCmdPushDescriptorSetKHR +CmdPushDescriptorSetWithTemplateKHR: ProcCmdPushDescriptorSetWithTemplateKHR +CmdResetEvent: ProcCmdResetEvent +CmdResetEvent2: ProcCmdResetEvent2 +CmdResetEvent2KHR: ProcCmdResetEvent2KHR +CmdResetQueryPool: ProcCmdResetQueryPool +CmdResolveImage: ProcCmdResolveImage +CmdResolveImage2: ProcCmdResolveImage2 +CmdResolveImage2KHR: ProcCmdResolveImage2KHR +CmdSetBlendConstants: ProcCmdSetBlendConstants +CmdSetCheckpointNV: ProcCmdSetCheckpointNV +CmdSetCoarseSampleOrderNV: ProcCmdSetCoarseSampleOrderNV +CmdSetCullMode: ProcCmdSetCullMode +CmdSetCullModeEXT: ProcCmdSetCullModeEXT +CmdSetDepthBias: ProcCmdSetDepthBias +CmdSetDepthBiasEnable: ProcCmdSetDepthBiasEnable +CmdSetDepthBiasEnableEXT: ProcCmdSetDepthBiasEnableEXT +CmdSetDepthBounds: ProcCmdSetDepthBounds +CmdSetDepthBoundsTestEnable: ProcCmdSetDepthBoundsTestEnable +CmdSetDepthBoundsTestEnableEXT: ProcCmdSetDepthBoundsTestEnableEXT +CmdSetDepthCompareOp: ProcCmdSetDepthCompareOp +CmdSetDepthCompareOpEXT: ProcCmdSetDepthCompareOpEXT +CmdSetDepthTestEnable: ProcCmdSetDepthTestEnable +CmdSetDepthTestEnableEXT: ProcCmdSetDepthTestEnableEXT +CmdSetDepthWriteEnable: ProcCmdSetDepthWriteEnable +CmdSetDepthWriteEnableEXT: ProcCmdSetDepthWriteEnableEXT +CmdSetDeviceMask: ProcCmdSetDeviceMask +CmdSetDeviceMaskKHR: ProcCmdSetDeviceMaskKHR +CmdSetDiscardRectangleEXT: ProcCmdSetDiscardRectangleEXT +CmdSetEvent: ProcCmdSetEvent +CmdSetEvent2: ProcCmdSetEvent2 +CmdSetEvent2KHR: ProcCmdSetEvent2KHR +CmdSetExclusiveScissorNV: ProcCmdSetExclusiveScissorNV +CmdSetFragmentShadingRateEnumNV: ProcCmdSetFragmentShadingRateEnumNV +CmdSetFragmentShadingRateKHR: ProcCmdSetFragmentShadingRateKHR +CmdSetFrontFace: ProcCmdSetFrontFace +CmdSetFrontFaceEXT: ProcCmdSetFrontFaceEXT +CmdSetLineStippleEXT: ProcCmdSetLineStippleEXT +CmdSetLineWidth: ProcCmdSetLineWidth +CmdSetLogicOpEXT: ProcCmdSetLogicOpEXT +CmdSetPatchControlPointsEXT: ProcCmdSetPatchControlPointsEXT +CmdSetPerformanceMarkerINTEL: ProcCmdSetPerformanceMarkerINTEL +CmdSetPerformanceOverrideINTEL: ProcCmdSetPerformanceOverrideINTEL +CmdSetPerformanceStreamMarkerINTEL: ProcCmdSetPerformanceStreamMarkerINTEL +CmdSetPrimitiveRestartEnable: ProcCmdSetPrimitiveRestartEnable +CmdSetPrimitiveRestartEnableEXT: ProcCmdSetPrimitiveRestartEnableEXT +CmdSetPrimitiveTopology: ProcCmdSetPrimitiveTopology +CmdSetPrimitiveTopologyEXT: ProcCmdSetPrimitiveTopologyEXT +CmdSetRasterizerDiscardEnable: ProcCmdSetRasterizerDiscardEnable +CmdSetRasterizerDiscardEnableEXT: ProcCmdSetRasterizerDiscardEnableEXT +CmdSetRayTracingPipelineStackSizeKHR: ProcCmdSetRayTracingPipelineStackSizeKHR +CmdSetSampleLocationsEXT: ProcCmdSetSampleLocationsEXT +CmdSetScissor: ProcCmdSetScissor +CmdSetScissorWithCount: ProcCmdSetScissorWithCount +CmdSetScissorWithCountEXT: ProcCmdSetScissorWithCountEXT +CmdSetStencilCompareMask: ProcCmdSetStencilCompareMask +CmdSetStencilOp: ProcCmdSetStencilOp +CmdSetStencilOpEXT: ProcCmdSetStencilOpEXT +CmdSetStencilReference: ProcCmdSetStencilReference +CmdSetStencilTestEnable: ProcCmdSetStencilTestEnable +CmdSetStencilTestEnableEXT: ProcCmdSetStencilTestEnableEXT +CmdSetStencilWriteMask: ProcCmdSetStencilWriteMask +CmdSetVertexInputEXT: ProcCmdSetVertexInputEXT +CmdSetViewport: ProcCmdSetViewport +CmdSetViewportShadingRatePaletteNV: ProcCmdSetViewportShadingRatePaletteNV +CmdSetViewportWScalingNV: ProcCmdSetViewportWScalingNV +CmdSetViewportWithCount: ProcCmdSetViewportWithCount +CmdSetViewportWithCountEXT: ProcCmdSetViewportWithCountEXT +CmdSubpassShadingHUAWEI: ProcCmdSubpassShadingHUAWEI +CmdTraceRaysIndirectKHR: ProcCmdTraceRaysIndirectKHR +CmdTraceRaysKHR: ProcCmdTraceRaysKHR +CmdTraceRaysNV: ProcCmdTraceRaysNV +CmdUpdateBuffer: ProcCmdUpdateBuffer +CmdWaitEvents: ProcCmdWaitEvents +CmdWaitEvents2: ProcCmdWaitEvents2 +CmdWaitEvents2KHR: ProcCmdWaitEvents2KHR +CmdWriteAccelerationStructuresPropertiesKHR: ProcCmdWriteAccelerationStructuresPropertiesKHR +CmdWriteAccelerationStructuresPropertiesNV: ProcCmdWriteAccelerationStructuresPropertiesNV +CmdWriteBufferMarker2AMD: ProcCmdWriteBufferMarker2AMD +CmdWriteBufferMarkerAMD: ProcCmdWriteBufferMarkerAMD +CmdWriteTimestamp: ProcCmdWriteTimestamp +CmdWriteTimestamp2: ProcCmdWriteTimestamp2 +CmdWriteTimestamp2KHR: ProcCmdWriteTimestamp2KHR +CompileDeferredNV: ProcCompileDeferredNV +CopyAccelerationStructureKHR: ProcCopyAccelerationStructureKHR +CopyAccelerationStructureToMemoryKHR: ProcCopyAccelerationStructureToMemoryKHR +CopyMemoryToAccelerationStructureKHR: ProcCopyMemoryToAccelerationStructureKHR +CreateAccelerationStructureKHR: ProcCreateAccelerationStructureKHR +CreateAccelerationStructureNV: ProcCreateAccelerationStructureNV +CreateBuffer: ProcCreateBuffer +CreateBufferView: ProcCreateBufferView +CreateCommandPool: ProcCreateCommandPool +CreateComputePipelines: ProcCreateComputePipelines +CreateCuFunctionNVX: ProcCreateCuFunctionNVX +CreateCuModuleNVX: ProcCreateCuModuleNVX +CreateDeferredOperationKHR: ProcCreateDeferredOperationKHR +CreateDescriptorPool: ProcCreateDescriptorPool +CreateDescriptorSetLayout: ProcCreateDescriptorSetLayout +CreateDescriptorUpdateTemplate: ProcCreateDescriptorUpdateTemplate +CreateDescriptorUpdateTemplateKHR: ProcCreateDescriptorUpdateTemplateKHR +CreateEvent: ProcCreateEvent +CreateFence: ProcCreateFence +CreateFramebuffer: ProcCreateFramebuffer +CreateGraphicsPipelines: ProcCreateGraphicsPipelines +CreateImage: ProcCreateImage +CreateImageView: ProcCreateImageView +CreateIndirectCommandsLayoutNV: ProcCreateIndirectCommandsLayoutNV +CreatePipelineCache: ProcCreatePipelineCache +CreatePipelineLayout: ProcCreatePipelineLayout +CreatePrivateDataSlot: ProcCreatePrivateDataSlot +CreatePrivateDataSlotEXT: ProcCreatePrivateDataSlotEXT +CreateQueryPool: ProcCreateQueryPool +CreateRayTracingPipelinesKHR: ProcCreateRayTracingPipelinesKHR +CreateRayTracingPipelinesNV: ProcCreateRayTracingPipelinesNV +CreateRenderPass: ProcCreateRenderPass +CreateRenderPass2: ProcCreateRenderPass2 +CreateRenderPass2KHR: ProcCreateRenderPass2KHR +CreateSampler: ProcCreateSampler +CreateSamplerYcbcrConversion: ProcCreateSamplerYcbcrConversion +CreateSamplerYcbcrConversionKHR: ProcCreateSamplerYcbcrConversionKHR +CreateSemaphore: ProcCreateSemaphore +CreateShaderModule: ProcCreateShaderModule +CreateSharedSwapchainsKHR: ProcCreateSharedSwapchainsKHR +CreateSwapchainKHR: ProcCreateSwapchainKHR +CreateValidationCacheEXT: ProcCreateValidationCacheEXT +DebugMarkerSetObjectNameEXT: ProcDebugMarkerSetObjectNameEXT +DebugMarkerSetObjectTagEXT: ProcDebugMarkerSetObjectTagEXT +DeferredOperationJoinKHR: ProcDeferredOperationJoinKHR +DestroyAccelerationStructureKHR: ProcDestroyAccelerationStructureKHR +DestroyAccelerationStructureNV: ProcDestroyAccelerationStructureNV +DestroyBuffer: ProcDestroyBuffer +DestroyBufferView: ProcDestroyBufferView +DestroyCommandPool: ProcDestroyCommandPool +DestroyCuFunctionNVX: ProcDestroyCuFunctionNVX +DestroyCuModuleNVX: ProcDestroyCuModuleNVX +DestroyDeferredOperationKHR: ProcDestroyDeferredOperationKHR +DestroyDescriptorPool: ProcDestroyDescriptorPool +DestroyDescriptorSetLayout: ProcDestroyDescriptorSetLayout +DestroyDescriptorUpdateTemplate: ProcDestroyDescriptorUpdateTemplate +DestroyDescriptorUpdateTemplateKHR: ProcDestroyDescriptorUpdateTemplateKHR +DestroyDevice: ProcDestroyDevice +DestroyEvent: ProcDestroyEvent +DestroyFence: ProcDestroyFence +DestroyFramebuffer: ProcDestroyFramebuffer +DestroyImage: ProcDestroyImage +DestroyImageView: ProcDestroyImageView +DestroyIndirectCommandsLayoutNV: ProcDestroyIndirectCommandsLayoutNV +DestroyPipeline: ProcDestroyPipeline +DestroyPipelineCache: ProcDestroyPipelineCache +DestroyPipelineLayout: ProcDestroyPipelineLayout +DestroyPrivateDataSlot: ProcDestroyPrivateDataSlot +DestroyPrivateDataSlotEXT: ProcDestroyPrivateDataSlotEXT +DestroyQueryPool: ProcDestroyQueryPool +DestroyRenderPass: ProcDestroyRenderPass +DestroySampler: ProcDestroySampler +DestroySamplerYcbcrConversion: ProcDestroySamplerYcbcrConversion +DestroySamplerYcbcrConversionKHR: ProcDestroySamplerYcbcrConversionKHR +DestroySemaphore: ProcDestroySemaphore +DestroyShaderModule: ProcDestroyShaderModule +DestroySwapchainKHR: ProcDestroySwapchainKHR +DestroyValidationCacheEXT: ProcDestroyValidationCacheEXT +DeviceWaitIdle: ProcDeviceWaitIdle +DisplayPowerControlEXT: ProcDisplayPowerControlEXT +EndCommandBuffer: ProcEndCommandBuffer +FlushMappedMemoryRanges: ProcFlushMappedMemoryRanges +FreeCommandBuffers: ProcFreeCommandBuffers +FreeDescriptorSets: ProcFreeDescriptorSets +FreeMemory: ProcFreeMemory +GetAccelerationStructureBuildSizesKHR: ProcGetAccelerationStructureBuildSizesKHR +GetAccelerationStructureDeviceAddressKHR: ProcGetAccelerationStructureDeviceAddressKHR +GetAccelerationStructureHandleNV: ProcGetAccelerationStructureHandleNV +GetAccelerationStructureMemoryRequirementsNV: ProcGetAccelerationStructureMemoryRequirementsNV +GetBufferDeviceAddress: ProcGetBufferDeviceAddress +GetBufferDeviceAddressEXT: ProcGetBufferDeviceAddressEXT +GetBufferDeviceAddressKHR: ProcGetBufferDeviceAddressKHR +GetBufferMemoryRequirements: ProcGetBufferMemoryRequirements +GetBufferMemoryRequirements2: ProcGetBufferMemoryRequirements2 +GetBufferMemoryRequirements2KHR: ProcGetBufferMemoryRequirements2KHR +GetBufferOpaqueCaptureAddress: ProcGetBufferOpaqueCaptureAddress +GetBufferOpaqueCaptureAddressKHR: ProcGetBufferOpaqueCaptureAddressKHR +GetCalibratedTimestampsEXT: ProcGetCalibratedTimestampsEXT +GetDeferredOperationMaxConcurrencyKHR: ProcGetDeferredOperationMaxConcurrencyKHR +GetDeferredOperationResultKHR: ProcGetDeferredOperationResultKHR +GetDescriptorSetHostMappingVALVE: ProcGetDescriptorSetHostMappingVALVE +GetDescriptorSetLayoutHostMappingInfoVALVE: ProcGetDescriptorSetLayoutHostMappingInfoVALVE +GetDescriptorSetLayoutSupport: ProcGetDescriptorSetLayoutSupport +GetDescriptorSetLayoutSupportKHR: ProcGetDescriptorSetLayoutSupportKHR +GetDeviceAccelerationStructureCompatibilityKHR: ProcGetDeviceAccelerationStructureCompatibilityKHR +GetDeviceBufferMemoryRequirements: ProcGetDeviceBufferMemoryRequirements +GetDeviceBufferMemoryRequirementsKHR: ProcGetDeviceBufferMemoryRequirementsKHR +GetDeviceGroupPeerMemoryFeatures: ProcGetDeviceGroupPeerMemoryFeatures +GetDeviceGroupPeerMemoryFeaturesKHR: ProcGetDeviceGroupPeerMemoryFeaturesKHR +GetDeviceGroupPresentCapabilitiesKHR: ProcGetDeviceGroupPresentCapabilitiesKHR +GetDeviceGroupSurfacePresentModes2EXT: ProcGetDeviceGroupSurfacePresentModes2EXT +GetDeviceGroupSurfacePresentModesKHR: ProcGetDeviceGroupSurfacePresentModesKHR +GetDeviceImageMemoryRequirements: ProcGetDeviceImageMemoryRequirements +GetDeviceImageMemoryRequirementsKHR: ProcGetDeviceImageMemoryRequirementsKHR +GetDeviceImageSparseMemoryRequirements: ProcGetDeviceImageSparseMemoryRequirements +GetDeviceImageSparseMemoryRequirementsKHR: ProcGetDeviceImageSparseMemoryRequirementsKHR +GetDeviceMemoryCommitment: ProcGetDeviceMemoryCommitment +GetDeviceMemoryOpaqueCaptureAddress: ProcGetDeviceMemoryOpaqueCaptureAddress +GetDeviceMemoryOpaqueCaptureAddressKHR: ProcGetDeviceMemoryOpaqueCaptureAddressKHR +GetDeviceProcAddr: ProcGetDeviceProcAddr +GetDeviceQueue: ProcGetDeviceQueue +GetDeviceQueue2: ProcGetDeviceQueue2 +GetDeviceSubpassShadingMaxWorkgroupSizeHUAWEI: ProcGetDeviceSubpassShadingMaxWorkgroupSizeHUAWEI +GetEventStatus: ProcGetEventStatus +GetFenceFdKHR: ProcGetFenceFdKHR +GetFenceStatus: ProcGetFenceStatus +GetFenceWin32HandleKHR: ProcGetFenceWin32HandleKHR +GetGeneratedCommandsMemoryRequirementsNV: ProcGetGeneratedCommandsMemoryRequirementsNV +GetImageDrmFormatModifierPropertiesEXT: ProcGetImageDrmFormatModifierPropertiesEXT +GetImageMemoryRequirements: ProcGetImageMemoryRequirements +GetImageMemoryRequirements2: ProcGetImageMemoryRequirements2 +GetImageMemoryRequirements2KHR: ProcGetImageMemoryRequirements2KHR +GetImageSparseMemoryRequirements: ProcGetImageSparseMemoryRequirements +GetImageSparseMemoryRequirements2: ProcGetImageSparseMemoryRequirements2 +GetImageSparseMemoryRequirements2KHR: ProcGetImageSparseMemoryRequirements2KHR +GetImageSubresourceLayout: ProcGetImageSubresourceLayout +GetImageViewAddressNVX: ProcGetImageViewAddressNVX +GetImageViewHandleNVX: ProcGetImageViewHandleNVX +GetMemoryFdKHR: ProcGetMemoryFdKHR +GetMemoryFdPropertiesKHR: ProcGetMemoryFdPropertiesKHR +GetMemoryHostPointerPropertiesEXT: ProcGetMemoryHostPointerPropertiesEXT +GetMemoryRemoteAddressNV: ProcGetMemoryRemoteAddressNV +GetMemoryWin32HandleKHR: ProcGetMemoryWin32HandleKHR +GetMemoryWin32HandleNV: ProcGetMemoryWin32HandleNV +GetMemoryWin32HandlePropertiesKHR: ProcGetMemoryWin32HandlePropertiesKHR +GetPastPresentationTimingGOOGLE: ProcGetPastPresentationTimingGOOGLE +GetPerformanceParameterINTEL: ProcGetPerformanceParameterINTEL +GetPipelineCacheData: ProcGetPipelineCacheData +GetPipelineExecutableInternalRepresentationsKHR: ProcGetPipelineExecutableInternalRepresentationsKHR +GetPipelineExecutablePropertiesKHR: ProcGetPipelineExecutablePropertiesKHR +GetPipelineExecutableStatisticsKHR: ProcGetPipelineExecutableStatisticsKHR +GetPrivateData: ProcGetPrivateData +GetPrivateDataEXT: ProcGetPrivateDataEXT +GetQueryPoolResults: ProcGetQueryPoolResults +GetQueueCheckpointData2NV: ProcGetQueueCheckpointData2NV +GetQueueCheckpointDataNV: ProcGetQueueCheckpointDataNV +GetRayTracingCaptureReplayShaderGroupHandlesKHR: ProcGetRayTracingCaptureReplayShaderGroupHandlesKHR +GetRayTracingShaderGroupHandlesKHR: ProcGetRayTracingShaderGroupHandlesKHR +GetRayTracingShaderGroupHandlesNV: ProcGetRayTracingShaderGroupHandlesNV +GetRayTracingShaderGroupStackSizeKHR: ProcGetRayTracingShaderGroupStackSizeKHR +GetRefreshCycleDurationGOOGLE: ProcGetRefreshCycleDurationGOOGLE +GetRenderAreaGranularity: ProcGetRenderAreaGranularity +GetSemaphoreCounterValue: ProcGetSemaphoreCounterValue +GetSemaphoreCounterValueKHR: ProcGetSemaphoreCounterValueKHR +GetSemaphoreFdKHR: ProcGetSemaphoreFdKHR +GetSemaphoreWin32HandleKHR: ProcGetSemaphoreWin32HandleKHR +GetShaderInfoAMD: ProcGetShaderInfoAMD +GetSwapchainCounterEXT: ProcGetSwapchainCounterEXT +GetSwapchainImagesKHR: ProcGetSwapchainImagesKHR +GetSwapchainStatusKHR: ProcGetSwapchainStatusKHR +GetValidationCacheDataEXT: ProcGetValidationCacheDataEXT +ImportFenceFdKHR: ProcImportFenceFdKHR +ImportFenceWin32HandleKHR: ProcImportFenceWin32HandleKHR +ImportSemaphoreFdKHR: ProcImportSemaphoreFdKHR +ImportSemaphoreWin32HandleKHR: ProcImportSemaphoreWin32HandleKHR +InitializePerformanceApiINTEL: ProcInitializePerformanceApiINTEL +InvalidateMappedMemoryRanges: ProcInvalidateMappedMemoryRanges +MapMemory: ProcMapMemory +MergePipelineCaches: ProcMergePipelineCaches +MergeValidationCachesEXT: ProcMergeValidationCachesEXT +QueueBeginDebugUtilsLabelEXT: ProcQueueBeginDebugUtilsLabelEXT +QueueBindSparse: ProcQueueBindSparse +QueueEndDebugUtilsLabelEXT: ProcQueueEndDebugUtilsLabelEXT +QueueInsertDebugUtilsLabelEXT: ProcQueueInsertDebugUtilsLabelEXT +QueuePresentKHR: ProcQueuePresentKHR +QueueSetPerformanceConfigurationINTEL: ProcQueueSetPerformanceConfigurationINTEL +QueueSubmit: ProcQueueSubmit +QueueSubmit2: ProcQueueSubmit2 +QueueSubmit2KHR: ProcQueueSubmit2KHR +QueueWaitIdle: ProcQueueWaitIdle +RegisterDeviceEventEXT: ProcRegisterDeviceEventEXT +RegisterDisplayEventEXT: ProcRegisterDisplayEventEXT +ReleaseFullScreenExclusiveModeEXT: ProcReleaseFullScreenExclusiveModeEXT +ReleasePerformanceConfigurationINTEL: ProcReleasePerformanceConfigurationINTEL +ReleaseProfilingLockKHR: ProcReleaseProfilingLockKHR +ResetCommandBuffer: ProcResetCommandBuffer +ResetCommandPool: ProcResetCommandPool +ResetDescriptorPool: ProcResetDescriptorPool +ResetEvent: ProcResetEvent +ResetFences: ProcResetFences +ResetQueryPool: ProcResetQueryPool +ResetQueryPoolEXT: ProcResetQueryPoolEXT +SetDebugUtilsObjectNameEXT: ProcSetDebugUtilsObjectNameEXT +SetDebugUtilsObjectTagEXT: ProcSetDebugUtilsObjectTagEXT +SetDeviceMemoryPriorityEXT: ProcSetDeviceMemoryPriorityEXT +SetEvent: ProcSetEvent +SetHdrMetadataEXT: ProcSetHdrMetadataEXT +SetLocalDimmingAMD: ProcSetLocalDimmingAMD +SetPrivateData: ProcSetPrivateData +SetPrivateDataEXT: ProcSetPrivateDataEXT +SignalSemaphore: ProcSignalSemaphore +SignalSemaphoreKHR: ProcSignalSemaphoreKHR +TrimCommandPool: ProcTrimCommandPool +TrimCommandPoolKHR: ProcTrimCommandPoolKHR +UninitializePerformanceApiINTEL: ProcUninitializePerformanceApiINTEL +UnmapMemory: ProcUnmapMemory +UpdateDescriptorSetWithTemplate: ProcUpdateDescriptorSetWithTemplate +UpdateDescriptorSetWithTemplateKHR: ProcUpdateDescriptorSetWithTemplateKHR +UpdateDescriptorSets: ProcUpdateDescriptorSets +WaitForFences: ProcWaitForFences +WaitForPresentKHR: ProcWaitForPresentKHR +WaitSemaphores: ProcWaitSemaphores +WaitSemaphoresKHR: ProcWaitSemaphoresKHR +WriteAccelerationStructuresPropertiesKHR: ProcWriteAccelerationStructuresPropertiesKHR +load_proc_addresses_custom :: proc(set_proc_address: SetProcAddressType) { // Loader Procedures set_proc_address(&CreateInstance, "vkCreateInstance") + set_proc_address(&DebugUtilsMessengerCallbackEXT, "vkDebugUtilsMessengerCallbackEXT") + set_proc_address(&DeviceMemoryReportCallbackEXT, "vkDeviceMemoryReportCallbackEXT") set_proc_address(&EnumerateInstanceExtensionProperties, "vkEnumerateInstanceExtensionProperties") set_proc_address(&EnumerateInstanceLayerProperties, "vkEnumerateInstanceLayerProperties") set_proc_address(&EnumerateInstanceVersion, "vkEnumerateInstanceVersion") - set_proc_address(&DebugUtilsMessengerCallbackEXT, "vkDebugUtilsMessengerCallbackEXT") - set_proc_address(&DeviceMemoryReportCallbackEXT, "vkDeviceMemoryReportCallbackEXT") + + // Instance Procedures + set_proc_address(&AcquireDrmDisplayEXT, "vkAcquireDrmDisplayEXT") + set_proc_address(&AcquireWinrtDisplayNV, "vkAcquireWinrtDisplayNV") + set_proc_address(&CreateDebugReportCallbackEXT, "vkCreateDebugReportCallbackEXT") + set_proc_address(&CreateDebugUtilsMessengerEXT, "vkCreateDebugUtilsMessengerEXT") + set_proc_address(&CreateDevice, "vkCreateDevice") + set_proc_address(&CreateDisplayModeKHR, "vkCreateDisplayModeKHR") + set_proc_address(&CreateDisplayPlaneSurfaceKHR, "vkCreateDisplayPlaneSurfaceKHR") + set_proc_address(&CreateHeadlessSurfaceEXT, "vkCreateHeadlessSurfaceEXT") + set_proc_address(&CreateIOSSurfaceMVK, "vkCreateIOSSurfaceMVK") + set_proc_address(&CreateMacOSSurfaceMVK, "vkCreateMacOSSurfaceMVK") + set_proc_address(&CreateMetalSurfaceEXT, "vkCreateMetalSurfaceEXT") + set_proc_address(&CreateWin32SurfaceKHR, "vkCreateWin32SurfaceKHR") + set_proc_address(&DebugReportMessageEXT, "vkDebugReportMessageEXT") + set_proc_address(&DestroyDebugReportCallbackEXT, "vkDestroyDebugReportCallbackEXT") + set_proc_address(&DestroyDebugUtilsMessengerEXT, "vkDestroyDebugUtilsMessengerEXT") + set_proc_address(&DestroyInstance, "vkDestroyInstance") + set_proc_address(&DestroySurfaceKHR, "vkDestroySurfaceKHR") + set_proc_address(&EnumerateDeviceExtensionProperties, "vkEnumerateDeviceExtensionProperties") + set_proc_address(&EnumerateDeviceLayerProperties, "vkEnumerateDeviceLayerProperties") + set_proc_address(&EnumeratePhysicalDeviceGroups, "vkEnumeratePhysicalDeviceGroups") + set_proc_address(&EnumeratePhysicalDeviceGroupsKHR, "vkEnumeratePhysicalDeviceGroupsKHR") + set_proc_address(&EnumeratePhysicalDeviceQueueFamilyPerformanceQueryCountersKHR, "vkEnumeratePhysicalDeviceQueueFamilyPerformanceQueryCountersKHR") + set_proc_address(&EnumeratePhysicalDevices, "vkEnumeratePhysicalDevices") + set_proc_address(&GetDisplayModeProperties2KHR, "vkGetDisplayModeProperties2KHR") + set_proc_address(&GetDisplayModePropertiesKHR, "vkGetDisplayModePropertiesKHR") + set_proc_address(&GetDisplayPlaneCapabilities2KHR, "vkGetDisplayPlaneCapabilities2KHR") + set_proc_address(&GetDisplayPlaneCapabilitiesKHR, "vkGetDisplayPlaneCapabilitiesKHR") + set_proc_address(&GetDisplayPlaneSupportedDisplaysKHR, "vkGetDisplayPlaneSupportedDisplaysKHR") + set_proc_address(&GetDrmDisplayEXT, "vkGetDrmDisplayEXT") + set_proc_address(&GetInstanceProcAddr, "vkGetInstanceProcAddr") + set_proc_address(&GetPhysicalDeviceCalibrateableTimeDomainsEXT, "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT") + set_proc_address(&GetPhysicalDeviceCooperativeMatrixPropertiesNV, "vkGetPhysicalDeviceCooperativeMatrixPropertiesNV") + set_proc_address(&GetPhysicalDeviceDisplayPlaneProperties2KHR, "vkGetPhysicalDeviceDisplayPlaneProperties2KHR") + set_proc_address(&GetPhysicalDeviceDisplayPlanePropertiesKHR, "vkGetPhysicalDeviceDisplayPlanePropertiesKHR") + set_proc_address(&GetPhysicalDeviceDisplayProperties2KHR, "vkGetPhysicalDeviceDisplayProperties2KHR") + set_proc_address(&GetPhysicalDeviceDisplayPropertiesKHR, "vkGetPhysicalDeviceDisplayPropertiesKHR") + set_proc_address(&GetPhysicalDeviceExternalBufferProperties, "vkGetPhysicalDeviceExternalBufferProperties") + set_proc_address(&GetPhysicalDeviceExternalBufferPropertiesKHR, "vkGetPhysicalDeviceExternalBufferPropertiesKHR") + set_proc_address(&GetPhysicalDeviceExternalFenceProperties, "vkGetPhysicalDeviceExternalFenceProperties") + set_proc_address(&GetPhysicalDeviceExternalFencePropertiesKHR, "vkGetPhysicalDeviceExternalFencePropertiesKHR") + set_proc_address(&GetPhysicalDeviceExternalImageFormatPropertiesNV, "vkGetPhysicalDeviceExternalImageFormatPropertiesNV") + set_proc_address(&GetPhysicalDeviceExternalSemaphoreProperties, "vkGetPhysicalDeviceExternalSemaphoreProperties") + set_proc_address(&GetPhysicalDeviceExternalSemaphorePropertiesKHR, "vkGetPhysicalDeviceExternalSemaphorePropertiesKHR") + set_proc_address(&GetPhysicalDeviceFeatures, "vkGetPhysicalDeviceFeatures") + set_proc_address(&GetPhysicalDeviceFeatures2, "vkGetPhysicalDeviceFeatures2") + set_proc_address(&GetPhysicalDeviceFeatures2KHR, "vkGetPhysicalDeviceFeatures2KHR") + set_proc_address(&GetPhysicalDeviceFormatProperties, "vkGetPhysicalDeviceFormatProperties") + set_proc_address(&GetPhysicalDeviceFormatProperties2, "vkGetPhysicalDeviceFormatProperties2") + set_proc_address(&GetPhysicalDeviceFormatProperties2KHR, "vkGetPhysicalDeviceFormatProperties2KHR") + set_proc_address(&GetPhysicalDeviceFragmentShadingRatesKHR, "vkGetPhysicalDeviceFragmentShadingRatesKHR") + set_proc_address(&GetPhysicalDeviceImageFormatProperties, "vkGetPhysicalDeviceImageFormatProperties") + set_proc_address(&GetPhysicalDeviceImageFormatProperties2, "vkGetPhysicalDeviceImageFormatProperties2") + set_proc_address(&GetPhysicalDeviceImageFormatProperties2KHR, "vkGetPhysicalDeviceImageFormatProperties2KHR") + set_proc_address(&GetPhysicalDeviceMemoryProperties, "vkGetPhysicalDeviceMemoryProperties") + set_proc_address(&GetPhysicalDeviceMemoryProperties2, "vkGetPhysicalDeviceMemoryProperties2") + set_proc_address(&GetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2KHR") + set_proc_address(&GetPhysicalDeviceMultisamplePropertiesEXT, "vkGetPhysicalDeviceMultisamplePropertiesEXT") + set_proc_address(&GetPhysicalDevicePresentRectanglesKHR, "vkGetPhysicalDevicePresentRectanglesKHR") + set_proc_address(&GetPhysicalDeviceProperties, "vkGetPhysicalDeviceProperties") + set_proc_address(&GetPhysicalDeviceProperties2, "vkGetPhysicalDeviceProperties2") + set_proc_address(&GetPhysicalDeviceProperties2KHR, "vkGetPhysicalDeviceProperties2KHR") + set_proc_address(&GetPhysicalDeviceQueueFamilyPerformanceQueryPassesKHR, "vkGetPhysicalDeviceQueueFamilyPerformanceQueryPassesKHR") + set_proc_address(&GetPhysicalDeviceQueueFamilyProperties, "vkGetPhysicalDeviceQueueFamilyProperties") + set_proc_address(&GetPhysicalDeviceQueueFamilyProperties2, "vkGetPhysicalDeviceQueueFamilyProperties2") + set_proc_address(&GetPhysicalDeviceQueueFamilyProperties2KHR, "vkGetPhysicalDeviceQueueFamilyProperties2KHR") + set_proc_address(&GetPhysicalDeviceSparseImageFormatProperties, "vkGetPhysicalDeviceSparseImageFormatProperties") + set_proc_address(&GetPhysicalDeviceSparseImageFormatProperties2, "vkGetPhysicalDeviceSparseImageFormatProperties2") + set_proc_address(&GetPhysicalDeviceSparseImageFormatProperties2KHR, "vkGetPhysicalDeviceSparseImageFormatProperties2KHR") + set_proc_address(&GetPhysicalDeviceSupportedFramebufferMixedSamplesCombinationsNV, "vkGetPhysicalDeviceSupportedFramebufferMixedSamplesCombinationsNV") + set_proc_address(&GetPhysicalDeviceSurfaceCapabilities2EXT, "vkGetPhysicalDeviceSurfaceCapabilities2EXT") + set_proc_address(&GetPhysicalDeviceSurfaceCapabilities2KHR, "vkGetPhysicalDeviceSurfaceCapabilities2KHR") + set_proc_address(&GetPhysicalDeviceSurfaceCapabilitiesKHR, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR") + set_proc_address(&GetPhysicalDeviceSurfaceFormats2KHR, "vkGetPhysicalDeviceSurfaceFormats2KHR") + set_proc_address(&GetPhysicalDeviceSurfaceFormatsKHR, "vkGetPhysicalDeviceSurfaceFormatsKHR") + set_proc_address(&GetPhysicalDeviceSurfacePresentModes2EXT, "vkGetPhysicalDeviceSurfacePresentModes2EXT") + set_proc_address(&GetPhysicalDeviceSurfacePresentModesKHR, "vkGetPhysicalDeviceSurfacePresentModesKHR") + set_proc_address(&GetPhysicalDeviceSurfaceSupportKHR, "vkGetPhysicalDeviceSurfaceSupportKHR") + set_proc_address(&GetPhysicalDeviceToolProperties, "vkGetPhysicalDeviceToolProperties") + set_proc_address(&GetPhysicalDeviceToolPropertiesEXT, "vkGetPhysicalDeviceToolPropertiesEXT") + set_proc_address(&GetPhysicalDeviceWin32PresentationSupportKHR, "vkGetPhysicalDeviceWin32PresentationSupportKHR") + set_proc_address(&GetWinrtDisplayNV, "vkGetWinrtDisplayNV") + set_proc_address(&ReleaseDisplayEXT, "vkReleaseDisplayEXT") + set_proc_address(&SubmitDebugUtilsMessageEXT, "vkSubmitDebugUtilsMessageEXT") + + // Device Procedures + set_proc_address(&AcquireFullScreenExclusiveModeEXT, "vkAcquireFullScreenExclusiveModeEXT") + set_proc_address(&AcquireNextImage2KHR, "vkAcquireNextImage2KHR") + set_proc_address(&AcquireNextImageKHR, "vkAcquireNextImageKHR") + set_proc_address(&AcquirePerformanceConfigurationINTEL, "vkAcquirePerformanceConfigurationINTEL") + set_proc_address(&AcquireProfilingLockKHR, "vkAcquireProfilingLockKHR") + set_proc_address(&AllocateCommandBuffers, "vkAllocateCommandBuffers") + set_proc_address(&AllocateDescriptorSets, "vkAllocateDescriptorSets") + set_proc_address(&AllocateMemory, "vkAllocateMemory") + set_proc_address(&BeginCommandBuffer, "vkBeginCommandBuffer") + set_proc_address(&BindAccelerationStructureMemoryNV, "vkBindAccelerationStructureMemoryNV") + set_proc_address(&BindBufferMemory, "vkBindBufferMemory") + set_proc_address(&BindBufferMemory2, "vkBindBufferMemory2") + set_proc_address(&BindBufferMemory2KHR, "vkBindBufferMemory2KHR") + set_proc_address(&BindImageMemory, "vkBindImageMemory") + set_proc_address(&BindImageMemory2, "vkBindImageMemory2") + set_proc_address(&BindImageMemory2KHR, "vkBindImageMemory2KHR") + set_proc_address(&BuildAccelerationStructuresKHR, "vkBuildAccelerationStructuresKHR") + set_proc_address(&CmdBeginConditionalRenderingEXT, "vkCmdBeginConditionalRenderingEXT") + set_proc_address(&CmdBeginDebugUtilsLabelEXT, "vkCmdBeginDebugUtilsLabelEXT") + set_proc_address(&CmdBeginQuery, "vkCmdBeginQuery") + set_proc_address(&CmdBeginQueryIndexedEXT, "vkCmdBeginQueryIndexedEXT") + set_proc_address(&CmdBeginRenderPass, "vkCmdBeginRenderPass") + set_proc_address(&CmdBeginRenderPass2, "vkCmdBeginRenderPass2") + set_proc_address(&CmdBeginRenderPass2KHR, "vkCmdBeginRenderPass2KHR") + set_proc_address(&CmdBeginRendering, "vkCmdBeginRendering") + set_proc_address(&CmdBeginRenderingKHR, "vkCmdBeginRenderingKHR") + set_proc_address(&CmdBeginTransformFeedbackEXT, "vkCmdBeginTransformFeedbackEXT") + set_proc_address(&CmdBindDescriptorSets, "vkCmdBindDescriptorSets") + set_proc_address(&CmdBindIndexBuffer, "vkCmdBindIndexBuffer") + set_proc_address(&CmdBindInvocationMaskHUAWEI, "vkCmdBindInvocationMaskHUAWEI") + set_proc_address(&CmdBindPipeline, "vkCmdBindPipeline") + set_proc_address(&CmdBindPipelineShaderGroupNV, "vkCmdBindPipelineShaderGroupNV") + set_proc_address(&CmdBindShadingRateImageNV, "vkCmdBindShadingRateImageNV") + set_proc_address(&CmdBindTransformFeedbackBuffersEXT, "vkCmdBindTransformFeedbackBuffersEXT") + set_proc_address(&CmdBindVertexBuffers, "vkCmdBindVertexBuffers") + set_proc_address(&CmdBindVertexBuffers2, "vkCmdBindVertexBuffers2") + set_proc_address(&CmdBindVertexBuffers2EXT, "vkCmdBindVertexBuffers2EXT") + set_proc_address(&CmdBlitImage, "vkCmdBlitImage") + set_proc_address(&CmdBlitImage2, "vkCmdBlitImage2") + set_proc_address(&CmdBlitImage2KHR, "vkCmdBlitImage2KHR") + set_proc_address(&CmdBuildAccelerationStructureNV, "vkCmdBuildAccelerationStructureNV") + set_proc_address(&CmdBuildAccelerationStructuresIndirectKHR, "vkCmdBuildAccelerationStructuresIndirectKHR") + set_proc_address(&CmdBuildAccelerationStructuresKHR, "vkCmdBuildAccelerationStructuresKHR") + set_proc_address(&CmdClearAttachments, "vkCmdClearAttachments") + set_proc_address(&CmdClearColorImage, "vkCmdClearColorImage") + set_proc_address(&CmdClearDepthStencilImage, "vkCmdClearDepthStencilImage") + set_proc_address(&CmdCopyAccelerationStructureKHR, "vkCmdCopyAccelerationStructureKHR") + set_proc_address(&CmdCopyAccelerationStructureNV, "vkCmdCopyAccelerationStructureNV") + set_proc_address(&CmdCopyAccelerationStructureToMemoryKHR, "vkCmdCopyAccelerationStructureToMemoryKHR") + set_proc_address(&CmdCopyBuffer, "vkCmdCopyBuffer") + set_proc_address(&CmdCopyBuffer2, "vkCmdCopyBuffer2") + set_proc_address(&CmdCopyBuffer2KHR, "vkCmdCopyBuffer2KHR") + set_proc_address(&CmdCopyBufferToImage, "vkCmdCopyBufferToImage") + set_proc_address(&CmdCopyBufferToImage2, "vkCmdCopyBufferToImage2") + set_proc_address(&CmdCopyBufferToImage2KHR, "vkCmdCopyBufferToImage2KHR") + set_proc_address(&CmdCopyImage, "vkCmdCopyImage") + set_proc_address(&CmdCopyImage2, "vkCmdCopyImage2") + set_proc_address(&CmdCopyImage2KHR, "vkCmdCopyImage2KHR") + set_proc_address(&CmdCopyImageToBuffer, "vkCmdCopyImageToBuffer") + set_proc_address(&CmdCopyImageToBuffer2, "vkCmdCopyImageToBuffer2") + set_proc_address(&CmdCopyImageToBuffer2KHR, "vkCmdCopyImageToBuffer2KHR") + set_proc_address(&CmdCopyMemoryToAccelerationStructureKHR, "vkCmdCopyMemoryToAccelerationStructureKHR") + set_proc_address(&CmdCopyQueryPoolResults, "vkCmdCopyQueryPoolResults") + set_proc_address(&CmdCuLaunchKernelNVX, "vkCmdCuLaunchKernelNVX") + set_proc_address(&CmdDebugMarkerBeginEXT, "vkCmdDebugMarkerBeginEXT") + set_proc_address(&CmdDebugMarkerEndEXT, "vkCmdDebugMarkerEndEXT") + set_proc_address(&CmdDebugMarkerInsertEXT, "vkCmdDebugMarkerInsertEXT") + set_proc_address(&CmdDispatch, "vkCmdDispatch") + set_proc_address(&CmdDispatchBase, "vkCmdDispatchBase") + set_proc_address(&CmdDispatchBaseKHR, "vkCmdDispatchBaseKHR") + set_proc_address(&CmdDispatchIndirect, "vkCmdDispatchIndirect") + set_proc_address(&CmdDraw, "vkCmdDraw") + set_proc_address(&CmdDrawIndexed, "vkCmdDrawIndexed") + set_proc_address(&CmdDrawIndexedIndirect, "vkCmdDrawIndexedIndirect") + set_proc_address(&CmdDrawIndexedIndirectCount, "vkCmdDrawIndexedIndirectCount") + set_proc_address(&CmdDrawIndexedIndirectCountAMD, "vkCmdDrawIndexedIndirectCountAMD") + set_proc_address(&CmdDrawIndexedIndirectCountKHR, "vkCmdDrawIndexedIndirectCountKHR") + set_proc_address(&CmdDrawIndirect, "vkCmdDrawIndirect") + set_proc_address(&CmdDrawIndirectByteCountEXT, "vkCmdDrawIndirectByteCountEXT") + set_proc_address(&CmdDrawIndirectCount, "vkCmdDrawIndirectCount") + set_proc_address(&CmdDrawIndirectCountAMD, "vkCmdDrawIndirectCountAMD") + set_proc_address(&CmdDrawIndirectCountKHR, "vkCmdDrawIndirectCountKHR") + set_proc_address(&CmdDrawMeshTasksIndirectCountNV, "vkCmdDrawMeshTasksIndirectCountNV") + set_proc_address(&CmdDrawMeshTasksIndirectNV, "vkCmdDrawMeshTasksIndirectNV") + set_proc_address(&CmdDrawMeshTasksNV, "vkCmdDrawMeshTasksNV") + set_proc_address(&CmdDrawMultiEXT, "vkCmdDrawMultiEXT") + set_proc_address(&CmdDrawMultiIndexedEXT, "vkCmdDrawMultiIndexedEXT") + set_proc_address(&CmdEndConditionalRenderingEXT, "vkCmdEndConditionalRenderingEXT") + set_proc_address(&CmdEndDebugUtilsLabelEXT, "vkCmdEndDebugUtilsLabelEXT") + set_proc_address(&CmdEndQuery, "vkCmdEndQuery") + set_proc_address(&CmdEndQueryIndexedEXT, "vkCmdEndQueryIndexedEXT") + set_proc_address(&CmdEndRenderPass, "vkCmdEndRenderPass") + set_proc_address(&CmdEndRenderPass2, "vkCmdEndRenderPass2") + set_proc_address(&CmdEndRenderPass2KHR, "vkCmdEndRenderPass2KHR") + set_proc_address(&CmdEndRendering, "vkCmdEndRendering") + set_proc_address(&CmdEndRenderingKHR, "vkCmdEndRenderingKHR") + set_proc_address(&CmdEndTransformFeedbackEXT, "vkCmdEndTransformFeedbackEXT") + set_proc_address(&CmdExecuteCommands, "vkCmdExecuteCommands") + set_proc_address(&CmdExecuteGeneratedCommandsNV, "vkCmdExecuteGeneratedCommandsNV") + set_proc_address(&CmdFillBuffer, "vkCmdFillBuffer") + set_proc_address(&CmdInsertDebugUtilsLabelEXT, "vkCmdInsertDebugUtilsLabelEXT") + set_proc_address(&CmdNextSubpass, "vkCmdNextSubpass") + set_proc_address(&CmdNextSubpass2, "vkCmdNextSubpass2") + set_proc_address(&CmdNextSubpass2KHR, "vkCmdNextSubpass2KHR") + set_proc_address(&CmdPipelineBarrier, "vkCmdPipelineBarrier") + set_proc_address(&CmdPipelineBarrier2, "vkCmdPipelineBarrier2") + set_proc_address(&CmdPipelineBarrier2KHR, "vkCmdPipelineBarrier2KHR") + set_proc_address(&CmdPreprocessGeneratedCommandsNV, "vkCmdPreprocessGeneratedCommandsNV") + set_proc_address(&CmdPushConstants, "vkCmdPushConstants") + set_proc_address(&CmdPushDescriptorSetKHR, "vkCmdPushDescriptorSetKHR") + set_proc_address(&CmdPushDescriptorSetWithTemplateKHR, "vkCmdPushDescriptorSetWithTemplateKHR") + set_proc_address(&CmdResetEvent, "vkCmdResetEvent") + set_proc_address(&CmdResetEvent2, "vkCmdResetEvent2") + set_proc_address(&CmdResetEvent2KHR, "vkCmdResetEvent2KHR") + set_proc_address(&CmdResetQueryPool, "vkCmdResetQueryPool") + set_proc_address(&CmdResolveImage, "vkCmdResolveImage") + set_proc_address(&CmdResolveImage2, "vkCmdResolveImage2") + set_proc_address(&CmdResolveImage2KHR, "vkCmdResolveImage2KHR") + set_proc_address(&CmdSetBlendConstants, "vkCmdSetBlendConstants") + set_proc_address(&CmdSetCheckpointNV, "vkCmdSetCheckpointNV") + set_proc_address(&CmdSetCoarseSampleOrderNV, "vkCmdSetCoarseSampleOrderNV") + set_proc_address(&CmdSetCullMode, "vkCmdSetCullMode") + set_proc_address(&CmdSetCullModeEXT, "vkCmdSetCullModeEXT") + set_proc_address(&CmdSetDepthBias, "vkCmdSetDepthBias") + set_proc_address(&CmdSetDepthBiasEnable, "vkCmdSetDepthBiasEnable") + set_proc_address(&CmdSetDepthBiasEnableEXT, "vkCmdSetDepthBiasEnableEXT") + set_proc_address(&CmdSetDepthBounds, "vkCmdSetDepthBounds") + set_proc_address(&CmdSetDepthBoundsTestEnable, "vkCmdSetDepthBoundsTestEnable") + set_proc_address(&CmdSetDepthBoundsTestEnableEXT, "vkCmdSetDepthBoundsTestEnableEXT") + set_proc_address(&CmdSetDepthCompareOp, "vkCmdSetDepthCompareOp") + set_proc_address(&CmdSetDepthCompareOpEXT, "vkCmdSetDepthCompareOpEXT") + set_proc_address(&CmdSetDepthTestEnable, "vkCmdSetDepthTestEnable") + set_proc_address(&CmdSetDepthTestEnableEXT, "vkCmdSetDepthTestEnableEXT") + set_proc_address(&CmdSetDepthWriteEnable, "vkCmdSetDepthWriteEnable") + set_proc_address(&CmdSetDepthWriteEnableEXT, "vkCmdSetDepthWriteEnableEXT") + set_proc_address(&CmdSetDeviceMask, "vkCmdSetDeviceMask") + set_proc_address(&CmdSetDeviceMaskKHR, "vkCmdSetDeviceMaskKHR") + set_proc_address(&CmdSetDiscardRectangleEXT, "vkCmdSetDiscardRectangleEXT") + set_proc_address(&CmdSetEvent, "vkCmdSetEvent") + set_proc_address(&CmdSetEvent2, "vkCmdSetEvent2") + set_proc_address(&CmdSetEvent2KHR, "vkCmdSetEvent2KHR") + set_proc_address(&CmdSetExclusiveScissorNV, "vkCmdSetExclusiveScissorNV") + set_proc_address(&CmdSetFragmentShadingRateEnumNV, "vkCmdSetFragmentShadingRateEnumNV") + set_proc_address(&CmdSetFragmentShadingRateKHR, "vkCmdSetFragmentShadingRateKHR") + set_proc_address(&CmdSetFrontFace, "vkCmdSetFrontFace") + set_proc_address(&CmdSetFrontFaceEXT, "vkCmdSetFrontFaceEXT") + set_proc_address(&CmdSetLineStippleEXT, "vkCmdSetLineStippleEXT") + set_proc_address(&CmdSetLineWidth, "vkCmdSetLineWidth") + set_proc_address(&CmdSetLogicOpEXT, "vkCmdSetLogicOpEXT") + set_proc_address(&CmdSetPatchControlPointsEXT, "vkCmdSetPatchControlPointsEXT") + set_proc_address(&CmdSetPerformanceMarkerINTEL, "vkCmdSetPerformanceMarkerINTEL") + set_proc_address(&CmdSetPerformanceOverrideINTEL, "vkCmdSetPerformanceOverrideINTEL") + set_proc_address(&CmdSetPerformanceStreamMarkerINTEL, "vkCmdSetPerformanceStreamMarkerINTEL") + set_proc_address(&CmdSetPrimitiveRestartEnable, "vkCmdSetPrimitiveRestartEnable") + set_proc_address(&CmdSetPrimitiveRestartEnableEXT, "vkCmdSetPrimitiveRestartEnableEXT") + set_proc_address(&CmdSetPrimitiveTopology, "vkCmdSetPrimitiveTopology") + set_proc_address(&CmdSetPrimitiveTopologyEXT, "vkCmdSetPrimitiveTopologyEXT") + set_proc_address(&CmdSetRasterizerDiscardEnable, "vkCmdSetRasterizerDiscardEnable") + set_proc_address(&CmdSetRasterizerDiscardEnableEXT, "vkCmdSetRasterizerDiscardEnableEXT") + set_proc_address(&CmdSetRayTracingPipelineStackSizeKHR, "vkCmdSetRayTracingPipelineStackSizeKHR") + set_proc_address(&CmdSetSampleLocationsEXT, "vkCmdSetSampleLocationsEXT") + set_proc_address(&CmdSetScissor, "vkCmdSetScissor") + set_proc_address(&CmdSetScissorWithCount, "vkCmdSetScissorWithCount") + set_proc_address(&CmdSetScissorWithCountEXT, "vkCmdSetScissorWithCountEXT") + set_proc_address(&CmdSetStencilCompareMask, "vkCmdSetStencilCompareMask") + set_proc_address(&CmdSetStencilOp, "vkCmdSetStencilOp") + set_proc_address(&CmdSetStencilOpEXT, "vkCmdSetStencilOpEXT") + set_proc_address(&CmdSetStencilReference, "vkCmdSetStencilReference") + set_proc_address(&CmdSetStencilTestEnable, "vkCmdSetStencilTestEnable") + set_proc_address(&CmdSetStencilTestEnableEXT, "vkCmdSetStencilTestEnableEXT") + set_proc_address(&CmdSetStencilWriteMask, "vkCmdSetStencilWriteMask") + set_proc_address(&CmdSetVertexInputEXT, "vkCmdSetVertexInputEXT") + set_proc_address(&CmdSetViewport, "vkCmdSetViewport") + set_proc_address(&CmdSetViewportShadingRatePaletteNV, "vkCmdSetViewportShadingRatePaletteNV") + set_proc_address(&CmdSetViewportWScalingNV, "vkCmdSetViewportWScalingNV") + set_proc_address(&CmdSetViewportWithCount, "vkCmdSetViewportWithCount") + set_proc_address(&CmdSetViewportWithCountEXT, "vkCmdSetViewportWithCountEXT") + set_proc_address(&CmdSubpassShadingHUAWEI, "vkCmdSubpassShadingHUAWEI") + set_proc_address(&CmdTraceRaysIndirectKHR, "vkCmdTraceRaysIndirectKHR") + set_proc_address(&CmdTraceRaysKHR, "vkCmdTraceRaysKHR") + set_proc_address(&CmdTraceRaysNV, "vkCmdTraceRaysNV") + set_proc_address(&CmdUpdateBuffer, "vkCmdUpdateBuffer") + set_proc_address(&CmdWaitEvents, "vkCmdWaitEvents") + set_proc_address(&CmdWaitEvents2, "vkCmdWaitEvents2") + set_proc_address(&CmdWaitEvents2KHR, "vkCmdWaitEvents2KHR") + set_proc_address(&CmdWriteAccelerationStructuresPropertiesKHR, "vkCmdWriteAccelerationStructuresPropertiesKHR") + set_proc_address(&CmdWriteAccelerationStructuresPropertiesNV, "vkCmdWriteAccelerationStructuresPropertiesNV") + set_proc_address(&CmdWriteBufferMarker2AMD, "vkCmdWriteBufferMarker2AMD") + set_proc_address(&CmdWriteBufferMarkerAMD, "vkCmdWriteBufferMarkerAMD") + set_proc_address(&CmdWriteTimestamp, "vkCmdWriteTimestamp") + set_proc_address(&CmdWriteTimestamp2, "vkCmdWriteTimestamp2") + set_proc_address(&CmdWriteTimestamp2KHR, "vkCmdWriteTimestamp2KHR") + set_proc_address(&CompileDeferredNV, "vkCompileDeferredNV") + set_proc_address(&CopyAccelerationStructureKHR, "vkCopyAccelerationStructureKHR") + set_proc_address(&CopyAccelerationStructureToMemoryKHR, "vkCopyAccelerationStructureToMemoryKHR") + set_proc_address(&CopyMemoryToAccelerationStructureKHR, "vkCopyMemoryToAccelerationStructureKHR") + set_proc_address(&CreateAccelerationStructureKHR, "vkCreateAccelerationStructureKHR") + set_proc_address(&CreateAccelerationStructureNV, "vkCreateAccelerationStructureNV") + set_proc_address(&CreateBuffer, "vkCreateBuffer") + set_proc_address(&CreateBufferView, "vkCreateBufferView") + set_proc_address(&CreateCommandPool, "vkCreateCommandPool") + set_proc_address(&CreateComputePipelines, "vkCreateComputePipelines") + set_proc_address(&CreateCuFunctionNVX, "vkCreateCuFunctionNVX") + set_proc_address(&CreateCuModuleNVX, "vkCreateCuModuleNVX") + set_proc_address(&CreateDeferredOperationKHR, "vkCreateDeferredOperationKHR") + set_proc_address(&CreateDescriptorPool, "vkCreateDescriptorPool") + set_proc_address(&CreateDescriptorSetLayout, "vkCreateDescriptorSetLayout") + set_proc_address(&CreateDescriptorUpdateTemplate, "vkCreateDescriptorUpdateTemplate") + set_proc_address(&CreateDescriptorUpdateTemplateKHR, "vkCreateDescriptorUpdateTemplateKHR") + set_proc_address(&CreateEvent, "vkCreateEvent") + set_proc_address(&CreateFence, "vkCreateFence") + set_proc_address(&CreateFramebuffer, "vkCreateFramebuffer") + set_proc_address(&CreateGraphicsPipelines, "vkCreateGraphicsPipelines") + set_proc_address(&CreateImage, "vkCreateImage") + set_proc_address(&CreateImageView, "vkCreateImageView") + set_proc_address(&CreateIndirectCommandsLayoutNV, "vkCreateIndirectCommandsLayoutNV") + set_proc_address(&CreatePipelineCache, "vkCreatePipelineCache") + set_proc_address(&CreatePipelineLayout, "vkCreatePipelineLayout") + set_proc_address(&CreatePrivateDataSlot, "vkCreatePrivateDataSlot") + set_proc_address(&CreatePrivateDataSlotEXT, "vkCreatePrivateDataSlotEXT") + set_proc_address(&CreateQueryPool, "vkCreateQueryPool") + set_proc_address(&CreateRayTracingPipelinesKHR, "vkCreateRayTracingPipelinesKHR") + set_proc_address(&CreateRayTracingPipelinesNV, "vkCreateRayTracingPipelinesNV") + set_proc_address(&CreateRenderPass, "vkCreateRenderPass") + set_proc_address(&CreateRenderPass2, "vkCreateRenderPass2") + set_proc_address(&CreateRenderPass2KHR, "vkCreateRenderPass2KHR") + set_proc_address(&CreateSampler, "vkCreateSampler") + set_proc_address(&CreateSamplerYcbcrConversion, "vkCreateSamplerYcbcrConversion") + set_proc_address(&CreateSamplerYcbcrConversionKHR, "vkCreateSamplerYcbcrConversionKHR") + set_proc_address(&CreateSemaphore, "vkCreateSemaphore") + set_proc_address(&CreateShaderModule, "vkCreateShaderModule") + set_proc_address(&CreateSharedSwapchainsKHR, "vkCreateSharedSwapchainsKHR") + set_proc_address(&CreateSwapchainKHR, "vkCreateSwapchainKHR") + set_proc_address(&CreateValidationCacheEXT, "vkCreateValidationCacheEXT") + set_proc_address(&DebugMarkerSetObjectNameEXT, "vkDebugMarkerSetObjectNameEXT") + set_proc_address(&DebugMarkerSetObjectTagEXT, "vkDebugMarkerSetObjectTagEXT") + set_proc_address(&DeferredOperationJoinKHR, "vkDeferredOperationJoinKHR") + set_proc_address(&DestroyAccelerationStructureKHR, "vkDestroyAccelerationStructureKHR") + set_proc_address(&DestroyAccelerationStructureNV, "vkDestroyAccelerationStructureNV") + set_proc_address(&DestroyBuffer, "vkDestroyBuffer") + set_proc_address(&DestroyBufferView, "vkDestroyBufferView") + set_proc_address(&DestroyCommandPool, "vkDestroyCommandPool") + set_proc_address(&DestroyCuFunctionNVX, "vkDestroyCuFunctionNVX") + set_proc_address(&DestroyCuModuleNVX, "vkDestroyCuModuleNVX") + set_proc_address(&DestroyDeferredOperationKHR, "vkDestroyDeferredOperationKHR") + set_proc_address(&DestroyDescriptorPool, "vkDestroyDescriptorPool") + set_proc_address(&DestroyDescriptorSetLayout, "vkDestroyDescriptorSetLayout") + set_proc_address(&DestroyDescriptorUpdateTemplate, "vkDestroyDescriptorUpdateTemplate") + set_proc_address(&DestroyDescriptorUpdateTemplateKHR, "vkDestroyDescriptorUpdateTemplateKHR") + set_proc_address(&DestroyDevice, "vkDestroyDevice") + set_proc_address(&DestroyEvent, "vkDestroyEvent") + set_proc_address(&DestroyFence, "vkDestroyFence") + set_proc_address(&DestroyFramebuffer, "vkDestroyFramebuffer") + set_proc_address(&DestroyImage, "vkDestroyImage") + set_proc_address(&DestroyImageView, "vkDestroyImageView") + set_proc_address(&DestroyIndirectCommandsLayoutNV, "vkDestroyIndirectCommandsLayoutNV") + set_proc_address(&DestroyPipeline, "vkDestroyPipeline") + set_proc_address(&DestroyPipelineCache, "vkDestroyPipelineCache") + set_proc_address(&DestroyPipelineLayout, "vkDestroyPipelineLayout") + set_proc_address(&DestroyPrivateDataSlot, "vkDestroyPrivateDataSlot") + set_proc_address(&DestroyPrivateDataSlotEXT, "vkDestroyPrivateDataSlotEXT") + set_proc_address(&DestroyQueryPool, "vkDestroyQueryPool") + set_proc_address(&DestroyRenderPass, "vkDestroyRenderPass") + set_proc_address(&DestroySampler, "vkDestroySampler") + set_proc_address(&DestroySamplerYcbcrConversion, "vkDestroySamplerYcbcrConversion") + set_proc_address(&DestroySamplerYcbcrConversionKHR, "vkDestroySamplerYcbcrConversionKHR") + set_proc_address(&DestroySemaphore, "vkDestroySemaphore") + set_proc_address(&DestroyShaderModule, "vkDestroyShaderModule") + set_proc_address(&DestroySwapchainKHR, "vkDestroySwapchainKHR") + set_proc_address(&DestroyValidationCacheEXT, "vkDestroyValidationCacheEXT") + set_proc_address(&DeviceWaitIdle, "vkDeviceWaitIdle") + set_proc_address(&DisplayPowerControlEXT, "vkDisplayPowerControlEXT") + set_proc_address(&EndCommandBuffer, "vkEndCommandBuffer") + set_proc_address(&FlushMappedMemoryRanges, "vkFlushMappedMemoryRanges") + set_proc_address(&FreeCommandBuffers, "vkFreeCommandBuffers") + set_proc_address(&FreeDescriptorSets, "vkFreeDescriptorSets") + set_proc_address(&FreeMemory, "vkFreeMemory") + set_proc_address(&GetAccelerationStructureBuildSizesKHR, "vkGetAccelerationStructureBuildSizesKHR") + set_proc_address(&GetAccelerationStructureDeviceAddressKHR, "vkGetAccelerationStructureDeviceAddressKHR") + set_proc_address(&GetAccelerationStructureHandleNV, "vkGetAccelerationStructureHandleNV") + set_proc_address(&GetAccelerationStructureMemoryRequirementsNV, "vkGetAccelerationStructureMemoryRequirementsNV") + set_proc_address(&GetBufferDeviceAddress, "vkGetBufferDeviceAddress") + set_proc_address(&GetBufferDeviceAddressEXT, "vkGetBufferDeviceAddressEXT") + set_proc_address(&GetBufferDeviceAddressKHR, "vkGetBufferDeviceAddressKHR") + set_proc_address(&GetBufferMemoryRequirements, "vkGetBufferMemoryRequirements") + set_proc_address(&GetBufferMemoryRequirements2, "vkGetBufferMemoryRequirements2") + set_proc_address(&GetBufferMemoryRequirements2KHR, "vkGetBufferMemoryRequirements2KHR") + set_proc_address(&GetBufferOpaqueCaptureAddress, "vkGetBufferOpaqueCaptureAddress") + set_proc_address(&GetBufferOpaqueCaptureAddressKHR, "vkGetBufferOpaqueCaptureAddressKHR") + set_proc_address(&GetCalibratedTimestampsEXT, "vkGetCalibratedTimestampsEXT") + set_proc_address(&GetDeferredOperationMaxConcurrencyKHR, "vkGetDeferredOperationMaxConcurrencyKHR") + set_proc_address(&GetDeferredOperationResultKHR, "vkGetDeferredOperationResultKHR") + set_proc_address(&GetDescriptorSetHostMappingVALVE, "vkGetDescriptorSetHostMappingVALVE") + set_proc_address(&GetDescriptorSetLayoutHostMappingInfoVALVE, "vkGetDescriptorSetLayoutHostMappingInfoVALVE") + set_proc_address(&GetDescriptorSetLayoutSupport, "vkGetDescriptorSetLayoutSupport") + set_proc_address(&GetDescriptorSetLayoutSupportKHR, "vkGetDescriptorSetLayoutSupportKHR") + set_proc_address(&GetDeviceAccelerationStructureCompatibilityKHR, "vkGetDeviceAccelerationStructureCompatibilityKHR") + set_proc_address(&GetDeviceBufferMemoryRequirements, "vkGetDeviceBufferMemoryRequirements") + set_proc_address(&GetDeviceBufferMemoryRequirementsKHR, "vkGetDeviceBufferMemoryRequirementsKHR") + set_proc_address(&GetDeviceGroupPeerMemoryFeatures, "vkGetDeviceGroupPeerMemoryFeatures") + set_proc_address(&GetDeviceGroupPeerMemoryFeaturesKHR, "vkGetDeviceGroupPeerMemoryFeaturesKHR") + set_proc_address(&GetDeviceGroupPresentCapabilitiesKHR, "vkGetDeviceGroupPresentCapabilitiesKHR") + set_proc_address(&GetDeviceGroupSurfacePresentModes2EXT, "vkGetDeviceGroupSurfacePresentModes2EXT") + set_proc_address(&GetDeviceGroupSurfacePresentModesKHR, "vkGetDeviceGroupSurfacePresentModesKHR") + set_proc_address(&GetDeviceImageMemoryRequirements, "vkGetDeviceImageMemoryRequirements") + set_proc_address(&GetDeviceImageMemoryRequirementsKHR, "vkGetDeviceImageMemoryRequirementsKHR") + set_proc_address(&GetDeviceImageSparseMemoryRequirements, "vkGetDeviceImageSparseMemoryRequirements") + set_proc_address(&GetDeviceImageSparseMemoryRequirementsKHR, "vkGetDeviceImageSparseMemoryRequirementsKHR") + set_proc_address(&GetDeviceMemoryCommitment, "vkGetDeviceMemoryCommitment") + set_proc_address(&GetDeviceMemoryOpaqueCaptureAddress, "vkGetDeviceMemoryOpaqueCaptureAddress") + set_proc_address(&GetDeviceMemoryOpaqueCaptureAddressKHR, "vkGetDeviceMemoryOpaqueCaptureAddressKHR") + set_proc_address(&GetDeviceProcAddr, "vkGetDeviceProcAddr") + set_proc_address(&GetDeviceQueue, "vkGetDeviceQueue") + set_proc_address(&GetDeviceQueue2, "vkGetDeviceQueue2") + set_proc_address(&GetDeviceSubpassShadingMaxWorkgroupSizeHUAWEI, "vkGetDeviceSubpassShadingMaxWorkgroupSizeHUAWEI") + set_proc_address(&GetEventStatus, "vkGetEventStatus") + set_proc_address(&GetFenceFdKHR, "vkGetFenceFdKHR") + set_proc_address(&GetFenceStatus, "vkGetFenceStatus") + set_proc_address(&GetFenceWin32HandleKHR, "vkGetFenceWin32HandleKHR") + set_proc_address(&GetGeneratedCommandsMemoryRequirementsNV, "vkGetGeneratedCommandsMemoryRequirementsNV") + set_proc_address(&GetImageDrmFormatModifierPropertiesEXT, "vkGetImageDrmFormatModifierPropertiesEXT") + set_proc_address(&GetImageMemoryRequirements, "vkGetImageMemoryRequirements") + set_proc_address(&GetImageMemoryRequirements2, "vkGetImageMemoryRequirements2") + set_proc_address(&GetImageMemoryRequirements2KHR, "vkGetImageMemoryRequirements2KHR") + set_proc_address(&GetImageSparseMemoryRequirements, "vkGetImageSparseMemoryRequirements") + set_proc_address(&GetImageSparseMemoryRequirements2, "vkGetImageSparseMemoryRequirements2") + set_proc_address(&GetImageSparseMemoryRequirements2KHR, "vkGetImageSparseMemoryRequirements2KHR") + set_proc_address(&GetImageSubresourceLayout, "vkGetImageSubresourceLayout") + set_proc_address(&GetImageViewAddressNVX, "vkGetImageViewAddressNVX") + set_proc_address(&GetImageViewHandleNVX, "vkGetImageViewHandleNVX") + set_proc_address(&GetMemoryFdKHR, "vkGetMemoryFdKHR") + set_proc_address(&GetMemoryFdPropertiesKHR, "vkGetMemoryFdPropertiesKHR") + set_proc_address(&GetMemoryHostPointerPropertiesEXT, "vkGetMemoryHostPointerPropertiesEXT") + set_proc_address(&GetMemoryRemoteAddressNV, "vkGetMemoryRemoteAddressNV") + set_proc_address(&GetMemoryWin32HandleKHR, "vkGetMemoryWin32HandleKHR") + set_proc_address(&GetMemoryWin32HandleNV, "vkGetMemoryWin32HandleNV") + set_proc_address(&GetMemoryWin32HandlePropertiesKHR, "vkGetMemoryWin32HandlePropertiesKHR") + set_proc_address(&GetPastPresentationTimingGOOGLE, "vkGetPastPresentationTimingGOOGLE") + set_proc_address(&GetPerformanceParameterINTEL, "vkGetPerformanceParameterINTEL") + set_proc_address(&GetPipelineCacheData, "vkGetPipelineCacheData") + set_proc_address(&GetPipelineExecutableInternalRepresentationsKHR, "vkGetPipelineExecutableInternalRepresentationsKHR") + set_proc_address(&GetPipelineExecutablePropertiesKHR, "vkGetPipelineExecutablePropertiesKHR") + set_proc_address(&GetPipelineExecutableStatisticsKHR, "vkGetPipelineExecutableStatisticsKHR") + set_proc_address(&GetPrivateData, "vkGetPrivateData") + set_proc_address(&GetPrivateDataEXT, "vkGetPrivateDataEXT") + set_proc_address(&GetQueryPoolResults, "vkGetQueryPoolResults") + set_proc_address(&GetQueueCheckpointData2NV, "vkGetQueueCheckpointData2NV") + set_proc_address(&GetQueueCheckpointDataNV, "vkGetQueueCheckpointDataNV") + set_proc_address(&GetRayTracingCaptureReplayShaderGroupHandlesKHR, "vkGetRayTracingCaptureReplayShaderGroupHandlesKHR") + set_proc_address(&GetRayTracingShaderGroupHandlesKHR, "vkGetRayTracingShaderGroupHandlesKHR") + set_proc_address(&GetRayTracingShaderGroupHandlesNV, "vkGetRayTracingShaderGroupHandlesNV") + set_proc_address(&GetRayTracingShaderGroupStackSizeKHR, "vkGetRayTracingShaderGroupStackSizeKHR") + set_proc_address(&GetRefreshCycleDurationGOOGLE, "vkGetRefreshCycleDurationGOOGLE") + set_proc_address(&GetRenderAreaGranularity, "vkGetRenderAreaGranularity") + set_proc_address(&GetSemaphoreCounterValue, "vkGetSemaphoreCounterValue") + set_proc_address(&GetSemaphoreCounterValueKHR, "vkGetSemaphoreCounterValueKHR") + set_proc_address(&GetSemaphoreFdKHR, "vkGetSemaphoreFdKHR") + set_proc_address(&GetSemaphoreWin32HandleKHR, "vkGetSemaphoreWin32HandleKHR") + set_proc_address(&GetShaderInfoAMD, "vkGetShaderInfoAMD") + set_proc_address(&GetSwapchainCounterEXT, "vkGetSwapchainCounterEXT") + set_proc_address(&GetSwapchainImagesKHR, "vkGetSwapchainImagesKHR") + set_proc_address(&GetSwapchainStatusKHR, "vkGetSwapchainStatusKHR") + set_proc_address(&GetValidationCacheDataEXT, "vkGetValidationCacheDataEXT") + set_proc_address(&ImportFenceFdKHR, "vkImportFenceFdKHR") + set_proc_address(&ImportFenceWin32HandleKHR, "vkImportFenceWin32HandleKHR") + set_proc_address(&ImportSemaphoreFdKHR, "vkImportSemaphoreFdKHR") + set_proc_address(&ImportSemaphoreWin32HandleKHR, "vkImportSemaphoreWin32HandleKHR") + set_proc_address(&InitializePerformanceApiINTEL, "vkInitializePerformanceApiINTEL") + set_proc_address(&InvalidateMappedMemoryRanges, "vkInvalidateMappedMemoryRanges") + set_proc_address(&MapMemory, "vkMapMemory") + set_proc_address(&MergePipelineCaches, "vkMergePipelineCaches") + set_proc_address(&MergeValidationCachesEXT, "vkMergeValidationCachesEXT") + set_proc_address(&QueueBeginDebugUtilsLabelEXT, "vkQueueBeginDebugUtilsLabelEXT") + set_proc_address(&QueueBindSparse, "vkQueueBindSparse") + set_proc_address(&QueueEndDebugUtilsLabelEXT, "vkQueueEndDebugUtilsLabelEXT") + set_proc_address(&QueueInsertDebugUtilsLabelEXT, "vkQueueInsertDebugUtilsLabelEXT") + set_proc_address(&QueuePresentKHR, "vkQueuePresentKHR") + set_proc_address(&QueueSetPerformanceConfigurationINTEL, "vkQueueSetPerformanceConfigurationINTEL") + set_proc_address(&QueueSubmit, "vkQueueSubmit") + set_proc_address(&QueueSubmit2, "vkQueueSubmit2") + set_proc_address(&QueueSubmit2KHR, "vkQueueSubmit2KHR") + set_proc_address(&QueueWaitIdle, "vkQueueWaitIdle") + set_proc_address(&RegisterDeviceEventEXT, "vkRegisterDeviceEventEXT") + set_proc_address(&RegisterDisplayEventEXT, "vkRegisterDisplayEventEXT") + set_proc_address(&ReleaseFullScreenExclusiveModeEXT, "vkReleaseFullScreenExclusiveModeEXT") + set_proc_address(&ReleasePerformanceConfigurationINTEL, "vkReleasePerformanceConfigurationINTEL") + set_proc_address(&ReleaseProfilingLockKHR, "vkReleaseProfilingLockKHR") + set_proc_address(&ResetCommandBuffer, "vkResetCommandBuffer") + set_proc_address(&ResetCommandPool, "vkResetCommandPool") + set_proc_address(&ResetDescriptorPool, "vkResetDescriptorPool") + set_proc_address(&ResetEvent, "vkResetEvent") + set_proc_address(&ResetFences, "vkResetFences") + set_proc_address(&ResetQueryPool, "vkResetQueryPool") + set_proc_address(&ResetQueryPoolEXT, "vkResetQueryPoolEXT") + set_proc_address(&SetDebugUtilsObjectNameEXT, "vkSetDebugUtilsObjectNameEXT") + set_proc_address(&SetDebugUtilsObjectTagEXT, "vkSetDebugUtilsObjectTagEXT") + set_proc_address(&SetDeviceMemoryPriorityEXT, "vkSetDeviceMemoryPriorityEXT") + set_proc_address(&SetEvent, "vkSetEvent") + set_proc_address(&SetHdrMetadataEXT, "vkSetHdrMetadataEXT") + set_proc_address(&SetLocalDimmingAMD, "vkSetLocalDimmingAMD") + set_proc_address(&SetPrivateData, "vkSetPrivateData") + set_proc_address(&SetPrivateDataEXT, "vkSetPrivateDataEXT") + set_proc_address(&SignalSemaphore, "vkSignalSemaphore") + set_proc_address(&SignalSemaphoreKHR, "vkSignalSemaphoreKHR") + set_proc_address(&TrimCommandPool, "vkTrimCommandPool") + set_proc_address(&TrimCommandPoolKHR, "vkTrimCommandPoolKHR") + set_proc_address(&UninitializePerformanceApiINTEL, "vkUninitializePerformanceApiINTEL") + set_proc_address(&UnmapMemory, "vkUnmapMemory") + set_proc_address(&UpdateDescriptorSetWithTemplate, "vkUpdateDescriptorSetWithTemplate") + set_proc_address(&UpdateDescriptorSetWithTemplateKHR, "vkUpdateDescriptorSetWithTemplateKHR") + set_proc_address(&UpdateDescriptorSets, "vkUpdateDescriptorSets") + set_proc_address(&WaitForFences, "vkWaitForFences") + set_proc_address(&WaitForPresentKHR, "vkWaitForPresentKHR") + set_proc_address(&WaitSemaphores, "vkWaitSemaphores") + set_proc_address(&WaitSemaphoresKHR, "vkWaitSemaphoresKHR") + set_proc_address(&WriteAccelerationStructuresPropertiesKHR, "vkWriteAccelerationStructuresPropertiesKHR") } +// Device Procedure VTable +Device_VTable :: struct { + AcquireFullScreenExclusiveModeEXT: ProcAcquireFullScreenExclusiveModeEXT, + AcquireNextImage2KHR: ProcAcquireNextImage2KHR, + AcquireNextImageKHR: ProcAcquireNextImageKHR, + AcquirePerformanceConfigurationINTEL: ProcAcquirePerformanceConfigurationINTEL, + AcquireProfilingLockKHR: ProcAcquireProfilingLockKHR, + AllocateCommandBuffers: ProcAllocateCommandBuffers, + AllocateDescriptorSets: ProcAllocateDescriptorSets, + AllocateMemory: ProcAllocateMemory, + BeginCommandBuffer: ProcBeginCommandBuffer, + BindAccelerationStructureMemoryNV: ProcBindAccelerationStructureMemoryNV, + BindBufferMemory: ProcBindBufferMemory, + BindBufferMemory2: ProcBindBufferMemory2, + BindBufferMemory2KHR: ProcBindBufferMemory2KHR, + BindImageMemory: ProcBindImageMemory, + BindImageMemory2: ProcBindImageMemory2, + BindImageMemory2KHR: ProcBindImageMemory2KHR, + BuildAccelerationStructuresKHR: ProcBuildAccelerationStructuresKHR, + CmdBeginConditionalRenderingEXT: ProcCmdBeginConditionalRenderingEXT, + CmdBeginDebugUtilsLabelEXT: ProcCmdBeginDebugUtilsLabelEXT, + CmdBeginQuery: ProcCmdBeginQuery, + CmdBeginQueryIndexedEXT: ProcCmdBeginQueryIndexedEXT, + CmdBeginRenderPass: ProcCmdBeginRenderPass, + CmdBeginRenderPass2: ProcCmdBeginRenderPass2, + CmdBeginRenderPass2KHR: ProcCmdBeginRenderPass2KHR, + CmdBeginRendering: ProcCmdBeginRendering, + CmdBeginRenderingKHR: ProcCmdBeginRenderingKHR, + CmdBeginTransformFeedbackEXT: ProcCmdBeginTransformFeedbackEXT, + CmdBindDescriptorSets: ProcCmdBindDescriptorSets, + CmdBindIndexBuffer: ProcCmdBindIndexBuffer, + CmdBindInvocationMaskHUAWEI: ProcCmdBindInvocationMaskHUAWEI, + CmdBindPipeline: ProcCmdBindPipeline, + CmdBindPipelineShaderGroupNV: ProcCmdBindPipelineShaderGroupNV, + CmdBindShadingRateImageNV: ProcCmdBindShadingRateImageNV, + CmdBindTransformFeedbackBuffersEXT: ProcCmdBindTransformFeedbackBuffersEXT, + CmdBindVertexBuffers: ProcCmdBindVertexBuffers, + CmdBindVertexBuffers2: ProcCmdBindVertexBuffers2, + CmdBindVertexBuffers2EXT: ProcCmdBindVertexBuffers2EXT, + CmdBlitImage: ProcCmdBlitImage, + CmdBlitImage2: ProcCmdBlitImage2, + CmdBlitImage2KHR: ProcCmdBlitImage2KHR, + CmdBuildAccelerationStructureNV: ProcCmdBuildAccelerationStructureNV, + CmdBuildAccelerationStructuresIndirectKHR: ProcCmdBuildAccelerationStructuresIndirectKHR, + CmdBuildAccelerationStructuresKHR: ProcCmdBuildAccelerationStructuresKHR, + CmdClearAttachments: ProcCmdClearAttachments, + CmdClearColorImage: ProcCmdClearColorImage, + CmdClearDepthStencilImage: ProcCmdClearDepthStencilImage, + CmdCopyAccelerationStructureKHR: ProcCmdCopyAccelerationStructureKHR, + CmdCopyAccelerationStructureNV: ProcCmdCopyAccelerationStructureNV, + CmdCopyAccelerationStructureToMemoryKHR: ProcCmdCopyAccelerationStructureToMemoryKHR, + CmdCopyBuffer: ProcCmdCopyBuffer, + CmdCopyBuffer2: ProcCmdCopyBuffer2, + CmdCopyBuffer2KHR: ProcCmdCopyBuffer2KHR, + CmdCopyBufferToImage: ProcCmdCopyBufferToImage, + CmdCopyBufferToImage2: ProcCmdCopyBufferToImage2, + CmdCopyBufferToImage2KHR: ProcCmdCopyBufferToImage2KHR, + CmdCopyImage: ProcCmdCopyImage, + CmdCopyImage2: ProcCmdCopyImage2, + CmdCopyImage2KHR: ProcCmdCopyImage2KHR, + CmdCopyImageToBuffer: ProcCmdCopyImageToBuffer, + CmdCopyImageToBuffer2: ProcCmdCopyImageToBuffer2, + CmdCopyImageToBuffer2KHR: ProcCmdCopyImageToBuffer2KHR, + CmdCopyMemoryToAccelerationStructureKHR: ProcCmdCopyMemoryToAccelerationStructureKHR, + CmdCopyQueryPoolResults: ProcCmdCopyQueryPoolResults, + CmdCuLaunchKernelNVX: ProcCmdCuLaunchKernelNVX, + CmdDebugMarkerBeginEXT: ProcCmdDebugMarkerBeginEXT, + CmdDebugMarkerEndEXT: ProcCmdDebugMarkerEndEXT, + CmdDebugMarkerInsertEXT: ProcCmdDebugMarkerInsertEXT, + CmdDispatch: ProcCmdDispatch, + CmdDispatchBase: ProcCmdDispatchBase, + CmdDispatchBaseKHR: ProcCmdDispatchBaseKHR, + CmdDispatchIndirect: ProcCmdDispatchIndirect, + CmdDraw: ProcCmdDraw, + CmdDrawIndexed: ProcCmdDrawIndexed, + CmdDrawIndexedIndirect: ProcCmdDrawIndexedIndirect, + CmdDrawIndexedIndirectCount: ProcCmdDrawIndexedIndirectCount, + CmdDrawIndexedIndirectCountAMD: ProcCmdDrawIndexedIndirectCountAMD, + CmdDrawIndexedIndirectCountKHR: ProcCmdDrawIndexedIndirectCountKHR, + CmdDrawIndirect: ProcCmdDrawIndirect, + CmdDrawIndirectByteCountEXT: ProcCmdDrawIndirectByteCountEXT, + CmdDrawIndirectCount: ProcCmdDrawIndirectCount, + CmdDrawIndirectCountAMD: ProcCmdDrawIndirectCountAMD, + CmdDrawIndirectCountKHR: ProcCmdDrawIndirectCountKHR, + CmdDrawMeshTasksIndirectCountNV: ProcCmdDrawMeshTasksIndirectCountNV, + CmdDrawMeshTasksIndirectNV: ProcCmdDrawMeshTasksIndirectNV, + CmdDrawMeshTasksNV: ProcCmdDrawMeshTasksNV, + CmdDrawMultiEXT: ProcCmdDrawMultiEXT, + CmdDrawMultiIndexedEXT: ProcCmdDrawMultiIndexedEXT, + CmdEndConditionalRenderingEXT: ProcCmdEndConditionalRenderingEXT, + CmdEndDebugUtilsLabelEXT: ProcCmdEndDebugUtilsLabelEXT, + CmdEndQuery: ProcCmdEndQuery, + CmdEndQueryIndexedEXT: ProcCmdEndQueryIndexedEXT, + CmdEndRenderPass: ProcCmdEndRenderPass, + CmdEndRenderPass2: ProcCmdEndRenderPass2, + CmdEndRenderPass2KHR: ProcCmdEndRenderPass2KHR, + CmdEndRendering: ProcCmdEndRendering, + CmdEndRenderingKHR: ProcCmdEndRenderingKHR, + CmdEndTransformFeedbackEXT: ProcCmdEndTransformFeedbackEXT, + CmdExecuteCommands: ProcCmdExecuteCommands, + CmdExecuteGeneratedCommandsNV: ProcCmdExecuteGeneratedCommandsNV, + CmdFillBuffer: ProcCmdFillBuffer, + CmdInsertDebugUtilsLabelEXT: ProcCmdInsertDebugUtilsLabelEXT, + CmdNextSubpass: ProcCmdNextSubpass, + CmdNextSubpass2: ProcCmdNextSubpass2, + CmdNextSubpass2KHR: ProcCmdNextSubpass2KHR, + CmdPipelineBarrier: ProcCmdPipelineBarrier, + CmdPipelineBarrier2: ProcCmdPipelineBarrier2, + CmdPipelineBarrier2KHR: ProcCmdPipelineBarrier2KHR, + CmdPreprocessGeneratedCommandsNV: ProcCmdPreprocessGeneratedCommandsNV, + CmdPushConstants: ProcCmdPushConstants, + CmdPushDescriptorSetKHR: ProcCmdPushDescriptorSetKHR, + CmdPushDescriptorSetWithTemplateKHR: ProcCmdPushDescriptorSetWithTemplateKHR, + CmdResetEvent: ProcCmdResetEvent, + CmdResetEvent2: ProcCmdResetEvent2, + CmdResetEvent2KHR: ProcCmdResetEvent2KHR, + CmdResetQueryPool: ProcCmdResetQueryPool, + CmdResolveImage: ProcCmdResolveImage, + CmdResolveImage2: ProcCmdResolveImage2, + CmdResolveImage2KHR: ProcCmdResolveImage2KHR, + CmdSetBlendConstants: ProcCmdSetBlendConstants, + CmdSetCheckpointNV: ProcCmdSetCheckpointNV, + CmdSetCoarseSampleOrderNV: ProcCmdSetCoarseSampleOrderNV, + CmdSetCullMode: ProcCmdSetCullMode, + CmdSetCullModeEXT: ProcCmdSetCullModeEXT, + CmdSetDepthBias: ProcCmdSetDepthBias, + CmdSetDepthBiasEnable: ProcCmdSetDepthBiasEnable, + CmdSetDepthBiasEnableEXT: ProcCmdSetDepthBiasEnableEXT, + CmdSetDepthBounds: ProcCmdSetDepthBounds, + CmdSetDepthBoundsTestEnable: ProcCmdSetDepthBoundsTestEnable, + CmdSetDepthBoundsTestEnableEXT: ProcCmdSetDepthBoundsTestEnableEXT, + CmdSetDepthCompareOp: ProcCmdSetDepthCompareOp, + CmdSetDepthCompareOpEXT: ProcCmdSetDepthCompareOpEXT, + CmdSetDepthTestEnable: ProcCmdSetDepthTestEnable, + CmdSetDepthTestEnableEXT: ProcCmdSetDepthTestEnableEXT, + CmdSetDepthWriteEnable: ProcCmdSetDepthWriteEnable, + CmdSetDepthWriteEnableEXT: ProcCmdSetDepthWriteEnableEXT, + CmdSetDeviceMask: ProcCmdSetDeviceMask, + CmdSetDeviceMaskKHR: ProcCmdSetDeviceMaskKHR, + CmdSetDiscardRectangleEXT: ProcCmdSetDiscardRectangleEXT, + CmdSetEvent: ProcCmdSetEvent, + CmdSetEvent2: ProcCmdSetEvent2, + CmdSetEvent2KHR: ProcCmdSetEvent2KHR, + CmdSetExclusiveScissorNV: ProcCmdSetExclusiveScissorNV, + CmdSetFragmentShadingRateEnumNV: ProcCmdSetFragmentShadingRateEnumNV, + CmdSetFragmentShadingRateKHR: ProcCmdSetFragmentShadingRateKHR, + CmdSetFrontFace: ProcCmdSetFrontFace, + CmdSetFrontFaceEXT: ProcCmdSetFrontFaceEXT, + CmdSetLineStippleEXT: ProcCmdSetLineStippleEXT, + CmdSetLineWidth: ProcCmdSetLineWidth, + CmdSetLogicOpEXT: ProcCmdSetLogicOpEXT, + CmdSetPatchControlPointsEXT: ProcCmdSetPatchControlPointsEXT, + CmdSetPerformanceMarkerINTEL: ProcCmdSetPerformanceMarkerINTEL, + CmdSetPerformanceOverrideINTEL: ProcCmdSetPerformanceOverrideINTEL, + CmdSetPerformanceStreamMarkerINTEL: ProcCmdSetPerformanceStreamMarkerINTEL, + CmdSetPrimitiveRestartEnable: ProcCmdSetPrimitiveRestartEnable, + CmdSetPrimitiveRestartEnableEXT: ProcCmdSetPrimitiveRestartEnableEXT, + CmdSetPrimitiveTopology: ProcCmdSetPrimitiveTopology, + CmdSetPrimitiveTopologyEXT: ProcCmdSetPrimitiveTopologyEXT, + CmdSetRasterizerDiscardEnable: ProcCmdSetRasterizerDiscardEnable, + CmdSetRasterizerDiscardEnableEXT: ProcCmdSetRasterizerDiscardEnableEXT, + CmdSetRayTracingPipelineStackSizeKHR: ProcCmdSetRayTracingPipelineStackSizeKHR, + CmdSetSampleLocationsEXT: ProcCmdSetSampleLocationsEXT, + CmdSetScissor: ProcCmdSetScissor, + CmdSetScissorWithCount: ProcCmdSetScissorWithCount, + CmdSetScissorWithCountEXT: ProcCmdSetScissorWithCountEXT, + CmdSetStencilCompareMask: ProcCmdSetStencilCompareMask, + CmdSetStencilOp: ProcCmdSetStencilOp, + CmdSetStencilOpEXT: ProcCmdSetStencilOpEXT, + CmdSetStencilReference: ProcCmdSetStencilReference, + CmdSetStencilTestEnable: ProcCmdSetStencilTestEnable, + CmdSetStencilTestEnableEXT: ProcCmdSetStencilTestEnableEXT, + CmdSetStencilWriteMask: ProcCmdSetStencilWriteMask, + CmdSetVertexInputEXT: ProcCmdSetVertexInputEXT, + CmdSetViewport: ProcCmdSetViewport, + CmdSetViewportShadingRatePaletteNV: ProcCmdSetViewportShadingRatePaletteNV, + CmdSetViewportWScalingNV: ProcCmdSetViewportWScalingNV, + CmdSetViewportWithCount: ProcCmdSetViewportWithCount, + CmdSetViewportWithCountEXT: ProcCmdSetViewportWithCountEXT, + CmdSubpassShadingHUAWEI: ProcCmdSubpassShadingHUAWEI, + CmdTraceRaysIndirectKHR: ProcCmdTraceRaysIndirectKHR, + CmdTraceRaysKHR: ProcCmdTraceRaysKHR, + CmdTraceRaysNV: ProcCmdTraceRaysNV, + CmdUpdateBuffer: ProcCmdUpdateBuffer, + CmdWaitEvents: ProcCmdWaitEvents, + CmdWaitEvents2: ProcCmdWaitEvents2, + CmdWaitEvents2KHR: ProcCmdWaitEvents2KHR, + CmdWriteAccelerationStructuresPropertiesKHR: ProcCmdWriteAccelerationStructuresPropertiesKHR, + CmdWriteAccelerationStructuresPropertiesNV: ProcCmdWriteAccelerationStructuresPropertiesNV, + CmdWriteBufferMarker2AMD: ProcCmdWriteBufferMarker2AMD, + CmdWriteBufferMarkerAMD: ProcCmdWriteBufferMarkerAMD, + CmdWriteTimestamp: ProcCmdWriteTimestamp, + CmdWriteTimestamp2: ProcCmdWriteTimestamp2, + CmdWriteTimestamp2KHR: ProcCmdWriteTimestamp2KHR, + CompileDeferredNV: ProcCompileDeferredNV, + CopyAccelerationStructureKHR: ProcCopyAccelerationStructureKHR, + CopyAccelerationStructureToMemoryKHR: ProcCopyAccelerationStructureToMemoryKHR, + CopyMemoryToAccelerationStructureKHR: ProcCopyMemoryToAccelerationStructureKHR, + CreateAccelerationStructureKHR: ProcCreateAccelerationStructureKHR, + CreateAccelerationStructureNV: ProcCreateAccelerationStructureNV, + CreateBuffer: ProcCreateBuffer, + CreateBufferView: ProcCreateBufferView, + CreateCommandPool: ProcCreateCommandPool, + CreateComputePipelines: ProcCreateComputePipelines, + CreateCuFunctionNVX: ProcCreateCuFunctionNVX, + CreateCuModuleNVX: ProcCreateCuModuleNVX, + CreateDeferredOperationKHR: ProcCreateDeferredOperationKHR, + CreateDescriptorPool: ProcCreateDescriptorPool, + CreateDescriptorSetLayout: ProcCreateDescriptorSetLayout, + CreateDescriptorUpdateTemplate: ProcCreateDescriptorUpdateTemplate, + CreateDescriptorUpdateTemplateKHR: ProcCreateDescriptorUpdateTemplateKHR, + CreateEvent: ProcCreateEvent, + CreateFence: ProcCreateFence, + CreateFramebuffer: ProcCreateFramebuffer, + CreateGraphicsPipelines: ProcCreateGraphicsPipelines, + CreateImage: ProcCreateImage, + CreateImageView: ProcCreateImageView, + CreateIndirectCommandsLayoutNV: ProcCreateIndirectCommandsLayoutNV, + CreatePipelineCache: ProcCreatePipelineCache, + CreatePipelineLayout: ProcCreatePipelineLayout, + CreatePrivateDataSlot: ProcCreatePrivateDataSlot, + CreatePrivateDataSlotEXT: ProcCreatePrivateDataSlotEXT, + CreateQueryPool: ProcCreateQueryPool, + CreateRayTracingPipelinesKHR: ProcCreateRayTracingPipelinesKHR, + CreateRayTracingPipelinesNV: ProcCreateRayTracingPipelinesNV, + CreateRenderPass: ProcCreateRenderPass, + CreateRenderPass2: ProcCreateRenderPass2, + CreateRenderPass2KHR: ProcCreateRenderPass2KHR, + CreateSampler: ProcCreateSampler, + CreateSamplerYcbcrConversion: ProcCreateSamplerYcbcrConversion, + CreateSamplerYcbcrConversionKHR: ProcCreateSamplerYcbcrConversionKHR, + CreateSemaphore: ProcCreateSemaphore, + CreateShaderModule: ProcCreateShaderModule, + CreateSharedSwapchainsKHR: ProcCreateSharedSwapchainsKHR, + CreateSwapchainKHR: ProcCreateSwapchainKHR, + CreateValidationCacheEXT: ProcCreateValidationCacheEXT, + DebugMarkerSetObjectNameEXT: ProcDebugMarkerSetObjectNameEXT, + DebugMarkerSetObjectTagEXT: ProcDebugMarkerSetObjectTagEXT, + DeferredOperationJoinKHR: ProcDeferredOperationJoinKHR, + DestroyAccelerationStructureKHR: ProcDestroyAccelerationStructureKHR, + DestroyAccelerationStructureNV: ProcDestroyAccelerationStructureNV, + DestroyBuffer: ProcDestroyBuffer, + DestroyBufferView: ProcDestroyBufferView, + DestroyCommandPool: ProcDestroyCommandPool, + DestroyCuFunctionNVX: ProcDestroyCuFunctionNVX, + DestroyCuModuleNVX: ProcDestroyCuModuleNVX, + DestroyDeferredOperationKHR: ProcDestroyDeferredOperationKHR, + DestroyDescriptorPool: ProcDestroyDescriptorPool, + DestroyDescriptorSetLayout: ProcDestroyDescriptorSetLayout, + DestroyDescriptorUpdateTemplate: ProcDestroyDescriptorUpdateTemplate, + DestroyDescriptorUpdateTemplateKHR: ProcDestroyDescriptorUpdateTemplateKHR, + DestroyDevice: ProcDestroyDevice, + DestroyEvent: ProcDestroyEvent, + DestroyFence: ProcDestroyFence, + DestroyFramebuffer: ProcDestroyFramebuffer, + DestroyImage: ProcDestroyImage, + DestroyImageView: ProcDestroyImageView, + DestroyIndirectCommandsLayoutNV: ProcDestroyIndirectCommandsLayoutNV, + DestroyPipeline: ProcDestroyPipeline, + DestroyPipelineCache: ProcDestroyPipelineCache, + DestroyPipelineLayout: ProcDestroyPipelineLayout, + DestroyPrivateDataSlot: ProcDestroyPrivateDataSlot, + DestroyPrivateDataSlotEXT: ProcDestroyPrivateDataSlotEXT, + DestroyQueryPool: ProcDestroyQueryPool, + DestroyRenderPass: ProcDestroyRenderPass, + DestroySampler: ProcDestroySampler, + DestroySamplerYcbcrConversion: ProcDestroySamplerYcbcrConversion, + DestroySamplerYcbcrConversionKHR: ProcDestroySamplerYcbcrConversionKHR, + DestroySemaphore: ProcDestroySemaphore, + DestroyShaderModule: ProcDestroyShaderModule, + DestroySwapchainKHR: ProcDestroySwapchainKHR, + DestroyValidationCacheEXT: ProcDestroyValidationCacheEXT, + DeviceWaitIdle: ProcDeviceWaitIdle, + DisplayPowerControlEXT: ProcDisplayPowerControlEXT, + EndCommandBuffer: ProcEndCommandBuffer, + FlushMappedMemoryRanges: ProcFlushMappedMemoryRanges, + FreeCommandBuffers: ProcFreeCommandBuffers, + FreeDescriptorSets: ProcFreeDescriptorSets, + FreeMemory: ProcFreeMemory, + GetAccelerationStructureBuildSizesKHR: ProcGetAccelerationStructureBuildSizesKHR, + GetAccelerationStructureDeviceAddressKHR: ProcGetAccelerationStructureDeviceAddressKHR, + GetAccelerationStructureHandleNV: ProcGetAccelerationStructureHandleNV, + GetAccelerationStructureMemoryRequirementsNV: ProcGetAccelerationStructureMemoryRequirementsNV, + GetBufferDeviceAddress: ProcGetBufferDeviceAddress, + GetBufferDeviceAddressEXT: ProcGetBufferDeviceAddressEXT, + GetBufferDeviceAddressKHR: ProcGetBufferDeviceAddressKHR, + GetBufferMemoryRequirements: ProcGetBufferMemoryRequirements, + GetBufferMemoryRequirements2: ProcGetBufferMemoryRequirements2, + GetBufferMemoryRequirements2KHR: ProcGetBufferMemoryRequirements2KHR, + GetBufferOpaqueCaptureAddress: ProcGetBufferOpaqueCaptureAddress, + GetBufferOpaqueCaptureAddressKHR: ProcGetBufferOpaqueCaptureAddressKHR, + GetCalibratedTimestampsEXT: ProcGetCalibratedTimestampsEXT, + GetDeferredOperationMaxConcurrencyKHR: ProcGetDeferredOperationMaxConcurrencyKHR, + GetDeferredOperationResultKHR: ProcGetDeferredOperationResultKHR, + GetDescriptorSetHostMappingVALVE: ProcGetDescriptorSetHostMappingVALVE, + GetDescriptorSetLayoutHostMappingInfoVALVE: ProcGetDescriptorSetLayoutHostMappingInfoVALVE, + GetDescriptorSetLayoutSupport: ProcGetDescriptorSetLayoutSupport, + GetDescriptorSetLayoutSupportKHR: ProcGetDescriptorSetLayoutSupportKHR, + GetDeviceAccelerationStructureCompatibilityKHR: ProcGetDeviceAccelerationStructureCompatibilityKHR, + GetDeviceBufferMemoryRequirements: ProcGetDeviceBufferMemoryRequirements, + GetDeviceBufferMemoryRequirementsKHR: ProcGetDeviceBufferMemoryRequirementsKHR, + GetDeviceGroupPeerMemoryFeatures: ProcGetDeviceGroupPeerMemoryFeatures, + GetDeviceGroupPeerMemoryFeaturesKHR: ProcGetDeviceGroupPeerMemoryFeaturesKHR, + GetDeviceGroupPresentCapabilitiesKHR: ProcGetDeviceGroupPresentCapabilitiesKHR, + GetDeviceGroupSurfacePresentModes2EXT: ProcGetDeviceGroupSurfacePresentModes2EXT, + GetDeviceGroupSurfacePresentModesKHR: ProcGetDeviceGroupSurfacePresentModesKHR, + GetDeviceImageMemoryRequirements: ProcGetDeviceImageMemoryRequirements, + GetDeviceImageMemoryRequirementsKHR: ProcGetDeviceImageMemoryRequirementsKHR, + GetDeviceImageSparseMemoryRequirements: ProcGetDeviceImageSparseMemoryRequirements, + GetDeviceImageSparseMemoryRequirementsKHR: ProcGetDeviceImageSparseMemoryRequirementsKHR, + GetDeviceMemoryCommitment: ProcGetDeviceMemoryCommitment, + GetDeviceMemoryOpaqueCaptureAddress: ProcGetDeviceMemoryOpaqueCaptureAddress, + GetDeviceMemoryOpaqueCaptureAddressKHR: ProcGetDeviceMemoryOpaqueCaptureAddressKHR, + GetDeviceProcAddr: ProcGetDeviceProcAddr, + GetDeviceQueue: ProcGetDeviceQueue, + GetDeviceQueue2: ProcGetDeviceQueue2, + GetDeviceSubpassShadingMaxWorkgroupSizeHUAWEI: ProcGetDeviceSubpassShadingMaxWorkgroupSizeHUAWEI, + GetEventStatus: ProcGetEventStatus, + GetFenceFdKHR: ProcGetFenceFdKHR, + GetFenceStatus: ProcGetFenceStatus, + GetFenceWin32HandleKHR: ProcGetFenceWin32HandleKHR, + GetGeneratedCommandsMemoryRequirementsNV: ProcGetGeneratedCommandsMemoryRequirementsNV, + GetImageDrmFormatModifierPropertiesEXT: ProcGetImageDrmFormatModifierPropertiesEXT, + GetImageMemoryRequirements: ProcGetImageMemoryRequirements, + GetImageMemoryRequirements2: ProcGetImageMemoryRequirements2, + GetImageMemoryRequirements2KHR: ProcGetImageMemoryRequirements2KHR, + GetImageSparseMemoryRequirements: ProcGetImageSparseMemoryRequirements, + GetImageSparseMemoryRequirements2: ProcGetImageSparseMemoryRequirements2, + GetImageSparseMemoryRequirements2KHR: ProcGetImageSparseMemoryRequirements2KHR, + GetImageSubresourceLayout: ProcGetImageSubresourceLayout, + GetImageViewAddressNVX: ProcGetImageViewAddressNVX, + GetImageViewHandleNVX: ProcGetImageViewHandleNVX, + GetMemoryFdKHR: ProcGetMemoryFdKHR, + GetMemoryFdPropertiesKHR: ProcGetMemoryFdPropertiesKHR, + GetMemoryHostPointerPropertiesEXT: ProcGetMemoryHostPointerPropertiesEXT, + GetMemoryRemoteAddressNV: ProcGetMemoryRemoteAddressNV, + GetMemoryWin32HandleKHR: ProcGetMemoryWin32HandleKHR, + GetMemoryWin32HandleNV: ProcGetMemoryWin32HandleNV, + GetMemoryWin32HandlePropertiesKHR: ProcGetMemoryWin32HandlePropertiesKHR, + GetPastPresentationTimingGOOGLE: ProcGetPastPresentationTimingGOOGLE, + GetPerformanceParameterINTEL: ProcGetPerformanceParameterINTEL, + GetPipelineCacheData: ProcGetPipelineCacheData, + GetPipelineExecutableInternalRepresentationsKHR: ProcGetPipelineExecutableInternalRepresentationsKHR, + GetPipelineExecutablePropertiesKHR: ProcGetPipelineExecutablePropertiesKHR, + GetPipelineExecutableStatisticsKHR: ProcGetPipelineExecutableStatisticsKHR, + GetPrivateData: ProcGetPrivateData, + GetPrivateDataEXT: ProcGetPrivateDataEXT, + GetQueryPoolResults: ProcGetQueryPoolResults, + GetQueueCheckpointData2NV: ProcGetQueueCheckpointData2NV, + GetQueueCheckpointDataNV: ProcGetQueueCheckpointDataNV, + GetRayTracingCaptureReplayShaderGroupHandlesKHR: ProcGetRayTracingCaptureReplayShaderGroupHandlesKHR, + GetRayTracingShaderGroupHandlesKHR: ProcGetRayTracingShaderGroupHandlesKHR, + GetRayTracingShaderGroupHandlesNV: ProcGetRayTracingShaderGroupHandlesNV, + GetRayTracingShaderGroupStackSizeKHR: ProcGetRayTracingShaderGroupStackSizeKHR, + GetRefreshCycleDurationGOOGLE: ProcGetRefreshCycleDurationGOOGLE, + GetRenderAreaGranularity: ProcGetRenderAreaGranularity, + GetSemaphoreCounterValue: ProcGetSemaphoreCounterValue, + GetSemaphoreCounterValueKHR: ProcGetSemaphoreCounterValueKHR, + GetSemaphoreFdKHR: ProcGetSemaphoreFdKHR, + GetSemaphoreWin32HandleKHR: ProcGetSemaphoreWin32HandleKHR, + GetShaderInfoAMD: ProcGetShaderInfoAMD, + GetSwapchainCounterEXT: ProcGetSwapchainCounterEXT, + GetSwapchainImagesKHR: ProcGetSwapchainImagesKHR, + GetSwapchainStatusKHR: ProcGetSwapchainStatusKHR, + GetValidationCacheDataEXT: ProcGetValidationCacheDataEXT, + ImportFenceFdKHR: ProcImportFenceFdKHR, + ImportFenceWin32HandleKHR: ProcImportFenceWin32HandleKHR, + ImportSemaphoreFdKHR: ProcImportSemaphoreFdKHR, + ImportSemaphoreWin32HandleKHR: ProcImportSemaphoreWin32HandleKHR, + InitializePerformanceApiINTEL: ProcInitializePerformanceApiINTEL, + InvalidateMappedMemoryRanges: ProcInvalidateMappedMemoryRanges, + MapMemory: ProcMapMemory, + MergePipelineCaches: ProcMergePipelineCaches, + MergeValidationCachesEXT: ProcMergeValidationCachesEXT, + QueueBeginDebugUtilsLabelEXT: ProcQueueBeginDebugUtilsLabelEXT, + QueueBindSparse: ProcQueueBindSparse, + QueueEndDebugUtilsLabelEXT: ProcQueueEndDebugUtilsLabelEXT, + QueueInsertDebugUtilsLabelEXT: ProcQueueInsertDebugUtilsLabelEXT, + QueuePresentKHR: ProcQueuePresentKHR, + QueueSetPerformanceConfigurationINTEL: ProcQueueSetPerformanceConfigurationINTEL, + QueueSubmit: ProcQueueSubmit, + QueueSubmit2: ProcQueueSubmit2, + QueueSubmit2KHR: ProcQueueSubmit2KHR, + QueueWaitIdle: ProcQueueWaitIdle, + RegisterDeviceEventEXT: ProcRegisterDeviceEventEXT, + RegisterDisplayEventEXT: ProcRegisterDisplayEventEXT, + ReleaseFullScreenExclusiveModeEXT: ProcReleaseFullScreenExclusiveModeEXT, + ReleasePerformanceConfigurationINTEL: ProcReleasePerformanceConfigurationINTEL, + ReleaseProfilingLockKHR: ProcReleaseProfilingLockKHR, + ResetCommandBuffer: ProcResetCommandBuffer, + ResetCommandPool: ProcResetCommandPool, + ResetDescriptorPool: ProcResetDescriptorPool, + ResetEvent: ProcResetEvent, + ResetFences: ProcResetFences, + ResetQueryPool: ProcResetQueryPool, + ResetQueryPoolEXT: ProcResetQueryPoolEXT, + SetDebugUtilsObjectNameEXT: ProcSetDebugUtilsObjectNameEXT, + SetDebugUtilsObjectTagEXT: ProcSetDebugUtilsObjectTagEXT, + SetDeviceMemoryPriorityEXT: ProcSetDeviceMemoryPriorityEXT, + SetEvent: ProcSetEvent, + SetHdrMetadataEXT: ProcSetHdrMetadataEXT, + SetLocalDimmingAMD: ProcSetLocalDimmingAMD, + SetPrivateData: ProcSetPrivateData, + SetPrivateDataEXT: ProcSetPrivateDataEXT, + SignalSemaphore: ProcSignalSemaphore, + SignalSemaphoreKHR: ProcSignalSemaphoreKHR, + TrimCommandPool: ProcTrimCommandPool, + TrimCommandPoolKHR: ProcTrimCommandPoolKHR, + UninitializePerformanceApiINTEL: ProcUninitializePerformanceApiINTEL, + UnmapMemory: ProcUnmapMemory, + UpdateDescriptorSetWithTemplate: ProcUpdateDescriptorSetWithTemplate, + UpdateDescriptorSetWithTemplateKHR: ProcUpdateDescriptorSetWithTemplateKHR, + UpdateDescriptorSets: ProcUpdateDescriptorSets, + WaitForFences: ProcWaitForFences, + WaitForPresentKHR: ProcWaitForPresentKHR, + WaitSemaphores: ProcWaitSemaphores, + WaitSemaphoresKHR: ProcWaitSemaphoresKHR, + WriteAccelerationStructuresPropertiesKHR: ProcWriteAccelerationStructuresPropertiesKHR, +} + +load_proc_addresses_device_vtable :: proc(device: Device, vtable: ^Device_VTable) { + vtable.AcquireFullScreenExclusiveModeEXT = auto_cast GetDeviceProcAddr(device, "vkAcquireFullScreenExclusiveModeEXT") + vtable.AcquireNextImage2KHR = auto_cast GetDeviceProcAddr(device, "vkAcquireNextImage2KHR") + vtable.AcquireNextImageKHR = auto_cast GetDeviceProcAddr(device, "vkAcquireNextImageKHR") + vtable.AcquirePerformanceConfigurationINTEL = auto_cast GetDeviceProcAddr(device, "vkAcquirePerformanceConfigurationINTEL") + vtable.AcquireProfilingLockKHR = auto_cast GetDeviceProcAddr(device, "vkAcquireProfilingLockKHR") + vtable.AllocateCommandBuffers = auto_cast GetDeviceProcAddr(device, "vkAllocateCommandBuffers") + vtable.AllocateDescriptorSets = auto_cast GetDeviceProcAddr(device, "vkAllocateDescriptorSets") + vtable.AllocateMemory = auto_cast GetDeviceProcAddr(device, "vkAllocateMemory") + vtable.BeginCommandBuffer = auto_cast GetDeviceProcAddr(device, "vkBeginCommandBuffer") + vtable.BindAccelerationStructureMemoryNV = auto_cast GetDeviceProcAddr(device, "vkBindAccelerationStructureMemoryNV") + vtable.BindBufferMemory = auto_cast GetDeviceProcAddr(device, "vkBindBufferMemory") + vtable.BindBufferMemory2 = auto_cast GetDeviceProcAddr(device, "vkBindBufferMemory2") + vtable.BindBufferMemory2KHR = auto_cast GetDeviceProcAddr(device, "vkBindBufferMemory2KHR") + vtable.BindImageMemory = auto_cast GetDeviceProcAddr(device, "vkBindImageMemory") + vtable.BindImageMemory2 = auto_cast GetDeviceProcAddr(device, "vkBindImageMemory2") + vtable.BindImageMemory2KHR = auto_cast GetDeviceProcAddr(device, "vkBindImageMemory2KHR") + vtable.BuildAccelerationStructuresKHR = auto_cast GetDeviceProcAddr(device, "vkBuildAccelerationStructuresKHR") + vtable.CmdBeginConditionalRenderingEXT = auto_cast GetDeviceProcAddr(device, "vkCmdBeginConditionalRenderingEXT") + vtable.CmdBeginDebugUtilsLabelEXT = auto_cast GetDeviceProcAddr(device, "vkCmdBeginDebugUtilsLabelEXT") + vtable.CmdBeginQuery = auto_cast GetDeviceProcAddr(device, "vkCmdBeginQuery") + vtable.CmdBeginQueryIndexedEXT = auto_cast GetDeviceProcAddr(device, "vkCmdBeginQueryIndexedEXT") + vtable.CmdBeginRenderPass = auto_cast GetDeviceProcAddr(device, "vkCmdBeginRenderPass") + vtable.CmdBeginRenderPass2 = auto_cast GetDeviceProcAddr(device, "vkCmdBeginRenderPass2") + vtable.CmdBeginRenderPass2KHR = auto_cast GetDeviceProcAddr(device, "vkCmdBeginRenderPass2KHR") + vtable.CmdBeginRendering = auto_cast GetDeviceProcAddr(device, "vkCmdBeginRendering") + vtable.CmdBeginRenderingKHR = auto_cast GetDeviceProcAddr(device, "vkCmdBeginRenderingKHR") + vtable.CmdBeginTransformFeedbackEXT = auto_cast GetDeviceProcAddr(device, "vkCmdBeginTransformFeedbackEXT") + vtable.CmdBindDescriptorSets = auto_cast GetDeviceProcAddr(device, "vkCmdBindDescriptorSets") + vtable.CmdBindIndexBuffer = auto_cast GetDeviceProcAddr(device, "vkCmdBindIndexBuffer") + vtable.CmdBindInvocationMaskHUAWEI = auto_cast GetDeviceProcAddr(device, "vkCmdBindInvocationMaskHUAWEI") + vtable.CmdBindPipeline = auto_cast GetDeviceProcAddr(device, "vkCmdBindPipeline") + vtable.CmdBindPipelineShaderGroupNV = auto_cast GetDeviceProcAddr(device, "vkCmdBindPipelineShaderGroupNV") + vtable.CmdBindShadingRateImageNV = auto_cast GetDeviceProcAddr(device, "vkCmdBindShadingRateImageNV") + vtable.CmdBindTransformFeedbackBuffersEXT = auto_cast GetDeviceProcAddr(device, "vkCmdBindTransformFeedbackBuffersEXT") + vtable.CmdBindVertexBuffers = auto_cast GetDeviceProcAddr(device, "vkCmdBindVertexBuffers") + vtable.CmdBindVertexBuffers2 = auto_cast GetDeviceProcAddr(device, "vkCmdBindVertexBuffers2") + vtable.CmdBindVertexBuffers2EXT = auto_cast GetDeviceProcAddr(device, "vkCmdBindVertexBuffers2EXT") + vtable.CmdBlitImage = auto_cast GetDeviceProcAddr(device, "vkCmdBlitImage") + vtable.CmdBlitImage2 = auto_cast GetDeviceProcAddr(device, "vkCmdBlitImage2") + vtable.CmdBlitImage2KHR = auto_cast GetDeviceProcAddr(device, "vkCmdBlitImage2KHR") + vtable.CmdBuildAccelerationStructureNV = auto_cast GetDeviceProcAddr(device, "vkCmdBuildAccelerationStructureNV") + vtable.CmdBuildAccelerationStructuresIndirectKHR = auto_cast GetDeviceProcAddr(device, "vkCmdBuildAccelerationStructuresIndirectKHR") + vtable.CmdBuildAccelerationStructuresKHR = auto_cast GetDeviceProcAddr(device, "vkCmdBuildAccelerationStructuresKHR") + vtable.CmdClearAttachments = auto_cast GetDeviceProcAddr(device, "vkCmdClearAttachments") + vtable.CmdClearColorImage = auto_cast GetDeviceProcAddr(device, "vkCmdClearColorImage") + vtable.CmdClearDepthStencilImage = auto_cast GetDeviceProcAddr(device, "vkCmdClearDepthStencilImage") + vtable.CmdCopyAccelerationStructureKHR = auto_cast GetDeviceProcAddr(device, "vkCmdCopyAccelerationStructureKHR") + vtable.CmdCopyAccelerationStructureNV = auto_cast GetDeviceProcAddr(device, "vkCmdCopyAccelerationStructureNV") + vtable.CmdCopyAccelerationStructureToMemoryKHR = auto_cast GetDeviceProcAddr(device, "vkCmdCopyAccelerationStructureToMemoryKHR") + vtable.CmdCopyBuffer = auto_cast GetDeviceProcAddr(device, "vkCmdCopyBuffer") + vtable.CmdCopyBuffer2 = auto_cast GetDeviceProcAddr(device, "vkCmdCopyBuffer2") + vtable.CmdCopyBuffer2KHR = auto_cast GetDeviceProcAddr(device, "vkCmdCopyBuffer2KHR") + vtable.CmdCopyBufferToImage = auto_cast GetDeviceProcAddr(device, "vkCmdCopyBufferToImage") + vtable.CmdCopyBufferToImage2 = auto_cast GetDeviceProcAddr(device, "vkCmdCopyBufferToImage2") + vtable.CmdCopyBufferToImage2KHR = auto_cast GetDeviceProcAddr(device, "vkCmdCopyBufferToImage2KHR") + vtable.CmdCopyImage = auto_cast GetDeviceProcAddr(device, "vkCmdCopyImage") + vtable.CmdCopyImage2 = auto_cast GetDeviceProcAddr(device, "vkCmdCopyImage2") + vtable.CmdCopyImage2KHR = auto_cast GetDeviceProcAddr(device, "vkCmdCopyImage2KHR") + vtable.CmdCopyImageToBuffer = auto_cast GetDeviceProcAddr(device, "vkCmdCopyImageToBuffer") + vtable.CmdCopyImageToBuffer2 = auto_cast GetDeviceProcAddr(device, "vkCmdCopyImageToBuffer2") + vtable.CmdCopyImageToBuffer2KHR = auto_cast GetDeviceProcAddr(device, "vkCmdCopyImageToBuffer2KHR") + vtable.CmdCopyMemoryToAccelerationStructureKHR = auto_cast GetDeviceProcAddr(device, "vkCmdCopyMemoryToAccelerationStructureKHR") + vtable.CmdCopyQueryPoolResults = auto_cast GetDeviceProcAddr(device, "vkCmdCopyQueryPoolResults") + vtable.CmdCuLaunchKernelNVX = auto_cast GetDeviceProcAddr(device, "vkCmdCuLaunchKernelNVX") + vtable.CmdDebugMarkerBeginEXT = auto_cast GetDeviceProcAddr(device, "vkCmdDebugMarkerBeginEXT") + vtable.CmdDebugMarkerEndEXT = auto_cast GetDeviceProcAddr(device, "vkCmdDebugMarkerEndEXT") + vtable.CmdDebugMarkerInsertEXT = auto_cast GetDeviceProcAddr(device, "vkCmdDebugMarkerInsertEXT") + vtable.CmdDispatch = auto_cast GetDeviceProcAddr(device, "vkCmdDispatch") + vtable.CmdDispatchBase = auto_cast GetDeviceProcAddr(device, "vkCmdDispatchBase") + vtable.CmdDispatchBaseKHR = auto_cast GetDeviceProcAddr(device, "vkCmdDispatchBaseKHR") + vtable.CmdDispatchIndirect = auto_cast GetDeviceProcAddr(device, "vkCmdDispatchIndirect") + vtable.CmdDraw = auto_cast GetDeviceProcAddr(device, "vkCmdDraw") + vtable.CmdDrawIndexed = auto_cast GetDeviceProcAddr(device, "vkCmdDrawIndexed") + vtable.CmdDrawIndexedIndirect = auto_cast GetDeviceProcAddr(device, "vkCmdDrawIndexedIndirect") + vtable.CmdDrawIndexedIndirectCount = auto_cast GetDeviceProcAddr(device, "vkCmdDrawIndexedIndirectCount") + vtable.CmdDrawIndexedIndirectCountAMD = auto_cast GetDeviceProcAddr(device, "vkCmdDrawIndexedIndirectCountAMD") + vtable.CmdDrawIndexedIndirectCountKHR = auto_cast GetDeviceProcAddr(device, "vkCmdDrawIndexedIndirectCountKHR") + vtable.CmdDrawIndirect = auto_cast GetDeviceProcAddr(device, "vkCmdDrawIndirect") + vtable.CmdDrawIndirectByteCountEXT = auto_cast GetDeviceProcAddr(device, "vkCmdDrawIndirectByteCountEXT") + vtable.CmdDrawIndirectCount = auto_cast GetDeviceProcAddr(device, "vkCmdDrawIndirectCount") + vtable.CmdDrawIndirectCountAMD = auto_cast GetDeviceProcAddr(device, "vkCmdDrawIndirectCountAMD") + vtable.CmdDrawIndirectCountKHR = auto_cast GetDeviceProcAddr(device, "vkCmdDrawIndirectCountKHR") + vtable.CmdDrawMeshTasksIndirectCountNV = auto_cast GetDeviceProcAddr(device, "vkCmdDrawMeshTasksIndirectCountNV") + vtable.CmdDrawMeshTasksIndirectNV = auto_cast GetDeviceProcAddr(device, "vkCmdDrawMeshTasksIndirectNV") + vtable.CmdDrawMeshTasksNV = auto_cast GetDeviceProcAddr(device, "vkCmdDrawMeshTasksNV") + vtable.CmdDrawMultiEXT = auto_cast GetDeviceProcAddr(device, "vkCmdDrawMultiEXT") + vtable.CmdDrawMultiIndexedEXT = auto_cast GetDeviceProcAddr(device, "vkCmdDrawMultiIndexedEXT") + vtable.CmdEndConditionalRenderingEXT = auto_cast GetDeviceProcAddr(device, "vkCmdEndConditionalRenderingEXT") + vtable.CmdEndDebugUtilsLabelEXT = auto_cast GetDeviceProcAddr(device, "vkCmdEndDebugUtilsLabelEXT") + vtable.CmdEndQuery = auto_cast GetDeviceProcAddr(device, "vkCmdEndQuery") + vtable.CmdEndQueryIndexedEXT = auto_cast GetDeviceProcAddr(device, "vkCmdEndQueryIndexedEXT") + vtable.CmdEndRenderPass = auto_cast GetDeviceProcAddr(device, "vkCmdEndRenderPass") + vtable.CmdEndRenderPass2 = auto_cast GetDeviceProcAddr(device, "vkCmdEndRenderPass2") + vtable.CmdEndRenderPass2KHR = auto_cast GetDeviceProcAddr(device, "vkCmdEndRenderPass2KHR") + vtable.CmdEndRendering = auto_cast GetDeviceProcAddr(device, "vkCmdEndRendering") + vtable.CmdEndRenderingKHR = auto_cast GetDeviceProcAddr(device, "vkCmdEndRenderingKHR") + vtable.CmdEndTransformFeedbackEXT = auto_cast GetDeviceProcAddr(device, "vkCmdEndTransformFeedbackEXT") + vtable.CmdExecuteCommands = auto_cast GetDeviceProcAddr(device, "vkCmdExecuteCommands") + vtable.CmdExecuteGeneratedCommandsNV = auto_cast GetDeviceProcAddr(device, "vkCmdExecuteGeneratedCommandsNV") + vtable.CmdFillBuffer = auto_cast GetDeviceProcAddr(device, "vkCmdFillBuffer") + vtable.CmdInsertDebugUtilsLabelEXT = auto_cast GetDeviceProcAddr(device, "vkCmdInsertDebugUtilsLabelEXT") + vtable.CmdNextSubpass = auto_cast GetDeviceProcAddr(device, "vkCmdNextSubpass") + vtable.CmdNextSubpass2 = auto_cast GetDeviceProcAddr(device, "vkCmdNextSubpass2") + vtable.CmdNextSubpass2KHR = auto_cast GetDeviceProcAddr(device, "vkCmdNextSubpass2KHR") + vtable.CmdPipelineBarrier = auto_cast GetDeviceProcAddr(device, "vkCmdPipelineBarrier") + vtable.CmdPipelineBarrier2 = auto_cast GetDeviceProcAddr(device, "vkCmdPipelineBarrier2") + vtable.CmdPipelineBarrier2KHR = auto_cast GetDeviceProcAddr(device, "vkCmdPipelineBarrier2KHR") + vtable.CmdPreprocessGeneratedCommandsNV = auto_cast GetDeviceProcAddr(device, "vkCmdPreprocessGeneratedCommandsNV") + vtable.CmdPushConstants = auto_cast GetDeviceProcAddr(device, "vkCmdPushConstants") + vtable.CmdPushDescriptorSetKHR = auto_cast GetDeviceProcAddr(device, "vkCmdPushDescriptorSetKHR") + vtable.CmdPushDescriptorSetWithTemplateKHR = auto_cast GetDeviceProcAddr(device, "vkCmdPushDescriptorSetWithTemplateKHR") + vtable.CmdResetEvent = auto_cast GetDeviceProcAddr(device, "vkCmdResetEvent") + vtable.CmdResetEvent2 = auto_cast GetDeviceProcAddr(device, "vkCmdResetEvent2") + vtable.CmdResetEvent2KHR = auto_cast GetDeviceProcAddr(device, "vkCmdResetEvent2KHR") + vtable.CmdResetQueryPool = auto_cast GetDeviceProcAddr(device, "vkCmdResetQueryPool") + vtable.CmdResolveImage = auto_cast GetDeviceProcAddr(device, "vkCmdResolveImage") + vtable.CmdResolveImage2 = auto_cast GetDeviceProcAddr(device, "vkCmdResolveImage2") + vtable.CmdResolveImage2KHR = auto_cast GetDeviceProcAddr(device, "vkCmdResolveImage2KHR") + vtable.CmdSetBlendConstants = auto_cast GetDeviceProcAddr(device, "vkCmdSetBlendConstants") + vtable.CmdSetCheckpointNV = auto_cast GetDeviceProcAddr(device, "vkCmdSetCheckpointNV") + vtable.CmdSetCoarseSampleOrderNV = auto_cast GetDeviceProcAddr(device, "vkCmdSetCoarseSampleOrderNV") + vtable.CmdSetCullMode = auto_cast GetDeviceProcAddr(device, "vkCmdSetCullMode") + vtable.CmdSetCullModeEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetCullModeEXT") + vtable.CmdSetDepthBias = auto_cast GetDeviceProcAddr(device, "vkCmdSetDepthBias") + vtable.CmdSetDepthBiasEnable = auto_cast GetDeviceProcAddr(device, "vkCmdSetDepthBiasEnable") + vtable.CmdSetDepthBiasEnableEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetDepthBiasEnableEXT") + vtable.CmdSetDepthBounds = auto_cast GetDeviceProcAddr(device, "vkCmdSetDepthBounds") + vtable.CmdSetDepthBoundsTestEnable = auto_cast GetDeviceProcAddr(device, "vkCmdSetDepthBoundsTestEnable") + vtable.CmdSetDepthBoundsTestEnableEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetDepthBoundsTestEnableEXT") + vtable.CmdSetDepthCompareOp = auto_cast GetDeviceProcAddr(device, "vkCmdSetDepthCompareOp") + vtable.CmdSetDepthCompareOpEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetDepthCompareOpEXT") + vtable.CmdSetDepthTestEnable = auto_cast GetDeviceProcAddr(device, "vkCmdSetDepthTestEnable") + vtable.CmdSetDepthTestEnableEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetDepthTestEnableEXT") + vtable.CmdSetDepthWriteEnable = auto_cast GetDeviceProcAddr(device, "vkCmdSetDepthWriteEnable") + vtable.CmdSetDepthWriteEnableEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetDepthWriteEnableEXT") + vtable.CmdSetDeviceMask = auto_cast GetDeviceProcAddr(device, "vkCmdSetDeviceMask") + vtable.CmdSetDeviceMaskKHR = auto_cast GetDeviceProcAddr(device, "vkCmdSetDeviceMaskKHR") + vtable.CmdSetDiscardRectangleEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetDiscardRectangleEXT") + vtable.CmdSetEvent = auto_cast GetDeviceProcAddr(device, "vkCmdSetEvent") + vtable.CmdSetEvent2 = auto_cast GetDeviceProcAddr(device, "vkCmdSetEvent2") + vtable.CmdSetEvent2KHR = auto_cast GetDeviceProcAddr(device, "vkCmdSetEvent2KHR") + vtable.CmdSetExclusiveScissorNV = auto_cast GetDeviceProcAddr(device, "vkCmdSetExclusiveScissorNV") + vtable.CmdSetFragmentShadingRateEnumNV = auto_cast GetDeviceProcAddr(device, "vkCmdSetFragmentShadingRateEnumNV") + vtable.CmdSetFragmentShadingRateKHR = auto_cast GetDeviceProcAddr(device, "vkCmdSetFragmentShadingRateKHR") + vtable.CmdSetFrontFace = auto_cast GetDeviceProcAddr(device, "vkCmdSetFrontFace") + vtable.CmdSetFrontFaceEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetFrontFaceEXT") + vtable.CmdSetLineStippleEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetLineStippleEXT") + vtable.CmdSetLineWidth = auto_cast GetDeviceProcAddr(device, "vkCmdSetLineWidth") + vtable.CmdSetLogicOpEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetLogicOpEXT") + vtable.CmdSetPatchControlPointsEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetPatchControlPointsEXT") + vtable.CmdSetPerformanceMarkerINTEL = auto_cast GetDeviceProcAddr(device, "vkCmdSetPerformanceMarkerINTEL") + vtable.CmdSetPerformanceOverrideINTEL = auto_cast GetDeviceProcAddr(device, "vkCmdSetPerformanceOverrideINTEL") + vtable.CmdSetPerformanceStreamMarkerINTEL = auto_cast GetDeviceProcAddr(device, "vkCmdSetPerformanceStreamMarkerINTEL") + vtable.CmdSetPrimitiveRestartEnable = auto_cast GetDeviceProcAddr(device, "vkCmdSetPrimitiveRestartEnable") + vtable.CmdSetPrimitiveRestartEnableEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetPrimitiveRestartEnableEXT") + vtable.CmdSetPrimitiveTopology = auto_cast GetDeviceProcAddr(device, "vkCmdSetPrimitiveTopology") + vtable.CmdSetPrimitiveTopologyEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetPrimitiveTopologyEXT") + vtable.CmdSetRasterizerDiscardEnable = auto_cast GetDeviceProcAddr(device, "vkCmdSetRasterizerDiscardEnable") + vtable.CmdSetRasterizerDiscardEnableEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetRasterizerDiscardEnableEXT") + vtable.CmdSetRayTracingPipelineStackSizeKHR = auto_cast GetDeviceProcAddr(device, "vkCmdSetRayTracingPipelineStackSizeKHR") + vtable.CmdSetSampleLocationsEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetSampleLocationsEXT") + vtable.CmdSetScissor = auto_cast GetDeviceProcAddr(device, "vkCmdSetScissor") + vtable.CmdSetScissorWithCount = auto_cast GetDeviceProcAddr(device, "vkCmdSetScissorWithCount") + vtable.CmdSetScissorWithCountEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetScissorWithCountEXT") + vtable.CmdSetStencilCompareMask = auto_cast GetDeviceProcAddr(device, "vkCmdSetStencilCompareMask") + vtable.CmdSetStencilOp = auto_cast GetDeviceProcAddr(device, "vkCmdSetStencilOp") + vtable.CmdSetStencilOpEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetStencilOpEXT") + vtable.CmdSetStencilReference = auto_cast GetDeviceProcAddr(device, "vkCmdSetStencilReference") + vtable.CmdSetStencilTestEnable = auto_cast GetDeviceProcAddr(device, "vkCmdSetStencilTestEnable") + vtable.CmdSetStencilTestEnableEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetStencilTestEnableEXT") + vtable.CmdSetStencilWriteMask = auto_cast GetDeviceProcAddr(device, "vkCmdSetStencilWriteMask") + vtable.CmdSetVertexInputEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetVertexInputEXT") + vtable.CmdSetViewport = auto_cast GetDeviceProcAddr(device, "vkCmdSetViewport") + vtable.CmdSetViewportShadingRatePaletteNV = auto_cast GetDeviceProcAddr(device, "vkCmdSetViewportShadingRatePaletteNV") + vtable.CmdSetViewportWScalingNV = auto_cast GetDeviceProcAddr(device, "vkCmdSetViewportWScalingNV") + vtable.CmdSetViewportWithCount = auto_cast GetDeviceProcAddr(device, "vkCmdSetViewportWithCount") + vtable.CmdSetViewportWithCountEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetViewportWithCountEXT") + vtable.CmdSubpassShadingHUAWEI = auto_cast GetDeviceProcAddr(device, "vkCmdSubpassShadingHUAWEI") + vtable.CmdTraceRaysIndirectKHR = auto_cast GetDeviceProcAddr(device, "vkCmdTraceRaysIndirectKHR") + vtable.CmdTraceRaysKHR = auto_cast GetDeviceProcAddr(device, "vkCmdTraceRaysKHR") + vtable.CmdTraceRaysNV = auto_cast GetDeviceProcAddr(device, "vkCmdTraceRaysNV") + vtable.CmdUpdateBuffer = auto_cast GetDeviceProcAddr(device, "vkCmdUpdateBuffer") + vtable.CmdWaitEvents = auto_cast GetDeviceProcAddr(device, "vkCmdWaitEvents") + vtable.CmdWaitEvents2 = auto_cast GetDeviceProcAddr(device, "vkCmdWaitEvents2") + vtable.CmdWaitEvents2KHR = auto_cast GetDeviceProcAddr(device, "vkCmdWaitEvents2KHR") + vtable.CmdWriteAccelerationStructuresPropertiesKHR = auto_cast GetDeviceProcAddr(device, "vkCmdWriteAccelerationStructuresPropertiesKHR") + vtable.CmdWriteAccelerationStructuresPropertiesNV = auto_cast GetDeviceProcAddr(device, "vkCmdWriteAccelerationStructuresPropertiesNV") + vtable.CmdWriteBufferMarker2AMD = auto_cast GetDeviceProcAddr(device, "vkCmdWriteBufferMarker2AMD") + vtable.CmdWriteBufferMarkerAMD = auto_cast GetDeviceProcAddr(device, "vkCmdWriteBufferMarkerAMD") + vtable.CmdWriteTimestamp = auto_cast GetDeviceProcAddr(device, "vkCmdWriteTimestamp") + vtable.CmdWriteTimestamp2 = auto_cast GetDeviceProcAddr(device, "vkCmdWriteTimestamp2") + vtable.CmdWriteTimestamp2KHR = auto_cast GetDeviceProcAddr(device, "vkCmdWriteTimestamp2KHR") + vtable.CompileDeferredNV = auto_cast GetDeviceProcAddr(device, "vkCompileDeferredNV") + vtable.CopyAccelerationStructureKHR = auto_cast GetDeviceProcAddr(device, "vkCopyAccelerationStructureKHR") + vtable.CopyAccelerationStructureToMemoryKHR = auto_cast GetDeviceProcAddr(device, "vkCopyAccelerationStructureToMemoryKHR") + vtable.CopyMemoryToAccelerationStructureKHR = auto_cast GetDeviceProcAddr(device, "vkCopyMemoryToAccelerationStructureKHR") + vtable.CreateAccelerationStructureKHR = auto_cast GetDeviceProcAddr(device, "vkCreateAccelerationStructureKHR") + vtable.CreateAccelerationStructureNV = auto_cast GetDeviceProcAddr(device, "vkCreateAccelerationStructureNV") + vtable.CreateBuffer = auto_cast GetDeviceProcAddr(device, "vkCreateBuffer") + vtable.CreateBufferView = auto_cast GetDeviceProcAddr(device, "vkCreateBufferView") + vtable.CreateCommandPool = auto_cast GetDeviceProcAddr(device, "vkCreateCommandPool") + vtable.CreateComputePipelines = auto_cast GetDeviceProcAddr(device, "vkCreateComputePipelines") + vtable.CreateCuFunctionNVX = auto_cast GetDeviceProcAddr(device, "vkCreateCuFunctionNVX") + vtable.CreateCuModuleNVX = auto_cast GetDeviceProcAddr(device, "vkCreateCuModuleNVX") + vtable.CreateDeferredOperationKHR = auto_cast GetDeviceProcAddr(device, "vkCreateDeferredOperationKHR") + vtable.CreateDescriptorPool = auto_cast GetDeviceProcAddr(device, "vkCreateDescriptorPool") + vtable.CreateDescriptorSetLayout = auto_cast GetDeviceProcAddr(device, "vkCreateDescriptorSetLayout") + vtable.CreateDescriptorUpdateTemplate = auto_cast GetDeviceProcAddr(device, "vkCreateDescriptorUpdateTemplate") + vtable.CreateDescriptorUpdateTemplateKHR = auto_cast GetDeviceProcAddr(device, "vkCreateDescriptorUpdateTemplateKHR") + vtable.CreateEvent = auto_cast GetDeviceProcAddr(device, "vkCreateEvent") + vtable.CreateFence = auto_cast GetDeviceProcAddr(device, "vkCreateFence") + vtable.CreateFramebuffer = auto_cast GetDeviceProcAddr(device, "vkCreateFramebuffer") + vtable.CreateGraphicsPipelines = auto_cast GetDeviceProcAddr(device, "vkCreateGraphicsPipelines") + vtable.CreateImage = auto_cast GetDeviceProcAddr(device, "vkCreateImage") + vtable.CreateImageView = auto_cast GetDeviceProcAddr(device, "vkCreateImageView") + vtable.CreateIndirectCommandsLayoutNV = auto_cast GetDeviceProcAddr(device, "vkCreateIndirectCommandsLayoutNV") + vtable.CreatePipelineCache = auto_cast GetDeviceProcAddr(device, "vkCreatePipelineCache") + vtable.CreatePipelineLayout = auto_cast GetDeviceProcAddr(device, "vkCreatePipelineLayout") + vtable.CreatePrivateDataSlot = auto_cast GetDeviceProcAddr(device, "vkCreatePrivateDataSlot") + vtable.CreatePrivateDataSlotEXT = auto_cast GetDeviceProcAddr(device, "vkCreatePrivateDataSlotEXT") + vtable.CreateQueryPool = auto_cast GetDeviceProcAddr(device, "vkCreateQueryPool") + vtable.CreateRayTracingPipelinesKHR = auto_cast GetDeviceProcAddr(device, "vkCreateRayTracingPipelinesKHR") + vtable.CreateRayTracingPipelinesNV = auto_cast GetDeviceProcAddr(device, "vkCreateRayTracingPipelinesNV") + vtable.CreateRenderPass = auto_cast GetDeviceProcAddr(device, "vkCreateRenderPass") + vtable.CreateRenderPass2 = auto_cast GetDeviceProcAddr(device, "vkCreateRenderPass2") + vtable.CreateRenderPass2KHR = auto_cast GetDeviceProcAddr(device, "vkCreateRenderPass2KHR") + vtable.CreateSampler = auto_cast GetDeviceProcAddr(device, "vkCreateSampler") + vtable.CreateSamplerYcbcrConversion = auto_cast GetDeviceProcAddr(device, "vkCreateSamplerYcbcrConversion") + vtable.CreateSamplerYcbcrConversionKHR = auto_cast GetDeviceProcAddr(device, "vkCreateSamplerYcbcrConversionKHR") + vtable.CreateSemaphore = auto_cast GetDeviceProcAddr(device, "vkCreateSemaphore") + vtable.CreateShaderModule = auto_cast GetDeviceProcAddr(device, "vkCreateShaderModule") + vtable.CreateSharedSwapchainsKHR = auto_cast GetDeviceProcAddr(device, "vkCreateSharedSwapchainsKHR") + vtable.CreateSwapchainKHR = auto_cast GetDeviceProcAddr(device, "vkCreateSwapchainKHR") + vtable.CreateValidationCacheEXT = auto_cast GetDeviceProcAddr(device, "vkCreateValidationCacheEXT") + vtable.DebugMarkerSetObjectNameEXT = auto_cast GetDeviceProcAddr(device, "vkDebugMarkerSetObjectNameEXT") + vtable.DebugMarkerSetObjectTagEXT = auto_cast GetDeviceProcAddr(device, "vkDebugMarkerSetObjectTagEXT") + vtable.DeferredOperationJoinKHR = auto_cast GetDeviceProcAddr(device, "vkDeferredOperationJoinKHR") + vtable.DestroyAccelerationStructureKHR = auto_cast GetDeviceProcAddr(device, "vkDestroyAccelerationStructureKHR") + vtable.DestroyAccelerationStructureNV = auto_cast GetDeviceProcAddr(device, "vkDestroyAccelerationStructureNV") + vtable.DestroyBuffer = auto_cast GetDeviceProcAddr(device, "vkDestroyBuffer") + vtable.DestroyBufferView = auto_cast GetDeviceProcAddr(device, "vkDestroyBufferView") + vtable.DestroyCommandPool = auto_cast GetDeviceProcAddr(device, "vkDestroyCommandPool") + vtable.DestroyCuFunctionNVX = auto_cast GetDeviceProcAddr(device, "vkDestroyCuFunctionNVX") + vtable.DestroyCuModuleNVX = auto_cast GetDeviceProcAddr(device, "vkDestroyCuModuleNVX") + vtable.DestroyDeferredOperationKHR = auto_cast GetDeviceProcAddr(device, "vkDestroyDeferredOperationKHR") + vtable.DestroyDescriptorPool = auto_cast GetDeviceProcAddr(device, "vkDestroyDescriptorPool") + vtable.DestroyDescriptorSetLayout = auto_cast GetDeviceProcAddr(device, "vkDestroyDescriptorSetLayout") + vtable.DestroyDescriptorUpdateTemplate = auto_cast GetDeviceProcAddr(device, "vkDestroyDescriptorUpdateTemplate") + vtable.DestroyDescriptorUpdateTemplateKHR = auto_cast GetDeviceProcAddr(device, "vkDestroyDescriptorUpdateTemplateKHR") + vtable.DestroyDevice = auto_cast GetDeviceProcAddr(device, "vkDestroyDevice") + vtable.DestroyEvent = auto_cast GetDeviceProcAddr(device, "vkDestroyEvent") + vtable.DestroyFence = auto_cast GetDeviceProcAddr(device, "vkDestroyFence") + vtable.DestroyFramebuffer = auto_cast GetDeviceProcAddr(device, "vkDestroyFramebuffer") + vtable.DestroyImage = auto_cast GetDeviceProcAddr(device, "vkDestroyImage") + vtable.DestroyImageView = auto_cast GetDeviceProcAddr(device, "vkDestroyImageView") + vtable.DestroyIndirectCommandsLayoutNV = auto_cast GetDeviceProcAddr(device, "vkDestroyIndirectCommandsLayoutNV") + vtable.DestroyPipeline = auto_cast GetDeviceProcAddr(device, "vkDestroyPipeline") + vtable.DestroyPipelineCache = auto_cast GetDeviceProcAddr(device, "vkDestroyPipelineCache") + vtable.DestroyPipelineLayout = auto_cast GetDeviceProcAddr(device, "vkDestroyPipelineLayout") + vtable.DestroyPrivateDataSlot = auto_cast GetDeviceProcAddr(device, "vkDestroyPrivateDataSlot") + vtable.DestroyPrivateDataSlotEXT = auto_cast GetDeviceProcAddr(device, "vkDestroyPrivateDataSlotEXT") + vtable.DestroyQueryPool = auto_cast GetDeviceProcAddr(device, "vkDestroyQueryPool") + vtable.DestroyRenderPass = auto_cast GetDeviceProcAddr(device, "vkDestroyRenderPass") + vtable.DestroySampler = auto_cast GetDeviceProcAddr(device, "vkDestroySampler") + vtable.DestroySamplerYcbcrConversion = auto_cast GetDeviceProcAddr(device, "vkDestroySamplerYcbcrConversion") + vtable.DestroySamplerYcbcrConversionKHR = auto_cast GetDeviceProcAddr(device, "vkDestroySamplerYcbcrConversionKHR") + vtable.DestroySemaphore = auto_cast GetDeviceProcAddr(device, "vkDestroySemaphore") + vtable.DestroyShaderModule = auto_cast GetDeviceProcAddr(device, "vkDestroyShaderModule") + vtable.DestroySwapchainKHR = auto_cast GetDeviceProcAddr(device, "vkDestroySwapchainKHR") + vtable.DestroyValidationCacheEXT = auto_cast GetDeviceProcAddr(device, "vkDestroyValidationCacheEXT") + vtable.DeviceWaitIdle = auto_cast GetDeviceProcAddr(device, "vkDeviceWaitIdle") + vtable.DisplayPowerControlEXT = auto_cast GetDeviceProcAddr(device, "vkDisplayPowerControlEXT") + vtable.EndCommandBuffer = auto_cast GetDeviceProcAddr(device, "vkEndCommandBuffer") + vtable.FlushMappedMemoryRanges = auto_cast GetDeviceProcAddr(device, "vkFlushMappedMemoryRanges") + vtable.FreeCommandBuffers = auto_cast GetDeviceProcAddr(device, "vkFreeCommandBuffers") + vtable.FreeDescriptorSets = auto_cast GetDeviceProcAddr(device, "vkFreeDescriptorSets") + vtable.FreeMemory = auto_cast GetDeviceProcAddr(device, "vkFreeMemory") + vtable.GetAccelerationStructureBuildSizesKHR = auto_cast GetDeviceProcAddr(device, "vkGetAccelerationStructureBuildSizesKHR") + vtable.GetAccelerationStructureDeviceAddressKHR = auto_cast GetDeviceProcAddr(device, "vkGetAccelerationStructureDeviceAddressKHR") + vtable.GetAccelerationStructureHandleNV = auto_cast GetDeviceProcAddr(device, "vkGetAccelerationStructureHandleNV") + vtable.GetAccelerationStructureMemoryRequirementsNV = auto_cast GetDeviceProcAddr(device, "vkGetAccelerationStructureMemoryRequirementsNV") + vtable.GetBufferDeviceAddress = auto_cast GetDeviceProcAddr(device, "vkGetBufferDeviceAddress") + vtable.GetBufferDeviceAddressEXT = auto_cast GetDeviceProcAddr(device, "vkGetBufferDeviceAddressEXT") + vtable.GetBufferDeviceAddressKHR = auto_cast GetDeviceProcAddr(device, "vkGetBufferDeviceAddressKHR") + vtable.GetBufferMemoryRequirements = auto_cast GetDeviceProcAddr(device, "vkGetBufferMemoryRequirements") + vtable.GetBufferMemoryRequirements2 = auto_cast GetDeviceProcAddr(device, "vkGetBufferMemoryRequirements2") + vtable.GetBufferMemoryRequirements2KHR = auto_cast GetDeviceProcAddr(device, "vkGetBufferMemoryRequirements2KHR") + vtable.GetBufferOpaqueCaptureAddress = auto_cast GetDeviceProcAddr(device, "vkGetBufferOpaqueCaptureAddress") + vtable.GetBufferOpaqueCaptureAddressKHR = auto_cast GetDeviceProcAddr(device, "vkGetBufferOpaqueCaptureAddressKHR") + vtable.GetCalibratedTimestampsEXT = auto_cast GetDeviceProcAddr(device, "vkGetCalibratedTimestampsEXT") + vtable.GetDeferredOperationMaxConcurrencyKHR = auto_cast GetDeviceProcAddr(device, "vkGetDeferredOperationMaxConcurrencyKHR") + vtable.GetDeferredOperationResultKHR = auto_cast GetDeviceProcAddr(device, "vkGetDeferredOperationResultKHR") + vtable.GetDescriptorSetHostMappingVALVE = auto_cast GetDeviceProcAddr(device, "vkGetDescriptorSetHostMappingVALVE") + vtable.GetDescriptorSetLayoutHostMappingInfoVALVE = auto_cast GetDeviceProcAddr(device, "vkGetDescriptorSetLayoutHostMappingInfoVALVE") + vtable.GetDescriptorSetLayoutSupport = auto_cast GetDeviceProcAddr(device, "vkGetDescriptorSetLayoutSupport") + vtable.GetDescriptorSetLayoutSupportKHR = auto_cast GetDeviceProcAddr(device, "vkGetDescriptorSetLayoutSupportKHR") + vtable.GetDeviceAccelerationStructureCompatibilityKHR = auto_cast GetDeviceProcAddr(device, "vkGetDeviceAccelerationStructureCompatibilityKHR") + vtable.GetDeviceBufferMemoryRequirements = auto_cast GetDeviceProcAddr(device, "vkGetDeviceBufferMemoryRequirements") + vtable.GetDeviceBufferMemoryRequirementsKHR = auto_cast GetDeviceProcAddr(device, "vkGetDeviceBufferMemoryRequirementsKHR") + vtable.GetDeviceGroupPeerMemoryFeatures = auto_cast GetDeviceProcAddr(device, "vkGetDeviceGroupPeerMemoryFeatures") + vtable.GetDeviceGroupPeerMemoryFeaturesKHR = auto_cast GetDeviceProcAddr(device, "vkGetDeviceGroupPeerMemoryFeaturesKHR") + vtable.GetDeviceGroupPresentCapabilitiesKHR = auto_cast GetDeviceProcAddr(device, "vkGetDeviceGroupPresentCapabilitiesKHR") + vtable.GetDeviceGroupSurfacePresentModes2EXT = auto_cast GetDeviceProcAddr(device, "vkGetDeviceGroupSurfacePresentModes2EXT") + vtable.GetDeviceGroupSurfacePresentModesKHR = auto_cast GetDeviceProcAddr(device, "vkGetDeviceGroupSurfacePresentModesKHR") + vtable.GetDeviceImageMemoryRequirements = auto_cast GetDeviceProcAddr(device, "vkGetDeviceImageMemoryRequirements") + vtable.GetDeviceImageMemoryRequirementsKHR = auto_cast GetDeviceProcAddr(device, "vkGetDeviceImageMemoryRequirementsKHR") + vtable.GetDeviceImageSparseMemoryRequirements = auto_cast GetDeviceProcAddr(device, "vkGetDeviceImageSparseMemoryRequirements") + vtable.GetDeviceImageSparseMemoryRequirementsKHR = auto_cast GetDeviceProcAddr(device, "vkGetDeviceImageSparseMemoryRequirementsKHR") + vtable.GetDeviceMemoryCommitment = auto_cast GetDeviceProcAddr(device, "vkGetDeviceMemoryCommitment") + vtable.GetDeviceMemoryOpaqueCaptureAddress = auto_cast GetDeviceProcAddr(device, "vkGetDeviceMemoryOpaqueCaptureAddress") + vtable.GetDeviceMemoryOpaqueCaptureAddressKHR = auto_cast GetDeviceProcAddr(device, "vkGetDeviceMemoryOpaqueCaptureAddressKHR") + vtable.GetDeviceProcAddr = auto_cast GetDeviceProcAddr(device, "vkGetDeviceProcAddr") + vtable.GetDeviceQueue = auto_cast GetDeviceProcAddr(device, "vkGetDeviceQueue") + vtable.GetDeviceQueue2 = auto_cast GetDeviceProcAddr(device, "vkGetDeviceQueue2") + vtable.GetDeviceSubpassShadingMaxWorkgroupSizeHUAWEI = auto_cast GetDeviceProcAddr(device, "vkGetDeviceSubpassShadingMaxWorkgroupSizeHUAWEI") + vtable.GetEventStatus = auto_cast GetDeviceProcAddr(device, "vkGetEventStatus") + vtable.GetFenceFdKHR = auto_cast GetDeviceProcAddr(device, "vkGetFenceFdKHR") + vtable.GetFenceStatus = auto_cast GetDeviceProcAddr(device, "vkGetFenceStatus") + vtable.GetFenceWin32HandleKHR = auto_cast GetDeviceProcAddr(device, "vkGetFenceWin32HandleKHR") + vtable.GetGeneratedCommandsMemoryRequirementsNV = auto_cast GetDeviceProcAddr(device, "vkGetGeneratedCommandsMemoryRequirementsNV") + vtable.GetImageDrmFormatModifierPropertiesEXT = auto_cast GetDeviceProcAddr(device, "vkGetImageDrmFormatModifierPropertiesEXT") + vtable.GetImageMemoryRequirements = auto_cast GetDeviceProcAddr(device, "vkGetImageMemoryRequirements") + vtable.GetImageMemoryRequirements2 = auto_cast GetDeviceProcAddr(device, "vkGetImageMemoryRequirements2") + vtable.GetImageMemoryRequirements2KHR = auto_cast GetDeviceProcAddr(device, "vkGetImageMemoryRequirements2KHR") + vtable.GetImageSparseMemoryRequirements = auto_cast GetDeviceProcAddr(device, "vkGetImageSparseMemoryRequirements") + vtable.GetImageSparseMemoryRequirements2 = auto_cast GetDeviceProcAddr(device, "vkGetImageSparseMemoryRequirements2") + vtable.GetImageSparseMemoryRequirements2KHR = auto_cast GetDeviceProcAddr(device, "vkGetImageSparseMemoryRequirements2KHR") + vtable.GetImageSubresourceLayout = auto_cast GetDeviceProcAddr(device, "vkGetImageSubresourceLayout") + vtable.GetImageViewAddressNVX = auto_cast GetDeviceProcAddr(device, "vkGetImageViewAddressNVX") + vtable.GetImageViewHandleNVX = auto_cast GetDeviceProcAddr(device, "vkGetImageViewHandleNVX") + vtable.GetMemoryFdKHR = auto_cast GetDeviceProcAddr(device, "vkGetMemoryFdKHR") + vtable.GetMemoryFdPropertiesKHR = auto_cast GetDeviceProcAddr(device, "vkGetMemoryFdPropertiesKHR") + vtable.GetMemoryHostPointerPropertiesEXT = auto_cast GetDeviceProcAddr(device, "vkGetMemoryHostPointerPropertiesEXT") + vtable.GetMemoryRemoteAddressNV = auto_cast GetDeviceProcAddr(device, "vkGetMemoryRemoteAddressNV") + vtable.GetMemoryWin32HandleKHR = auto_cast GetDeviceProcAddr(device, "vkGetMemoryWin32HandleKHR") + vtable.GetMemoryWin32HandleNV = auto_cast GetDeviceProcAddr(device, "vkGetMemoryWin32HandleNV") + vtable.GetMemoryWin32HandlePropertiesKHR = auto_cast GetDeviceProcAddr(device, "vkGetMemoryWin32HandlePropertiesKHR") + vtable.GetPastPresentationTimingGOOGLE = auto_cast GetDeviceProcAddr(device, "vkGetPastPresentationTimingGOOGLE") + vtable.GetPerformanceParameterINTEL = auto_cast GetDeviceProcAddr(device, "vkGetPerformanceParameterINTEL") + vtable.GetPipelineCacheData = auto_cast GetDeviceProcAddr(device, "vkGetPipelineCacheData") + vtable.GetPipelineExecutableInternalRepresentationsKHR = auto_cast GetDeviceProcAddr(device, "vkGetPipelineExecutableInternalRepresentationsKHR") + vtable.GetPipelineExecutablePropertiesKHR = auto_cast GetDeviceProcAddr(device, "vkGetPipelineExecutablePropertiesKHR") + vtable.GetPipelineExecutableStatisticsKHR = auto_cast GetDeviceProcAddr(device, "vkGetPipelineExecutableStatisticsKHR") + vtable.GetPrivateData = auto_cast GetDeviceProcAddr(device, "vkGetPrivateData") + vtable.GetPrivateDataEXT = auto_cast GetDeviceProcAddr(device, "vkGetPrivateDataEXT") + vtable.GetQueryPoolResults = auto_cast GetDeviceProcAddr(device, "vkGetQueryPoolResults") + vtable.GetQueueCheckpointData2NV = auto_cast GetDeviceProcAddr(device, "vkGetQueueCheckpointData2NV") + vtable.GetQueueCheckpointDataNV = auto_cast GetDeviceProcAddr(device, "vkGetQueueCheckpointDataNV") + vtable.GetRayTracingCaptureReplayShaderGroupHandlesKHR = auto_cast GetDeviceProcAddr(device, "vkGetRayTracingCaptureReplayShaderGroupHandlesKHR") + vtable.GetRayTracingShaderGroupHandlesKHR = auto_cast GetDeviceProcAddr(device, "vkGetRayTracingShaderGroupHandlesKHR") + vtable.GetRayTracingShaderGroupHandlesNV = auto_cast GetDeviceProcAddr(device, "vkGetRayTracingShaderGroupHandlesNV") + vtable.GetRayTracingShaderGroupStackSizeKHR = auto_cast GetDeviceProcAddr(device, "vkGetRayTracingShaderGroupStackSizeKHR") + vtable.GetRefreshCycleDurationGOOGLE = auto_cast GetDeviceProcAddr(device, "vkGetRefreshCycleDurationGOOGLE") + vtable.GetRenderAreaGranularity = auto_cast GetDeviceProcAddr(device, "vkGetRenderAreaGranularity") + vtable.GetSemaphoreCounterValue = auto_cast GetDeviceProcAddr(device, "vkGetSemaphoreCounterValue") + vtable.GetSemaphoreCounterValueKHR = auto_cast GetDeviceProcAddr(device, "vkGetSemaphoreCounterValueKHR") + vtable.GetSemaphoreFdKHR = auto_cast GetDeviceProcAddr(device, "vkGetSemaphoreFdKHR") + vtable.GetSemaphoreWin32HandleKHR = auto_cast GetDeviceProcAddr(device, "vkGetSemaphoreWin32HandleKHR") + vtable.GetShaderInfoAMD = auto_cast GetDeviceProcAddr(device, "vkGetShaderInfoAMD") + vtable.GetSwapchainCounterEXT = auto_cast GetDeviceProcAddr(device, "vkGetSwapchainCounterEXT") + vtable.GetSwapchainImagesKHR = auto_cast GetDeviceProcAddr(device, "vkGetSwapchainImagesKHR") + vtable.GetSwapchainStatusKHR = auto_cast GetDeviceProcAddr(device, "vkGetSwapchainStatusKHR") + vtable.GetValidationCacheDataEXT = auto_cast GetDeviceProcAddr(device, "vkGetValidationCacheDataEXT") + vtable.ImportFenceFdKHR = auto_cast GetDeviceProcAddr(device, "vkImportFenceFdKHR") + vtable.ImportFenceWin32HandleKHR = auto_cast GetDeviceProcAddr(device, "vkImportFenceWin32HandleKHR") + vtable.ImportSemaphoreFdKHR = auto_cast GetDeviceProcAddr(device, "vkImportSemaphoreFdKHR") + vtable.ImportSemaphoreWin32HandleKHR = auto_cast GetDeviceProcAddr(device, "vkImportSemaphoreWin32HandleKHR") + vtable.InitializePerformanceApiINTEL = auto_cast GetDeviceProcAddr(device, "vkInitializePerformanceApiINTEL") + vtable.InvalidateMappedMemoryRanges = auto_cast GetDeviceProcAddr(device, "vkInvalidateMappedMemoryRanges") + vtable.MapMemory = auto_cast GetDeviceProcAddr(device, "vkMapMemory") + vtable.MergePipelineCaches = auto_cast GetDeviceProcAddr(device, "vkMergePipelineCaches") + vtable.MergeValidationCachesEXT = auto_cast GetDeviceProcAddr(device, "vkMergeValidationCachesEXT") + vtable.QueueBeginDebugUtilsLabelEXT = auto_cast GetDeviceProcAddr(device, "vkQueueBeginDebugUtilsLabelEXT") + vtable.QueueBindSparse = auto_cast GetDeviceProcAddr(device, "vkQueueBindSparse") + vtable.QueueEndDebugUtilsLabelEXT = auto_cast GetDeviceProcAddr(device, "vkQueueEndDebugUtilsLabelEXT") + vtable.QueueInsertDebugUtilsLabelEXT = auto_cast GetDeviceProcAddr(device, "vkQueueInsertDebugUtilsLabelEXT") + vtable.QueuePresentKHR = auto_cast GetDeviceProcAddr(device, "vkQueuePresentKHR") + vtable.QueueSetPerformanceConfigurationINTEL = auto_cast GetDeviceProcAddr(device, "vkQueueSetPerformanceConfigurationINTEL") + vtable.QueueSubmit = auto_cast GetDeviceProcAddr(device, "vkQueueSubmit") + vtable.QueueSubmit2 = auto_cast GetDeviceProcAddr(device, "vkQueueSubmit2") + vtable.QueueSubmit2KHR = auto_cast GetDeviceProcAddr(device, "vkQueueSubmit2KHR") + vtable.QueueWaitIdle = auto_cast GetDeviceProcAddr(device, "vkQueueWaitIdle") + vtable.RegisterDeviceEventEXT = auto_cast GetDeviceProcAddr(device, "vkRegisterDeviceEventEXT") + vtable.RegisterDisplayEventEXT = auto_cast GetDeviceProcAddr(device, "vkRegisterDisplayEventEXT") + vtable.ReleaseFullScreenExclusiveModeEXT = auto_cast GetDeviceProcAddr(device, "vkReleaseFullScreenExclusiveModeEXT") + vtable.ReleasePerformanceConfigurationINTEL = auto_cast GetDeviceProcAddr(device, "vkReleasePerformanceConfigurationINTEL") + vtable.ReleaseProfilingLockKHR = auto_cast GetDeviceProcAddr(device, "vkReleaseProfilingLockKHR") + vtable.ResetCommandBuffer = auto_cast GetDeviceProcAddr(device, "vkResetCommandBuffer") + vtable.ResetCommandPool = auto_cast GetDeviceProcAddr(device, "vkResetCommandPool") + vtable.ResetDescriptorPool = auto_cast GetDeviceProcAddr(device, "vkResetDescriptorPool") + vtable.ResetEvent = auto_cast GetDeviceProcAddr(device, "vkResetEvent") + vtable.ResetFences = auto_cast GetDeviceProcAddr(device, "vkResetFences") + vtable.ResetQueryPool = auto_cast GetDeviceProcAddr(device, "vkResetQueryPool") + vtable.ResetQueryPoolEXT = auto_cast GetDeviceProcAddr(device, "vkResetQueryPoolEXT") + vtable.SetDebugUtilsObjectNameEXT = auto_cast GetDeviceProcAddr(device, "vkSetDebugUtilsObjectNameEXT") + vtable.SetDebugUtilsObjectTagEXT = auto_cast GetDeviceProcAddr(device, "vkSetDebugUtilsObjectTagEXT") + vtable.SetDeviceMemoryPriorityEXT = auto_cast GetDeviceProcAddr(device, "vkSetDeviceMemoryPriorityEXT") + vtable.SetEvent = auto_cast GetDeviceProcAddr(device, "vkSetEvent") + vtable.SetHdrMetadataEXT = auto_cast GetDeviceProcAddr(device, "vkSetHdrMetadataEXT") + vtable.SetLocalDimmingAMD = auto_cast GetDeviceProcAddr(device, "vkSetLocalDimmingAMD") + vtable.SetPrivateData = auto_cast GetDeviceProcAddr(device, "vkSetPrivateData") + vtable.SetPrivateDataEXT = auto_cast GetDeviceProcAddr(device, "vkSetPrivateDataEXT") + vtable.SignalSemaphore = auto_cast GetDeviceProcAddr(device, "vkSignalSemaphore") + vtable.SignalSemaphoreKHR = auto_cast GetDeviceProcAddr(device, "vkSignalSemaphoreKHR") + vtable.TrimCommandPool = auto_cast GetDeviceProcAddr(device, "vkTrimCommandPool") + vtable.TrimCommandPoolKHR = auto_cast GetDeviceProcAddr(device, "vkTrimCommandPoolKHR") + vtable.UninitializePerformanceApiINTEL = auto_cast GetDeviceProcAddr(device, "vkUninitializePerformanceApiINTEL") + vtable.UnmapMemory = auto_cast GetDeviceProcAddr(device, "vkUnmapMemory") + vtable.UpdateDescriptorSetWithTemplate = auto_cast GetDeviceProcAddr(device, "vkUpdateDescriptorSetWithTemplate") + vtable.UpdateDescriptorSetWithTemplateKHR = auto_cast GetDeviceProcAddr(device, "vkUpdateDescriptorSetWithTemplateKHR") + vtable.UpdateDescriptorSets = auto_cast GetDeviceProcAddr(device, "vkUpdateDescriptorSets") + vtable.WaitForFences = auto_cast GetDeviceProcAddr(device, "vkWaitForFences") + vtable.WaitForPresentKHR = auto_cast GetDeviceProcAddr(device, "vkWaitForPresentKHR") + vtable.WaitSemaphores = auto_cast GetDeviceProcAddr(device, "vkWaitSemaphores") + vtable.WaitSemaphoresKHR = auto_cast GetDeviceProcAddr(device, "vkWaitSemaphoresKHR") + vtable.WriteAccelerationStructuresPropertiesKHR = auto_cast GetDeviceProcAddr(device, "vkWriteAccelerationStructuresPropertiesKHR") +} + +load_proc_addresses_device :: proc(device: Device) { + AcquireFullScreenExclusiveModeEXT = auto_cast GetDeviceProcAddr(device, "vkAcquireFullScreenExclusiveModeEXT") + AcquireNextImage2KHR = auto_cast GetDeviceProcAddr(device, "vkAcquireNextImage2KHR") + AcquireNextImageKHR = auto_cast GetDeviceProcAddr(device, "vkAcquireNextImageKHR") + AcquirePerformanceConfigurationINTEL = auto_cast GetDeviceProcAddr(device, "vkAcquirePerformanceConfigurationINTEL") + AcquireProfilingLockKHR = auto_cast GetDeviceProcAddr(device, "vkAcquireProfilingLockKHR") + AllocateCommandBuffers = auto_cast GetDeviceProcAddr(device, "vkAllocateCommandBuffers") + AllocateDescriptorSets = auto_cast GetDeviceProcAddr(device, "vkAllocateDescriptorSets") + AllocateMemory = auto_cast GetDeviceProcAddr(device, "vkAllocateMemory") + BeginCommandBuffer = auto_cast GetDeviceProcAddr(device, "vkBeginCommandBuffer") + BindAccelerationStructureMemoryNV = auto_cast GetDeviceProcAddr(device, "vkBindAccelerationStructureMemoryNV") + BindBufferMemory = auto_cast GetDeviceProcAddr(device, "vkBindBufferMemory") + BindBufferMemory2 = auto_cast GetDeviceProcAddr(device, "vkBindBufferMemory2") + BindBufferMemory2KHR = auto_cast GetDeviceProcAddr(device, "vkBindBufferMemory2KHR") + BindImageMemory = auto_cast GetDeviceProcAddr(device, "vkBindImageMemory") + BindImageMemory2 = auto_cast GetDeviceProcAddr(device, "vkBindImageMemory2") + BindImageMemory2KHR = auto_cast GetDeviceProcAddr(device, "vkBindImageMemory2KHR") + BuildAccelerationStructuresKHR = auto_cast GetDeviceProcAddr(device, "vkBuildAccelerationStructuresKHR") + CmdBeginConditionalRenderingEXT = auto_cast GetDeviceProcAddr(device, "vkCmdBeginConditionalRenderingEXT") + CmdBeginDebugUtilsLabelEXT = auto_cast GetDeviceProcAddr(device, "vkCmdBeginDebugUtilsLabelEXT") + CmdBeginQuery = auto_cast GetDeviceProcAddr(device, "vkCmdBeginQuery") + CmdBeginQueryIndexedEXT = auto_cast GetDeviceProcAddr(device, "vkCmdBeginQueryIndexedEXT") + CmdBeginRenderPass = auto_cast GetDeviceProcAddr(device, "vkCmdBeginRenderPass") + CmdBeginRenderPass2 = auto_cast GetDeviceProcAddr(device, "vkCmdBeginRenderPass2") + CmdBeginRenderPass2KHR = auto_cast GetDeviceProcAddr(device, "vkCmdBeginRenderPass2KHR") + CmdBeginRendering = auto_cast GetDeviceProcAddr(device, "vkCmdBeginRendering") + CmdBeginRenderingKHR = auto_cast GetDeviceProcAddr(device, "vkCmdBeginRenderingKHR") + CmdBeginTransformFeedbackEXT = auto_cast GetDeviceProcAddr(device, "vkCmdBeginTransformFeedbackEXT") + CmdBindDescriptorSets = auto_cast GetDeviceProcAddr(device, "vkCmdBindDescriptorSets") + CmdBindIndexBuffer = auto_cast GetDeviceProcAddr(device, "vkCmdBindIndexBuffer") + CmdBindInvocationMaskHUAWEI = auto_cast GetDeviceProcAddr(device, "vkCmdBindInvocationMaskHUAWEI") + CmdBindPipeline = auto_cast GetDeviceProcAddr(device, "vkCmdBindPipeline") + CmdBindPipelineShaderGroupNV = auto_cast GetDeviceProcAddr(device, "vkCmdBindPipelineShaderGroupNV") + CmdBindShadingRateImageNV = auto_cast GetDeviceProcAddr(device, "vkCmdBindShadingRateImageNV") + CmdBindTransformFeedbackBuffersEXT = auto_cast GetDeviceProcAddr(device, "vkCmdBindTransformFeedbackBuffersEXT") + CmdBindVertexBuffers = auto_cast GetDeviceProcAddr(device, "vkCmdBindVertexBuffers") + CmdBindVertexBuffers2 = auto_cast GetDeviceProcAddr(device, "vkCmdBindVertexBuffers2") + CmdBindVertexBuffers2EXT = auto_cast GetDeviceProcAddr(device, "vkCmdBindVertexBuffers2EXT") + CmdBlitImage = auto_cast GetDeviceProcAddr(device, "vkCmdBlitImage") + CmdBlitImage2 = auto_cast GetDeviceProcAddr(device, "vkCmdBlitImage2") + CmdBlitImage2KHR = auto_cast GetDeviceProcAddr(device, "vkCmdBlitImage2KHR") + CmdBuildAccelerationStructureNV = auto_cast GetDeviceProcAddr(device, "vkCmdBuildAccelerationStructureNV") + CmdBuildAccelerationStructuresIndirectKHR = auto_cast GetDeviceProcAddr(device, "vkCmdBuildAccelerationStructuresIndirectKHR") + CmdBuildAccelerationStructuresKHR = auto_cast GetDeviceProcAddr(device, "vkCmdBuildAccelerationStructuresKHR") + CmdClearAttachments = auto_cast GetDeviceProcAddr(device, "vkCmdClearAttachments") + CmdClearColorImage = auto_cast GetDeviceProcAddr(device, "vkCmdClearColorImage") + CmdClearDepthStencilImage = auto_cast GetDeviceProcAddr(device, "vkCmdClearDepthStencilImage") + CmdCopyAccelerationStructureKHR = auto_cast GetDeviceProcAddr(device, "vkCmdCopyAccelerationStructureKHR") + CmdCopyAccelerationStructureNV = auto_cast GetDeviceProcAddr(device, "vkCmdCopyAccelerationStructureNV") + CmdCopyAccelerationStructureToMemoryKHR = auto_cast GetDeviceProcAddr(device, "vkCmdCopyAccelerationStructureToMemoryKHR") + CmdCopyBuffer = auto_cast GetDeviceProcAddr(device, "vkCmdCopyBuffer") + CmdCopyBuffer2 = auto_cast GetDeviceProcAddr(device, "vkCmdCopyBuffer2") + CmdCopyBuffer2KHR = auto_cast GetDeviceProcAddr(device, "vkCmdCopyBuffer2KHR") + CmdCopyBufferToImage = auto_cast GetDeviceProcAddr(device, "vkCmdCopyBufferToImage") + CmdCopyBufferToImage2 = auto_cast GetDeviceProcAddr(device, "vkCmdCopyBufferToImage2") + CmdCopyBufferToImage2KHR = auto_cast GetDeviceProcAddr(device, "vkCmdCopyBufferToImage2KHR") + CmdCopyImage = auto_cast GetDeviceProcAddr(device, "vkCmdCopyImage") + CmdCopyImage2 = auto_cast GetDeviceProcAddr(device, "vkCmdCopyImage2") + CmdCopyImage2KHR = auto_cast GetDeviceProcAddr(device, "vkCmdCopyImage2KHR") + CmdCopyImageToBuffer = auto_cast GetDeviceProcAddr(device, "vkCmdCopyImageToBuffer") + CmdCopyImageToBuffer2 = auto_cast GetDeviceProcAddr(device, "vkCmdCopyImageToBuffer2") + CmdCopyImageToBuffer2KHR = auto_cast GetDeviceProcAddr(device, "vkCmdCopyImageToBuffer2KHR") + CmdCopyMemoryToAccelerationStructureKHR = auto_cast GetDeviceProcAddr(device, "vkCmdCopyMemoryToAccelerationStructureKHR") + CmdCopyQueryPoolResults = auto_cast GetDeviceProcAddr(device, "vkCmdCopyQueryPoolResults") + CmdCuLaunchKernelNVX = auto_cast GetDeviceProcAddr(device, "vkCmdCuLaunchKernelNVX") + CmdDebugMarkerBeginEXT = auto_cast GetDeviceProcAddr(device, "vkCmdDebugMarkerBeginEXT") + CmdDebugMarkerEndEXT = auto_cast GetDeviceProcAddr(device, "vkCmdDebugMarkerEndEXT") + CmdDebugMarkerInsertEXT = auto_cast GetDeviceProcAddr(device, "vkCmdDebugMarkerInsertEXT") + CmdDispatch = auto_cast GetDeviceProcAddr(device, "vkCmdDispatch") + CmdDispatchBase = auto_cast GetDeviceProcAddr(device, "vkCmdDispatchBase") + CmdDispatchBaseKHR = auto_cast GetDeviceProcAddr(device, "vkCmdDispatchBaseKHR") + CmdDispatchIndirect = auto_cast GetDeviceProcAddr(device, "vkCmdDispatchIndirect") + CmdDraw = auto_cast GetDeviceProcAddr(device, "vkCmdDraw") + CmdDrawIndexed = auto_cast GetDeviceProcAddr(device, "vkCmdDrawIndexed") + CmdDrawIndexedIndirect = auto_cast GetDeviceProcAddr(device, "vkCmdDrawIndexedIndirect") + CmdDrawIndexedIndirectCount = auto_cast GetDeviceProcAddr(device, "vkCmdDrawIndexedIndirectCount") + CmdDrawIndexedIndirectCountAMD = auto_cast GetDeviceProcAddr(device, "vkCmdDrawIndexedIndirectCountAMD") + CmdDrawIndexedIndirectCountKHR = auto_cast GetDeviceProcAddr(device, "vkCmdDrawIndexedIndirectCountKHR") + CmdDrawIndirect = auto_cast GetDeviceProcAddr(device, "vkCmdDrawIndirect") + CmdDrawIndirectByteCountEXT = auto_cast GetDeviceProcAddr(device, "vkCmdDrawIndirectByteCountEXT") + CmdDrawIndirectCount = auto_cast GetDeviceProcAddr(device, "vkCmdDrawIndirectCount") + CmdDrawIndirectCountAMD = auto_cast GetDeviceProcAddr(device, "vkCmdDrawIndirectCountAMD") + CmdDrawIndirectCountKHR = auto_cast GetDeviceProcAddr(device, "vkCmdDrawIndirectCountKHR") + CmdDrawMeshTasksIndirectCountNV = auto_cast GetDeviceProcAddr(device, "vkCmdDrawMeshTasksIndirectCountNV") + CmdDrawMeshTasksIndirectNV = auto_cast GetDeviceProcAddr(device, "vkCmdDrawMeshTasksIndirectNV") + CmdDrawMeshTasksNV = auto_cast GetDeviceProcAddr(device, "vkCmdDrawMeshTasksNV") + CmdDrawMultiEXT = auto_cast GetDeviceProcAddr(device, "vkCmdDrawMultiEXT") + CmdDrawMultiIndexedEXT = auto_cast GetDeviceProcAddr(device, "vkCmdDrawMultiIndexedEXT") + CmdEndConditionalRenderingEXT = auto_cast GetDeviceProcAddr(device, "vkCmdEndConditionalRenderingEXT") + CmdEndDebugUtilsLabelEXT = auto_cast GetDeviceProcAddr(device, "vkCmdEndDebugUtilsLabelEXT") + CmdEndQuery = auto_cast GetDeviceProcAddr(device, "vkCmdEndQuery") + CmdEndQueryIndexedEXT = auto_cast GetDeviceProcAddr(device, "vkCmdEndQueryIndexedEXT") + CmdEndRenderPass = auto_cast GetDeviceProcAddr(device, "vkCmdEndRenderPass") + CmdEndRenderPass2 = auto_cast GetDeviceProcAddr(device, "vkCmdEndRenderPass2") + CmdEndRenderPass2KHR = auto_cast GetDeviceProcAddr(device, "vkCmdEndRenderPass2KHR") + CmdEndRendering = auto_cast GetDeviceProcAddr(device, "vkCmdEndRendering") + CmdEndRenderingKHR = auto_cast GetDeviceProcAddr(device, "vkCmdEndRenderingKHR") + CmdEndTransformFeedbackEXT = auto_cast GetDeviceProcAddr(device, "vkCmdEndTransformFeedbackEXT") + CmdExecuteCommands = auto_cast GetDeviceProcAddr(device, "vkCmdExecuteCommands") + CmdExecuteGeneratedCommandsNV = auto_cast GetDeviceProcAddr(device, "vkCmdExecuteGeneratedCommandsNV") + CmdFillBuffer = auto_cast GetDeviceProcAddr(device, "vkCmdFillBuffer") + CmdInsertDebugUtilsLabelEXT = auto_cast GetDeviceProcAddr(device, "vkCmdInsertDebugUtilsLabelEXT") + CmdNextSubpass = auto_cast GetDeviceProcAddr(device, "vkCmdNextSubpass") + CmdNextSubpass2 = auto_cast GetDeviceProcAddr(device, "vkCmdNextSubpass2") + CmdNextSubpass2KHR = auto_cast GetDeviceProcAddr(device, "vkCmdNextSubpass2KHR") + CmdPipelineBarrier = auto_cast GetDeviceProcAddr(device, "vkCmdPipelineBarrier") + CmdPipelineBarrier2 = auto_cast GetDeviceProcAddr(device, "vkCmdPipelineBarrier2") + CmdPipelineBarrier2KHR = auto_cast GetDeviceProcAddr(device, "vkCmdPipelineBarrier2KHR") + CmdPreprocessGeneratedCommandsNV = auto_cast GetDeviceProcAddr(device, "vkCmdPreprocessGeneratedCommandsNV") + CmdPushConstants = auto_cast GetDeviceProcAddr(device, "vkCmdPushConstants") + CmdPushDescriptorSetKHR = auto_cast GetDeviceProcAddr(device, "vkCmdPushDescriptorSetKHR") + CmdPushDescriptorSetWithTemplateKHR = auto_cast GetDeviceProcAddr(device, "vkCmdPushDescriptorSetWithTemplateKHR") + CmdResetEvent = auto_cast GetDeviceProcAddr(device, "vkCmdResetEvent") + CmdResetEvent2 = auto_cast GetDeviceProcAddr(device, "vkCmdResetEvent2") + CmdResetEvent2KHR = auto_cast GetDeviceProcAddr(device, "vkCmdResetEvent2KHR") + CmdResetQueryPool = auto_cast GetDeviceProcAddr(device, "vkCmdResetQueryPool") + CmdResolveImage = auto_cast GetDeviceProcAddr(device, "vkCmdResolveImage") + CmdResolveImage2 = auto_cast GetDeviceProcAddr(device, "vkCmdResolveImage2") + CmdResolveImage2KHR = auto_cast GetDeviceProcAddr(device, "vkCmdResolveImage2KHR") + CmdSetBlendConstants = auto_cast GetDeviceProcAddr(device, "vkCmdSetBlendConstants") + CmdSetCheckpointNV = auto_cast GetDeviceProcAddr(device, "vkCmdSetCheckpointNV") + CmdSetCoarseSampleOrderNV = auto_cast GetDeviceProcAddr(device, "vkCmdSetCoarseSampleOrderNV") + CmdSetCullMode = auto_cast GetDeviceProcAddr(device, "vkCmdSetCullMode") + CmdSetCullModeEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetCullModeEXT") + CmdSetDepthBias = auto_cast GetDeviceProcAddr(device, "vkCmdSetDepthBias") + CmdSetDepthBiasEnable = auto_cast GetDeviceProcAddr(device, "vkCmdSetDepthBiasEnable") + CmdSetDepthBiasEnableEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetDepthBiasEnableEXT") + CmdSetDepthBounds = auto_cast GetDeviceProcAddr(device, "vkCmdSetDepthBounds") + CmdSetDepthBoundsTestEnable = auto_cast GetDeviceProcAddr(device, "vkCmdSetDepthBoundsTestEnable") + CmdSetDepthBoundsTestEnableEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetDepthBoundsTestEnableEXT") + CmdSetDepthCompareOp = auto_cast GetDeviceProcAddr(device, "vkCmdSetDepthCompareOp") + CmdSetDepthCompareOpEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetDepthCompareOpEXT") + CmdSetDepthTestEnable = auto_cast GetDeviceProcAddr(device, "vkCmdSetDepthTestEnable") + CmdSetDepthTestEnableEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetDepthTestEnableEXT") + CmdSetDepthWriteEnable = auto_cast GetDeviceProcAddr(device, "vkCmdSetDepthWriteEnable") + CmdSetDepthWriteEnableEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetDepthWriteEnableEXT") + CmdSetDeviceMask = auto_cast GetDeviceProcAddr(device, "vkCmdSetDeviceMask") + CmdSetDeviceMaskKHR = auto_cast GetDeviceProcAddr(device, "vkCmdSetDeviceMaskKHR") + CmdSetDiscardRectangleEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetDiscardRectangleEXT") + CmdSetEvent = auto_cast GetDeviceProcAddr(device, "vkCmdSetEvent") + CmdSetEvent2 = auto_cast GetDeviceProcAddr(device, "vkCmdSetEvent2") + CmdSetEvent2KHR = auto_cast GetDeviceProcAddr(device, "vkCmdSetEvent2KHR") + CmdSetExclusiveScissorNV = auto_cast GetDeviceProcAddr(device, "vkCmdSetExclusiveScissorNV") + CmdSetFragmentShadingRateEnumNV = auto_cast GetDeviceProcAddr(device, "vkCmdSetFragmentShadingRateEnumNV") + CmdSetFragmentShadingRateKHR = auto_cast GetDeviceProcAddr(device, "vkCmdSetFragmentShadingRateKHR") + CmdSetFrontFace = auto_cast GetDeviceProcAddr(device, "vkCmdSetFrontFace") + CmdSetFrontFaceEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetFrontFaceEXT") + CmdSetLineStippleEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetLineStippleEXT") + CmdSetLineWidth = auto_cast GetDeviceProcAddr(device, "vkCmdSetLineWidth") + CmdSetLogicOpEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetLogicOpEXT") + CmdSetPatchControlPointsEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetPatchControlPointsEXT") + CmdSetPerformanceMarkerINTEL = auto_cast GetDeviceProcAddr(device, "vkCmdSetPerformanceMarkerINTEL") + CmdSetPerformanceOverrideINTEL = auto_cast GetDeviceProcAddr(device, "vkCmdSetPerformanceOverrideINTEL") + CmdSetPerformanceStreamMarkerINTEL = auto_cast GetDeviceProcAddr(device, "vkCmdSetPerformanceStreamMarkerINTEL") + CmdSetPrimitiveRestartEnable = auto_cast GetDeviceProcAddr(device, "vkCmdSetPrimitiveRestartEnable") + CmdSetPrimitiveRestartEnableEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetPrimitiveRestartEnableEXT") + CmdSetPrimitiveTopology = auto_cast GetDeviceProcAddr(device, "vkCmdSetPrimitiveTopology") + CmdSetPrimitiveTopologyEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetPrimitiveTopologyEXT") + CmdSetRasterizerDiscardEnable = auto_cast GetDeviceProcAddr(device, "vkCmdSetRasterizerDiscardEnable") + CmdSetRasterizerDiscardEnableEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetRasterizerDiscardEnableEXT") + CmdSetRayTracingPipelineStackSizeKHR = auto_cast GetDeviceProcAddr(device, "vkCmdSetRayTracingPipelineStackSizeKHR") + CmdSetSampleLocationsEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetSampleLocationsEXT") + CmdSetScissor = auto_cast GetDeviceProcAddr(device, "vkCmdSetScissor") + CmdSetScissorWithCount = auto_cast GetDeviceProcAddr(device, "vkCmdSetScissorWithCount") + CmdSetScissorWithCountEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetScissorWithCountEXT") + CmdSetStencilCompareMask = auto_cast GetDeviceProcAddr(device, "vkCmdSetStencilCompareMask") + CmdSetStencilOp = auto_cast GetDeviceProcAddr(device, "vkCmdSetStencilOp") + CmdSetStencilOpEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetStencilOpEXT") + CmdSetStencilReference = auto_cast GetDeviceProcAddr(device, "vkCmdSetStencilReference") + CmdSetStencilTestEnable = auto_cast GetDeviceProcAddr(device, "vkCmdSetStencilTestEnable") + CmdSetStencilTestEnableEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetStencilTestEnableEXT") + CmdSetStencilWriteMask = auto_cast GetDeviceProcAddr(device, "vkCmdSetStencilWriteMask") + CmdSetVertexInputEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetVertexInputEXT") + CmdSetViewport = auto_cast GetDeviceProcAddr(device, "vkCmdSetViewport") + CmdSetViewportShadingRatePaletteNV = auto_cast GetDeviceProcAddr(device, "vkCmdSetViewportShadingRatePaletteNV") + CmdSetViewportWScalingNV = auto_cast GetDeviceProcAddr(device, "vkCmdSetViewportWScalingNV") + CmdSetViewportWithCount = auto_cast GetDeviceProcAddr(device, "vkCmdSetViewportWithCount") + CmdSetViewportWithCountEXT = auto_cast GetDeviceProcAddr(device, "vkCmdSetViewportWithCountEXT") + CmdSubpassShadingHUAWEI = auto_cast GetDeviceProcAddr(device, "vkCmdSubpassShadingHUAWEI") + CmdTraceRaysIndirectKHR = auto_cast GetDeviceProcAddr(device, "vkCmdTraceRaysIndirectKHR") + CmdTraceRaysKHR = auto_cast GetDeviceProcAddr(device, "vkCmdTraceRaysKHR") + CmdTraceRaysNV = auto_cast GetDeviceProcAddr(device, "vkCmdTraceRaysNV") + CmdUpdateBuffer = auto_cast GetDeviceProcAddr(device, "vkCmdUpdateBuffer") + CmdWaitEvents = auto_cast GetDeviceProcAddr(device, "vkCmdWaitEvents") + CmdWaitEvents2 = auto_cast GetDeviceProcAddr(device, "vkCmdWaitEvents2") + CmdWaitEvents2KHR = auto_cast GetDeviceProcAddr(device, "vkCmdWaitEvents2KHR") + CmdWriteAccelerationStructuresPropertiesKHR = auto_cast GetDeviceProcAddr(device, "vkCmdWriteAccelerationStructuresPropertiesKHR") + CmdWriteAccelerationStructuresPropertiesNV = auto_cast GetDeviceProcAddr(device, "vkCmdWriteAccelerationStructuresPropertiesNV") + CmdWriteBufferMarker2AMD = auto_cast GetDeviceProcAddr(device, "vkCmdWriteBufferMarker2AMD") + CmdWriteBufferMarkerAMD = auto_cast GetDeviceProcAddr(device, "vkCmdWriteBufferMarkerAMD") + CmdWriteTimestamp = auto_cast GetDeviceProcAddr(device, "vkCmdWriteTimestamp") + CmdWriteTimestamp2 = auto_cast GetDeviceProcAddr(device, "vkCmdWriteTimestamp2") + CmdWriteTimestamp2KHR = auto_cast GetDeviceProcAddr(device, "vkCmdWriteTimestamp2KHR") + CompileDeferredNV = auto_cast GetDeviceProcAddr(device, "vkCompileDeferredNV") + CopyAccelerationStructureKHR = auto_cast GetDeviceProcAddr(device, "vkCopyAccelerationStructureKHR") + CopyAccelerationStructureToMemoryKHR = auto_cast GetDeviceProcAddr(device, "vkCopyAccelerationStructureToMemoryKHR") + CopyMemoryToAccelerationStructureKHR = auto_cast GetDeviceProcAddr(device, "vkCopyMemoryToAccelerationStructureKHR") + CreateAccelerationStructureKHR = auto_cast GetDeviceProcAddr(device, "vkCreateAccelerationStructureKHR") + CreateAccelerationStructureNV = auto_cast GetDeviceProcAddr(device, "vkCreateAccelerationStructureNV") + CreateBuffer = auto_cast GetDeviceProcAddr(device, "vkCreateBuffer") + CreateBufferView = auto_cast GetDeviceProcAddr(device, "vkCreateBufferView") + CreateCommandPool = auto_cast GetDeviceProcAddr(device, "vkCreateCommandPool") + CreateComputePipelines = auto_cast GetDeviceProcAddr(device, "vkCreateComputePipelines") + CreateCuFunctionNVX = auto_cast GetDeviceProcAddr(device, "vkCreateCuFunctionNVX") + CreateCuModuleNVX = auto_cast GetDeviceProcAddr(device, "vkCreateCuModuleNVX") + CreateDeferredOperationKHR = auto_cast GetDeviceProcAddr(device, "vkCreateDeferredOperationKHR") + CreateDescriptorPool = auto_cast GetDeviceProcAddr(device, "vkCreateDescriptorPool") + CreateDescriptorSetLayout = auto_cast GetDeviceProcAddr(device, "vkCreateDescriptorSetLayout") + CreateDescriptorUpdateTemplate = auto_cast GetDeviceProcAddr(device, "vkCreateDescriptorUpdateTemplate") + CreateDescriptorUpdateTemplateKHR = auto_cast GetDeviceProcAddr(device, "vkCreateDescriptorUpdateTemplateKHR") + CreateEvent = auto_cast GetDeviceProcAddr(device, "vkCreateEvent") + CreateFence = auto_cast GetDeviceProcAddr(device, "vkCreateFence") + CreateFramebuffer = auto_cast GetDeviceProcAddr(device, "vkCreateFramebuffer") + CreateGraphicsPipelines = auto_cast GetDeviceProcAddr(device, "vkCreateGraphicsPipelines") + CreateImage = auto_cast GetDeviceProcAddr(device, "vkCreateImage") + CreateImageView = auto_cast GetDeviceProcAddr(device, "vkCreateImageView") + CreateIndirectCommandsLayoutNV = auto_cast GetDeviceProcAddr(device, "vkCreateIndirectCommandsLayoutNV") + CreatePipelineCache = auto_cast GetDeviceProcAddr(device, "vkCreatePipelineCache") + CreatePipelineLayout = auto_cast GetDeviceProcAddr(device, "vkCreatePipelineLayout") + CreatePrivateDataSlot = auto_cast GetDeviceProcAddr(device, "vkCreatePrivateDataSlot") + CreatePrivateDataSlotEXT = auto_cast GetDeviceProcAddr(device, "vkCreatePrivateDataSlotEXT") + CreateQueryPool = auto_cast GetDeviceProcAddr(device, "vkCreateQueryPool") + CreateRayTracingPipelinesKHR = auto_cast GetDeviceProcAddr(device, "vkCreateRayTracingPipelinesKHR") + CreateRayTracingPipelinesNV = auto_cast GetDeviceProcAddr(device, "vkCreateRayTracingPipelinesNV") + CreateRenderPass = auto_cast GetDeviceProcAddr(device, "vkCreateRenderPass") + CreateRenderPass2 = auto_cast GetDeviceProcAddr(device, "vkCreateRenderPass2") + CreateRenderPass2KHR = auto_cast GetDeviceProcAddr(device, "vkCreateRenderPass2KHR") + CreateSampler = auto_cast GetDeviceProcAddr(device, "vkCreateSampler") + CreateSamplerYcbcrConversion = auto_cast GetDeviceProcAddr(device, "vkCreateSamplerYcbcrConversion") + CreateSamplerYcbcrConversionKHR = auto_cast GetDeviceProcAddr(device, "vkCreateSamplerYcbcrConversionKHR") + CreateSemaphore = auto_cast GetDeviceProcAddr(device, "vkCreateSemaphore") + CreateShaderModule = auto_cast GetDeviceProcAddr(device, "vkCreateShaderModule") + CreateSharedSwapchainsKHR = auto_cast GetDeviceProcAddr(device, "vkCreateSharedSwapchainsKHR") + CreateSwapchainKHR = auto_cast GetDeviceProcAddr(device, "vkCreateSwapchainKHR") + CreateValidationCacheEXT = auto_cast GetDeviceProcAddr(device, "vkCreateValidationCacheEXT") + DebugMarkerSetObjectNameEXT = auto_cast GetDeviceProcAddr(device, "vkDebugMarkerSetObjectNameEXT") + DebugMarkerSetObjectTagEXT = auto_cast GetDeviceProcAddr(device, "vkDebugMarkerSetObjectTagEXT") + DeferredOperationJoinKHR = auto_cast GetDeviceProcAddr(device, "vkDeferredOperationJoinKHR") + DestroyAccelerationStructureKHR = auto_cast GetDeviceProcAddr(device, "vkDestroyAccelerationStructureKHR") + DestroyAccelerationStructureNV = auto_cast GetDeviceProcAddr(device, "vkDestroyAccelerationStructureNV") + DestroyBuffer = auto_cast GetDeviceProcAddr(device, "vkDestroyBuffer") + DestroyBufferView = auto_cast GetDeviceProcAddr(device, "vkDestroyBufferView") + DestroyCommandPool = auto_cast GetDeviceProcAddr(device, "vkDestroyCommandPool") + DestroyCuFunctionNVX = auto_cast GetDeviceProcAddr(device, "vkDestroyCuFunctionNVX") + DestroyCuModuleNVX = auto_cast GetDeviceProcAddr(device, "vkDestroyCuModuleNVX") + DestroyDeferredOperationKHR = auto_cast GetDeviceProcAddr(device, "vkDestroyDeferredOperationKHR") + DestroyDescriptorPool = auto_cast GetDeviceProcAddr(device, "vkDestroyDescriptorPool") + DestroyDescriptorSetLayout = auto_cast GetDeviceProcAddr(device, "vkDestroyDescriptorSetLayout") + DestroyDescriptorUpdateTemplate = auto_cast GetDeviceProcAddr(device, "vkDestroyDescriptorUpdateTemplate") + DestroyDescriptorUpdateTemplateKHR = auto_cast GetDeviceProcAddr(device, "vkDestroyDescriptorUpdateTemplateKHR") + DestroyDevice = auto_cast GetDeviceProcAddr(device, "vkDestroyDevice") + DestroyEvent = auto_cast GetDeviceProcAddr(device, "vkDestroyEvent") + DestroyFence = auto_cast GetDeviceProcAddr(device, "vkDestroyFence") + DestroyFramebuffer = auto_cast GetDeviceProcAddr(device, "vkDestroyFramebuffer") + DestroyImage = auto_cast GetDeviceProcAddr(device, "vkDestroyImage") + DestroyImageView = auto_cast GetDeviceProcAddr(device, "vkDestroyImageView") + DestroyIndirectCommandsLayoutNV = auto_cast GetDeviceProcAddr(device, "vkDestroyIndirectCommandsLayoutNV") + DestroyPipeline = auto_cast GetDeviceProcAddr(device, "vkDestroyPipeline") + DestroyPipelineCache = auto_cast GetDeviceProcAddr(device, "vkDestroyPipelineCache") + DestroyPipelineLayout = auto_cast GetDeviceProcAddr(device, "vkDestroyPipelineLayout") + DestroyPrivateDataSlot = auto_cast GetDeviceProcAddr(device, "vkDestroyPrivateDataSlot") + DestroyPrivateDataSlotEXT = auto_cast GetDeviceProcAddr(device, "vkDestroyPrivateDataSlotEXT") + DestroyQueryPool = auto_cast GetDeviceProcAddr(device, "vkDestroyQueryPool") + DestroyRenderPass = auto_cast GetDeviceProcAddr(device, "vkDestroyRenderPass") + DestroySampler = auto_cast GetDeviceProcAddr(device, "vkDestroySampler") + DestroySamplerYcbcrConversion = auto_cast GetDeviceProcAddr(device, "vkDestroySamplerYcbcrConversion") + DestroySamplerYcbcrConversionKHR = auto_cast GetDeviceProcAddr(device, "vkDestroySamplerYcbcrConversionKHR") + DestroySemaphore = auto_cast GetDeviceProcAddr(device, "vkDestroySemaphore") + DestroyShaderModule = auto_cast GetDeviceProcAddr(device, "vkDestroyShaderModule") + DestroySwapchainKHR = auto_cast GetDeviceProcAddr(device, "vkDestroySwapchainKHR") + DestroyValidationCacheEXT = auto_cast GetDeviceProcAddr(device, "vkDestroyValidationCacheEXT") + DeviceWaitIdle = auto_cast GetDeviceProcAddr(device, "vkDeviceWaitIdle") + DisplayPowerControlEXT = auto_cast GetDeviceProcAddr(device, "vkDisplayPowerControlEXT") + EndCommandBuffer = auto_cast GetDeviceProcAddr(device, "vkEndCommandBuffer") + FlushMappedMemoryRanges = auto_cast GetDeviceProcAddr(device, "vkFlushMappedMemoryRanges") + FreeCommandBuffers = auto_cast GetDeviceProcAddr(device, "vkFreeCommandBuffers") + FreeDescriptorSets = auto_cast GetDeviceProcAddr(device, "vkFreeDescriptorSets") + FreeMemory = auto_cast GetDeviceProcAddr(device, "vkFreeMemory") + GetAccelerationStructureBuildSizesKHR = auto_cast GetDeviceProcAddr(device, "vkGetAccelerationStructureBuildSizesKHR") + GetAccelerationStructureDeviceAddressKHR = auto_cast GetDeviceProcAddr(device, "vkGetAccelerationStructureDeviceAddressKHR") + GetAccelerationStructureHandleNV = auto_cast GetDeviceProcAddr(device, "vkGetAccelerationStructureHandleNV") + GetAccelerationStructureMemoryRequirementsNV = auto_cast GetDeviceProcAddr(device, "vkGetAccelerationStructureMemoryRequirementsNV") + GetBufferDeviceAddress = auto_cast GetDeviceProcAddr(device, "vkGetBufferDeviceAddress") + GetBufferDeviceAddressEXT = auto_cast GetDeviceProcAddr(device, "vkGetBufferDeviceAddressEXT") + GetBufferDeviceAddressKHR = auto_cast GetDeviceProcAddr(device, "vkGetBufferDeviceAddressKHR") + GetBufferMemoryRequirements = auto_cast GetDeviceProcAddr(device, "vkGetBufferMemoryRequirements") + GetBufferMemoryRequirements2 = auto_cast GetDeviceProcAddr(device, "vkGetBufferMemoryRequirements2") + GetBufferMemoryRequirements2KHR = auto_cast GetDeviceProcAddr(device, "vkGetBufferMemoryRequirements2KHR") + GetBufferOpaqueCaptureAddress = auto_cast GetDeviceProcAddr(device, "vkGetBufferOpaqueCaptureAddress") + GetBufferOpaqueCaptureAddressKHR = auto_cast GetDeviceProcAddr(device, "vkGetBufferOpaqueCaptureAddressKHR") + GetCalibratedTimestampsEXT = auto_cast GetDeviceProcAddr(device, "vkGetCalibratedTimestampsEXT") + GetDeferredOperationMaxConcurrencyKHR = auto_cast GetDeviceProcAddr(device, "vkGetDeferredOperationMaxConcurrencyKHR") + GetDeferredOperationResultKHR = auto_cast GetDeviceProcAddr(device, "vkGetDeferredOperationResultKHR") + GetDescriptorSetHostMappingVALVE = auto_cast GetDeviceProcAddr(device, "vkGetDescriptorSetHostMappingVALVE") + GetDescriptorSetLayoutHostMappingInfoVALVE = auto_cast GetDeviceProcAddr(device, "vkGetDescriptorSetLayoutHostMappingInfoVALVE") + GetDescriptorSetLayoutSupport = auto_cast GetDeviceProcAddr(device, "vkGetDescriptorSetLayoutSupport") + GetDescriptorSetLayoutSupportKHR = auto_cast GetDeviceProcAddr(device, "vkGetDescriptorSetLayoutSupportKHR") + GetDeviceAccelerationStructureCompatibilityKHR = auto_cast GetDeviceProcAddr(device, "vkGetDeviceAccelerationStructureCompatibilityKHR") + GetDeviceBufferMemoryRequirements = auto_cast GetDeviceProcAddr(device, "vkGetDeviceBufferMemoryRequirements") + GetDeviceBufferMemoryRequirementsKHR = auto_cast GetDeviceProcAddr(device, "vkGetDeviceBufferMemoryRequirementsKHR") + GetDeviceGroupPeerMemoryFeatures = auto_cast GetDeviceProcAddr(device, "vkGetDeviceGroupPeerMemoryFeatures") + GetDeviceGroupPeerMemoryFeaturesKHR = auto_cast GetDeviceProcAddr(device, "vkGetDeviceGroupPeerMemoryFeaturesKHR") + GetDeviceGroupPresentCapabilitiesKHR = auto_cast GetDeviceProcAddr(device, "vkGetDeviceGroupPresentCapabilitiesKHR") + GetDeviceGroupSurfacePresentModes2EXT = auto_cast GetDeviceProcAddr(device, "vkGetDeviceGroupSurfacePresentModes2EXT") + GetDeviceGroupSurfacePresentModesKHR = auto_cast GetDeviceProcAddr(device, "vkGetDeviceGroupSurfacePresentModesKHR") + GetDeviceImageMemoryRequirements = auto_cast GetDeviceProcAddr(device, "vkGetDeviceImageMemoryRequirements") + GetDeviceImageMemoryRequirementsKHR = auto_cast GetDeviceProcAddr(device, "vkGetDeviceImageMemoryRequirementsKHR") + GetDeviceImageSparseMemoryRequirements = auto_cast GetDeviceProcAddr(device, "vkGetDeviceImageSparseMemoryRequirements") + GetDeviceImageSparseMemoryRequirementsKHR = auto_cast GetDeviceProcAddr(device, "vkGetDeviceImageSparseMemoryRequirementsKHR") + GetDeviceMemoryCommitment = auto_cast GetDeviceProcAddr(device, "vkGetDeviceMemoryCommitment") + GetDeviceMemoryOpaqueCaptureAddress = auto_cast GetDeviceProcAddr(device, "vkGetDeviceMemoryOpaqueCaptureAddress") + GetDeviceMemoryOpaqueCaptureAddressKHR = auto_cast GetDeviceProcAddr(device, "vkGetDeviceMemoryOpaqueCaptureAddressKHR") + GetDeviceProcAddr = auto_cast GetDeviceProcAddr(device, "vkGetDeviceProcAddr") + GetDeviceQueue = auto_cast GetDeviceProcAddr(device, "vkGetDeviceQueue") + GetDeviceQueue2 = auto_cast GetDeviceProcAddr(device, "vkGetDeviceQueue2") + GetDeviceSubpassShadingMaxWorkgroupSizeHUAWEI = auto_cast GetDeviceProcAddr(device, "vkGetDeviceSubpassShadingMaxWorkgroupSizeHUAWEI") + GetEventStatus = auto_cast GetDeviceProcAddr(device, "vkGetEventStatus") + GetFenceFdKHR = auto_cast GetDeviceProcAddr(device, "vkGetFenceFdKHR") + GetFenceStatus = auto_cast GetDeviceProcAddr(device, "vkGetFenceStatus") + GetFenceWin32HandleKHR = auto_cast GetDeviceProcAddr(device, "vkGetFenceWin32HandleKHR") + GetGeneratedCommandsMemoryRequirementsNV = auto_cast GetDeviceProcAddr(device, "vkGetGeneratedCommandsMemoryRequirementsNV") + GetImageDrmFormatModifierPropertiesEXT = auto_cast GetDeviceProcAddr(device, "vkGetImageDrmFormatModifierPropertiesEXT") + GetImageMemoryRequirements = auto_cast GetDeviceProcAddr(device, "vkGetImageMemoryRequirements") + GetImageMemoryRequirements2 = auto_cast GetDeviceProcAddr(device, "vkGetImageMemoryRequirements2") + GetImageMemoryRequirements2KHR = auto_cast GetDeviceProcAddr(device, "vkGetImageMemoryRequirements2KHR") + GetImageSparseMemoryRequirements = auto_cast GetDeviceProcAddr(device, "vkGetImageSparseMemoryRequirements") + GetImageSparseMemoryRequirements2 = auto_cast GetDeviceProcAddr(device, "vkGetImageSparseMemoryRequirements2") + GetImageSparseMemoryRequirements2KHR = auto_cast GetDeviceProcAddr(device, "vkGetImageSparseMemoryRequirements2KHR") + GetImageSubresourceLayout = auto_cast GetDeviceProcAddr(device, "vkGetImageSubresourceLayout") + GetImageViewAddressNVX = auto_cast GetDeviceProcAddr(device, "vkGetImageViewAddressNVX") + GetImageViewHandleNVX = auto_cast GetDeviceProcAddr(device, "vkGetImageViewHandleNVX") + GetMemoryFdKHR = auto_cast GetDeviceProcAddr(device, "vkGetMemoryFdKHR") + GetMemoryFdPropertiesKHR = auto_cast GetDeviceProcAddr(device, "vkGetMemoryFdPropertiesKHR") + GetMemoryHostPointerPropertiesEXT = auto_cast GetDeviceProcAddr(device, "vkGetMemoryHostPointerPropertiesEXT") + GetMemoryRemoteAddressNV = auto_cast GetDeviceProcAddr(device, "vkGetMemoryRemoteAddressNV") + GetMemoryWin32HandleKHR = auto_cast GetDeviceProcAddr(device, "vkGetMemoryWin32HandleKHR") + GetMemoryWin32HandleNV = auto_cast GetDeviceProcAddr(device, "vkGetMemoryWin32HandleNV") + GetMemoryWin32HandlePropertiesKHR = auto_cast GetDeviceProcAddr(device, "vkGetMemoryWin32HandlePropertiesKHR") + GetPastPresentationTimingGOOGLE = auto_cast GetDeviceProcAddr(device, "vkGetPastPresentationTimingGOOGLE") + GetPerformanceParameterINTEL = auto_cast GetDeviceProcAddr(device, "vkGetPerformanceParameterINTEL") + GetPipelineCacheData = auto_cast GetDeviceProcAddr(device, "vkGetPipelineCacheData") + GetPipelineExecutableInternalRepresentationsKHR = auto_cast GetDeviceProcAddr(device, "vkGetPipelineExecutableInternalRepresentationsKHR") + GetPipelineExecutablePropertiesKHR = auto_cast GetDeviceProcAddr(device, "vkGetPipelineExecutablePropertiesKHR") + GetPipelineExecutableStatisticsKHR = auto_cast GetDeviceProcAddr(device, "vkGetPipelineExecutableStatisticsKHR") + GetPrivateData = auto_cast GetDeviceProcAddr(device, "vkGetPrivateData") + GetPrivateDataEXT = auto_cast GetDeviceProcAddr(device, "vkGetPrivateDataEXT") + GetQueryPoolResults = auto_cast GetDeviceProcAddr(device, "vkGetQueryPoolResults") + GetQueueCheckpointData2NV = auto_cast GetDeviceProcAddr(device, "vkGetQueueCheckpointData2NV") + GetQueueCheckpointDataNV = auto_cast GetDeviceProcAddr(device, "vkGetQueueCheckpointDataNV") + GetRayTracingCaptureReplayShaderGroupHandlesKHR = auto_cast GetDeviceProcAddr(device, "vkGetRayTracingCaptureReplayShaderGroupHandlesKHR") + GetRayTracingShaderGroupHandlesKHR = auto_cast GetDeviceProcAddr(device, "vkGetRayTracingShaderGroupHandlesKHR") + GetRayTracingShaderGroupHandlesNV = auto_cast GetDeviceProcAddr(device, "vkGetRayTracingShaderGroupHandlesNV") + GetRayTracingShaderGroupStackSizeKHR = auto_cast GetDeviceProcAddr(device, "vkGetRayTracingShaderGroupStackSizeKHR") + GetRefreshCycleDurationGOOGLE = auto_cast GetDeviceProcAddr(device, "vkGetRefreshCycleDurationGOOGLE") + GetRenderAreaGranularity = auto_cast GetDeviceProcAddr(device, "vkGetRenderAreaGranularity") + GetSemaphoreCounterValue = auto_cast GetDeviceProcAddr(device, "vkGetSemaphoreCounterValue") + GetSemaphoreCounterValueKHR = auto_cast GetDeviceProcAddr(device, "vkGetSemaphoreCounterValueKHR") + GetSemaphoreFdKHR = auto_cast GetDeviceProcAddr(device, "vkGetSemaphoreFdKHR") + GetSemaphoreWin32HandleKHR = auto_cast GetDeviceProcAddr(device, "vkGetSemaphoreWin32HandleKHR") + GetShaderInfoAMD = auto_cast GetDeviceProcAddr(device, "vkGetShaderInfoAMD") + GetSwapchainCounterEXT = auto_cast GetDeviceProcAddr(device, "vkGetSwapchainCounterEXT") + GetSwapchainImagesKHR = auto_cast GetDeviceProcAddr(device, "vkGetSwapchainImagesKHR") + GetSwapchainStatusKHR = auto_cast GetDeviceProcAddr(device, "vkGetSwapchainStatusKHR") + GetValidationCacheDataEXT = auto_cast GetDeviceProcAddr(device, "vkGetValidationCacheDataEXT") + ImportFenceFdKHR = auto_cast GetDeviceProcAddr(device, "vkImportFenceFdKHR") + ImportFenceWin32HandleKHR = auto_cast GetDeviceProcAddr(device, "vkImportFenceWin32HandleKHR") + ImportSemaphoreFdKHR = auto_cast GetDeviceProcAddr(device, "vkImportSemaphoreFdKHR") + ImportSemaphoreWin32HandleKHR = auto_cast GetDeviceProcAddr(device, "vkImportSemaphoreWin32HandleKHR") + InitializePerformanceApiINTEL = auto_cast GetDeviceProcAddr(device, "vkInitializePerformanceApiINTEL") + InvalidateMappedMemoryRanges = auto_cast GetDeviceProcAddr(device, "vkInvalidateMappedMemoryRanges") + MapMemory = auto_cast GetDeviceProcAddr(device, "vkMapMemory") + MergePipelineCaches = auto_cast GetDeviceProcAddr(device, "vkMergePipelineCaches") + MergeValidationCachesEXT = auto_cast GetDeviceProcAddr(device, "vkMergeValidationCachesEXT") + QueueBeginDebugUtilsLabelEXT = auto_cast GetDeviceProcAddr(device, "vkQueueBeginDebugUtilsLabelEXT") + QueueBindSparse = auto_cast GetDeviceProcAddr(device, "vkQueueBindSparse") + QueueEndDebugUtilsLabelEXT = auto_cast GetDeviceProcAddr(device, "vkQueueEndDebugUtilsLabelEXT") + QueueInsertDebugUtilsLabelEXT = auto_cast GetDeviceProcAddr(device, "vkQueueInsertDebugUtilsLabelEXT") + QueuePresentKHR = auto_cast GetDeviceProcAddr(device, "vkQueuePresentKHR") + QueueSetPerformanceConfigurationINTEL = auto_cast GetDeviceProcAddr(device, "vkQueueSetPerformanceConfigurationINTEL") + QueueSubmit = auto_cast GetDeviceProcAddr(device, "vkQueueSubmit") + QueueSubmit2 = auto_cast GetDeviceProcAddr(device, "vkQueueSubmit2") + QueueSubmit2KHR = auto_cast GetDeviceProcAddr(device, "vkQueueSubmit2KHR") + QueueWaitIdle = auto_cast GetDeviceProcAddr(device, "vkQueueWaitIdle") + RegisterDeviceEventEXT = auto_cast GetDeviceProcAddr(device, "vkRegisterDeviceEventEXT") + RegisterDisplayEventEXT = auto_cast GetDeviceProcAddr(device, "vkRegisterDisplayEventEXT") + ReleaseFullScreenExclusiveModeEXT = auto_cast GetDeviceProcAddr(device, "vkReleaseFullScreenExclusiveModeEXT") + ReleasePerformanceConfigurationINTEL = auto_cast GetDeviceProcAddr(device, "vkReleasePerformanceConfigurationINTEL") + ReleaseProfilingLockKHR = auto_cast GetDeviceProcAddr(device, "vkReleaseProfilingLockKHR") + ResetCommandBuffer = auto_cast GetDeviceProcAddr(device, "vkResetCommandBuffer") + ResetCommandPool = auto_cast GetDeviceProcAddr(device, "vkResetCommandPool") + ResetDescriptorPool = auto_cast GetDeviceProcAddr(device, "vkResetDescriptorPool") + ResetEvent = auto_cast GetDeviceProcAddr(device, "vkResetEvent") + ResetFences = auto_cast GetDeviceProcAddr(device, "vkResetFences") + ResetQueryPool = auto_cast GetDeviceProcAddr(device, "vkResetQueryPool") + ResetQueryPoolEXT = auto_cast GetDeviceProcAddr(device, "vkResetQueryPoolEXT") + SetDebugUtilsObjectNameEXT = auto_cast GetDeviceProcAddr(device, "vkSetDebugUtilsObjectNameEXT") + SetDebugUtilsObjectTagEXT = auto_cast GetDeviceProcAddr(device, "vkSetDebugUtilsObjectTagEXT") + SetDeviceMemoryPriorityEXT = auto_cast GetDeviceProcAddr(device, "vkSetDeviceMemoryPriorityEXT") + SetEvent = auto_cast GetDeviceProcAddr(device, "vkSetEvent") + SetHdrMetadataEXT = auto_cast GetDeviceProcAddr(device, "vkSetHdrMetadataEXT") + SetLocalDimmingAMD = auto_cast GetDeviceProcAddr(device, "vkSetLocalDimmingAMD") + SetPrivateData = auto_cast GetDeviceProcAddr(device, "vkSetPrivateData") + SetPrivateDataEXT = auto_cast GetDeviceProcAddr(device, "vkSetPrivateDataEXT") + SignalSemaphore = auto_cast GetDeviceProcAddr(device, "vkSignalSemaphore") + SignalSemaphoreKHR = auto_cast GetDeviceProcAddr(device, "vkSignalSemaphoreKHR") + TrimCommandPool = auto_cast GetDeviceProcAddr(device, "vkTrimCommandPool") + TrimCommandPoolKHR = auto_cast GetDeviceProcAddr(device, "vkTrimCommandPoolKHR") + UninitializePerformanceApiINTEL = auto_cast GetDeviceProcAddr(device, "vkUninitializePerformanceApiINTEL") + UnmapMemory = auto_cast GetDeviceProcAddr(device, "vkUnmapMemory") + UpdateDescriptorSetWithTemplate = auto_cast GetDeviceProcAddr(device, "vkUpdateDescriptorSetWithTemplate") + UpdateDescriptorSetWithTemplateKHR = auto_cast GetDeviceProcAddr(device, "vkUpdateDescriptorSetWithTemplateKHR") + UpdateDescriptorSets = auto_cast GetDeviceProcAddr(device, "vkUpdateDescriptorSets") + WaitForFences = auto_cast GetDeviceProcAddr(device, "vkWaitForFences") + WaitForPresentKHR = auto_cast GetDeviceProcAddr(device, "vkWaitForPresentKHR") + WaitSemaphores = auto_cast GetDeviceProcAddr(device, "vkWaitSemaphores") + WaitSemaphoresKHR = auto_cast GetDeviceProcAddr(device, "vkWaitSemaphoresKHR") + WriteAccelerationStructuresPropertiesKHR = auto_cast GetDeviceProcAddr(device, "vkWriteAccelerationStructuresPropertiesKHR") +} + +load_proc_addresses_instance :: proc(instance: Instance) { + AcquireDrmDisplayEXT = auto_cast GetInstanceProcAddr(instance, "vkAcquireDrmDisplayEXT") + AcquireWinrtDisplayNV = auto_cast GetInstanceProcAddr(instance, "vkAcquireWinrtDisplayNV") + CreateDebugReportCallbackEXT = auto_cast GetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT") + CreateDebugUtilsMessengerEXT = auto_cast GetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT") + CreateDevice = auto_cast GetInstanceProcAddr(instance, "vkCreateDevice") + CreateDisplayModeKHR = auto_cast GetInstanceProcAddr(instance, "vkCreateDisplayModeKHR") + CreateDisplayPlaneSurfaceKHR = auto_cast GetInstanceProcAddr(instance, "vkCreateDisplayPlaneSurfaceKHR") + CreateHeadlessSurfaceEXT = auto_cast GetInstanceProcAddr(instance, "vkCreateHeadlessSurfaceEXT") + CreateIOSSurfaceMVK = auto_cast GetInstanceProcAddr(instance, "vkCreateIOSSurfaceMVK") + CreateMacOSSurfaceMVK = auto_cast GetInstanceProcAddr(instance, "vkCreateMacOSSurfaceMVK") + CreateMetalSurfaceEXT = auto_cast GetInstanceProcAddr(instance, "vkCreateMetalSurfaceEXT") + CreateWin32SurfaceKHR = auto_cast GetInstanceProcAddr(instance, "vkCreateWin32SurfaceKHR") + DebugReportMessageEXT = auto_cast GetInstanceProcAddr(instance, "vkDebugReportMessageEXT") + DestroyDebugReportCallbackEXT = auto_cast GetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT") + DestroyDebugUtilsMessengerEXT = auto_cast GetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT") + DestroyInstance = auto_cast GetInstanceProcAddr(instance, "vkDestroyInstance") + DestroySurfaceKHR = auto_cast GetInstanceProcAddr(instance, "vkDestroySurfaceKHR") + EnumerateDeviceExtensionProperties = auto_cast GetInstanceProcAddr(instance, "vkEnumerateDeviceExtensionProperties") + EnumerateDeviceLayerProperties = auto_cast GetInstanceProcAddr(instance, "vkEnumerateDeviceLayerProperties") + EnumeratePhysicalDeviceGroups = auto_cast GetInstanceProcAddr(instance, "vkEnumeratePhysicalDeviceGroups") + EnumeratePhysicalDeviceGroupsKHR = auto_cast GetInstanceProcAddr(instance, "vkEnumeratePhysicalDeviceGroupsKHR") + EnumeratePhysicalDeviceQueueFamilyPerformanceQueryCountersKHR = auto_cast GetInstanceProcAddr(instance, "vkEnumeratePhysicalDeviceQueueFamilyPerformanceQueryCountersKHR") + EnumeratePhysicalDevices = auto_cast GetInstanceProcAddr(instance, "vkEnumeratePhysicalDevices") + GetDisplayModeProperties2KHR = auto_cast GetInstanceProcAddr(instance, "vkGetDisplayModeProperties2KHR") + GetDisplayModePropertiesKHR = auto_cast GetInstanceProcAddr(instance, "vkGetDisplayModePropertiesKHR") + GetDisplayPlaneCapabilities2KHR = auto_cast GetInstanceProcAddr(instance, "vkGetDisplayPlaneCapabilities2KHR") + GetDisplayPlaneCapabilitiesKHR = auto_cast GetInstanceProcAddr(instance, "vkGetDisplayPlaneCapabilitiesKHR") + GetDisplayPlaneSupportedDisplaysKHR = auto_cast GetInstanceProcAddr(instance, "vkGetDisplayPlaneSupportedDisplaysKHR") + GetDrmDisplayEXT = auto_cast GetInstanceProcAddr(instance, "vkGetDrmDisplayEXT") + GetInstanceProcAddr = auto_cast GetInstanceProcAddr(instance, "vkGetInstanceProcAddr") + GetPhysicalDeviceCalibrateableTimeDomainsEXT = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT") + GetPhysicalDeviceCooperativeMatrixPropertiesNV = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceCooperativeMatrixPropertiesNV") + GetPhysicalDeviceDisplayPlaneProperties2KHR = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceDisplayPlaneProperties2KHR") + GetPhysicalDeviceDisplayPlanePropertiesKHR = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceDisplayPlanePropertiesKHR") + GetPhysicalDeviceDisplayProperties2KHR = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceDisplayProperties2KHR") + GetPhysicalDeviceDisplayPropertiesKHR = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceDisplayPropertiesKHR") + GetPhysicalDeviceExternalBufferProperties = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceExternalBufferProperties") + GetPhysicalDeviceExternalBufferPropertiesKHR = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceExternalBufferPropertiesKHR") + GetPhysicalDeviceExternalFenceProperties = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceExternalFenceProperties") + GetPhysicalDeviceExternalFencePropertiesKHR = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceExternalFencePropertiesKHR") + GetPhysicalDeviceExternalImageFormatPropertiesNV = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceExternalImageFormatPropertiesNV") + GetPhysicalDeviceExternalSemaphoreProperties = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceExternalSemaphoreProperties") + GetPhysicalDeviceExternalSemaphorePropertiesKHR = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceExternalSemaphorePropertiesKHR") + GetPhysicalDeviceFeatures = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceFeatures") + GetPhysicalDeviceFeatures2 = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceFeatures2") + GetPhysicalDeviceFeatures2KHR = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceFeatures2KHR") + GetPhysicalDeviceFormatProperties = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceFormatProperties") + GetPhysicalDeviceFormatProperties2 = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceFormatProperties2") + GetPhysicalDeviceFormatProperties2KHR = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceFormatProperties2KHR") + GetPhysicalDeviceFragmentShadingRatesKHR = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceFragmentShadingRatesKHR") + GetPhysicalDeviceImageFormatProperties = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceImageFormatProperties") + GetPhysicalDeviceImageFormatProperties2 = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceImageFormatProperties2") + GetPhysicalDeviceImageFormatProperties2KHR = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceImageFormatProperties2KHR") + GetPhysicalDeviceMemoryProperties = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceMemoryProperties") + GetPhysicalDeviceMemoryProperties2 = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceMemoryProperties2") + GetPhysicalDeviceMemoryProperties2KHR = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceMemoryProperties2KHR") + GetPhysicalDeviceMultisamplePropertiesEXT = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceMultisamplePropertiesEXT") + GetPhysicalDevicePresentRectanglesKHR = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDevicePresentRectanglesKHR") + GetPhysicalDeviceProperties = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceProperties") + GetPhysicalDeviceProperties2 = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceProperties2") + GetPhysicalDeviceProperties2KHR = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceProperties2KHR") + GetPhysicalDeviceQueueFamilyPerformanceQueryPassesKHR = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceQueueFamilyPerformanceQueryPassesKHR") + GetPhysicalDeviceQueueFamilyProperties = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceQueueFamilyProperties") + GetPhysicalDeviceQueueFamilyProperties2 = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceQueueFamilyProperties2") + GetPhysicalDeviceQueueFamilyProperties2KHR = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceQueueFamilyProperties2KHR") + GetPhysicalDeviceSparseImageFormatProperties = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceSparseImageFormatProperties") + GetPhysicalDeviceSparseImageFormatProperties2 = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceSparseImageFormatProperties2") + GetPhysicalDeviceSparseImageFormatProperties2KHR = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceSparseImageFormatProperties2KHR") + GetPhysicalDeviceSupportedFramebufferMixedSamplesCombinationsNV = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceSupportedFramebufferMixedSamplesCombinationsNV") + GetPhysicalDeviceSurfaceCapabilities2EXT = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfaceCapabilities2EXT") + GetPhysicalDeviceSurfaceCapabilities2KHR = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfaceCapabilities2KHR") + GetPhysicalDeviceSurfaceCapabilitiesKHR = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR") + GetPhysicalDeviceSurfaceFormats2KHR = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfaceFormats2KHR") + GetPhysicalDeviceSurfaceFormatsKHR = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfaceFormatsKHR") + GetPhysicalDeviceSurfacePresentModes2EXT = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfacePresentModes2EXT") + GetPhysicalDeviceSurfacePresentModesKHR = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfacePresentModesKHR") + GetPhysicalDeviceSurfaceSupportKHR = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfaceSupportKHR") + GetPhysicalDeviceToolProperties = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceToolProperties") + GetPhysicalDeviceToolPropertiesEXT = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceToolPropertiesEXT") + GetPhysicalDeviceWin32PresentationSupportKHR = auto_cast GetInstanceProcAddr(instance, "vkGetPhysicalDeviceWin32PresentationSupportKHR") + GetWinrtDisplayNV = auto_cast GetInstanceProcAddr(instance, "vkGetWinrtDisplayNV") + ReleaseDisplayEXT = auto_cast GetInstanceProcAddr(instance, "vkReleaseDisplayEXT") + SubmitDebugUtilsMessageEXT = auto_cast GetInstanceProcAddr(instance, "vkSubmitDebugUtilsMessageEXT") + + // Device Procedures (may call into dispatch) + AcquireFullScreenExclusiveModeEXT = auto_cast GetInstanceProcAddr(instance, "vkAcquireFullScreenExclusiveModeEXT") + AcquireNextImage2KHR = auto_cast GetInstanceProcAddr(instance, "vkAcquireNextImage2KHR") + AcquireNextImageKHR = auto_cast GetInstanceProcAddr(instance, "vkAcquireNextImageKHR") + AcquirePerformanceConfigurationINTEL = auto_cast GetInstanceProcAddr(instance, "vkAcquirePerformanceConfigurationINTEL") + AcquireProfilingLockKHR = auto_cast GetInstanceProcAddr(instance, "vkAcquireProfilingLockKHR") + AllocateCommandBuffers = auto_cast GetInstanceProcAddr(instance, "vkAllocateCommandBuffers") + AllocateDescriptorSets = auto_cast GetInstanceProcAddr(instance, "vkAllocateDescriptorSets") + AllocateMemory = auto_cast GetInstanceProcAddr(instance, "vkAllocateMemory") + BeginCommandBuffer = auto_cast GetInstanceProcAddr(instance, "vkBeginCommandBuffer") + BindAccelerationStructureMemoryNV = auto_cast GetInstanceProcAddr(instance, "vkBindAccelerationStructureMemoryNV") + BindBufferMemory = auto_cast GetInstanceProcAddr(instance, "vkBindBufferMemory") + BindBufferMemory2 = auto_cast GetInstanceProcAddr(instance, "vkBindBufferMemory2") + BindBufferMemory2KHR = auto_cast GetInstanceProcAddr(instance, "vkBindBufferMemory2KHR") + BindImageMemory = auto_cast GetInstanceProcAddr(instance, "vkBindImageMemory") + BindImageMemory2 = auto_cast GetInstanceProcAddr(instance, "vkBindImageMemory2") + BindImageMemory2KHR = auto_cast GetInstanceProcAddr(instance, "vkBindImageMemory2KHR") + BuildAccelerationStructuresKHR = auto_cast GetInstanceProcAddr(instance, "vkBuildAccelerationStructuresKHR") + CmdBeginConditionalRenderingEXT = auto_cast GetInstanceProcAddr(instance, "vkCmdBeginConditionalRenderingEXT") + CmdBeginDebugUtilsLabelEXT = auto_cast GetInstanceProcAddr(instance, "vkCmdBeginDebugUtilsLabelEXT") + CmdBeginQuery = auto_cast GetInstanceProcAddr(instance, "vkCmdBeginQuery") + CmdBeginQueryIndexedEXT = auto_cast GetInstanceProcAddr(instance, "vkCmdBeginQueryIndexedEXT") + CmdBeginRenderPass = auto_cast GetInstanceProcAddr(instance, "vkCmdBeginRenderPass") + CmdBeginRenderPass2 = auto_cast GetInstanceProcAddr(instance, "vkCmdBeginRenderPass2") + CmdBeginRenderPass2KHR = auto_cast GetInstanceProcAddr(instance, "vkCmdBeginRenderPass2KHR") + CmdBeginRendering = auto_cast GetInstanceProcAddr(instance, "vkCmdBeginRendering") + CmdBeginRenderingKHR = auto_cast GetInstanceProcAddr(instance, "vkCmdBeginRenderingKHR") + CmdBeginTransformFeedbackEXT = auto_cast GetInstanceProcAddr(instance, "vkCmdBeginTransformFeedbackEXT") + CmdBindDescriptorSets = auto_cast GetInstanceProcAddr(instance, "vkCmdBindDescriptorSets") + CmdBindIndexBuffer = auto_cast GetInstanceProcAddr(instance, "vkCmdBindIndexBuffer") + CmdBindInvocationMaskHUAWEI = auto_cast GetInstanceProcAddr(instance, "vkCmdBindInvocationMaskHUAWEI") + CmdBindPipeline = auto_cast GetInstanceProcAddr(instance, "vkCmdBindPipeline") + CmdBindPipelineShaderGroupNV = auto_cast GetInstanceProcAddr(instance, "vkCmdBindPipelineShaderGroupNV") + CmdBindShadingRateImageNV = auto_cast GetInstanceProcAddr(instance, "vkCmdBindShadingRateImageNV") + CmdBindTransformFeedbackBuffersEXT = auto_cast GetInstanceProcAddr(instance, "vkCmdBindTransformFeedbackBuffersEXT") + CmdBindVertexBuffers = auto_cast GetInstanceProcAddr(instance, "vkCmdBindVertexBuffers") + CmdBindVertexBuffers2 = auto_cast GetInstanceProcAddr(instance, "vkCmdBindVertexBuffers2") + CmdBindVertexBuffers2EXT = auto_cast GetInstanceProcAddr(instance, "vkCmdBindVertexBuffers2EXT") + CmdBlitImage = auto_cast GetInstanceProcAddr(instance, "vkCmdBlitImage") + CmdBlitImage2 = auto_cast GetInstanceProcAddr(instance, "vkCmdBlitImage2") + CmdBlitImage2KHR = auto_cast GetInstanceProcAddr(instance, "vkCmdBlitImage2KHR") + CmdBuildAccelerationStructureNV = auto_cast GetInstanceProcAddr(instance, "vkCmdBuildAccelerationStructureNV") + CmdBuildAccelerationStructuresIndirectKHR = auto_cast GetInstanceProcAddr(instance, "vkCmdBuildAccelerationStructuresIndirectKHR") + CmdBuildAccelerationStructuresKHR = auto_cast GetInstanceProcAddr(instance, "vkCmdBuildAccelerationStructuresKHR") + CmdClearAttachments = auto_cast GetInstanceProcAddr(instance, "vkCmdClearAttachments") + CmdClearColorImage = auto_cast GetInstanceProcAddr(instance, "vkCmdClearColorImage") + CmdClearDepthStencilImage = auto_cast GetInstanceProcAddr(instance, "vkCmdClearDepthStencilImage") + CmdCopyAccelerationStructureKHR = auto_cast GetInstanceProcAddr(instance, "vkCmdCopyAccelerationStructureKHR") + CmdCopyAccelerationStructureNV = auto_cast GetInstanceProcAddr(instance, "vkCmdCopyAccelerationStructureNV") + CmdCopyAccelerationStructureToMemoryKHR = auto_cast GetInstanceProcAddr(instance, "vkCmdCopyAccelerationStructureToMemoryKHR") + CmdCopyBuffer = auto_cast GetInstanceProcAddr(instance, "vkCmdCopyBuffer") + CmdCopyBuffer2 = auto_cast GetInstanceProcAddr(instance, "vkCmdCopyBuffer2") + CmdCopyBuffer2KHR = auto_cast GetInstanceProcAddr(instance, "vkCmdCopyBuffer2KHR") + CmdCopyBufferToImage = auto_cast GetInstanceProcAddr(instance, "vkCmdCopyBufferToImage") + CmdCopyBufferToImage2 = auto_cast GetInstanceProcAddr(instance, "vkCmdCopyBufferToImage2") + CmdCopyBufferToImage2KHR = auto_cast GetInstanceProcAddr(instance, "vkCmdCopyBufferToImage2KHR") + CmdCopyImage = auto_cast GetInstanceProcAddr(instance, "vkCmdCopyImage") + CmdCopyImage2 = auto_cast GetInstanceProcAddr(instance, "vkCmdCopyImage2") + CmdCopyImage2KHR = auto_cast GetInstanceProcAddr(instance, "vkCmdCopyImage2KHR") + CmdCopyImageToBuffer = auto_cast GetInstanceProcAddr(instance, "vkCmdCopyImageToBuffer") + CmdCopyImageToBuffer2 = auto_cast GetInstanceProcAddr(instance, "vkCmdCopyImageToBuffer2") + CmdCopyImageToBuffer2KHR = auto_cast GetInstanceProcAddr(instance, "vkCmdCopyImageToBuffer2KHR") + CmdCopyMemoryToAccelerationStructureKHR = auto_cast GetInstanceProcAddr(instance, "vkCmdCopyMemoryToAccelerationStructureKHR") + CmdCopyQueryPoolResults = auto_cast GetInstanceProcAddr(instance, "vkCmdCopyQueryPoolResults") + CmdCuLaunchKernelNVX = auto_cast GetInstanceProcAddr(instance, "vkCmdCuLaunchKernelNVX") + CmdDebugMarkerBeginEXT = auto_cast GetInstanceProcAddr(instance, "vkCmdDebugMarkerBeginEXT") + CmdDebugMarkerEndEXT = auto_cast GetInstanceProcAddr(instance, "vkCmdDebugMarkerEndEXT") + CmdDebugMarkerInsertEXT = auto_cast GetInstanceProcAddr(instance, "vkCmdDebugMarkerInsertEXT") + CmdDispatch = auto_cast GetInstanceProcAddr(instance, "vkCmdDispatch") + CmdDispatchBase = auto_cast GetInstanceProcAddr(instance, "vkCmdDispatchBase") + CmdDispatchBaseKHR = auto_cast GetInstanceProcAddr(instance, "vkCmdDispatchBaseKHR") + CmdDispatchIndirect = auto_cast GetInstanceProcAddr(instance, "vkCmdDispatchIndirect") + CmdDraw = auto_cast GetInstanceProcAddr(instance, "vkCmdDraw") + CmdDrawIndexed = auto_cast GetInstanceProcAddr(instance, "vkCmdDrawIndexed") + CmdDrawIndexedIndirect = auto_cast GetInstanceProcAddr(instance, "vkCmdDrawIndexedIndirect") + CmdDrawIndexedIndirectCount = auto_cast GetInstanceProcAddr(instance, "vkCmdDrawIndexedIndirectCount") + CmdDrawIndexedIndirectCountAMD = auto_cast GetInstanceProcAddr(instance, "vkCmdDrawIndexedIndirectCountAMD") + CmdDrawIndexedIndirectCountKHR = auto_cast GetInstanceProcAddr(instance, "vkCmdDrawIndexedIndirectCountKHR") + CmdDrawIndirect = auto_cast GetInstanceProcAddr(instance, "vkCmdDrawIndirect") + CmdDrawIndirectByteCountEXT = auto_cast GetInstanceProcAddr(instance, "vkCmdDrawIndirectByteCountEXT") + CmdDrawIndirectCount = auto_cast GetInstanceProcAddr(instance, "vkCmdDrawIndirectCount") + CmdDrawIndirectCountAMD = auto_cast GetInstanceProcAddr(instance, "vkCmdDrawIndirectCountAMD") + CmdDrawIndirectCountKHR = auto_cast GetInstanceProcAddr(instance, "vkCmdDrawIndirectCountKHR") + CmdDrawMeshTasksIndirectCountNV = auto_cast GetInstanceProcAddr(instance, "vkCmdDrawMeshTasksIndirectCountNV") + CmdDrawMeshTasksIndirectNV = auto_cast GetInstanceProcAddr(instance, "vkCmdDrawMeshTasksIndirectNV") + CmdDrawMeshTasksNV = auto_cast GetInstanceProcAddr(instance, "vkCmdDrawMeshTasksNV") + CmdDrawMultiEXT = auto_cast GetInstanceProcAddr(instance, "vkCmdDrawMultiEXT") + CmdDrawMultiIndexedEXT = auto_cast GetInstanceProcAddr(instance, "vkCmdDrawMultiIndexedEXT") + CmdEndConditionalRenderingEXT = auto_cast GetInstanceProcAddr(instance, "vkCmdEndConditionalRenderingEXT") + CmdEndDebugUtilsLabelEXT = auto_cast GetInstanceProcAddr(instance, "vkCmdEndDebugUtilsLabelEXT") + CmdEndQuery = auto_cast GetInstanceProcAddr(instance, "vkCmdEndQuery") + CmdEndQueryIndexedEXT = auto_cast GetInstanceProcAddr(instance, "vkCmdEndQueryIndexedEXT") + CmdEndRenderPass = auto_cast GetInstanceProcAddr(instance, "vkCmdEndRenderPass") + CmdEndRenderPass2 = auto_cast GetInstanceProcAddr(instance, "vkCmdEndRenderPass2") + CmdEndRenderPass2KHR = auto_cast GetInstanceProcAddr(instance, "vkCmdEndRenderPass2KHR") + CmdEndRendering = auto_cast GetInstanceProcAddr(instance, "vkCmdEndRendering") + CmdEndRenderingKHR = auto_cast GetInstanceProcAddr(instance, "vkCmdEndRenderingKHR") + CmdEndTransformFeedbackEXT = auto_cast GetInstanceProcAddr(instance, "vkCmdEndTransformFeedbackEXT") + CmdExecuteCommands = auto_cast GetInstanceProcAddr(instance, "vkCmdExecuteCommands") + CmdExecuteGeneratedCommandsNV = auto_cast GetInstanceProcAddr(instance, "vkCmdExecuteGeneratedCommandsNV") + CmdFillBuffer = auto_cast GetInstanceProcAddr(instance, "vkCmdFillBuffer") + CmdInsertDebugUtilsLabelEXT = auto_cast GetInstanceProcAddr(instance, "vkCmdInsertDebugUtilsLabelEXT") + CmdNextSubpass = auto_cast GetInstanceProcAddr(instance, "vkCmdNextSubpass") + CmdNextSubpass2 = auto_cast GetInstanceProcAddr(instance, "vkCmdNextSubpass2") + CmdNextSubpass2KHR = auto_cast GetInstanceProcAddr(instance, "vkCmdNextSubpass2KHR") + CmdPipelineBarrier = auto_cast GetInstanceProcAddr(instance, "vkCmdPipelineBarrier") + CmdPipelineBarrier2 = auto_cast GetInstanceProcAddr(instance, "vkCmdPipelineBarrier2") + CmdPipelineBarrier2KHR = auto_cast GetInstanceProcAddr(instance, "vkCmdPipelineBarrier2KHR") + CmdPreprocessGeneratedCommandsNV = auto_cast GetInstanceProcAddr(instance, "vkCmdPreprocessGeneratedCommandsNV") + CmdPushConstants = auto_cast GetInstanceProcAddr(instance, "vkCmdPushConstants") + CmdPushDescriptorSetKHR = auto_cast GetInstanceProcAddr(instance, "vkCmdPushDescriptorSetKHR") + CmdPushDescriptorSetWithTemplateKHR = auto_cast GetInstanceProcAddr(instance, "vkCmdPushDescriptorSetWithTemplateKHR") + CmdResetEvent = auto_cast GetInstanceProcAddr(instance, "vkCmdResetEvent") + CmdResetEvent2 = auto_cast GetInstanceProcAddr(instance, "vkCmdResetEvent2") + CmdResetEvent2KHR = auto_cast GetInstanceProcAddr(instance, "vkCmdResetEvent2KHR") + CmdResetQueryPool = auto_cast GetInstanceProcAddr(instance, "vkCmdResetQueryPool") + CmdResolveImage = auto_cast GetInstanceProcAddr(instance, "vkCmdResolveImage") + CmdResolveImage2 = auto_cast GetInstanceProcAddr(instance, "vkCmdResolveImage2") + CmdResolveImage2KHR = auto_cast GetInstanceProcAddr(instance, "vkCmdResolveImage2KHR") + CmdSetBlendConstants = auto_cast GetInstanceProcAddr(instance, "vkCmdSetBlendConstants") + CmdSetCheckpointNV = auto_cast GetInstanceProcAddr(instance, "vkCmdSetCheckpointNV") + CmdSetCoarseSampleOrderNV = auto_cast GetInstanceProcAddr(instance, "vkCmdSetCoarseSampleOrderNV") + CmdSetCullMode = auto_cast GetInstanceProcAddr(instance, "vkCmdSetCullMode") + CmdSetCullModeEXT = auto_cast GetInstanceProcAddr(instance, "vkCmdSetCullModeEXT") + CmdSetDepthBias = auto_cast GetInstanceProcAddr(instance, "vkCmdSetDepthBias") + CmdSetDepthBiasEnable = auto_cast GetInstanceProcAddr(instance, "vkCmdSetDepthBiasEnable") + CmdSetDepthBiasEnableEXT = auto_cast GetInstanceProcAddr(instance, "vkCmdSetDepthBiasEnableEXT") + CmdSetDepthBounds = auto_cast GetInstanceProcAddr(instance, "vkCmdSetDepthBounds") + CmdSetDepthBoundsTestEnable = auto_cast GetInstanceProcAddr(instance, "vkCmdSetDepthBoundsTestEnable") + CmdSetDepthBoundsTestEnableEXT = auto_cast GetInstanceProcAddr(instance, "vkCmdSetDepthBoundsTestEnableEXT") + CmdSetDepthCompareOp = auto_cast GetInstanceProcAddr(instance, "vkCmdSetDepthCompareOp") + CmdSetDepthCompareOpEXT = auto_cast GetInstanceProcAddr(instance, "vkCmdSetDepthCompareOpEXT") + CmdSetDepthTestEnable = auto_cast GetInstanceProcAddr(instance, "vkCmdSetDepthTestEnable") + CmdSetDepthTestEnableEXT = auto_cast GetInstanceProcAddr(instance, "vkCmdSetDepthTestEnableEXT") + CmdSetDepthWriteEnable = auto_cast GetInstanceProcAddr(instance, "vkCmdSetDepthWriteEnable") + CmdSetDepthWriteEnableEXT = auto_cast GetInstanceProcAddr(instance, "vkCmdSetDepthWriteEnableEXT") + CmdSetDeviceMask = auto_cast GetInstanceProcAddr(instance, "vkCmdSetDeviceMask") + CmdSetDeviceMaskKHR = auto_cast GetInstanceProcAddr(instance, "vkCmdSetDeviceMaskKHR") + CmdSetDiscardRectangleEXT = auto_cast GetInstanceProcAddr(instance, "vkCmdSetDiscardRectangleEXT") + CmdSetEvent = auto_cast GetInstanceProcAddr(instance, "vkCmdSetEvent") + CmdSetEvent2 = auto_cast GetInstanceProcAddr(instance, "vkCmdSetEvent2") + CmdSetEvent2KHR = auto_cast GetInstanceProcAddr(instance, "vkCmdSetEvent2KHR") + CmdSetExclusiveScissorNV = auto_cast GetInstanceProcAddr(instance, "vkCmdSetExclusiveScissorNV") + CmdSetFragmentShadingRateEnumNV = auto_cast GetInstanceProcAddr(instance, "vkCmdSetFragmentShadingRateEnumNV") + CmdSetFragmentShadingRateKHR = auto_cast GetInstanceProcAddr(instance, "vkCmdSetFragmentShadingRateKHR") + CmdSetFrontFace = auto_cast GetInstanceProcAddr(instance, "vkCmdSetFrontFace") + CmdSetFrontFaceEXT = auto_cast GetInstanceProcAddr(instance, "vkCmdSetFrontFaceEXT") + CmdSetLineStippleEXT = auto_cast GetInstanceProcAddr(instance, "vkCmdSetLineStippleEXT") + CmdSetLineWidth = auto_cast GetInstanceProcAddr(instance, "vkCmdSetLineWidth") + CmdSetLogicOpEXT = auto_cast GetInstanceProcAddr(instance, "vkCmdSetLogicOpEXT") + CmdSetPatchControlPointsEXT = auto_cast GetInstanceProcAddr(instance, "vkCmdSetPatchControlPointsEXT") + CmdSetPerformanceMarkerINTEL = auto_cast GetInstanceProcAddr(instance, "vkCmdSetPerformanceMarkerINTEL") + CmdSetPerformanceOverrideINTEL = auto_cast GetInstanceProcAddr(instance, "vkCmdSetPerformanceOverrideINTEL") + CmdSetPerformanceStreamMarkerINTEL = auto_cast GetInstanceProcAddr(instance, "vkCmdSetPerformanceStreamMarkerINTEL") + CmdSetPrimitiveRestartEnable = auto_cast GetInstanceProcAddr(instance, "vkCmdSetPrimitiveRestartEnable") + CmdSetPrimitiveRestartEnableEXT = auto_cast GetInstanceProcAddr(instance, "vkCmdSetPrimitiveRestartEnableEXT") + CmdSetPrimitiveTopology = auto_cast GetInstanceProcAddr(instance, "vkCmdSetPrimitiveTopology") + CmdSetPrimitiveTopologyEXT = auto_cast GetInstanceProcAddr(instance, "vkCmdSetPrimitiveTopologyEXT") + CmdSetRasterizerDiscardEnable = auto_cast GetInstanceProcAddr(instance, "vkCmdSetRasterizerDiscardEnable") + CmdSetRasterizerDiscardEnableEXT = auto_cast GetInstanceProcAddr(instance, "vkCmdSetRasterizerDiscardEnableEXT") + CmdSetRayTracingPipelineStackSizeKHR = auto_cast GetInstanceProcAddr(instance, "vkCmdSetRayTracingPipelineStackSizeKHR") + CmdSetSampleLocationsEXT = auto_cast GetInstanceProcAddr(instance, "vkCmdSetSampleLocationsEXT") + CmdSetScissor = auto_cast GetInstanceProcAddr(instance, "vkCmdSetScissor") + CmdSetScissorWithCount = auto_cast GetInstanceProcAddr(instance, "vkCmdSetScissorWithCount") + CmdSetScissorWithCountEXT = auto_cast GetInstanceProcAddr(instance, "vkCmdSetScissorWithCountEXT") + CmdSetStencilCompareMask = auto_cast GetInstanceProcAddr(instance, "vkCmdSetStencilCompareMask") + CmdSetStencilOp = auto_cast GetInstanceProcAddr(instance, "vkCmdSetStencilOp") + CmdSetStencilOpEXT = auto_cast GetInstanceProcAddr(instance, "vkCmdSetStencilOpEXT") + CmdSetStencilReference = auto_cast GetInstanceProcAddr(instance, "vkCmdSetStencilReference") + CmdSetStencilTestEnable = auto_cast GetInstanceProcAddr(instance, "vkCmdSetStencilTestEnable") + CmdSetStencilTestEnableEXT = auto_cast GetInstanceProcAddr(instance, "vkCmdSetStencilTestEnableEXT") + CmdSetStencilWriteMask = auto_cast GetInstanceProcAddr(instance, "vkCmdSetStencilWriteMask") + CmdSetVertexInputEXT = auto_cast GetInstanceProcAddr(instance, "vkCmdSetVertexInputEXT") + CmdSetViewport = auto_cast GetInstanceProcAddr(instance, "vkCmdSetViewport") + CmdSetViewportShadingRatePaletteNV = auto_cast GetInstanceProcAddr(instance, "vkCmdSetViewportShadingRatePaletteNV") + CmdSetViewportWScalingNV = auto_cast GetInstanceProcAddr(instance, "vkCmdSetViewportWScalingNV") + CmdSetViewportWithCount = auto_cast GetInstanceProcAddr(instance, "vkCmdSetViewportWithCount") + CmdSetViewportWithCountEXT = auto_cast GetInstanceProcAddr(instance, "vkCmdSetViewportWithCountEXT") + CmdSubpassShadingHUAWEI = auto_cast GetInstanceProcAddr(instance, "vkCmdSubpassShadingHUAWEI") + CmdTraceRaysIndirectKHR = auto_cast GetInstanceProcAddr(instance, "vkCmdTraceRaysIndirectKHR") + CmdTraceRaysKHR = auto_cast GetInstanceProcAddr(instance, "vkCmdTraceRaysKHR") + CmdTraceRaysNV = auto_cast GetInstanceProcAddr(instance, "vkCmdTraceRaysNV") + CmdUpdateBuffer = auto_cast GetInstanceProcAddr(instance, "vkCmdUpdateBuffer") + CmdWaitEvents = auto_cast GetInstanceProcAddr(instance, "vkCmdWaitEvents") + CmdWaitEvents2 = auto_cast GetInstanceProcAddr(instance, "vkCmdWaitEvents2") + CmdWaitEvents2KHR = auto_cast GetInstanceProcAddr(instance, "vkCmdWaitEvents2KHR") + CmdWriteAccelerationStructuresPropertiesKHR = auto_cast GetInstanceProcAddr(instance, "vkCmdWriteAccelerationStructuresPropertiesKHR") + CmdWriteAccelerationStructuresPropertiesNV = auto_cast GetInstanceProcAddr(instance, "vkCmdWriteAccelerationStructuresPropertiesNV") + CmdWriteBufferMarker2AMD = auto_cast GetInstanceProcAddr(instance, "vkCmdWriteBufferMarker2AMD") + CmdWriteBufferMarkerAMD = auto_cast GetInstanceProcAddr(instance, "vkCmdWriteBufferMarkerAMD") + CmdWriteTimestamp = auto_cast GetInstanceProcAddr(instance, "vkCmdWriteTimestamp") + CmdWriteTimestamp2 = auto_cast GetInstanceProcAddr(instance, "vkCmdWriteTimestamp2") + CmdWriteTimestamp2KHR = auto_cast GetInstanceProcAddr(instance, "vkCmdWriteTimestamp2KHR") + CompileDeferredNV = auto_cast GetInstanceProcAddr(instance, "vkCompileDeferredNV") + CopyAccelerationStructureKHR = auto_cast GetInstanceProcAddr(instance, "vkCopyAccelerationStructureKHR") + CopyAccelerationStructureToMemoryKHR = auto_cast GetInstanceProcAddr(instance, "vkCopyAccelerationStructureToMemoryKHR") + CopyMemoryToAccelerationStructureKHR = auto_cast GetInstanceProcAddr(instance, "vkCopyMemoryToAccelerationStructureKHR") + CreateAccelerationStructureKHR = auto_cast GetInstanceProcAddr(instance, "vkCreateAccelerationStructureKHR") + CreateAccelerationStructureNV = auto_cast GetInstanceProcAddr(instance, "vkCreateAccelerationStructureNV") + CreateBuffer = auto_cast GetInstanceProcAddr(instance, "vkCreateBuffer") + CreateBufferView = auto_cast GetInstanceProcAddr(instance, "vkCreateBufferView") + CreateCommandPool = auto_cast GetInstanceProcAddr(instance, "vkCreateCommandPool") + CreateComputePipelines = auto_cast GetInstanceProcAddr(instance, "vkCreateComputePipelines") + CreateCuFunctionNVX = auto_cast GetInstanceProcAddr(instance, "vkCreateCuFunctionNVX") + CreateCuModuleNVX = auto_cast GetInstanceProcAddr(instance, "vkCreateCuModuleNVX") + CreateDeferredOperationKHR = auto_cast GetInstanceProcAddr(instance, "vkCreateDeferredOperationKHR") + CreateDescriptorPool = auto_cast GetInstanceProcAddr(instance, "vkCreateDescriptorPool") + CreateDescriptorSetLayout = auto_cast GetInstanceProcAddr(instance, "vkCreateDescriptorSetLayout") + CreateDescriptorUpdateTemplate = auto_cast GetInstanceProcAddr(instance, "vkCreateDescriptorUpdateTemplate") + CreateDescriptorUpdateTemplateKHR = auto_cast GetInstanceProcAddr(instance, "vkCreateDescriptorUpdateTemplateKHR") + CreateEvent = auto_cast GetInstanceProcAddr(instance, "vkCreateEvent") + CreateFence = auto_cast GetInstanceProcAddr(instance, "vkCreateFence") + CreateFramebuffer = auto_cast GetInstanceProcAddr(instance, "vkCreateFramebuffer") + CreateGraphicsPipelines = auto_cast GetInstanceProcAddr(instance, "vkCreateGraphicsPipelines") + CreateImage = auto_cast GetInstanceProcAddr(instance, "vkCreateImage") + CreateImageView = auto_cast GetInstanceProcAddr(instance, "vkCreateImageView") + CreateIndirectCommandsLayoutNV = auto_cast GetInstanceProcAddr(instance, "vkCreateIndirectCommandsLayoutNV") + CreatePipelineCache = auto_cast GetInstanceProcAddr(instance, "vkCreatePipelineCache") + CreatePipelineLayout = auto_cast GetInstanceProcAddr(instance, "vkCreatePipelineLayout") + CreatePrivateDataSlot = auto_cast GetInstanceProcAddr(instance, "vkCreatePrivateDataSlot") + CreatePrivateDataSlotEXT = auto_cast GetInstanceProcAddr(instance, "vkCreatePrivateDataSlotEXT") + CreateQueryPool = auto_cast GetInstanceProcAddr(instance, "vkCreateQueryPool") + CreateRayTracingPipelinesKHR = auto_cast GetInstanceProcAddr(instance, "vkCreateRayTracingPipelinesKHR") + CreateRayTracingPipelinesNV = auto_cast GetInstanceProcAddr(instance, "vkCreateRayTracingPipelinesNV") + CreateRenderPass = auto_cast GetInstanceProcAddr(instance, "vkCreateRenderPass") + CreateRenderPass2 = auto_cast GetInstanceProcAddr(instance, "vkCreateRenderPass2") + CreateRenderPass2KHR = auto_cast GetInstanceProcAddr(instance, "vkCreateRenderPass2KHR") + CreateSampler = auto_cast GetInstanceProcAddr(instance, "vkCreateSampler") + CreateSamplerYcbcrConversion = auto_cast GetInstanceProcAddr(instance, "vkCreateSamplerYcbcrConversion") + CreateSamplerYcbcrConversionKHR = auto_cast GetInstanceProcAddr(instance, "vkCreateSamplerYcbcrConversionKHR") + CreateSemaphore = auto_cast GetInstanceProcAddr(instance, "vkCreateSemaphore") + CreateShaderModule = auto_cast GetInstanceProcAddr(instance, "vkCreateShaderModule") + CreateSharedSwapchainsKHR = auto_cast GetInstanceProcAddr(instance, "vkCreateSharedSwapchainsKHR") + CreateSwapchainKHR = auto_cast GetInstanceProcAddr(instance, "vkCreateSwapchainKHR") + CreateValidationCacheEXT = auto_cast GetInstanceProcAddr(instance, "vkCreateValidationCacheEXT") + DebugMarkerSetObjectNameEXT = auto_cast GetInstanceProcAddr(instance, "vkDebugMarkerSetObjectNameEXT") + DebugMarkerSetObjectTagEXT = auto_cast GetInstanceProcAddr(instance, "vkDebugMarkerSetObjectTagEXT") + DeferredOperationJoinKHR = auto_cast GetInstanceProcAddr(instance, "vkDeferredOperationJoinKHR") + DestroyAccelerationStructureKHR = auto_cast GetInstanceProcAddr(instance, "vkDestroyAccelerationStructureKHR") + DestroyAccelerationStructureNV = auto_cast GetInstanceProcAddr(instance, "vkDestroyAccelerationStructureNV") + DestroyBuffer = auto_cast GetInstanceProcAddr(instance, "vkDestroyBuffer") + DestroyBufferView = auto_cast GetInstanceProcAddr(instance, "vkDestroyBufferView") + DestroyCommandPool = auto_cast GetInstanceProcAddr(instance, "vkDestroyCommandPool") + DestroyCuFunctionNVX = auto_cast GetInstanceProcAddr(instance, "vkDestroyCuFunctionNVX") + DestroyCuModuleNVX = auto_cast GetInstanceProcAddr(instance, "vkDestroyCuModuleNVX") + DestroyDeferredOperationKHR = auto_cast GetInstanceProcAddr(instance, "vkDestroyDeferredOperationKHR") + DestroyDescriptorPool = auto_cast GetInstanceProcAddr(instance, "vkDestroyDescriptorPool") + DestroyDescriptorSetLayout = auto_cast GetInstanceProcAddr(instance, "vkDestroyDescriptorSetLayout") + DestroyDescriptorUpdateTemplate = auto_cast GetInstanceProcAddr(instance, "vkDestroyDescriptorUpdateTemplate") + DestroyDescriptorUpdateTemplateKHR = auto_cast GetInstanceProcAddr(instance, "vkDestroyDescriptorUpdateTemplateKHR") + DestroyDevice = auto_cast GetInstanceProcAddr(instance, "vkDestroyDevice") + DestroyEvent = auto_cast GetInstanceProcAddr(instance, "vkDestroyEvent") + DestroyFence = auto_cast GetInstanceProcAddr(instance, "vkDestroyFence") + DestroyFramebuffer = auto_cast GetInstanceProcAddr(instance, "vkDestroyFramebuffer") + DestroyImage = auto_cast GetInstanceProcAddr(instance, "vkDestroyImage") + DestroyImageView = auto_cast GetInstanceProcAddr(instance, "vkDestroyImageView") + DestroyIndirectCommandsLayoutNV = auto_cast GetInstanceProcAddr(instance, "vkDestroyIndirectCommandsLayoutNV") + DestroyPipeline = auto_cast GetInstanceProcAddr(instance, "vkDestroyPipeline") + DestroyPipelineCache = auto_cast GetInstanceProcAddr(instance, "vkDestroyPipelineCache") + DestroyPipelineLayout = auto_cast GetInstanceProcAddr(instance, "vkDestroyPipelineLayout") + DestroyPrivateDataSlot = auto_cast GetInstanceProcAddr(instance, "vkDestroyPrivateDataSlot") + DestroyPrivateDataSlotEXT = auto_cast GetInstanceProcAddr(instance, "vkDestroyPrivateDataSlotEXT") + DestroyQueryPool = auto_cast GetInstanceProcAddr(instance, "vkDestroyQueryPool") + DestroyRenderPass = auto_cast GetInstanceProcAddr(instance, "vkDestroyRenderPass") + DestroySampler = auto_cast GetInstanceProcAddr(instance, "vkDestroySampler") + DestroySamplerYcbcrConversion = auto_cast GetInstanceProcAddr(instance, "vkDestroySamplerYcbcrConversion") + DestroySamplerYcbcrConversionKHR = auto_cast GetInstanceProcAddr(instance, "vkDestroySamplerYcbcrConversionKHR") + DestroySemaphore = auto_cast GetInstanceProcAddr(instance, "vkDestroySemaphore") + DestroyShaderModule = auto_cast GetInstanceProcAddr(instance, "vkDestroyShaderModule") + DestroySwapchainKHR = auto_cast GetInstanceProcAddr(instance, "vkDestroySwapchainKHR") + DestroyValidationCacheEXT = auto_cast GetInstanceProcAddr(instance, "vkDestroyValidationCacheEXT") + DeviceWaitIdle = auto_cast GetInstanceProcAddr(instance, "vkDeviceWaitIdle") + DisplayPowerControlEXT = auto_cast GetInstanceProcAddr(instance, "vkDisplayPowerControlEXT") + EndCommandBuffer = auto_cast GetInstanceProcAddr(instance, "vkEndCommandBuffer") + FlushMappedMemoryRanges = auto_cast GetInstanceProcAddr(instance, "vkFlushMappedMemoryRanges") + FreeCommandBuffers = auto_cast GetInstanceProcAddr(instance, "vkFreeCommandBuffers") + FreeDescriptorSets = auto_cast GetInstanceProcAddr(instance, "vkFreeDescriptorSets") + FreeMemory = auto_cast GetInstanceProcAddr(instance, "vkFreeMemory") + GetAccelerationStructureBuildSizesKHR = auto_cast GetInstanceProcAddr(instance, "vkGetAccelerationStructureBuildSizesKHR") + GetAccelerationStructureDeviceAddressKHR = auto_cast GetInstanceProcAddr(instance, "vkGetAccelerationStructureDeviceAddressKHR") + GetAccelerationStructureHandleNV = auto_cast GetInstanceProcAddr(instance, "vkGetAccelerationStructureHandleNV") + GetAccelerationStructureMemoryRequirementsNV = auto_cast GetInstanceProcAddr(instance, "vkGetAccelerationStructureMemoryRequirementsNV") + GetBufferDeviceAddress = auto_cast GetInstanceProcAddr(instance, "vkGetBufferDeviceAddress") + GetBufferDeviceAddressEXT = auto_cast GetInstanceProcAddr(instance, "vkGetBufferDeviceAddressEXT") + GetBufferDeviceAddressKHR = auto_cast GetInstanceProcAddr(instance, "vkGetBufferDeviceAddressKHR") + GetBufferMemoryRequirements = auto_cast GetInstanceProcAddr(instance, "vkGetBufferMemoryRequirements") + GetBufferMemoryRequirements2 = auto_cast GetInstanceProcAddr(instance, "vkGetBufferMemoryRequirements2") + GetBufferMemoryRequirements2KHR = auto_cast GetInstanceProcAddr(instance, "vkGetBufferMemoryRequirements2KHR") + GetBufferOpaqueCaptureAddress = auto_cast GetInstanceProcAddr(instance, "vkGetBufferOpaqueCaptureAddress") + GetBufferOpaqueCaptureAddressKHR = auto_cast GetInstanceProcAddr(instance, "vkGetBufferOpaqueCaptureAddressKHR") + GetCalibratedTimestampsEXT = auto_cast GetInstanceProcAddr(instance, "vkGetCalibratedTimestampsEXT") + GetDeferredOperationMaxConcurrencyKHR = auto_cast GetInstanceProcAddr(instance, "vkGetDeferredOperationMaxConcurrencyKHR") + GetDeferredOperationResultKHR = auto_cast GetInstanceProcAddr(instance, "vkGetDeferredOperationResultKHR") + GetDescriptorSetHostMappingVALVE = auto_cast GetInstanceProcAddr(instance, "vkGetDescriptorSetHostMappingVALVE") + GetDescriptorSetLayoutHostMappingInfoVALVE = auto_cast GetInstanceProcAddr(instance, "vkGetDescriptorSetLayoutHostMappingInfoVALVE") + GetDescriptorSetLayoutSupport = auto_cast GetInstanceProcAddr(instance, "vkGetDescriptorSetLayoutSupport") + GetDescriptorSetLayoutSupportKHR = auto_cast GetInstanceProcAddr(instance, "vkGetDescriptorSetLayoutSupportKHR") + GetDeviceAccelerationStructureCompatibilityKHR = auto_cast GetInstanceProcAddr(instance, "vkGetDeviceAccelerationStructureCompatibilityKHR") + GetDeviceBufferMemoryRequirements = auto_cast GetInstanceProcAddr(instance, "vkGetDeviceBufferMemoryRequirements") + GetDeviceBufferMemoryRequirementsKHR = auto_cast GetInstanceProcAddr(instance, "vkGetDeviceBufferMemoryRequirementsKHR") + GetDeviceGroupPeerMemoryFeatures = auto_cast GetInstanceProcAddr(instance, "vkGetDeviceGroupPeerMemoryFeatures") + GetDeviceGroupPeerMemoryFeaturesKHR = auto_cast GetInstanceProcAddr(instance, "vkGetDeviceGroupPeerMemoryFeaturesKHR") + GetDeviceGroupPresentCapabilitiesKHR = auto_cast GetInstanceProcAddr(instance, "vkGetDeviceGroupPresentCapabilitiesKHR") + GetDeviceGroupSurfacePresentModes2EXT = auto_cast GetInstanceProcAddr(instance, "vkGetDeviceGroupSurfacePresentModes2EXT") + GetDeviceGroupSurfacePresentModesKHR = auto_cast GetInstanceProcAddr(instance, "vkGetDeviceGroupSurfacePresentModesKHR") + GetDeviceImageMemoryRequirements = auto_cast GetInstanceProcAddr(instance, "vkGetDeviceImageMemoryRequirements") + GetDeviceImageMemoryRequirementsKHR = auto_cast GetInstanceProcAddr(instance, "vkGetDeviceImageMemoryRequirementsKHR") + GetDeviceImageSparseMemoryRequirements = auto_cast GetInstanceProcAddr(instance, "vkGetDeviceImageSparseMemoryRequirements") + GetDeviceImageSparseMemoryRequirementsKHR = auto_cast GetInstanceProcAddr(instance, "vkGetDeviceImageSparseMemoryRequirementsKHR") + GetDeviceMemoryCommitment = auto_cast GetInstanceProcAddr(instance, "vkGetDeviceMemoryCommitment") + GetDeviceMemoryOpaqueCaptureAddress = auto_cast GetInstanceProcAddr(instance, "vkGetDeviceMemoryOpaqueCaptureAddress") + GetDeviceMemoryOpaqueCaptureAddressKHR = auto_cast GetInstanceProcAddr(instance, "vkGetDeviceMemoryOpaqueCaptureAddressKHR") + GetDeviceProcAddr = auto_cast GetInstanceProcAddr(instance, "vkGetDeviceProcAddr") + GetDeviceQueue = auto_cast GetInstanceProcAddr(instance, "vkGetDeviceQueue") + GetDeviceQueue2 = auto_cast GetInstanceProcAddr(instance, "vkGetDeviceQueue2") + GetDeviceSubpassShadingMaxWorkgroupSizeHUAWEI = auto_cast GetInstanceProcAddr(instance, "vkGetDeviceSubpassShadingMaxWorkgroupSizeHUAWEI") + GetEventStatus = auto_cast GetInstanceProcAddr(instance, "vkGetEventStatus") + GetFenceFdKHR = auto_cast GetInstanceProcAddr(instance, "vkGetFenceFdKHR") + GetFenceStatus = auto_cast GetInstanceProcAddr(instance, "vkGetFenceStatus") + GetFenceWin32HandleKHR = auto_cast GetInstanceProcAddr(instance, "vkGetFenceWin32HandleKHR") + GetGeneratedCommandsMemoryRequirementsNV = auto_cast GetInstanceProcAddr(instance, "vkGetGeneratedCommandsMemoryRequirementsNV") + GetImageDrmFormatModifierPropertiesEXT = auto_cast GetInstanceProcAddr(instance, "vkGetImageDrmFormatModifierPropertiesEXT") + GetImageMemoryRequirements = auto_cast GetInstanceProcAddr(instance, "vkGetImageMemoryRequirements") + GetImageMemoryRequirements2 = auto_cast GetInstanceProcAddr(instance, "vkGetImageMemoryRequirements2") + GetImageMemoryRequirements2KHR = auto_cast GetInstanceProcAddr(instance, "vkGetImageMemoryRequirements2KHR") + GetImageSparseMemoryRequirements = auto_cast GetInstanceProcAddr(instance, "vkGetImageSparseMemoryRequirements") + GetImageSparseMemoryRequirements2 = auto_cast GetInstanceProcAddr(instance, "vkGetImageSparseMemoryRequirements2") + GetImageSparseMemoryRequirements2KHR = auto_cast GetInstanceProcAddr(instance, "vkGetImageSparseMemoryRequirements2KHR") + GetImageSubresourceLayout = auto_cast GetInstanceProcAddr(instance, "vkGetImageSubresourceLayout") + GetImageViewAddressNVX = auto_cast GetInstanceProcAddr(instance, "vkGetImageViewAddressNVX") + GetImageViewHandleNVX = auto_cast GetInstanceProcAddr(instance, "vkGetImageViewHandleNVX") + GetMemoryFdKHR = auto_cast GetInstanceProcAddr(instance, "vkGetMemoryFdKHR") + GetMemoryFdPropertiesKHR = auto_cast GetInstanceProcAddr(instance, "vkGetMemoryFdPropertiesKHR") + GetMemoryHostPointerPropertiesEXT = auto_cast GetInstanceProcAddr(instance, "vkGetMemoryHostPointerPropertiesEXT") + GetMemoryRemoteAddressNV = auto_cast GetInstanceProcAddr(instance, "vkGetMemoryRemoteAddressNV") + GetMemoryWin32HandleKHR = auto_cast GetInstanceProcAddr(instance, "vkGetMemoryWin32HandleKHR") + GetMemoryWin32HandleNV = auto_cast GetInstanceProcAddr(instance, "vkGetMemoryWin32HandleNV") + GetMemoryWin32HandlePropertiesKHR = auto_cast GetInstanceProcAddr(instance, "vkGetMemoryWin32HandlePropertiesKHR") + GetPastPresentationTimingGOOGLE = auto_cast GetInstanceProcAddr(instance, "vkGetPastPresentationTimingGOOGLE") + GetPerformanceParameterINTEL = auto_cast GetInstanceProcAddr(instance, "vkGetPerformanceParameterINTEL") + GetPipelineCacheData = auto_cast GetInstanceProcAddr(instance, "vkGetPipelineCacheData") + GetPipelineExecutableInternalRepresentationsKHR = auto_cast GetInstanceProcAddr(instance, "vkGetPipelineExecutableInternalRepresentationsKHR") + GetPipelineExecutablePropertiesKHR = auto_cast GetInstanceProcAddr(instance, "vkGetPipelineExecutablePropertiesKHR") + GetPipelineExecutableStatisticsKHR = auto_cast GetInstanceProcAddr(instance, "vkGetPipelineExecutableStatisticsKHR") + GetPrivateData = auto_cast GetInstanceProcAddr(instance, "vkGetPrivateData") + GetPrivateDataEXT = auto_cast GetInstanceProcAddr(instance, "vkGetPrivateDataEXT") + GetQueryPoolResults = auto_cast GetInstanceProcAddr(instance, "vkGetQueryPoolResults") + GetQueueCheckpointData2NV = auto_cast GetInstanceProcAddr(instance, "vkGetQueueCheckpointData2NV") + GetQueueCheckpointDataNV = auto_cast GetInstanceProcAddr(instance, "vkGetQueueCheckpointDataNV") + GetRayTracingCaptureReplayShaderGroupHandlesKHR = auto_cast GetInstanceProcAddr(instance, "vkGetRayTracingCaptureReplayShaderGroupHandlesKHR") + GetRayTracingShaderGroupHandlesKHR = auto_cast GetInstanceProcAddr(instance, "vkGetRayTracingShaderGroupHandlesKHR") + GetRayTracingShaderGroupHandlesNV = auto_cast GetInstanceProcAddr(instance, "vkGetRayTracingShaderGroupHandlesNV") + GetRayTracingShaderGroupStackSizeKHR = auto_cast GetInstanceProcAddr(instance, "vkGetRayTracingShaderGroupStackSizeKHR") + GetRefreshCycleDurationGOOGLE = auto_cast GetInstanceProcAddr(instance, "vkGetRefreshCycleDurationGOOGLE") + GetRenderAreaGranularity = auto_cast GetInstanceProcAddr(instance, "vkGetRenderAreaGranularity") + GetSemaphoreCounterValue = auto_cast GetInstanceProcAddr(instance, "vkGetSemaphoreCounterValue") + GetSemaphoreCounterValueKHR = auto_cast GetInstanceProcAddr(instance, "vkGetSemaphoreCounterValueKHR") + GetSemaphoreFdKHR = auto_cast GetInstanceProcAddr(instance, "vkGetSemaphoreFdKHR") + GetSemaphoreWin32HandleKHR = auto_cast GetInstanceProcAddr(instance, "vkGetSemaphoreWin32HandleKHR") + GetShaderInfoAMD = auto_cast GetInstanceProcAddr(instance, "vkGetShaderInfoAMD") + GetSwapchainCounterEXT = auto_cast GetInstanceProcAddr(instance, "vkGetSwapchainCounterEXT") + GetSwapchainImagesKHR = auto_cast GetInstanceProcAddr(instance, "vkGetSwapchainImagesKHR") + GetSwapchainStatusKHR = auto_cast GetInstanceProcAddr(instance, "vkGetSwapchainStatusKHR") + GetValidationCacheDataEXT = auto_cast GetInstanceProcAddr(instance, "vkGetValidationCacheDataEXT") + ImportFenceFdKHR = auto_cast GetInstanceProcAddr(instance, "vkImportFenceFdKHR") + ImportFenceWin32HandleKHR = auto_cast GetInstanceProcAddr(instance, "vkImportFenceWin32HandleKHR") + ImportSemaphoreFdKHR = auto_cast GetInstanceProcAddr(instance, "vkImportSemaphoreFdKHR") + ImportSemaphoreWin32HandleKHR = auto_cast GetInstanceProcAddr(instance, "vkImportSemaphoreWin32HandleKHR") + InitializePerformanceApiINTEL = auto_cast GetInstanceProcAddr(instance, "vkInitializePerformanceApiINTEL") + InvalidateMappedMemoryRanges = auto_cast GetInstanceProcAddr(instance, "vkInvalidateMappedMemoryRanges") + MapMemory = auto_cast GetInstanceProcAddr(instance, "vkMapMemory") + MergePipelineCaches = auto_cast GetInstanceProcAddr(instance, "vkMergePipelineCaches") + MergeValidationCachesEXT = auto_cast GetInstanceProcAddr(instance, "vkMergeValidationCachesEXT") + QueueBeginDebugUtilsLabelEXT = auto_cast GetInstanceProcAddr(instance, "vkQueueBeginDebugUtilsLabelEXT") + QueueBindSparse = auto_cast GetInstanceProcAddr(instance, "vkQueueBindSparse") + QueueEndDebugUtilsLabelEXT = auto_cast GetInstanceProcAddr(instance, "vkQueueEndDebugUtilsLabelEXT") + QueueInsertDebugUtilsLabelEXT = auto_cast GetInstanceProcAddr(instance, "vkQueueInsertDebugUtilsLabelEXT") + QueuePresentKHR = auto_cast GetInstanceProcAddr(instance, "vkQueuePresentKHR") + QueueSetPerformanceConfigurationINTEL = auto_cast GetInstanceProcAddr(instance, "vkQueueSetPerformanceConfigurationINTEL") + QueueSubmit = auto_cast GetInstanceProcAddr(instance, "vkQueueSubmit") + QueueSubmit2 = auto_cast GetInstanceProcAddr(instance, "vkQueueSubmit2") + QueueSubmit2KHR = auto_cast GetInstanceProcAddr(instance, "vkQueueSubmit2KHR") + QueueWaitIdle = auto_cast GetInstanceProcAddr(instance, "vkQueueWaitIdle") + RegisterDeviceEventEXT = auto_cast GetInstanceProcAddr(instance, "vkRegisterDeviceEventEXT") + RegisterDisplayEventEXT = auto_cast GetInstanceProcAddr(instance, "vkRegisterDisplayEventEXT") + ReleaseFullScreenExclusiveModeEXT = auto_cast GetInstanceProcAddr(instance, "vkReleaseFullScreenExclusiveModeEXT") + ReleasePerformanceConfigurationINTEL = auto_cast GetInstanceProcAddr(instance, "vkReleasePerformanceConfigurationINTEL") + ReleaseProfilingLockKHR = auto_cast GetInstanceProcAddr(instance, "vkReleaseProfilingLockKHR") + ResetCommandBuffer = auto_cast GetInstanceProcAddr(instance, "vkResetCommandBuffer") + ResetCommandPool = auto_cast GetInstanceProcAddr(instance, "vkResetCommandPool") + ResetDescriptorPool = auto_cast GetInstanceProcAddr(instance, "vkResetDescriptorPool") + ResetEvent = auto_cast GetInstanceProcAddr(instance, "vkResetEvent") + ResetFences = auto_cast GetInstanceProcAddr(instance, "vkResetFences") + ResetQueryPool = auto_cast GetInstanceProcAddr(instance, "vkResetQueryPool") + ResetQueryPoolEXT = auto_cast GetInstanceProcAddr(instance, "vkResetQueryPoolEXT") + SetDebugUtilsObjectNameEXT = auto_cast GetInstanceProcAddr(instance, "vkSetDebugUtilsObjectNameEXT") + SetDebugUtilsObjectTagEXT = auto_cast GetInstanceProcAddr(instance, "vkSetDebugUtilsObjectTagEXT") + SetDeviceMemoryPriorityEXT = auto_cast GetInstanceProcAddr(instance, "vkSetDeviceMemoryPriorityEXT") + SetEvent = auto_cast GetInstanceProcAddr(instance, "vkSetEvent") + SetHdrMetadataEXT = auto_cast GetInstanceProcAddr(instance, "vkSetHdrMetadataEXT") + SetLocalDimmingAMD = auto_cast GetInstanceProcAddr(instance, "vkSetLocalDimmingAMD") + SetPrivateData = auto_cast GetInstanceProcAddr(instance, "vkSetPrivateData") + SetPrivateDataEXT = auto_cast GetInstanceProcAddr(instance, "vkSetPrivateDataEXT") + SignalSemaphore = auto_cast GetInstanceProcAddr(instance, "vkSignalSemaphore") + SignalSemaphoreKHR = auto_cast GetInstanceProcAddr(instance, "vkSignalSemaphoreKHR") + TrimCommandPool = auto_cast GetInstanceProcAddr(instance, "vkTrimCommandPool") + TrimCommandPoolKHR = auto_cast GetInstanceProcAddr(instance, "vkTrimCommandPoolKHR") + UninitializePerformanceApiINTEL = auto_cast GetInstanceProcAddr(instance, "vkUninitializePerformanceApiINTEL") + UnmapMemory = auto_cast GetInstanceProcAddr(instance, "vkUnmapMemory") + UpdateDescriptorSetWithTemplate = auto_cast GetInstanceProcAddr(instance, "vkUpdateDescriptorSetWithTemplate") + UpdateDescriptorSetWithTemplateKHR = auto_cast GetInstanceProcAddr(instance, "vkUpdateDescriptorSetWithTemplateKHR") + UpdateDescriptorSets = auto_cast GetInstanceProcAddr(instance, "vkUpdateDescriptorSets") + WaitForFences = auto_cast GetInstanceProcAddr(instance, "vkWaitForFences") + WaitForPresentKHR = auto_cast GetInstanceProcAddr(instance, "vkWaitForPresentKHR") + WaitSemaphores = auto_cast GetInstanceProcAddr(instance, "vkWaitSemaphores") + WaitSemaphoresKHR = auto_cast GetInstanceProcAddr(instance, "vkWaitSemaphoresKHR") + WriteAccelerationStructuresPropertiesKHR = auto_cast GetInstanceProcAddr(instance, "vkWriteAccelerationStructuresPropertiesKHR") +} + +load_proc_addresses_global :: proc(vk_get_instance_proc_addr: rawptr) { + GetInstanceProcAddr = auto_cast vk_get_instance_proc_addr + + CreateInstance = auto_cast GetInstanceProcAddr(nil, "vkCreateInstance") + DebugUtilsMessengerCallbackEXT = auto_cast GetInstanceProcAddr(nil, "vkDebugUtilsMessengerCallbackEXT") + DeviceMemoryReportCallbackEXT = auto_cast GetInstanceProcAddr(nil, "vkDeviceMemoryReportCallbackEXT") + EnumerateInstanceExtensionProperties = auto_cast GetInstanceProcAddr(nil, "vkEnumerateInstanceExtensionProperties") + EnumerateInstanceLayerProperties = auto_cast GetInstanceProcAddr(nil, "vkEnumerateInstanceLayerProperties") + EnumerateInstanceVersion = auto_cast GetInstanceProcAddr(nil, "vkEnumerateInstanceVersion") +} + +load_proc_addresses :: proc{ + load_proc_addresses_global, + load_proc_addresses_instance, + load_proc_addresses_device, + load_proc_addresses_device_vtable, + load_proc_addresses_custom, +} diff --git a/vendor/vulkan/structs.odin b/vendor/vulkan/structs.odin index 4d90a53fa..3bc3e1935 100644 --- a/vendor/vulkan/structs.odin +++ b/vendor/vulkan/structs.odin @@ -5,7 +5,7 @@ package vulkan import "core:c" -when ODIN_OS == "windows" { +when ODIN_OS == .Windows { import win32 "core:sys/windows" HINSTANCE :: win32.HINSTANCE @@ -2170,6 +2170,537 @@ DeviceMemoryOpaqueCaptureAddressInfo :: struct { memory: DeviceMemory, } +PhysicalDeviceVulkan13Features :: struct { + sType: StructureType, + pNext: rawptr, + robustImageAccess: b32, + inlineUniformBlock: b32, + descriptorBindingInlineUniformBlockUpdateAfterBind: b32, + pipelineCreationCacheControl: b32, + privateData: b32, + shaderDemoteToHelperInvocation: b32, + shaderTerminateInvocation: b32, + subgroupSizeControl: b32, + computeFullSubgroups: b32, + synchronization2: b32, + textureCompressionASTC_HDR: b32, + shaderZeroInitializeWorkgroupMemory: b32, + dynamicRendering: b32, + shaderIntegerDotProduct: b32, + maintenance4: b32, +} + +PhysicalDeviceVulkan13Properties :: struct { + sType: StructureType, + pNext: rawptr, + minSubgroupSize: u32, + maxSubgroupSize: u32, + maxComputeWorkgroupSubgroups: u32, + requiredSubgroupSizeStages: ShaderStageFlags, + maxInlineUniformBlockSize: u32, + maxPerStageDescriptorInlineUniformBlocks: u32, + maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks: u32, + maxDescriptorSetInlineUniformBlocks: u32, + maxDescriptorSetUpdateAfterBindInlineUniformBlocks: u32, + maxInlineUniformTotalSize: u32, + integerDotProduct8BitUnsignedAccelerated: b32, + integerDotProduct8BitSignedAccelerated: b32, + integerDotProduct8BitMixedSignednessAccelerated: b32, + integerDotProduct4x8BitPackedUnsignedAccelerated: b32, + integerDotProduct4x8BitPackedSignedAccelerated: b32, + integerDotProduct4x8BitPackedMixedSignednessAccelerated: b32, + integerDotProduct16BitUnsignedAccelerated: b32, + integerDotProduct16BitSignedAccelerated: b32, + integerDotProduct16BitMixedSignednessAccelerated: b32, + integerDotProduct32BitUnsignedAccelerated: b32, + integerDotProduct32BitSignedAccelerated: b32, + integerDotProduct32BitMixedSignednessAccelerated: b32, + integerDotProduct64BitUnsignedAccelerated: b32, + integerDotProduct64BitSignedAccelerated: b32, + integerDotProduct64BitMixedSignednessAccelerated: b32, + integerDotProductAccumulatingSaturating8BitUnsignedAccelerated: b32, + integerDotProductAccumulatingSaturating8BitSignedAccelerated: b32, + integerDotProductAccumulatingSaturating8BitMixedSignednessAccelerated: b32, + integerDotProductAccumulatingSaturating4x8BitPackedUnsignedAccelerated: b32, + integerDotProductAccumulatingSaturating4x8BitPackedSignedAccelerated: b32, + integerDotProductAccumulatingSaturating4x8BitPackedMixedSignednessAccelerated: b32, + integerDotProductAccumulatingSaturating16BitUnsignedAccelerated: b32, + integerDotProductAccumulatingSaturating16BitSignedAccelerated: b32, + integerDotProductAccumulatingSaturating16BitMixedSignednessAccelerated: b32, + integerDotProductAccumulatingSaturating32BitUnsignedAccelerated: b32, + integerDotProductAccumulatingSaturating32BitSignedAccelerated: b32, + integerDotProductAccumulatingSaturating32BitMixedSignednessAccelerated: b32, + integerDotProductAccumulatingSaturating64BitUnsignedAccelerated: b32, + integerDotProductAccumulatingSaturating64BitSignedAccelerated: b32, + integerDotProductAccumulatingSaturating64BitMixedSignednessAccelerated: b32, + storageTexelBufferOffsetAlignmentBytes: DeviceSize, + storageTexelBufferOffsetSingleTexelAlignment: b32, + uniformTexelBufferOffsetAlignmentBytes: DeviceSize, + uniformTexelBufferOffsetSingleTexelAlignment: b32, + maxBufferSize: DeviceSize, +} + +PipelineCreationFeedback :: struct { + flags: PipelineCreationFeedbackFlags, + duration: u64, +} + +PipelineCreationFeedbackCreateInfo :: struct { + sType: StructureType, + pNext: rawptr, + pPipelineCreationFeedback: ^PipelineCreationFeedback, + pipelineStageCreationFeedbackCount: u32, + pPipelineStageCreationFeedbacks: [^]PipelineCreationFeedback, +} + +PhysicalDeviceShaderTerminateInvocationFeatures :: struct { + sType: StructureType, + pNext: rawptr, + shaderTerminateInvocation: b32, +} + +PhysicalDeviceToolProperties :: struct { + sType: StructureType, + pNext: rawptr, + name: [MAX_EXTENSION_NAME_SIZE]byte, + version: [MAX_EXTENSION_NAME_SIZE]byte, + purposes: ToolPurposeFlags, + description: [MAX_DESCRIPTION_SIZE]byte, + layer: [MAX_EXTENSION_NAME_SIZE]byte, +} + +PhysicalDeviceShaderDemoteToHelperInvocationFeatures :: struct { + sType: StructureType, + pNext: rawptr, + shaderDemoteToHelperInvocation: b32, +} + +PhysicalDevicePrivateDataFeatures :: struct { + sType: StructureType, + pNext: rawptr, + privateData: b32, +} + +DevicePrivateDataCreateInfo :: struct { + sType: StructureType, + pNext: rawptr, + privateDataSlotRequestCount: u32, +} + +PrivateDataSlotCreateInfo :: struct { + sType: StructureType, + pNext: rawptr, + flags: PrivateDataSlotCreateFlags, +} + +PhysicalDevicePipelineCreationCacheControlFeatures :: struct { + sType: StructureType, + pNext: rawptr, + pipelineCreationCacheControl: b32, +} + +MemoryBarrier2 :: struct { + sType: StructureType, + pNext: rawptr, + srcStageMask: PipelineStageFlags2, + srcAccessMask: AccessFlags2, + dstStageMask: PipelineStageFlags2, + dstAccessMask: AccessFlags2, +} + +BufferMemoryBarrier2 :: struct { + sType: StructureType, + pNext: rawptr, + srcStageMask: PipelineStageFlags2, + srcAccessMask: AccessFlags2, + dstStageMask: PipelineStageFlags2, + dstAccessMask: AccessFlags2, + srcQueueFamilyIndex: u32, + dstQueueFamilyIndex: u32, + buffer: Buffer, + offset: DeviceSize, + size: DeviceSize, +} + +ImageMemoryBarrier2 :: struct { + sType: StructureType, + pNext: rawptr, + srcStageMask: PipelineStageFlags2, + srcAccessMask: AccessFlags2, + dstStageMask: PipelineStageFlags2, + dstAccessMask: AccessFlags2, + oldLayout: ImageLayout, + newLayout: ImageLayout, + srcQueueFamilyIndex: u32, + dstQueueFamilyIndex: u32, + image: Image, + subresourceRange: ImageSubresourceRange, +} + +DependencyInfo :: struct { + sType: StructureType, + pNext: rawptr, + dependencyFlags: DependencyFlags, + memoryBarrierCount: u32, + pMemoryBarriers: [^]MemoryBarrier2, + bufferMemoryBarrierCount: u32, + pBufferMemoryBarriers: [^]BufferMemoryBarrier2, + imageMemoryBarrierCount: u32, + pImageMemoryBarriers: [^]ImageMemoryBarrier2, +} + +SemaphoreSubmitInfo :: struct { + sType: StructureType, + pNext: rawptr, + semaphore: Semaphore, + value: u64, + stageMask: PipelineStageFlags2, + deviceIndex: u32, +} + +CommandBufferSubmitInfo :: struct { + sType: StructureType, + pNext: rawptr, + commandBuffer: CommandBuffer, + deviceMask: u32, +} + +SubmitInfo2 :: struct { + sType: StructureType, + pNext: rawptr, + flags: SubmitFlags, + waitSemaphoreInfoCount: u32, + pWaitSemaphoreInfos: [^]SemaphoreSubmitInfo, + commandBufferInfoCount: u32, + pCommandBufferInfos: [^]CommandBufferSubmitInfo, + signalSemaphoreInfoCount: u32, + pSignalSemaphoreInfos: [^]SemaphoreSubmitInfo, +} + +PhysicalDeviceSynchronization2Features :: struct { + sType: StructureType, + pNext: rawptr, + synchronization2: b32, +} + +PhysicalDeviceZeroInitializeWorkgroupMemoryFeatures :: struct { + sType: StructureType, + pNext: rawptr, + shaderZeroInitializeWorkgroupMemory: b32, +} + +PhysicalDeviceImageRobustnessFeatures :: struct { + sType: StructureType, + pNext: rawptr, + robustImageAccess: b32, +} + +BufferCopy2 :: struct { + sType: StructureType, + pNext: rawptr, + srcOffset: DeviceSize, + dstOffset: DeviceSize, + size: DeviceSize, +} + +CopyBufferInfo2 :: struct { + sType: StructureType, + pNext: rawptr, + srcBuffer: Buffer, + dstBuffer: Buffer, + regionCount: u32, + pRegions: [^]BufferCopy2, +} + +ImageCopy2 :: struct { + sType: StructureType, + pNext: rawptr, + srcSubresource: ImageSubresourceLayers, + srcOffset: Offset3D, + dstSubresource: ImageSubresourceLayers, + dstOffset: Offset3D, + extent: Extent3D, +} + +CopyImageInfo2 :: struct { + sType: StructureType, + pNext: rawptr, + srcImage: Image, + srcImageLayout: ImageLayout, + dstImage: Image, + dstImageLayout: ImageLayout, + regionCount: u32, + pRegions: [^]ImageCopy2, +} + +BufferImageCopy2 :: struct { + sType: StructureType, + pNext: rawptr, + bufferOffset: DeviceSize, + bufferRowLength: u32, + bufferImageHeight: u32, + imageSubresource: ImageSubresourceLayers, + imageOffset: Offset3D, + imageExtent: Extent3D, +} + +CopyBufferToImageInfo2 :: struct { + sType: StructureType, + pNext: rawptr, + srcBuffer: Buffer, + dstImage: Image, + dstImageLayout: ImageLayout, + regionCount: u32, + pRegions: [^]BufferImageCopy2, +} + +CopyImageToBufferInfo2 :: struct { + sType: StructureType, + pNext: rawptr, + srcImage: Image, + srcImageLayout: ImageLayout, + dstBuffer: Buffer, + regionCount: u32, + pRegions: [^]BufferImageCopy2, +} + +ImageBlit2 :: struct { + sType: StructureType, + pNext: rawptr, + srcSubresource: ImageSubresourceLayers, + srcOffsets: [2]Offset3D, + dstSubresource: ImageSubresourceLayers, + dstOffsets: [2]Offset3D, +} + +BlitImageInfo2 :: struct { + sType: StructureType, + pNext: rawptr, + srcImage: Image, + srcImageLayout: ImageLayout, + dstImage: Image, + dstImageLayout: ImageLayout, + regionCount: u32, + pRegions: [^]ImageBlit2, + filter: Filter, +} + +ImageResolve2 :: struct { + sType: StructureType, + pNext: rawptr, + srcSubresource: ImageSubresourceLayers, + srcOffset: Offset3D, + dstSubresource: ImageSubresourceLayers, + dstOffset: Offset3D, + extent: Extent3D, +} + +ResolveImageInfo2 :: struct { + sType: StructureType, + pNext: rawptr, + srcImage: Image, + srcImageLayout: ImageLayout, + dstImage: Image, + dstImageLayout: ImageLayout, + regionCount: u32, + pRegions: [^]ImageResolve2, +} + +PhysicalDeviceSubgroupSizeControlFeatures :: struct { + sType: StructureType, + pNext: rawptr, + subgroupSizeControl: b32, + computeFullSubgroups: b32, +} + +PhysicalDeviceSubgroupSizeControlProperties :: struct { + sType: StructureType, + pNext: rawptr, + minSubgroupSize: u32, + maxSubgroupSize: u32, + maxComputeWorkgroupSubgroups: u32, + requiredSubgroupSizeStages: ShaderStageFlags, +} + +PipelineShaderStageRequiredSubgroupSizeCreateInfo :: struct { + sType: StructureType, + pNext: rawptr, + requiredSubgroupSize: u32, +} + +PhysicalDeviceInlineUniformBlockFeatures :: struct { + sType: StructureType, + pNext: rawptr, + inlineUniformBlock: b32, + descriptorBindingInlineUniformBlockUpdateAfterBind: b32, +} + +PhysicalDeviceInlineUniformBlockProperties :: struct { + sType: StructureType, + pNext: rawptr, + maxInlineUniformBlockSize: u32, + maxPerStageDescriptorInlineUniformBlocks: u32, + maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks: u32, + maxDescriptorSetInlineUniformBlocks: u32, + maxDescriptorSetUpdateAfterBindInlineUniformBlocks: u32, +} + +WriteDescriptorSetInlineUniformBlock :: struct { + sType: StructureType, + pNext: rawptr, + dataSize: u32, + pData: rawptr, +} + +DescriptorPoolInlineUniformBlockCreateInfo :: struct { + sType: StructureType, + pNext: rawptr, + maxInlineUniformBlockBindings: u32, +} + +PhysicalDeviceTextureCompressionASTCHDRFeatures :: struct { + sType: StructureType, + pNext: rawptr, + textureCompressionASTC_HDR: b32, +} + +RenderingAttachmentInfo :: struct { + sType: StructureType, + pNext: rawptr, + imageView: ImageView, + imageLayout: ImageLayout, + resolveMode: ResolveModeFlags, + resolveImageView: ImageView, + resolveImageLayout: ImageLayout, + loadOp: AttachmentLoadOp, + storeOp: AttachmentStoreOp, + clearValue: ClearValue, +} + +RenderingInfo :: struct { + sType: StructureType, + pNext: rawptr, + flags: RenderingFlags, + renderArea: Rect2D, + layerCount: u32, + viewMask: u32, + colorAttachmentCount: u32, + pColorAttachments: [^]RenderingAttachmentInfo, + pDepthAttachment: ^RenderingAttachmentInfo, + pStencilAttachment: ^RenderingAttachmentInfo, +} + +PipelineRenderingCreateInfo :: struct { + sType: StructureType, + pNext: rawptr, + viewMask: u32, + colorAttachmentCount: u32, + pColorAttachmentFormats: [^]Format, + depthAttachmentFormat: Format, + stencilAttachmentFormat: Format, +} + +PhysicalDeviceDynamicRenderingFeatures :: struct { + sType: StructureType, + pNext: rawptr, + dynamicRendering: b32, +} + +CommandBufferInheritanceRenderingInfo :: struct { + sType: StructureType, + pNext: rawptr, + flags: RenderingFlags, + viewMask: u32, + colorAttachmentCount: u32, + pColorAttachmentFormats: [^]Format, + depthAttachmentFormat: Format, + stencilAttachmentFormat: Format, + rasterizationSamples: SampleCountFlags, +} + +PhysicalDeviceShaderIntegerDotProductFeatures :: struct { + sType: StructureType, + pNext: rawptr, + shaderIntegerDotProduct: b32, +} + +PhysicalDeviceShaderIntegerDotProductProperties :: struct { + sType: StructureType, + pNext: rawptr, + integerDotProduct8BitUnsignedAccelerated: b32, + integerDotProduct8BitSignedAccelerated: b32, + integerDotProduct8BitMixedSignednessAccelerated: b32, + integerDotProduct4x8BitPackedUnsignedAccelerated: b32, + integerDotProduct4x8BitPackedSignedAccelerated: b32, + integerDotProduct4x8BitPackedMixedSignednessAccelerated: b32, + integerDotProduct16BitUnsignedAccelerated: b32, + integerDotProduct16BitSignedAccelerated: b32, + integerDotProduct16BitMixedSignednessAccelerated: b32, + integerDotProduct32BitUnsignedAccelerated: b32, + integerDotProduct32BitSignedAccelerated: b32, + integerDotProduct32BitMixedSignednessAccelerated: b32, + integerDotProduct64BitUnsignedAccelerated: b32, + integerDotProduct64BitSignedAccelerated: b32, + integerDotProduct64BitMixedSignednessAccelerated: b32, + integerDotProductAccumulatingSaturating8BitUnsignedAccelerated: b32, + integerDotProductAccumulatingSaturating8BitSignedAccelerated: b32, + integerDotProductAccumulatingSaturating8BitMixedSignednessAccelerated: b32, + integerDotProductAccumulatingSaturating4x8BitPackedUnsignedAccelerated: b32, + integerDotProductAccumulatingSaturating4x8BitPackedSignedAccelerated: b32, + integerDotProductAccumulatingSaturating4x8BitPackedMixedSignednessAccelerated: b32, + integerDotProductAccumulatingSaturating16BitUnsignedAccelerated: b32, + integerDotProductAccumulatingSaturating16BitSignedAccelerated: b32, + integerDotProductAccumulatingSaturating16BitMixedSignednessAccelerated: b32, + integerDotProductAccumulatingSaturating32BitUnsignedAccelerated: b32, + integerDotProductAccumulatingSaturating32BitSignedAccelerated: b32, + integerDotProductAccumulatingSaturating32BitMixedSignednessAccelerated: b32, + integerDotProductAccumulatingSaturating64BitUnsignedAccelerated: b32, + integerDotProductAccumulatingSaturating64BitSignedAccelerated: b32, + integerDotProductAccumulatingSaturating64BitMixedSignednessAccelerated: b32, +} + +PhysicalDeviceTexelBufferAlignmentProperties :: struct { + sType: StructureType, + pNext: rawptr, + storageTexelBufferOffsetAlignmentBytes: DeviceSize, + storageTexelBufferOffsetSingleTexelAlignment: b32, + uniformTexelBufferOffsetAlignmentBytes: DeviceSize, + uniformTexelBufferOffsetSingleTexelAlignment: b32, +} + +FormatProperties3 :: struct { + sType: StructureType, + pNext: rawptr, + linearTilingFeatures: FormatFeatureFlags2, + optimalTilingFeatures: FormatFeatureFlags2, + bufferFeatures: FormatFeatureFlags2, +} + +PhysicalDeviceMaintenance4Features :: struct { + sType: StructureType, + pNext: rawptr, + maintenance4: b32, +} + +PhysicalDeviceMaintenance4Properties :: struct { + sType: StructureType, + pNext: rawptr, + maxBufferSize: DeviceSize, +} + +DeviceBufferMemoryRequirements :: struct { + sType: StructureType, + pNext: rawptr, + pCreateInfo: ^BufferCreateInfo, +} + +DeviceImageMemoryRequirements :: struct { + sType: StructureType, + pNext: rawptr, + pCreateInfo: ^ImageCreateInfo, + planeAspect: ImageAspectFlags, +} + SurfaceCapabilitiesKHR :: struct { minImageCount: u32, maxImageCount: u32, @@ -2329,6 +2860,36 @@ DisplayPresentInfoKHR :: struct { persistent: b32, } +RenderingFragmentShadingRateAttachmentInfoKHR :: struct { + sType: StructureType, + pNext: rawptr, + imageView: ImageView, + imageLayout: ImageLayout, + shadingRateAttachmentTexelSize: Extent2D, +} + +RenderingFragmentDensityMapAttachmentInfoEXT :: struct { + sType: StructureType, + pNext: rawptr, + imageView: ImageView, + imageLayout: ImageLayout, +} + +AttachmentSampleCountInfoAMD :: struct { + sType: StructureType, + pNext: rawptr, + colorAttachmentCount: u32, + pColorAttachmentSamples: [^]SampleCountFlags, + depthStencilAttachmentSamples: SampleCountFlags, +} + +MultiviewPerViewAttributesInfoNVX :: struct { + sType: StructureType, + pNext: rawptr, + perViewAttributes: b32, + perViewAttributesPositionXOnly: b32, +} + ImportMemoryFdInfoKHR :: struct { sType: StructureType, pNext: rawptr, @@ -2528,10 +3089,23 @@ PhysicalDeviceShaderClockFeaturesKHR :: struct { shaderDeviceClock: b32, } -PhysicalDeviceShaderTerminateInvocationFeaturesKHR :: struct { - sType: StructureType, - pNext: rawptr, - shaderTerminateInvocation: b32, +DeviceQueueGlobalPriorityCreateInfoKHR :: struct { + sType: StructureType, + pNext: rawptr, + globalPriority: QueueGlobalPriorityKHR, +} + +PhysicalDeviceGlobalPriorityQueryFeaturesKHR :: struct { + sType: StructureType, + pNext: rawptr, + globalPriorityQuery: b32, +} + +QueueFamilyGlobalPriorityPropertiesKHR :: struct { + sType: StructureType, + pNext: rawptr, + priorityCount: u32, + priorities: [MAX_GLOBAL_PRIORITY_SIZE_KHR]QueueGlobalPriorityKHR, } FragmentShadingRateAttachmentInfoKHR :: struct { @@ -2651,47 +3225,6 @@ PipelineExecutableInternalRepresentationKHR :: struct { pData: rawptr, } -PhysicalDeviceShaderIntegerDotProductFeaturesKHR :: struct { - sType: StructureType, - pNext: rawptr, - shaderIntegerDotProduct: b32, -} - -PhysicalDeviceShaderIntegerDotProductPropertiesKHR :: struct { - sType: StructureType, - pNext: rawptr, - integerDotProduct8BitUnsignedAccelerated: b32, - integerDotProduct8BitSignedAccelerated: b32, - integerDotProduct8BitMixedSignednessAccelerated: b32, - integerDotProduct4x8BitPackedUnsignedAccelerated: b32, - integerDotProduct4x8BitPackedSignedAccelerated: b32, - integerDotProduct4x8BitPackedMixedSignednessAccelerated: b32, - integerDotProduct16BitUnsignedAccelerated: b32, - integerDotProduct16BitSignedAccelerated: b32, - integerDotProduct16BitMixedSignednessAccelerated: b32, - integerDotProduct32BitUnsignedAccelerated: b32, - integerDotProduct32BitSignedAccelerated: b32, - integerDotProduct32BitMixedSignednessAccelerated: b32, - integerDotProduct64BitUnsignedAccelerated: b32, - integerDotProduct64BitSignedAccelerated: b32, - integerDotProduct64BitMixedSignednessAccelerated: b32, - integerDotProductAccumulatingSaturating8BitUnsignedAccelerated: b32, - integerDotProductAccumulatingSaturating8BitSignedAccelerated: b32, - integerDotProductAccumulatingSaturating8BitMixedSignednessAccelerated: b32, - integerDotProductAccumulatingSaturating4x8BitPackedUnsignedAccelerated: b32, - integerDotProductAccumulatingSaturating4x8BitPackedSignedAccelerated: b32, - integerDotProductAccumulatingSaturating4x8BitPackedMixedSignednessAccelerated: b32, - integerDotProductAccumulatingSaturating16BitUnsignedAccelerated: b32, - integerDotProductAccumulatingSaturating16BitSignedAccelerated: b32, - integerDotProductAccumulatingSaturating16BitMixedSignednessAccelerated: b32, - integerDotProductAccumulatingSaturating32BitUnsignedAccelerated: b32, - integerDotProductAccumulatingSaturating32BitSignedAccelerated: b32, - integerDotProductAccumulatingSaturating32BitMixedSignednessAccelerated: b32, - integerDotProductAccumulatingSaturating64BitUnsignedAccelerated: b32, - integerDotProductAccumulatingSaturating64BitSignedAccelerated: b32, - integerDotProductAccumulatingSaturating64BitMixedSignednessAccelerated: b32, -} - PipelineLibraryCreateInfoKHR :: struct { sType: StructureType, pNext: rawptr, @@ -2712,100 +3245,16 @@ PhysicalDevicePresentIdFeaturesKHR :: struct { presentId: b32, } -MemoryBarrier2KHR :: struct { - sType: StructureType, - pNext: rawptr, - srcStageMask: PipelineStageFlags2KHR, - srcAccessMask: AccessFlags2KHR, - dstStageMask: PipelineStageFlags2KHR, - dstAccessMask: AccessFlags2KHR, -} - -BufferMemoryBarrier2KHR :: struct { - sType: StructureType, - pNext: rawptr, - srcStageMask: PipelineStageFlags2KHR, - srcAccessMask: AccessFlags2KHR, - dstStageMask: PipelineStageFlags2KHR, - dstAccessMask: AccessFlags2KHR, - srcQueueFamilyIndex: u32, - dstQueueFamilyIndex: u32, - buffer: Buffer, - offset: DeviceSize, - size: DeviceSize, -} - -ImageMemoryBarrier2KHR :: struct { - sType: StructureType, - pNext: rawptr, - srcStageMask: PipelineStageFlags2KHR, - srcAccessMask: AccessFlags2KHR, - dstStageMask: PipelineStageFlags2KHR, - dstAccessMask: AccessFlags2KHR, - oldLayout: ImageLayout, - newLayout: ImageLayout, - srcQueueFamilyIndex: u32, - dstQueueFamilyIndex: u32, - image: Image, - subresourceRange: ImageSubresourceRange, -} - -DependencyInfoKHR :: struct { - sType: StructureType, - pNext: rawptr, - dependencyFlags: DependencyFlags, - memoryBarrierCount: u32, - pMemoryBarriers: [^]MemoryBarrier2KHR, - bufferMemoryBarrierCount: u32, - pBufferMemoryBarriers: [^]BufferMemoryBarrier2KHR, - imageMemoryBarrierCount: u32, - pImageMemoryBarriers: [^]ImageMemoryBarrier2KHR, -} - -SemaphoreSubmitInfoKHR :: struct { - sType: StructureType, - pNext: rawptr, - semaphore: Semaphore, - value: u64, - stageMask: PipelineStageFlags2KHR, - deviceIndex: u32, -} - -CommandBufferSubmitInfoKHR :: struct { - sType: StructureType, - pNext: rawptr, - commandBuffer: CommandBuffer, - deviceMask: u32, -} - -SubmitInfo2KHR :: struct { - sType: StructureType, - pNext: rawptr, - flags: SubmitFlagsKHR, - waitSemaphoreInfoCount: u32, - pWaitSemaphoreInfos: [^]SemaphoreSubmitInfoKHR, - commandBufferInfoCount: u32, - pCommandBufferInfos: [^]CommandBufferSubmitInfoKHR, - signalSemaphoreInfoCount: u32, - pSignalSemaphoreInfos: [^]SemaphoreSubmitInfoKHR, -} - -PhysicalDeviceSynchronization2FeaturesKHR :: struct { - sType: StructureType, - pNext: rawptr, - synchronization2: b32, -} - QueueFamilyCheckpointProperties2NV :: struct { sType: StructureType, pNext: rawptr, - checkpointExecutionStageMask: PipelineStageFlags2KHR, + checkpointExecutionStageMask: PipelineStageFlags2, } CheckpointData2NV :: struct { sType: StructureType, pNext: rawptr, - stage: PipelineStageFlags2KHR, + stage: PipelineStageFlags2, pCheckpointMarker: rawptr, } @@ -2815,12 +3264,6 @@ PhysicalDeviceShaderSubgroupUniformControlFlowFeaturesKHR :: struct { shaderSubgroupUniformControlFlow: b32, } -PhysicalDeviceZeroInitializeWorkgroupMemoryFeaturesKHR :: struct { - sType: StructureType, - pNext: rawptr, - shaderZeroInitializeWorkgroupMemory: b32, -} - PhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR :: struct { sType: StructureType, pNext: rawptr, @@ -2830,117 +3273,6 @@ PhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR :: struct { workgroupMemoryExplicitLayout16BitAccess: b32, } -BufferCopy2KHR :: struct { - sType: StructureType, - pNext: rawptr, - srcOffset: DeviceSize, - dstOffset: DeviceSize, - size: DeviceSize, -} - -CopyBufferInfo2KHR :: struct { - sType: StructureType, - pNext: rawptr, - srcBuffer: Buffer, - dstBuffer: Buffer, - regionCount: u32, - pRegions: [^]BufferCopy2KHR, -} - -ImageCopy2KHR :: struct { - sType: StructureType, - pNext: rawptr, - srcSubresource: ImageSubresourceLayers, - srcOffset: Offset3D, - dstSubresource: ImageSubresourceLayers, - dstOffset: Offset3D, - extent: Extent3D, -} - -CopyImageInfo2KHR :: struct { - sType: StructureType, - pNext: rawptr, - srcImage: Image, - srcImageLayout: ImageLayout, - dstImage: Image, - dstImageLayout: ImageLayout, - regionCount: u32, - pRegions: [^]ImageCopy2KHR, -} - -BufferImageCopy2KHR :: struct { - sType: StructureType, - pNext: rawptr, - bufferOffset: DeviceSize, - bufferRowLength: u32, - bufferImageHeight: u32, - imageSubresource: ImageSubresourceLayers, - imageOffset: Offset3D, - imageExtent: Extent3D, -} - -CopyBufferToImageInfo2KHR :: struct { - sType: StructureType, - pNext: rawptr, - srcBuffer: Buffer, - dstImage: Image, - dstImageLayout: ImageLayout, - regionCount: u32, - pRegions: [^]BufferImageCopy2KHR, -} - -CopyImageToBufferInfo2KHR :: struct { - sType: StructureType, - pNext: rawptr, - srcImage: Image, - srcImageLayout: ImageLayout, - dstBuffer: Buffer, - regionCount: u32, - pRegions: [^]BufferImageCopy2KHR, -} - -ImageBlit2KHR :: struct { - sType: StructureType, - pNext: rawptr, - srcSubresource: ImageSubresourceLayers, - srcOffsets: [2]Offset3D, - dstSubresource: ImageSubresourceLayers, - dstOffsets: [2]Offset3D, -} - -BlitImageInfo2KHR :: struct { - sType: StructureType, - pNext: rawptr, - srcImage: Image, - srcImageLayout: ImageLayout, - dstImage: Image, - dstImageLayout: ImageLayout, - regionCount: u32, - pRegions: [^]ImageBlit2KHR, - filter: Filter, -} - -ImageResolve2KHR :: struct { - sType: StructureType, - pNext: rawptr, - srcSubresource: ImageSubresourceLayers, - srcOffset: Offset3D, - dstSubresource: ImageSubresourceLayers, - dstOffset: Offset3D, - extent: Extent3D, -} - -ResolveImageInfo2KHR :: struct { - sType: StructureType, - pNext: rawptr, - srcImage: Image, - srcImageLayout: ImageLayout, - dstImage: Image, - dstImageLayout: ImageLayout, - regionCount: u32, - pRegions: [^]ImageResolve2KHR, -} - DebugReportCallbackCreateInfoEXT :: struct { sType: StructureType, pNext: rawptr, @@ -3130,12 +3462,6 @@ ValidationFlagsEXT :: struct { pDisabledValidationChecks: [^]ValidationCheckEXT, } -PhysicalDeviceTextureCompressionASTCHDRFeaturesEXT :: struct { - sType: StructureType, - pNext: rawptr, - textureCompressionASTC_HDR: b32, -} - ImageViewASTCDecodeModeEXT :: struct { sType: StructureType, pNext: rawptr, @@ -3385,36 +3711,6 @@ DebugUtilsObjectTagInfoEXT :: struct { pTag: rawptr, } -PhysicalDeviceInlineUniformBlockFeaturesEXT :: struct { - sType: StructureType, - pNext: rawptr, - inlineUniformBlock: b32, - descriptorBindingInlineUniformBlockUpdateAfterBind: b32, -} - -PhysicalDeviceInlineUniformBlockPropertiesEXT :: struct { - sType: StructureType, - pNext: rawptr, - maxInlineUniformBlockSize: u32, - maxPerStageDescriptorInlineUniformBlocks: u32, - maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks: u32, - maxDescriptorSetInlineUniformBlocks: u32, - maxDescriptorSetUpdateAfterBindInlineUniformBlocks: u32, -} - -WriteDescriptorSetInlineUniformBlockEXT :: struct { - sType: StructureType, - pNext: rawptr, - dataSize: u32, - pData: rawptr, -} - -DescriptorPoolInlineUniformBlockCreateInfoEXT :: struct { - sType: StructureType, - pNext: rawptr, - maxInlineUniformBlockBindings: u32, -} - SampleLocationEXT :: struct { x: f32, y: f32, @@ -3570,6 +3866,19 @@ ImageDrmFormatModifierPropertiesEXT :: struct { drmFormatModifier: u64, } +DrmFormatModifierProperties2EXT :: struct { + drmFormatModifier: u64, + drmFormatModifierPlaneCount: u32, + drmFormatModifierTilingFeatures: FormatFeatureFlags2, +} + +DrmFormatModifierPropertiesList2EXT :: struct { + sType: StructureType, + pNext: rawptr, + drmFormatModifierCount: u32, + pDrmFormatModifierProperties: [^]DrmFormatModifierProperties2EXT, +} + ValidationCacheCreateInfoEXT :: struct { sType: StructureType, pNext: rawptr, @@ -3792,12 +4101,6 @@ FilterCubicImageViewImageFormatPropertiesEXT :: struct { filterCubicMinmax: b32, } -DeviceQueueGlobalPriorityCreateInfoEXT :: struct { - sType: StructureType, - pNext: rawptr, - globalPriority: QueueGlobalPriorityEXT, -} - ImportMemoryHostPointerInfoEXT :: struct { sType: StructureType, pNext: rawptr, @@ -3879,19 +4182,6 @@ PhysicalDeviceVertexAttributeDivisorFeaturesEXT :: struct { vertexAttributeInstanceRateZeroDivisor: b32, } -PipelineCreationFeedbackEXT :: struct { - flags: PipelineCreationFeedbackFlagsEXT, - duration: u64, -} - -PipelineCreationFeedbackCreateInfoEXT :: struct { - sType: StructureType, - pNext: rawptr, - pPipelineCreationFeedback: ^PipelineCreationFeedbackEXT, - pipelineStageCreationFeedbackCount: u32, - pPipelineStageCreationFeedbacks: [^]PipelineCreationFeedbackEXT, -} - PhysicalDeviceComputeShaderDerivativesFeaturesNV :: struct { sType: StructureType, pNext: rawptr, @@ -4067,28 +4357,6 @@ RenderPassFragmentDensityMapCreateInfoEXT :: struct { fragmentDensityMapAttachment: AttachmentReference, } -PhysicalDeviceSubgroupSizeControlFeaturesEXT :: struct { - sType: StructureType, - pNext: rawptr, - subgroupSizeControl: b32, - computeFullSubgroups: b32, -} - -PhysicalDeviceSubgroupSizeControlPropertiesEXT :: struct { - sType: StructureType, - pNext: rawptr, - minSubgroupSize: u32, - maxSubgroupSize: u32, - maxComputeWorkgroupSubgroups: u32, - requiredSubgroupSizeStages: ShaderStageFlags, -} - -PipelineShaderStageRequiredSubgroupSizeCreateInfoEXT :: struct { - sType: StructureType, - pNext: rawptr, - requiredSubgroupSize: u32, -} - PhysicalDeviceShaderCoreProperties2AMD :: struct { sType: StructureType, pNext: rawptr, @@ -4148,16 +4416,6 @@ BufferDeviceAddressCreateInfoEXT :: struct { deviceAddress: DeviceAddress, } -PhysicalDeviceToolPropertiesEXT :: struct { - sType: StructureType, - pNext: rawptr, - name: [MAX_EXTENSION_NAME_SIZE]byte, - version: [MAX_EXTENSION_NAME_SIZE]byte, - purposes: ToolPurposeFlagsEXT, - description: [MAX_DESCRIPTION_SIZE]byte, - layer: [MAX_EXTENSION_NAME_SIZE]byte, -} - ValidationFeaturesEXT :: struct { sType: StructureType, pNext: rawptr, @@ -4327,12 +4585,6 @@ PhysicalDeviceShaderAtomicFloat2FeaturesEXT :: struct { sparseImageFloat32AtomicMinMax: b32, } -PhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT :: struct { - sType: StructureType, - pNext: rawptr, - shaderDemoteToHelperInvocation: b32, -} - PhysicalDeviceDeviceGeneratedCommandsPropertiesNV :: struct { sType: StructureType, pNext: rawptr, @@ -4472,15 +4724,6 @@ PhysicalDeviceTexelBufferAlignmentFeaturesEXT :: struct { texelBufferAlignment: b32, } -PhysicalDeviceTexelBufferAlignmentPropertiesEXT :: struct { - sType: StructureType, - pNext: rawptr, - storageTexelBufferOffsetAlignmentBytes: DeviceSize, - storageTexelBufferOffsetSingleTexelAlignment: b32, - uniformTexelBufferOffsetAlignmentBytes: DeviceSize, - uniformTexelBufferOffsetSingleTexelAlignment: b32, -} - RenderPassTransformBeginInfoQCOM :: struct { sType: StructureType, pNext: rawptr, @@ -4555,30 +4798,6 @@ PhysicalDeviceCustomBorderColorFeaturesEXT :: struct { customBorderColorWithoutFormat: b32, } -PhysicalDevicePrivateDataFeaturesEXT :: struct { - sType: StructureType, - pNext: rawptr, - privateData: b32, -} - -DevicePrivateDataCreateInfoEXT :: struct { - sType: StructureType, - pNext: rawptr, - privateDataSlotRequestCount: u32, -} - -PrivateDataSlotCreateInfoEXT :: struct { - sType: StructureType, - pNext: rawptr, - flags: PrivateDataSlotCreateFlagsEXT, -} - -PhysicalDevicePipelineCreationCacheControlFeaturesEXT :: struct { - sType: StructureType, - pNext: rawptr, - pipelineCreationCacheControl: b32, -} - PhysicalDeviceDiagnosticsConfigFeaturesNV :: struct { sType: StructureType, pNext: rawptr, @@ -4591,6 +4810,25 @@ DeviceDiagnosticsConfigCreateInfoNV :: struct { flags: DeviceDiagnosticsConfigFlagsNV, } +PhysicalDeviceGraphicsPipelineLibraryFeaturesEXT :: struct { + sType: StructureType, + pNext: rawptr, + graphicsPipelineLibrary: b32, +} + +PhysicalDeviceGraphicsPipelineLibraryPropertiesEXT :: struct { + sType: StructureType, + pNext: rawptr, + graphicsPipelineLibraryFastLinking: b32, + graphicsPipelineLibraryIndependentInterpolationDecoration: b32, +} + +GraphicsPipelineLibraryCreateInfoEXT :: struct { + sType: StructureType, + pNext: rawptr, + flags: GraphicsPipelineLibraryFlagsEXT, +} + PhysicalDeviceFragmentShadingRateEnumsFeaturesNV :: struct { sType: StructureType, pNext: rawptr, @@ -4708,12 +4946,6 @@ CopyCommandTransformInfoQCOM :: struct { transform: SurfaceTransformFlagsKHR, } -PhysicalDeviceImageRobustnessFeaturesEXT :: struct { - sType: StructureType, - pNext: rawptr, - robustImageAccess: b32, -} - PhysicalDevice4444FormatsFeaturesEXT :: struct { sType: StructureType, pNext: rawptr, @@ -4721,6 +4953,20 @@ PhysicalDevice4444FormatsFeaturesEXT :: struct { formatA4B4G4R4: b32, } +PhysicalDeviceRasterizationOrderAttachmentAccessFeaturesARM :: struct { + sType: StructureType, + pNext: rawptr, + rasterizationOrderColorAttachmentAccess: b32, + rasterizationOrderDepthAttachmentAccess: b32, + rasterizationOrderStencilAttachmentAccess: b32, +} + +PhysicalDeviceRGBA10X6FormatsFeaturesEXT :: struct { + sType: StructureType, + pNext: rawptr, + formatRgba10x6WithoutYCbCrSampler: b32, +} + PhysicalDeviceMutableDescriptorTypeFeaturesVALVE :: struct { sType: StructureType, pNext: rawptr, @@ -4774,6 +5020,18 @@ PhysicalDeviceDrmPropertiesEXT :: struct { renderMinor: i64, } +PhysicalDeviceDepthClipControlFeaturesEXT :: struct { + sType: StructureType, + pNext: rawptr, + depthClipControl: b32, +} + +PipelineViewportDepthClipControlCreateInfoEXT :: struct { + sType: StructureType, + pNext: rawptr, + negativeOneToOne: b32, +} + PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT :: struct { sType: StructureType, pNext: rawptr, @@ -4840,17 +5098,24 @@ PipelineColorWriteCreateInfoEXT :: struct { pColorWriteEnables: [^]b32, } -PhysicalDeviceGlobalPriorityQueryFeaturesEXT :: struct { - sType: StructureType, - pNext: rawptr, - globalPriorityQuery: b32, +PhysicalDevicePrimitivesGeneratedQueryFeaturesEXT :: struct { + sType: StructureType, + pNext: rawptr, + primitivesGeneratedQuery: b32, + primitivesGeneratedQueryWithRasterizerDiscard: b32, + primitivesGeneratedQueryWithNonZeroStreams: b32, } -QueueFamilyGlobalPriorityPropertiesEXT :: struct { - sType: StructureType, - pNext: rawptr, - priorityCount: u32, - priorities: [MAX_GLOBAL_PRIORITY_SIZE_EXT]QueueGlobalPriorityEXT, +PhysicalDeviceImageViewMinLodFeaturesEXT :: struct { + sType: StructureType, + pNext: rawptr, + minLod: b32, +} + +ImageViewMinLodCreateInfoEXT :: struct { + sType: StructureType, + pNext: rawptr, + minLod: f32, } PhysicalDeviceMultiDrawFeaturesEXT :: struct { @@ -4876,12 +5141,78 @@ MultiDrawIndexedInfoEXT :: struct { vertexOffset: i32, } +PhysicalDeviceImage2DViewOf3DFeaturesEXT :: struct { + sType: StructureType, + pNext: rawptr, + image2DViewOf3D: b32, + sampler2DViewOf3D: b32, +} + +PhysicalDeviceBorderColorSwizzleFeaturesEXT :: struct { + sType: StructureType, + pNext: rawptr, + borderColorSwizzle: b32, + borderColorSwizzleFromImage: b32, +} + +SamplerBorderColorComponentMappingCreateInfoEXT :: struct { + sType: StructureType, + pNext: rawptr, + components: ComponentMapping, + srgb: b32, +} + PhysicalDevicePageableDeviceLocalMemoryFeaturesEXT :: struct { sType: StructureType, pNext: rawptr, pageableDeviceLocalMemory: b32, } +PhysicalDeviceDescriptorSetHostMappingFeaturesVALVE :: struct { + sType: StructureType, + pNext: rawptr, + descriptorSetHostMapping: b32, +} + +DescriptorSetBindingReferenceVALVE :: struct { + sType: StructureType, + pNext: rawptr, + descriptorSetLayout: DescriptorSetLayout, + binding: u32, +} + +DescriptorSetLayoutHostMappingInfoVALVE :: struct { + sType: StructureType, + pNext: rawptr, + descriptorOffset: int, + descriptorSize: u32, +} + +PhysicalDeviceFragmentDensityMapOffsetFeaturesQCOM :: struct { + sType: StructureType, + pNext: rawptr, + fragmentDensityMapOffset: b32, +} + +PhysicalDeviceFragmentDensityMapOffsetPropertiesQCOM :: struct { + sType: StructureType, + pNext: rawptr, + fragmentDensityOffsetGranularity: Extent2D, +} + +SubpassFragmentDensityMapOffsetEndInfoQCOM :: struct { + sType: StructureType, + pNext: rawptr, + fragmentDensityOffsetCount: u32, + pFragmentDensityOffsets: [^]Offset2D, +} + +PhysicalDeviceLinearColorAttachmentFeaturesNV :: struct { + sType: StructureType, + pNext: rawptr, + linearColorAttachment: b32, +} + DeviceOrHostAddressKHR :: struct #raw_union { deviceAddress: DeviceAddress, hostAddress: rawptr, @@ -5283,178 +5614,252 @@ IOSSurfaceCreateInfoMVK :: struct { } // Aliases -PhysicalDeviceVariablePointerFeatures :: PhysicalDeviceVariablePointersFeatures -PhysicalDeviceShaderDrawParameterFeatures :: PhysicalDeviceShaderDrawParametersFeatures -RenderPassMultiviewCreateInfoKHR :: RenderPassMultiviewCreateInfo -PhysicalDeviceMultiviewFeaturesKHR :: PhysicalDeviceMultiviewFeatures -PhysicalDeviceMultiviewPropertiesKHR :: PhysicalDeviceMultiviewProperties -PhysicalDeviceFeatures2KHR :: PhysicalDeviceFeatures2 -PhysicalDeviceProperties2KHR :: PhysicalDeviceProperties2 -FormatProperties2KHR :: FormatProperties2 -ImageFormatProperties2KHR :: ImageFormatProperties2 -PhysicalDeviceImageFormatInfo2KHR :: PhysicalDeviceImageFormatInfo2 -QueueFamilyProperties2KHR :: QueueFamilyProperties2 -PhysicalDeviceMemoryProperties2KHR :: PhysicalDeviceMemoryProperties2 -SparseImageFormatProperties2KHR :: SparseImageFormatProperties2 -PhysicalDeviceSparseImageFormatInfo2KHR :: PhysicalDeviceSparseImageFormatInfo2 -PeerMemoryFeatureFlagsKHR :: PeerMemoryFeatureFlags -PeerMemoryFeatureFlagKHR :: PeerMemoryFeatureFlag -MemoryAllocateFlagsKHR :: MemoryAllocateFlags -MemoryAllocateFlagKHR :: MemoryAllocateFlag -MemoryAllocateFlagsInfoKHR :: MemoryAllocateFlagsInfo -DeviceGroupRenderPassBeginInfoKHR :: DeviceGroupRenderPassBeginInfo -DeviceGroupCommandBufferBeginInfoKHR :: DeviceGroupCommandBufferBeginInfo -DeviceGroupSubmitInfoKHR :: DeviceGroupSubmitInfo -DeviceGroupBindSparseInfoKHR :: DeviceGroupBindSparseInfo -BindBufferMemoryDeviceGroupInfoKHR :: BindBufferMemoryDeviceGroupInfo -BindImageMemoryDeviceGroupInfoKHR :: BindImageMemoryDeviceGroupInfo -CommandPoolTrimFlagsKHR :: CommandPoolTrimFlags -PhysicalDeviceGroupPropertiesKHR :: PhysicalDeviceGroupProperties -DeviceGroupDeviceCreateInfoKHR :: DeviceGroupDeviceCreateInfo -ExternalMemoryHandleTypeFlagsKHR :: ExternalMemoryHandleTypeFlags -ExternalMemoryHandleTypeFlagKHR :: ExternalMemoryHandleTypeFlag -ExternalMemoryFeatureFlagsKHR :: ExternalMemoryFeatureFlags -ExternalMemoryFeatureFlagKHR :: ExternalMemoryFeatureFlag -ExternalMemoryPropertiesKHR :: ExternalMemoryProperties -PhysicalDeviceExternalImageFormatInfoKHR :: PhysicalDeviceExternalImageFormatInfo -ExternalImageFormatPropertiesKHR :: ExternalImageFormatProperties -PhysicalDeviceExternalBufferInfoKHR :: PhysicalDeviceExternalBufferInfo -ExternalBufferPropertiesKHR :: ExternalBufferProperties -PhysicalDeviceIDPropertiesKHR :: PhysicalDeviceIDProperties -ExternalMemoryImageCreateInfoKHR :: ExternalMemoryImageCreateInfo -ExternalMemoryBufferCreateInfoKHR :: ExternalMemoryBufferCreateInfo -ExportMemoryAllocateInfoKHR :: ExportMemoryAllocateInfo -ExternalSemaphoreHandleTypeFlagsKHR :: ExternalSemaphoreHandleTypeFlags -ExternalSemaphoreHandleTypeFlagKHR :: ExternalSemaphoreHandleTypeFlag -ExternalSemaphoreFeatureFlagsKHR :: ExternalSemaphoreFeatureFlags -ExternalSemaphoreFeatureFlagKHR :: ExternalSemaphoreFeatureFlag -PhysicalDeviceExternalSemaphoreInfoKHR :: PhysicalDeviceExternalSemaphoreInfo -ExternalSemaphorePropertiesKHR :: ExternalSemaphoreProperties -SemaphoreImportFlagsKHR :: SemaphoreImportFlags -SemaphoreImportFlagKHR :: SemaphoreImportFlag -ExportSemaphoreCreateInfoKHR :: ExportSemaphoreCreateInfo -PhysicalDeviceShaderFloat16Int8FeaturesKHR :: PhysicalDeviceShaderFloat16Int8Features -PhysicalDeviceFloat16Int8FeaturesKHR :: PhysicalDeviceShaderFloat16Int8Features -PhysicalDevice16BitStorageFeaturesKHR :: PhysicalDevice16BitStorageFeatures -DescriptorUpdateTemplateKHR :: DescriptorUpdateTemplate -DescriptorUpdateTemplateTypeKHR :: DescriptorUpdateTemplateType -DescriptorUpdateTemplateCreateFlagsKHR :: DescriptorUpdateTemplateCreateFlags -DescriptorUpdateTemplateEntryKHR :: DescriptorUpdateTemplateEntry -DescriptorUpdateTemplateCreateInfoKHR :: DescriptorUpdateTemplateCreateInfo -PhysicalDeviceImagelessFramebufferFeaturesKHR :: PhysicalDeviceImagelessFramebufferFeatures -FramebufferAttachmentsCreateInfoKHR :: FramebufferAttachmentsCreateInfo -FramebufferAttachmentImageInfoKHR :: FramebufferAttachmentImageInfo -RenderPassAttachmentBeginInfoKHR :: RenderPassAttachmentBeginInfo -RenderPassCreateInfo2KHR :: RenderPassCreateInfo2 -AttachmentDescription2KHR :: AttachmentDescription2 -AttachmentReference2KHR :: AttachmentReference2 -SubpassDescription2KHR :: SubpassDescription2 -SubpassDependency2KHR :: SubpassDependency2 -SubpassBeginInfoKHR :: SubpassBeginInfo -SubpassEndInfoKHR :: SubpassEndInfo -ExternalFenceHandleTypeFlagsKHR :: ExternalFenceHandleTypeFlags -ExternalFenceHandleTypeFlagKHR :: ExternalFenceHandleTypeFlag -ExternalFenceFeatureFlagsKHR :: ExternalFenceFeatureFlags -ExternalFenceFeatureFlagKHR :: ExternalFenceFeatureFlag -PhysicalDeviceExternalFenceInfoKHR :: PhysicalDeviceExternalFenceInfo -ExternalFencePropertiesKHR :: ExternalFenceProperties -FenceImportFlagsKHR :: FenceImportFlags -FenceImportFlagKHR :: FenceImportFlag -ExportFenceCreateInfoKHR :: ExportFenceCreateInfo -PointClippingBehaviorKHR :: PointClippingBehavior -TessellationDomainOriginKHR :: TessellationDomainOrigin -PhysicalDevicePointClippingPropertiesKHR :: PhysicalDevicePointClippingProperties -RenderPassInputAttachmentAspectCreateInfoKHR :: RenderPassInputAttachmentAspectCreateInfo -InputAttachmentAspectReferenceKHR :: InputAttachmentAspectReference -ImageViewUsageCreateInfoKHR :: ImageViewUsageCreateInfo -PipelineTessellationDomainOriginStateCreateInfoKHR :: PipelineTessellationDomainOriginStateCreateInfo -PhysicalDeviceVariablePointerFeaturesKHR :: PhysicalDeviceVariablePointersFeatures -PhysicalDeviceVariablePointersFeaturesKHR :: PhysicalDeviceVariablePointersFeatures -MemoryDedicatedRequirementsKHR :: MemoryDedicatedRequirements -MemoryDedicatedAllocateInfoKHR :: MemoryDedicatedAllocateInfo -BufferMemoryRequirementsInfo2KHR :: BufferMemoryRequirementsInfo2 -ImageMemoryRequirementsInfo2KHR :: ImageMemoryRequirementsInfo2 -ImageSparseMemoryRequirementsInfo2KHR :: ImageSparseMemoryRequirementsInfo2 -MemoryRequirements2KHR :: MemoryRequirements2 -SparseImageMemoryRequirements2KHR :: SparseImageMemoryRequirements2 -ImageFormatListCreateInfoKHR :: ImageFormatListCreateInfo -SamplerYcbcrConversionKHR :: SamplerYcbcrConversion -SamplerYcbcrModelConversionKHR :: SamplerYcbcrModelConversion -SamplerYcbcrRangeKHR :: SamplerYcbcrRange -ChromaLocationKHR :: ChromaLocation -SamplerYcbcrConversionCreateInfoKHR :: SamplerYcbcrConversionCreateInfo -SamplerYcbcrConversionInfoKHR :: SamplerYcbcrConversionInfo -BindImagePlaneMemoryInfoKHR :: BindImagePlaneMemoryInfo -ImagePlaneMemoryRequirementsInfoKHR :: ImagePlaneMemoryRequirementsInfo -PhysicalDeviceSamplerYcbcrConversionFeaturesKHR :: PhysicalDeviceSamplerYcbcrConversionFeatures -SamplerYcbcrConversionImageFormatPropertiesKHR :: SamplerYcbcrConversionImageFormatProperties -BindBufferMemoryInfoKHR :: BindBufferMemoryInfo -BindImageMemoryInfoKHR :: BindImageMemoryInfo -PhysicalDeviceMaintenance3PropertiesKHR :: PhysicalDeviceMaintenance3Properties -DescriptorSetLayoutSupportKHR :: DescriptorSetLayoutSupport -PhysicalDeviceShaderSubgroupExtendedTypesFeaturesKHR :: PhysicalDeviceShaderSubgroupExtendedTypesFeatures -PhysicalDevice8BitStorageFeaturesKHR :: PhysicalDevice8BitStorageFeatures -PhysicalDeviceShaderAtomicInt64FeaturesKHR :: PhysicalDeviceShaderAtomicInt64Features -DriverIdKHR :: DriverId -ConformanceVersionKHR :: ConformanceVersion -PhysicalDeviceDriverPropertiesKHR :: PhysicalDeviceDriverProperties -ShaderFloatControlsIndependenceKHR :: ShaderFloatControlsIndependence -PhysicalDeviceFloatControlsPropertiesKHR :: PhysicalDeviceFloatControlsProperties -ResolveModeFlagKHR :: ResolveModeFlag -ResolveModeFlagsKHR :: ResolveModeFlags -SubpassDescriptionDepthStencilResolveKHR :: SubpassDescriptionDepthStencilResolve -PhysicalDeviceDepthStencilResolvePropertiesKHR :: PhysicalDeviceDepthStencilResolveProperties -SemaphoreTypeKHR :: SemaphoreType -SemaphoreWaitFlagKHR :: SemaphoreWaitFlag -SemaphoreWaitFlagsKHR :: SemaphoreWaitFlags -PhysicalDeviceTimelineSemaphoreFeaturesKHR :: PhysicalDeviceTimelineSemaphoreFeatures -PhysicalDeviceTimelineSemaphorePropertiesKHR :: PhysicalDeviceTimelineSemaphoreProperties -SemaphoreTypeCreateInfoKHR :: SemaphoreTypeCreateInfo -TimelineSemaphoreSubmitInfoKHR :: TimelineSemaphoreSubmitInfo -SemaphoreWaitInfoKHR :: SemaphoreWaitInfo -SemaphoreSignalInfoKHR :: SemaphoreSignalInfo -PhysicalDeviceVulkanMemoryModelFeaturesKHR :: PhysicalDeviceVulkanMemoryModelFeatures -PhysicalDeviceSeparateDepthStencilLayoutsFeaturesKHR :: PhysicalDeviceSeparateDepthStencilLayoutsFeatures -AttachmentReferenceStencilLayoutKHR :: AttachmentReferenceStencilLayout -AttachmentDescriptionStencilLayoutKHR :: AttachmentDescriptionStencilLayout -PhysicalDeviceUniformBufferStandardLayoutFeaturesKHR :: PhysicalDeviceUniformBufferStandardLayoutFeatures -PhysicalDeviceBufferDeviceAddressFeaturesKHR :: PhysicalDeviceBufferDeviceAddressFeatures -BufferDeviceAddressInfoKHR :: BufferDeviceAddressInfo -BufferOpaqueCaptureAddressCreateInfoKHR :: BufferOpaqueCaptureAddressCreateInfo -MemoryOpaqueCaptureAddressAllocateInfoKHR :: MemoryOpaqueCaptureAddressAllocateInfo -DeviceMemoryOpaqueCaptureAddressInfoKHR :: DeviceMemoryOpaqueCaptureAddressInfo -PipelineStageFlags2KHR :: Flags64 -PipelineStageFlag2KHR :: Flags64 -AccessFlags2KHR :: Flags64 -AccessFlag2KHR :: Flags64 -SamplerReductionModeEXT :: SamplerReductionMode -SamplerReductionModeCreateInfoEXT :: SamplerReductionModeCreateInfo -PhysicalDeviceSamplerFilterMinmaxPropertiesEXT :: PhysicalDeviceSamplerFilterMinmaxProperties -DescriptorBindingFlagEXT :: DescriptorBindingFlag -DescriptorBindingFlagsEXT :: DescriptorBindingFlags -DescriptorSetLayoutBindingFlagsCreateInfoEXT :: DescriptorSetLayoutBindingFlagsCreateInfo -PhysicalDeviceDescriptorIndexingFeaturesEXT :: PhysicalDeviceDescriptorIndexingFeatures -PhysicalDeviceDescriptorIndexingPropertiesEXT :: PhysicalDeviceDescriptorIndexingProperties -DescriptorSetVariableDescriptorCountAllocateInfoEXT :: DescriptorSetVariableDescriptorCountAllocateInfo -DescriptorSetVariableDescriptorCountLayoutSupportEXT :: DescriptorSetVariableDescriptorCountLayoutSupport -RayTracingShaderGroupTypeNV :: RayTracingShaderGroupTypeKHR -GeometryTypeNV :: GeometryTypeKHR -AccelerationStructureTypeNV :: AccelerationStructureTypeKHR -CopyAccelerationStructureModeNV :: CopyAccelerationStructureModeKHR -GeometryFlagsNV :: GeometryFlagsKHR -GeometryFlagNV :: GeometryFlagKHR -GeometryInstanceFlagsNV :: GeometryInstanceFlagsKHR -GeometryInstanceFlagNV :: GeometryInstanceFlagKHR -BuildAccelerationStructureFlagsNV :: BuildAccelerationStructureFlagsKHR -BuildAccelerationStructureFlagNV :: BuildAccelerationStructureFlagKHR -TransformMatrixNV :: TransformMatrixKHR -AabbPositionsNV :: AabbPositionsKHR -AccelerationStructureInstanceNV :: AccelerationStructureInstanceKHR -QueryPoolCreateInfoINTEL :: QueryPoolPerformanceQueryCreateInfoINTEL -PhysicalDeviceScalarBlockLayoutFeaturesEXT :: PhysicalDeviceScalarBlockLayoutFeatures -PhysicalDeviceBufferAddressFeaturesEXT :: PhysicalDeviceBufferDeviceAddressFeaturesEXT -BufferDeviceAddressInfoEXT :: BufferDeviceAddressInfo -ImageStencilUsageCreateInfoEXT :: ImageStencilUsageCreateInfo -PhysicalDeviceHostQueryResetFeaturesEXT :: PhysicalDeviceHostQueryResetFeatures +PhysicalDeviceVariablePointerFeatures :: PhysicalDeviceVariablePointersFeatures +PhysicalDeviceShaderDrawParameterFeatures :: PhysicalDeviceShaderDrawParametersFeatures +PipelineStageFlags2 :: Flags64 +PipelineStageFlag2 :: Flags64 +AccessFlags2 :: Flags64 +AccessFlag2 :: Flags64 +FormatFeatureFlags2 :: Flags64 +FormatFeatureFlag2 :: Flags64 +RenderingFlagsKHR :: RenderingFlags +RenderingFlagKHR :: RenderingFlag +RenderingInfoKHR :: RenderingInfo +RenderingAttachmentInfoKHR :: RenderingAttachmentInfo +PipelineRenderingCreateInfoKHR :: PipelineRenderingCreateInfo +PhysicalDeviceDynamicRenderingFeaturesKHR :: PhysicalDeviceDynamicRenderingFeatures +CommandBufferInheritanceRenderingInfoKHR :: CommandBufferInheritanceRenderingInfo +AttachmentSampleCountInfoNV :: AttachmentSampleCountInfoAMD +RenderPassMultiviewCreateInfoKHR :: RenderPassMultiviewCreateInfo +PhysicalDeviceMultiviewFeaturesKHR :: PhysicalDeviceMultiviewFeatures +PhysicalDeviceMultiviewPropertiesKHR :: PhysicalDeviceMultiviewProperties +PhysicalDeviceFeatures2KHR :: PhysicalDeviceFeatures2 +PhysicalDeviceProperties2KHR :: PhysicalDeviceProperties2 +FormatProperties2KHR :: FormatProperties2 +ImageFormatProperties2KHR :: ImageFormatProperties2 +PhysicalDeviceImageFormatInfo2KHR :: PhysicalDeviceImageFormatInfo2 +QueueFamilyProperties2KHR :: QueueFamilyProperties2 +PhysicalDeviceMemoryProperties2KHR :: PhysicalDeviceMemoryProperties2 +SparseImageFormatProperties2KHR :: SparseImageFormatProperties2 +PhysicalDeviceSparseImageFormatInfo2KHR :: PhysicalDeviceSparseImageFormatInfo2 +PeerMemoryFeatureFlagsKHR :: PeerMemoryFeatureFlags +PeerMemoryFeatureFlagKHR :: PeerMemoryFeatureFlag +MemoryAllocateFlagsKHR :: MemoryAllocateFlags +MemoryAllocateFlagKHR :: MemoryAllocateFlag +MemoryAllocateFlagsInfoKHR :: MemoryAllocateFlagsInfo +DeviceGroupRenderPassBeginInfoKHR :: DeviceGroupRenderPassBeginInfo +DeviceGroupCommandBufferBeginInfoKHR :: DeviceGroupCommandBufferBeginInfo +DeviceGroupSubmitInfoKHR :: DeviceGroupSubmitInfo +DeviceGroupBindSparseInfoKHR :: DeviceGroupBindSparseInfo +BindBufferMemoryDeviceGroupInfoKHR :: BindBufferMemoryDeviceGroupInfo +BindImageMemoryDeviceGroupInfoKHR :: BindImageMemoryDeviceGroupInfo +CommandPoolTrimFlagsKHR :: CommandPoolTrimFlags +PhysicalDeviceGroupPropertiesKHR :: PhysicalDeviceGroupProperties +DeviceGroupDeviceCreateInfoKHR :: DeviceGroupDeviceCreateInfo +ExternalMemoryHandleTypeFlagsKHR :: ExternalMemoryHandleTypeFlags +ExternalMemoryHandleTypeFlagKHR :: ExternalMemoryHandleTypeFlag +ExternalMemoryFeatureFlagsKHR :: ExternalMemoryFeatureFlags +ExternalMemoryFeatureFlagKHR :: ExternalMemoryFeatureFlag +ExternalMemoryPropertiesKHR :: ExternalMemoryProperties +PhysicalDeviceExternalImageFormatInfoKHR :: PhysicalDeviceExternalImageFormatInfo +ExternalImageFormatPropertiesKHR :: ExternalImageFormatProperties +PhysicalDeviceExternalBufferInfoKHR :: PhysicalDeviceExternalBufferInfo +ExternalBufferPropertiesKHR :: ExternalBufferProperties +PhysicalDeviceIDPropertiesKHR :: PhysicalDeviceIDProperties +ExternalMemoryImageCreateInfoKHR :: ExternalMemoryImageCreateInfo +ExternalMemoryBufferCreateInfoKHR :: ExternalMemoryBufferCreateInfo +ExportMemoryAllocateInfoKHR :: ExportMemoryAllocateInfo +ExternalSemaphoreHandleTypeFlagsKHR :: ExternalSemaphoreHandleTypeFlags +ExternalSemaphoreHandleTypeFlagKHR :: ExternalSemaphoreHandleTypeFlag +ExternalSemaphoreFeatureFlagsKHR :: ExternalSemaphoreFeatureFlags +ExternalSemaphoreFeatureFlagKHR :: ExternalSemaphoreFeatureFlag +PhysicalDeviceExternalSemaphoreInfoKHR :: PhysicalDeviceExternalSemaphoreInfo +ExternalSemaphorePropertiesKHR :: ExternalSemaphoreProperties +SemaphoreImportFlagsKHR :: SemaphoreImportFlags +SemaphoreImportFlagKHR :: SemaphoreImportFlag +ExportSemaphoreCreateInfoKHR :: ExportSemaphoreCreateInfo +PhysicalDeviceShaderFloat16Int8FeaturesKHR :: PhysicalDeviceShaderFloat16Int8Features +PhysicalDeviceFloat16Int8FeaturesKHR :: PhysicalDeviceShaderFloat16Int8Features +PhysicalDevice16BitStorageFeaturesKHR :: PhysicalDevice16BitStorageFeatures +DescriptorUpdateTemplateKHR :: DescriptorUpdateTemplate +DescriptorUpdateTemplateTypeKHR :: DescriptorUpdateTemplateType +DescriptorUpdateTemplateCreateFlagsKHR :: DescriptorUpdateTemplateCreateFlags +DescriptorUpdateTemplateEntryKHR :: DescriptorUpdateTemplateEntry +DescriptorUpdateTemplateCreateInfoKHR :: DescriptorUpdateTemplateCreateInfo +PhysicalDeviceImagelessFramebufferFeaturesKHR :: PhysicalDeviceImagelessFramebufferFeatures +FramebufferAttachmentsCreateInfoKHR :: FramebufferAttachmentsCreateInfo +FramebufferAttachmentImageInfoKHR :: FramebufferAttachmentImageInfo +RenderPassAttachmentBeginInfoKHR :: RenderPassAttachmentBeginInfo +RenderPassCreateInfo2KHR :: RenderPassCreateInfo2 +AttachmentDescription2KHR :: AttachmentDescription2 +AttachmentReference2KHR :: AttachmentReference2 +SubpassDescription2KHR :: SubpassDescription2 +SubpassDependency2KHR :: SubpassDependency2 +SubpassBeginInfoKHR :: SubpassBeginInfo +SubpassEndInfoKHR :: SubpassEndInfo +ExternalFenceHandleTypeFlagsKHR :: ExternalFenceHandleTypeFlags +ExternalFenceHandleTypeFlagKHR :: ExternalFenceHandleTypeFlag +ExternalFenceFeatureFlagsKHR :: ExternalFenceFeatureFlags +ExternalFenceFeatureFlagKHR :: ExternalFenceFeatureFlag +PhysicalDeviceExternalFenceInfoKHR :: PhysicalDeviceExternalFenceInfo +ExternalFencePropertiesKHR :: ExternalFenceProperties +FenceImportFlagsKHR :: FenceImportFlags +FenceImportFlagKHR :: FenceImportFlag +ExportFenceCreateInfoKHR :: ExportFenceCreateInfo +PointClippingBehaviorKHR :: PointClippingBehavior +TessellationDomainOriginKHR :: TessellationDomainOrigin +PhysicalDevicePointClippingPropertiesKHR :: PhysicalDevicePointClippingProperties +RenderPassInputAttachmentAspectCreateInfoKHR :: RenderPassInputAttachmentAspectCreateInfo +InputAttachmentAspectReferenceKHR :: InputAttachmentAspectReference +ImageViewUsageCreateInfoKHR :: ImageViewUsageCreateInfo +PipelineTessellationDomainOriginStateCreateInfoKHR :: PipelineTessellationDomainOriginStateCreateInfo +PhysicalDeviceVariablePointerFeaturesKHR :: PhysicalDeviceVariablePointersFeatures +PhysicalDeviceVariablePointersFeaturesKHR :: PhysicalDeviceVariablePointersFeatures +MemoryDedicatedRequirementsKHR :: MemoryDedicatedRequirements +MemoryDedicatedAllocateInfoKHR :: MemoryDedicatedAllocateInfo +BufferMemoryRequirementsInfo2KHR :: BufferMemoryRequirementsInfo2 +ImageMemoryRequirementsInfo2KHR :: ImageMemoryRequirementsInfo2 +ImageSparseMemoryRequirementsInfo2KHR :: ImageSparseMemoryRequirementsInfo2 +MemoryRequirements2KHR :: MemoryRequirements2 +SparseImageMemoryRequirements2KHR :: SparseImageMemoryRequirements2 +ImageFormatListCreateInfoKHR :: ImageFormatListCreateInfo +SamplerYcbcrConversionKHR :: SamplerYcbcrConversion +SamplerYcbcrModelConversionKHR :: SamplerYcbcrModelConversion +SamplerYcbcrRangeKHR :: SamplerYcbcrRange +ChromaLocationKHR :: ChromaLocation +SamplerYcbcrConversionCreateInfoKHR :: SamplerYcbcrConversionCreateInfo +SamplerYcbcrConversionInfoKHR :: SamplerYcbcrConversionInfo +BindImagePlaneMemoryInfoKHR :: BindImagePlaneMemoryInfo +ImagePlaneMemoryRequirementsInfoKHR :: ImagePlaneMemoryRequirementsInfo +PhysicalDeviceSamplerYcbcrConversionFeaturesKHR :: PhysicalDeviceSamplerYcbcrConversionFeatures +SamplerYcbcrConversionImageFormatPropertiesKHR :: SamplerYcbcrConversionImageFormatProperties +BindBufferMemoryInfoKHR :: BindBufferMemoryInfo +BindImageMemoryInfoKHR :: BindImageMemoryInfo +PhysicalDeviceMaintenance3PropertiesKHR :: PhysicalDeviceMaintenance3Properties +DescriptorSetLayoutSupportKHR :: DescriptorSetLayoutSupport +PhysicalDeviceShaderSubgroupExtendedTypesFeaturesKHR :: PhysicalDeviceShaderSubgroupExtendedTypesFeatures +PhysicalDevice8BitStorageFeaturesKHR :: PhysicalDevice8BitStorageFeatures +PhysicalDeviceShaderAtomicInt64FeaturesKHR :: PhysicalDeviceShaderAtomicInt64Features +DriverIdKHR :: DriverId +ConformanceVersionKHR :: ConformanceVersion +PhysicalDeviceDriverPropertiesKHR :: PhysicalDeviceDriverProperties +ShaderFloatControlsIndependenceKHR :: ShaderFloatControlsIndependence +PhysicalDeviceFloatControlsPropertiesKHR :: PhysicalDeviceFloatControlsProperties +ResolveModeFlagKHR :: ResolveModeFlag +ResolveModeFlagsKHR :: ResolveModeFlags +SubpassDescriptionDepthStencilResolveKHR :: SubpassDescriptionDepthStencilResolve +PhysicalDeviceDepthStencilResolvePropertiesKHR :: PhysicalDeviceDepthStencilResolveProperties +SemaphoreTypeKHR :: SemaphoreType +SemaphoreWaitFlagKHR :: SemaphoreWaitFlag +SemaphoreWaitFlagsKHR :: SemaphoreWaitFlags +PhysicalDeviceTimelineSemaphoreFeaturesKHR :: PhysicalDeviceTimelineSemaphoreFeatures +PhysicalDeviceTimelineSemaphorePropertiesKHR :: PhysicalDeviceTimelineSemaphoreProperties +SemaphoreTypeCreateInfoKHR :: SemaphoreTypeCreateInfo +TimelineSemaphoreSubmitInfoKHR :: TimelineSemaphoreSubmitInfo +SemaphoreWaitInfoKHR :: SemaphoreWaitInfo +SemaphoreSignalInfoKHR :: SemaphoreSignalInfo +PhysicalDeviceVulkanMemoryModelFeaturesKHR :: PhysicalDeviceVulkanMemoryModelFeatures +PhysicalDeviceShaderTerminateInvocationFeaturesKHR :: PhysicalDeviceShaderTerminateInvocationFeatures +PhysicalDeviceSeparateDepthStencilLayoutsFeaturesKHR :: PhysicalDeviceSeparateDepthStencilLayoutsFeatures +AttachmentReferenceStencilLayoutKHR :: AttachmentReferenceStencilLayout +AttachmentDescriptionStencilLayoutKHR :: AttachmentDescriptionStencilLayout +PhysicalDeviceUniformBufferStandardLayoutFeaturesKHR :: PhysicalDeviceUniformBufferStandardLayoutFeatures +PhysicalDeviceBufferDeviceAddressFeaturesKHR :: PhysicalDeviceBufferDeviceAddressFeatures +BufferDeviceAddressInfoKHR :: BufferDeviceAddressInfo +BufferOpaqueCaptureAddressCreateInfoKHR :: BufferOpaqueCaptureAddressCreateInfo +MemoryOpaqueCaptureAddressAllocateInfoKHR :: MemoryOpaqueCaptureAddressAllocateInfo +DeviceMemoryOpaqueCaptureAddressInfoKHR :: DeviceMemoryOpaqueCaptureAddressInfo +PhysicalDeviceShaderIntegerDotProductFeaturesKHR :: PhysicalDeviceShaderIntegerDotProductFeatures +PhysicalDeviceShaderIntegerDotProductPropertiesKHR :: PhysicalDeviceShaderIntegerDotProductProperties +PipelineStageFlags2KHR :: PipelineStageFlags2 +PipelineStageFlag2KHR :: PipelineStageFlag2 +AccessFlags2KHR :: AccessFlags2 +AccessFlag2KHR :: AccessFlag2 +SubmitFlagKHR :: SubmitFlag +SubmitFlagsKHR :: SubmitFlags +MemoryBarrier2KHR :: MemoryBarrier2 +BufferMemoryBarrier2KHR :: BufferMemoryBarrier2 +ImageMemoryBarrier2KHR :: ImageMemoryBarrier2 +DependencyInfoKHR :: DependencyInfo +SubmitInfo2KHR :: SubmitInfo2 +SemaphoreSubmitInfoKHR :: SemaphoreSubmitInfo +CommandBufferSubmitInfoKHR :: CommandBufferSubmitInfo +PhysicalDeviceSynchronization2FeaturesKHR :: PhysicalDeviceSynchronization2Features +PhysicalDeviceZeroInitializeWorkgroupMemoryFeaturesKHR :: PhysicalDeviceZeroInitializeWorkgroupMemoryFeatures +CopyBufferInfo2KHR :: CopyBufferInfo2 +CopyImageInfo2KHR :: CopyImageInfo2 +CopyBufferToImageInfo2KHR :: CopyBufferToImageInfo2 +CopyImageToBufferInfo2KHR :: CopyImageToBufferInfo2 +BlitImageInfo2KHR :: BlitImageInfo2 +ResolveImageInfo2KHR :: ResolveImageInfo2 +BufferCopy2KHR :: BufferCopy2 +ImageCopy2KHR :: ImageCopy2 +ImageBlit2KHR :: ImageBlit2 +BufferImageCopy2KHR :: BufferImageCopy2 +ImageResolve2KHR :: ImageResolve2 +FormatFeatureFlags2KHR :: FormatFeatureFlags2 +FormatFeatureFlag2KHR :: FormatFeatureFlag2 +FormatProperties3KHR :: FormatProperties3 +PhysicalDeviceMaintenance4FeaturesKHR :: PhysicalDeviceMaintenance4Features +PhysicalDeviceMaintenance4PropertiesKHR :: PhysicalDeviceMaintenance4Properties +DeviceBufferMemoryRequirementsKHR :: DeviceBufferMemoryRequirements +DeviceImageMemoryRequirementsKHR :: DeviceImageMemoryRequirements +PhysicalDeviceTextureCompressionASTCHDRFeaturesEXT :: PhysicalDeviceTextureCompressionASTCHDRFeatures +SamplerReductionModeEXT :: SamplerReductionMode +SamplerReductionModeCreateInfoEXT :: SamplerReductionModeCreateInfo +PhysicalDeviceSamplerFilterMinmaxPropertiesEXT :: PhysicalDeviceSamplerFilterMinmaxProperties +PhysicalDeviceInlineUniformBlockFeaturesEXT :: PhysicalDeviceInlineUniformBlockFeatures +PhysicalDeviceInlineUniformBlockPropertiesEXT :: PhysicalDeviceInlineUniformBlockProperties +WriteDescriptorSetInlineUniformBlockEXT :: WriteDescriptorSetInlineUniformBlock +DescriptorPoolInlineUniformBlockCreateInfoEXT :: DescriptorPoolInlineUniformBlockCreateInfo +DescriptorBindingFlagEXT :: DescriptorBindingFlag +DescriptorBindingFlagsEXT :: DescriptorBindingFlags +DescriptorSetLayoutBindingFlagsCreateInfoEXT :: DescriptorSetLayoutBindingFlagsCreateInfo +PhysicalDeviceDescriptorIndexingFeaturesEXT :: PhysicalDeviceDescriptorIndexingFeatures +PhysicalDeviceDescriptorIndexingPropertiesEXT :: PhysicalDeviceDescriptorIndexingProperties +DescriptorSetVariableDescriptorCountAllocateInfoEXT :: DescriptorSetVariableDescriptorCountAllocateInfo +DescriptorSetVariableDescriptorCountLayoutSupportEXT :: DescriptorSetVariableDescriptorCountLayoutSupport +RayTracingShaderGroupTypeNV :: RayTracingShaderGroupTypeKHR +GeometryTypeNV :: GeometryTypeKHR +AccelerationStructureTypeNV :: AccelerationStructureTypeKHR +CopyAccelerationStructureModeNV :: CopyAccelerationStructureModeKHR +GeometryFlagsNV :: GeometryFlagsKHR +GeometryFlagNV :: GeometryFlagKHR +GeometryInstanceFlagsNV :: GeometryInstanceFlagsKHR +GeometryInstanceFlagNV :: GeometryInstanceFlagKHR +BuildAccelerationStructureFlagsNV :: BuildAccelerationStructureFlagsKHR +BuildAccelerationStructureFlagNV :: BuildAccelerationStructureFlagKHR +TransformMatrixNV :: TransformMatrixKHR +AabbPositionsNV :: AabbPositionsKHR +AccelerationStructureInstanceNV :: AccelerationStructureInstanceKHR +QueueGlobalPriorityEXT :: QueueGlobalPriorityKHR +DeviceQueueGlobalPriorityCreateInfoEXT :: DeviceQueueGlobalPriorityCreateInfoKHR +PipelineCreationFeedbackFlagEXT :: PipelineCreationFeedbackFlag +PipelineCreationFeedbackFlagsEXT :: PipelineCreationFeedbackFlags +PipelineCreationFeedbackCreateInfoEXT :: PipelineCreationFeedbackCreateInfo +PipelineCreationFeedbackEXT :: PipelineCreationFeedback +QueryPoolCreateInfoINTEL :: QueryPoolPerformanceQueryCreateInfoINTEL +PhysicalDeviceScalarBlockLayoutFeaturesEXT :: PhysicalDeviceScalarBlockLayoutFeatures +PhysicalDeviceSubgroupSizeControlFeaturesEXT :: PhysicalDeviceSubgroupSizeControlFeatures +PhysicalDeviceSubgroupSizeControlPropertiesEXT :: PhysicalDeviceSubgroupSizeControlProperties +PipelineShaderStageRequiredSubgroupSizeCreateInfoEXT :: PipelineShaderStageRequiredSubgroupSizeCreateInfo +PhysicalDeviceBufferAddressFeaturesEXT :: PhysicalDeviceBufferDeviceAddressFeaturesEXT +BufferDeviceAddressInfoEXT :: BufferDeviceAddressInfo +ToolPurposeFlagEXT :: ToolPurposeFlag +ToolPurposeFlagsEXT :: ToolPurposeFlags +PhysicalDeviceToolPropertiesEXT :: PhysicalDeviceToolProperties +ImageStencilUsageCreateInfoEXT :: ImageStencilUsageCreateInfo +PhysicalDeviceHostQueryResetFeaturesEXT :: PhysicalDeviceHostQueryResetFeatures +PhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT :: PhysicalDeviceShaderDemoteToHelperInvocationFeatures +PhysicalDeviceTexelBufferAlignmentPropertiesEXT :: PhysicalDeviceTexelBufferAlignmentProperties +PrivateDataSlotEXT :: PrivateDataSlot +PrivateDataSlotCreateFlagsEXT :: PrivateDataSlotCreateFlags +PhysicalDevicePrivateDataFeaturesEXT :: PhysicalDevicePrivateDataFeatures +DevicePrivateDataCreateInfoEXT :: DevicePrivateDataCreateInfo +PrivateDataSlotCreateInfoEXT :: PrivateDataSlotCreateInfo +PhysicalDevicePipelineCreationCacheControlFeaturesEXT :: PhysicalDevicePipelineCreationCacheControlFeatures +PhysicalDeviceImageRobustnessFeaturesEXT :: PhysicalDeviceImageRobustnessFeatures +PhysicalDeviceGlobalPriorityQueryFeaturesEXT :: PhysicalDeviceGlobalPriorityQueryFeaturesKHR +QueueFamilyGlobalPriorityPropertiesEXT :: QueueFamilyGlobalPriorityPropertiesKHR diff --git a/vendor/wasm/README.md b/vendor/wasm/README.md index 8567f2eab..d0b0a2f6f 100644 --- a/vendor/wasm/README.md +++ b/vendor/wasm/README.md @@ -4,56 +4,12 @@ This directory is for use when targeting the `js_wasm32` target and the packages The `js_wasm32` target assumes that the WASM output will be ran within a web browser rather than a standalone VM. In the VM cases, either `wasi_wasm32` or `freestanding_wasm32` should be used accordingly. -## Example +## Example for `js_wasm32` ```js -import {WasmMemoryInterface, odinSetupDefaultImports} from "./js/runtime.js"; -import {WebGLInterface} from "./WebGL/runtime.js"; - -const runWasm = async (wasm_path, webglCanvasElement, consoleElement) => { - let wasmMemoryInterface = new WasmMemoryInterface(); - - let imports = odinSetupDefaultImports(wasmMemoryInterface, consoleElement); - - if (webglCanvasElement !== undefined) { - let gl_context = new WebGLInterface( - wasmMemoryInterface, - webglCanvasElement, - {antialias: false}, - ); - if (!gl_context.ctx) { - return "WebGL is not available."; - } - imports["webgl"] = gl_context.getWebGL1Interface() - imports["webgl2"] = gl_context.getWebGL2Interface() - } - - const response = await fetch(wasm_path); - const file = await response.arrayBuffer(); - const wasm = await WebAssembly.instantiate(file, imports); - const exports = wasm.instance.exports; - wasmMemoryInterface.setMemory(exports.memory); - - exports._start(); - - if (exports.step) { - const odin_ctx = exports.default_context_ptr(); - - let prevTimeStamp = undefined; - const step = (currTimeStamp) => { - if (prevTimeStamp == undefined) { - prevTimeStamp = currTimeStamp; - } - - const dt = (currTimeStamp - prevTimeStamp)*0.001; - prevTimeStamp = currTimeStamp; - exports.step(dt, odin_ctx); - window.requestAnimationFrame(step); - }; - - window.requestAnimationFrame(step); - } - - return; -}; -``` \ No newline at end of file + + + +``` diff --git a/vendor/wasm/WebGL/runtime.js b/vendor/wasm/WebGL/runtime.js deleted file mode 100644 index 3dc5186ca..000000000 --- a/vendor/wasm/WebGL/runtime.js +++ /dev/null @@ -1,1034 +0,0 @@ -class WebGLInterface { - constructor(wasmMemoryInterface, canvasElement, contextSettings) { - this.wasmMemoryInterface = wasmMemoryInterface; - this.ctx = null; - this.ctx_version = 1; - this.counter = 1; - this.lastError = 0; - this.buffers = []; - this.mappedBuffers = {}; - this.programs = []; - this.framebuffers = []; - this.renderbuffers = []; - this.textures = []; - this.uniforms = []; - this.shaders = []; - this.vaos = []; - this.contexts = []; - this.currentContext = null; - this.offscreenCanvases = {}; - this.timerQueriesEXT = []; - this.queries = []; - this.samplers = []; - this.transformFeedbacks = []; - this.syncs = []; - this.programInfos = {}; - - if (contextSettings === undefined) { - contextSettings = {antialias: false}; - } - - this.ctx = canvasElement.getContext("webgl2", contextSettings) || canvasElement.getContext("webgl", contextSettings); - if (!this.ctx) { - return; - } - if (this.ctx.getParameter(0x1F02).indexOf("WebGL 2.0") !== -1) { - this.ctx_version = 2.0; - } else { - this.ctx_version = 1.0; - } - } - - get mem() { - return this.wasmMemoryInterface - } - - assertWebGL2() { - if (this.ctx_version < 2) { - throw new Error("WebGL2 procedure called in a canvas without a WebGL2 context"); - } - } - getNewId(table) { - for (var ret = this.counter++, i = table.length; i < ret; i++) { - table[i] = null; - } - return ret; - } - recordError(errorCode) { - this.lastError || (this.lastError = errorCode); - } - populateUniformTable(program) { - let p = this.programs[program]; - this.programInfos[program] = { - uniforms: {}, - maxUniformLength: 0, - maxAttributeLength: -1, - maxUniformBlockNameLength: -1, - }; - for (let ptable = this.programInfos[program], utable = ptable.uniforms, numUniforms = this.ctx.getProgramParameter(p, this.ctx.ACTIVE_UNIFORMS), i = 0; i < numUniforms; ++i) { - let u = this.ctx.getActiveUniform(p, i); - let name = u.name; - if (ptable.maxUniformLength = Math.max(ptable.maxUniformLength, name.length + 1), name.indexOf("]", name.length - 1) !== -1) { - name = name.slice(0, name.lastIndexOf("[")); - } - let loc = this.ctx.getUniformLocation(p, name); - if (loc !== null) { - let id = this.getNewId(this.uniforms); - utable[name] = [u.size, id], this.uniforms[id] = loc; - for (let j = 1; j < u.size; ++j) { - let n = name + "[" + j + "]"; - let loc = this.ctx.getUniformLocation(p, n); - let id = this.getNewId(this.uniforms); - this.uniforms[id] = loc; - } - } - } - } - getSource(shader, strings_ptr, strings_length) { - const STRING_SIZE = 2*4; - let source = ""; - for (let i = 0; i < strings_length; i++) { - let ptr = this.mem.loadPtr(strings_ptr + i*STRING_SIZE); - let len = this.mem.loadPtr(strings_ptr + i*STRING_SIZE + 4); - let str = this.mem.loadString(ptr, len); - source += str; - } - return source; - } - - getWebGL1Interface() { - return { - DrawingBufferWidth: () => this.ctx.drawingBufferWidth, - DrawingBufferHeight: () => this.ctx.drawingBufferHeight, - - IsExtensionSupported: (name_ptr, name_len) => { - let name = this.mem.loadString(name_ptr, name_len); - let extensions = this.ctx.getSupportedExtensions(); - return extensions.indexOf(name) !== -1 - }, - - - GetError: () => { - let err = this.lastError; - this.recordError(0); - if (err) { - return err; - } - return this.ctx.getError(); - }, - - GetWebGLVersion: (major_ptr, minor_ptr) => { - let version = this.ctx.getParameter(0x1F02); - if (version.indexOf("WebGL 2.0") !== -1) { - this.mem.storeI32(major_ptr, 2); - this.mem.storeI32(minor_ptr, 0); - return; - } - - this.mem.storeI32(major_ptr, 1); - this.mem.storeI32(minor_ptr, 0); - }, - GetESVersion: (major_ptr, minor_ptr) => { - let version = this.ctx.getParameter(0x1F02); - if (version.indexOf("OpenGL ES 3.0") !== -1) { - this.mem.storeI32(major_ptr, 3); - this.mem.storeI32(minor_ptr, 0); - return; - } - - this.mem.storeI32(major_ptr, 2); - this.mem.storeI32(minor_ptr, 0); - }, - - - ActiveTexture: (x) => { - this.ctx.activeTexture(x); - }, - AttachShader: (program, shader) => { - this.ctx.attachShader(this.programs[program], this.shaders[shader]); - }, - BindAttribLocation: (program, index, name_ptr, name_len) => { - let name = this.mem.loadString(name_ptr, name_len); - this.ctx.bindAttribLocation(this.programs[program], index, name) - }, - BindBuffer: (target, buffer) => { - let bufferObj = buffer ? this.buffers[buffer] : null; - if (target == 35051) { - this.ctx.currentPixelPackBufferBinding = buffer; - } else { - if (target == 35052) { - this.ctx.currentPixelUnpackBufferBinding = buffer; - } - this.ctx.bindBuffer(target, bufferObj) - } - }, - BindFramebuffer: (target, buffer) => { - // TODO: BindFramebuffer - }, - BindTexture: (target, texture) => { - this.ctx.bindTexture(target, texture ? this.textures[texture] : null) - }, - BlendColor: (red, green, blue, alpha) => { - this.ctx.blendColor(red, green, blue, alpha); - }, - BlendEquation: (mode) => { - this.ctx.blendEquation(mode); - }, - BlendFunc: (sfactor, dfactor) => { - this.ctx.blendFunc(sfactor, dfactor); - }, - BlendFuncSeparate: (srcRGB, dstRGB, srcAlpha, dstAlpha) => { - this.ctx.blendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha); - }, - - - BufferData: (target, size, data, usage) => { - if (data) { - this.ctx.bufferData(target, this.mem.loadBytes(data, size), usage); - } else { - this.ctx.bufferData(target, size, usage); - } - }, - BufferSubData: (target, offset, size, data) => { - if (data) { - this.ctx.bufferSubData(target, offset, this.mem.loadBytes(data, size)); - } else { - this.ctx.bufferSubData(target, offset, null); - } - }, - - - Clear: (x) => { - this.ctx.clear(x); - }, - ClearColor: (r, g, b, a) => { - this.ctx.clearColor(r, g, b, a); - }, - ClearDepth: (x) => { - this.ctx.clearDepth(x); - }, - ClearStencil: (x) => { - this.ctx.clearStencil(x); - }, - ColorMask: (r, g, b, a) => { - this.ctx.colorMask(!!r, !!g, !!b, !!a); - }, - CompileShader: (shader) => { - this.ctx.compileShader(this.shaders[shader]); - }, - - - CompressedTexImage2D: (target, level, internalformat, width, height, border, imageSize, data) => { - if (data) { - this.ctx.compressedTexImage2D(target, level, internalformat, width, height, border, this.mem.loadBytes(data, imageSize)); - } else { - this.ctx.compressedTexImage2D(target, level, internalformat, width, height, border, null); - } - }, - CompressedTexSubImage2D: (target, level, xoffset, yoffset, width, height, format, imageSize, data) => { - if (data) { - this.ctx.compressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, this.mem.loadBytes(data, imageSize)); - } else { - this.ctx.compressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, null); - } - }, - - CopyTexImage2D: (target, level, internalformat, x, y, width, height, border) => { - this.ctx.copyTexImage2D(target, level, internalformat, x, y, width, height, border); - }, - CopyTexSubImage2D: (target, level, xoffset, yoffset, x, y, width, height) => { - this.ctx.copyTexImage2D(target, level, xoffset, yoffset, x, y, width, height); - }, - - - CreateBuffer: () => { - let buffer = this.ctx.createBuffer(); - if (!buffer) { - this.recordError(1282); - return 0; - } - let id = this.getNewId(this.buffers); - buffer.name = id - this.buffers[id] = buffer; - return id; - }, - CreateFramebuffer: () => { - let buffer = this.ctx.createFramebuffer(); - let id = this.getNewId(this.framebuffers); - buffer.name = id - this.framebuffers[id] = buffer; - return id; - }, - CreateProgram: () => { - let program = this.ctx.createProgram(); - let id = this.getNewId(this.programs); - program.name = id; - this.programs[id] = program; - return id; - }, - CreateRenderbuffer: () => { - let buffer = this.ctx.createRenderbuffer(); - let id = this.getNewId(this.renderbuffers); - buffer.name = id; - this.renderbuffers[id] = buffer; - return id; - }, - CreateShader: (shaderType) => { - let shader = this.ctx.createShader(shaderType); - let id = this.getNewId(this.shaders); - shader.name = id; - this.shaders[id] = shader; - return id; - }, - CreateTexture: () => { - let texture = this.ctx.createTexture(); - if (!texture) { - this.recordError(1282) - return 0; - } - let id = this.getNewId(this.textures); - texture.name = id; - this.textures[id] = texture; - return id; - }, - - - CullFace: (mode) => { - this.ctx.cullFace(mode); - }, - - - DeleteBuffer: (id) => { - let obj = this.buffers[id]; - if (obj && id != 0) { - this.ctx.deleteBuffer(obj); - this.buffers[id] = null; - } - }, - DeleteFramebuffer: (id) => { - let obj = this.framebuffers[id]; - if (obj && id != 0) { - this.ctx.deleteFramebuffer(obj); - this.framebuffers[id] = null; - } - }, - DeleteProgram: (id) => { - let obj = this.programs[id]; - if (obj && id != 0) { - this.ctx.deleteProgram(obj); - this.programs[id] = null; - } - }, - DeleteRenderbuffer: (id) => { - let obj = this.renderbuffers[id]; - if (obj && id != 0) { - this.ctx.deleteRenderbuffer(obj); - this.renderbuffers[id] = null; - } - }, - DeleteShader: (id) => { - let obj = this.shaders[id]; - if (obj && id != 0) { - this.ctx.deleteShader(obj); - this.shaders[id] = null; - } - }, - DeleteTexture: (id) => { - let obj = this.textures[id]; - if (obj && id != 0) { - this.ctx.deleteTexture(obj); - this.textures[id] = null; - } - }, - - - DepthFunc: (func) => { - this.ctx.depthFunc(func); - }, - DepthMask: (flag) => { - this.ctx.depthMask(!!flag); - }, - DepthRange: (zNear, zFar) => { - this.ctx.depthRange(zNear, zFar); - }, - DetachShader: (program, shader) => { - this.ctx.detachShader(this.programs[program], this.shaders[shader]); - }, - Disable: (cap) => { - this.ctx.disable(cap); - }, - DisableVertexAttribArray: (index) => { - this.ctx.disableVertexAttribArray(index); - }, - DrawArrays: (mode, first, count) => { - this.ctx.drawArrays(mode, first, count); - }, - DrawElements: (mode, count, type, indices) => { - this.ctx.drawElements(mode, count, type, indices); - }, - - - Enable: (cap) => { - this.ctx.enable(cap); - }, - EnableVertexAttribArray: (index) => { - this.ctx.enableVertexAttribArray(index); - }, - Finish: () => { - this.ctx.finish(); - }, - Flush: () => { - this.ctx.flush(); - }, - FramebufferRenderBuffer: (target, attachment, renderbuffertarget, renderbuffer) => { - this.ctx.framebufferRenderBuffer(target, attachment, renderbuffertarget, this.renderbuffers[renderbuffer]); - }, - FramebufferTexture2D: (target, attachment, textarget, texture, level) => { - this.ctx.framebufferTexture2D(target, attachment, textarget, this.textures[texture], level); - }, - FrontFace: (mode) => { - this.ctx.frontFace(mode); - }, - - - GenerateMipmap: (target) => { - this.ctx.generateMipmap(target); - }, - - - GetAttribLocation: (program, name_ptr, name_len) => { - let name = this.mem.loadString(name_ptr, name_len); - return this.ctx.getAttribLocation(this.programs[program], name); - }, - - - - GetProgramParameter: (program, pname) => { - return this.ctx.getProgramParameter(this.programs[program], pname) - }, - GetProgramInfoLog: (program, buf_ptr, buf_len, length_ptr) => { - let log = this.ctx.getProgramInfoLog(this.programs[program]); - if (log === null) { - log = "(unknown error)"; - } - if (buf_len > 0 && buf_ptr) { - let n = Math.min(buf_len, log.length); - log = log.substring(0, n); - this.mem.loadBytes(buf_ptr, buf_len).set(new TextEncoder("utf-8").encode(log)) - - this.mem.storeInt(length_ptr, n); - } - }, - GetShaderInfoLog: (shader, buf_ptr, buf_len, length_ptr) => { - let log = this.ctx.getShaderInfoLog(this.shaders[shader]); - if (log === null) { - log = "(unknown error)"; - } - if (buf_len > 0 && buf_ptr) { - let n = Math.min(buf_len, log.length); - log = log.substring(0, n); - this.mem.loadBytes(buf_ptr, buf_len).set(new TextEncoder("utf-8").encode(log)) - - this.mem.storeInt(length_ptr, n); - } - }, - GetShaderiv: (shader, pname, p) => { - if (p) { - if (pname == 35716) { - let log = this.ctx.getShaderInfoLog(this.shaders[shader]); - if (log === null) { - log = "(unknown error)"; - } - this.mem.storeInt(p, log.length+1); - } else if (pname == 35720) { - let source = this.ctx.getShaderSource(this.shaders[shader]); - let sourceLength = (source === null || source.length == 0) ? 0 : source.length+1; - this.mem.storeInt(p, sourceLength); - } else { - let param = this.ctx.getShaderParameter(this.shaders[shader], pname); - this.mem.storeI32(p, param); - } - } else { - this.recordError(1281); - } - }, - - - GetUniformLocation: (program, name_ptr, name_len) => { - let name = this.mem.loadString(name_ptr, name_len); - let arrayOffset = 0; - if (name.indexOf("]", name.length - 1) !== -1) { - let ls = name.lastIndexOf("["), - arrayIndex = name.slice(ls + 1, -1); - if (arrayIndex.length > 0 && (arrayOffset = parseInt(arrayIndex)) < 0) { - return -1; - } - name = name.slice(0, ls) - } - var ptable = this.programInfos[program]; - if (!ptable) { - return -1; - } - var uniformInfo = ptable.uniforms[name]; - return (uniformInfo && arrayOffset < uniformInfo[0]) ? uniformInfo[1] + arrayOffset : -1 - }, - - - GetVertexAttribOffset: (index, pname) => { - return this.ctx.getVertexAttribOffset(index, pname); - }, - - - Hint: (target, mode) => { - this.ctx.hint(target, mode); - }, - - - IsBuffer: (buffer) => this.ctx.isBuffer(this.buffers[buffer]), - IsEnabled: (enabled) => this.ctx.isEnabled(this.enableds[enabled]), - IsFramebuffer: (framebuffer) => this.ctx.isFramebuffer(this.framebuffers[framebuffer]), - IsProgram: (program) => this.ctx.isProgram(this.programs[program]), - IsRenderbuffer: (renderbuffer) => this.ctx.isRenderbuffer(this.renderbuffers[renderbuffer]), - IsShader: (shader) => this.ctx.isShader(this.shaders[shader]), - IsTexture: (texture) => this.ctx.isTexture(this.textures[texture]), - - LineWidth: (width) => { - this.ctx.lineWidth(width); - }, - LinkProgram: (program) => { - this.ctx.linkProgram(this.programs[program]); - this.programInfos[program] = null; - this.populateUniformTable(program); - }, - PixelStorei: (pname, param) => { - this.ctx.pixelStorei(pname, param); - }, - PolygonOffset: (factor, units) => { - this.ctx.polygonOffset(factor, units); - }, - - - ReadnPixels: (x, y, width, height, format, type, bufSize, data) => { - this.ctx.readPixels(x, y, width, format, type, this.mem.loadBytes(data, bufSize)); - }, - RenderbufferStorage: (target, internalformat, width, height) => { - this.ctx.renderbufferStorage(target, internalformat, width, height); - }, - SampleCoverage: (value, invert) => { - this.ctx.sampleCoverage(value, !!invert); - }, - Scissor: (x, y, width, height) => { - this.ctx.scissor(x, y, width, height); - }, - ShaderSource: (shader, strings_ptr, strings_length) => { - let source = this.getSource(shader, strings_ptr, strings_length); - this.ctx.shaderSource(this.shaders[shader], source); - }, - - StencilFunc: (func, ref, mask) => { - this.ctx.stencilFunc(func, ref, mask); - }, - StencilFuncSeparate: (face, func, ref, mask) => { - this.ctx.stencilFuncSeparate(face, func, ref, mask); - }, - StencilMask: (mask) => { - this.ctx.stencilMask(mask); - }, - StencilMaskSeparate: (face, mask) => { - this.ctx.stencilMaskSeparate(face, mask); - }, - StencilOp: (fail, zfail, zpass) => { - this.ctx.stencilOp(fail, zfail, zpass); - }, - StencilOpSeparate: (face, fail, zfail, zpass) => { - this.ctx.stencilOpSeparate(face, fail, zfail, zpass); - }, - - - TexImage2D: (target, level, internalformat, width, height, border, format, type, size, data) => { - if (data) { - this.ctx.texImage2D(target, level, internalformat, width, height, border, format, type, this.mem.loadBytes(data, size)); - } else { - this.ctx.texImage2D(target, level, internalformat, width, height, border, format, type, null); - } - }, - TexParameterf: (target, pname, param) => { - this.ctx.texParameterf(target, pname, param); - }, - TexParameteri: (target, pname, param) => { - this.ctx.texParameteri(target, pname, param); - }, - TexSubImage2D: (target, level, xoffset, yoffset, width, height, format, type, size, data) => { - this.ctx.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, this.mem.loadBytes(data, size)); - }, - - - Uniform1f: (location, v0) => { this.ctx.uniform1f(this.uniforms[location], v0); }, - Uniform2f: (location, v0, v1) => { this.ctx.uniform2f(this.uniforms[location], v0, v1); }, - Uniform3f: (location, v0, v1, v2) => { this.ctx.uniform3f(this.uniforms[location], v0, v1, v2); }, - Uniform4f: (location, v0, v1, v2, v3) => { this.ctx.uniform4f(this.uniforms[location], v0, v1, v2, v3); }, - - Uniform1i: (location, v0) => { this.ctx.uniform1i(this.uniforms[location], v0); }, - Uniform2i: (location, v0, v1) => { this.ctx.uniform2i(this.uniforms[location], v0, v1); }, - Uniform3i: (location, v0, v1, v2) => { this.ctx.uniform3i(this.uniforms[location], v0, v1, v2); }, - Uniform4i: (location, v0, v1, v2, v3) => { this.ctx.uniform4i(this.uniforms[location], v0, v1, v2, v3); }, - - UniformMatrix2fv: (location, addr) => { - let array = this.mem.loadF32Array(addr, 2*2); - this.ctx.uniformMatrix4fv(this.uniforms[location], false, array); - }, - UniformMatrix3fv: (location, addr) => { - let array = this.mem.loadF32Array(addr, 3*3); - this.ctx.uniformMatrix4fv(this.uniforms[location], false, array); - }, - UniformMatrix4fv: (location, addr) => { - let array = this.mem.loadF32Array(addr, 4*4); - this.ctx.uniformMatrix4fv(this.uniforms[location], false, array); - }, - - UseProgram: (program) => { - this.ctx.useProgram(this.programs[program]); - }, - ValidateProgram: (program) => { - this.ctx.validateProgram(this.programs[program]); - }, - - - VertexAttrib1f: (index, x) => { - this.ctx.vertexAttrib1f(index, x); - }, - VertexAttrib2f: (index, x, y) => { - this.ctx.vertexAttrib2f(index, x, y); - }, - VertexAttrib3f: (index, x, y, z) => { - this.ctx.vertexAttrib3f(index, x, y, z); - }, - VertexAttrib4f: (index, x, y, z, w) => { - this.ctx.vertexAttrib4f(index, x, y, z, w); - }, - VertexAttribPointer: (index, size, type, normalized, stride, ptr) => { - this.ctx.vertexAttribPointer(index, size, type, !!normalized, stride, ptr); - }, - - Viewport: (x, y, w, h) => { - this.ctx.viewport(x, y, w, h); - }, - }; - } - - getWebGL2Interface() { - return { - /* Buffer objects */ - CopyBufferSubData: (readTarget, writeTarget, readOffset, writeOffset, size) => { - this.assertWebGL2(); - this.ctx.copyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size); - }, - GetBufferSubData: (target, srcByteOffset, dst_buffer_ptr, dst_buffer_len, dstOffset, length) => { - this.assertWebGL2(); - this.ctx.getBufferSubData(target, srcByteOffset, this.mem.loadBytes(dst_buffer_ptr, dst_buffer_len), dstOffset, length); - }, - - /* Framebuffer objects */ - BlitFramebuffer: (srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter) => { - this.assertWebGL2(); - this.ctx.glitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); - }, - FramebufferTextureLayer: (target, attachment, texture, level, layer) => { - this.assertWebGL2(); - this.ctx.framebufferTextureLayer(target, attachment, this.textures[texture], level, layer); - }, - InvalidateFramebuffer: (target, attachments_ptr, attachments_len) => { - this.assertWebGL2(); - let attachments = this.mem.loadU32Array(attachments_ptr, attachments_len); - this.ctx.invalidateFramebuffer(target, attachments); - }, - InvalidateSubFramebuffer: (target, attachments_ptr, attachments_len, x, y, width, height) => { - this.assertWebGL2(); - let attachments = this.mem.loadU32Array(attachments_ptr, attachments_len); - this.ctx.invalidateSubFramebuffer(target, attachments, x, y, width, height); - }, - ReadBuffer: (src) => { - this.assertWebGL2(); - this.ctx.readBuffer(src); - }, - - /* Renderbuffer objects */ - RenderbufferStorageMultisample: (target, samples, internalformat, width, height) => { - this.assertWebGL2(); - this.ctx.renderbufferStorageMultisample(target, samples, internalformat, width, height); - }, - - /* Texture objects */ - - TexStorage3D: (target, levels, internalformat, width, height, depth) => { - this.assertWebGL2(); - this.ctx.texStorage3D(target, level, internalformat, width, heigh, depth); - }, - TexImage3D: (target, level, internalformat, width, height, depth, border, format, type, size, data) => { - this.assertWebGL2(); - if (data) { - this.ctx.texImage3D(target, level, internalformat, width, height, depth, border, format, type, this.mem.loadBytes(data, size)); - } else { - this.ctx.texImage3D(target, level, internalformat, width, height, depth, border, format, type, null); - } - }, - TexSubImage3D: (target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, size, data) => { - this.assertWebGL2(); - this.ctx.texSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, this.mem.loadBytes(data, size)); - }, - CompressedTexImage3D: (target, level, internalformat, width, height, depth, border, imageSize, data) => { - this.assertWebGL2(); - if (data) { - this.ctx.compressedTexImage3D(target, level, internalformat, width, height, depth, border, this.mem.loadBytes(data, imageSize)); - } else { - this.ctx.compressedTexImage3D(target, level, internalformat, width, height, depth, border, null); - } - }, - CompressedTexSubImage3D: (target, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, data) => { - this.assertWebGL2(); - if (data) { - this.ctx.compressedTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, this.mem.loadBytes(data, imageSize)); - } else { - this.ctx.compressedTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, null); - } - }, - - CopyTexSubImage3D: (target, level, xoffset, yoffset, zoffset, x, y, width, height) => { - this.assertWebGL2(); - this.ctx.copyTexImage3D(target, level, xoffset, yoffset, zoffset, x, y, width, height); - }, - - /* Programs and shaders */ - GetFragDataLocation: (program, name_ptr, name_len) => { - this.assertWebGL2(); - return this.ctx.getFragDataLocation(this.programs[program], this.mem.loadString(name_ptr, name_len)); - }, - - /* Uniforms */ - Uniform1ui: (location, v0) => { - this.assertWebGL2(); - this.ctx.uniform1ui(this.uniforms[location], v0); - }, - Uniform2ui: (location, v0, v1) => { - this.assertWebGL2(); - this.ctx.uniform2ui(this.uniforms[location], v0, v1); - }, - Uniform3ui: (location, v0, v1, v2) => { - this.assertWebGL2(); - this.ctx.uniform3ui(this.uniforms[location], v0, v1, v2); - }, - Uniform4ui: (location, v0, v1, v2, v3) => { - this.assertWebGL2(); - this.ctx.uniform4ui(this.uniforms[location], v0, v1, v2, v3); - }, - - UniformMatrix3x2fv: (location, addr) => { - this.assertWebGL2(); - let array = this.mem.loadF32Array(addr, 3*2); - this.ctx.uniformMatrix3x2fv(this.uniforms[location], false, array); - }, - UniformMatrix4x2fv: (location, addr) => { - this.assertWebGL2(); - let array = this.mem.loadF32Array(addr, 4*2); - this.ctx.uniformMatrix4x2fv(this.uniforms[location], false, array); - }, - UniformMatrix2x3fv: (location, addr) => { - this.assertWebGL2(); - let array = this.mem.loadF32Array(addr, 2*3); - this.ctx.uniformMatrix2x3fv(this.uniforms[location], false, array); - }, - UniformMatrix4x3fv: (location, addr) => { - this.assertWebGL2(); - let array = this.mem.loadF32Array(addr, 4*3); - this.ctx.uniformMatrix4x3fv(this.uniforms[location], false, array); - }, - UniformMatrix2x4fv: (location, addr) => { - this.assertWebGL2(); - let array = this.mem.loadF32Array(addr, 2*4); - this.ctx.uniformMatrix2x4fv(this.uniforms[location], false, array); - }, - UniformMatrix3x4fv: (location, addr) => { - this.assertWebGL2(); - let array = this.mem.loadF32Array(addr, 3*4); - this.ctx.uniformMatrix3x4fv(this.uniforms[location], false, array); - }, - - /* Vertex attribs */ - VertexAttribI4i: (index, x, y, z, w) => { - this.assertWebGL2(); - this.ctx.vertexAttribI4i(index, x, y, z, w); - }, - VertexAttribI4ui: (index, x, y, z, w) => { - this.assertWebGL2(); - this.ctx.vertexAttribI4ui(index, x, y, z, w); - }, - VertexAttribIPointer: (index, size, type, stride, offset) => { - this.assertWebGL2(); - this.ctx.vertexAttribIPointer(index, size, type, stride, offset); - }, - - /* Writing to the drawing buffer */ - VertexAttribDivisor: (index, divisor) => { - this.assertWebGL2(); - this.ctx.vertexAttribDivisor(index, divisor); - }, - DrawArraysInstanced: (mode, first, count, instanceCount) => { - this.assertWebGL2(); - this.ctx.drawArraysInstanced(mode, first, count, instanceCount); - }, - DrawElementsInstanced: (mode, count, type, offset, instanceCount) => { - this.assertWebGL2(); - this.ctx.drawElementsInstanced(mode, count, type, offset, instanceCount); - }, - DrawRangeElements: (mode, start, end, count, type, offset) => { - this.assertWebGL2(); - this.ctx.drawRangeElements(mode, start, end, count, type, offset); - }, - - /* Multiple Render Targets */ - DrawBuffers: (buffers_ptr, buffers_len) => { - this.assertWebGL2(); - let array = this.mem.loadU32Array(buffers_ptr, buffers_len); - this.ctx.drawBuffers(array); - }, - ClearBufferfv: (buffer, drawbuffer, values_ptr, values_len) => { - this.assertWebGL2(); - let array = this.mem.loadF32Array(values_ptr, values_len); - this.ctx.clearBufferfv(buffer, drawbuffer, array); - }, - ClearBufferiv: (buffer, drawbuffer, values_ptr, values_len) => { - this.assertWebGL2(); - let array = this.mem.loadI32Array(values_ptr, values_len); - this.ctx.clearBufferiv(buffer, drawbuffer, array); - }, - ClearBufferuiv: (buffer, drawbuffer, values_ptr, values_len) => { - this.assertWebGL2(); - let array = this.mem.loadU32Array(values_ptr, values_len); - this.ctx.clearBufferuiv(buffer, drawbuffer, array); - }, - ClearBufferfi: (buffer, drawbuffer, depth, stencil) => { - this.assertWebGL2(); - this.ctx.clearBufferfi(buffer, drawbuffer, depth, stencil); - }, - - /* Query Objects */ - CreateQuery: () => { - this.assertWebGL2(); - let query = this.ctx.createQuery(); - let id = this.getNewId(this.queries); - query.name = id; - this.queries[id] = query; - return id; - }, - DeleteQuery: (id) => { - this.assertWebGL2(); - let obj = this.querys[id]; - if (obj && id != 0) { - this.ctx.deleteQuery(obj); - this.querys[id] = null; - } - }, - IsQuery: (query) => { - this.assertWebGL2(); - return this.ctx.isQuery(this.queries[query]); - }, - BeginQuery: (target, query) => { - this.assertWebGL2(); - this.ctx.beginQuery(target, this.queries[query]) - }, - EndQuery: (target) => { - this.assertWebGL2(); - this.ctx.endQuery(target); - }, - GetQuery: (target, pname) => { - this.assertWebGL2(); - let query = this.ctx.getQuery(target, pname); - if (!query) { - return 0; - } - if (this.queries.indexOf(query) !== -1) { - return query.name; - } - let id = this.getNewId(this.queries); - query.name = id; - this.queries[id] = query; - return id; - }, - - /* Sampler Objects */ - CreateSampler: () => { - this.assertWebGL2(); - let sampler = this.ctx.createSampler(); - let id = this.getNewId(this.samplers); - sampler.name = id; - this.samplers[id] = sampler; - return id; - }, - DeleteSampler: (id) => { - this.assertWebGL2(); - let obj = this.samplers[id]; - if (obj && id != 0) { - this.ctx.deleteSampler(obj); - this.samplers[id] = null; - } - }, - IsSampler: (sampler) => { - this.assertWebGL2(); - return this.ctx.isSampler(this.samplers[sampler]); - }, - BindSampler: (unit, sampler) => { - this.assertWebGL2(); - this.ctx.bindSampler(unit, this.samplers[Sampler]); - }, - SamplerParameteri: (sampler, pname, param) => { - this.assertWebGL2(); - this.ctx.samplerParameteri(this.samplers[sampler], pname, param); - }, - SamplerParameterf: (sampler, pname, param) => { - this.assertWebGL2(); - this.ctx.samplerParameterf(this.samplers[sampler], pname, param); - }, - - /* Sync objects */ - FenceSync: (condition, flags) => { - this.assertWebGL2(); - let sync = this.ctx.fenceSync(condition, flags); - let id = this.getNewId(this.syncs); - sync.name = id; - this.syncs[id] = sync; - return id; - }, - IsSync: (sync) => { - this.assertWebGL2(); - return this.ctx.isSync(this.syncs[sync]); - }, - DeleteSync: (id) => { - this.assertWebGL2(); - let obj = this.syncs[id]; - if (obj && id != 0) { - this.ctx.deleteSampler(obj); - this.syncs[id] = null; - } - }, - ClientWaitSync: (sync, flags, timeout) => { - this.assertWebGL2(); - return this.ctx.clientWaitSync(this.syncs[sync], flags, timeout); - }, - WaitSync: (sync, flags, timeout) => { - this.assertWebGL2(); - this.ctx.waitSync(this.syncs[sync], flags, timeout) ; - }, - - - /* Transform Feedback */ - CreateTransformFeedback: () => { - this.assertWebGL2(); - let transformFeedback = this.ctx.createtransformFeedback(); - let id = this.getNewId(this.transformFeedbacks); - transformFeedback.name = id; - this.transformFeedbacks[id] = transformFeedback; - return id; - }, - DeleteTransformFeedback: (id) => { - this.assertWebGL2(); - let obj = this.transformFeedbacks[id]; - if (obj && id != 0) { - this.ctx.deleteTransformFeedback(obj); - this.transformFeedbacks[id] = null; - } - }, - IsTransformFeedback: (tf) => { - this.assertWebGL2(); - return this.ctx.isTransformFeedback(this.transformFeedbacks[tf]); - }, - BindTransformFeedback: (target, tf) => { - this.assertWebGL2(); - this.ctx.bindTransformFeedback(target, this.transformFeedbacks[tf]); - }, - BeginTransformFeedback: (primitiveMode) => { - this.assertWebGL2(); - this.ctx.beginTransformFeedback(primitiveMode); - }, - EndTransformFeedback: () => { - this.assertWebGL2(); - this.ctx.endTransformFeedback(); - }, - TransformFeedbackVaryings: (program, varyings_ptr, varyings_len, bufferMode) => { - this.assertWebGL2(); - let varyings = []; - for (let i = 0; i < varyings_len; i++) { - let ptr = this.mem.loadPtr(varyings_ptr + i*STRING_SIZE + 0*4); - let len = this.mem.loadPtr(varyings_ptr + i*STRING_SIZE + 1*4); - varyings.push(this.mem.loadString(ptr, len)); - } - this.ctx.transformFeedbackVaryings(this.programs[program], varyings, bufferMode); - }, - PauseTransformFeedback: () => { - this.assertWebGL2(); - this.ctx.pauseTransformFeedback(); - }, - ResumeTransformFeedback: () => { - this.assertWebGL2(); - this.ctx.resumeTransformFeedback(); - }, - - - /* Uniform Buffer Objects and Transform Feedback Buffers */ - BindBufferBase: (target, index, buffer) => { - this.assertWebGL2(); - this.ctx.bindBufferBase(target, index, this.buffers[buffer]); - }, - BindBufferRange: (target, index, buffer, offset, size) => { - this.assertWebGL2(); - this.ctx.bindBufferRange(target, index, this.buffers[buffer], offset, size); - }, - GetUniformBlockIndex: (program, uniformBlockName_ptr, uniformBlockName_len) => { - this.assertWebGL2(); - return this.ctx.getUniformBlockIndex(this.programs[program], this.mem.loadString(uniformBlockName_ptr, uniformBlockName_len)); - }, - // any getActiveUniformBlockParameter(WebGLProgram program, GLuint uniformBlockIndex, GLenum pname); - GetActiveUniformBlockName: (program, uniformBlockIndex, buf_ptr, buf_len, length_ptr) => { - this.assertWebGL2(); - let name = this.ctx.getActiveUniformBlockName(this.programs[program], uniformBlockIndex); - - let n = Math.min(buf_len, name.length); - name = name.substring(0, n); - this.mem.loadBytes(buf_ptr, buf_len).set(new TextEncoder("utf-8").encode(name)) - this.mem.storeInt(length_ptr, n); - }, - UniformBlockBinding: (program, uniformBlockIndex, uniformBlockBinding) => { - this.assertWebGL2(); - this.ctx.uniformBlockBinding(this.programs[program], uniformBlockIndex, uniformBlockBinding); - }, - - /* Vertex Array Objects */ - CreateVertexArray: () => { - this.assertWebGL2(); - let vao = this.ctx.createVertexArray(); - let id = this.getNewId(this.vaos); - vao.name = id; - this.vaos[id] = vao; - return id; - }, - DeleteVertexArray: (id) => { - this.assertWebGL2(); - let obj = this.vaos[id]; - if (obj && id != 0) { - this.ctx.deleteVertexArray(obj); - this.vaos[id] = null; - } - }, - IsVertexArray: (vertexArray) => { - this.assertWebGL2(); - return this.ctx.isVertexArray(this.vaos[vertexArray]); - }, - BindVertexArray: (vertexArray) => { - this.assertWebGL2(); - this.ctx.bindVertexArray(this.vaos[vertexArray]); - }, - }; - } -}; - - -export {WebGLInterface}; diff --git a/vendor/wasm/WebGL/webgl.odin b/vendor/wasm/WebGL/webgl.odin index c211fc526..04efc785b 100644 --- a/vendor/wasm/WebGL/webgl.odin +++ b/vendor/wasm/WebGL/webgl.odin @@ -13,8 +13,29 @@ Renderbuffer :: distinct u32 Shader :: distinct u32 Texture :: distinct u32 +ContextAttribute :: enum u32 { + disableAlpha = 0, + disableAntialias = 1, + disableDepth = 2, + failIfMajorPerformanceCaveat = 3, + disablePremultipliedAlpha = 4, + preserveDrawingBuffer = 5, + stencil = 6, + desynchronized = 7, +} +ContextAttributes :: distinct bit_set[ContextAttribute; u32] + +DEFAULT_CONTEXT_ATTRIBUTES :: ContextAttributes{} + @(default_calling_convention="c") foreign webgl { + // CreateCurrentContextById must be called before `GetCurrentContextAttributes` if the user wants to + // set specific attributes, otherwise the default attributes will be set for the WebGL context + CreateCurrentContextById :: proc(name: string, attributes: ContextAttributes) -> bool --- + // Acquire the WebGL context from a canvas element by id + SetCurrentContextById :: proc(name: string) -> bool --- + GetCurrentContextAttributes :: proc() -> ContextAttributes --- + DrawingBufferWidth :: proc() -> i32 --- DrawingBufferHeight :: proc() -> i32 --- diff --git a/vendor/wasm/WebGL/webgl_helpers.odin b/vendor/wasm/WebGL/webgl_helpers.odin new file mode 100644 index 000000000..585706fbc --- /dev/null +++ b/vendor/wasm/WebGL/webgl_helpers.odin @@ -0,0 +1,49 @@ +package webgl + +import "core:fmt" + +CreateProgramFromStrings :: proc(vs_sources, fs_sources: []string) -> (program: Program, ok: bool) { + ok = true + log: [1024]byte + + vs := CreateShader(VERTEX_SHADER) + fs := CreateShader(FRAGMENT_SHADER) + defer DeleteShader(vs) + defer DeleteShader(fs) + ShaderSource(vs, vs_sources) + ShaderSource(fs, fs_sources) + CompileShader(vs) + if GetShaderiv(vs, COMPILE_STATUS) == 0 { + err := GetShaderInfoLog(vs, log[:]) + fmt.eprintln("Vertex shader did not compile successfully", err) + ok = false + return + } + + CompileShader(fs) + if GetShaderiv(fs, COMPILE_STATUS) == 0 { + err := GetShaderInfoLog(fs, log[:]) + fmt.eprintln("Fragment shader did not compile successfully", err) + ok = false + return + } + + program = CreateProgram() + defer if !ok do DeleteProgram(program) + + AttachShader(program, vs) + AttachShader(program, fs) + LinkProgram(program) + DetachShader(program, vs) + DetachShader(program, fs) + + if GetProgramParameter(program, LINK_STATUS) == 0 { + err := GetProgramInfoLog(program, log[:]) + fmt.eprintln("Shader program did not link successfully", err) + ok = false + return + } + + return + +} diff --git a/vendor/wasm/js/dom.odin b/vendor/wasm/js/dom.odin new file mode 100644 index 000000000..d650dd70a --- /dev/null +++ b/vendor/wasm/js/dom.odin @@ -0,0 +1,76 @@ +//+build js wasm32, js wasm64 +package wasm_js_interface + +foreign import dom_lib "odin_dom" + +@(default_calling_convention="contextless") +foreign dom_lib { + get_element_value_f64 :: proc(id: string) -> f64 --- + set_element_value_f64 :: proc(id: string, value: f64) --- + + set_element_value_string :: proc(id: string, value: string) --- + get_element_value_string_length :: proc(id: string) -> int --- + + device_pixel_ratio :: proc() -> f64 --- + + window_set_scroll :: proc(x, y: f64) --- +} + +get_element_value_string :: proc "contextless" (id: string, buf: []byte) -> string { + @(default_calling_convention="contextless") + foreign dom_lib { + @(link_name="get_element_value_string") + _get_element_value_string :: proc(id: string, buf: []byte) -> int --- + } + n := _get_element_value_string(id, buf) + return string(buf[:n]) + +} + + +get_element_min_max :: proc "contextless" (id: string) -> (min, max: f64) { + @(default_calling_convention="contextless") + foreign dom_lib { + @(link_name="get_element_min_max") + _get_element_min_max :: proc(min_max: ^[2]f64, id: string) --- + } + min_max: [2]f64 + _get_element_min_max(&min_max, id) + return min_max[0], min_max[1] +} + + +Rect :: struct { + x, y, width, height: f64, +} + +get_bounding_client_rect :: proc "contextless" (id: string) -> (rect: Rect) { + @(default_calling_convention="contextless") + foreign dom_lib { + @(link_name="get_bounding_client_rect") + _get_bounding_client_rect :: proc(rect: ^Rect, id: string) --- + } + _get_bounding_client_rect(&rect, id) + return +} + +window_get_rect :: proc "contextless" () -> (rect: Rect) { + @(default_calling_convention="contextless") + foreign dom_lib { + @(link_name="window_get_rect") + _window_get_rect :: proc(rect: ^Rect) --- + } + _window_get_rect(&rect) + return +} + +window_get_scroll :: proc "contextless" () -> (x, y: f64) { + @(default_calling_convention="contextless") + foreign dom_lib { + @(link_name="window_get_scroll") + _window_get_scroll :: proc(scroll: ^[2]f64) --- + } + scroll := [2]f64{x, y} + _window_get_scroll(&scroll) + return +} diff --git a/vendor/wasm/js/events.odin b/vendor/wasm/js/events.odin new file mode 100644 index 000000000..136c0610d --- /dev/null +++ b/vendor/wasm/js/events.odin @@ -0,0 +1,349 @@ +//+build js wasm32, js wasm64 +package wasm_js_interface + +foreign import dom_lib "odin_dom" + +Event_Kind :: enum u32 { + Invalid, + + Load, + Unload, + Error, + Resize, + Visibility_Change, + Fullscreen_Change, + Fullscreen_Error, + + Click, + Double_Click, + Mouse_Move, + Mouse_Over, + Mouse_Out, + Mouse_Up, + Mouse_Down, + + Key_Up, + Key_Down, + Key_Press, + + Scroll, + Wheel, + + Focus, + Submit, + Blur, + Change, + Select, + + Animation_Start, + Animation_End, + Animation_Iteration, + Animation_Cancel, + + Copy, + Cut, + Paste, + + // Drag, + // Drag_Start, + // Drag_End, + // Drag_Enter, + // Drag_Leave, + // Drag_Over, + // Drop, + + Pointer_Cancel, + Pointer_Down, + Pointer_Enter, + Pointer_Leave, + Pointer_Move, + Pointer_Over, + Pointer_Up, + Got_Pointer_Capture, + Lost_Pointer_Capture, + Pointer_Lock_Change, + Pointer_Lock_Error, + + Selection_Change, + Selection_Start, + + Touch_Cancel, + Touch_End, + Touch_Move, + Touch_Start, + + Transition_Start, + Transition_End, + Transition_Run, + Transition_Cancel, + + Context_Menu, + + Custom, + +} +event_kind_string := [Event_Kind]string{ + .Invalid = "", + + .Load = "load", + .Unload = "unload", + .Error = "error", + .Resize = "resize", + .Visibility_Change = "visibilitychange", + .Fullscreen_Change = "fullscreenchange", + .Fullscreen_Error = "fullscreenerror", + + .Click = "click", + .Double_Click = "dblclick", + .Mouse_Move = "mousemove", + .Mouse_Over = "mouseover", + .Mouse_Out = "mouseout", + .Mouse_Up = "mouseup", + .Mouse_Down = "mousedown", + + .Key_Up = "keyup", + .Key_Down = "keydown", + .Key_Press = "keypress", + + .Scroll = "scroll", + .Wheel = "wheel", + + .Focus = "focus", + .Submit = "submit", + .Blur = "blur", + .Change = "change", + .Select = "select", + + .Animation_Start = "animationstart", + .Animation_End = "animationend", + .Animation_Iteration = "animationiteration", + .Animation_Cancel = "animationcancel", + + .Copy = "copy", + .Cut = "cut", + .Paste = "paste", + + // .Drag, = "drag", + // .Drag_Start, = "dragstart", + // .Drag_End, = "dragend", + // .Drag_Enter, = "dragenter", + // .Drag_Leave, = "dragleave", + // .Drag_Over, = "dragover", + // .Drop, = "drop", + + .Pointer_Cancel = "pointercancel", + .Pointer_Down = "pointerdown", + .Pointer_Enter = "pointerenter", + .Pointer_Leave = "pointerleave", + .Pointer_Move = "pointermove", + .Pointer_Over = "pointerover", + .Pointer_Up = "pointerup", + .Got_Pointer_Capture = "gotpointercapture", + .Lost_Pointer_Capture = "lostpointercapture", + .Pointer_Lock_Change = "pointerlockchange", + .Pointer_Lock_Error = "pointerlockerror", + + .Selection_Change = "selectionchange", + .Selection_Start = "selectionstart", + + .Transition_Start = "transitionstart", + .Transition_End = "transitionend", + .Transition_Run = "transitionrun", + .Transition_Cancel = "transitioncancel", + + .Touch_Cancel = "touchcancel", + .Touch_End = "touchend", + .Touch_Move = "touchmove", + .Touch_Start = "touchstart", + + .Context_Menu = "contextmenu", + + .Custom = "?custom?", +} + +Delta_Mode :: enum u32 { + Pixel = 0, + Line = 1, + Page = 2, +} + +Key_Location :: enum u8 { + Standard = 0, + Left = 1, + Right = 2, + Numpad = 3, +} + +KEYBOARD_MAX_KEY_SIZE :: 16 +KEYBOARD_MAX_CODE_SIZE :: 16 + +Event_Target_Kind :: enum u32 { + Element = 0, + Document = 1, + Window = 2, +} + +Event_Phase :: enum u8 { + None = 0, + Capturing_Phase = 1, + At_Target = 2, + Bubbling_Phase = 3, +} + +Event_Option :: enum u8 { + Bubbles = 0, + Cancelable = 1, + Composed = 2, +} +Event_Options :: distinct bit_set[Event_Option; u8] + +Event :: struct { + kind: Event_Kind, + target_kind: Event_Target_Kind, + current_target_kind: Event_Target_Kind, + id: string, + timestamp: f64, + + phase: Event_Phase, + options: Event_Options, + is_composing: bool, + is_trusted: bool, + + using data: struct #raw_union #align 8 { + scroll: struct { + delta: [2]f64, + }, + visibility_change: struct { + is_visible: bool, + }, + wheel: struct { + delta: [3]f64, + delta_mode: Delta_Mode, + }, + + key: struct { + key: string, + code: string, + location: Key_Location, + + ctrl: bool, + shift: bool, + alt: bool, + meta: bool, + + repeat: bool, + + _key_buf: [KEYBOARD_MAX_KEY_SIZE]byte, + _code_buf: [KEYBOARD_MAX_KEY_SIZE]byte, + }, + + mouse: struct { + screen: [2]i64, + client: [2]i64, + offset: [2]i64, + page: [2]i64, + movement: [2]i64, + + ctrl: bool, + shift: bool, + alt: bool, + meta: bool, + + button: i16, + buttons: bit_set[0..<16; u16], + }, + }, + + + user_data: rawptr, + callback: proc(e: Event), +} + +@(default_calling_convention="contextless") +foreign dom_lib { + event_stop_propagation :: proc() --- + event_stop_immediate_propagation :: proc() --- + event_prevent_default :: proc() --- + dispatch_custom_event :: proc(id: string, name: string, options := Event_Options{}) -> bool --- +} + +add_event_listener :: proc(id: string, kind: Event_Kind, user_data: rawptr, callback: proc(e: Event), use_capture := false) -> bool { + @(default_calling_convention="contextless") + foreign dom_lib { + @(link_name="add_event_listener") + _add_event_listener :: proc(id: string, name: string, name_code: Event_Kind, user_data: rawptr, callback: proc "odin" (Event), use_capture: bool) -> bool --- + } + // TODO: Pointer_Lock_Change etc related stuff for all different browsers + return _add_event_listener(id, event_kind_string[kind], kind, user_data, callback, use_capture) +} + +remove_event_listener :: proc(id: string, kind: Event_Kind, user_data: rawptr, callback: proc(e: Event)) -> bool { + @(default_calling_convention="contextless") + foreign dom_lib { + @(link_name="remove_event_listener") + _remove_event_listener :: proc(id: string, name: string, user_data: rawptr, callback: proc "odin" (Event)) -> bool --- + } + return _remove_event_listener(id, event_kind_string[kind], user_data, callback) +} + +add_window_event_listener :: proc(kind: Event_Kind, user_data: rawptr, callback: proc(e: Event), use_capture := false) -> bool { + @(default_calling_convention="contextless") + foreign dom_lib { + @(link_name="add_window_event_listener") + _add_window_event_listener :: proc(name: string, name_code: Event_Kind, user_data: rawptr, callback: proc "odin" (Event), use_capture: bool) -> bool --- + } + return _add_window_event_listener(event_kind_string[kind], kind, user_data, callback, use_capture) +} + +remove_window_event_listener :: proc(kind: Event_Kind, user_data: rawptr, callback: proc(e: Event)) -> bool { + @(default_calling_convention="contextless") + foreign dom_lib { + @(link_name="remove_window_event_listener") + _remove_window_event_listener :: proc(name: string, user_data: rawptr, callback: proc "odin" (Event)) -> bool --- + } + return _remove_window_event_listener(event_kind_string[kind], user_data, callback) +} + +remove_event_listener_from_event :: proc(e: Event) -> bool { + if e.id == "" { + return remove_window_event_listener(e.kind, e.user_data, e.callback) + } + return remove_event_listener(e.id, e.kind, e.user_data, e.callback) +} + +add_custom_event_listener :: proc(id: string, name: string, user_data: rawptr, callback: proc(e: Event), use_capture := false) -> bool { + @(default_calling_convention="contextless") + foreign dom_lib { + @(link_name="add_event_listener") + _add_event_listener :: proc(id: string, name: string, name_code: Event_Kind, user_data: rawptr, callback: proc "odin" (Event), use_capture: bool) -> bool --- + } + return _add_event_listener(id, name, .Custom, user_data, callback, use_capture) +} +remove_custom_event_listener :: proc(id: string, name: string, user_data: rawptr, callback: proc(e: Event)) -> bool { + @(default_calling_convention="contextless") + foreign dom_lib { + @(link_name="remove_event_listener") + _remove_event_listener :: proc(id: string, name: string, user_data: rawptr, callback: proc "odin" (Event)) -> bool --- + } + return _remove_event_listener(id, name, user_data, callback) +} + + + + +@(export, link_name="odin_dom_do_event_callback") +do_event_callback :: proc(user_data: rawptr, callback: proc(e: Event)) { + @(default_calling_convention="contextless") + foreign dom_lib { + init_event_raw :: proc(e: ^Event) --- + } + + if callback != nil { + event := Event{ + user_data = user_data, + callback = callback, + } + init_event_raw(&event) + callback(event) + } +} \ No newline at end of file diff --git a/vendor/wasm/js/general.odin b/vendor/wasm/js/general.odin new file mode 100644 index 000000000..0f6a9589c --- /dev/null +++ b/vendor/wasm/js/general.odin @@ -0,0 +1,12 @@ +//+build js wasm32, js wasm64 +package wasm_js_interface + +foreign import "odin_env" + +@(default_calling_convention="contextless") +foreign odin_env { + trap :: proc() -> ! --- + abort :: proc() -> ! --- + alert :: proc(msg: string) --- + evaluate :: proc(str: string) --- +} \ No newline at end of file diff --git a/vendor/wasm/js/memory.odin b/vendor/wasm/js/memory.odin new file mode 100644 index 000000000..84bb16d01 --- /dev/null +++ b/vendor/wasm/js/memory.odin @@ -0,0 +1,44 @@ +//+build js wasm32, js wasm64 +package wasm_js_interface + +import "core:mem" +import "core:intrinsics" + +PAGE_SIZE :: 64 * 1024 +page_alloc :: proc(page_count: int) -> (data: []byte, err: mem.Allocator_Error) { + prev_page_count := intrinsics.wasm_memory_grow(0, uintptr(page_count)) + if prev_page_count < 0 { + return nil, .Out_Of_Memory + } + + ptr := ([^]u8)(uintptr(prev_page_count) * PAGE_SIZE) + return ptr[:page_count * PAGE_SIZE], nil +} + +page_allocator :: proc() -> mem.Allocator { + procedure :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode, + size, alignment: int, + old_memory: rawptr, old_size: int, + location := #caller_location) -> ([]byte, mem.Allocator_Error) { + switch mode { + case .Alloc: + assert(size % PAGE_SIZE == 0) + return page_alloc(size/PAGE_SIZE) + case .Resize, .Free, .Free_All, .Query_Info: + return nil, .Mode_Not_Implemented + case .Query_Features: + set := (^mem.Allocator_Mode_Set)(old_memory) + if set != nil { + set^ = {.Alloc, .Query_Features} + } + } + + return nil, nil + } + + return { + procedure = procedure, + data = nil, + } +} + diff --git a/vendor/wasm/js/runtime.js b/vendor/wasm/js/runtime.js index 42ee257eb..60d114506 100644 --- a/vendor/wasm/js/runtime.js +++ b/vendor/wasm/js/runtime.js @@ -1,21 +1,42 @@ -class WasmMemoryInterface { - constructor() { - this.memory = null; +"use strict"; + +(function() { + +function getElement(name) { + if (name) { + return document.getElementById(name); } - + return undefined; +} + +class WasmMemoryInterface { + constructor() { + this.memory = null; + this.exports = null; + this.listenerMap = {}; + } + setMemory(memory) { this.memory = memory; } - + + setExports(exports) { + this.exports = exports; + } + get mem() { return new DataView(this.memory.buffer); } - - + + loadF32Array(addr, len) { let array = new Float32Array(this.memory.buffer, addr, len); return array; } + loadF64Array(addr, len) { + let array = new Float64Array(this.memory.buffer, addr, len); + return array; + } loadU32Array(addr, len) { let array = new Uint32Array(this.memory.buffer, addr, len); return array; @@ -24,8 +45,8 @@ class WasmMemoryInterface { let array = new Int32Array(this.memory.buffer, addr, len); return array; } - - + + loadU8(addr) { return this.mem.getUint8 (addr, true); } loadI8(addr) { return this.mem.getInt8 (addr, true); } loadU16(addr) { return this.mem.getUint16 (addr, true); } @@ -47,18 +68,18 @@ class WasmMemoryInterface { loadF64(addr) { return this.mem.getFloat64(addr, true); } loadInt(addr) { return this.mem.getInt32 (addr, true); } loadUint(addr) { return this.mem.getUint32 (addr, true); } - + loadPtr(addr) { return this.loadUint(addr); } - + loadBytes(ptr, len) { return new Uint8Array(this.memory.buffer, ptr, len); } - + loadString(ptr, len) { const bytes = this.loadBytes(ptr, len); return new TextDecoder("utf-8").decode(bytes); } - + storeU8(addr, value) { this.mem.setUint8 (addr, value, true); } storeI8(addr, value) { this.mem.setInt8 (addr, value, true); } storeU16(addr, value) { this.mem.setUint16 (addr, value, true); } @@ -80,50 +101,1218 @@ class WasmMemoryInterface { storeUint(addr, value) { this.mem.setUint32 (addr, value, true); } }; +class WebGLInterface { + constructor(wasmMemoryInterface) { + this.wasmMemoryInterface = wasmMemoryInterface; + this.ctxElement = null; + this.ctx = null; + this.ctxVersion = 1.0; + this.counter = 1; + this.lastError = 0; + this.buffers = []; + this.mappedBuffers = {}; + this.programs = []; + this.framebuffers = []; + this.renderbuffers = []; + this.textures = []; + this.uniforms = []; + this.shaders = []; + this.vaos = []; + this.contexts = []; + this.currentContext = null; + this.offscreenCanvases = {}; + this.timerQueriesEXT = []; + this.queries = []; + this.samplers = []; + this.transformFeedbacks = []; + this.syncs = []; + this.programInfos = {}; + } + + get mem() { + return this.wasmMemoryInterface + } + + setCurrentContext(element, contextSettings) { + if (!element) { + return false; + } + if (this.ctxElement == element) { + return true; + } + + contextSettings = contextSettings ?? {}; + this.ctx = element.getContext("webgl2", contextSettings) || element.getContext("webgl", contextSettings); + if (!this.ctx) { + return false; + } + this.ctxElement = element; + if (this.ctx.getParameter(0x1F02).indexOf("WebGL 2.0") !== -1) { + this.ctxVersion = 2.0; + } else { + this.ctxVersion = 1.0; + } + return true; + } + + assertWebGL2() { + if (this.ctxVersion < 2) { + throw new Error("WebGL2 procedure called in a canvas without a WebGL2 context"); + } + } + getNewId(table) { + for (var ret = this.counter++, i = table.length; i < ret; i++) { + table[i] = null; + } + return ret; + } + recordError(errorCode) { + this.lastError || (this.lastError = errorCode); + } + populateUniformTable(program) { + let p = this.programs[program]; + this.programInfos[program] = { + uniforms: {}, + maxUniformLength: 0, + maxAttributeLength: -1, + maxUniformBlockNameLength: -1, + }; + for (let ptable = this.programInfos[program], utable = ptable.uniforms, numUniforms = this.ctx.getProgramParameter(p, this.ctx.ACTIVE_UNIFORMS), i = 0; i < numUniforms; ++i) { + let u = this.ctx.getActiveUniform(p, i); + let name = u.name; + if (ptable.maxUniformLength = Math.max(ptable.maxUniformLength, name.length + 1), name.indexOf("]", name.length - 1) !== -1) { + name = name.slice(0, name.lastIndexOf("[")); + } + let loc = this.ctx.getUniformLocation(p, name); + if (loc !== null) { + let id = this.getNewId(this.uniforms); + utable[name] = [u.size, id], this.uniforms[id] = loc; + for (let j = 1; j < u.size; ++j) { + let n = name + "[" + j + "]"; + let loc = this.ctx.getUniformLocation(p, n); + let id = this.getNewId(this.uniforms); + this.uniforms[id] = loc; + } + } + } + } + getSource(shader, strings_ptr, strings_length) { + const STRING_SIZE = 2*4; + let source = ""; + for (let i = 0; i < strings_length; i++) { + let ptr = this.mem.loadPtr(strings_ptr + i*STRING_SIZE); + let len = this.mem.loadPtr(strings_ptr + i*STRING_SIZE + 4); + let str = this.mem.loadString(ptr, len); + source += str; + } + return source; + } + + getWebGL1Interface() { + return { + SetCurrentContextById: (name_ptr, name_len) => { + let name = this.mem.loadString(name_ptr, name_len); + let element = getElement(name); + return this.setCurrentContext(element, {alpha: true, antialias: true, depth: true, premultipliedAlpha: true}); + }, + CreateCurrentContextById: (name_ptr, name_len, attributes) => { + let name = this.mem.loadString(name_ptr, name_len); + let element = getElement(name); + + let contextSettings = { + alpha: !(attributes & (1<<0)), + antialias: !(attributes & (1<<1)), + depth: !(attributes & (1<<2)), + failIfMajorPerformanceCaveat: !!(attributes & (1<<3)), + premultipliedAlpha: !(attributes & (1<<4)), + preserveDrawingBuffer: !!(attributes & (1<<5)), + stencil: !!(attributes & (1<<6)), + desynchronized: !!(attributes & (1<<7)), + }; + + return this.setCurrentContext(element, contextSettings); + }, + GetCurrentContextAttributes: () => { + if (!this.ctx) { + return 0; + } + let attrs = this.ctx.getContextAttributes(); + let res = 0; + if (!attrs.alpha) res |= 1<<0; + if (!attrs.antialias) res |= 1<<1; + if (!attrs.depth) res |= 1<<2; + if (attrs.failIfMajorPerformanceCaveat) res |= 1<<3; + if (!attrs.premultipliedAlpha) res |= 1<<4; + if (attrs.preserveDrawingBuffer) res |= 1<<5; + if (attrs.stencil) res |= 1<<6; + if (attrs.desynchronized) res |= 1<<7; + return res; + }, + + DrawingBufferWidth: () => this.ctx.drawingBufferWidth, + DrawingBufferHeight: () => this.ctx.drawingBufferHeight, + + IsExtensionSupported: (name_ptr, name_len) => { + let name = this.mem.loadString(name_ptr, name_len); + let extensions = this.ctx.getSupportedExtensions(); + return extensions.indexOf(name) !== -1 + }, + + + GetError: () => { + let err = this.lastError; + this.recordError(0); + if (err) { + return err; + } + return this.ctx.getError(); + }, + + GetWebGLVersion: (major_ptr, minor_ptr) => { + let version = this.ctx.getParameter(0x1F02); + if (version.indexOf("WebGL 2.0") !== -1) { + this.mem.storeI32(major_ptr, 2); + this.mem.storeI32(minor_ptr, 0); + return; + } + + this.mem.storeI32(major_ptr, 1); + this.mem.storeI32(minor_ptr, 0); + }, + GetESVersion: (major_ptr, minor_ptr) => { + let version = this.ctx.getParameter(0x1F02); + if (version.indexOf("OpenGL ES 3.0") !== -1) { + this.mem.storeI32(major_ptr, 3); + this.mem.storeI32(minor_ptr, 0); + return; + } + + this.mem.storeI32(major_ptr, 2); + this.mem.storeI32(minor_ptr, 0); + }, + + + ActiveTexture: (x) => { + this.ctx.activeTexture(x); + }, + AttachShader: (program, shader) => { + this.ctx.attachShader(this.programs[program], this.shaders[shader]); + }, + BindAttribLocation: (program, index, name_ptr, name_len) => { + let name = this.mem.loadString(name_ptr, name_len); + this.ctx.bindAttribLocation(this.programs[program], index, name) + }, + BindBuffer: (target, buffer) => { + let bufferObj = buffer ? this.buffers[buffer] : null; + if (target == 35051) { + this.ctx.currentPixelPackBufferBinding = buffer; + } else { + if (target == 35052) { + this.ctx.currentPixelUnpackBufferBinding = buffer; + } + this.ctx.bindBuffer(target, bufferObj) + } + }, + BindFramebuffer: (target, buffer) => { + // TODO: BindFramebuffer + }, + BindTexture: (target, texture) => { + this.ctx.bindTexture(target, texture ? this.textures[texture] : null) + }, + BlendColor: (red, green, blue, alpha) => { + this.ctx.blendColor(red, green, blue, alpha); + }, + BlendEquation: (mode) => { + this.ctx.blendEquation(mode); + }, + BlendFunc: (sfactor, dfactor) => { + this.ctx.blendFunc(sfactor, dfactor); + }, + BlendFuncSeparate: (srcRGB, dstRGB, srcAlpha, dstAlpha) => { + this.ctx.blendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha); + }, + + + BufferData: (target, size, data, usage) => { + if (data) { + this.ctx.bufferData(target, this.mem.loadBytes(data, size), usage); + } else { + this.ctx.bufferData(target, size, usage); + } + }, + BufferSubData: (target, offset, size, data) => { + if (data) { + this.ctx.bufferSubData(target, offset, this.mem.loadBytes(data, size)); + } else { + this.ctx.bufferSubData(target, offset, null); + } + }, + + + Clear: (x) => { + this.ctx.clear(x); + }, + ClearColor: (r, g, b, a) => { + this.ctx.clearColor(r, g, b, a); + }, + ClearDepth: (x) => { + this.ctx.clearDepth(x); + }, + ClearStencil: (x) => { + this.ctx.clearStencil(x); + }, + ColorMask: (r, g, b, a) => { + this.ctx.colorMask(!!r, !!g, !!b, !!a); + }, + CompileShader: (shader) => { + this.ctx.compileShader(this.shaders[shader]); + }, + + + CompressedTexImage2D: (target, level, internalformat, width, height, border, imageSize, data) => { + if (data) { + this.ctx.compressedTexImage2D(target, level, internalformat, width, height, border, this.mem.loadBytes(data, imageSize)); + } else { + this.ctx.compressedTexImage2D(target, level, internalformat, width, height, border, null); + } + }, + CompressedTexSubImage2D: (target, level, xoffset, yoffset, width, height, format, imageSize, data) => { + if (data) { + this.ctx.compressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, this.mem.loadBytes(data, imageSize)); + } else { + this.ctx.compressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, null); + } + }, + + CopyTexImage2D: (target, level, internalformat, x, y, width, height, border) => { + this.ctx.copyTexImage2D(target, level, internalformat, x, y, width, height, border); + }, + CopyTexSubImage2D: (target, level, xoffset, yoffset, x, y, width, height) => { + this.ctx.copyTexImage2D(target, level, xoffset, yoffset, x, y, width, height); + }, + + + CreateBuffer: () => { + let buffer = this.ctx.createBuffer(); + if (!buffer) { + this.recordError(1282); + return 0; + } + let id = this.getNewId(this.buffers); + buffer.name = id + this.buffers[id] = buffer; + return id; + }, + CreateFramebuffer: () => { + let buffer = this.ctx.createFramebuffer(); + let id = this.getNewId(this.framebuffers); + buffer.name = id + this.framebuffers[id] = buffer; + return id; + }, + CreateProgram: () => { + let program = this.ctx.createProgram(); + let id = this.getNewId(this.programs); + program.name = id; + this.programs[id] = program; + return id; + }, + CreateRenderbuffer: () => { + let buffer = this.ctx.createRenderbuffer(); + let id = this.getNewId(this.renderbuffers); + buffer.name = id; + this.renderbuffers[id] = buffer; + return id; + }, + CreateShader: (shaderType) => { + let shader = this.ctx.createShader(shaderType); + let id = this.getNewId(this.shaders); + shader.name = id; + this.shaders[id] = shader; + return id; + }, + CreateTexture: () => { + let texture = this.ctx.createTexture(); + if (!texture) { + this.recordError(1282) + return 0; + } + let id = this.getNewId(this.textures); + texture.name = id; + this.textures[id] = texture; + return id; + }, + + + CullFace: (mode) => { + this.ctx.cullFace(mode); + }, + + + DeleteBuffer: (id) => { + let obj = this.buffers[id]; + if (obj && id != 0) { + this.ctx.deleteBuffer(obj); + this.buffers[id] = null; + } + }, + DeleteFramebuffer: (id) => { + let obj = this.framebuffers[id]; + if (obj && id != 0) { + this.ctx.deleteFramebuffer(obj); + this.framebuffers[id] = null; + } + }, + DeleteProgram: (id) => { + let obj = this.programs[id]; + if (obj && id != 0) { + this.ctx.deleteProgram(obj); + this.programs[id] = null; + } + }, + DeleteRenderbuffer: (id) => { + let obj = this.renderbuffers[id]; + if (obj && id != 0) { + this.ctx.deleteRenderbuffer(obj); + this.renderbuffers[id] = null; + } + }, + DeleteShader: (id) => { + let obj = this.shaders[id]; + if (obj && id != 0) { + this.ctx.deleteShader(obj); + this.shaders[id] = null; + } + }, + DeleteTexture: (id) => { + let obj = this.textures[id]; + if (obj && id != 0) { + this.ctx.deleteTexture(obj); + this.textures[id] = null; + } + }, + + + DepthFunc: (func) => { + this.ctx.depthFunc(func); + }, + DepthMask: (flag) => { + this.ctx.depthMask(!!flag); + }, + DepthRange: (zNear, zFar) => { + this.ctx.depthRange(zNear, zFar); + }, + DetachShader: (program, shader) => { + this.ctx.detachShader(this.programs[program], this.shaders[shader]); + }, + Disable: (cap) => { + this.ctx.disable(cap); + }, + DisableVertexAttribArray: (index) => { + this.ctx.disableVertexAttribArray(index); + }, + DrawArrays: (mode, first, count) => { + this.ctx.drawArrays(mode, first, count); + }, + DrawElements: (mode, count, type, indices) => { + this.ctx.drawElements(mode, count, type, indices); + }, + + + Enable: (cap) => { + this.ctx.enable(cap); + }, + EnableVertexAttribArray: (index) => { + this.ctx.enableVertexAttribArray(index); + }, + Finish: () => { + this.ctx.finish(); + }, + Flush: () => { + this.ctx.flush(); + }, + FramebufferRenderBuffer: (target, attachment, renderbuffertarget, renderbuffer) => { + this.ctx.framebufferRenderBuffer(target, attachment, renderbuffertarget, this.renderbuffers[renderbuffer]); + }, + FramebufferTexture2D: (target, attachment, textarget, texture, level) => { + this.ctx.framebufferTexture2D(target, attachment, textarget, this.textures[texture], level); + }, + FrontFace: (mode) => { + this.ctx.frontFace(mode); + }, + + + GenerateMipmap: (target) => { + this.ctx.generateMipmap(target); + }, + + + GetAttribLocation: (program, name_ptr, name_len) => { + let name = this.mem.loadString(name_ptr, name_len); + return this.ctx.getAttribLocation(this.programs[program], name); + }, + + + + GetProgramParameter: (program, pname) => { + return this.ctx.getProgramParameter(this.programs[program], pname) + }, + GetProgramInfoLog: (program, buf_ptr, buf_len, length_ptr) => { + let log = this.ctx.getProgramInfoLog(this.programs[program]); + if (log === null) { + log = "(unknown error)"; + } + if (buf_len > 0 && buf_ptr) { + let n = Math.min(buf_len, log.length); + log = log.substring(0, n); + this.mem.loadBytes(buf_ptr, buf_len).set(new TextEncoder("utf-8").encode(log)) + + this.mem.storeInt(length_ptr, n); + } + }, + GetShaderInfoLog: (shader, buf_ptr, buf_len, length_ptr) => { + let log = this.ctx.getShaderInfoLog(this.shaders[shader]); + if (log === null) { + log = "(unknown error)"; + } + if (buf_len > 0 && buf_ptr) { + let n = Math.min(buf_len, log.length); + log = log.substring(0, n); + this.mem.loadBytes(buf_ptr, buf_len).set(new TextEncoder("utf-8").encode(log)) + + this.mem.storeInt(length_ptr, n); + } + }, + GetShaderiv: (shader, pname, p) => { + if (p) { + if (pname == 35716) { + let log = this.ctx.getShaderInfoLog(this.shaders[shader]); + if (log === null) { + log = "(unknown error)"; + } + this.mem.storeInt(p, log.length+1); + } else if (pname == 35720) { + let source = this.ctx.getShaderSource(this.shaders[shader]); + let sourceLength = (source === null || source.length == 0) ? 0 : source.length+1; + this.mem.storeInt(p, sourceLength); + } else { + let param = this.ctx.getShaderParameter(this.shaders[shader], pname); + this.mem.storeI32(p, param); + } + } else { + this.recordError(1281); + } + }, + + + GetUniformLocation: (program, name_ptr, name_len) => { + let name = this.mem.loadString(name_ptr, name_len); + let arrayOffset = 0; + if (name.indexOf("]", name.length - 1) !== -1) { + let ls = name.lastIndexOf("["), + arrayIndex = name.slice(ls + 1, -1); + if (arrayIndex.length > 0 && (arrayOffset = parseInt(arrayIndex)) < 0) { + return -1; + } + name = name.slice(0, ls) + } + var ptable = this.programInfos[program]; + if (!ptable) { + return -1; + } + var uniformInfo = ptable.uniforms[name]; + return (uniformInfo && arrayOffset < uniformInfo[0]) ? uniformInfo[1] + arrayOffset : -1 + }, + + + GetVertexAttribOffset: (index, pname) => { + return this.ctx.getVertexAttribOffset(index, pname); + }, + + + Hint: (target, mode) => { + this.ctx.hint(target, mode); + }, + + + IsBuffer: (buffer) => this.ctx.isBuffer(this.buffers[buffer]), + IsEnabled: (enabled) => this.ctx.isEnabled(this.enableds[enabled]), + IsFramebuffer: (framebuffer) => this.ctx.isFramebuffer(this.framebuffers[framebuffer]), + IsProgram: (program) => this.ctx.isProgram(this.programs[program]), + IsRenderbuffer: (renderbuffer) => this.ctx.isRenderbuffer(this.renderbuffers[renderbuffer]), + IsShader: (shader) => this.ctx.isShader(this.shaders[shader]), + IsTexture: (texture) => this.ctx.isTexture(this.textures[texture]), + + LineWidth: (width) => { + this.ctx.lineWidth(width); + }, + LinkProgram: (program) => { + this.ctx.linkProgram(this.programs[program]); + this.programInfos[program] = null; + this.populateUniformTable(program); + }, + PixelStorei: (pname, param) => { + this.ctx.pixelStorei(pname, param); + }, + PolygonOffset: (factor, units) => { + this.ctx.polygonOffset(factor, units); + }, + + + ReadnPixels: (x, y, width, height, format, type, bufSize, data) => { + this.ctx.readPixels(x, y, width, format, type, this.mem.loadBytes(data, bufSize)); + }, + RenderbufferStorage: (target, internalformat, width, height) => { + this.ctx.renderbufferStorage(target, internalformat, width, height); + }, + SampleCoverage: (value, invert) => { + this.ctx.sampleCoverage(value, !!invert); + }, + Scissor: (x, y, width, height) => { + this.ctx.scissor(x, y, width, height); + }, + ShaderSource: (shader, strings_ptr, strings_length) => { + let source = this.getSource(shader, strings_ptr, strings_length); + this.ctx.shaderSource(this.shaders[shader], source); + }, + + StencilFunc: (func, ref, mask) => { + this.ctx.stencilFunc(func, ref, mask); + }, + StencilFuncSeparate: (face, func, ref, mask) => { + this.ctx.stencilFuncSeparate(face, func, ref, mask); + }, + StencilMask: (mask) => { + this.ctx.stencilMask(mask); + }, + StencilMaskSeparate: (face, mask) => { + this.ctx.stencilMaskSeparate(face, mask); + }, + StencilOp: (fail, zfail, zpass) => { + this.ctx.stencilOp(fail, zfail, zpass); + }, + StencilOpSeparate: (face, fail, zfail, zpass) => { + this.ctx.stencilOpSeparate(face, fail, zfail, zpass); + }, + + + TexImage2D: (target, level, internalformat, width, height, border, format, type, size, data) => { + if (data) { + this.ctx.texImage2D(target, level, internalformat, width, height, border, format, type, this.mem.loadBytes(data, size)); + } else { + this.ctx.texImage2D(target, level, internalformat, width, height, border, format, type, null); + } + }, + TexParameterf: (target, pname, param) => { + this.ctx.texParameterf(target, pname, param); + }, + TexParameteri: (target, pname, param) => { + this.ctx.texParameteri(target, pname, param); + }, + TexSubImage2D: (target, level, xoffset, yoffset, width, height, format, type, size, data) => { + this.ctx.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, this.mem.loadBytes(data, size)); + }, + + + Uniform1f: (location, v0) => { this.ctx.uniform1f(this.uniforms[location], v0); }, + Uniform2f: (location, v0, v1) => { this.ctx.uniform2f(this.uniforms[location], v0, v1); }, + Uniform3f: (location, v0, v1, v2) => { this.ctx.uniform3f(this.uniforms[location], v0, v1, v2); }, + Uniform4f: (location, v0, v1, v2, v3) => { this.ctx.uniform4f(this.uniforms[location], v0, v1, v2, v3); }, + + Uniform1i: (location, v0) => { this.ctx.uniform1i(this.uniforms[location], v0); }, + Uniform2i: (location, v0, v1) => { this.ctx.uniform2i(this.uniforms[location], v0, v1); }, + Uniform3i: (location, v0, v1, v2) => { this.ctx.uniform3i(this.uniforms[location], v0, v1, v2); }, + Uniform4i: (location, v0, v1, v2, v3) => { this.ctx.uniform4i(this.uniforms[location], v0, v1, v2, v3); }, + + UniformMatrix2fv: (location, addr) => { + let array = this.mem.loadF32Array(addr, 2*2); + this.ctx.uniformMatrix4fv(this.uniforms[location], false, array); + }, + UniformMatrix3fv: (location, addr) => { + let array = this.mem.loadF32Array(addr, 3*3); + this.ctx.uniformMatrix4fv(this.uniforms[location], false, array); + }, + UniformMatrix4fv: (location, addr) => { + let array = this.mem.loadF32Array(addr, 4*4); + this.ctx.uniformMatrix4fv(this.uniforms[location], false, array); + }, + + UseProgram: (program) => { + if (program) this.ctx.useProgram(this.programs[program]); + }, + ValidateProgram: (program) => { + if (program) this.ctx.validateProgram(this.programs[program]); + }, + + + VertexAttrib1f: (index, x) => { + this.ctx.vertexAttrib1f(index, x); + }, + VertexAttrib2f: (index, x, y) => { + this.ctx.vertexAttrib2f(index, x, y); + }, + VertexAttrib3f: (index, x, y, z) => { + this.ctx.vertexAttrib3f(index, x, y, z); + }, + VertexAttrib4f: (index, x, y, z, w) => { + this.ctx.vertexAttrib4f(index, x, y, z, w); + }, + VertexAttribPointer: (index, size, type, normalized, stride, ptr) => { + this.ctx.vertexAttribPointer(index, size, type, !!normalized, stride, ptr); + }, + + Viewport: (x, y, w, h) => { + this.ctx.viewport(x, y, w, h); + }, + }; + } + + getWebGL2Interface() { + return { + /* Buffer objects */ + CopyBufferSubData: (readTarget, writeTarget, readOffset, writeOffset, size) => { + this.assertWebGL2(); + this.ctx.copyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size); + }, + GetBufferSubData: (target, srcByteOffset, dst_buffer_ptr, dst_buffer_len, dstOffset, length) => { + this.assertWebGL2(); + this.ctx.getBufferSubData(target, srcByteOffset, this.mem.loadBytes(dst_buffer_ptr, dst_buffer_len), dstOffset, length); + }, + + /* Framebuffer objects */ + BlitFramebuffer: (srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter) => { + this.assertWebGL2(); + this.ctx.glitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); + }, + FramebufferTextureLayer: (target, attachment, texture, level, layer) => { + this.assertWebGL2(); + this.ctx.framebufferTextureLayer(target, attachment, this.textures[texture], level, layer); + }, + InvalidateFramebuffer: (target, attachments_ptr, attachments_len) => { + this.assertWebGL2(); + let attachments = this.mem.loadU32Array(attachments_ptr, attachments_len); + this.ctx.invalidateFramebuffer(target, attachments); + }, + InvalidateSubFramebuffer: (target, attachments_ptr, attachments_len, x, y, width, height) => { + this.assertWebGL2(); + let attachments = this.mem.loadU32Array(attachments_ptr, attachments_len); + this.ctx.invalidateSubFramebuffer(target, attachments, x, y, width, height); + }, + ReadBuffer: (src) => { + this.assertWebGL2(); + this.ctx.readBuffer(src); + }, + + /* Renderbuffer objects */ + RenderbufferStorageMultisample: (target, samples, internalformat, width, height) => { + this.assertWebGL2(); + this.ctx.renderbufferStorageMultisample(target, samples, internalformat, width, height); + }, + + /* Texture objects */ + + TexStorage3D: (target, levels, internalformat, width, height, depth) => { + this.assertWebGL2(); + this.ctx.texStorage3D(target, level, internalformat, width, heigh, depth); + }, + TexImage3D: (target, level, internalformat, width, height, depth, border, format, type, size, data) => { + this.assertWebGL2(); + if (data) { + this.ctx.texImage3D(target, level, internalformat, width, height, depth, border, format, type, this.mem.loadBytes(data, size)); + } else { + this.ctx.texImage3D(target, level, internalformat, width, height, depth, border, format, type, null); + } + }, + TexSubImage3D: (target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, size, data) => { + this.assertWebGL2(); + this.ctx.texSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, this.mem.loadBytes(data, size)); + }, + CompressedTexImage3D: (target, level, internalformat, width, height, depth, border, imageSize, data) => { + this.assertWebGL2(); + if (data) { + this.ctx.compressedTexImage3D(target, level, internalformat, width, height, depth, border, this.mem.loadBytes(data, imageSize)); + } else { + this.ctx.compressedTexImage3D(target, level, internalformat, width, height, depth, border, null); + } + }, + CompressedTexSubImage3D: (target, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, data) => { + this.assertWebGL2(); + if (data) { + this.ctx.compressedTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, this.mem.loadBytes(data, imageSize)); + } else { + this.ctx.compressedTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, null); + } + }, + + CopyTexSubImage3D: (target, level, xoffset, yoffset, zoffset, x, y, width, height) => { + this.assertWebGL2(); + this.ctx.copyTexImage3D(target, level, xoffset, yoffset, zoffset, x, y, width, height); + }, + + /* Programs and shaders */ + GetFragDataLocation: (program, name_ptr, name_len) => { + this.assertWebGL2(); + return this.ctx.getFragDataLocation(this.programs[program], this.mem.loadString(name_ptr, name_len)); + }, + + /* Uniforms */ + Uniform1ui: (location, v0) => { + this.assertWebGL2(); + this.ctx.uniform1ui(this.uniforms[location], v0); + }, + Uniform2ui: (location, v0, v1) => { + this.assertWebGL2(); + this.ctx.uniform2ui(this.uniforms[location], v0, v1); + }, + Uniform3ui: (location, v0, v1, v2) => { + this.assertWebGL2(); + this.ctx.uniform3ui(this.uniforms[location], v0, v1, v2); + }, + Uniform4ui: (location, v0, v1, v2, v3) => { + this.assertWebGL2(); + this.ctx.uniform4ui(this.uniforms[location], v0, v1, v2, v3); + }, + + UniformMatrix3x2fv: (location, addr) => { + this.assertWebGL2(); + let array = this.mem.loadF32Array(addr, 3*2); + this.ctx.uniformMatrix3x2fv(this.uniforms[location], false, array); + }, + UniformMatrix4x2fv: (location, addr) => { + this.assertWebGL2(); + let array = this.mem.loadF32Array(addr, 4*2); + this.ctx.uniformMatrix4x2fv(this.uniforms[location], false, array); + }, + UniformMatrix2x3fv: (location, addr) => { + this.assertWebGL2(); + let array = this.mem.loadF32Array(addr, 2*3); + this.ctx.uniformMatrix2x3fv(this.uniforms[location], false, array); + }, + UniformMatrix4x3fv: (location, addr) => { + this.assertWebGL2(); + let array = this.mem.loadF32Array(addr, 4*3); + this.ctx.uniformMatrix4x3fv(this.uniforms[location], false, array); + }, + UniformMatrix2x4fv: (location, addr) => { + this.assertWebGL2(); + let array = this.mem.loadF32Array(addr, 2*4); + this.ctx.uniformMatrix2x4fv(this.uniforms[location], false, array); + }, + UniformMatrix3x4fv: (location, addr) => { + this.assertWebGL2(); + let array = this.mem.loadF32Array(addr, 3*4); + this.ctx.uniformMatrix3x4fv(this.uniforms[location], false, array); + }, + + /* Vertex attribs */ + VertexAttribI4i: (index, x, y, z, w) => { + this.assertWebGL2(); + this.ctx.vertexAttribI4i(index, x, y, z, w); + }, + VertexAttribI4ui: (index, x, y, z, w) => { + this.assertWebGL2(); + this.ctx.vertexAttribI4ui(index, x, y, z, w); + }, + VertexAttribIPointer: (index, size, type, stride, offset) => { + this.assertWebGL2(); + this.ctx.vertexAttribIPointer(index, size, type, stride, offset); + }, + + /* Writing to the drawing buffer */ + VertexAttribDivisor: (index, divisor) => { + this.assertWebGL2(); + this.ctx.vertexAttribDivisor(index, divisor); + }, + DrawArraysInstanced: (mode, first, count, instanceCount) => { + this.assertWebGL2(); + this.ctx.drawArraysInstanced(mode, first, count, instanceCount); + }, + DrawElementsInstanced: (mode, count, type, offset, instanceCount) => { + this.assertWebGL2(); + this.ctx.drawElementsInstanced(mode, count, type, offset, instanceCount); + }, + DrawRangeElements: (mode, start, end, count, type, offset) => { + this.assertWebGL2(); + this.ctx.drawRangeElements(mode, start, end, count, type, offset); + }, + + /* Multiple Render Targets */ + DrawBuffers: (buffers_ptr, buffers_len) => { + this.assertWebGL2(); + let array = this.mem.loadU32Array(buffers_ptr, buffers_len); + this.ctx.drawBuffers(array); + }, + ClearBufferfv: (buffer, drawbuffer, values_ptr, values_len) => { + this.assertWebGL2(); + let array = this.mem.loadF32Array(values_ptr, values_len); + this.ctx.clearBufferfv(buffer, drawbuffer, array); + }, + ClearBufferiv: (buffer, drawbuffer, values_ptr, values_len) => { + this.assertWebGL2(); + let array = this.mem.loadI32Array(values_ptr, values_len); + this.ctx.clearBufferiv(buffer, drawbuffer, array); + }, + ClearBufferuiv: (buffer, drawbuffer, values_ptr, values_len) => { + this.assertWebGL2(); + let array = this.mem.loadU32Array(values_ptr, values_len); + this.ctx.clearBufferuiv(buffer, drawbuffer, array); + }, + ClearBufferfi: (buffer, drawbuffer, depth, stencil) => { + this.assertWebGL2(); + this.ctx.clearBufferfi(buffer, drawbuffer, depth, stencil); + }, + + /* Query Objects */ + CreateQuery: () => { + this.assertWebGL2(); + let query = this.ctx.createQuery(); + let id = this.getNewId(this.queries); + query.name = id; + this.queries[id] = query; + return id; + }, + DeleteQuery: (id) => { + this.assertWebGL2(); + let obj = this.querys[id]; + if (obj && id != 0) { + this.ctx.deleteQuery(obj); + this.querys[id] = null; + } + }, + IsQuery: (query) => { + this.assertWebGL2(); + return this.ctx.isQuery(this.queries[query]); + }, + BeginQuery: (target, query) => { + this.assertWebGL2(); + this.ctx.beginQuery(target, this.queries[query]) + }, + EndQuery: (target) => { + this.assertWebGL2(); + this.ctx.endQuery(target); + }, + GetQuery: (target, pname) => { + this.assertWebGL2(); + let query = this.ctx.getQuery(target, pname); + if (!query) { + return 0; + } + if (this.queries.indexOf(query) !== -1) { + return query.name; + } + let id = this.getNewId(this.queries); + query.name = id; + this.queries[id] = query; + return id; + }, + + /* Sampler Objects */ + CreateSampler: () => { + this.assertWebGL2(); + let sampler = this.ctx.createSampler(); + let id = this.getNewId(this.samplers); + sampler.name = id; + this.samplers[id] = sampler; + return id; + }, + DeleteSampler: (id) => { + this.assertWebGL2(); + let obj = this.samplers[id]; + if (obj && id != 0) { + this.ctx.deleteSampler(obj); + this.samplers[id] = null; + } + }, + IsSampler: (sampler) => { + this.assertWebGL2(); + return this.ctx.isSampler(this.samplers[sampler]); + }, + BindSampler: (unit, sampler) => { + this.assertWebGL2(); + this.ctx.bindSampler(unit, this.samplers[Sampler]); + }, + SamplerParameteri: (sampler, pname, param) => { + this.assertWebGL2(); + this.ctx.samplerParameteri(this.samplers[sampler], pname, param); + }, + SamplerParameterf: (sampler, pname, param) => { + this.assertWebGL2(); + this.ctx.samplerParameterf(this.samplers[sampler], pname, param); + }, + + /* Sync objects */ + FenceSync: (condition, flags) => { + this.assertWebGL2(); + let sync = this.ctx.fenceSync(condition, flags); + let id = this.getNewId(this.syncs); + sync.name = id; + this.syncs[id] = sync; + return id; + }, + IsSync: (sync) => { + this.assertWebGL2(); + return this.ctx.isSync(this.syncs[sync]); + }, + DeleteSync: (id) => { + this.assertWebGL2(); + let obj = this.syncs[id]; + if (obj && id != 0) { + this.ctx.deleteSampler(obj); + this.syncs[id] = null; + } + }, + ClientWaitSync: (sync, flags, timeout) => { + this.assertWebGL2(); + return this.ctx.clientWaitSync(this.syncs[sync], flags, timeout); + }, + WaitSync: (sync, flags, timeout) => { + this.assertWebGL2(); + this.ctx.waitSync(this.syncs[sync], flags, timeout) ; + }, + + + /* Transform Feedback */ + CreateTransformFeedback: () => { + this.assertWebGL2(); + let transformFeedback = this.ctx.createtransformFeedback(); + let id = this.getNewId(this.transformFeedbacks); + transformFeedback.name = id; + this.transformFeedbacks[id] = transformFeedback; + return id; + }, + DeleteTransformFeedback: (id) => { + this.assertWebGL2(); + let obj = this.transformFeedbacks[id]; + if (obj && id != 0) { + this.ctx.deleteTransformFeedback(obj); + this.transformFeedbacks[id] = null; + } + }, + IsTransformFeedback: (tf) => { + this.assertWebGL2(); + return this.ctx.isTransformFeedback(this.transformFeedbacks[tf]); + }, + BindTransformFeedback: (target, tf) => { + this.assertWebGL2(); + this.ctx.bindTransformFeedback(target, this.transformFeedbacks[tf]); + }, + BeginTransformFeedback: (primitiveMode) => { + this.assertWebGL2(); + this.ctx.beginTransformFeedback(primitiveMode); + }, + EndTransformFeedback: () => { + this.assertWebGL2(); + this.ctx.endTransformFeedback(); + }, + TransformFeedbackVaryings: (program, varyings_ptr, varyings_len, bufferMode) => { + this.assertWebGL2(); + let varyings = []; + for (let i = 0; i < varyings_len; i++) { + let ptr = this.mem.loadPtr(varyings_ptr + i*STRING_SIZE + 0*4); + let len = this.mem.loadPtr(varyings_ptr + i*STRING_SIZE + 1*4); + varyings.push(this.mem.loadString(ptr, len)); + } + this.ctx.transformFeedbackVaryings(this.programs[program], varyings, bufferMode); + }, + PauseTransformFeedback: () => { + this.assertWebGL2(); + this.ctx.pauseTransformFeedback(); + }, + ResumeTransformFeedback: () => { + this.assertWebGL2(); + this.ctx.resumeTransformFeedback(); + }, + + + /* Uniform Buffer Objects and Transform Feedback Buffers */ + BindBufferBase: (target, index, buffer) => { + this.assertWebGL2(); + this.ctx.bindBufferBase(target, index, this.buffers[buffer]); + }, + BindBufferRange: (target, index, buffer, offset, size) => { + this.assertWebGL2(); + this.ctx.bindBufferRange(target, index, this.buffers[buffer], offset, size); + }, + GetUniformBlockIndex: (program, uniformBlockName_ptr, uniformBlockName_len) => { + this.assertWebGL2(); + return this.ctx.getUniformBlockIndex(this.programs[program], this.mem.loadString(uniformBlockName_ptr, uniformBlockName_len)); + }, + // any getActiveUniformBlockParameter(WebGLProgram program, GLuint uniformBlockIndex, GLenum pname); + GetActiveUniformBlockName: (program, uniformBlockIndex, buf_ptr, buf_len, length_ptr) => { + this.assertWebGL2(); + let name = this.ctx.getActiveUniformBlockName(this.programs[program], uniformBlockIndex); + + let n = Math.min(buf_len, name.length); + name = name.substring(0, n); + this.mem.loadBytes(buf_ptr, buf_len).set(new TextEncoder("utf-8").encode(name)) + this.mem.storeInt(length_ptr, n); + }, + UniformBlockBinding: (program, uniformBlockIndex, uniformBlockBinding) => { + this.assertWebGL2(); + this.ctx.uniformBlockBinding(this.programs[program], uniformBlockIndex, uniformBlockBinding); + }, + + /* Vertex Array Objects */ + CreateVertexArray: () => { + this.assertWebGL2(); + let vao = this.ctx.createVertexArray(); + let id = this.getNewId(this.vaos); + vao.name = id; + this.vaos[id] = vao; + return id; + }, + DeleteVertexArray: (id) => { + this.assertWebGL2(); + let obj = this.vaos[id]; + if (obj && id != 0) { + this.ctx.deleteVertexArray(obj); + this.vaos[id] = null; + } + }, + IsVertexArray: (vertexArray) => { + this.assertWebGL2(); + return this.ctx.isVertexArray(this.vaos[vertexArray]); + }, + BindVertexArray: (vertexArray) => { + this.assertWebGL2(); + this.ctx.bindVertexArray(this.vaos[vertexArray]); + }, + }; + } +}; + + function odinSetupDefaultImports(wasmMemoryInterface, consoleElement) { const MAX_INFO_CONSOLE_LINES = 512; let infoConsoleLines = new Array(); - const addConsoleLine = (line) => { - if (line === undefined) { + let currentLine = {}; + currentLine[false] = ""; + currentLine[true] = ""; + let prevIsError = false; + + const writeToConsole = (line, isError) => { + if (!line) { return; } - if (line.endsWith("\n")) { - line = line.substring(0, line.length-1); - } else if (infoConsoleLines.length > 0) { - let prev_line = infoConsoleLines.pop(); - line = prev_line.concat(line); + + const println = (text, forceIsError) => { + let style = [ + "color: #eee", + "background-color: #d20", + "padding: 2px 4px", + "border-radius: 2px", + ].join(";"); + let doIsError = isError; + if (forceIsError !== undefined) { + doIsError = forceIsError; + } + + if (doIsError) { + console.log("%c"+text, style); + } else { + console.log(text); + } + + }; + + // Print to console + if (line == "\n") { + println(currentLine[isError]); + currentLine[isError] = ""; + } else if (!line.includes("\n")) { + currentLine[isError] = currentLine[isError].concat(line); + } else { + let lines = line.split("\n"); + let printLast = lines.length > 1 && line.endsWith("\n"); + println(currentLine[isError].concat(lines[0])); + currentLine[isError] = ""; + for (let i = 1; i < lines.length-1; i++) { + println(lines[i]); + } + let last = lines[lines.length-1]; + if (printLast) { + println(last); + } else { + currentLine[isError] = last; + } } - infoConsoleLines.push(line); - + + if (prevIsError != isError) { + if (prevIsError) { + println(currentLine[prevIsError], prevIsError); + currentLine[prevIsError] = ""; + } + } + prevIsError = isError; + + + // HTML based console + if (!consoleElement) { + return; + } + const wrap = (x) => { + if (isError) { + return ''+x+''; + } + return x; + }; + + if (line == "\n") { + infoConsoleLines.push(line); + } else if (!line.includes("\n")) { + let prevLine = ""; + if (infoConsoleLines.length > 0) { + prevLine = infoConsoleLines.pop(); + } + infoConsoleLines.push(prevLine.concat(wrap(line))); + } else { + let lines = line.split("\n"); + let lastHasNewline = lines.length > 1 && line.endsWith("\n"); + + let prevLine = ""; + if (infoConsoleLines.length > 0) { + prevLine = infoConsoleLines.pop(); + } + infoConsoleLines.push(prevLine.concat(wrap(lines[0]).concat("\n"))); + + for (let i = 1; i < lines.length-1; i++) { + infoConsoleLines.push(wrap(lines[i]).concat("\n")); + } + let last = lines[lines.length-1]; + if (lastHasNewline) { + infoConsoleLines.push(last.concat("\n")); + } else { + infoConsoleLines.push(last); + } + } + if (infoConsoleLines.length > MAX_INFO_CONSOLE_LINES) { - infoConsoleLines.shift(); + infoConsoleLines.shift(MAX_INFO_CONSOLE_LINES); } - + let data = ""; for (let i = 0; i < infoConsoleLines.length; i++) { - if (i != 0) { - data = data.concat("\n"); - } data = data.concat(infoConsoleLines[i]); } - - if (consoleElement !== undefined) { - let info = consoleElement; - info.innerHTML = data; - info.scrollTop = info.scrollHeight; - } + + let info = consoleElement; + info.innerHTML = data; + info.scrollTop = info.scrollHeight; }; + let event_temp_data = {}; + + let webglContext = new WebGLInterface(wasmMemoryInterface); return { "env": {}, "odin_env": { write: (fd, ptr, len) => { const str = wasmMemoryInterface.loadString(ptr, len); if (fd == 1) { - addConsoleLine(str); + writeToConsole(str, false); return; } else if (fd == 2) { - addConsoleLine(str); + writeToConsole(str, true); return; } else { throw new Error("Invalid fd to 'write'" + stripNewline(str)); @@ -133,11 +1322,21 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement) { alert: (ptr, len) => { alert(wasmMemoryInterface.loadString(ptr, len)) }, abort: () => { Module.abort() }, evaluate: (str_ptr, str_len) => { eval.call(null, wasmMemoryInterface.loadString(str_ptr, str_len)); }, - + time_now: () => { + // convert ms to ns + return Date.now() * 1e6; + }, + tick_now: () => { + // convert ms to ns return performance.now() * 1e6; }, - + time_sleep: (duration_ms) => { + if (duration_ms > 0) { + // TODO(bill): Does this even make any sense? + } + }, + sqrt: (x) => Math.sqrt(x), sin: (x) => Math.sin(x), cos: (x) => Math.cos(x), @@ -147,8 +1346,346 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement) { exp: (x) => Math.exp(x), ldexp: (x) => Math.ldexp(x), }, + "odin_dom": { + init_event_raw: (ep) => { + const W = 4; + let offset = ep; + let off = (amount, alignment) => { + if (alignment === undefined) { + alignment = Math.min(amount, W); + } + if (offset % alignment != 0) { + offset += alignment - (offset%alignment); + } + let x = offset; + offset += amount; + return x; + }; + + let wmi = wasmMemoryInterface; + + let e = event_temp_data.event; + + wmi.storeU32(off(4), event_temp_data.name_code); + if (e.target == document) { + wmi.storeU32(off(4), 1); + } else if (e.target == window) { + wmi.storeU32(off(4), 2); + } else { + wmi.storeU32(off(4), 0); + } + if (e.currentTarget == document) { + wmi.storeU32(off(4), 1); + } else if (e.currentTarget == window) { + wmi.storeU32(off(4), 2); + } else { + wmi.storeU32(off(4), 0); + } + + wmi.storeUint(off(W), event_temp_data.id_ptr); + wmi.storeUint(off(W), event_temp_data.id_len); + + wmi.storeF64(off(8), e.timeStamp*1e-3); + + wmi.storeU8(off(1), e.eventPhase); + let options = 0; + if (!!e.bubbles) { options |= 1<<0; } + if (!!e.cancelable) { options |= 1<<1; } + if (!!e.composed) { options |= 1<<2; } + wmi.storeU8(off(1), options); + wmi.storeU8(off(1), !!e.isComposing); + wmi.storeU8(off(1), !!e.isTrusted); + + let base = off(0, 8); + if (e instanceof MouseEvent) { + wmi.storeI64(off(8), e.screenX); + wmi.storeI64(off(8), e.screenY); + wmi.storeI64(off(8), e.clientX); + wmi.storeI64(off(8), e.clientY); + wmi.storeI64(off(8), e.offsetX); + wmi.storeI64(off(8), e.offsetY); + wmi.storeI64(off(8), e.pageX); + wmi.storeI64(off(8), e.pageY); + wmi.storeI64(off(8), e.movementX); + wmi.storeI64(off(8), e.movementY); + + wmi.storeU8(off(1), !!e.ctrlKey); + wmi.storeU8(off(1), !!e.shiftKey); + wmi.storeU8(off(1), !!e.altKey); + wmi.storeU8(off(1), !!e.metaKey); + + wmi.storeI16(off(2), e.button); + wmi.storeU16(off(2), e.buttons); + } else if (e instanceof KeyboardEvent) { + let keyOffset = off(W*2, W); + let codeOffet = off(W*2, W); + wmi.storeU8(off(1), e.location); + + wmi.storeU8(off(1), !!e.ctrlKey); + wmi.storeU8(off(1), !!e.shiftKey); + wmi.storeU8(off(1), !!e.altKey); + wmi.storeU8(off(1), !!e.metaKey); + + wmi.storeU8(off(1), !!e.repeat); + } else if (e instanceof WheelEvent) { + wmi.storeF64(off(8), e.deltaX); + wmi.storeF64(off(8), e.deltaY); + wmi.storeF64(off(8), e.deltaZ); + wmi.storeU32(off(4), e.deltaMode); + } else if (e instanceof Event) { + if ('scrollX' in e) { + wmi.storeF64(off(8), e.scrollX); + wmi.storeF64(off(8), e.scrollY); + } + } + }, + + add_event_listener: (id_ptr, id_len, name_ptr, name_len, name_code, data, callback, use_capture) => { + let id = wasmMemoryInterface.loadString(id_ptr, id_len); + let name = wasmMemoryInterface.loadString(name_ptr, name_len); + let element = getElement(id); + if (element == undefined) { + return false; + } + + let listener = (e) => { + const odin_ctx = wasmMemoryInterface.exports.default_context_ptr(); + event_temp_data.id_ptr = id_ptr; + event_temp_data.id_len = id_len; + event_temp_data.event = e; + event_temp_data.name_code = name_code; + wasmMemoryInterface.exports.odin_dom_do_event_callback(data, callback, odin_ctx); + }; + wasmMemoryInterface.listenerMap[{data: data, callback: callback}] = listener; + element.addEventListener(name, listener, !!use_capture); + return true; + }, + + remove_event_listener: (id_ptr, id_len, name_ptr, name_len, data, callback) => { + let id = wasmMemoryInterface.loadString(id_ptr, id_len); + let name = wasmMemoryInterface.loadString(name_ptr, name_len); + let element = getElement(id); + if (element == undefined) { + return false; + } + + let listener = wasmMemoryInterface.listenerMap[{data: data, callback: callback}]; + if (listener == undefined) { + return false; + } + element.removeEventListener(name, listener); + return true; + }, + + + add_window_event_listener: (name_ptr, name_len, name_code, data, callback, use_capture) => { + let name = wasmMemoryInterface.loadString(name_ptr, name_len); + let element = window; + let listener = (e) => { + const odin_ctx = wasmMemoryInterface.exports.default_context_ptr(); + event_temp_data.id_ptr = 0; + event_temp_data.id_len = 0; + event_temp_data.event = e; + event_temp_data.name_code = name_code; + wasmMemoryInterface.exports.odin_dom_do_event_callback(data, callback, odin_ctx); + }; + wasmMemoryInterface.listenerMap[{data: data, callback: callback}] = listener; + element.addEventListener(name, listener, !!use_capture); + return true; + }, + + remove_window_event_listener: (name_ptr, name_len, data, callback) => { + let name = wasmMemoryInterface.loadString(name_ptr, name_len); + let element = window; + let key = {data: data, callback: callback}; + let listener = wasmMemoryInterface.listenerMap[key]; + if (!listener) { + return false; + } + wasmMemoryInterface.listenerMap[key] = undefined; + + element.removeEventListener(name, listener); + return true; + }, + + event_stop_propagation: () => { + if (event_temp_data && event_temp_data.event) { + event_temp_data.event.eventStopPropagation(); + } + }, + event_stop_immediate_propagation: () => { + if (event_temp_data && event_temp_data.event) { + event_temp_data.event.eventStopImmediatePropagation(); + } + }, + event_prevent_default: () => { + if (event_temp_data && event_temp_data.event) { + event_temp_data.event.preventDefault(); + } + }, + + dispatch_custom_event: (id_ptr, id_len, name_ptr, name_len, options_bits) => { + let id = wasmMemoryInterface.loadString(id_ptr, id_len); + let name = wasmMemoryInterface.loadString(name_ptr, name_len); + let options = { + bubbles: (options_bits & (1<<0)) !== 0, + cancelabe: (options_bits & (1<<1)) !== 0, + composed: (options_bits & (1<<2)) !== 0, + }; + + let element = getElement(id); + if (element) { + element.dispatchEvent(new Event(name, options)); + return true; + } + return false; + }, + + get_element_value_f64: (id_ptr, id_len) => { + let id = wasmMemoryInterface.loadString(id_ptr, id_len); + let element = getElement(id); + return element ? element.value : 0; + }, + get_element_value_string: (id_ptr, id_len, buf_ptr, buf_len) => { + let id = wasmMemoryInterface.loadString(id_ptr, id_len); + let element = getElement(id); + if (element) { + let str = element.value; + if (buf_len > 0 && buf_ptr) { + let n = Math.min(buf_len, str.length); + str = str.substring(0, n); + this.mem.loadBytes(buf_ptr, buf_len).set(new TextEncoder("utf-8").encode(str)) + return n; + } + } + return 0; + }, + get_element_value_string_length: (id_ptr, id_len) => { + let id = wasmMemoryInterface.loadString(id_ptr, id_len); + let element = getElement(id); + if (element) { + return element.value.length; + } + return 0; + }, + get_element_min_max: (ptr_array2_f64, id_ptr, id_len) => { + let id = wasmMemoryInterface.loadString(id_ptr, id_len); + let element = getElement(id); + if (element) { + let values = wasmMemoryInterface.loadF64Array(ptr_array2_f64, 2); + values[0] = element.min; + values[1] = element.max; + } + }, + set_element_value_f64: (id_ptr, id_len, value) => { + let id = wasmMemoryInterface.loadString(id_ptr, id_len); + let element = getElement(id); + if (element) { + element.value = value; + } + }, + set_element_value_string: (id_ptr, id_len, value_ptr, value_id) => { + let id = wasmMemoryInterface.loadString(id_ptr, id_len); + let value = wasmMemoryInterface.loadString(value_ptr, value_len); + let element = getElement(id); + if (element) { + element.value = value; + } + }, + + + get_bounding_client_rect: (rect_ptr, id_ptr, id_len) => { + let id = wasmMemoryInterface.loadString(id_ptr, id_len); + let element = getElement(id); + if (element) { + let values = wasmMemoryInterface.loadF64Array(rect_ptr, 4); + let rect = element.getBoundingClientRect(); + values[0] = rect.left; + values[1] = rect.top; + values[2] = rect.right - rect.left; + values[3] = rect.bottom - rect.top; + } + }, + window_get_rect: (rect_ptr) => { + let values = wasmMemoryInterface.loadF64Array(rect_ptr, 4); + values[0] = window.screenX; + values[1] = window.screenY; + values[2] = window.screen.width; + values[3] = window.screen.height; + }, + + window_get_scroll: (pos_ptr) => { + let values = wasmMemoryInterface.loadF64Array(pos_ptr, 2); + values[0] = window.scrollX; + values[1] = window.scrollY; + }, + window_set_scroll: (x, y) => { + window.scroll(x, y); + }, + + device_pixel_ratio: () => { + return window.devicePixelRatio; + }, + + }, + + "webgl": webglContext.getWebGL1Interface(), + "webgl2": webglContext.getWebGL2Interface(), }; -} +}; +async function runWasm(wasmPath, consoleElement, extraForeignImports) { + let wasmMemoryInterface = new WasmMemoryInterface(); -export {WasmMemoryInterface, odinSetupDefaultImports}; \ No newline at end of file + let imports = odinSetupDefaultImports(wasmMemoryInterface, consoleElement); + let exports = {}; + + if (extraForeignImports !== undefined) { + imports = { + ...imports, + ...extraForeignImports, + }; + } + + const response = await fetch(wasmPath); + const file = await response.arrayBuffer(); + const wasm = await WebAssembly.instantiate(file, imports); + exports = wasm.instance.exports; + wasmMemoryInterface.setExports(exports); + wasmMemoryInterface.setMemory(exports.memory); + + exports._start(); + + if (exports.step) { + const odin_ctx = exports.default_context_ptr(); + + let prevTimeStamp = undefined; + const step = (currTimeStamp) => { + if (prevTimeStamp == undefined) { + prevTimeStamp = currTimeStamp; + } + + const dt = (currTimeStamp - prevTimeStamp)*0.001; + prevTimeStamp = currTimeStamp; + exports.step(dt, odin_ctx); + window.requestAnimationFrame(step); + }; + + window.requestAnimationFrame(step); + } + + exports._end(); + + return; +}; + +window.odin = { + // Interface Types + WasmMemoryInterface: WasmMemoryInterface, + WebGLInterface: WebGLInterface, + + // Functions + setupDefaultImports: odinSetupDefaultImports, + runWasm: runWasm, +}; +})(); \ No newline at end of file